Tanel
-
Posts
205 -
Joined
-
Last visited
-
Days Won
3
Posts posted by Tanel
-
-
Sharpen+ plugin for fine sharpening.
Local contrast enhancement plugin does similar thing but is optimized for large radius / small amount.
-
Pyrochild helped me out with exactly the same problem when I tried to implement feather control into my "Color to Alpha" plugin (link to original discussion).
Feel free to copy below code (visual studio). It's not cleaned up but has some comments; those marked with "???" are by pyrochild.
Note that I have moved blurring into separate if-block which is ignored until feather token is >0. This is for better performance - because with larger images UI of Color to Alpha becomes quite laggy as soon as feather is on.
using System; using System.Collections; using System.Drawing; using PaintDotNet; using PaintDotNet.Effects; using PaintDotNet.PropertySystem; using System.Collections.Generic; namespace ColorToAlpha { [EffectCategory(EffectCategory.Adjustment)] public class EffectPlugin : PaintDotNet.Effects.Effect { public static string StaticName { get { return "Color to Alpha"; } } public static Bitmap StaticImage { get { return new Bitmap(typeof(EffectPlugin), "EffectPluginIcon.png"); } } public static string StaticSubMenuName { get { return null; // Use for no submenu // return "My SubMenu"; // Use for custom submenu } } public EffectPlugin() : base(StaticName, StaticImage, StaticSubMenuName, EffectFlags.Configurable) { blurEffect = new GaussianBlurEffect(); } public override EffectConfigDialog CreateConfigDialog() { return new ColorToAlpha(); } private GaussianBlurEffect blurEffect; PropertyBasedEffectConfigToken blurToken; Surface tempSurface; RenderArgs temp; bool changed; protected override void OnSetRenderInfo(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs) { this.changed = true; EffectPluginConfigToken token = (EffectPluginConfigToken)parameters; PropertyCollection blurProps = blurEffect.CreatePropertyCollection(); blurToken = new PropertyBasedEffectConfigToken(blurProps); blurToken.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, token.Feather); base.OnSetRenderInfo(parameters, dstArgs, srcArgs); } public override unsafe void Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, int startIndex, int length) { EffectPluginConfigToken token = (EffectPluginConfigToken)parameters; int feax = token.Feather; bool inv = token.Invert; int basea = token.BaseAlpha; if (feax > 0) // THIS IS FOR FASTER PERFORMANCE WHILE FEATHER IS NOT USED { PdnRegion selectionRegion = EnvironmentParameters.GetSelection(srcArgs.Bounds); if (changed && tempSurface != null) // ??? I set changed = true in OnSetRenderInfo(), to indicate that the user has changed settings and therefore, invalidated our tempSurface. But if it's null it doesn't matter. { tempSurface.Dispose(); // ??? Clear up our resource usage. We have to be nice to our user's RAM. tempSurface = null; // ??? Then set it to null so the next ifblock gets executed. I could combine this more elegantly, but the code here evolved organically from the old version, and I'm lazy. } if (tempSurface == null) // ??? If we don't have a tempSurface at this point (whether it's because we never made one, or just invalidated it above), we need to make one. { Rectangle[] gorois = selectionRegion.GetRegionScansInt(); // build new surface "tempSurface" tempSurface = new Surface(srcArgs.Size); temp = new RenderArgs(tempSurface); RenderAlpha(parameters, tempSurface, srcArgs, gorois, 0, gorois.Length); changed = false; // ??? Set changed to false since we've made our tempSurface and it's currently valid, until changed gets set to true again. This prevents us from creating a new overlay every time OnRender() is called blurEffect.SetRenderInfo(blurToken, dstArgs, temp); } this.blurEffect.Render(rois, startIndex, length); for (int i = startIndex; i < startIndex + length; ++i) { Rectangle roi = rois[i]; for (int y = roi.Top; y < roi.Bottom; ++y) { ColorBgra* dstPtr = dstArgs.Surface.GetPointAddress(roi.X, roi.Y); ColorBgra NewPixel; ColorBgra OrigPixel; byte a, ro, go, bo, ao, ry, by, gy, ay; // r, g, b, for (int x = roi.Left; x < roi.Right; ++x) { NewPixel = dstArgs.Surface[x, y]; //r = NewPixel.R; //g = NewPixel.G; //b = NewPixel.B; a = NewPixel.R; // WE MADE GRAYSCALE IMAGE FROM ALPHA VALUES BEFORE BLURRING (see bottom of RenderAlpha), NOW WE TURN IT BACK TO ALPHA OrigPixel = srcArgs.Surface[x, y]; ro = OrigPixel.R; go = OrigPixel.G; bo = OrigPixel.B; ao = OrigPixel.A; byte baseab = (byte)basea; if (baseab > 0 && a < baseab) a = baseab; // INVERT if (inv == true) a = (byte)(255 - a); if (selectionRegion.IsVisible(x, y)) { ry = Utility.ClampToByte(ro); gy = Utility.ClampToByte(go); by = Utility.ClampToByte(bo); ay = Utility.ClampToByte(a); NewPixel = ColorBgra.FromBgra(by, gy, ry, ay); *dstPtr = NewPixel; ++dstPtr; } } } } } else // feather token = 0; no need for 3rd surface rendering { int hue1 = token.huefrom; int hue2 = token.hueto; int sat1 = token.satfrom; int sat2 = token.satto; int b1 = token.bnessfrom; int b2 = token.bnessto; int tol = token.Tolerance; bool huebox = token.Huebox; bool satbox = token.Satbox; bool bnessbox = token.Bnessbox; bool ovrride = token.Ovrride; // NEW for (int i = startIndex; i < startIndex + length; ++i) { Rectangle roi = rois[i]; for (int y = roi.Top; y < roi.Bottom; ++y) { ColorBgra* srcPtr = srcArgs.Surface.GetPointAddress(roi.X, roi.Y); ColorBgra* dstPtr = dstArgs.Surface.GetPointAddress(roi.X, roi.Y); ColorBgra CurrentPixel; ColorBgra OrigPixel; byte ro, go, bo, ao, ry, by, gy, ay; // r, g, b, a, float amtx; double ax, huej, huel, satj, satl, brtj, brtl, huej1, huel1, huej0, huel0, satj1, satl1, brtj1, brtl1, fn, ft, fu, fv, ffn, fft, ffu, ffv, fxx; for (int x = roi.Left; x < roi.Right; ++x) { OrigPixel = srcArgs.Surface[x, y]; ro = OrigPixel.R; go = OrigPixel.G; bo = OrigPixel.B; ao = OrigPixel.A; amtx = 1 - basea / 255f; CurrentPixel = dstArgs.Surface[x, y]; // GET HUE: FROM UnaryPixelOps.cs: HsvColor hsvColor = HsvColor.FromColor(OrigPixel.ToColor()); int hue = hsvColor.Hue; int satur = hsvColor.Saturation; int brt = (int)OrigPixel.GetIntensityByte(); if (hue1 > 359) hue1 = 359; if (hue2 > 359) hue2 = 359; double huearea = (hue2 - hue1); if (hue2 < hue1) huearea += 360; double hfea = tol; if (tol > ((360 - huearea) / 2) * 255 / 360) hfea = ((360 - huearea) / 2) * 255 / 360; // adjust hue fading to avoid crossing over total 360 // virtually shift hue into "normal" min-max bounds in case hue_from > hue_to double nhue0 = -(hue2 - hue1); double nhue1 = (((nhue0 + Math.Abs(nhue0)) / 2) / 360); double nhuex = (Math.Ceiling(nhue1)); // return 1 if hue_from > hue_to; else 0 int nhue = (int)((360 - hue1) * nhuex); int huex = hue; huex += nhue; while (huex > 359) { huex -= 360; } double hue22 = 360 - hue1; double hue12 = 360 - hue2; double huez = 360 - hue; // hue limiter *while hue_from > hue_to* huej = (hue2 + nhue - huex + 1 - 0.1); // huej = (hue2 + nhue - huex + 1 - 0.1); huel = (((huej + Math.Abs(huej)) / 2) / huej); // hue_to limiter // hue limiters *while hue_from < hue_to* huej0 = ((hue2 - hue + 1 - 0.1) * (1 - nhuex)) + ((hue22 - huez + 1 - 0.1) * nhuex); huel0 = (((huej0 + Math.Abs(huej0)) / 2) / huej0); // hue_to limiter huej1 = ((hue - hue1 + 1 - 0.1) * (1 - nhuex)) + ((huez - hue12 + 1 - 0.1) * nhuex); huel1 = (((huej1 + Math.Abs(huej1)) / 2) / huej1);// hue_from limiter double huelx = (huel * nhuex + huel0 * huel1 * (1 - nhuex)); // use only one set of limiters from cases hue_from > hue_to OR hue_from < hue_to satj = (sat2 - satur + 1 - 0.1); satl = (((satj + Math.Abs(satj)) / 2) / satj); // saturation_to limiter satj1 = (satur - sat1 + 1 - 0.1); satl1 = (((satj1 + Math.Abs(satj1)) / 2) / satj1); // saturation_from limiter brtj = (b2 - brt + 1 - 0.1); brtl = (((brtj + Math.Abs(brtj)) / 2) / brtj); // brightness_to limiter brtj1 = (brt - b1 + 1 - 0.1); brtl1 = (((brtj1 + Math.Abs(brtj1)) / 2) / brtj1); // brightness_from limiter double ax0 = (huelx * satl * brtl * satl1 * brtl1); double ax1 = ((1 - ax0) * (1 - amtx) + ax0); // set alpha of non-picked pixels to inverse of amount double ax0h = huelx; // double ax0h = (satl * brtl * satl1 * brtl1); double ax1h = ((1 - ax0h) * (1 - amtx) + ax0h); // set alpha of non-picked pixels to inverse of amount double ax0s = (satl * satl1); // double ax0s = (huelx * brtl * brtl1); double ax1s = ((1 - ax0s) * (1 - amtx) + ax0s); // set alpha of non-picked pixels to inverse of amount double ax0b = (brtl * brtl1); // double ax0b = (huelx * satl * satl1); double ax1b = ((1 - ax0b) * (1 - amtx) + ax0b); // set alpha of non-picked pixels to inverse of amount // HUE FADING TO_LIMITER double huej0f = (huej0 * 255 / 360); if (huej0 - 1 + 0.1 + (hfea * 360 / 255) > 359) // XXXX TEST if (huej0 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej0f = ((huej0 - 1 + 0.1 - 360) * 255 / 360); // XXX TEST huej0f = ((huej0 - 1 + 0.1 - 361) * 255 / 360); if (huej0 - 1 + 0.1 + (hfea * 360 / 255) < 0) // XXXX TEST if (huej0 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej0f = ((huej0 - 1 + 0.1 + 360) * 255 / 360); // XXX TEST huej0f = ((huej0 - 1 + 0.1 - 361) * 255 / 360); fn = (((-huej0f + Math.Abs(-huej0f)) / 2) / -huej0f); // * ax1h; ft = (-huej0f / (hfea + 0.01)); // tol -> hfea fu = (1 - ft * amtx); fv = (fu * fn + ax0h * (1 - fn)); //(fu * fn * fo + ax1 * fm + ax1 * (1 - fn)); // // HUE FADING FROM_LIMITER double huej1f = (huej1 * 255 / 360); if (huej1 - 1 + 0.1 + (hfea * 360 / 255) > 359) //XXXX if (huej1 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej1f = ((huej1 - 1 + 0.1 - 360) * 255 / 360); // XXX huej1f = ((huej1 - 1 + 0.1 - 361) * 255 / 360); if (huej1 - 1 + 0.1 + (hfea * 360 / 255) < 0) //XXXX if (huej1 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej1f = ((huej1 - 1 + 0.1 + 360) * 255 / 360); // XXX huej1f = ((huej1 - 1 + 0.1 - 361) * 255 / 360); ffn = (((-huej1f + Math.Abs(-huej1f)) / 2) / -huej1f); // *ax1h; fft = (-huej1f / (hfea + 0.01)); // tol -> hfea ffu = (1 - fft * amtx); ffv = (ffu * ffn + ax0h * (1 - ffn)); //(ffu * ffn * ffo + ax1 * ffm + ax1 * (1 - ffn)); // fxx = (Math.Max(fv, ffv)); // HUE TO ALPHA MULTIPLIER if (huebox == false) fxx = 1; // fxx = ax1h; // // SATURATION FADING TO_LIMITER double satjf = (satj * 255 / 100); // * ax1 ei sobi siia double fns = (((-satjf + Math.Abs(-satjf)) / 2) / -satjf); // * ax1s; double fts = (-satjf / (tol + 0.01)); // tol -> hfea double fus = (1 - fts * amtx); double fvs = (fus * fns + ax0s * (1 - fns)); //double fvs = (fus * fns + ax1 * (1 - fns)); // SATURATION FADING FROM_LIMITER double satj1f = (satj1 * 255 / 100); // * ax1 ei sobi siia double ffns = (((-satj1f + Math.Abs(-satj1f)) / 2) / -satj1f); // * ax1s; double ffts = (-satj1f / (tol + 0.01)); // tol -> hfea double ffus = (1 - ffts * amtx); double ffvs = (ffus * ffns + ax0s * (1 - ffns)); //(ffu * ffn * ffo + ax1 * ffm + ax1 * (1 - ffn)); // double fxxs = (Math.Max(fvs, ffvs)) * fxx; // SATURATION+HUE TO ALPHA MULTIPLIER if (satbox == false) fxxs = fxx; // fxxs = ax1s * fxx; // // BRIGHTNESS FADING TO_LIMITER double fnb = (((-brtj + Math.Abs(-brtj)) / 2) / -brtj); // * ax1b; double ftb = (-brtj / (tol + 0.01)); // tol -> hfea double fub = (1 - ftb * amtx); double fvb = (fub * fnb + ax0b * (1 - fnb)); //(fu * fn * fo + ax1 * fm + ax1 * (1 - fn)); // // BRIGHTNESS FADING FROM_LIMITER double ffnb = (((-brtj1 + Math.Abs(-brtj1)) / 2) / -brtj1); // * ax1b; double fftb = (-brtj1 / (tol + 0.01)); // tol -> hfea double ffub = (1 - fftb * amtx); double ffvb = (ffub * ffnb + ax0b * (1 - ffnb)); //(ffu * ffn * ffo + ax1 * ffm + ax1 * (1 - ffn)); // double fxxb = (Math.Max(fvb, ffvb)) * fxxs; // BRIGHTNESS+HUE+SATURATION TO ALPHA MULTIPLIER if (bnessbox == false) fxxb = fxxs; // fxxb = ax1b * fxxs;// // even out alpha for non-selected areas double fxxx0 = Math.Ceiling(fxxb); double fxxx = fxxb + (1 - amtx) * (1 - fxxx0); if (fxxx < (1 - amtx)) fxxx = (1 - amtx); if (ovrride == true) // NEW ao = 255; ax = (ao * fxxx); // FINAL ALPHA byte baseab = (byte)basea; if (baseab > 0 && ax < baseab) // REVISED 21-01-08 ax = baseab; // invert if (inv == true) ax = (255 - ax); ry = Utility.ClampToByte(ro); gy = Utility.ClampToByte(go); by = Utility.ClampToByte(bo); ay = Utility.ClampToByte(ax); CurrentPixel = ColorBgra.FromBgra(by, gy, ry, ay); *dstPtr = CurrentPixel; ++dstPtr; } } } } } private unsafe void RenderAlpha(EffectConfigToken parameters, Surface surface, RenderArgs srcArgs, Rectangle[] rois, int startIndex, int length) { EffectPluginConfigToken token = (EffectPluginConfigToken)parameters; int hue1 = token.huefrom; int hue2 = token.hueto; int sat1 = token.satfrom; int sat2 = token.satto; int b1 = token.bnessfrom; int b2 = token.bnessto; int tol = token.Tolerance; int basea = token.BaseAlpha; basea += 1; // HEAL THE SHIFT IN RESULTS bool huebox = token.Huebox; bool satbox = token.Satbox; bool bnessbox = token.Bnessbox; bool ovrride = token.Ovrride; for (int i = startIndex; i < startIndex + length; ++i) { Rectangle roi = rois[i]; for (int y = roi.Top; y < roi.Bottom; ++y) { ColorBgra* ptr = surface.GetPointAddressUnchecked(roi.Left, y); ColorBgra OrigPixel; byte ro, go, bo, ao, ry, by, gy, ay; // r, g, b, a, float amtx; double ax, huej, huel, satj, satl, brtj, brtl, huej1, huel1, huej0, huel0, satj1, satl1, brtj1, brtl1, fn, ft, fu, fv, ffn, fft, ffu, ffv, fxx; for (int x = roi.Left; x < roi.Right; ++x) { OrigPixel = srcArgs.Surface[x, y]; ro = OrigPixel.R; go = OrigPixel.G; bo = OrigPixel.B; ao = OrigPixel.A; amtx = 1 - basea / 255f; // amtx = amt / 100f; // GET HUE: FROM UnaryPixelOps.cs: HsvColor hsvColor = HsvColor.FromColor(OrigPixel.ToColor()); int hue = hsvColor.Hue; int satur = hsvColor.Saturation; int brt = (int)OrigPixel.GetIntensityByte(); if (hue1 > 359) hue1 = 359; if (hue2 > 359) hue2 = 359; double huearea = (hue2 - hue1); if (hue2 < hue1) huearea += 360; double hfea = tol; if (tol > ((360 - huearea) / 2) * 255 / 360) hfea = ((360 - huearea) / 2) * 255 / 360; // adjust hue fading to avoid crossing over total 360 // virtually shift hue into "normal" min-max bounds in case hue_from > hue_to double nhue0 = -(hue2 - hue1); double nhue1 = (((nhue0 + Math.Abs(nhue0)) / 2) / 360); double nhuex = (Math.Ceiling(nhue1)); // return 1 if hue_from > hue_to; else 0 int nhue = (int)((360 - hue1) * nhuex); int huex = hue; huex += nhue; while (huex > 359) { huex -= 360; } double hue22 = 360 - hue1; double hue12 = 360 - hue2; double huez = 360 - hue; // hue limiter *while hue_from > hue_to* huej = (hue2 + nhue - huex + 1 - 0.1); // huej = (hue2 + nhue - huex + 1 - 0.1); huel = (((huej + Math.Abs(huej)) / 2) / huej); // hue_to limiter // hue limiters *while hue_from < hue_to* huej0 = ((hue2 - hue + 1 - 0.1) * (1 - nhuex)) + ((hue22 - huez + 1 - 0.1) * nhuex); huel0 = (((huej0 + Math.Abs(huej0)) / 2) / huej0); // hue_to limiter huej1 = ((hue - hue1 + 1 - 0.1) * (1 - nhuex)) + ((huez - hue12 + 1 - 0.1) * nhuex); huel1 = (((huej1 + Math.Abs(huej1)) / 2) / huej1);// hue_from limiter double huelx = (huel * nhuex + huel0 * huel1 * (1 - nhuex)); // use only one set of limiters from cases hue_from > hue_to OR hue_from < hue_to satj = (sat2 - satur + 1 - 0.1); satl = (((satj + Math.Abs(satj)) / 2) / satj); // saturation_to limiter satj1 = (satur - sat1 + 1 - 0.1); satl1 = (((satj1 + Math.Abs(satj1)) / 2) / satj1); // saturation_from limiter brtj = (b2 - brt + 1 - 0.1); brtl = (((brtj + Math.Abs(brtj)) / 2) / brtj); // brightness_to limiter brtj1 = (brt - b1 + 1 - 0.1); brtl1 = (((brtj1 + Math.Abs(brtj1)) / 2) / brtj1); // brightness_from limiter double ax0 = (huelx * satl * brtl * satl1 * brtl1); double ax1 = ((1 - ax0) * (1 - amtx) + ax0); // set alpha of non-picked pixels to inverse of amount double ax0h = huelx; // double ax0h = (satl * brtl * satl1 * brtl1); double ax1h = ((1 - ax0h) * (1 - amtx) + ax0h); // set alpha of non-picked pixels to inverse of amount double ax0s = (satl * satl1); // double ax0s = (huelx * brtl * brtl1); double ax1s = ((1 - ax0s) * (1 - amtx) + ax0s); // set alpha of non-picked pixels to inverse of amount double ax0b = (brtl * brtl1); // double ax0b = (huelx * satl * satl1); double ax1b = ((1 - ax0b) * (1 - amtx) + ax0b); // set alpha of non-picked pixels to inverse of amount // HUE FADING TO_LIMITER double huej0f = (huej0 * 255 / 360); if (huej0 - 1 + 0.1 + (hfea * 360 / 255) > 359) // XXXX TEST if (huej0 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej0f = ((huej0 - 1 + 0.1 - 360) * 255 / 360); // XXX TEST huej0f = ((huej0 - 1 + 0.1 - 361) * 255 / 360); if (huej0 - 1 + 0.1 + (hfea * 360 / 255) < 0) // XXXX TEST if (huej0 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej0f = ((huej0 - 1 + 0.1 + 360) * 255 / 360); // XXX TEST huej0f = ((huej0 - 1 + 0.1 - 361) * 255 / 360); fn = (((-huej0f + Math.Abs(-huej0f)) / 2) / -huej0f); // * ax1h; ft = (-huej0f / (hfea + 0.01)); // tol -> hfea fu = (1 - ft * amtx); fv = (fu * fn + ax0h * (1 - fn)); //(fu * fn * fo + ax1 * fm + ax1 * (1 - fn)); // // HUE FADING FROM_LIMITER double huej1f = (huej1 * 255 / 360); if (huej1 - 1 + 0.1 + (hfea * 360 / 255) > 359) //XXXX if (huej1 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej1f = ((huej1 - 1 + 0.1 - 360) * 255 / 360); // XXX huej1f = ((huej1 - 1 + 0.1 - 361) * 255 / 360); if (huej1 - 1 + 0.1 + (hfea * 360 / 255) < 0) //XXXX if (huej1 - 1 + 0.1 + (hfea * 360 / 255) > 360) huej1f = ((huej1 - 1 + 0.1 + 360) * 255 / 360); // XXX huej1f = ((huej1 - 1 + 0.1 - 361) * 255 / 360); ffn = (((-huej1f + Math.Abs(-huej1f)) / 2) / -huej1f); // *ax1h; fft = (-huej1f / (hfea + 0.01)); // tol -> hfea ffu = (1 - fft * amtx); ffv = (ffu * ffn + ax0h * (1 - ffn)); //(ffu * ffn * ffo + ax1 * ffm + ax1 * (1 - ffn)); // fxx = (Math.Max(fv, ffv)); // HUE TO ALPHA MULTIPLIER if (huebox == false) fxx = 1; // fxx = ax1h; // // SATURATION FADING TO_LIMITER double satjf = (satj * 255 / 100); // * ax1 ei sobi siia double fns = (((-satjf + Math.Abs(-satjf)) / 2) / -satjf); // * ax1s; double fts = (-satjf / (tol + 0.01)); // tol -> hfea double fus = (1 - fts * amtx); double fvs = (fus * fns + ax0s * (1 - fns)); //double fvs = (fus * fns + ax1 * (1 - fns)); // SATURATION FADING FROM_LIMITER double satj1f = (satj1 * 255 / 100); // * ax1 ei sobi siia double ffns = (((-satj1f + Math.Abs(-satj1f)) / 2) / -satj1f); // * ax1s; double ffts = (-satj1f / (tol + 0.01)); // tol -> hfea double ffus = (1 - ffts * amtx); double ffvs = (ffus * ffns + ax0s * (1 - ffns)); //(ffu * ffn * ffo + ax1 * ffm + ax1 * (1 - ffn)); // double fxxs = (Math.Max(fvs, ffvs)) * fxx; // SATURATION+HUE TO ALPHA MULTIPLIER if (satbox == false) fxxs = fxx; // fxxs = ax1s * fxx; // // BRIGHTNESS FADING TO_LIMITER double fnb = (((-brtj + Math.Abs(-brtj)) / 2) / -brtj); // * ax1b; double ftb = (-brtj / (tol + 0.01)); // tol -> hfea double fub = (1 - ftb * amtx); double fvb = (fub * fnb + ax0b * (1 - fnb)); //(fu * fn * fo + ax1 * fm + ax1 * (1 - fn)); // // BRIGHTNESS FADING FROM_LIMITER double ffnb = (((-brtj1 + Math.Abs(-brtj1)) / 2) / -brtj1); // * ax1b; double fftb = (-brtj1 / (tol + 0.01)); // tol -> hfea double ffub = (1 - fftb * amtx); double ffvb = (ffub * ffnb + ax0b * (1 - ffnb)); //(ffu * ffn * ffo + ax1 * ffm + ax1 * (1 - ffn)); // double fxxb = (Math.Max(fvb, ffvb)) * fxxs; // BRIGHTNESS+HUE+SATURATION TO ALPHA MULTIPLIER if (bnessbox == false) fxxb = fxxs; // fxxb = ax1b * fxxs;// // even out alpha for non-selected areas double fxxx0 = Math.Ceiling(fxxb); double fxxx = fxxb + (1 - amtx) * (1 - fxxx0); if (fxxx < (1 - amtx)) fxxx = (1 - amtx); if (ovrride == true) // NEW ao = 255; ax = (ao * fxxx); // FINAL ALPHA // TRICK TO BYPASS GAUSSIAN BLUR MISBEHAVIOUR ON SEMITRANSPARENT PIXELS (BUG IN PDN 3.22) // TYPE OF BUG: ALPHA (and Value) OF SEMITRANSPARENT PIXELS IS REDUCED // WE MAKE GRAYSCALE IMAGE WITH NO TRANSPARENCY, BASED ON ALPHA VALUES // THEN WE BLUR THIS (CALLED FROM MAIN RENDER) WITHOUT MISTAKES AND TURN RESULT BACK TO ALPHA VALUES ry = Utility.ClampToByte(ax); gy = Utility.ClampToByte(ax); by = Utility.ClampToByte(ax); ay = Utility.ClampToByte(255); OrigPixel = ColorBgra.FromBgra(by, gy, ry, ay); *ptr = OrigPixel; ++ptr; } } } } } }
-
There is very simple one-step solution, see my post with screenshot here: viewtopic.php?f=12&t=22749#p143464
-
-
For plugin developers being interested, here is the C# class for RGB to HSV transformations, operating with floats. It returns near perfect accuracy. I used it in my Color Mixer plugin.
(Somehow I couldn't get ennixo's solution work as intended)
RGBtoHSV.zipIf using it in your project, call the transformation as follows:
RGBtoHSV.GetHSV(R, G, B, out H, out S, out V);
or
RGBtoHSV.GetRGB(H, S, V, out R, out G, out ;
All RGB and HSV values to be (float).
-
Well, none really. They are completely different purpose tools (color manipulation vs brightness defects recovery).What overlapping effect, if any, is there between this plugin and the shadow/highlight plugin?
I would like to study that gimp plugin. Can you point me some details?This is a great plugin! Very much like one I use to use in GIMP... only better!
Which control? Saturation range is 0-200 same as PdN original. Does anybody really need more?Would it be possible to increase the control range from 0-200 to 0-255? -
If you mean fading the edges to transparent then see this: viewtopic.php?f=12&t=5513#p57490
(just note that "Gaussian Blur Channel" plugin is now called GaussianBlur+, available in BoltBait's plugin pack).
But if you want to just blur the edges then you can use Gradient Blur plugin:
-
Thanks for support guys!
Just note that it still needs few improvements.
Beware that in *some* circumstances, especially when used on noisy photos, the brightness and contrast adjustments *may* bring up some nasty grain patterns at otherwise smooth looking areas (for example clouds). It's not a bug but I try to find some workaround later.
Saturation and hue controls are still ok to use (those were the main purpose of this plugin anyway).
-
This plugin is designed for fine color adjustments. It appears under Adjustments menu.
Download as part of my plugin pack.
Menu location: Adjustments.
Updated to version 1.2 (2008-12-24)!
Changes: few code optimizations.
Updated to version 1.1 (2008-08-31)!
- Compatibility fix for paint.net 3.36
You can adjust saturation, brightness, contrast and hue for each color (hue) range separately or all together.
Amount of adjustment between the ranges is changed gradually, so there is no sharp cut-off, for example between red and yellow.
Due to high precision calculations in the code, this plugin renders saturation and hue better than PdN's original Hue/Saturation effect. (the issue has been discussed here)
Example:
this pic asks for some saturation, but regular saturation adjustment is likely to make sky over-saturated. That's where Color Mixer comes handy.
Settings used:
Saturation: base 134, cyan 83, blue 74
Brightness: cyan -13, blue -13
Contrast: yellow -13, green -28
I may want to improve the UI soon, so far all your requests are welcome.
I have thought about placing all the hue nubs into a single continuous hue-gradient bar.
Save/Load settings option is also on my wishlist (wish I knew how to do it :wink: ).
- 1
-
Thanks ennixo for interesting solution,
is it ok if I use your conversion algorithm in my plugin?
I'm curious to see how it works.
-
Simon, I guess you are following Tom Jackson's "How to start a fire" tutorial.
In this case I suggest you to use linear transformation instead of curves. That is just few lines of code, easy to handle and basically same result.
Here is a codelab code that imitates linear "curve" with middle control points located as:
Red: 0, 0
Green: 150, 0
Blue: 215, 0
// Author: Tanel Rüütli int Amount1=0; //[0,255]Red Control Point int Amount2=150; //[0,255]Green Control Point int Amount3=215; //[0,255]Blue Control Point void Render(Surface dst, Surface src, Rectangle rect) { Rectangle selection = this.EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt(); ColorBgra CurrentPixel; for(int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; // initial rgb values that you have after clouds effect byte r = (byte)CurrentPixel.R; byte g = (byte)CurrentPixel.G; byte b = (byte)CurrentPixel.B; byte a = (byte)CurrentPixel.A; // Return multipliers from our "Control Point" values double rx = 255 / (255 - Amount1 + 0.01); // there is + 0.01 to avoid divide-by-zero exception when Amount is 255 double gx = 255 / (255 - Amount2 + 0.01); // there is + 0.01 to avoid divide-by-zero exception when Amount is 255 double bx = 255 / (255 - Amount3 + 0.01); // there is + 0.01 to avoid divide-by-zero exception when Amount is 255 // Calculate new rgb values double r1 = 255 - rx * (double)(255 - r); double g1 = 255 - gx * (double)(255 - g); double b1 = 255 - bx * (double)(255 - ; // Final RGBA CurrentPixel.R = Utility.ClampToByte(r1); CurrentPixel.G = Utility.ClampToByte(g1); CurrentPixel.B = Utility.ClampToByte(b1); CurrentPixel.A = a; dst[x,y] = CurrentPixel; } } }
You can test it on the gray fire image from Tom's tutorial.
Effectively my code does the same as applying following curves setup (I used Curves+ for this example, because it allows "straight curves"):
If you tie the above Amount1, Amount2 and Amount3 with your plugin controls, or even better, a color wheel control (through opposite RGB values)* then user can easily change color of fire.
* Edit:
by "opposite RGB values" I mean Amount1=255-ColorWheel.R; Amount2=255-ColorWheel.G; Amount3=255-ColorWheel.B;
this way the color of fire turns to same range as the selection in ColorWheel.
-
Rick,
Note that the dotPDN download site still offers Paint.NET v3.22 and Paint.NET v3.30 Release Candidate 1.
However, the new version is downloadable when editing url manually to http://www.dotpdn.com/files/Paint.NET.3.30.zip
-
This is exactly the key question as I imagine... And I don't know the answer. (I have only few skills to make some rgb hacks)
If clipboard is accessible then plugin should be doable.
It would still be an effect by design - taking src values, mixing it with clipboard and putting the result to dst. :idea: :?:
-
Hi,
If you have used MS Office programs then you know there is additional "paste special" function where you can specify which property of clipboard object will be pasted over destination (for example there is options text only, format, formulas, etc).
I think it would be cool to have similar thing is PdN: you could choose between RGB, Alpha, Hue, Saturation, Value...
For example if I chose Paste Special > Alpha then resulting image would keep it's original color values but apply Alpha from copied image.
What do you think? Could someone make such plugin?
-
Try Gaussian Blur+ instead (in "Alpha channel only" mode).
-
Check out the CloudsEffect code from PdN source (CloudsEffect.cs):
UserBlendOp blendOp = token.BlendOp;
...
blendOp.Apply(...);
-
ennixo,
I'm sure you're correct, but that's far beyond my understanding of code...
Hope that Rick will pick up something here. So far he keeps ignoring my bug-reports and suggestions. :?
-
Hi,
while building and testing a new plugin, I discovered that Hue/Saturation effect in PdN renders somewhat jagged results, especially noticable on smooth gradients.
I know that this is caused by RGB>HSV>RGB conversion which is not 100% accurate.
I studied the PdN 3.22 source and came to conclusion that accuracy falls mostly during color values being thrown between several operations (UnaryPixelOps, RgbColor, HsvColor, ColorBgra). Current effect system passes color values between color operations by (int) or (byte). Therefore precision is rounded down repeatedly, resulting with coarse results.
I thought that this can be improved. "Bad" rounding can be avoided with passing color values by (float).
To test my theory, I collected the pieces of PdN 3.22 source and put together Codelab code to imitate the color operations. I replaced the (int) or (byte) conversions with (float) where possible. Where converting to (byte) was inevitable, I used round() function before conversion. (Sidenote: I haven't analysed the impact of each change separately, so some of those may be useless.)
Results are very good. Gradients are rendered smoothly even after running the effect several times.
You can download the codelab source with few comments in it (.cs file) and improved effect (.dll file) here:
-- attachment removed 2008-12-24 --
download the "Hue / Saturation+" as part of my plugin pack.
To test it and compare with original effect I recommend this test image with color, saturation and brightness gradients.
Here are some examples
This is a piece of the linked testimage. See how gradients have got tiny steps in the middle pic. Even gray one which should not be affected by saturation adjustment. The right pic is still nicely smooth.
Now a "real life subject": piece of sky from a photo. Sky is always smooth gradient in terms of color.
I added contrast to exaggerate the result. See how middle pic became blotchy in some areas, while the right pic still holds up the gradient quite well.
One more issue which I *think* is wrong with original Hue/Saturation effect: order of processing.
Currently the adjustments order is Saturation>Hue>Lightness.
I think this is wrong because in case of extremely saturated colors (R, G or B channel at 255) the Saturation adjustment itself will change hue *before* Hue adjustment takes over. Some of you may think what's the difference, but there *is* difference because Saturation adjustment handles different colors differently. Correct adjustments order would be Hue>Saturation>Lightness, so that Saturation adjustment reads the intensity() value from Hue-adjusted color.
For example, take the above linked test image and open original Hue/Saturation effect. play with hue slider - color wheels "roll" smoothly. Now dial in saturation 150 and play with hue slider again. No more smooth rolling - color areas get stretched and compressed while you change hue.
Now try the same with "Hue/Saturation IMPROVED" effect where I have changed the order. See the difference?
I hope the improvements can be included to PdN somehow.
Please, your comments!
-
Yeah, I always thought that the tooltips indicate features coming soon... until I peeked the source.Goodness, has this always been like this?
Hey, we never had chance to try! :?Looking for features to cut? Apparently, no one's ever used that one.Color chooser is very useful for input and gamma points, especially if the color can be picked from original image, or from preview box. I hope eyedropper-style behaviour could be iplemented here also.
-
Fixed for the next update.
Could you also fix swatch_DoubleClick behaviour in Levels dialog:
for example the tooltip on first color swatch says "Input White Point (double click to choose)" - but actually double clicking has no effect on any color swatches.
-
Do you know if anyone has a plugin that will creat a soft white border around a picture (like a vignette effect only white) I will be shooting several weddings this year and some of the clients want this soft white border effect on several shots. It can be done with the filters I have but it's better done afterwards because the filter effect is part of the image that can't be changed.
Check out this tutorial: viewtopic.php?f=12&t=5513#p57490
Maybe Jesse get some ideas for new plugin there too.
-
Plugins can only do effects, adjustments, and filetypes; they can't do tools.
It could be possible as a variation of the new custom brushes plugin.
But I'm not saying it's easy, of course.
-
Rick, would you fix the 2 bugs I have reported earlier?
-
This brush makes a bubble.
So you can create bubble bath like this:
Note: Default bubble is gray; to apply color shade use Color Tint or Color Balance plugin (or Levels) after bubbling.
Paint.NET is getting noticed!
in Paint.NET Discussion and Questions
Posted
There's a large headline on download.com frontpage today.