Jump to content
How to Install Plugins ×

BoltBait's Object Inner Shadow - Version 3.0 - Updated Jan 31, 2015


BoltBait

Recommended Posts

As requested here... https://forums.getpaint.net/topic/31461-plugin-request-inner-drop-shadow/

Object Inner Shadow Plugin

post-44727-0-31855900-1422502148.png

 

Download here for latest version:

https://forums.getpaint.net/BoltBaitPluginPack

 

Download here for Paint.NET 3.5.11:

InnerShadow3511.zip

I'll edit in a nice description of this later when I'm not so tired. You all know what to do.

For the curious... (This does NOT work in CodeLab, sorry):

 

Spoiler
using System;
using System.Linq;
using System.Text;
using System.Windows;
using System.Drawing;
using Microsoft.Win32;
using System.Reflection;
using System.Drawing.Text;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using PaintDotNet;
using PaintDotNet.Effects;
using PaintDotNet.AppModel;
using PaintDotNet.IndirectUI;
using PaintDotNet.Collections;
using PaintDotNet.PropertySystem;

[assembly: AssemblyTitle("Inner Shadow Plugin for Paint.NET")]
[assembly: AssemblyDescription("Apply inner shadow to objects.")]
[assembly: AssemblyConfiguration("inner|shadow|bevel")]
[assembly: AssemblyCompany("BoltBait")]
[assembly: AssemblyProduct("Inner Shadow")]
[assembly: AssemblyCopyright("Copyright © BoltBait")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("3.0.*")]

namespace InnerShadowEffect
{
    public class PluginSupportInfo : IPluginSupportInfo
    {
        public string Author
        {
            get
            {
                return ((AssemblyCopyrightAttribute)base.GetType().Assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0]).Copyright;
            }
        }
        public string Copyright
        {
            get
            {
                return ((AssemblyDescriptionAttribute)base.GetType().Assembly.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false)[0]).Description;
            }
        }

        public string DisplayName
        {
            get
            {
                return ((AssemblyProductAttribute)base.GetType().Assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false)[0]).Product;
            }
        }

        public Version Version
        {
            get
            {
                return base.GetType().Assembly.GetName().Version;
            }
        }

        public Uri WebsiteUri
        {
            get
            {
                return new Uri("http://www.BoltBait.com/pdn");
            }
        }
    }

    [PluginSupportInfo(typeof(PluginSupportInfo), DisplayName = "Inner Shadow")]
    public class InnerShadowEffectPlugin : PropertyBasedEffect
    {
        public static string StaticName
        {
            get
            {
                return "Inner Shadow";
            }
        }

        public static Image StaticIcon
        {
            get
            {
                return new Bitmap(typeof(InnerShadowEffectPlugin), "InnerShadow.png");
            }
        }

        public static string SubmenuName
        {
            get
            {
                return "Object";
            }
        }

        public InnerShadowEffectPlugin()
            : base(StaticName, StaticIcon, SubmenuName, EffectFlags.Configurable)
        {
        }

        public enum PropertyNames
        {
            Amount1,
            Amount2,
            Amount3,
            Amount4,
            Amount5,
            Amount6
        }


        protected override PropertyCollection OnCreatePropertyCollection()
        {
            List<Property> props = new List<Property>();

            props.Add(new DoubleProperty(PropertyNames.Amount1, -45, -180, +180));
            props.Add(new Int32Property(PropertyNames.Amount2, 10, 1, 100));
            props.Add(new Int32Property(PropertyNames.Amount3, 2, 0, 50));
            props.Add(new DoubleProperty(PropertyNames.Amount4, 0.8, 0, 1));
            props.Add(new Int32Property(PropertyNames.Amount5, ColorBgra.ToOpaqueInt32(ColorBgra.Black), 0, 0xffffff));
            props.Add(new BooleanProperty(PropertyNames.Amount6, true));

            return new PropertyCollection(props);
        }

        protected override ControlInfo OnCreateConfigUI(PropertyCollection props)
        {
            ControlInfo configUI = CreateDefaultConfigUI(props);

            configUI.SetPropertyControlValue(PropertyNames.Amount1, ControlInfoPropertyNames.DisplayName, "Light Angle");
            configUI.SetPropertyControlType(PropertyNames.Amount1, PropertyControlType.AngleChooser);
            configUI.SetPropertyControlValue(PropertyNames.Amount2, ControlInfoPropertyNames.DisplayName, "Shadow Length");
            configUI.SetPropertyControlValue(PropertyNames.Amount3, ControlInfoPropertyNames.DisplayName, "Blur Radius");
            configUI.SetPropertyControlValue(PropertyNames.Amount4, ControlInfoPropertyNames.DisplayName, "Shadow Strength");
            configUI.SetPropertyControlValue(PropertyNames.Amount4, ControlInfoPropertyNames.SliderLargeChange, 0.25);
            configUI.SetPropertyControlValue(PropertyNames.Amount4, ControlInfoPropertyNames.SliderSmallChange, 0.05);
            configUI.SetPropertyControlValue(PropertyNames.Amount4, ControlInfoPropertyNames.UpDownIncrement, 0.01);
            configUI.SetPropertyControlValue(PropertyNames.Amount4, ControlInfoPropertyNames.ControlColors, new ColorBgra[] { ColorBgra.White, ColorBgra.Black });
            configUI.SetPropertyControlValue(PropertyNames.Amount5, ControlInfoPropertyNames.DisplayName, "Shadow Color");
            configUI.SetPropertyControlType(PropertyNames.Amount5, PropertyControlType.ColorWheel);
            configUI.SetPropertyControlValue(PropertyNames.Amount6, ControlInfoPropertyNames.DisplayName, string.Empty);
            configUI.SetPropertyControlValue(PropertyNames.Amount6, ControlInfoPropertyNames.Description, "Keep original image");

            return configUI;
        }

        protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
        {
            this.Amount1 = newToken.GetProperty<DoubleProperty>(PropertyNames.Amount1).Value;
            this.Amount2 = newToken.GetProperty<Int32Property>(PropertyNames.Amount2).Value;
            this.Amount3 = newToken.GetProperty<Int32Property>(PropertyNames.Amount3).Value;
            this.Amount4 = newToken.GetProperty<DoubleProperty>(PropertyNames.Amount4).Value;
            this.Amount5 = ColorBgra.FromOpaqueInt32(newToken.GetProperty<Int32Property>(PropertyNames.Amount5).Value);
            this.Amount6 = newToken.GetProperty<BooleanProperty>(PropertyNames.Amount6).Value;

            // Save time by only calculating this stuff when necessary.
            // This makes the UI much more responsive.

            if (scratch1 == null)
            {
                scratch1 = new Surface(dstArgs.Size);
                scratch1.Clear(ColorBgra.Black);
                for (int y = 0; y < srcArgs.Height; y++)
                {
                    for (int x = 0; x < srcArgs.Width; x++)
                    {
                        if (srcArgs.Surface[x, y].A != 0)
                        {
                            scratch1[x, y] = ColorBgra.Transparent;
                        }
                    }
                }
            }

            if (scratch2 == null)
            {
                scratch2 = new Surface(dstArgs.Size);
                PreviousDistance = 0;
                PreviousAngle = -999;
            }

            if ((PreviousAngle != Amount1) || (PreviousDistance != Amount2))
            {
                // Setup for calling the Motion Blur effect
                MotionBlurEffect mblurEffect = new MotionBlurEffect();
                PropertyCollection mblurProps = mblurEffect.CreatePropertyCollection();
                PropertyBasedEffectConfigToken mBlurParameters = new PropertyBasedEffectConfigToken(mblurProps);
                mBlurParameters.SetPropertyValue(MotionBlurEffect.PropertyNames.Angle, Amount1);
                mBlurParameters.SetPropertyValue(MotionBlurEffect.PropertyNames.Centered, false);
                mBlurParameters.SetPropertyValue(MotionBlurEffect.PropertyNames.Distance, Amount2);
                mblurEffect.SetRenderInfo(mBlurParameters, new RenderArgs(scratch2), new RenderArgs(scratch1));
                // Call the Motion Blur function
                mblurEffect.Render(new Rectangle[1] { scratch1.Bounds }, 0, 1);
                PreviousAngle = Amount1;
                PreviousDistance = Amount2;
            }

            base.OnSetRenderInfo(newToken, dstArgs, srcArgs);
        }


        protected override void OnCustomizeConfigUIWindowProperties(PropertyCollection props)
        {
            // Change the effect's window title
            props[ControlInfoPropertyNames.WindowTitle].Value = "BoltBait's Object Inner Shadow v3.0";
            base.OnCustomizeConfigUIWindowProperties(props);
        }

        protected override unsafe void OnRender(Rectangle[] rois, int startIndex, int length)
        {
            if (length == 0) return;
            for (int i = startIndex; i < startIndex + length; ++i)
            {
                Render(DstArgs.Surface, SrcArgs.Surface, rois[i]);
            }
        }

        #region User Entered Code
        // Title: BoltBait's Object Inner Shadow v3.0
        // Author: BoltBait
        // Submenu: Object
        // Name: Inner Shadow
        // URL: http://www.BoltBait.com/pdn
        // Desc: Apply inner shadow to objects
        // Keywords: Inner|Shadow
        #region UICode
        double Amount1 = -45; // [-180,180] Angle
        int Amount2 = 10; // [1,100] Distance
        int Amount3 = 2; // [0,25] Blur Radius
        double Amount4 = 0.8; // [0,1] Density
        ColorBgra Amount5 = ColorBgra.FromBgr(0, 0, 0); // Shadow Color
        bool Amount6 = true; // [0,1] Keep original image
        #endregion

        // Let's speed up the rendering by only calculating what changes
        int PreviousDistance = 0;
        double PreviousAngle = -999;

        // Setup 2 scratch surfaces
        private Surface scratch1 = null;
        private Surface scratch2 = null;

        // Setup for using Normal blend op
        private BinaryPixelOp normalOp = LayerBlendModeUtil.CreateCompositionOp(LayerBlendMode.Normal);
        //private BinaryPixelOp normalOp = new UserBlendOps.NormalBlendOp();

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

            for (int y = rect.Top; y < rect.Bottom; y++)
            {
                if (IsCancelRequested) return;
                ColorBgra* srcPtr = src.GetPointAddressUnchecked(rect.Left, y);
                ColorBgra* dstPtr = dst.GetPointAddressUnchecked(rect.Left, y);
                for (int x = rect.Left; x < rect.Right; x++)
                {
                    ColorBgra CurrentPixel = *srcPtr;
                    A = CurrentPixel.A;

                    ColorBgra BlackPixel = *dstPtr;
                    BA = (byte)(BlackPixel.A * Amount4);
                    BlackPixel = Amount5;
                    BlackPixel.A = BA;

                    if (Amount6)
                    {
                        CurrentPixel = normalOp.Apply(CurrentPixel, BlackPixel);
                        CurrentPixel.A = A;
                    }
                    else
                    {
                        CurrentPixel = BlackPixel;
                        CurrentPixel.A = Math.Min(A, BlackPixel.A);
                    }

                    *dstPtr = CurrentPixel;
                    srcPtr++;
                    dstPtr++;
                }
            }
        }
        #endregion
    }
}

 

 

History:

v1.0 - Initial Release

v2.0 - Speed up rendering for all but the first two controls

v3.0 - Corrected "keep only shadow" alpha calculation

 

ObjectInnerShadowUI.png

  • Upvote 9
Link to comment
Share on other sites

Brilliant plugin.  Easy to use and so obvious when you think about it.

 

yhsjjie-559.png

 

Drop Shadow's twin :D

Link to comment
Share on other sites

OK, so all 32 people that downloaded the first version, time to upgrade!

Updates:

-The first 2 controls in the UI take some time to calculate, same as before.

-The other controls in the UI are MUCH faster.

Please test the hell out of it. If it looks good, I'll add it to my plugin pack.

Wut?

Well, if you change either Angle or Distance the calculations are done in a single thread... so it is slow. If you change the other controls the calculations are done in multiple threads... so the calculations are faster. This is a change from the first version where ALL the calculations were done in a single thread.

Link to comment
Share on other sites

Hello. I really like the faster speeds for some of the settings, the plugin is looking better and better. :)

Here is some more feedback though...

Most of the plugin is looking pretty good, I love all of the available settings and features it has to offer. But I do want to point out that the option to keep only the shadow (uncheck "keep original image") gives the shadow rough outer edges.

InnerShadowTestShadowOnly_zpsga5v80hm.pn

Link to comment
Share on other sites

I do want to point out that the option to keep only the shadow (uncheck "keep original image") gives the shadow rough outer edges.

If only there was a plugin to... uh... feather those edges.

:D

 

I'm afraid that this is just something you'll have to learn to live with.  There is no easy way to calculate a proper edge alpha.

Link to comment
Share on other sites

Since the shadow has a blur to it, when you apply feather it takes away the hard outer edge, which then lessens the “inner shadow" appearance and gives the shadow an overall blurry appearance. Other edge smoothing plugins don't seem to smooth just the hard outer edge either, they smooth everything, which effects the "inner shadow" area.

Here is a sample image of trying to use feather.

InnerShadowTestFeather_zpslvw2axan.jpg

 

I really like the feature to have the shadow only, as it allows me to place objects under the shadow layer and make cool layering effects.

Edited by Cc4FuzzyHuggles
Link to comment
Share on other sites

I see it only works on the latest version of pdn.   ;)

I compiled it against 4.0. So it should work on 4.0+.

 

It contains certain features that are only supported on 4.0, so that's the reason.  I posted the source, so you can try to change it to work on 3.5.11 if you like.

  • Like 1
Link to comment
Share on other sites

I'm afraid that this is just something you'll have to learn to live with.  There is no easy way to calculate a proper edge alpha.

It's a shame to hear that. I was hoping for an all in one plugin. I thought some kind of anti-aliasing might be possible or some kind of one-sided AA's assistant thing. I wasn't thinking to add more settings, just a behind the scenes edge smoothing processes.

 

But alright, I guess the Alpha Mask plugin is still going to be my best friend for cutting off ugly edges.

Edited by Cc4FuzzyHuggles
Link to comment
Share on other sites

I do want to point out that the option to keep only the shadow (uncheck "keep original image") gives the shadow rough outer edges.

 

There is no easy way to calculate a proper edge alpha.

Turns out that was a lie. I have a solution to that problem.  I'll post a fix tonight.

I see it only works on the latest version of pdn.

I'll post a version for 3.5.11 tonight.

Link to comment
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.

×
×
  • Create New...