xod

Unfinished plugins

Recommended Posts

I think you might want to move the following code to PreRender:

 

    try
    {
         myFont = new FontFamily(font.Name);
    }
    catch
    {
        myFont = new FontFamily("Arial");
    }

 Naturally, myFont would  need to be made global.

Share this post


Link to post
Share on other sites

A bit more complex, but I think you could create the path in PreRender. That would require you dispose of it in an OnDispose routine instead of relying on a "using" block.

 

EDIT: I haven't tried this yet, so I don't know if there might be problems accessing path from multiple threads.

Share this post


Link to post
Share on other sites
On 2/15/2018 at 12:51 PM, xod said:

This is my Outlined text plugin. Is not working very well because of the approach.

 

Seems to work pretty good to me.

 

What issues are you seeing?

Share this post


Link to post
Share on other sites

Hi

 

Some fonts show peaks I consider as a bug in the font not in the plugin.

 

XOD.png

 

With ELI's sample I was thinking the outline was the square... no it's the font...

What about adding the square for other fonts? or a circle? Or a variable gap between letters?

 

Share this post


Link to post
Share on other sites
28 minutes ago, MadJik said:

Some fonts show peaks I consider as a bug in the font not in the plugin.

 

That is easily fixed with a single line of code:

 

outlinePen.LineJoin = LineJoin.Round; // Just add this line and those mitred line ends will be gone!
path.AddString(text, myFont, (int)FontStyle.Regular, g.DpiY * txtSize / 72, new PointF(adrX, adrY), format);
g.DrawPath(outlinePen, path);
g.FillPath(myBrush, path);

 

  • Like 1
  • Upvote 2

Share this post


Link to post
Share on other sites

Thanks BoltBait.
Now you can increase the 'Outline size' to 100 or more without peek, as MadJik has noticed.

But some fonts like Algerian will not even look like the original, (e.g. see D letter) but it's pretty good.

Edited by xod

Share this post


Link to post
Share on other sites

You need to override InitDialogFromToken, and set UI control values from your Token.

 

protected override void InitDialogFromToken(EffectConfigToken effectTokenCopy)
{
	MyControl.Value = effectTokenCopy.MyAmount;
}

 

  • Upvote 1

Share this post


Link to post
Share on other sites

I fixed the issue that MadJik found.  Then, I extended the functionality to add gradient fills:

// Name: Outlined / Gradient text
// Submenu: Text Formations
// Author: xod & BoltBait
// Title: Outlined / Gradient text
// Version: 1.0
// Desc: Draws outlined, gradient filled text
// Keywords: text|gradient|outline
// URL:
#region UICode
MultiLineTextboxControl Amount1 = "TEST"; // [1,32767] Text
FontFamily Amount2 = new FontFamily("Arial"); // Font
IntSliderControl Amount3 = 72; // [2,400] Font size
IntSliderControl Amount4 = 8; // [0,100] Outline size
ColorWheelControl Amount5 = ColorBgra.FromBgr(0,0,0); // [PrimaryColor] Outline color
ColorWheelControl Amount6 = ColorBgra.FromBgr(255,255,255); // [SecondaryColor] Fill color
ListBoxControl Amount7 = 0; // Fill Type|Solid Fill|Vertical Gradient|Horizontal Gradient|Diagonal Gradient|Reverse Diagonal Gradient
ColorWheelControl Amount8 = ColorBgra.FromBgr(0,0,0); // [SecondaryColor] {!Amount7} Secondary Fill Color
PanSliderControl Amount9 = Pair.Create(0.000,0.000); // Location
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    Rectangle sel = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
    dst.CopySurface(src,rect.Location,rect);

    int adrX = (int)Math.Round((((Amount9.First + 1) / 2) * (sel.Right - sel.Left)) + sel.Left);
    int adrY = (int)Math.Round((((Amount9.Second + 1) / 2) * (sel.Bottom - sel.Top)) + sel.Top);

    String text = Amount1;
    if (text == "") text = " ";
    FontFamily font = Amount2;
    int txtSize = Amount3;
    Color fontColor = Amount6;
    int outlineSize = Amount4;
    Color outlineColor = Amount5;
    byte opacity = 255;

    Graphics g = new RenderArgs(dst).Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.TextRenderingHint = TextRenderingHint.AntiAlias;

    StringFormat format = new StringFormat();
    format.Alignment = StringAlignment.Center;
    format.LineAlignment = StringAlignment.Center;

    ColorBgra FillColor1 = Amount6;
    ColorBgra FillColor2 = Amount8;    
    if (Amount7 == 0) FillColor2 = FillColor1;
    
    SizeF stringSize = new SizeF();
    stringSize = g.MeasureString(text, new Font(Amount2, Amount3));
    
    int GStartX = 0;
    int GStartY = 0;
    int GEndX = 1;
    int GEndY = 1;
    
    switch (Amount7)
    {
        case 1:
            GStartX = adrX;
            GEndX = adrX;
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 2:
            GStartX = (int)(adrX - stringSize.Width / 2);
            GEndX = (int)(adrX + stringSize.Width / 2);
            GStartY = adrY;
            GEndY = adrY;
            break;
        case 3:
            GStartX = (int)(adrX - stringSize.Width / 2);
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndX = (int)(adrX + stringSize.Width / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 4:
            GStartX = (int)(adrX + stringSize.Width / 2);
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndX = (int)(adrX - stringSize.Width / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        default:
            break;
    }
    using (GraphicsPath path = new GraphicsPath())
    using (Pen outlinePen = new Pen(Color.FromArgb(opacity, outlineColor), outlineSize))
    using (LinearGradientBrush myGradientBrush = new LinearGradientBrush(new Point(GStartX,GStartY),new Point(GEndX,GEndY), FillColor1, FillColor2))
    {
        g.Clip = new Region(rect);
        FontFamily myFont;
        try
        {
            myFont = new FontFamily(font.Name);
        }
        catch
        {
            myFont = new FontFamily("Arial");
        }
        outlinePen.LineJoin = LineJoin.Round;
        path.AddString(text, myFont, (int)FontStyle.Regular, g.DpiY * txtSize / 72, new PointF(adrX, adrY), format);
        if (Amount4 > 0)
        {
            g.DrawPath(outlinePen, path);
        }
        g.FillPath(myGradientBrush, path);
    }
    g.Dispose();
}

People are always searching for a way to outline text and also to draw gradient filled text.  Now, they can get it from you.

 

Enjoy.  :beer:  

 

 

  • Like 2
  • Upvote 3

Share this post


Link to post
Share on other sites
3 hours ago, xod said:

how can I update (in code) the controls to preserve previous values for a new run in another layer?

 

Excerpt from a document MJW and I are occasionally working on....

Tokens & tokenization

Quite simply, ‘tokens’ are the current value of each control in the plugin GUI. Each time a control is changed in the GUI, the value is updated (tokenization) and stored. These values are passed back and forth between the Dialog class and the Token class so they reflect the current state of the GUI controls.

 

Tokenization provides the default values for the plugin when it opens. Tokenization also ensures changed values persists between instances of the plugin being opened in a single paint.net session.

 

The Token class also supplies the values to the plugin’s Effect class, so the Effect class always has the latest values with which to render the effect.

 

The orange lines in the previous diagram and the two following diagrams show the flow of Token values between classes for the different type of plugin.

 

In IndirectUI plugins, tokenization is performed internally by paint.net, so the plugin author does not need to concern themselves with it.

 

hekJPzm_mjxKSRqwRYJuVAilvIZte094CTIJ9fw_                 


 

 

The more complex WinForm plugins require that the author make provision for

 

  1. Creating tokens
  2. Establishing the initial values of the tokens (the plugin defaults)
  3. Handling the flow of tokens between the Dialog, Token and Effect classes.

 

 bNvwVFF9bD8sv7Os9tam9dCU3osGEWn3dfdokbUv


 


 

1 and 2 are done in your Effectconfigtoken.cs (or equivalent). First create the token

 // Example not using getters/setters:
        //----------------------------------------
        // public double variable;
        public int storedTileSize;

Then set the default value

public EffectPluginConfigToken()
            : base()
        {
            // Set default variables here
            // this.variable = 0.0;
            this.storedTileSize = 32;
		}

Finally add code to copy the token

       protected EffectPluginConfigToken(EffectPluginConfigToken copyMe)
            : base(copyMe)
        {
            // this.variable = copyMe.variable;
            this.storedTileSize = copyMe.storedTileSize;
		}

 

That's the token construction taken care of. So, how is it used?

 

In the EffectPluginConfigDialog.cs (or equivalent) there are two places tokens are used. First, this...

        protected override void InitialInitToken()
        {
            theEffectToken = new EffectPluginConfigToken();
        }

Then you can read the tokens in to create the setting(s) for the dialog

 protected override void InitDialogFromToken(EffectConfigToken effectToken)
        {
            EffectPluginConfigToken token = (EffectPluginConfigToken)effectToken;
            
            // restore last used Tile Size
            TileSize = token.storedTileSize;
		}

^^ assuming you've set up an int named TileSize ;)

 

Of course, you need a way to save the tokens from the dialog controls too...,

       protected override void InitTokenFromDialog()
        {
            EffectPluginConfigToken token = (EffectPluginConfigToken)theEffectToken;
            token.storedTileSize = (int)numericUpDown1.Value;
		}

^^ this sets the Tilesize token to the value of the numericupdown control

 

Finally, you need to update the tokens (i.e. save a fresh set) when the OK button in your Effect is clicked. This happens when you call FinishTokenUpdate(), which is supplied by PDN (basically it fires InitTokenFromDialog()).

        private void buttonOK_Click(object sender, EventArgs e)
        {
            FinishTokenUpdate();
            //DisposeImages(); if you have any

            DialogResult = DialogResult.OK;
            this.Close();
        }

 

  • Upvote 3

Share this post


Link to post
Share on other sites

@BoltBait

For vertical and horizontal gradient you need to affect  GStart and GEnd also:

 

    int GStartX = 0;
    int GStartY = 0;
    int GEndX = 10;
    int GEndY = 10;

    switch (Amount7)
    {
        case 1:
            GStartX =adrX;
            GEndX = adrX;
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 2:
            GStartX = (int)(adrX - stringSize.Width / 2);
            GEndX = (int)(adrX + stringSize.Width / 2);
            GStartY =adrY;
            GEndY = adrY;
            break;

 

  • Upvote 1

Share this post


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

For vertical and horizontal gradient you need to affect  GStart and GEnd also:

 

I suppose you're right.

 

It doesn't matter what the values are, as long as they are the same.

  • Upvote 1

Share this post


Link to post
Share on other sites

Thank you very much both of you: BoltBait and MadJik who have shown interest in completing this plugin. Thanks to you it looks very professional now.
Thank you toehead_2001 and special thanks for Ego Eram Reputo who gave me some very clear and detailed explanation about VS template.

Share this post


Link to post
Share on other sites
12 hours ago, BoltBait said:

People are always searching for a way to outline text and also to draw gradient filled text.

Thanks xod and Boltbait. The gradient fills are great. Can other fills be added?

textoutlinefills-53fb1bb.png

Share this post


Link to post
Share on other sites

Added new gradient facilities.

 

Spoiler

// Name: Outlined / Gradient text
// Submenu: Text Formations
// Author: xod & BoltBait
// Title: Outlined / Gradient text
// Version: 1.0
// Desc: Draws outlined, gradient filled text
// Keywords: text|gradient|outline
// URL:
#region UICode
MultiLineTextboxControl Amount1 = "TEST"; // [1,32767] Text
FontFamily Amount2 = new FontFamily("Impact"); // Font
IntSliderControl Amount3 = 140; // [2,400] Font size
IntSliderControl Amount4 = 8; // [0,100] Outline size
ColorWheelControl Amount5 = ColorBgra.FromBgr(0,0,255); // [Red] Outline color
ColorWheelControl Amount6 = ColorBgra.FromBgr(139,0,0); // [DarkBlue] Fill color
ListBoxControl Amount7 = 5; // Fill Type|Solid Fill|Vertical Gradient|Horizontal Gradient|Diagonal Gradient|Reverse Diagonal Gradient|Vertical Gradient Reflected|Horizontal Gradient Reflected
ColorWheelControl Amount8 = ColorBgra.FromBgr(255,255,0); // [Cyan] {!Amount7} Secondary Fill Color
DoubleSliderControl Amount9 = 0.5; // [0,1] {!Amount7} Start position
DoubleSliderControl Amount10 = 1; // [0,1] {!Amount7} Intensity
PanSliderControl Amount11 = Pair.Create(0.000,0.000); // Location
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    Rectangle sel = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
    dst.CopySurface(src,rect.Location,rect);

    int adrX = (int)Math.Round(((Amount11.First + 1) / 2) * (sel.Right - sel.Left));
    int adrY = (int)Math.Round(((Amount11.Second + 1) / 2) * (sel.Bottom - sel.Top));

    String text = Amount1;
    FontFamily font = Amount2;
    int txtSize = Amount3;
    Color fontColor = Amount6;
    int outlineSize = Amount4;
    Color outlineColor = Amount5;
    byte opacity = 255;
    if (Amount4 == 0) opacity = 0; //no outline
    float startGradient = (float) Amount9;
    float intensity = (float) Amount10;

    Graphics g = new RenderArgs(dst).Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.TextRenderingHint = TextRenderingHint.AntiAlias;

    StringFormat format = new StringFormat();
    format.Alignment = StringAlignment.Center;
    format.LineAlignment = StringAlignment.Center;

    ColorBgra FillColor1 = Amount6;
    ColorBgra FillColor2 = Amount8;
    if (Amount7 == 0) FillColor2 = FillColor1;

    SizeF stringSize = new SizeF();
    stringSize = g.MeasureString(text, new Font(Amount2, Amount3));

    int GStartX = 0;
    int GStartY = 0;
    int GEndX = 1;
    int GEndY = 1;

    switch (Amount7)
    {
        case 1:
            GStartX = adrX;
            GEndX = adrX;
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 2:
            GStartX = (int)(adrX - stringSize.Width / 2);
            GEndX = (int)(adrX + stringSize.Width / 2);
            GStartY = adrY;
            GEndY = adrY;
            break;
        case 3:
            GStartX = (int)(adrX - stringSize.Width / 2);
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndX = (int)(adrX + stringSize.Width / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 4:
            GStartX = (int)(adrX + stringSize.Width / 2);
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndX = (int)(adrX - stringSize.Width / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 5:
            GStartX = adrX;
            GEndX = adrX;
            GStartY = (int)(adrY - stringSize.Height / 2);
            GEndY = (int)(adrY + stringSize.Height / 2);
            break;
        case 6:
            GStartX = (int)(adrX - stringSize.Width / 2);
            GEndX = (int)(adrX + stringSize.Width / 2);
            GStartY = adrY;
            GEndY = adrY;
            break;
        default:
            break;
    }
    using (GraphicsPath path = new GraphicsPath())
    using (Pen outlinePen = new Pen(Color.FromArgb(opacity, outlineColor), outlineSize))
    using (LinearGradientBrush myGradientBrush = new LinearGradientBrush(new Point(GStartX,GStartY),new Point(GEndX,GEndY), FillColor1, FillColor2))
    {
        if(Amount7 == 5|| Amount7 == 6) myGradientBrush.SetBlendTriangularShape(startGradient, intensity);
        g.Clip = new Region(rect);
        FontFamily myFont;
        try
        {
            myFont = new FontFamily(font.Name);
        }
        catch
        {
            myFont = new FontFamily("Arial");
        }
        outlinePen.LineJoin = LineJoin.Round;
        path.AddString(text, myFont, (int)FontStyle.Regular, g.DpiY * txtSize / 72, new PointF(adrX, adrY), format);
        if (Amount4 > 0)
        {
            g.DrawPath(outlinePen, path);
        }
        g.FillPath(myGradientBrush, path);
    }
    g.Dispose();
}

 

 

Edited by xod
  • Upvote 1

Share this post


Link to post
Share on other sites

@xod

Trouble after the build

 

File: C:\Program Files\paint.net\Effects\OutlinedGradientText.dll
      Name: OutlinedGradientTextEffect.OutlinedGradientTextEffectPlugin
      Version: 1.0.6623.16135
      Author: Copyright ©2018 by xod & BoltBait
      Copyright: Draws outlined, gradient filled text
      Website: http://www.getpaint.net/redirect/plugins.html
      Full error message: PaintDotNet.WorkerThreadException: Worker thread threw an exception ---> System.OutOfMemoryException: Out of memory.
   at System.Drawing.Drawing2D.LinearGradientBrush..ctor(Point point1, Point point2, Color color1, Color color2)
   at OutlinedGradientTextEffect.OutlinedGradientTextEffectPlugin.Render(Surface dst, Surface src, Rectangle rect)
   at OutlinedGradientTextEffect.OutlinedGradientTextEffectPlugin.OnRender(Rectangle[] rois, Int32 startIndex, Int32 length)
   at PaintDotNet.Effects.Effect`1.Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, Int32 startIndex, Int32 length) in D:\src\pdn\src\Effects\Effect`1.cs:line 98
   at PaintDotNet.Effects.BackgroundEffectRenderer.RenderWithClipMask(Effect effect, EffectConfigToken token, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, IRenderer`1 clipMaskRenderer) in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 170
   at PaintDotNet.Effects.BackgroundEffectRenderer.RendererContext.RenderTile(EffectConfigToken token, Int32 tileIndex) in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 150
   at PaintDotNet.Effects.BackgroundEffectRenderer.RendererContext.RenderNextTile(EffectConfigToken token) in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 142
   at PaintDotNet.Effects.BackgroundEffectRenderer.ThreadFunction() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 239
   --- End of inner exception stack trace ---
   at PaintDotNet.Effects.BackgroundEffectRenderer.DrainExceptions() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 418
   at PaintDotNet.Effects.BackgroundEffectRenderer.Abort() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 374
   at PaintDotNet.Effects.BackgroundEffectRenderer.Start() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 320
   at PaintDotNet.Menus.EffectMenuBase.<>c__DisplayClass42_5.<RunEffectImpl>b__5() in D:\src\pdn\src\PaintDotNet\Menus\EffectMenuBase.cs:line 1032
 

Share this post


Link to post
Share on other sites

piYVmn.png

It works fine now. Now I'm curious about why that had to be done. The first time I have encountered that in over 20 builds. Something unique in your plugin I think. B)

 

Edited by AndrewDavid

Share this post


Link to post
Share on other sites
1 hour ago, Eli said:

The text is truncated when making a selection

 

I updated my code above to fix that problem.

Share this post


Link to post
Share on other sites

Given that:

On 2/18/2018 at 3:39 AM, BoltBait said:

...

People are always searching for a way to outline text and also to draw gradient filled text.  Now, they can get it from you.

 

and that:

14 hours ago, Eli said:

Yes, it is working OK now. Thanks BoltBait.

 

... would it be too much to ask for a .dll to be released for the code-handicapped masses? 

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