%ffp Category :"RC Filters" Title :"Glamour Blur" Filename :"GlamourBlur.8bf" Description:"Selective unsharp masking contrast reduction for portraits." Copyright :"© 2018 by Russell Cottrell; released under the Gnu General Public License." Author :"Russell Cottrell" Version :"1.201" URL :"http://www.russellcottrell.com/RCFilters" About :"Thank you for using !C!\n!t v!V by !A.\n!D\nPlease visit russellcottrell.com/RCFilters." // 6/5/2018 /* Acknowledgements: Recursive gaussian demo code implemented by Tom Fiddaman from the article: Title: Recursive implementation of the Gaussian filter Authors: I.T. Young, L.J. van Vliet in: Signal Processing, vol. 44, no. 2, 1995, 139-151. Interpolation by Harald Heim The Plugin Site, http://thepluginsite.com Copyright 2002 by Harald Heim */ Dialog: Size=(590,406), MaxBox, MinBox, Theme=on, Initevent SupportedModes: RGBMode, RGB48Mode, GrayScaleMode, Gray16Mode Embed: Bitmap="logo.bmp" ctl(0): Standard, "Radius", Range=(0,100), Divisor=100, pos=(400,20), Val=30 ctl(1): StaticText, "0px", pos=(525,20), size=(60,10) ctl(2): Standard, "Amount", Range=(0,200), pos=(400,32), Val=50 ctl(10): checkbox(Pushlike), text="Normal", Pos=(494,43), Size=(56,12), val=1, Action=Preview ctl(4): Standard, "Dark/Light Mixer", Range=(-100,100), pos=(425,59), Val=50 ctl(14): Standard, "Radius", pos=(400,92), range=(0,100), Divisor=100, val=10 ctl(15): checkbox(Pushlike), text="Enabled", Pos=(524,89), Size=(34,12), val=1 ctl(16): Standard, "Intensity", pos=(400,104), range=(0,100), Divisor=10, val=20 ctl(17): checkbox(Pushlike), text="Show", Pos=(524,101), Size=(34,12), val=0 ctl(18): Standard, "Level", pos=(400,116), range=(0,100), val=10 ctl(19): GroupBox, "Edge Mask", pos=(355,80), size=(224,50) ctl(70): StaticText, "Blend Ranges:", pos=(365,146), size=(160,10) ctl(71): GroupBox, pos=(363,153), size=(204,28) ctl(72): Trackbar(Noticks,Bottom,Toptip), Pos=(365,158), Size=(200,11), Range=(0,255), Val=0, Color=Black ctl(73): Trackbar(Noticks,Top,Toptip), Pos=(365,168), Size=(200,11), Range=(0,255), Val=0, Color=Black ctl(74): Trackbar(Noticks,Bottom,Toptip), Pos=(365,183), Size=(200,11), Range=(0,255), Val=255, Color=White ctl(75): Trackbar(Noticks,Top,Toptip), Pos=(365,193), Size=(200,11), Range=(0,255), Val=255, Color=White ctl(76): GroupBox, pos=(363,178), size=(204,28) ctl(78): checkbox(Pushlike), text="Enabled", Pos=(526,144), Size=(40,12), val=1 ctl(41): Rect(Modalframe, White), pos=(0,0), size=(292, 149), Anchor=ANCHOR_LEFT|ANCHOR_TOP, Invisible ctl(42): Rect(Gray), pos=(3, 3), size=(286,87), Anchor=ANCHOR_LEFT|ANCHOR_TOP, Invisible // ********** SOURCE ctl(43): StaticText(CENTER), pos=(3,94), size=(286, 8), Anchor=ANCHOR_LEFT|ANCHOR_TOP, Invisible, "Thank you for using RC Filters!" ctl(44): StaticText(CENTER), pos=(3,102), size=(286, 16), Anchor=ANCHOR_LEFT|ANCHOR_TOP, Invisible, "" ctl(45): StaticText(CENTER,NOTIFY), pos=(3,124), size=(286, 10), Anchor=ANCHOR_LEFT|ANCHOR_TOP, FontColor=blue, Invisible, "Please visit russellcottrell.com/RCFilters for more information." ctl(46): StaticText(CENTER,NOTIFY), pos=(3,134), size=(286, 10), Anchor=ANCHOR_LEFT|ANCHOR_TOP, FontColor=blue, Invisible, "Click here to open the plugin folder." ctl[CTL_PREVIEW]: Modify, Size=(333,383) ctl[CTL_PROGRESS]: Modify, Pos=(4,394) ctl[CTL_ZOOM]: Modify, Pos=(91,394) ctl(50): Pushbutton, "100%", Pos=(160,391), Anchor=ANCHOR_LEFT|ANCHOR_BOTTOM, Size=(30,14), Action=Preview ctl(51): Pushbutton, "Fit", Pos=(195,391), Anchor=ANCHOR_LEFT|ANCHOR_BOTTOM, Size=(30,14), Action=Preview // GlamourBlurHelp.htm ctl(53): checkbox(Pushlike), text="About", Pos=(431,389), Size=(34,14), val=0, Anchor=ANCHOR_RIGHT|ANCHOR_BOTTOM ctl(54): Pushbutton, text="Reset", Pos=(474,389), Size=(34,14), val=0, Anchor=ANCHOR_RIGHT|ANCHOR_BOTTOM, Action=Preview ctl[CTL_OK]: Pushbutton, text="Apply", Pos=(517,389), Size=(34,14), Anchor=ANCHOR_RIGHT|ANCHOR_BOTTOM ctl[CTL_CANCEL]: Pushbutton, text="Cancel", Pos=(552,389), Size=(34,14), Anchor=ANCHOR_RIGHT|ANCHOR_BOTTOM OnFilterstart: { switch(imageMode) { case RGBMode: sprintf(str0, "8-bit RGB Color"); break; case RGB48Mode: sprintf(str0, "16-bit RGB Color"); break; case GrayScaleMode: sprintf(str0, "8-bit Grayscale"); break; case Gray16Mode: sprintf(str0, "16-bit Grayscale"); break; } if (e==FME_INIT) { if ((imageMode == RGBMode) || (imageMode == RGB48Mode)) enableCtl(10, 3); else enableCtl(10, 1); } sprintf(str1, formatString("!t (%s, !f)"), str0); setDialogText(str1); setCtlText(44, formatString("!t v!V by !A.\n!D")); isTileable=false; refreshWindow(); return false; } OnCtl(n): { int logoL, logoT; void *TXT_FILE; int bctl0 = 72; // the blend range sliders int bctl1 = 73; int wctl0 = 74; int wctl1 = 75; if ((n==bctl0 || n==bctl1 || n==wctl0 || n==wctl1) && (e==FME_CHANGED)) { switch(n) { case 72: // can't use variables here if (ctl(bctl1) < ctl(bctl0)) setCtlVal(bctl1,ctl(bctl0)); if (ctl(wctl0) < ctl(bctl0)) setCtlVal(wctl0,ctl(bctl0)); if (ctl(wctl1) < ctl(bctl0)) setCtlVal(wctl1,ctl(bctl0)); break; case 73: if (ctl(bctl0) > ctl(bctl1)) setCtlVal(bctl0,ctl(bctl1)); if (ctl(wctl0) < ctl(bctl1)) setCtlVal(wctl0,ctl(bctl1)); if (ctl(wctl1) < ctl(bctl1)) setCtlVal(wctl1,ctl(bctl1)); break; case 74: if (ctl(bctl0) > ctl(wctl0)) setCtlVal(bctl0,ctl(wctl0)); if (ctl(bctl1) > ctl(wctl0)) setCtlVal(bctl1,ctl(wctl0)); if (ctl(wctl1) < ctl(wctl0)) setCtlVal(wctl1,ctl(wctl0)); break; case 75: if (ctl(bctl0) > ctl(wctl1)) setCtlVal(bctl0,ctl(wctl1)); if (ctl(bctl1) > ctl(wctl1)) setCtlVal(bctl1,ctl(wctl1)); if (ctl(wctl0) > ctl(wctl1)) setCtlVal(wctl0,ctl(wctl1)); break; } } if (n==10) { switch(ctl(10)) { case 1: setCtlText(10, "Normal"); break; case 0: setCtlText(10, "Grayscale Only"); } } if (n==15) setCtlText(15, ctl(15) ? "Enabled" : "Off"); else if (n==17) setCtlText(17, ctl(17) ? "Hide" : "Show"); else if (n==78) setCtlText(78, ctl(78) ? "Enabled" : "Off"); if (e==FME_CLICKED) { if (ctl(53)) // About { logoL = (getDialogWidth() - getCtlPos(41, 2))/2; logoT = (getDialogHeight() - getCtlPos(41, 3))/2 - 10; setCtlPos(41, logoL, logoT, -1, -1); setCtlPos(42, logoL+3, logoT+3, -1, -1); setCtlPos(43, logoL+3, logoT+94, -1, -1); setCtlPos(44, logoL+3, logoT+102, -1, -1); setCtlPos(45, logoL+3, logoT+124, -1, -1); setCtlPos(46, logoL+3, logoT+134, -1, -1); for (i=41; i<=46; i++) { enableCtl(i, 3); setCtlOrder(i, 1); } } else // close About { for (i=41; i<=46; i++) enableCtl(i, 0); } if ((n==45) && (ctlEnabled(45)!=0)) shellExec("open", "http://www.russellcottrell.com/RCFilters", NULL, filterInstallDir); if ((n==46) && (ctlEnabled(46)!=0)) shellExec("open", filterInstallDir, NULL, NULL); if (ctl(54) || (n==54 && (e==FME_CUSTOMEVENT))) // Reset { setCtlVal(0,30); setCtlVal(2,50); setCtlVal(4,50); setCtlVal(10,1); setCtlVal(14,10); setCtlVal(15,1); setCtlVal(16,20); setCtlVal(17,0); setCtlVal(18,10); setCtlVal(72,0); setCtlVal(73,0); setCtlVal(74,255); setCtlVal(75,255); setCtlVal(78,1); setCtlVal(53,0); setCtlVal(54,0); triggerEvent(54, FME_CLICKED, 0); if (ctlEnabled(10) != 1) triggerEvent(10, FME_CLICKED, 0); // ***AFTER*** 54 triggerEvent(15, FME_CLICKED, 0); triggerEvent(17, FME_CLICKED, 0); triggerEvent(78, FME_CLICKED, 0); triggerEvent(53, FME_CLICKED, 0); } } if (e==FME_INIT) setZoom(-888); if (e==FME_SIZE) setZoom(-888); if (n==50) setZoom(1); else if (n==51) setZoom(-888); return false; } ForEveryTile: { int C=imageMode (double)minD) radius = (double)minD; //rc = (int)ceil(radius); if (doingProxy) { // save the preview dimensions k0 = X; k1 = Y; x0 = radius; x5 = radiusM; } else if (radius == 0) // can’t blur abort(); if (radius*(double)scaleFactor < 10) snprintf(str2, 3, "%f", radius*(double)scaleFactor); else sprintf(str2, "%d", (int)(radius*(double)scaleFactor)); if (radius < 10) snprintf(str3, 3, "%f", radius); else sprintf(str3, "%d", (int)radius); strcat(str4, str2); strcat(str4, " / "); strcat(str4, str3); strcat(str4, " px"); setCtlText(1, str4); strcpy(str4, ""); sprintf(str1, "Blend Ranges: %d-%d %d-%d", ctl(72), ctl(73), ctl(74), ctl(75)); setCtlText(70, str1); // mode: 0 = grayscale, 1 = RGB “Grayscale Only,” 2 = RGB “Normal.” // Array(99,x,y,0) is a virtual Y src; Array(99,x,y,1) is a reduced copy for the edge mask. // In “Grayscale Only” mode, all changes target the virtual Y channel to minimize runtime calculations, // and the color components of src (Cb and Cr) are added back at the very end. if ((imageMode==GrayScaleMode) || (imageMode==Gray16Mode)) mode = 0; else if (ctl(10)==0) // “Grayscale Only” mode = 1; else mode = 2; zmax = ((mode < 2) ? 0 : 2); allocArray(99, X, Y, 2, 2); for (y=y_start; yx_start; x--) { wn = BB*(double)(pget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); // src wn3=wn2; wn2=wn1; wn1=wn; } // for x for (x=x_start; xx_end_new-2-2*rc; x--) // chill down forward { wn = BB*(double)(pget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); // src t2set(x,y,z, (int)wn); wn3=wn2; wn2=wn1; wn1=wn; } // for x // warmup backward - no write for (x=x_end_new-1-2*rc; x=x_start; x--) // backward pass { wn = BB*(double)(tget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); pset(x,y,z, (int)wn); wn3=wn2; wn2=wn1; wn1=wn; } // for x } // for y // NOW DO COLUMNS for (x=x_start; xy_start; y--) { wn = BB*(double)(pget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); wn3=wn2; wn2=wn1; wn1=wn; } // for y for (y=y_start; yy_end_new-2-2*rc; y--) // chill down forward { wn = BB*(double)(pget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); t2set(x,y,z, (int)wn); wn3=wn2; wn2=wn1; wn1=wn; } // for y // warmup backward - no write for (y=y_end_new-1-2*rc; y=y_start; y--) // backward pass { wn = BB*(double)(tget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); if (!doingProxy) t2set(x,y,z, (int)wn); // enlargement else pset(x,y,z, (int)wn); // no enlargement wn3=wn2; wn2=wn1; wn1=wn; } // for y } // for x } // for z ////////// enlarge ////////// if (!doingProxy) { Xold = Xnew; Yold = Ynew; Xnew = (X*X)/(Xnew); // enlarge to original size Ynew = (Y*Y)/(Ynew); for (y=y_start; y y_end_new-1) yj -= 2*(y_end_new-1-yj); bg = (double)getArray(99,x,yj,1); // bg because we are blurring not sharpening sum += bg*fgetArray(1,1,i,j+rcS); // apply the kernel } fputArray(11,x,y,0, sum); } } for (y=y_start; y x_end_new-1) xi -= 2*(x_end_new-1-xi); j = 0; fg = fgetArray(11,xi,y,0); sum += fg*fgetArray(1,1,i+rcS,j); } fputArray(2,x,y,0, sum); } } // the y kernel for (y=y_start; y y_end_new-1) yj -= 2*(y_end_new-1-yj); bg = (double)getArray(99,x,yj,1); sum += bg*fgetArray(1,2,i,j+rcS); } fputArray(11,x,y,0, sum); } } for (y=y_start; y x_end_new-1) xi -= 2*(x_end_new-1-xi); j = 0; fg = fgetArray(11,xi,y,0); sum += fg*fgetArray(1,2,i+rcS,j); } fputArray(3,x,y,0, sum); } } for (y=y_start; y x2) x2 = sum; } } } // scale the results vMin = x1; vMax = x2; for (y=y_start; yx_start; x--) { wn = BB*fgetArray(12,x,y,z) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); // src wn3=wn2; wn2=wn1; wn1=wn; } // for x for (x=x_start; xx_end_new-2-2*rcM; x--) // chill down forward { wn = BB*fgetArray(12,x,y,z) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); // src t2set(x,y,z, (int)wn); wn3=wn2; wn2=wn1; wn1=wn; } // for x // warmup backward - no write for (x=x_end_new-1-2*rcM; x=x_start; x--) // backward pass { wn = BB*(double)(tget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); fputArray(12,x,y,z, wn); wn3=wn2; wn2=wn1; wn1=wn; } // for x } // for y // NOW DO COLUMNS for (x=x_start; xy_start; y--) { wn = BB*fgetArray(12,x,y,z) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); wn3=wn2; wn2=wn1; wn1=wn; } // for y for (y=y_start; yy_end_new-2-2*rcM; y--) // chill down forward { wn = BB*fgetArray(12,x,y,z) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); t2set(x,y,z, (int)wn); wn3=wn2; wn2=wn1; wn1=wn; } // for y // warmup backward - no write for (y=y_end_new-1-2*rcM; y=y_start; y--) // backward pass { wn = BB*(double)(tget(x,y,z)) + (b1_b0*wn1 + b2_b0*wn2 + b3_b0*wn3); fputArray(12,x,y,z, wn); if (doingProxy) { // save min and max pixel values if (wn < x3) x3 = wn; else if (wn > x4) x4 = wn; } wn3=wn2; wn2=wn1; wn1=wn; } // for y } // for x vMin = x3; vMax = x4; ////////// enlarge ////////// if (!doingProxy) { Xold = Xnew; Yold = Ynew; Xnew = (X*X)/(Xnew); // enlarge to original size Ynew = (Y*Y)/(Ynew); for (y=y_start; y0) ? 2 : 0); z++) // avoid transparency pset(x,y,z, tget(x,y,0)); } } } freeArray(12); if (ctl(15) && !ctl(17)) { // enabled ////////// apply edge mask ////////// for (y=y_start; y0) || (ctl(73)>0) || (ctl(74)<255) || (ctl(75)<255)) ) { for (y=y_start; y wc1)) alpha = 0.0; else if ((bg >= bc0) && (bg < bc1)) alpha = (bg - bc0)/(bc1 - bc0); else if ((bg > wc0) && (bg <= wc1)) alpha = (wc1 - bg)/(wc1 - wc0); for (z=0; z<=zmax; z++) { if (mode==1) bg2 = (double)getArray(99,x,y,0); else bg2 = (double)src(x,y,z); fg2 = (double)pget(x,y,z); fg2 = alpha*fg2 + (1-alpha)*bg2; // blend range pset(x,y,z, (int)fg2); } } } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// ////////// darken/lighten mixer ////////// if (ctl(4) != 0) { for (y=y_start; y 0) && (bg > fg))) { if (mode < 2) bg2 = (double)getArray(99,x,y,0); else bg2 = (double)src(x,y,z); fg2 = (double)pget(x,y,z); fg2 = alpha2*bg2 + (1-alpha2)*fg2; pset(x,y,z, (int)fg2); } } } } } ////////// color ////////// if (mode==1) { for (y=y_start; y