"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "f.repair.cc" between
fotoxx-22.41.tar.gz and fotoxx-22.50.tar.gz

About: fotoxx is a program for photo editing and collection management.

f.repair.cc  (fotoxx-22.41):f.repair.cc  (fotoxx-22.50)
skipping to change at line 24 skipping to change at line 24
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details. See the GNU General Public License for more details.
******************************************************************************** * ******************************************************************************** *
Fotoxx image edit - Repair menu functions Fotoxx image edit - Repair menu functions
m_sharpen sharpen an image m_sharpen sharpen an image
m_fix_motionblur fix motion blur using Richardson-Lucy algorithm
m_blur blur an image m_blur blur an image
m_blur_background blur background (called via m_blur())
m_blur_motionblur add motion blur to an image m_blur_normal mix pixels with immediate neighbors
m_blur_radial mix pixels in radial lines from chosen center
m_blur_directed blur locally in direction of mouse drag
m_blur_graduated blur relative to pixel contrast
m_blur_paint paint normal blur locally with mouse drags
m_blur_background blur relative to distance from foreground
m_blur_motion add motion blur to selected areas
m_fix_motionblur fix motion blur using Richardson-Lucy algorithm
m_denoise remove noise from an image m_denoise remove noise from an image
m_defog add or remove fog/haze in an image or selected area m_defog add or remove fog/haze in an image or selected area
m_redeyes remove red-eyes from flash photos m_redeyes remove red-eyes from flash photos
m_smart_erase replace pixels inside selected areas with background m_smart_erase replace pixels inside selected areas with background
m_remove_halo remove halo effect caused by sharpen or other edits m_remove_halo remove halo effect caused by sharpen or other edits
m_adjust_RGB adjust brightness/color using RGB or CMY colors m_adjust_RGB adjust brightness/color using RGB or CMY colors
m_adjust_HSL adjust color using HSL model m_adjust_HSL adjust color using HSL model
m_color_profile convert from one color profile to another m_color_profile convert from one color profile to another
m_vignette highlight selected image region
m_remove_dust remove dust specs from an image m_remove_dust remove dust specs from an image
m_chromatic1 fix lateral CA (color bands increasing with radius) m_chromatic1 fix lateral CA (color bands increasing with radius)
m_chromatic2 fix axial CA (color bands on dark/bright edges) m_chromatic2 fix axial CA (color bands on dark/bright edges)
******************************************************************************** */ ******************************************************************************** */
#define EX extern // enable extern declarations #define EX extern // enable extern declarations
#include "fotoxx.h" // (variables in fotoxx.h are refs) #include "fotoxx.h" // (variables in fotoxx.h are refs)
/******************************************************************************* */ /******************************************************************************* */
skipping to change at line 70 skipping to change at line 76
int brhood_radius; int brhood_radius;
float brhood_kernel[200][200]; // up to radius = 99 float brhood_kernel[200][200]; // up to radius = 99
char brhood_method; // g = gaussian, f = flat distribution char brhood_method; // g = gaussian, f = flat distribution
float *brhood_brightness = 0; // neighborhood brightness per pixel float *brhood_brightness = 0; // neighborhood brightness per pixel
int BRH_radius = 0; // radius base for above calculation int BRH_radius = 0; // radius base for above calculation
VOL int sharp_cancel, brhood_cancel; // avoid GCC optimizing code away VOL int sharp_cancel, brhood_cancel; // avoid GCC optimizing code away
editfunc EFsharp; editfunc EFsharp;
char edit_hist[100] = ""; char edit_hist[200];
} }
// menu function // menu function
void m_sharpen(GtkWidget *, cchar *menu) void m_sharpen(GtkWidget *, cchar *menu)
{ {
using namespace sharpen_names; using namespace sharpen_names;
int sharp_dialog_event(zdialog *zd, cchar *event); int sharp_dialog_event(zdialog *zd, cchar *event);
void * sharp_thread(void *); void * sharp_thread(void *);
skipping to change at line 256 skipping to change at line 262
if (zd->zstat == 2) { // [apply] if (zd->zstat == 2) { // [apply]
zd->zstat = 0; zd->zstat = 0;
edit_reset(); edit_reset();
if (*sharp_function) thread_signal(); // start thread function if (*sharp_function) thread_signal(); // start thread function
else zmessageACK(Mwin,"no slection"); // no choice made else zmessageACK(Mwin,"no slection"); // no choice made
return 1; return 1;
} }
if (zd->zstat == 3) { // [OK] if (zd->zstat == 3) { // [OK]
strncpy0(EFsharp.edit_hist,edit_hist,100); // log exif hist data edit_addhist(edit_hist); // record edit history 22.50
edit_done(0); // done edit_done(0); // done
if (brhood_brightness) zfree(brhood_brightness); if (brhood_brightness) zfree(brhood_brightness);
brhood_brightness = 0; brhood_brightness = 0;
return 1; return 1;
} }
sharp_cancel = 1; // [cancel] or [x] sharp_cancel = 1; // [cancel] or [x]
edit_cancel(0); // discard edit edit_cancel(0); // discard edit
if (brhood_brightness) zfree(brhood_brightness); if (brhood_brightness) zfree(brhood_brightness);
brhood_brightness = 0; brhood_brightness = 0;
skipping to change at line 364 skipping to change at line 370
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,0); // initz. pixel loop get_edit_pixels_init(NWT,0); // initz. pixel loop
do_wthreads(sharp_UM_wthread,NWT); // worker threads do_wthreads(sharp_UM_wthread,NWT); // worker threads
progress_reset(0); progress_reset(0);
snprintf(edit_hist,100,"unsharp mask %d %d",UM_amount,UM_thresh); // record edit hist snprintf(edit_hist,200,"unsharp mask %d %d",UM_amount,UM_thresh); // record edit hist
return 1; return 1;
} }
void * sharp_UM_wthread(void *arg) // worker thread function void * sharp_UM_wthread(void *arg) // worker thread function
{ {
using namespace sharpen_names; using namespace sharpen_names;
int index = *((int *) arg); int index = *((int *) arg);
int px, py, ii, Fend; int px, py, ii, Fend;
float amount, thresh, bright; float amount, thresh, bright;
skipping to change at line 473 skipping to change at line 479
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,1); // initz. pixel loop get_edit_pixels_init(NWT,1); // initz. pixel loop
do_wthreads(sharp_GR_wthread,NWT); // worker threads do_wthreads(sharp_GR_wthread,NWT); // worker threads
progress_reset(0); progress_reset(0);
snprintf(edit_hist,100,"gradient %d %d",GR_amount,GR_thresh); // record edit hist snprintf(edit_hist,200,"gradient %d %d",GR_amount,GR_thresh); // record edit hist
return 1; return 1;
} }
// callable sharp_GR_callable() used by rotate function // callable sharp_GR_callable() used by rotate function
// returns E3 = sharpened E3 // returns E3 = sharpened E3
void sharp_GR_callable(int amount, int thresh) void sharp_GR_callable(int amount, int thresh)
{ {
using namespace sharpen_names; using namespace sharpen_names;
skipping to change at line 614 skipping to change at line 620
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,KH_radius); // initz. pixel loop get_edit_pixels_init(NWT,KH_radius); // initz. pixel loop
do_wthreads(sharp_KH_wthread,NWT); // worker threads do_wthreads(sharp_KH_wthread,NWT); // worker threads
progress_reset(0); progress_reset(0);
snprintf(edit_hist,100,"kuwahara %d",KH_radius); // record edit hist snprintf(edit_hist,200,"kuwahara %d",KH_radius); // record edit hist
return 1; return 1;
} }
void * sharp_KH_wthread(void *arg) // worker thread function void * sharp_KH_wthread(void *arg) // worker thread function
{ {
using namespace sharpen_names; using namespace sharpen_names;
float *pix1, *pix3; float *pix1, *pix3;
int px, py, qx, qy, rx, ry; int px, py, qx, qy, rx, ry;
int rad, N, Fend; int rad, N, Fend;
skipping to change at line 759 skipping to change at line 765
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,MD_radius); // initz. pixel loop get_edit_pixels_init(NWT,MD_radius); // initz. pixel loop
do_wthreads(sharp_MD_wthread,NWT); do_wthreads(sharp_MD_wthread,NWT);
progress_reset(0); progress_reset(0);
zfree(MD_britemap); zfree(MD_britemap);
snprintf(edit_hist,100,"median diff %d %d %d",MD_radius,MD_dark,MD_light); // record edit hist snprintf(edit_hist,200,"median diff %d %d %d",MD_radius,MD_dark,MD_light); // record edit hist
return 1; return 1;
} }
void * sharp_MD_wthread(void *arg) // worker thread function void * sharp_MD_wthread(void *arg) // worker thread function
{ {
using namespace sharpen_names; using namespace sharpen_names;
int index = *((int *) arg); int index = *((int *) arg);
int rad, dark, light, *britemap; int rad, dark, light, *britemap;
int ii, px, py, Fend; int ii, px, py, Fend;
skipping to change at line 885 skipping to change at line 891
// Sharpen image using the Richardson-Lucy deconvolution algorithm // Sharpen image using the Richardson-Lucy deconvolution algorithm
// (best for camera out-of-focus area - point source blur disc is uniform) // (best for camera out-of-focus area - point source blur disc is uniform)
int sharp_RL_thread() int sharp_RL_thread()
{ {
using namespace sharpen_names; using namespace sharpen_names;
void * sharp_RL_wthread(void *arg); void * sharp_RL_wthread(void *arg);
do_wthreads(sharp_RL_wthread,3); // 3 threads for 3 RGB colors do_wthreads(sharp_RL_wthread,3); // 3 threads for 3 RGB colors
snprintf(edit_hist,100,"Richardson-Lucy %.1f %d",RL_radius,RL_iters); // record edit hist snprintf(edit_hist,200,"Richardson-Lucy %.1f %d",RL_radius,RL_iters); // record edit hist
return 1; return 1;
} }
void * sharp_RL_wthread(void *arg) // worker thread function void * sharp_RL_wthread(void *arg) // worker thread function
{ {
using namespace sharpen_names; using namespace sharpen_names;
void RLdecon(PXM *pxm, int rgb, float frad, int iter); void RLdecon(PXM *pxm, int rgb, float frad, int iter);
skipping to change at line 1265 skipping to change at line 1271
progress_add(index,1); // track progress progress_add(index,1); // track progress
if (sharp_cancel) break; if (sharp_cancel) break;
} }
} }
return 0; return 0;
} }
/******************************************************************************* */ /******************************************************************************* */
// remove blur from camera motion - Richardson-Lucy method // blur image steering function - choose from multiple blur functions
namespace fix_motionblur_RL_names
{
editfunc EFfixmotionblur;
int Eww, Ehh;
// image dimensions
int span;
// blur span, pixels
int angle;
// blur angle, 0-180 deg.
int iter;
// algorithm iterations
int supring;
// suppress ringing
PXM *E2pxm;
}
// menu function
void m_fix_motionblur(GtkWidget *, const char *menu) void m_blur(GtkWidget *, cchar *menu)
{ {
using namespace fix_motionblur_RL_names; int blur_dialog_event(zdialog *zd, cchar *event);
void fix_motionblur_RL_mousefunc();
int fix_motionblur_RL_dialog_event(zdialog* zd, const char *event);
void * fix_motionblur_RL_thread(void *);
cchar *hintmess = "Shift + drag mouse across image \n"
" to indicate blur direction";
Plog(1,"m_fix_motionblur \n"); zdialog *zd;
EFfixmotionblur.menufunc = m_fix_motionblur; F1_help_topic = "blur";
EFfixmotionblur.menuname = "Fix Motion Blur";
EFfixmotionblur.Farea = 2;
// select area usable
EFfixmotionblur.threadfunc = fix_motionblur_RL_thread;
// thread function
EFfixmotionblur.mousefunc = fix_motionblur_RL_mousefunc;
// mouse function
if (! edit_setup(EFfixmotionblur)) return;
// setup edit
Eww = E3pxm->ww; Plog(1,"m_blur \n");
// image dimensions
Ehh = E3pxm->hh;
/*** /***
___________________________________ ____________________________________________________________________
| Fix Motion Blur | | Blur Image |
| | | |
| Shift + drag mouse across image | | [_] Normal Blur - mix pixels with all neighbor pixels |
| to indicate blur direction | | [_] Radial Blur - mix pixels in radial lines from chosen center |
| | | [_] Directed Blur - blur locally in direction of mouse drag |
| Blur Span (pixels) [___] | | [_] Graduated Blur - blur relative to pixel contrast |
| Blur Angle (degrees) [___] | | [_] Paint Blur - paint normal blur locally with mouse drags |
| Algorithm Iterations [___] | | [_] Background Blur - blur relative to distance from foreground |
| Suppress Ringing [___] | | [_] Motion Blur - add motion blur to selected areas |
| | | |
| [Reset] [Apply] [OK] [Cancel] | | [cancel] |
|___________________________________| |____________________________________________________________________|
***/ ***/
zdialog *zd = zdialog_new("Fix Motion Blur",Mwin,"Reset","Apply","OK","Cancel zd = zdialog_new("Blur Image",Mwin,"Cancel",0);
",null); zdialog_add_widget(zd,"check","normal","dialog","Normal Blur - mix pixels wit
CEF->zd = zd; h all neighbor pixels");
zdialog_add_widget(zd,"check","radial","dialog","Radial Blur - mix pixels in
zdialog_add_widget(zd,"label","labhint","dialog",hintmess,"space=5"); radial lines from chosen center");
zdialog_add_widget(zd,"check","directed","dialog","Directed Blur - blur local
zdialog_add_widget(zd,"hbox","hb1","dialog"); ly in direction of mouse drag");
zdialog_add_widget(zd,"label","space","hb1",0,"space=3"); zdialog_add_widget(zd,"check","graduated","dialog","Graduated Blur - blur rel
zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=4|homog"); ative to pixel contrast");
zdialog_add_widget(zd,"label","space","hb1",0,"space=8"); zdialog_add_widget(zd,"check","paint","dialog","Paint Blur - paint normal blu
zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=4|homog"); r locally with mouse drags");
zdialog_add_widget(zd,"label","space","hb1",0,"space=3"); zdialog_add_widget(zd,"check","background","dialog","Background Blur - blur r
zdialog_add_widget(zd,"label","labspan","vb1","Blur Span (pixels)"); elative to distance from foreground");
zdialog_add_widget(zd,"label","labangle","vb1","Blur Angle (degrees)"); zdialog_add_widget(zd,"check","motion","dialog","Motion Blur - add motion blu
zdialog_add_widget(zd,"label","labiter","vb1","Algorithm Iterations"); r to selected areas");
zdialog_add_widget(zd,"label","labsup","vb1","Suppress Ringing");
zdialog_add_widget(zd,"zspin","span","vb2","0|40|1|0"); zdialog_stuff(zd,"normal",0);
zdialog_add_widget(zd,"zspin","angle","vb2","-180|180|1|0"); zdialog_stuff(zd,"radial",0);
zdialog_add_widget(zd,"zspin","iter","vb2","0|100|1|0"); zdialog_stuff(zd,"directed",0);
zdialog_add_widget(zd,"zspin","supring","vb2","0|9|1|0"); zdialog_stuff(zd,"graduated",0);
zdialog_stuff(zd,"paint",0);
zdialog_stuff(zd,"background",0);
zdialog_stuff(zd,"motion",0);
zdialog_restore_inputs(zd); zdialog_run(zd,blur_dialog_event,"save");
zdialog_run(zd,fix_motionblur_RL_dialog_event);
// run dialog - parallel
zdialog_send_event(zd,"init");
return; return;
} }
// dialog event and completion function // dialog event and completion function
int fix_motionblur_RL_dialog_event(zdialog *zd, const char *event) int blur_dialog_event(zdialog *zd, cchar *event)
{ {
using namespace fix_motionblur_RL_names; void m_blur_normal(GtkWidget *, cchar *menu);
void m_blur_radial(GtkWidget *, cchar *menu);
void fix_motionblur_RL_mousefunc(); void m_blur_directed(GtkWidget *, cchar *menu);
void m_blur_graduated(GtkWidget *, cchar *menu);
if (strmatch(event,"done")) zd->zstat = 3; void m_blur_paint(GtkWidget *, cchar *menu);
// from edit_setup() or f_save() void m_blur_background(GtkWidget *, cchar *menu);
if (strmatch(event,"cancel")) zd->zstat = 4; void m_blur_motion(GtkWidget *, cchar *menu);
// from f_open()
if (zd->zstat)
{
if (zd->zstat == 1) {
// [reset]
zd->zstat = 0;
// keep dialog active
edit_reset();
}
else if (zd->zstat == 2) {
// [apply]
zd->zstat = 0;
// keep dialog active
edit_reset();
zdialog_fetch(zd,"angle",angle);
// get all inputs
zdialog_fetch(zd,"span",span);
zdialog_fetch(zd,"iter",iter);
zdialog_fetch(zd,"supring",supring);
if (angle < 0) {
angle = 180 + angle;
// -1 --> 179
zdialog_stuff(zd,"angle",angle);
}
if (angle >= 180) {
// 180 --> 0
angle = angle - 180;
zdialog_stuff(zd,"angle",angle);
}
thread_signal();
// process
}
else if (zd->zstat == 3)
// [ OK ] commit edit
{
edit_addhist("span:%d angle:%d iter:%d supR:%d",
// edit params > edit hist
span,angle,iter,supring);
edit_done(0);
}
else edit_cancel(0); if (strmatch(event,"escape")) zd->zstat = -2;
// [cancel] or [x] discard edit // escape key
return 1;
}
if (strmatch(event,"focus")) { if (zd->zstat) {
takeMouse(fix_motionblur_RL_mousefunc,0); zdialog_free(zd);
// connect mouse
return 1; return 1;
} }
return 1; if (strmatch(event,"normal")) m_blur_normal(0,0);
} if (strmatch(event,"radial")) m_blur_radial(0,0);
if (strmatch(event,"directed")) m_blur_directed(0,0);
// mouse function - capture mouse drag direction and set rotate angle if (strmatch(event,"graduated")) m_blur_graduated(0,0);
if (strmatch(event,"paint")) m_blur_paint(0,0);
void fix_motionblur_RL_mousefunc() if (strmatch(event,"background")) m_blur_background(0,0);
{ if (strmatch(event,"motion")) m_blur_motion(0,0);
using namespace fix_motionblur_RL_names;
int dx, dy;
float R;
if (! KBshiftkey) {
takeMouse(fix_motionblur_RL_mousefunc,0);
return;
}
takeMouse(fix_motionblur_RL_mousefunc,dragcursor);
if (! Mxdrag && ! Mydrag) return;
dx = Mxdrag - Mxdown;
// drag vector
dy = Mydrag - Mydown;
Mxdrag = Mydrag = 0;
R = sqrtf(dx*dx + dy*dy);
// get angle of drag
angle = RAD * acosf(dx/R);
if (dy > 0) angle = 180 - angle;
// top quadrant only, 0-180 deg.
if (angle == 180) angle = 0;
if (CEF) zdialog_stuff(CEF->zd,"angle",angle);
return;
}
// thread function
void * fix_motionblur_RL_thread(void *)
{
using namespace fix_motionblur_RL_names;
void * fix_motionblur_RL_wthread0(void *arg);
// worker threads
void * fix_motionblur_RL_wthread1(void *arg);
void * fix_motionblur_RL_wthread2(void *arg);
float rotate;
int mww, mhh;
if (angle <= 90) rotate = angle;
// avoid upside-down result
else rotate = angle - 180;
PXM_free(E3pxm);
E3pxm = PXM_rotate(E1pxm,rotate);
// rotate to make blur angle 0
E9pxm = PXM_copy(E3pxm);
// E3 = E9 = rotated E1
do_wthreads(fix_motionblur_RL_wthread0,NWT);
// fill black margins from edge pixels
progress_reset(E3pxm->hh);
// progress monitor = rows
do_wthreads(fix_motionblur_RL_wthread1,NWT);
// do worker thread
progress_reset(0);
PXM_free(E9pxm);
E9pxm = 0;
E2pxm = PXM_rotate(E3pxm,-rotate);
// un-rotate
mww = (E2pxm->ww - Eww) / 2;
mhh = (E2pxm->hh - Ehh) / 2;
// cut-off margins from rotate
PXM_free(E3pxm);
E3pxm = PXM_copy_area(E2pxm,mww,mhh,Eww,Ehh);
PXM_free(E2pxm);
E2pxm = 0;
if (sa_stat == 3)
// process select area
do_wthreads(fix_motionblur_RL_wthread2,NWT);
CEF->Fmods++;
// image modified
CEF->Fsaved = 0;
// not saved
Fpaint2();
return 0;
// not executed, stop warning
}
// propagate image edge pixels into the black margins created from rotation
void * fix_motionblur_RL_wthread0(void *arg)
{
using namespace fix_motionblur_RL_names;
int index = *((int *) arg);
int Eww = E3pxm->ww;
// rotated and larger image
int Ehh = E3pxm->hh;
int px, py, pxL;
float *pix3, R, G, B;
if (angle == 0) return 0;
for (py = index; py < Ehh; py += NWT)
// loop image rows
{
for (px = 0; px < Eww; px++)
// loop columns
{
pix3 = PXMpix(E3pxm,px,py);
if (pix3[0] > 0 || pix3[1] > 0 || pix3[2] > 0) break;
// find first non-black pixel in row
}
pxL = px + 2;
// use next pixel
if (pxL < Eww) {
pix3 = PXMpix(E3pxm,pxL,py);
// get RGB values
R = pix3[0];
G = pix3[1];
B = pix3[2];
for (px = 0; px < pxL; px++) {
// fill black pixels with RGB
pix3 = PXMpix(E3pxm,px,py);
pix3[0] = R;
pix3[1] = G;
pix3[2] = B;
}
}
for (px = Eww-1; px > 0; px--)
// find last non-black pixel in row
{
pix3 = PXMpix(E3pxm,px,py);
if (pix3[0] > 0 || pix3[1] > 0 || pix3[2] > 0) break;
}
pxL = px - 2;
// use previous pixel
if (pxL > 0) {
pix3 = PXMpix(E3pxm,pxL,py);
// get RGB values
R = pix3[0];
G = pix3[1];
B = pix3[2];
for (px = pxL+1; px < Eww; px++) {
// fill black pixels with RGB
pix3 = PXMpix(E3pxm,px,py);
pix3[0] = R;
pix3[1] = G;
pix3[2] = B;
}
}
}
return 0;
}
// fix motion blur worker thread function
// E3 = rotated blurred image so that blur angle = 0
// span = N: each blurred pixel is average of N input pixels
void * fix_motionblur_RL_wthread1(void *arg)
{
using namespace fix_motionblur_RL_names;
void RLdecon(float *pixels, int Np, int Nd, int Nt);
int index = *((int *) arg);
int px, py, rgb;
int Eww = E3pxm->ww;
// rotated and larger image
int Ehh = E3pxm->hh;
int nc = E3pxm->nc;
int pcc = nc * sizeof(float);
float *pix2, *pix3, *RLval;
float *pix9, *pix9a, *pix9b;
float con, F1, F2;
if (span < 1) return 0;
RLval = (float *) zmalloc(Eww * sizeof(float),"motionblur-RL");
for (py = index; py < Ehh; py += NWT)
// loop image rows
{
progress_add(index,1);
// count progress
for (rgb = 0; rgb < 2; rgb++)
// loop RGB
{
for (px = 0; px < Eww; px++)
// loop pixels in row
{
pix3 = PXMpix(E3pxm,px,py);
// one RGB value per pixel
RLval[px] = pix3[rgb];
}
RLdecon(RLval,Eww,span,iter);
// do R-L algorithm on pixel array
for (px = 0; px < Eww; px++)
{
pix3 = PXMpix(E3pxm,px,py);
// save revised RGB values
pix3[rgb] = RLval[px];
}
}
for (px = Eww-1; px > span; px--)
// fix position shift
{
pix3 = PXMpix(E3pxm,px,py);
pix2 = PXMpix(E3pxm,px-span/2,py);
memcpy(pix3,pix2,pcc);
}
for (px = 1; px < Eww-1; px++)
// suppress ringing
for (rgb = 0; rgb < 3; rgb++)
{
pix9 = PXMpix(E9pxm,px,py);
// blurred input image
pix9a = pix9 - nc;
pix9b = pix9 + nc;
con = span * 0.0039 * fabsf(pix9a[rgb] - pix9b[rgb]);
// 0 - 1 = max. contrast
if (con > 1.0) con = 1.0;
F1 = 0.1 * supring;
// 0 - 1 = max. suppression
F2 = F1 * (1.0 - con);
// min. contrast --> max. suppression
pix3 = PXMpix(E3pxm,px,py);
// sharpened output image
pix3[rgb] = F2 * pix9[rgb] + (1.0 - F2) * pix3[rgb];
}
}
zfree(RLval);
return 0;
// exit thread
}
// fix motion blur worker thread function
// replace output with input image in areas outside the select area
void * fix_motionblur_RL_wthread2(void *arg)
{
using namespace fix_motionblur_RL_names;
int index = *((int *) arg);
int px, py, ii, dist;
float *pix1, *pix3, f1, f2;
int pcc = E1pxm->nc * sizeof(float);
for (py = index; py < Ehh; py += NWT)
for (px = 0; px < Eww; px++)
{
pix1 = PXMpix(E1pxm,px,py);
pix3 = PXMpix(E3pxm,px,py);
ii = py * Eww + px;
dist = sa_pixmap[ii];
if (dist) {
f1 = sa_blendfunc(dist);
// blend changes over sa_blendwidth
f2 = 1.0 - f1;
pix3[0] = f1 * pix3[0] + f2 * pix1[0];
pix3[1] = f1 * pix3[1] + f2 * pix1[1];
pix3[2] = f1 * pix3[2] + f2 * pix1[2];
}
else memcpy(pix3,pix1,pcc);
}
return 0;
}
/***
Richardson-Lucy deconvolution for special case: linear uniform blur.
pixels[] row of motion blurred pixels in blur direction (one RGB cha
nnel)
Np pixel row length
Nd blur span: Nd pixels contribute equally to each blurred pix
el
(inclusive: Nd = 1 means no blur)
Nt algorithm iterations
Variable names follow Wikipedia article on Richardson-Lucy deconvolution.
***/
void RLdecon(float *pixels, int Np, int Nd, int Nt)
{
int ii, jj, tt;
float Pij = 1.0 / Nd;
// pixel contribution factor
float *Ci = (float *) malloc(Np * sizeof(float));
// Ci factor per pixel
float *Uj = (float *) malloc(Np * sizeof(float));
// old/new estimated value per pixel
for (jj = 0; jj < Np; jj++)
// initial Uj per pixel jj
Uj[jj] = pixels[jj];
for (tt = 0; tt < Nt; tt++)
// algorithm iterations
{
for (ii = Nd; ii < Np-Nd; ii++)
// compute Ci per pixel ii
{
Ci[ii] = 0;
for (jj = 0; jj < Nd; jj++)
// Nd pixels contributing to ii
Ci[ii] += Pij * Uj[ii-jj];
if (Ci[ii] <= 0) Ci[ii] = 1;
}
for (jj = Nd; jj < Np-Nd-Nd; jj++)
// compute new Uj per pixel jj
{
float S = 0;
for (ii = 0; ii < Nd; ii++)
// Nd pixels contributing to pixel jj
S += pixels[jj+ii] / Ci[jj+ii] * Pij;
Uj[jj] = Uj[jj] * S;
// new Uj replaces old Uj
}
}
for (jj = 0; jj < Np; jj++) if (zstrstr("normal radial directed graduated paint background motion",event)
{ )
pixels[jj] = Uj[jj]; zdialog_free(zd);
if (pixels[jj] < 0) pixels[jj] = 0;
if (pixels[jj] > 255) pixels[jj] = 255;
}
free(Ci); return 1;
free(Uj);
return;
} }
/******************************************************************************* */ /******************************************************************************* */
// image blur function // normal blur
namespace blur_names namespace blur_normal_names
{ {
int Fnormblur;
// normal blur
float Nblur_radius;
// blur radius
float blur_weight[1415];
// blur radius limit 999
int Fradblur;
// radial blur
int RBrad, RBlen;
// radial blur radius, length
int Cx, Cy;
// image center of radial blur
int Fdirblur;
// directed blur
float Dmdx, Dmdy, Dmdw, Dmdh;
float DD, Dspan, Dintens;
int Fgradblur;
// graduated blur
float gblur_radius;
// blur radius
int con_limit;
// contrast limit
uint8 *pixcon;
int pixseq_done[122][122];
// up to gblur_radius = 60
int pixseq_angle[1000];
int pixseq_dx[13000], pixseq_dy[13000];
int pixseq_rad[13000];
int max1 = 999, max2 = 12999;
// for later overflow check
int Fpaintblur;
// paint blur
int pmode = 1;
// 1/2 = blend/restore
int pblur_radius = 20;
// mouse radius
float powcent, powedge;
// power at center and edge
float kernel[402][402];
// radius limit 200
int mousex, mousey;
// mouse click/drag position
editfunc EFblur; editfunc EFblur;
VOL int Fcancel; VOL int Fcancel;
PXM *E2pxm; PXM *E2pxm;
int Eww, Ehh; int Eww, Ehh;
// image dimensions
}
// menu function float blur_radius;
// blur radius
float blur_weight[1415];
// blur radius limit 999
}
void m_blur(GtkWidget *, cchar *menu) // consolidate all blur functions void m_blur_normal(GtkWidget *, cchar *menu)
{ {
using namespace blur_names; using namespace blur_normal_names;
int blur_dialog_event(zdialog *zd, cchar *event); int blur_normal_dialog_event(zdialog *zd, cchar *event);
void blur_mousefunc(); void * blur_normal_thread(void *);
void * blur_thread(void *);
cchar *radblur_tip = "Click to set center";
cchar *dirblur_tip = "Pull image using the mouse";
cchar *paintblur_tip = "left drag: blend image \n"
"right drag: restore image";
cchar *blurbackground_tip = "blur image outside selected area";
cchar *addmotionblur_tip = "add motion blur to image or area";
F1_help_topic = "blur"; F1_help_topic = "blur";
Plog(1,"m_blur \n"); Plog(1,"m_blur_normal \n");
EFblur.menuname = "Blur"; EFblur.menuname = "Blur Normal";
EFblur.menufunc = m_blur; EFblur.menufunc = m_blur_normal;
EFblur.Farea = 2; // select area usable EFblur.Farea = 2; // select area usable
EFblur.threadfunc = blur_thread; EFblur.threadfunc = blur_normal_thread;
// thread function // thread function
EFblur.mousefunc = blur_mousefunc;
EFblur.Frestart = 1; // allow restart EFblur.Frestart = 1; // allow restart
if (! edit_setup(EFblur)) return; // setup edit if (! edit_setup(EFblur)) return; // setup edit
/*** /***
_________________________________________ _________________________________________
| Blur Image | | Normal Blur |
| | | |
| [x] Normal Blur Radius [____] | | Blur Radius [____] |
| - - - - - - - - - - - - - - - - - - - |
| [x] Radial Blur |
| Radius [___] Length [___] |
// central area, no blur
| Center X [___] Y [___] |
| - - - - - - - - - - - - - - - - - - - |
| [X] Directed Blur |
| Blur Span [___] Intensity [___] |
| - - - - - - - - - - - - - - - - - - - |
| [X] Graduated Blur |
| Radius [___] Contrast Limit [___] |
| - - - - - - - - - - - - - - - - - - - |
| [X] Paint Blur |
| Radius [___] Power [___] Edge [___] |
| - - - - - - - - - - - - - - - - - - - |
| [ Blur Background ] |
| [ Add Motion Blur ] |
| | | |
| [Reset] [Apply] [ OK ] [Cancel] | | [Reset] [Apply] [ OK ] [Cancel] |
|_________________________________________| |_________________________________________|
***/ ***/
zdialog *zd = zdialog_new("Blur Radius",Mwin,"Reset","Apply","OK","Cancel",nu ll); zdialog *zd = zdialog_new("Normal Blur",Mwin,"Reset","Apply","OK","Cancel",nu ll);
EFblur.zd = zd; EFblur.zd = zd;
zdialog_add_widget(zd,"hbox","hbnb","dialog"); zdialog_add_widget(zd,"hbox","hbnb","dialog");
zdialog_add_widget(zd,"check","Fnormblur","hbnb","Normal Blur","space=2");
zdialog_add_widget(zd,"label","space","hbnb",0,"space=5");
zdialog_add_widget(zd,"label","labrad","hbnb","Radius","space=5"); zdialog_add_widget(zd,"label","labrad","hbnb","Radius","space=5");
zdialog_add_widget(zd,"zspin","Nblur_radius","hbnb","1|999|1|10","space=5|siz zdialog_add_widget(zd,"zspin","blur_radius","hbnb","1|999|1|10","space=5|size
e=3"); =3");
zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5");
zdialog_add_widget(zd,"hbox","hbrb1","dialog");
zdialog_add_widget(zd,"check","Fradblur","hbrb1","Radial Blur","space=2");
zdialog_add_widget(zd,"hbox","hbrb2","dialog");
zdialog_add_widget(zd,"label","labrbr","hbrb2","Radius","space=5");
zdialog_add_widget(zd,"zspin","RBrad","hbrb2","1|999|1|100","space=3|size=3")
;
zdialog_add_widget(zd,"label","space","hbrb2",0,"space=5");
zdialog_add_widget(zd,"label","labrbl","hbrb2","Length","space=5");
zdialog_add_widget(zd,"zspin","RBlen","hbrb2","1|999|1|100","space=3|size=3")
;
zdialog_add_widget(zd,"hbox","hbrb3","dialog");
zdialog_add_widget(zd,"label","labc","hbrb3","Center","space=5");
zdialog_add_widget(zd,"label","labcx","hbrb3","X","space=3");
zdialog_add_widget(zd,"zentry","Cx","hbrb3",0,"space=3|size=3");
zdialog_add_widget(zd,"label","space","hbrb3",0,"space=5");
zdialog_add_widget(zd,"label","labcy","hbrb3","Y","space=3");
zdialog_add_widget(zd,"zentry","Cy","hbrb3",0,"space=3|size=3");
zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5");
zdialog_add_widget(zd,"hbox","hbdb1","dialog");
zdialog_add_widget(zd,"check","Fdirblur","hbdb1","Directed Blur","space=2");
zdialog_add_widget(zd,"hbox","hbdb2","dialog");
zdialog_add_widget(zd,"label","labspan","hbdb2","Blur Span","space=5");
zdialog_add_widget(zd,"zspin","span","hbdb2","0.00|1.0|0.01|0.1","space=3|siz
e=3");
zdialog_add_widget(zd,"label","space","hbdb2",0,"space=5");
zdialog_add_widget(zd,"label","labint","hbdb2","Intensity");
zdialog_add_widget(zd,"zspin","intens","hbdb2","0.00|1.0|0.01|0.2","space=3|s
ize=3");
zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5");
zdialog_add_widget(zd,"hbox","hbgb1","dialog");
zdialog_add_widget(zd,"check","Fgradblur","hbgb1","Graduated Blur","space=2")
;
zdialog_add_widget(zd,"hbox","hbgb2","dialog");
zdialog_add_widget(zd,"label","labgrad","hbgb2","Radius","space=5");
zdialog_add_widget(zd,"zspin","gblur_radius","hbgb2","1|50|1|10","space=3|siz
e=3");
zdialog_add_widget(zd,"label","space","hbgb2",0,"space=5");
zdialog_add_widget(zd,"label","lablim","hbgb2","Contrast Limit");
zdialog_add_widget(zd,"zspin","con_limit","hbgb2","1|255|1|50","space=3|size=
3");
zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5");
zdialog_add_widget(zd,"hbox","hbpb1","dialog");
zdialog_add_widget(zd,"check","Fpaintblur","hbpb1","Paint Blur","space=2");
zdialog_add_widget(zd,"hbox","hbpb2","dialog");
zdialog_add_widget(zd,"label","labpaint","hbpb2","Radius","space=5");
zdialog_add_widget(zd,"zspin","pblur_radius","hbpb2","2|200|1|20","space=3|si
ze=3");
zdialog_add_widget(zd,"label","space","hbpb2",0,"space=5");
zdialog_add_widget(zd,"label","labpow","hbpb2","Power");
zdialog_add_widget(zd,"zspin","powcent","hbpb2","0|100|1|30","space=3|size=3"
);
zdialog_add_widget(zd,"label","space","hbpb2",0,"space=5");
zdialog_add_widget(zd,"label","labedge","hbpb2","Edge");
zdialog_add_widget(zd,"zspin","powedge","hbpb2","0|100|1|10","space=3|size=3"
);
zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5");
zdialog_add_widget(zd,"hbox","hbbg","dialog",0,"space=3");
zdialog_add_widget(zd,"button","Fblurbackground","hbbg","Blur Background","sp
ace=5");
zdialog_add_widget(zd,"hbox","hbbm","dialog",0,"space=3");
zdialog_add_widget(zd,"button","Faddmotionblur","hbbm","Motion Blur","space=5
");
zdialog_add_ttip(zd,"Fradblur",radblur_tip);
zdialog_add_ttip(zd,"Fdirblur",dirblur_tip);
zdialog_add_ttip(zd,"Fpaintblur",paintblur_tip);
zdialog_add_ttip(zd,"Fblurbackground",blurbackground_tip);
zdialog_add_ttip(zd,"Faddmotionblur",addmotionblur_tip);
Eww = E3pxm->ww; // image dimensions Eww = E3pxm->ww; // image dimensions
Ehh = E3pxm->hh; Ehh = E3pxm->hh;
Fcancel = 0; // initial status Fcancel = 0; // initial status
E2pxm = 0; E2pxm = 0;
blur_radius = 10;
Fnormblur = 0;
// default settings
Nblur_radius = 10;
Fradblur = 0;
RBrad = 100;
RBlen = 100;
Cx = Eww / 2;
Cy = Ehh / 2;
zdialog_stuff(zd,"Cx",Cx);
zdialog_stuff(zd,"Cy",Cy);
Fdirblur = 0;
Dspan = 0.1;
Dintens = 0.2;
Fgradblur = 0;
con_limit = 1;
gblur_radius = 10;
Fpaintblur = 0;
pmode = 1;
pblur_radius = 20;
powcent = 30;
powedge = 10;
zdialog_restore_inputs(zd); zdialog_restore_inputs(zd);
zdialog_run(zd,blur_normal_dialog_event,"save"); // run dialog
zdialog_run(zd,blur_dialog_event,"save");
// run dialog - parallel
zdialog_send_event(zd,"pblur_radius");
// get kernel initialized
return; return;
} }
// dialog event and completion callback function // dialog event and completion callback function
int blur_dialog_event(zdialog * zd, cchar *event) int blur_normal_dialog_event(zdialog * zd, cchar *event)
{ {
using namespace blur_names; using namespace blur_normal_names;
void blur_mousefunc(); void blur_mousefunc();
float frad, kern;
int rad, dx, dy;
if (strmatch(event,"escape")) zd->zstat = -2; // escape key if (strmatch(event,"escape")) zd->zstat = -2; // escape key
if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open()
if (strmatch(event,"Fblurbackground")) { zdialog_fetch(zd,"blur_radius",blur_radius);
Fcancel = 1;
edit_cancel(0);
if (E2pxm) PXM_free(E2pxm);
E2pxm = 0;
m_blur_background(0,0);
return 1;
}
if (strmatch(event,"Faddmotionblur")) {
Fcancel = 1;
edit_cancel(0);
if (E2pxm) PXM_free(E2pxm);
E2pxm = 0;
m_blur_motionblur(0,0);
return 1;
}
if (zstrstr("Fnormblur Fradblur Fdirblur Fgradblur Fpaintblur",event)) {
// checkboxes work like radio buttons
zdialog_stuff(zd,"Fnormblur",0);
zdialog_stuff(zd,"Fradblur",0);
zdialog_stuff(zd,"Fdirblur",0);
zdialog_stuff(zd,"Fgradblur",0);
zdialog_stuff(zd,"Fpaintblur",0);
zdialog_stuff(zd,event,1);
zdialog_fetch(zd,"Fnormblur",Fnormblur);
zdialog_fetch(zd,"Fradblur",Fradblur);
zdialog_fetch(zd,"Fdirblur",Fdirblur);
zdialog_fetch(zd,"Fgradblur",Fgradblur);
zdialog_fetch(zd,"Fpaintblur",Fpaintblur);
}
if (Fradblur || Fdirblur)
// connect mouse
takeMouse(blur_mousefunc,dragcursor);
else if (Fpaintblur)
takeMouse(blur_mousefunc,0);
else freeMouse();
zdialog_fetch(zd,"Fnormblur",Fnormblur);
// get all dialog inputs
zdialog_fetch(zd,"Nblur_radius",Nblur_radius);
zdialog_fetch(zd,"Fradblur",Fradblur);
zdialog_fetch(zd,"RBrad",RBrad);
zdialog_fetch(zd,"RBlen",RBlen);
zdialog_fetch(zd,"Cx",Cx);
zdialog_fetch(zd,"Cy",Cy);
zdialog_fetch(zd,"Fdirblur",Fdirblur);
zdialog_fetch(zd,"span",Dspan);
zdialog_fetch(zd,"intens",Dintens);
zdialog_fetch(zd,"Fgradblur",Fgradblur);
zdialog_fetch(zd,"gblur_radius",gblur_radius);
zdialog_fetch(zd,"con_limit",con_limit);
zdialog_fetch(zd,"Fpaintblur",Fpaintblur);
zdialog_fetch(zd,"pblur_radius",pblur_radius);
zdialog_fetch(zd,"powcent",powcent);
zdialog_fetch(zd,"powedge",powedge);
if (zstrstr("pblur_radius powcent powedge",event))
// paint blur parameters
{
zdialog_fetch(zd,"pblur_radius",pblur_radius);
// mouse radius
zdialog_fetch(zd,"powcent",powcent);
// center transparency
zdialog_fetch(zd,"powedge",powedge);
// powedge transparency
powcent = 0.01 * powcent;
// scale 0 ... 1
powedge = 0.01 * powedge;
rad = pblur_radius;
for (dy = -rad; dy <= rad; dy++)
// build kernel
for (dx = -rad; dx <= rad; dx++)
{
frad = sqrt(dx*dx + dy*dy);
kern = (rad - frad) / rad;
// center ... powedge >> 1 ... 0
kern = kern * (powcent - powedge) + powedge;
// strength center ... powedge
if (kern < 0) kern = 0;
if (kern > 1) kern = 1;
if (frad > rad) kern = 2;
// beyond radius, within square
kernel[dx+rad][dy+rad] = kern;
}
}
if (zd->zstat) if (zd->zstat)
{ {
if (zd->zstat == 1) // [reset] if (zd->zstat == 1) // [reset]
{ {
zd->zstat = 0; // keep dialog active zd->zstat = 0; // keep dialog active
edit_reset(); edit_reset();
} }
else if (zd->zstat == 2) // [apply] else if (zd->zstat == 2) // [apply]
{ {
zd->zstat = 0; // keep dialog active zd->zstat = 0; // keep dialog active
thread_signal(); // trigger thread thread_signal(); // trigger thread
return 1; // do not free E2 return 1; // do not free E2
} }
else if (zd->zstat == 3) else if (zd->zstat == 3) {
// [ OK ] // [ OK ]
edit_addhist("rad:%.0f",blur_radius);
edit_done(0); edit_done(0);
}
else { // [cancel] else { // [cancel]
Fcancel = 1; Fcancel = 1;
edit_cancel(0); // discard edit edit_cancel(0); // discard edit
} }
if (E2pxm) PXM_free(E2pxm); // free memory if (E2pxm) PXM_free(E2pxm); // free memory
E2pxm = 0; E2pxm = 0;
} }
return 1; return 1;
} }
// blur mouse function // thread function
void blur_mousefunc()
// mouse function
{
using namespace blur_names;
if (! CEF) return;
if (Fnormblur)
{
freeMouse();
return;
}
if (Fradblur && LMclick)
// radial blur, new center
{
zdialog *zd = CEF->zd;
Cx = Mxposn;
Cy = Myposn;
zdialog_stuff(zd,"Cx",Cx);
zdialog_stuff(zd,"Cy",Cy);
LMclick = 0;
thread_signal();
}
if (Fdirblur && (Mxdrag || Mydrag))
// directed blur, mouse drag
{
Dmdx = Mxdown;
// drag origin
Dmdy = Mydown;
Dmdw = Mxdrag - Mxdown;
// drag increment
Dmdh = Mydrag - Mydown;
Mxdrag = Mydrag = 0;
thread_signal();
}
if (Fpaintblur)
// paint blur
{
int px, py, rr;
if (LMclick || RMclick)
// mouse click
{
if (LMclick) pmode = 1;
// left click, paint
if (RMclick) pmode = 2;
// right click, erase
mousex = Mxclick;
mousey = Myclick;
thread_signal();
}
else if (Mxdrag || Mydrag)
// mouse drag in progress
{
if (Mbutton == 1) pmode = 1;
// left drag, paint
if (Mbutton == 3) pmode = 2;
// right drag, erase
mousex = Mxdrag;
mousey = Mydrag;
thread_signal();
}
cairo_t *cr = draw_context_create(gdkwin,draw_context);
px = mousex - pblur_radius - 1;
// repaint modified area
py = mousey - pblur_radius - 1;
rr = 2 * pblur_radius + 3;
Fpaint3(px,py,rr,rr,cr);
draw_mousecircle(Mxposn,Myposn,pblur_radius,0,cr);
// redraw mouse circle
draw_context_destroy(draw_context);
LMclick = RMclick = Mxdrag = Mydrag = 0;
// reset mouse
}
return;
}
// image blur thread function
void * blur_thread(void *) void * blur_normal_thread(void *)
{ {
using namespace blur_names; using namespace blur_normal_names;
void * normblur_wthread(void *); void * blur_normal_wthread(void *);
void * radblur_wthread(void *);
void * dirblur_wthread(void *);
void * gradblur_wthread(void *);
void * paintblur_wthread(void *);
int ii, jj; int ii;
float rad, w, wsum; float rad, w, wsum;
float dd, d1, d2, d3, d4;
int px, py, dx, dy, adx, ady;
float *pix1, *pix2;
float contrast, maxcon;
float rad1, rad2, angle, astep;
if (Fnormblur)
// normal blur
{
if (E2pxm) PXM_free(E2pxm);
E2pxm = PXM_copy(E1pxm);
// intermediate image
rad = Nblur_radius;
wsum = 0;
for (ii = 0; ii < rad; ii++)
// set pixel weight per distance
{
// example, rad = 10
w = 1.0 - ii / rad;
// dist: 0 1 2 3 5 7 9
w = w * w;
// weight: 1 .81 .64 .49 .25 .09 .01
blur_weight[ii] = w;
wsum += w;
}
for (ii = 0; ii < rad; ii++)
// make weights sum to 1.0
blur_weight[ii] = blur_weight[ii] / wsum;
if (sa_stat == 3) progress_reset(sa_Npixel * 2);
// initz. progress counter
else progress_reset(Eww * Ehh * 2);
do_wthreads(normblur_wthread,NWT);
// worker threads
}
if (Fradblur)
// radial blur
{
if (E2pxm) PXM_free(E2pxm);
if (Fnormblur)
// if normal blur done before,
E2pxm = PXM_copy(E3pxm);
// use the blur output image
else E2pxm = PXM_copy(E1pxm);
// else use the original image
if (sa_stat == 3) progress_reset(sa_Npixel);
// initz. progress counter
else progress_reset(Eww * Ehh);
do_wthreads(radblur_wthread,NWT);
// worker threads
}
if (Fdirblur)
// directed blur
{
d1 = (Dmdx-0) * (Dmdx-0) + (Dmdy-0) * (Dmdy-0);
// distance, mouse to 4 corners
d2 = (Eww-Dmdx) * (Eww-Dmdx) + (Dmdy-0) * (Dmdy-0);
d3 = (Eww-Dmdx) * (Eww-Dmdx) + (Ehh-Dmdy) * (Ehh-Dmdy);
d4 = (Dmdx-0) * (Dmdx-0) + (Ehh-Dmdy) * (Ehh-Dmdy);
dd = d1; if (E2pxm) PXM_free(E2pxm);
if (d2 > dd) dd = d2; E2pxm = PXM_copy(E1pxm);
// find greatest corner distance // intermediate image
if (d3 > dd) dd = d3;
if (d4 > dd) dd = d4;
DD = dd * 0.5 * Dspan; rad = blur_radius;
wsum = 0;
do_wthreads(dirblur_wthread,NWT); for (ii = 0; ii < rad; ii++)
// worker threads // set pixel weight per distance
{
// example, rad = 10
w = 1.0 - ii / rad;
// dist: 0 1 2 3 5 7 9
w = w * w;
// weight: 1 .81 .64 .49 .25 .09 .01
blur_weight[ii] = w;
wsum += w;
} }
if (Fgradblur) for (ii = 0; ii < rad; ii++)
// graduated blur // make weights sum to 1.0
{ blur_weight[ii] = blur_weight[ii] / wsum;
pixcon = (uint8 *) zmalloc(Eww * Ehh,"grad blur");
// pixel contrast map
for (py = 1; py < Ehh-1; py++)
// loop interior pixels
for (px = 1; px < Eww-1; px++)
{
pix1 = PXMpix(E1pxm,px,py);
// this pixel in base image E1
contrast = maxcon = 0.0;
for (dx = px-1; dx <= px+1; dx++)
// loop neighbor pixels
for (dy = py-1; dy <= py+1; dy++)
{
pix2 = PXMpix(E1pxm,dx,dy);
contrast = 1.0 - PIXMATCH(pix1,pix2);
// contrast, 0-1
if (contrast > maxcon) maxcon = contrast;
}
ii = py * Eww + px;
// ii maps to (px,py)
pixcon[ii] = 255 * maxcon;
// pixel contrast, 0 to 255
}
rad1 = gblur_radius;
for (dy = 0; dy <= 2*rad1; dy++)
// no pixels mapped yet
for (dx = 0; dx <= 2*rad1; dx++)
pixseq_done[dx][dy] = 0;
ii = jj = 0;
astep = 0.5 / rad1;
// 0.5 pixel steps at rad1 from center
for (angle = 0; angle < 2*PI; angle += astep)
// loop full circle
{
pixseq_angle[ii] = jj;
// start pixel sequence for this angle
ii++;
for (rad2 = 1; rad2 <= rad1; rad2++)
// loop rad2 from center to edge
{
dx = lround(rad2 * cos(angle));
// pixel at angle and rad2
dy = lround(rad2 * sin(angle));
adx = rad1 + dx;
ady = rad1 + dy;
if (pixseq_done[adx][ady]) continue;
// pixel already mapped
pixseq_done[adx][ady] = 1;
// map pixel
pixseq_dx[jj] = dx;
// save pixel sequence for angle
pixseq_dy[jj] = dy;
pixseq_rad[jj] = rad2;
// pixel radius
jj++;
}
pixseq_rad[jj] = 9999;
// mark end of pixels for angle
jj++;
}
pixseq_angle[ii] = 9999; if (sa_stat == 3) progress_reset(sa_Npixel * 2);
// mark end of angle steps // initz. progress counter
else progress_reset(Eww * Ehh * 2);
if (ii > max1 || jj > max2)
// should not happen
zappcrash("gradblur array overflow");
if (sa_stat == 3) progress_reset(sa_Npixel);
// initz. progress counter
else progress_reset(Eww * Ehh);
do_wthreads(gradblur_wthread,NWT);
// worker threads
zfree(pixcon);
}
if (Fpaintblur) do_wthreads(blur_normal_wthread,NWT);
do_wthreads(paintblur_wthread,NWT); // worker threads
// worker threads
progress_reset(0); progress_reset(0);
CEF->Fmods++; CEF->Fmods++;
CEF->Fsaved = 0; CEF->Fsaved = 0;
Fpaint2(); Fpaint2();
return 0; return 0;
} }
// normal blur worker thread // worker thread
void * normblur_wthread(void *arg) void * blur_normal_wthread(void *arg)
{ {
using namespace blur_names; using namespace blur_normal_names;
int index = *((int *) arg); int index = *((int *) arg);
int rad = Nblur_radius; int rad = blur_radius;
int ii, dist = 0; int ii, dist = 0;
int px, py, qx, qy; int px, py, qx, qy;
int ylo, yhi, xlo, xhi; int ylo, yhi, xlo, xhi;
float R, G, B, w1, w2, f1, f2; float R, G, B, w1, w2, f1, f2;
float *pix1, *pix3, *pix2; float *pix1, *pix3, *pix2;
for (py = index; py < Ehh; py += NWT) // loop all image pixels for (py = index; py < Ehh; py += NWT) // loop all image pixels
for (px = 0; px < Eww; px++) for (px = 0; px < Eww; px++)
{ {
if (Fcancel) return 0; // user cancel if (Fcancel) return 0; // user cancel
skipping to change at line 2403 skipping to change at line 1643
f2 = 1.0 - f1; f2 = 1.0 - f1;
pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[0] = f1 * pix3[0] + f2 * pix1[0];
pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[1] = f1 * pix3[1] + f2 * pix1[1];
pix3[2] = f1 * pix3[2] + f2 * pix1[2]; pix3[2] = f1 * pix3[2] + f2 * pix1[2];
} }
} }
return 0; return 0;
} }
// radial blur worker thread /*******************************************************************************
*/
// radial blur
namespace blur_radial_names
{
editfunc EFblur;
VOL int Fcancel;
PXM *E2pxm;
int Eww, Ehh;
int RBrad, RBlen;
// radial blur radius, length
int Cx, Cy;
// image center of radial blur
}
void m_blur_radial(GtkWidget *, cchar *menu)
{
using namespace blur_radial_names;
int blur_radial_dialog_event(zdialog *zd, cchar *event);
void blur_radial_mousefunc();
void * blur_radial_thread(void *);
F1_help_topic = "blur";
Plog(1,"m_blur_radial \n");
EFblur.menuname = "Blur Radial";
EFblur.menufunc = m_blur_radial;
EFblur.Farea = 2;
// select area usable
EFblur.threadfunc = blur_radial_thread;
// thread function
EFblur.mousefunc = blur_radial_mousefunc;
EFblur.Frestart = 1;
// allow restart
if (! edit_setup(EFblur)) return;
// setup edit
/***
_________________________________________
| Radial Blur |
| |
| Radius [___] Length [___] |
// central area, no blur
| Center X [___] Y [___] |
| |
| [Reset] [Apply] [ OK ] [Cancel] |
|_________________________________________|
***/
zdialog *zd = zdialog_new("Radial Blur",Mwin,"Reset","Apply","OK","Cancel",nu
ll);
EFblur.zd = zd;
zdialog_add_widget(zd,"hbox","hbrb2","dialog");
zdialog_add_widget(zd,"label","labrbr","hbrb2","Radius","space=5");
zdialog_add_widget(zd,"zspin","RBrad","hbrb2","1|999|1|100","space=3|size=3")
;
zdialog_add_widget(zd,"label","space","hbrb2",0,"space=5");
zdialog_add_widget(zd,"label","labrbl","hbrb2","Length","space=5");
zdialog_add_widget(zd,"zspin","RBlen","hbrb2","1|999|1|100","space=3|size=3")
;
zdialog_add_widget(zd,"hbox","hbrb3","dialog");
zdialog_add_widget(zd,"label","labc","hbrb3","Center","space=5");
zdialog_add_widget(zd,"label","labcx","hbrb3","X","space=3");
zdialog_add_widget(zd,"zentry","Cx","hbrb3",0,"space=3|size=3");
zdialog_add_widget(zd,"label","space","hbrb3",0,"space=5");
zdialog_add_widget(zd,"label","labcy","hbrb3","Y","space=3");
zdialog_add_widget(zd,"zentry","Cy","hbrb3",0,"space=3|size=3");
Eww = E3pxm->ww;
// image dimensions
Ehh = E3pxm->hh;
Fcancel = 0;
// initial status
E2pxm = 0;
RBrad = 100;
RBlen = 100;
Cx = Eww / 2;
Cy = Ehh / 2;
zdialog_stuff(zd,"Cx",Cx);
zdialog_stuff(zd,"Cy",Cy);
zdialog_restore_inputs(zd);
zdialog_run(zd,blur_radial_dialog_event,"save");
// run dialog
return;
}
// dialog event and completion callback function
int blur_radial_dialog_event(zdialog * zd, cchar *event)
{
using namespace blur_radial_names;
void blur_radial_mousefunc();
if (strmatch(event,"escape")) zd->zstat = -2;
// escape key
if (strmatch(event,"done")) zd->zstat = 3;
// from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4;
// from f_open()
zdialog_fetch(zd,"RBrad",RBrad);
zdialog_fetch(zd,"RBlen",RBlen);
zdialog_fetch(zd,"Cx",Cx);
zdialog_fetch(zd,"Cy",Cy);
takeMouse(blur_radial_mousefunc,dragcursor);
if (zd->zstat)
{
if (zd->zstat == 1)
// [reset]
{
zd->zstat = 0;
// keep dialog active
edit_reset();
}
else if (zd->zstat == 2)
// [apply]
{
zd->zstat = 0;
// keep dialog active
thread_signal();
// trigger thread
return 1;
// do not free E2
}
else if (zd->zstat == 3) {
// [ OK ]
edit_addhist("rad:%d length:%d",RBrad,RBlen);
edit_done(0);
}
else {
// [cancel]
Fcancel = 1;
edit_cancel(0);
// discard edit
}
if (E2pxm) PXM_free(E2pxm);
// free memory
E2pxm = 0;
}
return 1;
}
// mouse function
void blur_radial_mousefunc()
{
using namespace blur_radial_names;
if (! CEF) return;
if (LMclick)
// radial blur, new center
{
zdialog *zd = CEF->zd;
Cx = Mxposn;
Cy = Myposn;
zdialog_stuff(zd,"Cx",Cx);
zdialog_stuff(zd,"Cy",Cy);
LMclick = 0;
thread_signal();
}
return;
}
// thread function
void * blur_radial_thread(void *)
{
using namespace blur_radial_names;
void * blur_radial_wthread(void *);
if (E2pxm) PXM_free(E2pxm);
E2pxm = PXM_copy(E1pxm);
// use original image
if (sa_stat == 3) progress_reset(sa_Npixel);
// initz. progress counter
else progress_reset(Eww * Ehh);
do_wthreads(blur_radial_wthread,NWT);
// worker threads
progress_reset(0);
CEF->Fmods++;
CEF->Fsaved = 0;
Fpaint2();
return 0;
}
// worker thread
void * radblur_wthread(void *arg) void * blur_radial_wthread(void *arg)
{ {
using namespace blur_names; using namespace blur_radial_names;
int index = *((int *) arg); int index = *((int *) arg);
int ii, dist = 0; int ii, dist = 0;
int px, py, qx, qy, qz; int px, py, qx, qy, qz;
float RBlen2; float RBlen2;
float *pix2, *pix3; float *pix2, *pix3;
float R, Rx, Ry, Rz; float R, Rx, Ry, Rz;
float f1, f2; float f1, f2;
int Rsum, Gsum, Bsum, Npix; int Rsum, Gsum, Bsum, Npix;
skipping to change at line 2497 skipping to change at line 1919
f2 = 1.0 - f1; f2 = 1.0 - f1;
pix3[0] = f1 * pix3[0] + f2 * pix2[0]; pix3[0] = f1 * pix3[0] + f2 * pix2[0];
pix3[1] = f1 * pix3[1] + f2 * pix2[1]; pix3[1] = f1 * pix3[1] + f2 * pix2[1];
pix3[2] = f1 * pix3[2] + f2 * pix2[2]; pix3[2] = f1 * pix3[2] + f2 * pix2[2];
} }
} }
return 0; return 0;
} }
// directed blur worker thread /*******************************************************************************
*/
// directed blur
namespace blur_directed_names
{
float Dmdx, Dmdy, Dmdw, Dmdh;
float DD, Dspan, Dintens;
editfunc EFblur;
int Eww, Ehh;
}
void m_blur_directed(GtkWidget *, cchar *menu)
{
using namespace blur_directed_names;
int blur_directed_dialog_event(zdialog *zd, cchar *event);
void blur_directed_mousefunc();
void * blur_directed_thread(void *);
F1_help_topic = "blur";
Plog(1,"m_blur_directed \n");
EFblur.menuname = "Blur Directed";
EFblur.menufunc = m_blur_directed;
EFblur.Farea = 2;
// select area usable
EFblur.threadfunc = blur_directed_thread;
// thread function
EFblur.mousefunc = blur_directed_mousefunc;
EFblur.Frestart = 1;
// allow restart
if (! edit_setup(EFblur)) return;
// setup edit
/***
_________________________________________
| Directed Blur |
| |
| Blur Span [___] Intensity [___] |
| |
| [Reset] [ OK ] [Cancel] |
|_________________________________________|
***/
zdialog *zd = zdialog_new("Directed Blur",Mwin,"Reset","OK","Cancel",null);
EFblur.zd = zd;
zdialog_add_widget(zd,"hbox","hbdb2","dialog");
zdialog_add_widget(zd,"label","labspan","hbdb2","Blur Span","space=5");
zdialog_add_widget(zd,"zspin","span","hbdb2","0.00|1.0|0.002|0.1","space=3|si
ze=3");
zdialog_add_widget(zd,"label","space","hbdb2",0,"space=5");
zdialog_add_widget(zd,"label","labint","hbdb2","Intensity");
zdialog_add_widget(zd,"zspin","intens","hbdb2","0.00|1.0|0.01|0.2","space=3|s
ize=3");
Eww = E3pxm->ww;
// image dimensions
Ehh = E3pxm->hh;
Dspan = 0.1;
Dintens = 0.2;
zdialog_restore_inputs(zd);
zdialog_run(zd,blur_directed_dialog_event,"save");
// run dialog
return;
}
// dialog event and completion callback function
int blur_directed_dialog_event(zdialog * zd, cchar *event)
{
using namespace blur_directed_names;
void blur_directed_mousefunc();
if (strmatch(event,"escape")) zd->zstat = -2;
// escape key
if (strmatch(event,"done")) zd->zstat = 3;
// from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4;
// from f_open()
takeMouse(blur_directed_mousefunc,dragcursor);
zdialog_fetch(zd,"span",Dspan);
zdialog_fetch(zd,"intens",Dintens);
if (zd->zstat)
{
if (zd->zstat == 1)
// [reset]
{
zd->zstat = 0;
// keep dialog active
edit_reset();
}
else if (zd->zstat == 2) {
// [ OK ]
edit_addhist("span:%.2f intens:%.2f",Dspan,Dintens);
edit_done(0);
}
else edit_cancel(0);
// discard edit
}
return 1;
}
// mouse function
void blur_directed_mousefunc()
{
using namespace blur_directed_names;
if (! CEF) return;
if (Mxdrag || Mydrag)
// directed blur, mouse drag
{
Dmdx = Mxdown;
// drag origin
Dmdy = Mydown;
Dmdw = Mxdrag - Mxdown;
// drag increment
Dmdh = Mydrag - Mydown;
Mxdrag = Mydrag = 0;
thread_signal();
}
return;
}
// thread function
void * blur_directed_thread(void *)
{
using namespace blur_directed_names;
void * blur_directed_wthread(void *);
float dd, d1, d2, d3, d4;
d1 = (Dmdx-0) * (Dmdx-0) + (Dmdy-0) * (Dmdy-0);
// distance, mouse to 4 corners
d2 = (Eww-Dmdx) * (Eww-Dmdx) + (Dmdy-0) * (Dmdy-0);
d3 = (Eww-Dmdx) * (Eww-Dmdx) + (Ehh-Dmdy) * (Ehh-Dmdy);
d4 = (Dmdx-0) * (Dmdx-0) + (Ehh-Dmdy) * (Ehh-Dmdy);
dd = d1;
if (d2 > dd) dd = d2;
// find greatest corner distance
if (d3 > dd) dd = d3;
if (d4 > dd) dd = d4;
DD = dd * 0.5 * Dspan;
do_wthreads(blur_directed_wthread,NWT);
// worker threads
progress_reset(0);
CEF->Fmods++;
CEF->Fsaved = 0;
Fpaint2();
return 0;
}
// worker thread
void * dirblur_wthread(void *arg) void * blur_directed_wthread(void *arg)
{ {
using namespace blur_names; using namespace blur_directed_names;
int index = *((int *) arg); int index = *((int *) arg);
int ii, px, py, dist = 0, vstat; int ii, px, py, dist = 0, vstat;
float d, mag, dispx, dispy; float d, mag, dispx, dispy;
float F1, F2, f1, f2; float F1, F2, f1, f2;
float vpix[4], *pix1, *pix3; float vpix[4], *pix1, *pix3;
F1 = Dintens * Dintens; F1 = Dintens * Dintens;
F2 = 1.0 - F1; F2 = 1.0 - F1;
skipping to change at line 2553 skipping to change at line 2132
f2 = 1.0 - f1; f2 = 1.0 - f1;
pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[0] = f1 * pix3[0] + f2 * pix1[0];
pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[1] = f1 * pix3[1] + f2 * pix1[1];
pix3[2] = f1 * pix3[2] + f2 * pix1[2]; pix3[2] = f1 * pix3[2] + f2 * pix1[2];
} }
} }
return 0; // exit thread return 0; // exit thread
} }
// graduated blur worker thread /*******************************************************************************
*/
// graduated blur
namespace blur_graduated_names
{
float radius;
// blur radius
int con_limit;
// contrast limit
uint8 *pixcon;
int pixseq_done[122][122];
// up to blur_radius = 60
int pixseq_angle[1000];
int pixseq_dx[13000], pixseq_dy[13000];
int pixseq_rad[13000];
int max1 = 999, max2 = 12999;
// for later overflow check
editfunc EFblur;
VOL int Fcancel;
int Eww, Ehh;
}
void m_blur_graduated(GtkWidget *, cchar *menu)
{
using namespace blur_graduated_names;
int blur_graduated_dialog_event(zdialog *zd, cchar *event);
void * blur_graduated_thread(void *);
F1_help_topic = "blur";
Plog(1,"m_blur_graduated \n");
EFblur.menuname = "Blur Graduated";
EFblur.menufunc = m_blur_graduated;
EFblur.Farea = 2;
// select area usable
EFblur.threadfunc = blur_graduated_thread;
// thread function
EFblur.Frestart = 1;
// allow restart
if (! edit_setup(EFblur)) return;
// setup edit
/***
_________________________________________
| Graduated Blur |
| |
| Radius [___] Contrast Limit [___] |
| |
| [Reset] [Apply] [ OK ] [Cancel] |
|_________________________________________|
***/
zdialog *zd = zdialog_new("Graduated Blur",Mwin,"Reset","Apply","OK","Cancel"
,null);
EFblur.zd = zd;
zdialog_add_widget(zd,"hbox","hbgb2","dialog");
zdialog_add_widget(zd,"label","labgrad","hbgb2","Radius","space=5");
zdialog_add_widget(zd,"zspin","radius","hbgb2","1|50|1|10","space=3|size=3");
zdialog_add_widget(zd,"label","space","hbgb2",0,"space=5");
zdialog_add_widget(zd,"label","lablim","hbgb2","Contrast Limit");
zdialog_add_widget(zd,"zspin","con_limit","hbgb2","1|255|1|50","space=3|size=
3");
Eww = E3pxm->ww;
// image dimensions
Ehh = E3pxm->hh;
Fcancel = 0;
// initial status
con_limit = 1;
radius = 10;
zdialog_restore_inputs(zd);
zdialog_run(zd,blur_graduated_dialog_event,"save");
// run dialog
return;
}
// dialog event and completion callback function
int blur_graduated_dialog_event(zdialog * zd, cchar *event)
{
using namespace blur_graduated_names;
if (strmatch(event,"escape")) zd->zstat = -2;
// escape key
if (strmatch(event,"done")) zd->zstat = 3;
// from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4;
// from f_open()
zdialog_fetch(zd,"radius",radius);
zdialog_fetch(zd,"con_limit",con_limit);
if (zd->zstat)
{
if (zd->zstat == 1)
// [reset]
{
zd->zstat = 0;
// keep dialog active
edit_reset();
}
else if (zd->zstat == 2)
// [apply]
{
zd->zstat = 0;
// keep dialog active
thread_signal();
// trigger thread
return 1;
// do not free E2
}
else if (zd->zstat == 3) {
// [ OK ]
edit_addhist("rad:%.0f conlim:%d",radius,con_limit);
edit_done(0);
}
else {
// [cancel]
Fcancel = 1;
edit_cancel(0);
// discard edit
}
}
return 1;
}
// thread function
void * blur_graduated_thread(void *)
{
using namespace blur_graduated_names;
void * blur_graduated_wthread(void *);
int ii, jj;
int px, py, dx, dy, adx, ady;
float *pix1, *pix2;
float contrast, maxcon;
float rad1, rad2, angle, astep;
pixcon = (uint8 *) zmalloc(Eww * Ehh,"grad blur");
// pixel contrast map
for (py = 1; py < Ehh-1; py++)
// loop interior pixels
for (px = 1; px < Eww-1; px++)
{
pix1 = PXMpix(E1pxm,px,py);
// this pixel in base image E1
contrast = maxcon = 0.0;
for (dx = px-1; dx <= px+1; dx++)
// loop neighbor pixels
for (dy = py-1; dy <= py+1; dy++)
{
pix2 = PXMpix(E1pxm,dx,dy);
contrast = 1.0 - PIXMATCH(pix1,pix2);
// contrast, 0-1
if (contrast > maxcon) maxcon = contrast;
}
ii = py * Eww + px;
// ii maps to (px,py)
pixcon[ii] = 255 * maxcon;
// pixel contrast, 0 to 255
}
rad1 = radius;
for (dy = 0; dy <= 2*rad1; dy++)
// no pixels mapped yet
for (dx = 0; dx <= 2*rad1; dx++)
pixseq_done[dx][dy] = 0;
ii = jj = 0;
astep = 0.5 / rad1;
// 0.5 pixel steps at rad1 from center
for (angle = 0; angle < 2*PI; angle += astep)
// loop full circle
{
pixseq_angle[ii] = jj;
// start pixel sequence for this angle
ii++;
for (rad2 = 1; rad2 <= rad1; rad2++)
// loop rad2 from center to edge
{
dx = lround(rad2 * cos(angle));
// pixel at angle and rad2
dy = lround(rad2 * sin(angle));
adx = rad1 + dx;
ady = rad1 + dy;
if (pixseq_done[adx][ady]) continue;
// pixel already mapped
pixseq_done[adx][ady] = 1;
// map pixel
pixseq_dx[jj] = dx;
// save pixel sequence for angle
pixseq_dy[jj] = dy;
pixseq_rad[jj] = rad2;
// pixel radius
jj++;
}
pixseq_rad[jj] = 9999;
// mark end of pixels for angle
jj++;
}
pixseq_angle[ii] = 9999;
// mark end of angle steps
if (ii > max1 || jj > max2)
// should not happen
zappcrash("gradblur array overflow");
if (sa_stat == 3) progress_reset(sa_Npixel);
// initz. progress counter
else progress_reset(Eww * Ehh);
do_wthreads(blur_graduated_wthread,NWT);
// worker threads
zfree(pixcon);
progress_reset(0);
CEF->Fmods++;
CEF->Fsaved = 0;
Fpaint2();
return 0;
}
// worker thread
void * gradblur_wthread(void *arg) void * blur_graduated_wthread(void *arg)
{ {
using namespace blur_names; using namespace blur_graduated_names;
int index = *((int *) arg); int index = *((int *) arg);
int ii, jj, npix, dist = 0; int ii, jj, npix, dist = 0;
int px, py, dx, dy; int px, py, dx, dy;
float red, green, blue, f1, f2; float red, green, blue, f1, f2;
float *pix1, *pix3, *pixN; float *pix1, *pix3, *pixN;
int rad, blurrad, con; int rad, blurrad, con;
for (py = index+1; py < Ehh-1; py += NWT) // loop interior pixels for (py = index+1; py < Ehh-1; py += NWT) // loop interior pixels
for (px = 1; px < Eww-1; px++) for (px = 1; px < Eww-1; px++)
skipping to change at line 2588 skipping to change at line 2370
if (pixcon[ii] > con_limit) continue; // high contrast pixel if (pixcon[ii] > con_limit) continue; // high contrast pixel
pix1 = PXMpix(E1pxm,px,py); // source pixel pix1 = PXMpix(E1pxm,px,py); // source pixel
pix3 = PXMpix(E3pxm,px,py); // target pixel pix3 = PXMpix(E3pxm,px,py); // target pixel
red = pix1[0]; // blur center pixel red = pix1[0]; // blur center pixel
green = pix1[1]; green = pix1[1];
blue = pix1[2]; blue = pix1[2];
npix = 1; npix = 1;
blurrad = 1.0 + gblur_radius * (con_limit - pixcon[ii]) / con_limit; // blur radius for pixel, 1 - gblur_radius blurrad = 1.0 + radius * (con_limit - pixcon[ii]) / con_limit; // blur radius for pixel, 1 - blur_radius
for (ii = 0; ii < 2000; ii++) // loop angle around center pixel for (ii = 0; ii < 2000; ii++) // loop angle around center pixel
{ {
jj = pixseq_angle[ii]; // 1st pixel for angle step ii jj = pixseq_angle[ii]; // 1st pixel for angle step ii
if (jj == 9999) break; // none, end of angle loop if (jj == 9999) break; // none, end of angle loop
while (true) // loop pixels from center to radius while (true) // loop pixels from center to radius
{ {
rad = pixseq_rad[jj]; // next pixel step radius rad = pixseq_rad[jj]; // next pixel step radius
if (rad > blurrad) break; // stop here if beyond blur radius if (rad > blurrad) break; // stop here if beyond blur radius
skipping to change at line 2634 skipping to change at line 2416
pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[1] = f1 * pix3[1] + f2 * pix1[1];
pix3[2] = f1 * pix3[2] + f2 * pix1[2]; pix3[2] = f1 * pix3[2] + f2 * pix1[2];
} }
progress_add(index,1); // track progress progress_add(index,1); // track progress
} }
return 0; return 0;
} }
// paint blur worker thread /*******************************************************************************
*/
// paint blur
namespace blur_paint_names
{
int pmode = 1;
// 1/2 = blend/restore
int radius = 20;
// mouse radius
float powcent, powedge;
// power at center and edge
float kernel[402][402];
// radius limit 200
int mousex, mousey;
// mouse click/drag position
editfunc EFblur;
int Eww, Ehh;
}
void m_blur_paint(GtkWidget *, cchar *menu)
{
using namespace blur_paint_names;
int blur_paint_dialog_event(zdialog *zd, cchar *event);
void blur_paint_mousefunc();
void * blur_paint_thread(void *);
F1_help_topic = "blur";
Plog(1,"m_blur_paint \n");
EFblur.menuname = "Blur Paint";
EFblur.menufunc = m_blur_paint;
EFblur.Farea = 2;
// select area usable
EFblur.threadfunc = blur_paint_thread;
// thread function
EFblur.mousefunc = blur_paint_mousefunc;
EFblur.Frestart = 1;
// allow restart
if (! edit_setup(EFblur)) return;
// setup edit
/***
_________________________________________
| Paint Blur |
| |
| Radius [___] Power [___] Edge [___] |
| |
| [Reset] [ OK ] [Cancel] |
|_________________________________________|
***/
zdialog *zd = zdialog_new("Paint Blur",Mwin,"Reset","OK","Cancel",null);
EFblur.zd = zd;
zdialog_add_widget(zd,"hbox","hbpb2","dialog");
zdialog_add_widget(zd,"label","labpaint","hbpb2","Radius","space=5");
zdialog_add_widget(zd,"zspin","radius","hbpb2","2|200|1|20","space=3|size=3")
;
zdialog_add_widget(zd,"label","space","hbpb2",0,"space=5");
zdialog_add_widget(zd,"label","labpow","hbpb2","Power");
zdialog_add_widget(zd,"zspin","powcent","hbpb2","0|100|1|30","space=3|size=3"
);
zdialog_add_widget(zd,"label","space","hbpb2",0,"space=5");
zdialog_add_widget(zd,"label","labedge","hbpb2","Edge");
zdialog_add_widget(zd,"zspin","powedge","hbpb2","0|100|1|10","space=3|size=3"
);
Eww = E3pxm->ww;
// image dimensions
Ehh = E3pxm->hh;
pmode = 1;
radius = 20;
powcent = 30;
powedge = 10;
zdialog_restore_inputs(zd);
zdialog_run(zd,blur_paint_dialog_event,"save");
// run dialog
zdialog_send_event(zd,"radius");
// get kernel initialized
return;
}
// dialog event and completion callback function
int blur_paint_dialog_event(zdialog * zd, cchar *event)
{
using namespace blur_paint_names;
void blur_paint_mousefunc();
float frad, kern;
int rad, dx, dy;
if (strmatch(event,"escape")) zd->zstat = -2;
// escape key
if (strmatch(event,"done")) zd->zstat = 3;
// from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4;
// from f_open()
takeMouse(blur_paint_mousefunc,0);
zdialog_fetch(zd,"radius",radius);
zdialog_fetch(zd,"powcent",powcent);
zdialog_fetch(zd,"powedge",powedge);
if (zstrstr("radius powcent powedge",event))
// paint blur parameters
{
zdialog_fetch(zd,"radius",radius);
// mouse radius
zdialog_fetch(zd,"powcent",powcent);
// center transparency
zdialog_fetch(zd,"powedge",powedge);
// powedge transparency
powcent = 0.01 * powcent;
// scale 0 ... 1
powedge = 0.01 * powedge;
rad = radius;
for (dy = -rad; dy <= rad; dy++)
// build kernel
for (dx = -rad; dx <= rad; dx++)
{
frad = sqrt(dx*dx + dy*dy);
kern = (rad - frad) / rad;
// center ... powedge >> 1 ... 0
kern = kern * (powcent - powedge) + powedge;
// strength center ... powedge
if (kern < 0) kern = 0;
if (kern > 1) kern = 1;
if (frad > rad) kern = 2;
// beyond radius, within square
kernel[dx+rad][dy+rad] = kern;
}
}
if (zd->zstat)
{
if (zd->zstat == 1)
// [reset]
{
zd->zstat = 0;
// keep dialog active
edit_reset();
}
else if (zd->zstat == 2)
// [ OK ]
{
edit_addhist("rad:%d powC:%.0f powE:%.0f",radius,powcent,powedge);
edit_done(0);
}
else edit_cancel(0);
// discard edit
}
return 1;
}
// mouse function
void blur_paint_mousefunc()
{
using namespace blur_paint_names;
int px, py, rr;
if (! CEF) return;
if (LMclick || RMclick)
// mouse click
{
if (LMclick) pmode = 1;
// left click, paint
if (RMclick) pmode = 2;
// right click, erase
mousex = Mxclick;
mousey = Myclick;
thread_signal();
}
else if (Mxdrag || Mydrag)
// mouse drag in progress
{
if (Mbutton == 1) pmode = 1;
// left drag, paint
if (Mbutton == 3) pmode = 2;
// right drag, erase
mousex = Mxdrag;
mousey = Mydrag;
thread_signal();
}
cairo_t *cr = draw_context_create(gdkwin,draw_context);
px = mousex - radius - 1;
// repaint modified area
py = mousey - radius - 1;
rr = 2 * radius + 3;
Fpaint3(px,py,rr,rr,cr);
draw_mousecircle(Mxposn,Myposn,radius,0,cr);
// redraw mouse circle
draw_context_destroy(draw_context);
LMclick = RMclick = Mxdrag = Mydrag = 0;
// reset mouse
return;
}
// thread function
void * blur_paint_thread(void *)
{
using namespace blur_paint_names;
void * blur_paint_wthread(void *);
do_wthreads(blur_paint_wthread,NWT);
// worker threads
progress_reset(0);
CEF->Fmods++;
CEF->Fsaved = 0;
return 0;
}
// worker thread
void * paintblur_wthread(void *arg) void * blur_paint_wthread(void *arg)
{ {
using namespace blur_names; using namespace blur_paint_names;
int index = *((int *) arg); int index = *((int *) arg);
float *pix1, *pix3, *pixm; float *pix1, *pix3, *pixm;
int radius, radius2, npix; int rad, rad2, npix;
int px, py, dx, dy, qx, qy, rx, ry, sx, sy; int px, py, dx, dy, qx, qy, rx, ry, sx, sy;
int ii, dist = 0; int ii, dist = 0;
float kern, kern2, meanR, meanG, meanB; float kern, kern2, meanR, meanG, meanB;
px = mousex; px = mousex;
py = mousey; py = mousey;
radius = pblur_radius; rad = radius;
for (dy = -radius+index; dy <= radius; dy += NWT) for (dy = -rad+index; dy <= rad; dy += NWT)
// loop within mouse radius // loop within mouse radius
for (dx = -radius; dx <= radius; dx++) for (dx = -rad; dx <= rad; dx++)
{ {
qx = px + dx; qx = px + dx;
qy = py + dy; qy = py + dy;
if (qx < 0 || qx > Eww-1) continue; // off image if (qx < 0 || qx > Eww-1) continue; // off image
if (qy < 0 || qy > Ehh-1) continue; if (qy < 0 || qy > Ehh-1) continue;
if (sa_stat == 3) { // select area active if (sa_stat == 3) { // select area active
ii = qy * Eww + qx; ii = qy * Eww + qx;
dist = sa_pixmap[ii]; dist = sa_pixmap[ii];
if (! dist) continue; // pixel is outside area if (! dist) continue; // pixel is outside area
} }
kern = kernel[dx+radius][dy+radius]; // mouse transparencies kern = kernel[dx+rad][dy+rad]; // mouse transparencies
if (kern > 1) continue; // outside mouse radius if (kern > 1) continue; // outside mouse radius
if (sa_stat == 3 && dist < sa_blendwidth) // within blend distance if (sa_stat == 3 && dist < sa_blendwidth) // within blend distance
kern = kern * sa_blendfunc(dist); kern = kern * sa_blendfunc(dist);
pix1 = PXMpix(E1pxm,qx,qy); // original pixel pix1 = PXMpix(E1pxm,qx,qy); // original pixel
pix3 = PXMpix(E3pxm,qx,qy); // edited pixel pix3 = PXMpix(E3pxm,qx,qy); // edited pixel
meanR = meanG = meanB = npix = 0; meanR = meanG = meanB = npix = 0;
radius2 = sqrtf(radius); // radius = 2..99 >> radius2 = 1..9 rad2 = sqrtf(rad); // rad = 2..99 >> rad2 = 1..9
for (ry = -radius2; ry <= radius2; ry++) for (ry = -rad2; ry <= rad2; ry++)
for (rx = -radius2; rx <= radius2; rx++) for (rx = -rad2; rx <= rad2; rx++)
{ {
sx = qx + rx; sx = qx + rx;
sy = qy + ry; sy = qy + ry;
if (px - sx < -radius || px - sx > radius) continue; if (px - sx < -rad || px - sx > rad) continue;
// outside mouse radius // outside mouse radius
if (py - sy < -radius || py - sy > radius) continue; if (py - sy < -rad || py - sy > rad) continue;
if (sx < 0 || sx > Eww-1) continue; // off image if (sx < 0 || sx > Eww-1) continue; // off image
if (sy < 0 || sy > Ehh-1) continue; if (sy < 0 || sy > Ehh-1) continue;
pixm = PXMpix(E3pxm,sx,sy); pixm = PXMpix(E3pxm,sx,sy);
meanR += pixm[0]; meanR += pixm[0];
meanG += pixm[1]; meanG += pixm[1];
meanB += pixm[2]; meanB += pixm[2];
npix++; npix++;
} }
skipping to change at line 2727 skipping to change at line 2709
return 0; return 0;
} }
/******************************************************************************* */ /******************************************************************************* */
// Blur Background // Blur Background
// Blur the image outside of a selected area or areas. // Blur the image outside of a selected area or areas.
// Blur increases with distance from selected area edges. // Blur increases with distance from selected area edges.
namespace blur_BG_names namespace blur_background_names
{ {
int conrad, incrad; // constant or increasing blur int conrad, incrad; // constant or increasing blur
int conbrad; // constant blur radius int conbrad; // constant blur radius
int minbrad; // min. blur radius int minbrad; // min. blur radius
int maxbrad; // max. blur radius int maxbrad; // max. blur radius
VOL int Fcancel; // GCC inconsistent VOL int Fcancel; // GCC inconsistent
int Eww, Ehh; // image dimensions int Eww, Ehh; // image dimensions
int maxdist; // max. area edge distance int maxdist; // max. area edge distance
editfunc EFblurBG; editfunc EFblur;
} }
// called from main blur function, no separate menu
void m_blur_background(GtkWidget *, const char *menu) void m_blur_background(GtkWidget *, const char *menu)
{ {
using namespace blur_BG_names; using namespace blur_background_names;
int blur_BG_dialog_event(zdialog* zd, const char *event); int blur_background_dialog_event(zdialog* zd, const char *event);
void * blur_BG_thread(void *); void * blur_background_thread(void *);
Plog(1,"m_blur_background \n"); Plog(1,"m_blur_background \n");
EFblurBG.menufunc = m_blur_background; EFblur.menufunc = m_blur_background;
EFblurBG.menuname = "Blur Background"; EFblur.menuname = "Blur Background";
// function name // function name
EFblurBG.Farea = 2; EFblur.Farea = 2;
// select area usable (required) // select area usable (required)
EFblurBG.threadfunc = blur_BG_thread; EFblur.threadfunc = blur_background_thread;
// thread function // thread function
if (! edit_setup(EFblurBG)) return; // setup edit if (! edit_setup(EFblur)) return; // setup edit
minbrad = 10; // defaults minbrad = 10; // defaults
maxbrad = 20; maxbrad = 20;
conbrad = 10; conbrad = 10;
conrad = 1; conrad = 1;
incrad = 0; incrad = 0;
Fcancel = 0; Fcancel = 0;
Eww = E3pxm->ww; Eww = E3pxm->ww;
Ehh = E3pxm->hh; Ehh = E3pxm->hh;
skipping to change at line 2802 skipping to change at line 2782
zdialog_add_widget(zd,"zspin","minbrad","hbmin","0|100|1|10","space=3"); zdialog_add_widget(zd,"zspin","minbrad","hbmin","0|100|1|10","space=3");
zdialog_add_widget(zd,"hbox","hbmax","dialog"); zdialog_add_widget(zd,"hbox","hbmax","dialog");
zdialog_add_widget(zd,"label","labmax","hbmax","max. blur radius","space=8"); zdialog_add_widget(zd,"label","labmax","hbmax","max. blur radius","space=8");
zdialog_add_widget(zd,"zspin","maxbrad","hbmax","1|100|1|20","space=3"); zdialog_add_widget(zd,"zspin","maxbrad","hbmax","1|100|1|20","space=3");
zdialog_stuff(zd,"conrad",conrad); zdialog_stuff(zd,"conrad",conrad);
zdialog_stuff(zd,"incrad",incrad); zdialog_stuff(zd,"incrad",incrad);
zdialog_resize(zd,300,0); zdialog_resize(zd,300,0);
zdialog_restore_inputs(zd); // restore previous inputs zdialog_restore_inputs(zd); // restore previous inputs
zdialog_run(zd,blur_BG_dialog_event,"save"); // run dialog - parallel zdialog_run(zd,blur_background_dialog_event,"save"); // run dialog - parallel
if (sa_stat != 3) m_select_area(0,0); // start select area dialog if (sa_stat != 3) m_select_area(0,0); // start select area dialog
return; return;
} }
// blur_BG dialog event and completion function // dialog event and completion function
int blur_BG_dialog_event(zdialog *zd, const char *event) // blur_BG dialog event function int blur_background_dialog_event(zdialog *zd, const char *event) // blur_background dialog event function
{ {
using namespace blur_BG_names; using namespace blur_background_names;
if (strmatch(event,"escape")) zd->zstat = -2; // escape key if (strmatch(event,"escape")) zd->zstat = -2; // escape key
if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open()
if (zd->zstat) if (zd->zstat)
{ {
if (zd->zstat == 1) // [apply] if (zd->zstat == 1) // [apply]
{ {
zd->zstat = 0; // keep dialog active zd->zstat = 0; // keep dialog active
skipping to change at line 2840 skipping to change at line 2820
if (incrad && ! sa_edgecalc_done) // if increasing blur radius, if (incrad && ! sa_edgecalc_done) // if increasing blur radius,
sa_edgecalc(); // calc. area edge distances sa_edgecalc(); // calc. area edge distances
sa_show(0,0); sa_show(0,0);
edit_reset(); edit_reset();
thread_signal(); thread_signal();
return 1; return 1;
} }
else if (zd->zstat == 2) { else if (zd->zstat == 2) {
edit_done(0); // [ OK ]
if (zd_sela) zdialog_send_event(zd_sela,"done"); // kill select area dialog if (zd_sela) zdialog_send_event(zd_sela,"done"); // kill select area dialog
if (conrad) edit_addhist("rad:%d",conrad);
if (incrad) edit_addhist("rad:%d-%d",minbrad,maxbrad);
edit_done(0);
} }
else { else {
Fcancel = 1; // kill threads Fcancel = 1; // kill threads
edit_cancel(0); // [cancel] or [x], discard edit edit_cancel(0); // [cancel] or [x], discard edit
if (zd_sela) zdialog_send_event(zd_sela,"done"); if (zd_sela) zdialog_send_event(zd_sela,"done");
} }
return 1; return 1;
} }
skipping to change at line 2868 skipping to change at line 2850
zdialog_fetch(zd,"conrad",conrad); zdialog_fetch(zd,"conrad",conrad);
zdialog_fetch(zd,"incrad",incrad); zdialog_fetch(zd,"incrad",incrad);
zdialog_fetch(zd,"conbrad",conbrad); zdialog_fetch(zd,"conbrad",conbrad);
zdialog_fetch(zd,"minbrad",minbrad); zdialog_fetch(zd,"minbrad",minbrad);
zdialog_fetch(zd,"maxbrad",maxbrad); zdialog_fetch(zd,"maxbrad",maxbrad);
return 1; return 1;
} }
// thread function - multiple working threads to update image // thread function
void * blur_BG_thread(void *) void * blur_background_thread(void *)
{ {
using namespace blur_BG_names; using namespace blur_background_names;
void * blur_BG_wthread(void *arg); // worker thread void * blur_background_wthread(void *arg); // worker thread
int ii, dist; int ii, dist;
if (incrad && sa_edgecalc_done) { // if increasing blur radius, if (incrad && sa_edgecalc_done) { // if increasing blur radius,
maxdist = 0; // get max. area edge distance maxdist = 0; // get max. area edge distance
for (ii = 0; ii < Eww * Ehh; ii++) { for (ii = 0; ii < Eww * Ehh; ii++) {
dist = sa_pixmap[ii]; dist = sa_pixmap[ii];
if (dist > maxdist) maxdist = dist; if (dist > maxdist) maxdist = dist;
} }
} }
progress_reset(sa_Npixel); // initz. progress counter progress_reset(sa_Npixel); // initz. progress counter
do_wthreads(blur_BG_wthread,NWT); // worker threads do_wthreads(blur_background_wthread,NWT); // worker threads
progress_reset(0); progress_reset(0);
CEF->Fmods++; // image modified CEF->Fmods++; // image modified
CEF->Fsaved = 0; // not saved CEF->Fsaved = 0; // not saved
Fpaint2(); // update window Fpaint2(); // update window
return 0; return 0;
} }
void * blur_BG_wthread(void *arg) // worker thread function
// worker thread function
void * blur_background_wthread(void *arg)
{ {
using namespace blur_BG_names; using namespace blur_background_names;
int index = *((int *) (arg)); int index = *((int *) (arg));
int ii, rad = 0, dist, npix; int ii, rad = 0, dist, npix;
int px, py, qx, qy; int px, py, qx, qy;
float *pix1, *pix3; float *pix1, *pix3;
float red, green, blue, F; float red, green, blue, F;
for (py = index; py < Ehh; py += NWT) // loop all image pixels for (py = index; py < Ehh; py += NWT) // loop all image pixels
for (px = 0; px < Eww; px++) for (px = 0; px < Eww; px++)
{ {
skipping to change at line 2959 skipping to change at line 2943
pix3[2] = blue; pix3[2] = blue;
progress_add(index,1); // count pixels done progress_add(index,1); // count pixels done
} }
return 0; // exit thread return 0; // exit thread
} }
/******************************************************************************* */ /******************************************************************************* */
// add motion blur to an image // add motion blur to selected areas in an image
namespace blur_motionblur_names namespace blur_motion_names
{ {
editfunc EFaddmotionblur; editfunc EFblur;
int Eww, Ehh; // image dimensions int Eww, Ehh; // image dimensions
int span; // blur span, pixels int span; // blur span, pixels
int angle; // blur angle, 0-180 deg. int angle; // blur angle, 0-180 deg.
PXM *tempxm;
} }
// menu function void m_blur_motion(GtkWidget *, const char *menu)
void m_blur_motionblur(GtkWidget *, const char *menu)
{ {
using namespace blur_motionblur_names; using namespace blur_motion_names;
void blur_motionblur_mousefunc(); void blur_motion_mousefunc();
int blur_motionblur_dialog_event(zdialog* zd, const char *event); int blur_motion_dialog_event(zdialog* zd, const char *event);
void * blur_motionblur_thread(void *); void * blur_motion_thread(void *);
cchar *hintmess = "Drag mouse across image \n" cchar *hintmess = "Drag mouse across image \n"
" to indicate blur direction"; " to indicate blur direction";
Plog(1,"m_blur_motionblur \n"); Plog(1,"m_blur_motion \n");
EFaddmotionblur.menufunc = m_blur_motionblur; EFblur.menufunc = m_blur_motion;
EFaddmotionblur.menuname = "Add Motion Blur"; EFblur.menuname = "Blur Motion";
EFaddmotionblur.Farea = 2; EFblur.Farea = 2;
// select area usable // select area usable
EFaddmotionblur.threadfunc = blur_motionblur_thread; EFblur.threadfunc = blur_motion_thread;
// thread function // thread function
EFaddmotionblur.mousefunc = blur_motionblur_mousefunc; EFblur.mousefunc = blur_motion_mousefunc;
// mouse function // mouse function
if (! edit_setup(EFaddmotionblur)) return; // setup edit if (! edit_setup(EFblur)) return; // setup edit
Eww = E3pxm->ww; // image dimensions Eww = E3pxm->ww; // image dimensions
Ehh = E3pxm->hh; Ehh = E3pxm->hh;
/*** /***
___________________________________ ___________________________________
| Add Motion Blur | | Add Motion Blur |
| | | |
| Drag mouse across image | | Drag mouse across image |
| to indicate blur direction | | to indicate blur direction |
skipping to change at line 3026 skipping to change at line 3009
zdialog_add_widget(zd,"zspin","span","hbspan","0|50|1|0"); zdialog_add_widget(zd,"zspin","span","hbspan","0|50|1|0");
zdialog_add_widget(zd,"label","space","hbspan",0,"space=20"); zdialog_add_widget(zd,"label","space","hbspan",0,"space=20");
zdialog_add_widget(zd,"hbox","hbangle","dialog"); zdialog_add_widget(zd,"hbox","hbangle","dialog");
zdialog_add_widget(zd,"label","labangle","hbangle","Blur Angle (degrees)","sp ace=5"); zdialog_add_widget(zd,"label","labangle","hbangle","Blur Angle (degrees)","sp ace=5");
zdialog_add_widget(zd,"zspin","angle","hbangle","0|180|1|0"); zdialog_add_widget(zd,"zspin","angle","hbangle","0|180|1|0");
zdialog_add_widget(zd,"label","space","hbangle",0,"space=20"); zdialog_add_widget(zd,"label","space","hbangle",0,"space=20");
angle = 0; angle = 0;
span = 0; span = 0;
takeMouse(blur_motionblur_mousefunc,dragcursor); takeMouse(blur_motion_mousefunc,dragcursor);
// connect mouse // connect mouse
zdialog_run(zd,blur_motion_dialog_event);
// run dialog - parallel
if (sa_stat != 3) m_select_area(0,0);
// start select area dialog 22.50
zdialog_run(zd,blur_motionblur_dialog_event); // run dialog - parallel
return; return;
} }
// dialog event and completion function // dialog event and completion function
int blur_motionblur_dialog_event(zdialog *zd, const char *event) int blur_motion_dialog_event(zdialog *zd, const char *event)
{ {
using namespace blur_motionblur_names; using namespace blur_motion_names;
void blur_motionblur_mousefunc(); void blur_motion_mousefunc();
void blur_motionblur_update(); void blur_motion_update();
if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open()
if (strmatch(event,"focus")) if (strmatch(event,"focus"))
takeMouse(blur_motionblur_mousefunc,dragcursor); // connect mouse takeMouse(blur_motion_mousefunc,dragcursor); // connect mouse
if (zd->zstat) if (zd->zstat)
{ {
if (zd->zstat == 1) { // [reset] if (zd->zstat == 1) { // [reset]
zd->zstat = 0; // keep dialog active zd->zstat = 0; // keep dialog active
edit_reset(); edit_reset();
} }
else if (zd->zstat == 2) { // [apply] else if (zd->zstat == 2) { // [apply]
zd->zstat = 0; // keep dialog active zd->zstat = 0; // keep dialog active
blur_motionblur_update(); blur_motion_update();
}
else if (zd->zstat == 3) {
// [ OK ] commit edit
edit_addhist("span:%d angle:%d",span,angle);
edit_done(0);
} }
else if (zd->zstat == 3) edit_done(0); // [ OK ] commit edit
else edit_cancel(0); // [cancel] or [x] discard edit else edit_cancel(0); // [cancel] or [x] discard edit
Fpaint2(); Fpaint2();
return 1; return 1;
} }
if (strmatch(event,"span")) // span input if (strmatch(event,"span")) // span input
zdialog_fetch(zd,"span",span); zdialog_fetch(zd,"span",span);
if (strmatch(event,"angle")) { // angle input if (strmatch(event,"angle")) { // angle input
skipping to change at line 3084 skipping to change at line 3074
} }
if (angle >= 180) { // 180 --> 0 if (angle >= 180) { // 180 --> 0
angle = angle - 180; angle = angle - 180;
zdialog_stuff(zd,"angle",angle); zdialog_stuff(zd,"angle",angle);
} }
} }
return 1; return 1;
} }
// finish addition of motion blur // mouse function - capture mouse drag direction to set image rotate angle
void blur_motion_mousefunc()
{
using namespace blur_motion_names;
int dx, dy;
float R;
if (! Mxdrag && ! Mydrag) return;
dx = Mxdrag - Mxdown;
// drag vector
dy = Mydrag - Mydown;
Mxdrag = Mydrag = 0;
R = sqrtf(dx*dx + dy*dy);
// get angle of drag
angle = RAD * acosf(dx/R);
if (dy > 0) angle = 180 - angle;
// top quadrant only, 0-180 deg.
if (angle == 180) angle = 0;
if (CEF) zdialog_stuff(CEF->zd,"angle",angle);
return;
}
// add motion blur with specified span and angle
void blur_motionblur_update() void blur_motion_update() // 22.50
{ {
using namespace blur_motionblur_names; using namespace blur_motion_names;
int rotate, mww, mhh; int rotate, mww, mhh;
int ii, px, py, dist = 0; int ii, px, py, dist = 0;
float *pix1, *pix3, f1, f2; float *pix1, *pix3, f1, f2;
int pcc = E1pxm->nc * sizeof(float); int pcc = E1pxm->nc * sizeof(float);
if (angle <= 90) rotate = angle; // avoid upside-down result if (angle <= 90) rotate = angle; // avoid upside-down result
else rotate = angle - 180; else rotate = angle - 180;
PXM_free(E3pxm); E9pxm = PXM_rotate(E1pxm,rotate);
// rotate image so blur is horizontal // rotate image so blur is horizontal
E3pxm = PXM_rotate(E1pxm,rotate); thread_signal();
// add blur to E9
E9pxm = PXM_copy(E3pxm);
// create input image for E3 output
thread_signal();
// add motion blur
thread_wait(); thread_wait();
tempxm = PXM_rotate(E9pxm,-rotate); // unrotate E9
PXM_free(E9pxm); PXM_free(E9pxm);
E9pxm = tempxm;
E9pxm = PXM_rotate(E3pxm,-rotate); mww = (E9pxm->ww - Eww) / 2;
// un-rotate // margins added by 2 rotates
mww = (E9pxm->ww - Eww) / 2; mhh = (E9pxm->hh - Ehh) / 2;
mhh = (E9pxm->hh - Ehh) / 2;
// cut-off margins from rotate PXM_copy_area(E9pxm,E3pxm,mww,mhh,0,0,Eww,Ehh);
PXM_free(E3pxm); // copy E9 (-margins) into E3
E3pxm = PXM_copy_area(E9pxm,mww,mhh,Eww,Ehh);
PXM_free(E9pxm); PXM_free(E9pxm);
E9pxm = 0; E9pxm = 0;
if (sa_stat == 3) if (sa_stat == 3) // select area present
{ {
for (py = 0; py < Ehh; py++) // replace output with input image for (py = 0; py < Ehh; py++) // replace output with input image
for (px = 0; px < Ehh; px++) // in areas outside the select area for (px = 0; px < Eww; px++) // in areas outside the select area
{ {
pix1 = PXMpix(E1pxm,px,py); pix1 = PXMpix(E1pxm,px,py);
pix3 = PXMpix(E3pxm,px,py); pix3 = PXMpix(E3pxm,px,py);
ii = py * Eww + px; ii = py * Eww + px;
dist = sa_pixmap[ii]; dist = sa_pixmap[ii];
if (dist) { if (dist) {
f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth f1 = sa_blendfunc(dist); // blend changes over sa_blendwidth
f2 = 1.0 - f1; f2 = 1.0 - f1;
pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[0] = f1 * pix3[0] + f2 * pix1[0];
pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[1] = f1 * pix3[1] + f2 * pix1[1];
pix3[2] = f1 * pix3[2] + f2 * pix1[2]; pix3[2] = f1 * pix3[2] + f2 * pix1[2];
} }
else memcpy(pix3,pix1,pcc); else memcpy(pix3,pix1,pcc);
} }
}
return; return;
}
// thread function - multiple working threads to update image
void * blur_motion_thread(void *)
{
using namespace blur_motion_names;
void * blur_motion_wthread(void *arg);
// worker thread
tempxm = PXM_copy(E9pxm);
progress_reset(tempxm->hh);
// progress monitor
do_wthreads(blur_motion_wthread,NWT);
// do worker threads
progress_reset(0);
PXM_free(tempxm);
CEF->Fmods++;
// image modified
CEF->Fsaved = 0;
// not saved
Fpaint2();
// update window
return 0;
}
// add motion blur worker thread function
// span = N: each blurred pixel is average of N original pixels
void * blur_motion_wthread(void *arg)
// 22.50
{
using namespace blur_motion_names;
int index = *((int *) arg);
int px, py, ii;
int span2 = (span+1) / 2, span3 = 2 * span2 + 1;
// use even number
float *pix1, *pix9;
float R, G, B;
int Tww = tempxm->ww;
// input image dimensions
int Thh = tempxm->hh;
for (py = index; py < Thh; py += NWT)
// loop all image pixels
{
progress_add(index,1);
// count progress
for (px = span2+1; px < Tww-span2; px++)
{
R = G = B = 0;
for (ii = -span2; ii <= span2; ii++)
// sum input pixels
{
pix1 = PXMpix(tempxm,px-ii,py);
R += pix1[0];
G += pix1[1];
B += pix1[2];
}
R = R / span3;
// average input pixels
G = G / span3;
B = B / span3;
pix9 = PXMpix(E9pxm,px,py);
// output pixel (blurred)
pix9[0] = R;
pix9[1] = G;
pix9[2] = B;
}
} }
Eww = E3pxm->ww; return 0;
// cut-off fuzzy margins // exit thread
}
/*******************************************************************************
*/
// remove blur from camera motion - Richardson-Lucy method
namespace fix_motionblur_names
{
editfunc EFfixmotionblur;
int Eww, Ehh;
// image dimensions
int span;
// blur span, pixels
int angle;
// blur angle, 0-180 deg.
int iter;
// algorithm iterations
int supring;
// suppress ringing
PXM *E2pxm;
}
// menu function
void m_fix_motionblur(GtkWidget *, const char *menu)
{
using namespace fix_motionblur_names;
void fix_motionblur_mousefunc();
int fix_motionblur_dialog_event(zdialog* zd, const char *event);
void * fix_motionblur_thread(void *);
cchar *hintmess = "Shift + drag mouse across image \n"
" to indicate blur direction";
Plog(1,"m_fix_motionblur \n");
EFfixmotionblur.menufunc = m_fix_motionblur;
EFfixmotionblur.menuname = "Fix Motion Blur";
EFfixmotionblur.Farea = 2;
// select area usable
EFfixmotionblur.threadfunc = fix_motionblur_thread;
// thread function
EFfixmotionblur.mousefunc = fix_motionblur_mousefunc;
// mouse function
if (! edit_setup(EFfixmotionblur)) return;
// setup edit
Eww = E3pxm->ww;
// image dimensions
Ehh = E3pxm->hh; Ehh = E3pxm->hh;
E9pxm = PXM_copy_area(E3pxm,span,span,Eww-span,Ehh-span);
PXM_free(E3pxm);
E3pxm = E9pxm;
E9pxm = 0;
sa_clear(); /***
// select area no longer valid ___________________________________
| Fix Motion Blur |
| |
| Shift + drag mouse across image |
| to indicate blur direction |
| |
| Blur Span (pixels) [___] |
| Blur Angle (degrees) [___] |
| Algorithm Iterations [___] |
| Suppress Ringing [___] |
| |
| [Reset] [Apply] [OK] [Cancel] |
|___________________________________|
***/
zdialog *zd = zdialog_new("Fix Motion Blur",Mwin,"Reset","Apply","OK","Cancel
",null);
CEF->zd = zd;
zdialog_add_widget(zd,"label","labhint","dialog",hintmess,"space=5");
zdialog_add_widget(zd,"hbox","hb1","dialog");
zdialog_add_widget(zd,"label","space","hb1",0,"space=3");
zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=4|homog");
zdialog_add_widget(zd,"label","space","hb1",0,"space=8");
zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=4|homog");
zdialog_add_widget(zd,"label","space","hb1",0,"space=3");
zdialog_add_widget(zd,"label","labspan","vb1","Blur Span (pixels)");
zdialog_add_widget(zd,"label","labangle","vb1","Blur Angle (degrees)");
zdialog_add_widget(zd,"label","labiter","vb1","Algorithm Iterations");
zdialog_add_widget(zd,"label","labsup","vb1","Suppress Ringing");
zdialog_add_widget(zd,"zspin","span","vb2","0|40|1|0");
zdialog_add_widget(zd,"zspin","angle","vb2","-180|180|1|0");
zdialog_add_widget(zd,"zspin","iter","vb2","0|100|1|0");
zdialog_add_widget(zd,"zspin","supring","vb2","0|9|1|0");
zdialog_restore_inputs(zd);
zdialog_run(zd,fix_motionblur_dialog_event);
// run dialog - parallel
zdialog_send_event(zd,"init");
return; return;
} }
// dialog event and completion function
int fix_motionblur_dialog_event(zdialog *zd, const char *event)
{
using namespace fix_motionblur_names;
void fix_motionblur_mousefunc();
if (strmatch(event,"done")) zd->zstat = 3;
// from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 4;
// from f_open()
if (zd->zstat)
{
if (zd->zstat == 1) {
// [reset]
zd->zstat = 0;
// keep dialog active
edit_reset();
}
else if (zd->zstat == 2) {
// [apply]
zd->zstat = 0;
// keep dialog active
edit_reset();
zdialog_fetch(zd,"angle",angle);
// get all inputs
zdialog_fetch(zd,"span",span);
zdialog_fetch(zd,"iter",iter);
zdialog_fetch(zd,"supring",supring);
if (angle < 0) {
angle = 180 + angle;
// -1 --> 179
zdialog_stuff(zd,"angle",angle);
}
if (angle >= 180) {
// 180 --> 0
angle = angle - 180;
zdialog_stuff(zd,"angle",angle);
}
thread_signal();
// process
}
else if (zd->zstat == 3)
// [ OK ] commit edit
{
edit_addhist("span:%d angle:%d iter:%d supR:%d",
// edit params > edit hist
span,angle,iter,supring);
edit_done(0);
}
else edit_cancel(0);
// [cancel] or [x] discard edit
return 1;
}
if (strmatch(event,"focus")) {
takeMouse(fix_motionblur_mousefunc,0);
// connect mouse
return 1;
}
return 1;
}
// mouse function - capture mouse drag direction and set rotate angle // mouse function - capture mouse drag direction and set rotate angle
void blur_motionblur_mousefunc() void fix_motionblur_mousefunc()
{ {
using namespace blur_motionblur_names; using namespace fix_motionblur_names;
int dx, dy; int dx, dy;
float R; float R;
if (! KBshiftkey) {
takeMouse(fix_motionblur_mousefunc,0);
return;
}
takeMouse(fix_motionblur_mousefunc,dragcursor);
if (! Mxdrag && ! Mydrag) return; if (! Mxdrag && ! Mydrag) return;
dx = Mxdrag - Mxdown; // drag vector dx = Mxdrag - Mxdown; // drag vector
dy = Mydrag - Mydown; dy = Mydrag - Mydown;
Mxdrag = Mydrag = 0; Mxdrag = Mydrag = 0;
R = sqrtf(dx*dx + dy*dy); // get angle of drag R = sqrtf(dx*dx + dy*dy); // get angle of drag
angle = RAD * acosf(dx/R); angle = RAD * acosf(dx/R);
if (dy > 0) angle = 180 - angle; // top quadrant only, 0-180 deg. if (dy > 0) angle = 180 - angle; // top quadrant only, 0-180 deg.
if (angle == 180) angle = 0; if (angle == 180) angle = 0;
if (CEF) zdialog_stuff(CEF->zd,"angle",angle); if (CEF) zdialog_stuff(CEF->zd,"angle",angle);
return; return;
} }
// thread function - multiple working threads to update image // thread function
void * blur_motionblur_thread(void *) void * fix_motionblur_thread(void *)
{ {
using namespace blur_motionblur_names; using namespace fix_motionblur_names;
void * fix_motionblur_wthread0(void *arg);
// worker threads
void * fix_motionblur_wthread1(void *arg);
void * fix_motionblur_wthread2(void *arg);
float rotate;
int mww, mhh;
if (angle <= 90) rotate = angle;
// avoid upside-down result
else rotate = angle - 180;
PXM_free(E3pxm);
E3pxm = PXM_rotate(E1pxm,rotate);
// rotate to make blur angle 0
E9pxm = PXM_copy(E3pxm);
// E3 = E9 = rotated E1
void * blur_motionblur_wthread(void *arg); // worker thread do_wthreads(fix_motionblur_wthread0,NWT); // fill black margins from edge pixels
progress_reset(E3pxm->hh); // progress monitor progress_reset(E3pxm->hh); // progress monitor = rows
do_wthreads(blur_motionblur_wthread,NWT); // do worker threads do_wthreads(fix_motionblur_wthread1,NWT); // do worker thread
progress_reset(0); progress_reset(0);
PXM_free(E9pxm);
E9pxm = 0;
E2pxm = PXM_rotate(E3pxm,-rotate);
// un-rotate
mww = (E2pxm->ww - Eww) / 2;
mhh = (E2pxm->hh - Ehh) / 2;
// cut-off margins from rotate
PXM_free(E3pxm);
E3pxm = PXM_copy_area(E2pxm,mww,mhh,Eww,Ehh);
PXM_free(E2pxm);
E2pxm = 0;
if (sa_stat == 3)
// process select area
do_wthreads(fix_motionblur_wthread2,NWT);
CEF->Fmods++; // image modified CEF->Fmods++; // image modified
CEF->Fsaved = 0; // not saved CEF->Fsaved = 0; // not saved
Fpaint2(); // update window Fpaint2();
return 0; // not executed, stop warning return 0; // not executed, stop warning
} }
// add motion blur worker thread function // propagate image edge pixels into the black margins created from rotation
// span = N: each blurred pixel is average of N original pixels
void * blur_motionblur_wthread(void *arg) void * fix_motionblur_wthread0(void *arg)
{ {
using namespace blur_motionblur_names; using namespace fix_motionblur_names;
int index = *((int *) arg); int index = *((int *) arg);
int px, py, ii;
float *pix1, *pix3;
float R, G, B;
int Eww = E3pxm->ww; // rotated and larger image int Eww = E3pxm->ww; // rotated and larger image
int Ehh = E3pxm->hh; int Ehh = E3pxm->hh;
int px, py, pxL;
float *pix3, R, G, B;
for (py = index; py < Ehh; py += NWT) if (angle == 0) return 0;
// loop all image pixels
for (py = index; py < Ehh; py += NWT)
// loop image rows
{
for (px = 0; px < Eww; px++)
// loop columns
{
pix3 = PXMpix(E3pxm,px,py);
if (pix3[0] > 0 || pix3[1] > 0 || pix3[2] > 0) break;
// find first non-black pixel in row
}
pxL = px + 2;
// use next pixel
if (pxL < Eww) {
pix3 = PXMpix(E3pxm,pxL,py);
// get RGB values
R = pix3[0];
G = pix3[1];
B = pix3[2];
for (px = 0; px < pxL; px++) {
// fill black pixels with RGB
pix3 = PXMpix(E3pxm,px,py);
pix3[0] = R;
pix3[1] = G;
pix3[2] = B;
}
}
for (px = Eww-1; px > 0; px--)
// find last non-black pixel in row
{
pix3 = PXMpix(E3pxm,px,py);
if (pix3[0] > 0 || pix3[1] > 0 || pix3[2] > 0) break;
}
pxL = px - 2;
// use previous pixel
if (pxL > 0) {
pix3 = PXMpix(E3pxm,pxL,py);
// get RGB values
R = pix3[0];
G = pix3[1];
B = pix3[2];
for (px = pxL+1; px < Eww; px++) {
// fill black pixels with RGB
pix3 = PXMpix(E3pxm,px,py);
pix3[0] = R;
pix3[1] = G;
pix3[2] = B;
}
}
}
return 0;
}
// fix motion blur worker thread function
// E3 = rotated blurred image so that blur angle = 0
// span = N: each blurred pixel is average of N input pixels
void * fix_motionblur_wthread1(void *arg)
{
using namespace fix_motionblur_names;
void RLdecon(float *pixels, int Np, int Nd, int Nt);
int index = *((int *) arg);
int px, py, rgb;
int Eww = E3pxm->ww;
// rotated and larger image
int Ehh = E3pxm->hh;
int nc = E3pxm->nc;
int pcc = nc * sizeof(float);
float *pix2, *pix3, *RLval;
float *pix9, *pix9a, *pix9b;
float con, F1, F2;
if (span < 1) return 0;
RLval = (float *) zmalloc(Eww * sizeof(float),"motionblur-RL");
for (py = index; py < Ehh; py += NWT)
// loop image rows
{ {
progress_add(index,1); // count progress progress_add(index,1); // count progress
for (px = span+1; px < Eww; px++) for (rgb = 0; rgb < 2; rgb++) // loop RGB
{ {
pix3 = PXMpix(E3pxm,px,py); for (px = 0; px < Eww; px++)
// output pixel (blurred) // loop pixels in row
{
pix3 = PXMpix(E3pxm,px,py);
// one RGB value per pixel
RLval[px] = pix3[rgb];
}
R = G = B = 0; RLdecon(RLval,Eww,span,iter); // do R-L algorithm on pixel array
for (ii = 0; ii <= span; ii++) // sum input pixels for (px = 0; px < Eww; px++)
{ {
pix1 = PXMpix(E9pxm,px-ii,py); pix3 = PXMpix(E3pxm,px,py);
R += pix1[0]; // save revised RGB values
G += pix1[1]; pix3[rgb] = RLval[px];
B += pix1[2];
} }
}
for (px = Eww-1; px > span; px--)
// fix position shift
{
pix3 = PXMpix(E3pxm,px,py);
pix2 = PXMpix(E3pxm,px-span/2,py);
memcpy(pix3,pix2,pcc);
}
R = R / (span + 1); for (px = 1; px < Eww-1; px++)
// average input pixels // suppress ringing
G = G / (span + 1); for (rgb = 0; rgb < 3; rgb++)
B = B / (span + 1); {
pix9 = PXMpix(E9pxm,px,py);
pix3[0] = R; // blurred input image
// output RGB pix9a = pix9 - nc;
pix3[1] = G; pix9b = pix9 + nc;
pix3[2] = B; con = span * 0.0039 * fabsf(pix9a[rgb] - pix9b[rgb]);
// 0 - 1 = max. contrast
if (con > 1.0) con = 1.0;
F1 = 0.1 * supring;
// 0 - 1 = max. suppression
F2 = F1 * (1.0 - con);
// min. contrast --> max. suppression
pix3 = PXMpix(E3pxm,px,py);
// sharpened output image
pix3[rgb] = F2 * pix9[rgb] + (1.0 - F2) * pix3[rgb];
} }
} }
zfree(RLval);
return 0; // exit thread return 0; // exit thread
} }
// fix motion blur worker thread function
// replace output with input image in areas outside the select area
void * fix_motionblur_wthread2(void *arg)
{
using namespace fix_motionblur_names;
int index = *((int *) arg);
int px, py, ii, dist;
float *pix1, *pix3, f1, f2;
int pcc = E1pxm->nc * sizeof(float);
for (py = index; py < Ehh; py += NWT)
for (px = 0; px < Eww; px++)
{
pix1 = PXMpix(E1pxm,px,py);
pix3 = PXMpix(E3pxm,px,py);
ii = py * Eww + px;
dist = sa_pixmap[ii];
if (dist) {
f1 = sa_blendfunc(dist);
// blend changes over sa_blendwidth
f2 = 1.0 - f1;
pix3[0] = f1 * pix3[0] + f2 * pix1[0];
pix3[1] = f1 * pix3[1] + f2 * pix1[1];
pix3[2] = f1 * pix3[2] + f2 * pix1[2];
}
else memcpy(pix3,pix1,pcc);
}
return 0;
}
/***
Richardson-Lucy deconvolution for special case: linear uniform blur.
pixels[] row of motion blurred pixels in blur direction (one RGB cha
nnel)
Np pixel row length
Nd blur span: Nd pixels contribute equally to each blurred pix
el
(inclusive: Nd = 1 means no blur)
Nt algorithm iterations
Variable names follow Wikipedia article on Richardson-Lucy deconvolution.
***/
void RLdecon(float *pixels, int Np, int Nd, int Nt)
{
int ii, jj, tt;
float Pij = 1.0 / Nd;
// pixel contribution factor
float *Ci = (float *) malloc(Np * sizeof(float));
// Ci factor per pixel
float *Uj = (float *) malloc(Np * sizeof(float));
// old/new estimated value per pixel
for (jj = 0; jj < Np; jj++)
// initial Uj per pixel jj
Uj[jj] = pixels[jj];
for (tt = 0; tt < Nt; tt++)
// algorithm iterations
{
for (ii = Nd; ii < Np-Nd; ii++)
// compute Ci per pixel ii
{
Ci[ii] = 0;
for (jj = 0; jj < Nd; jj++)
// Nd pixels contributing to ii
Ci[ii] += Pij * Uj[ii-jj];
if (Ci[ii] <= 0) Ci[ii] = 1;
}
for (jj = Nd; jj < Np-Nd-Nd; jj++)
// compute new Uj per pixel jj
{
float S = 0;
for (ii = 0; ii < Nd; ii++)
// Nd pixels contributing to pixel jj
S += pixels[jj+ii] / Ci[jj+ii] * Pij;
Uj[jj] = Uj[jj] * S;
// new Uj replaces old Uj
}
}
for (jj = 0; jj < Np; jj++)
{
pixels[jj] = Uj[jj];
if (pixels[jj] < 0) pixels[jj] = 0;
if (pixels[jj] > 255) pixels[jj] = 255;
}
free(Ci);
free(Uj);
return;
}
/******************************************************************************* */ /******************************************************************************* */
// image noise reduction // image noise reduction
namespace denoise_names namespace denoise_names
{ {
enum dn_method { voodoo, chroma, anneal, flatten, median, SNN } enum dn_method { voodoo, chroma, anneal, flatten, median, SNN }
dn_method; dn_method;
int noise_histogram[3][256]; int noise_histogram[3][256];
int dn_radius, dn_thresh; int dn_radius, dn_thresh;
float dn_darkareas; float dn_darkareas;
int Tradius, Tthresh; int Tradius, Tthresh;
zdialog *zd_denoise_measure; zdialog *zd_denoise_measure;
cchar *mformat = " mean RGB: %5.0f %5.0f %5.0f "; cchar *mformat = " mean RGB: %5.0f %5.0f %5.0f ";
cchar *nformat = " mean noise: %5.2f %5.2f %5.2f "; cchar *nformat = " mean noise: %5.2f %5.2f %5.2f ";
int Eww, Ehh; // image dimensions int Eww, Ehh; // image dimensions
editfunc EFdenoise; editfunc EFdenoise;
char edit_hist[100]; char edit_hist[200];
GtkWidget *denoise_measure_drawwin; GtkWidget *denoise_measure_drawwin;
} }
// menu function // menu function
void m_denoise(GtkWidget *, cchar *menu) // overhauled for paint edits void m_denoise(GtkWidget *, cchar *menu) // overhauled for paint edits
{ {
using namespace denoise_names; using namespace denoise_names;
void denoise_characterize(); void denoise_characterize();
skipping to change at line 3537 skipping to change at line 3958
return 1; return 1;
} }
if (zd->zstat == 3) { // [reset] if (zd->zstat == 3) { // [reset]
edit_undo(); // undo edits edit_undo(); // undo edits
zd->zstat = 0; // keep dialog active zd->zstat = 0; // keep dialog active
return 1; return 1;
} }
if (zd->zstat == 4) { // [ OK ] commit edit if (zd->zstat == 4) { // [ OK ] commit edit
strncpy0(EFdenoise.edit_hist,edit_hist,100); // edit params > edit hist edit_addhist(edit_hist); // edit params > edit hist
edit_done(0); edit_done(0);
} }
else edit_cancel(0); // [cancel] or [x] discard edit else edit_cancel(0); // [cancel] or [x] discard edit
if (zd_denoise_measure) { // kill measure dialog if (zd_denoise_measure) { // kill measure dialog
freeMouse(); freeMouse();
zdialog_free(zd_denoise_measure); zdialog_free(zd_denoise_measure);
zd_denoise_measure = 0; zd_denoise_measure = 0;
} }
skipping to change at line 3596 skipping to change at line 4017
Tradius = 2; Tradius = 2;
Tthresh = 0.7 * dn_thresh; Tthresh = 0.7 * dn_thresh;
get_edit_pixels_init(NWT,Tradius); get_edit_pixels_init(NWT,Tradius);
do_wthreads(denoise_anneal_wthread,NWT); // anneal 2x do_wthreads(denoise_anneal_wthread,NWT); // anneal 2x
PXM_copy(E3pxm,E1pxm); PXM_copy(E3pxm,E1pxm);
get_edit_pixels_init(NWT,Tradius); get_edit_pixels_init(NWT,Tradius);
do_wthreads(denoise_anneal_wthread,NWT); do_wthreads(denoise_anneal_wthread,NWT);
PXM_copy(E0pxm,E1pxm); // restore normal E1 PXM_copy(E0pxm,E1pxm); // restore normal E1
snprintf(edit_hist,100,"voodoo thresh:%d",dn_thresh); // exif edit hist snprintf(edit_hist,200,"voodoo thresh:%d",dn_thresh); // exif edit hist
} }
Tradius = dn_radius; // keep dialog parameters constant 22.31 Tradius = dn_radius; // keep dialog parameters constant 22.31
Tthresh = dn_thresh; // during thread execution (bugfix) Tthresh = dn_thresh; // during thread execution (bugfix)
if (dn_method == chroma) if (dn_method == chroma)
{ {
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
if (! E8pxm) E8pxm = PXM_make(Eww,Ehh,3); if (! E8pxm) E8pxm = PXM_make(Eww,Ehh,3);
if (! E8pxm) quitxx(); if (! E8pxm) quitxx();
get_edit_pixels_init(NWT,0); get_edit_pixels_init(NWT,0);
do_wthreads(denoise_chroma_wthread1,NWT); do_wthreads(denoise_chroma_wthread1,NWT);
get_edit_pixels_init(NWT,Tradius); // initz. pixel loop get_edit_pixels_init(NWT,Tradius); // initz. pixel loop
do_wthreads(denoise_chroma_wthread2,NWT); // chroma denoise do_wthreads(denoise_chroma_wthread2,NWT); // chroma denoise
snprintf(edit_hist,100,"chroma rad:%d thresh:%d",Tradius,Tthresh); // edit params > edit hist snprintf(edit_hist,200,"chroma rad:%d thresh:%d",Tradius,Tthresh); // edit params > edit hist
} }
if (dn_method == anneal) if (dn_method == anneal)
{ {
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,Tradius); // initz. pixel loop get_edit_pixels_init(NWT,Tradius); // initz. pixel loop
do_wthreads(denoise_anneal_wthread,NWT); // anneal denoise do_wthreads(denoise_anneal_wthread,NWT); // anneal denoise
snprintf(edit_hist,100,"anneal rad:%d thresh:%d",Tradius,Tthresh); // edit params > edit hist snprintf(edit_hist,200,"anneal rad:%d thresh:%d",Tradius,Tthresh); // edit params > edit hist
} }
if (dn_method == flatten) if (dn_method == flatten)
{ {
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,Tradius); // initz. pixel loop get_edit_pixels_init(NWT,Tradius); // initz. pixel loop
do_wthreads(denoise_flatten_wthread,NWT); // flatten denoise do_wthreads(denoise_flatten_wthread,NWT); // flatten denoise
snprintf(edit_hist,100,"flatten rad:%d thresh:%d",Tradius,Tthresh); // exif edit params > edit hist snprintf(edit_hist,200,"flatten rad:%d thresh:%d",Tradius,Tthresh); // exif edit params > edit hist
} }
if (dn_method == median) if (dn_method == median)
{ {
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,Tradius); // initz. pixel loop get_edit_pixels_init(NWT,Tradius); // initz. pixel loop
do_wthreads(denoise_median_wthread,NWT); // median denoise do_wthreads(denoise_median_wthread,NWT); // median denoise
snprintf(edit_hist,100,"median rad:%d thresh:%d",Tradius,Tthresh); // exif edit params > edit hist snprintf(edit_hist,200,"median rad:%d thresh:%d",Tradius,Tthresh); // exif edit params > edit hist
} }
if (dn_method == SNN) // 22.18 if (dn_method == SNN) // 22.18
{ {
if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter if (sa_stat == 3) progress_reset(sa_Npixel); // initz. progress counter
else progress_reset(Eww * Ehh); else progress_reset(Eww * Ehh);
get_edit_pixels_init(NWT,Tradius); // initz. pixel loop get_edit_pixels_init(NWT,Tradius); // initz. pixel loop
do_wthreads(denoise_SNN_wthread,NWT); // SNN denoise do_wthreads(denoise_SNN_wthread,NWT); // SNN denoise
snprintf(edit_hist,100,"SNN rad:%d",Tradius); // exif edit params > edit hist snprintf(edit_hist,200,"SNN rad:%d",Tradius); // exif edit params > edit hist
} }
progress_reset(0); progress_reset(0);
if (dn_darkareas < 256) // revert brighter areas if (dn_darkareas < 256) // revert brighter areas
{ {
get_edit_pixels_init(1,Tradius); // initz. pixel loop get_edit_pixels_init(1,Tradius); // initz. pixel loop
while (true) while (true)
{ {
skipping to change at line 6061 skipping to change at line 6482
zdialog_stuff(zd,"GreenCon",0); zdialog_stuff(zd,"GreenCon",0);
zdialog_stuff(zd,"BlueCon",0); zdialog_stuff(zd,"BlueCon",0);
edit_reset(); edit_reset();
return 1; return 1;
} }
else if (zd->zstat == 2) { // done else if (zd->zstat == 2) { // done
edit_fullsize(); // get full size image edit_fullsize(); // get full size image
Eww = E3pxm->ww; Eww = E3pxm->ww;
Ehh = E3pxm->hh; Ehh = E3pxm->hh;
thread_signal(); thread_signal();
edit_addhist("+Br:%.3f +R:%.3f +G:%.3f +B:%.3f " // edit params > edit hist edit_addhist("+Brite:%.3f +R:%.3f +G:%.3f +B:%.3f " // edit params > edit hist
"Con:%.3f R:%.3f G:%.3f B:%.3f", "Con:%.3f R:%.3f G:%.3f B:%.3f",
inputs[0],inputs[1],inputs[2],inputs[3], inputs[0],inputs[1],inputs[2],inputs[3],
inputs[4],inputs[5],inputs[6],inputs[7]); inputs[4],inputs[5],inputs[6],inputs[7]);
edit_done(0); // commit edit edit_done(0); // commit edit
return 1; return 1;
} }
else { else {
edit_cancel(0); // discard edit edit_cancel(0); // discard edit
return 1; return 1;
} }
skipping to change at line 7040 skipping to change at line 7461
cmsCloseProfile(cmsprof2); cmsCloseProfile(cmsprof2);
zadd_locked(Ffuncbusy,-1); zadd_locked(Ffuncbusy,-1);
CEF->Fmods++; // image is modified CEF->Fmods++; // image is modified
CEF->Fsaved = 0; CEF->Fsaved = 0;
Fpaint2(); // update window image Fpaint2(); // update window image
return 1; return 1;
} }
/*******************************************************************************
*
Vignette function
1. Change the brightness from center to edge using a curve.
2. Change the color from center to edge using a color and a curve.
(the pixel varies between original RGB and selected color)
3. Mouse click or drag on image sets a new vignette center.
********************************************************************************
*/
void vign_mousefunc();
editfunc EFvignette;
uint8 vignette_RGB[3] = { 0, 0, 255 };
int vignette_spc;
float vign_cx, vign_cy;
float vign_rad;
void m_vignette(GtkWidget *, cchar *menu)
{
int Vign_dialog_event(zdialog *zd, cchar *event);
void Vign_curvedit(int);
void * Vign_thread(void *);
cchar *title = "Vignette";
F1_help_topic = "vignette";
Plog(1,"m_vignette \n");
EFvignette.menuname = "Vignette";
EFvignette.Farea = 2;
// select area usable
EFvignette.FprevReq = 1;
// use preview image
EFvignette.threadfunc = Vign_thread;
// thread function
EFvignette.mousefunc = vign_mousefunc;
// mouse function
if (! edit_setup(EFvignette)) return;
// setup edit
/***
___________________________________
| _______________________________ |
| | | |
| | | |
| | curve drawing area | |
| | | |
| | | |
| |_______________________________| |
| center edge |
| |
| (o) Brightness (o) Color [___] |
| Curve File: [ Open ] [ Save ] |
| |
| [ OK ] [Cancel] |
|___________________________________|
***/
zdialog *zd = zdialog_new(title,Mwin,"OK","Cancel",null);
EFvignette.zd = zd;
zdialog_add_widget(zd,"frame","frame","dialog",0,"expand");
zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3");
zdialog_add_widget(zd,"label","labcenter","hb1","Center","space=4");
zdialog_add_widget(zd,"label","space","hb1",0,"expand");
zdialog_add_widget(zd,"label","labedge","hb1","Edge","space=5");
zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3");
zdialog_add_widget(zd,"radio","RBbrite","hb2","Brightness","space=5");
zdialog_add_widget(zd,"radio","RBcolor","hb2","Color","space=5");
zdialog_add_widget(zd,"colorbutt","color","hb2","0|0|255");
zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5");
zdialog_add_widget(zd,"label","labcurve","hb3","Curve File","space=5");
zdialog_add_widget(zd,"button","load","hb3","Open","space=5");
zdialog_add_widget(zd,"button","save","hb3","Save","space=5");
vignette_RGB[0] = vignette_RGB[1] = 0;
// initial color = blue
vignette_RGB[2] = 255;
vign_cx = E3pxm->ww / 2;
// initial vignette center
vign_cy = E3pxm->hh / 2;
vign_rad = vign_cx * vign_cx + vign_cy * vign_cy;
// radius = distance to corners
vign_rad = sqrtf(vign_rad);
zdialog_stuff(zd,"RBbrite",1);
// default curve = brightness
GtkWidget *frame = zdialog_gtkwidget(zd,"frame");
// set up curve edit
spldat *sd = splcurve_init(frame,Vign_curvedit);
EFvignette.sd = sd;
sd->Nspc = 2;
// 2 curves
sd->vert[0] = 0;
// curve 0 = brightness curve
sd->nap[0] = 2;
sd->apx[0][0] = 0.01;
sd->apy[0][0] = 0.5;
sd->apx[0][1] = 0.99;
sd->apy[0][1] = 0.5;
splcurve_generate(sd,0);
sd->vert[1] = 0;
// curve 1 = color curve
sd->nap[1] = 2;
sd->apx[1][0] = 0.01;
sd->apy[1][0] = 0.01;
sd->apx[1][1] = 0.99;
sd->apy[1][1] = 0.01;
splcurve_generate(sd,1);
vignette_spc = 0;
// initial curve = brightness
sd->fact[0] = 1;
sd->fact[1] = 0;
zdialog_run(zd,Vign_dialog_event,"save");
// run dialog - parallel
takeMouse(vign_mousefunc,dragcursor);
// connect mouse function
return;
}
// dialog event and completion callback function
int Vign_dialog_event(zdialog *zd, cchar *event)
{
void Vign_curvedit(int);
spldat *sd = EFvignette.sd;
int ii;
char color[20];
char *file, *pp;
cchar *ppc;
FILE *fid;
if (strmatch(event,"escape")) zd->zstat = -2;
// escape key
if (strmatch(event,"done")) zd->zstat = 1;
// from edit_setup() or f_save()
if (strmatch(event,"cancel")) zd->zstat = 2;
// from f_open()
if (strmatch(event,"fullsize")) {
// from select area
edit_fullsize();
thread_signal();
return 1;
}
if (zd->zstat)
{
if (zd->zstat == 1) {
// done
thread_wait();
// insure thread done
float R = 1.0 * E0pxm->ww / E3pxm->ww;
vign_cx = R * vign_cx;
// scale geometries to full size
vign_cy = R * vign_cy;
vign_rad = R * vign_rad;
edit_fullsize();
// get full size image
thread_signal();
edit_done(0);
// commit edit
}
else edit_cancel(0);
// discard edit
return 1;
}
if (strmatch(event,"focus"))
// toggle mouse capture
takeMouse(vign_mousefunc,dragcursor);
// connect mouse function
if (strmatchN(event,"RB",2)) {
// new choice of curve
sd->fact[0] = sd->fact[1] = 0;
ii = strmatchV(event,"RBbrite","RBcolor",null);
vignette_spc = ii = ii - 1;
sd->fact[ii] = 1;
// active curve
splcurve_generate(sd,ii);
// regenerate curve
gtk_widget_queue_draw(sd->drawarea);
// draw curve
thread_signal();
}
if (strmatch(event,"blendwidth")) thread_signal();
if (strmatch(event,"color")) {
// change color
zdialog_fetch(zd,"color",color,19);
// get color from color wheel
ppc = substring(color,"|",1);
if (ppc) vignette_RGB[0] = atoi(ppc);
ppc = substring(color,"|",2);
if (ppc) vignette_RGB[1] = atoi(ppc);
ppc = substring(color,"|",3);
if (ppc) vignette_RGB[2] = atoi(ppc);
thread_signal();
// trigger update thread
}
if (strmatch(event,"load"))
// load saved curve
{
file = zgetfile("load curve from a file",MWIN,"file",saved_curves_folder);
if (! file) return 1;
fid = fopen(file,"r");
zfree(file);
if (! fid) return 1;
splcurve_load(sd,fid);
fclose(fid);
Vign_curvedit(0);
thread_signal();
return 1;
}
if (strmatch(event,"save"))
// save curve to file
{
file = zgetfile("save curve to a file",MWIN,"save",saved_curves_folder);
if (! file) return 1;
pp = zstrdup(file,"vignette",8);
zfree(file);
file = pp;
pp = strrchr(file,'/');
// force .curve extension
if (pp) pp = strrchr(pp,'.');
if (pp) strcpy(pp,".curve");
else strcat(file,".curve");
fid = fopen(file,"w");
zfree(file);
if (! fid) return 1;
splcurve_save(sd,fid);
fclose(fid);
return 1;
}
return 1;
}
// get mouse position and set new center for vignette
void vign_mousefunc()
// mouse function
{
if (! LMclick && ! Mdrag) return;
LMclick = 0;
vign_cx = Mxposn;
// new vignette center = mouse position
vign_cy = Myposn;
Mxdrag = Mydrag = 0;
thread_signal();
// trigger image update
return;
}
// this function is called when the curve is edited
void Vign_curvedit(int)
{
thread_signal();
// update image
return;
}
// thread function
void * Vign_thread(void *)
{
void * Vign_wthread(void *arg);
do_wthreads(Vign_wthread,NWT);
// worker threads
CEF->Fmods++;
CEF->Fsaved = 0;
Fpaint2();
return 0;
}
// working thread
void * Vign_wthread(void *arg)
{
float *pix1, *pix3;
int index, ii, kk, px, py, dist = 0;
float cx, cy, rad, radx, rady, f1, f2, xval, yval;
float R1, G1, B1, R3, G3, B3;
float max$;
spldat *sd = EFvignette.sd;
cx = vign_cx;
// vignette center (mouse)
cy = vign_cy;
index = *((int *) arg);
for (py = index; py < E3pxm->hh; py += NWT)
// loop all image pixels
for (px = 0; px < E3pxm->ww; px++)
{
ii = py * E3pxm->ww + px;
if (sa_stat == 3) {
// select area active
dist = sa_pixmap[ii];
// distance from edge
if (! dist) continue;
// pixel is outside area
}
pix1 = PXMpix(E1pxm,px,py);
// input pixel
pix3 = PXMpix(E3pxm,px,py);
// output pixel
R1 = pix1[0];
// input RGB
G1 = pix1[1];
B1 = pix1[2];
radx = px - cx;
// distance from vignette center
rady = py - cy;
rad = sqrtf(radx*radx + rady*rady);
// (px,py) distance from center
xval = rad / vign_rad;
// scale 0 to 1.0
kk = 999.0 * xval;
// scale 0 to 999
if (kk > 999) kk = 999;
// beyond radius
yval = sd->yval[0][kk];
// brightness curve y-value 0 to 1.0
if (yval > 1.0) yval = 1.0;
yval = 2.0 * yval;
// 0 to 2.0
R3 = yval * R1;
// adjust brightness
G3 = yval * G1;
B3 = yval * B1;
yval = sd->yval[1][kk];
// color curve y-value 0 to 1.0
if (yval > 1.0) yval = 1.0;
f1 = yval;
// 0 to 1.0 new color
f2 = 1.0 - f1;
// 1.0 to 0 old color
R3 = f1 * vignette_RGB[0] + f2 * R3;
// mix input and vignette color
G3 = f1 * vignette_RGB[1] + f2 * G3;
B3 = f1 * vignette_RGB[2] + f2 * B3;
if (sa_stat == 3 && dist < sa_blendwidth) {
// select area is active,
f1 = sa_blendfunc(dist);
// blend changes over sa_blendwidth
f2 = 1.0 - f1;
R3 = f1 * R3 + f2 * R1;
G3 = f1 * G3 + f2 * G1;
B3 = f1 * B3 + f2 * B1;
}
RGBFIX(R3,G3,B3)
pix3[0] = R3;
pix3[1] = G3;
pix3[2] = B3;
}
return 0;
}
/******************************************************************************* */ /******************************************************************************* */
// find and remove "dust" from an image (e.g. from a scanned dusty slide) // find and remove "dust" from an image (e.g. from a scanned dusty slide)
// dust is defined as small dark areas surrounded by brighter areas // dust is defined as small dark areas surrounded by brighter areas
// image 1 original with prior edits // image 1 original with prior edits
// image 3 accumulated dust removals that have been committed // image 3 accumulated dust removals that have been committed
// image 9 committed dust removals + pending removal (work in process) // image 9 committed dust removals + pending removal (work in process)
namespace dust_names namespace dust_names
{ {
 End of changes. 176 change blocks. 
1633 lines changed or deleted 1709 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)