Jump to content

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:




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



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

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);
                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;
         fx -= oddX; fy -= oddY;
    if (edgeBehavior == 0)
        return Src.GetBilinearSample(fx, fy);
    else if (edgeBehavior == 1)
        return Src.GetBilinearSampleWrapped(fx, fy);
        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;
        // 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
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:





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

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

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

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.

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.

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.

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.

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