Sign in to follow this  
Red ochre

'Scribble', temp surface & prop rules questions

Recommended Posts

'Scribble' is almost finished but I would like to make a couple of improvements if possible.

It is laid out in a very similar way to both 'FurBlur' and 'Cobweb'. I have two issues:-

1. Removing the temporary surface 'dest'.

(As Midora suggested in the Furblur thread).

I have tried removing the extra surface 'dest' and changing all references to it in 'Render' to 'dst'.

Also removing the 'dest' Surface declaration and the OnDispose method for it and removing all code from within 'OnRender'.

Problem: It builds and works fine for whole layers and rectangular selections, however, with an irregular lasso selection it renders to all the enclosing rectangle and 'undo' will only undo the lasso selection, leaving the enclosing rectangle (minus selection) unchanged.?

Should there be some code within 'OnRender' to limit the effect to what is actually selected?

Any ideas would be appreciated as a two surface soloution would be tidier.

However, I don't think using three surfaces is a big problem, as if it is used for an image large enough for memory to be an issue, then it would probably take an age to render.

-----------------------------------------------------------------------

2. Presets:

I have included some presets which simply override the 'property' controls. Meaning the controls move but do nothing - confusing for the user (and the plugin writer!).

Ideally, the preset would just set the initial default values of the controls. They could then be tweaked by the user. Is this possible?

If not, then it would be useful to 'gray out' the controls that will have no effect if a preset is used. I suspect this is possible using 'ReadOnlyBoundToValueRule' - but I'm getting lost with the syntax of how to use it. Any help (or code examples)would be much appreciated.

------------------------------------------------------------------------

Below is the .dll to play with (the 3 surface safe version), some example images and the source code.

Keep a note of any useful settings - I may 'borrow' them!

Many thanks

http://i.imgur.com/ifIXMUW.png http://i.imgur.com/BwDqYf5.png http://i.imgur.com/3fgeZZM.png http://i.imgur.com/EYA2KkJ.png http://i.imgur.com/rqjCiYj.png http://i.imgur.com/avRehJH.png http://i.imgur.com/BkBihHg.png http://i.imgur.com/bS3UE1t.png

http://i.imgur.com/XbIRUta.png http://i.imgur.com/n5NB6PY.png http://i.imgur.com/yKGs6ck.png http://i.imgur.com/FK5s9VK.png http://i.imgur.com/DyRTJKx.png http://i.imgur.com/2Qm776K.png

 

Scribble.zip

The 'code' tags don't seem to be working so here is the zipped CS file

ScribbleCode.zip

  • Upvote 2

Share this post


Link to post
Share on other sites

Another fascinating plugin.

 

An interesting variation would be to combine the ideas of this plugin with outlines determined by the color gradients; drawing the outline, and preventing the scribbles from crossing the outlines. I may eventually pursue that idea if you don't.

Share this post


Link to post
Share on other sites

Perhaps someone who knows about the PDN internals could answer a question related to this: Does creating a separate surface in a plugin require significantly more memory than creating another layer? Few people would think twice about creating a temporary layer to achieve some effect.

Share this post


Link to post
Share on other sites

Perhaps someone who knows about the PDN internals could answer a question related to this: Does creating a separate surface in a plugin require significantly more memory than creating another layer? 

 

I'm betting a scratch surface would actually be cheaper in memory terms (no blend mode, visibility flag, layer thumbnail, history, etc...)

Share this post


Link to post
Share on other sites

Hi MJW,

One way to detect edges is to call the built in Gaussian blur and then use the difference between the blurred surface and the source surface to define boundaries. You could then prevent an added line being drawn across that threshold.
It may be worthwhile using the GDI to draw lines (or enclosed shapes) too. That should give you control over the line width.

Regarding surface and memory, I tend to think - if works on my old computer then there can't be too much wrong with it! - But I tend to use 'sensible' sized canvasses. Some users work as large as possible to hide the jagged effects of non-anti-aliased plugins.(I've written some of them!) :lol:

Share this post


Link to post
Share on other sites

 

 

Problem: It builds and works fine for whole layers and rectangular selections, however, with an irregular lasso selection it renders to all the enclosing rectangle and 'undo' will only undo the lasso selection, leaving the enclosing rectangle (minus selection) unchanged.?

 

Try adding this line to this routine:

 

        void MyLine2Dest(Surface dest, Surface src, Rectangle rect, int L, double Angle, double Rx, double Ry, int nB, int nG, int nR, int nA)
        {
            PdnRegion selectionRegion = EnvironmentParameters.GetSelection(src.Bounds);//add this line
 
 
and set the pixels like this:
 
                        if (selectionRegion.IsVisible(Lxi, Tyi)) dest[Lxi, Tyi] = LT;
                        if (selectionRegion.IsVisible(Rxi, Tyi)) dest[Rxi, Tyi] = RT;
                        if (selectionRegion.IsVisible(Lxi, Byi)) dest[Lxi, Byi] = LB;
                        if (selectionRegion.IsVisible(Rxi, Byi)) dest[Rxi, Byi] = RB;
 
Nice Plugin by the way.
Edited by TechnoRobbo
  • Upvote 1

Share this post


Link to post
Share on other sites

Thanks TR for the idea - I think I've explained myself badly!

In the Furblur thread Midora suggested getting rid of the 'dest' temporary surface. When I tried doing that I found the bug with lasso selections. This one still uses three surfaces (src,dest and dst) and works fine. However I will experiment with your idea - but using 'dst' (as 'dest' won't exist). As MJW points out, using 3 surfaces instead of 2 probably isn't an issue anyway.

Share this post


Link to post
Share on other sites

One way to detect edges is to call the built in Gaussian blur and then use the difference between the blurred surface and the source surface to define boundaries. You could then prevent an added line being drawn across that threshold.

 

I've tried that method, but wasn't happy with the result. The trouble is, it seems to result in double lines. When there's a sharp transition, the outline consists of two dark lines separated by a light line. Here's a rather primitive CodeLab test version:

Hidden Content:

#region UICode
int Amount1=5; // [0,100] Radius
double Amount2=5; // [0, 20] Intensity
#endregion


// Here is the main render loop function
void Render(Surface dst, Surface src, Rectangle rect)
{
    // Setup for calling the Gaussian Blur effect
    GaussianBlurEffect blurEffect = new GaussianBlurEffect();
    PropertyCollection blurProps = blurEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken BlurParameters = new PropertyBasedEffectConfigToken(blurProps);
    BlurParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1);
    blurEffect.SetRenderInfo(BlurParameters, new RenderArgs(dst), new RenderArgs(src));
    // Call the Gaussian Blur function
    blurEffect.Render(new Rectangle[1] {rect},0,1);

    scale = Amount2 * 100.0 / 256.0;
    
    // Now in the main render loop, the dst canvas has a blurred version of the src canvas
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra CurrentPixel = ComputeColor(src[x, y], dst[x, y]);

            // TODO: Add additional pixel processing code here



            dst[x,y] = CurrentPixel;
        }
    }
}

double scale;

ColorBgra ComputeColor(ColorBgra c1, ColorBgra c2)
{
    double dB = c1.B - c2.B;
    double dG = c1.G - c2.G;
    double dR = c1.R - c2.R;
    double d = scale * Math.Sqrt(dB * dB + dG * dG + dR * dR);
    byte c = (byte)Math.Round(255.0 - d);
    if (c < 0)
        c = 0;
    return ColorBgra.FromBgr(c, c, c);
}

Edited by MJW

Share this post


Link to post
Share on other sites

For comparison, I'll post a test version of an outline method that seems to work reasonably well, even though it's a bit ad hoc. It uses the method I previously alluded to, basing the outline strength on the magnitude of the color gradient. Interestingly, the outline does not depend on the central pixel, only its neighbors. (I'm not sure if that's good or bad.) There are almost certainly better methods.
 

Hidden Content:
#region UICode
double Amount1 = 3.0; // [0, 10.0] Outline Scale
double Amount2 = 0.0; // [-1, 1] Outline Nonlinearity
double Amount3 = 0.1; // [0, 1] Outline Low Threshhold;
double Amount4 = 1.0; // [0, 1] Outline Contrast
#endregion

const double middleWeight = 1.4;

Surface Src;

double outlineScale, outlineNonLin, outlineThreshhold, outlineContrast;
double outlineTScale;

int top, right, bottom, left;
int xMax, yMax;

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Delete any of these lines you don't need
    Rectangle selection = this.EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
    long CenterX = (long)(((selection.Right - selection.Left) / 2)+selection.Left);
    long CenterY = (long)(((selection.Bottom - selection.Top) / 2)+selection.Top);
    ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor;
    ColorBgra SecondaryColor = (ColorBgra)EnvironmentParameters.SecondaryColor;

	Src = src;
	top = rect.Top; right = rect.Right; bottom = rect.Bottom; left = rect.Left;
      xMax = src.Width - 1; yMax = src.Height - 1;

	

	outlineScale = Amount1;
	outlineNonLin = Amount2;
	outlineThreshhold = 0.99 * Amount3;
	outlineTScale = 1.0 / (1.0 - outlineThreshhold); 
	outlineContrast = Amount4;

    
    ColorBgra CurrentPixel;
    for (int y = top; y < bottom; y++)
    {
        for (int x = left; x < right; x++)
        {
            double alpha = GradientAlpha(x, y);
            byte c = (byte)Math.Round(255.0 * (1.0 - alpha));
            CurrentPixel = ColorBgra.FromBgr(c, c, c);

			dst[x, y] = CurrentPixel;
        }
    }
}

protected double GradientAlpha(int x, int y)
{
	ColorBgra MM, UM, UR, MR, LR, LM, LL, ML, UL;

      //return 1.0;
	if ((x == 0) || (x == xMax) || (y == 0) || (y == yMax))
		return 1.0;
	
	MM = Src[x, y];
	UM = Src[x, y - 1];
	UR = Src[x + 1, y - 1];
	MR = Src[x + 1, y];
	LR = Src[x + 1, y + 1];
	LM = Src[x, y + 1];
	LL = Src[x - 1, y + 1];
	ML = Src[x - 1, y];
	UL = Src[x - 1, y - 1];

	double xR = (UL.R - UR.R) + middleWeight * (ML.R - MR.R) + (LL.R - LR.R);
	double xG = (UL.G - UR.G) + middleWeight * (ML.G - MR.G) + (LL.G - LR.G);
	double xB = (UL.B - UR. + middleWeight * (ML.B - MR. + (LL.B - LR.;
	double yR = (UL.R - LL.R) + middleWeight * (UM.R - LM.R) + (UR.R - LR.R);
	double yG = (UL.G - LL.G) + middleWeight * (UM.G - LM.G) + (UR.G - LR.G);
	double yB = (UL.B - LL. + middleWeight * (UM.B - LM. + (UR.B - LR.;

	double xComponent2 = xR * xR + xG * xG + xB * xB;
	double yComponent2 = yR * yR + yG * yG + yB * yB;

	double alpha = xComponent2 + yComponent2;
	alpha = Math.Sqrt(alpha / 6.0) / (255.0 * (2.0 + middleWeight));
	alpha *= outlineScale;
	alpha = outlineTScale * (alpha - outlineThreshhold);
	if (alpha < 0.0)
		alpha = 0.0;
	else if (alpha > 1.0)
		alpha = 1.0;
	alpha *= 1.0 - outlineNonLin * (1.0 - alpha);
	alpha *= outlineContrast;
	return alpha;
}

Share this post


Link to post
Share on other sites

One way to detect edges is to call the built in Gaussian blur and then use the difference between the blurred surface and the source surface to define boundaries.

Here is some code to do that:

http://forums.getpaint.net/index.php?/topic/18105-line-drawing/

Share this post


Link to post
Share on other sites

However, I don't think using three surfaces is a big problem, as if it is used for an image large enough for memory to be an issue, then it would probably take an age to render.

PSFilterPdn uses 5 surfaces, plus the Photoshop filters can allocate additional memory.

 

I have included some presets which simply override the 'property' controls. Meaning the controls move but do nothing - confusing for the user (and the plugin writer!).

Ideally, the preset would just set the initial default values of the controls. They could then be tweaked by the user. Is this possible?

If not, then it would be useful to 'gray out' the controls that will have no effect if a preset is used. I suspect this is possible using 'ReadOnlyBoundToValueRule' - but I'm getting lost with the syntax of how to use it. Any help (or code examples)would be much appreciated.

I converted the dialog to use Windows Forms which fixes the issue with the preset not overridding the control values, and added the ability to load and save presets as XML.

 

Compiled DLL and VS 2010 project file attached.

 

ScribbleEffect.zip

ScribbleEffectSrc.zip

 

  • Upvote 1

Share this post


Link to post
Share on other sites

 

 

If not, then it would be useful to 'gray out' the controls that will have no effect if a preset is used. I suspect this is possible using 'ReadOnlyBoundToValueRule' - but I'm getting lost with the syntax of how to use it. Any help (or code examples)would be much appreciated.

 

Red, If you still wants to grey out the controls while using a Preset try this code it will only leave reseed active.

 

        protected override PropertyCollection OnCreatePropertyCollection()

        {
            List<Property> props = new List<Property>();
            props.Add(StaticListChoiceProperty.CreateForEnum<PresetStyle>(PropertyNames.PresetStyle, 0, false));
            props.Add(StaticListChoiceProperty.CreateForEnum<Back>(PropertyNames.Back, 0, false));
            props.Add(StaticListChoiceProperty.CreateForEnum<Fore>(PropertyNames.Fore, 0, false));
            props.Add(new DoubleProperty(PropertyNames.Reps, 4, 0, 40));
            props.Add(new DoubleProperty(PropertyNames.Lmult, 1, 0, 10));
            props.Add(new DoubleProperty(PropertyNames.CrossH, 0, -1.00, 1.00));
            props.Add(new DoubleProperty(PropertyNames.Ltone, 0, -1.00, 1.00));
            props.Add(new DoubleProperty(PropertyNames.Tfreq, 0, -1.00, 1.00));
            props.Add(new Int32Property(PropertyNames.Tbands, 5, 2, 32));
            props.Add(new DoubleProperty(PropertyNames.AngLite, 60, 0, 180));
            props.Add(new DoubleProperty(PropertyNames.AngDark, 120, 0, 180));
            props.Add(new DoubleProperty(PropertyNames.Humanize, 0, 0.00, 1.00));
            props.Add(new DoubleProperty(PropertyNames.BTrat, 0, 0, 1));
            props.Add(StaticListChoiceProperty.CreateForEnum<Tpriority>(PropertyNames.Tpriority, 0, false));
            props.Add(new Int32Property(PropertyNames.ReSeed, 0, 0, 255));
            //can I use prop rules here to change the defualt values based on what is selected in 'PresetStyle' enum?
 
             List<PropertyCollectionRule> propRules = new List<PropertyCollectionRule>();
 
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Back, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Fore, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Reps, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Lmult, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.CrossH, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Ltone, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Tfreq, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Tbands, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.AngLite, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.AngDark, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.BTrat, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Tpriority, PropertyNames.PresetStyle, PresetStyle.None, true));
            propRules.Add(new ReadOnlyBoundToValueRule<object, StaticListChoiceProperty>(PropertyNames.Humanize, PropertyNames.PresetStyle, PresetStyle.None, true));
            return new PropertyCollection(props, propRules);
        }
 
(post-edited) left out a couple of controls - fixed now.
Edited by TechnoRobbo
  • Upvote 1

Share this post


Link to post
Share on other sites

Null54 - Absolutely stunning - perfect!! Thank you. :trophy:B)
The .dll behaves exactly as I had hoped and the source code will be a very useful reference for future plugins.
Clipwarp, FurBlur, Cobweb, now 'Scribble' - none would exist without your generous and knowledgeable help!

TR - thanks for the useful 'ReadOnlyBoundToValueRule' examples - I think I can see where I was going wrong now. :GlowEffect:

Seerose - lovely example of what the plugin is capable of - great colours!
Hopefully I will officially publish this in it's own thread soon. It would be good if you can re-post your picture there, then.

MJW - It may be worth you starting a new a thread in developer's central to discuss various ways to detect edges and how to use that information in a plugin.

It could be interesting to see peoples different approaches.

 

Edit (my terrible spelling).

Share this post


Link to post
Share on other sites

Null54 - Absoloutly stunning - perfect!! Thank you. :trophy:B)

The .dll behaves exactly as I had hoped and the source code will be a very useful reference for future plugins.

Clipwarp, FurBlur, Cobweb, now 'Scribble' - none would exist without your generous and knowledgeable help!

TR - thanks for the useful 'ReadOnlyBoundToValueRule' examples - I think I can see where I was going wrong now. :GlowEffect:

Seerose - lovely example of what the plugin is capable of - great colours!

Hopefully I will officially publish this in it's own thread soon. It would be good if you can re-post your picture there, then.

MJW - It may be worth you starting a new a thread in developer's central to discuss various ways to detect edges and how to use that information in a plugin.

It could be interesting to see peoples different approaches.

 

Red Ocker;

 

Okay, no problem, thank you for the information.

Edited by Seerose

Share this post


Link to post
Share on other sites

Oh Seerose - I didn't mean you to take away the picture  <3- I was just hoping you would post it again when I publish properly.
I'm really pleased you are showing an interest in the plugin!

Share this post


Link to post
Share on other sites

Progress report:

I've changed it to point at the .NET Framework 3.5 (I'm still on Vista).
Added some reset buttons and played around with the U.I.

Here is a Beta to try out - all comments useful.

ScribbleBeta1.zip

One thing I've not investigated (haven't touched that code yet) is the Reseed button, which doesn't appear to work as expected?
Also not sure when to use 'this.' and when not to, in 'ScribbleConfigDialog' for each slider ValueChanged method?

Seems to work ok anyway! - thanks :)

Share this post


Link to post
Share on other sites

Red ochre, this is a very minor thing, but I noticed the sine and cosine calculations are inside the line loop, though they could be computed outside. It may make no difference, since the compiler may optimize by moving them outside the loop, but it might be better to move the computation outside the loop.

  • Upvote 1

Share this post


Link to post
Share on other sites

MJW - well spotted!
As you can see 'Scribble' is based on 'FurBlur' - the angle calculations had to be inside the loop for that to generate curves. However, Scribble only uses straight lines, so I will try moving the angle calculations outside the loop - if there is a noticeable speed increase I will update.
Thanks.

Share this post


Link to post
Share on other sites

Red ochre, I thought of an interesting foreground color option you might consider adding. When using the tone band colors, allow the option of adding a random number between 0 and 1, instead of 0.5, before truncating to an integer. That way, the strokes would be one of eight different tone band colors, with colors closer to the true color occurring proportionally more often.

 

Something like:

                        double bRound, gRound, bRound;
                        bRound = RandomNumber.NextDouble();
                        gRound = RandomNumber.NextDouble();
                        rRound = RandomNumber.NextDouble();
                        int Bnumber = (int)((avBtone9 / colwidth) + bRound);
                        int Gnumber = (int)((avGtone9 / colwidth) + gRound);
                        int Rnumber = (int)((avRtone9 / colwidth) + rRound)

Added: Perhaps a better method is to compute the rounding factor as: 0.5 + randomness * (randomNumber - 0.5)

 

Where randomNumber is a random number between 0 and 1, and randomness is a variable between 0 and 1. If randomness is 0, the closest color is chosen; if it's 1, the colors are chosen proportionally; if it's, for instance, 0.1, different colors will be used when the true color is nearly equally distant from two colors.

 

(Hope you don't mind me offering this suggestion, which you are, of course, welcome to ignore.)

Edited by MJW

Share this post


Link to post
Share on other sites

Red ochre, would you please grant me permission to post a variation of the Scribble plugin on the "Displaying intermediate results" thread? It will just be as a demo, not a plugin release. I need an impressive plugin that takes a while to do the rendering and that doesn't use PropertyBasedEffectConfigToken.

  • Upvote 1

Share this post


Link to post
Share on other sites

Hi MJW,

Sorry for not replying sooner - I've been working on improving another plugin and you did say I could ignore the last post! :D

By all means use Scribble for example purposes on this forum - I'm glad you asked first though. The code posted for scribble is very slightly out of date - but most of the changes were to do with the custom U.I. so not really relevant. One thing to check is that the 'Tone priority' choices are the right way round though.

------------------------------------------

Regarding 'Displaying intermediate results', perhaps you could render the whole selection but only apply the effect to a smaller portion to give a preview. eg.

1. Include a 'preview' checkbox in the U.I.
2. If checked only apply the effect to pixels in the middle third (1/9th of the area) - make sure all other dst pixels are assigned the src pixel values. This would give the user a quick way to adjust settings and assess full rendering time.
3. If unchecked the effect would be re - applied to the whole selection.

Similarly, checkboxes could be used to show previews at different stages of the effect, provided the dst pixels have all been given a value. The effect would have to be rerun each time the U.I is changed though - so no time saver.

Perhaps that would be considered a 'hack' or overly simplistic - but it may be a useful for experimental plugins.
Bear in mind that I am not a 'real' programmer and you will get better advice from those here that really know what they are doing.
 

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.

Sign in to follow this