1
/**************************************************************************
3
Fotoxx edit photos and manage collections
5
Copyright 2007 2008 2009 2010 2011 Michael Cornelison
6
Source URL: http://kornelix.squarespace.com/fotoxx
7
Contact: kornelix2@googlemail.com
9
This program is free software: you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation, either version 3 of the License, or
12
(at your option) any later version.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License
20
along with this program. If not, see http://www.gnu.org/licenses/.
22
***************************************************************************/
24
#define EX extern // enable extern declarations
27
/**************************************************************************
29
Fotoxx image editor - select area functions
31
Select an area within the current image.
32
Subsequent edit functions are carried out within the area.
33
Otherwise, edit functions apply to the entire image.
35
sa_stat 0/1/2/3 = none/edit/pause/complete
36
sa_mode is current area selection method:
37
1 select rectangle by drag or clicks
38
2 select ellipse by drag
39
3 freehand draw by drag
40
4 follow edge indicated by clicks
41
5 select adjacent pixels matching color
42
6 select pixels within mouse radius
45
***************************************************************************/
47
// user select area dialog
48
// line drawing and selection by color range are combined // v.9.7
50
void m_select(GtkWidget *, cchar *) // menu function
52
int select_dialog_event(zdialog *, cchar *event); // dialog event and completion funcs
54
cchar *title = ZTX("Select Area for Edits");
55
cchar *helptext = ZTX("Press F1 for help");
57
zfuncs::F1_help_topic = "select_area";
59
if (! curr_file) return; // no image
60
if (zdsela) return; // already active
62
if (CEF && CEF->Farea != 2) { // active edit function
63
zmessageACK(mWin,ZTX("Select Area not supported \n" // v.11.08
64
"by this edit function"));
68
if (Fpreview) edit_fullsize(); // use full-size image
70
if (! Fpxm16) { // create Fpxm16 if not already
71
mutex_lock(&Fpixmap_lock);
72
Fpxm16 = f_load(curr_file,16);
73
mutex_unlock(&Fpixmap_lock);
78
_________________________________________________________
80
| (o) select rectangle |
81
| (o) select ellipse |
82
| (o) draw: freehand |
83
| (o) draw: follow edge |
84
| (o) select by mouse radius [__] |
85
| (o) select by color match [__] |
86
| [x] my mouse [x] firewall Blend Width [__] |
88
| [Show] [Hide] [Color] [Finish] [Unfinish] |
89
| [Enable] [Disable] [Invert] [Unselect] [Done] |
90
|_________________________________________________________|
94
zdsela = zdialog_new(title,mWin,null);
95
zdialog_add_widget(zdsela,"label","labhelp","dialog",helptext);
96
zdialog_add_widget(zdsela,"hbox","hb1","dialog");
97
zdialog_add_widget(zdsela,"vbox","vb1","hb1",0,"homog");
98
zdialog_add_widget(zdsela,"vbox","vb2","hb1",0,"homog");
99
zdialog_add_widget(zdsela,"radio","rbrect","vb1",ZTX("rectangle"));
100
zdialog_add_widget(zdsela,"radio","rbelips","vb1",ZTX("ellipse"));
101
zdialog_add_widget(zdsela,"radio","rbdraw","vb1",ZTX("draw: freehand"));
102
zdialog_add_widget(zdsela,"radio","rbfollow","vb1",ZTX("draw: follow edge"));
103
zdialog_add_widget(zdsela,"radio","rbmouse","vb1",ZTX("select by mouse"));
104
zdialog_add_widget(zdsela,"radio","rbcolor","vb1",ZTX("select by color"));
106
zdialog_add_widget(zdsela,"label","space1","vb2");
107
zdialog_add_widget(zdsela,"label","space2","vb2");
108
zdialog_add_widget(zdsela,"label","space3","vb2");
109
zdialog_add_widget(zdsela,"label","space4","vb2");
111
zdialog_add_widget(zdsela,"hbox","hbmouserad","vb2");
112
zdialog_add_widget(zdsela,"label","labmouserad","hbmouserad",ZTX("radius"),"space=5");
113
zdialog_add_widget(zdsela,"spin","mouseradius","hbmouserad","1|300|1|20");
115
zdialog_add_widget(zdsela,"hbox","hbcolor","vb2");
116
zdialog_add_widget(zdsela,"label","labmatch","hbcolor",ZTX("match"),"space=5");
117
zdialog_add_widget(zdsela,"spin","colormatch","hbcolor","0|100|1|90");
119
zdialog_add_widget(zdsela,"hbox","hbb1","dialog",0,"space=5");
120
zdialog_add_widget(zdsela,"check","mymouse","hbb1",BmyMouse,"space=5");
121
zdialog_add_widget(zdsela,"check","firewall","hbb1",ZTX("firewall"),"space=5");
122
zdialog_add_widget(zdsela,"label","labblend","hbb1",Bblendwidth,"space=5");
123
zdialog_add_widget(zdsela,"spin","blendwidth","hbb1","0|500|1|0");
125
zdialog_add_widget(zdsela,"hbox","hbb2","dialog",0,"space=5");
126
zdialog_add_widget(zdsela,"button","show","hbb2",Bshow);
127
zdialog_add_widget(zdsela,"button","hide","hbb2",Bhide);
128
zdialog_add_widget(zdsela,"button","color","hbb2",Bcolor);
129
zdialog_add_widget(zdsela,"button","finish","hbb2",Bfinish);
130
zdialog_add_widget(zdsela,"button","unfinish","hbb2",Bunfinish);
132
zdialog_add_widget(zdsela,"hbox","hbb3","dialog",0,"space=3");
133
zdialog_add_widget(zdsela,"button","enable","hbb3",Benable);
134
zdialog_add_widget(zdsela,"button","disable","hbb3",Bdisable);
135
zdialog_add_widget(zdsela,"button","invert","hbb3",Binvert);
136
zdialog_add_widget(zdsela,"button","unselect","hbb3",Bunselect);
137
zdialog_add_widget(zdsela,"button","done","hbb3",Bdone);
139
zdialog_help(zdsela,"select_area"); // v.11.08
140
zdialog_run(zdsela,select_dialog_event,"save"); // run dialog - parallel v.11.07
142
sa_mouseradius = 20; // initial values matching dialog
146
sa_mode = 3; // default mode = freehand draw
147
zdialog_stuff(zdsela,"rbdraw",1);
148
sa_show(1); // show existing area, if any
154
// dialog event and completion callback function
156
int select_dialog_event(zdialog *zd, cchar *event)
160
if (strEqu(event,"done") || zd->zstat) // done or cancel
162
freeMouse(); // disconnect mouse function v.10.12
163
zdialog_free(zdsela); // kill dialog
167
if (CEF && CEF->Farea != 2) { // select area not supported v.11.08
168
printf("select area ignored \n");
172
if (sa_fww != Fww || sa_fhh != Fhh) // delete area not valid for image
173
sa_unselect(); // bugfix v.11.08
175
if (! sa_stat) { // no area, create one v.11.07
176
cc = Fww * Fhh * sizeof(uint16); // allocate sa_pixmap[] for new area
177
sa_pixmap = (uint16 *) zmalloc(cc,"sa_select"); // maps pixels in area
178
memset(sa_pixmap,0,cc);
179
sa_currseq = sa_Ncurrseq = 0; // reset selection sequence
180
sa_Npixel = sa_blend = sa_calced = Factivearea = 0;
181
sa_fww = Fww; // valid image size for area v.11.08
183
sa_stat = 1; // status = edit
186
ii = strcmpv(event,"rbrect","rbelips","rbdraw","rbfollow","rbcolor","rbmouse",null);
188
sa_mode = ii; // radio button 1-6
189
sa_stat = 1; // resume edit
190
sa_Npixel = sa_blend = sa_calced = Factivearea = 0;
191
zdialog_stuff(zd,"blendwidth",0); // reset blend width v.11.01
192
sa_show(1); // show area v.11.04
195
if (strEqu(event,"mouseradius")) // mouse selection radius
196
zdialog_fetch(zd,"mouseradius",sa_mouseradius);
198
if (strEqu(event,"colormatch")) // color match range, 0 to 99.9
199
zdialog_fetch(zdsela,"colormatch",sa_colormatch);
201
if (strEqu(event,"firewall")) // color select firewall on/off v.10.11
202
zdialog_fetch(zdsela,"firewall",sa_firewall);
204
if (strEqu(event,"mymouse")) { // toggle mouse capture v.10.12
205
zdialog_fetch(zd,"mymouse",mymouse);
207
sa_stat = 1; // resume edit
208
sa_Npixel = sa_blend = sa_calced = Factivearea = 0;
209
zdialog_stuff(zd,"blendwidth",0); // reset blend width v.11.01
212
else sa_stat = 2; // pause edit
215
if (strEqu(event,"show")) sa_show(1); // show area
217
if (strEqu(event,"hide")) sa_show(0); // hide area
219
if (strEqu(event,"color")) { // switch colors
220
if (sa_pixRGB == &red) sa_pixRGB = &green;
221
else if (sa_pixRGB == &green) sa_pixRGB = &black;
222
else sa_pixRGB = &red;
226
if (strstr("finish unselect able invert blend",event)) { // dialog buttons
227
freeMouse(); // disconnect mouse v.11.04
228
gdk_window_set_cursor(drWin->window,null); // normal cursor v.11.03
231
if (strEqu(event,"finish")) sa_finish(); // finish (finalize) area
232
if (strEqu(event,"unfinish")) sa_unfinish(); // unfinish area v.11.07
233
if (strEqu(event,"unselect")) sa_unselect(); // unselect area
234
if (strEqu(event,"enable")) sa_enable(); // enable area
235
if (strEqu(event,"disable")) sa_disable(); // disable area
236
if (strEqu(event,"invert")) sa_invert(); // invert area
238
if (strEqu(event,"blendwidth") && Factivearea) { // blend width changed
239
sa_edgecalc(); // do edge calc. if not already
240
if (sa_calced && CEF && CEF->zd) { // edit is active
241
zdialog_fetch(zd,"blendwidth",sa_blend); // update sa_blend
242
zdialog_send_event(CEF->zd,event); // notify edit dialog
244
} // "ignore once" logic removed v.11.01
246
if (sa_stat == 1) // active edit mode
248
if (sa_mode == 1) takeMouse(zd,sa_geom_mousefunc,dragcursor); // rectangle v.11.03
249
if (sa_mode == 2) takeMouse(zd,sa_geom_mousefunc,dragcursor); // ellipse
250
if (sa_mode == 3) takeMouse(zd,sa_draw_mousefunc,drawcursor); // freehand draw
251
if (sa_mode == 4) takeMouse(zd,sa_draw_mousefunc,drawcursor); // follow edge
252
if (sa_mode == 5) takeMouse(zd,sa_color_mousefunc,0); // color match
253
if (sa_mode == 6) takeMouse(zd,sa_radius_mousefunc,0); // mouse radius
254
if (sa_mode < 3) sa_geom1 = sa_geom2 = 0; // new rectangle or ellipse
255
if (sa_mode < 5) paint_toparc(2); // erase radius circle
258
freeMouse(); // disconnect mouse v.10.12
264
// select area mouse function - select a rectangle or ellipse
266
void sa_geom_mousefunc() // new v.11.03
268
static int mx1, my1, mx2, my2;
269
static int mdx0, mdy0;
271
if (sa_stat != 1) return; // area gone? v.11.07
273
if (sa_currseq > sa_maxseq-2) {
274
zmessageACK(mWin,ZTX("exceed %d edits"),sa_maxseq); // cannot continue
278
if (RMclick) // right mouse click
281
sa_unselect_pixels(); // remove latest selection
282
sa_geom1 = sa_geom2 = 0;
287
if (! Mxdrag && ! Mydrag) return; // v.11.04
289
if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated
292
mx1 = mdx0; // drag start, one corner
299
if (sa_geom2) sa_unselect_pixels(); // remove prior selection
300
mx2 = Mxdrag; // drag continues, 2nd corner
304
sa_nextseq(); // next sequence number
306
if (sa_mode == 1) // draw rectangle
308
sa_draw_line(mx1,my1,mx2,my1);
309
sa_draw_line(mx2,my1,mx2,my2);
310
sa_draw_line(mx2,my2,mx1,my2);
311
sa_draw_line(mx1,my2,mx1,my1);
314
if (sa_mode == 2) // draw ellipse
317
double x, y, x2, y2, cx, cy;
320
a = abs(mx2 - mx1); // ellipse constants from v.11.04
321
b = abs(my2 - my1); // enclosing rectangle
324
cx = mx1; // center at drag origin v.11.04
327
for (y = -b; y < b; y++) // step through y values
330
x2 = a2 * (1 - y2 / b2);
331
x = sqrt(x2); // corresp. x values, + and -
334
sa_draw1pix(px,py); // draw 2 points on ellipse
339
for (x = -a; x < a; x++) // step through x values
342
y2 = b2 * (1 - x2 / a2);
343
y = sqrt(y2); // corresp. y values, + and -
346
sa_draw1pix(px,py); // draw 2 points on ellipse
357
// select area mouse function - freehand draw and follow edge
359
void sa_draw_mousefunc()
361
void sa_follow_edge(int mx1, int my1, int mx2, int my2);
363
int mx1, my1, mx2, my2;
364
int npdist, npx, npy;
365
int ii, click, newseq, thresh;
366
static int drag = 0, mdx0, mdy0, mdx1, mdy1;
368
if (sa_stat != 1) return; // area gone? v.11.07
370
sa_thresh = 4.0 / Mscale + 1; // mouse pixel distance threshold
373
if (LMclick || Mxdrag || Mydrag) // left mouse click or mouse drag
375
if (LMclick) // left mouse click
378
mx1 = mx2 = Mxclick; // click position
386
if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated
387
mdx0 = mdx1 = Mxdown;
388
mdy0 = mdy1 = Mydown;
391
mx1 = mdx1; // drag start
393
mx2 = Mxdrag; // drag position
395
mdx1 = mx2; // next drag start
401
if (Mbutton == 3) // right mouse >> erase
405
npdist = sa_nearpix(mx2,my2,thresh,npx,npy);
407
ii = npy * Fww + npx;
414
if (sa_currseq > sa_maxseq-2) {
415
zmessageACK(mWin,ZTX("exceed %d edits"),sa_maxseq); // cannot continue
419
if (sa_currseq == 0 && newseq) // 1st pixel(s) of 1st sequence
421
sa_nextseq(); // set next (1st) sequence no. v.10.8
422
sa_draw_line(mx1,my1,mx2,my2); // draw initial pixel or line
423
sa_endpx[sa_currseq] = mx2;
424
sa_endpy[sa_currseq] = my2;
429
mx1 = sa_endpx[sa_currseq]; // prior sequence end pixel
430
my1 = sa_endpy[sa_currseq]; // (before this click)
434
if (newseq) thresh = 2 * sa_thresh; // new drag threshold
435
else thresh = 5 * sa_thresh; // continuation drag threshold
436
npx = sa_endpx[sa_currseq]; // distance from prior end pixel
437
npy = sa_endpy[sa_currseq]; // (before this drag)
438
if (abs(mx1-npx) < thresh && abs(my1-npy) < thresh) {
439
mx1 = sa_endpx[sa_currseq]; // if < threshold, connect this
440
my1 = sa_endpy[sa_currseq]; // drag to prior drag or click
444
if (newseq || drag > 50) {
445
sa_nextseq(); // set next sequence no. v.10.8
446
drag = 1; // drag length within sequence
449
if (sa_mode == 4) sa_follow_edge(mx1,my1,mx2,my2); // follow edge or draw line
450
else sa_draw_line(mx1,my1,mx2,my2); // from end pixel to mouse
452
sa_endpx[sa_currseq] = mx2; // set end pixel for this sequence
453
sa_endpy[sa_currseq] = my2;
456
else if (RMclick) // right mouse click
459
sa_unselect_pixels(); // remove latest selection v.10.8
467
// Find the nearest drawn pixel within a radius of a given pixel.
468
// Returns distance to pixel, or zero if nothing found.
469
// Returns 1 for adjacent or diagonally adjacent pixel.
471
int sa_nearpix(int mx, int my, int rad2, int &npx, int &npy)
473
int ii, rad, qx, qy, dx, dy;
477
mindist = (rad2+1) * (rad2+1);
479
for (rad = 1; rad <= rad2; rad++) // seek neighbors within range
481
if (rad * rad > mindist) break; // can stop searching now
483
for (qx = mx-rad; qx <= mx+rad; qx++) // search within rad
484
for (qy = my-rad; qy <= my+rad; qy++)
486
if (qx != mx-rad && qx != mx+rad && // exclude within rad-1
487
qy != my-rad && qy != my+rad) continue; // (already searched)
488
if (qx < 0 || qx > Fww-1) continue;
489
if (qy < 0 || qy > Fhh-1) continue;
491
if (! sa_pixmap[ii]) continue;
492
dx = (mx - qx) * (mx - qx); // found pixel
493
dy = (my - qy) * (my - qy);
494
dist = dx + dy; // distance**2
495
if (dist < mindist) {
497
npx = qx; // save nearest pixel found
503
if (npx + npy) return sqrt(mindist) + 0.5;
508
// draw a line between two given pixels
509
// add all in-line pixels to sa_pixmap[]
511
void sa_draw_line(int px1, int py1, int px2, int py2)
513
void sa_draw1pix(int px, int py);
518
if (sa_stat != 1) return; // area gone? v.11.07
520
if (px1 == px2 && py1 == py2) { // only one pixel
521
sa_draw1pix(px1,py1);
525
if (abs(py2 - py1) > abs(px2 - px1)) {
526
slope = 1.0 * (px2 - px1) / (py2 - py1);
528
for (pym = py1; pym <= py2; pym++) {
529
pxm = round(px1 + slope * (pym - py1));
530
sa_draw1pix(pxm,pym);
534
for (pym = py1; pym >= py2; pym--) {
535
pxm = round(px1 + slope * (pym - py1));
536
sa_draw1pix(pxm,pym);
541
slope = 1.0 * (py2 - py1) / (px2 - px1);
543
for (pxm = px1; pxm <= px2; pxm++) {
544
pym = round(py1 + slope * (pxm - px1));
545
sa_draw1pix(pxm,pym);
549
for (pxm = px1; pxm >= px2; pxm--) {
550
pym = round(py1 + slope * (pxm - px1));
551
sa_draw1pix(pxm,pym);
560
// draw one pixel only if not already drawn
562
void sa_draw1pix(int px, int py)
564
if (px < 0 || px > Fww-1) return; // bugfix v.11.03.1
565
if (py < 0 || py > Fhh-1) return;
566
int ii = Fww * py + px;
567
if (sa_pixmap[ii]) return; // map to curr. selection sequence
568
sa_pixmap[ii] = sa_currseq;
569
sa_Ncurrseq++; // v.10.8
570
draw_fat_pixel(px,py,sa_pixRGB); // v.11.04
575
// Find series of edge pixels from px1/py1 to px2/py2 and connect them together.
577
void sa_follow_edge(int px1, int py1, int px2, int py2) // v.10.8
579
double sa_get_contrast(int px, int py);
581
double px3, py3, px4, py4, px5, py5, px6, py6;
582
double dx, dy, dist, contrast, maxcontrast;
584
if (sa_stat != 1) return; // area gone? v.11.07
586
px3 = px1; // p3 progresses from p1 to p2
594
dist = sqrt(dx * dx + dy * dy); // last segment
597
px4 = px3 + dx / dist; // p4 = p3 moved toward p2
598
py4 = py3 + dy / dist;
604
for (int ii = -2; ii <= +2; ii++) // p5 points are in a line through p4
605
{ // and perpendicular to p4 - p2
606
px5 = px4 + ii * dy / dist;
607
py5 = py4 - ii * dx / dist;
608
contrast = sa_get_contrast(px5,py5);
609
contrast *= (7 - abs(ii)); // favor points closer together v.10.9
610
if (contrast > maxcontrast) {
611
px6 = px5; // p6 = highest contrast point in p5
613
maxcontrast = contrast;
617
sa_draw_line(px3,py3,px6,py6); // draw p3 to p6
619
px3 = px6; // next p3
623
sa_draw_line(px3,py3,px2,py2);
628
// Find max. contrast between neighbors on opposite sides of given pixel
630
double sa_get_contrast(int px, int py) // v.10.8
632
int map[4][2] = { {1, 0}, {1, 1}, {0, 1}, {-1, 1} };
635
double red, green, blue;
636
double contrast, maxcontrast = 0;
637
double f65k = 1.0 / 65535.0;
639
if (px < 1 || px > Fww-2) return 0; // avoid edge pixels
640
if (py < 1 || py > Fhh-2) return 0;
642
for (ii = 0; ii < 4; ii++) // compare pixels around target
643
{ // e.g. (px-1,py) to (px+1,py)
646
pix1 = PXMpix(Fpxm16,px+qx,py+qy);
647
pix2 = PXMpix(Fpxm16,px-qx,py-qy);
648
red = f65k * abs(pix1[0] - pix2[0]);
649
green = f65k * abs(pix1[1] - pix2[1]);
650
blue = f65k * abs(pix1[2] - pix2[2]);
651
contrast = (1.0 - red) * (1.0 - green) * (1.0 - blue); // no contrast = 1.0
652
contrast = 1.0 - contrast; // max. contrast = 1.0
653
if (contrast > maxcontrast) maxcontrast = contrast;
660
// select area by color range - mouse function
662
void sa_color_mousefunc()
664
void sa_find_color_pixels();
667
static int mxdown, mydown, drag = 0;
669
if (sa_stat != 1) return; // area gone? v.11.07
671
sa_radius = sa_mouseradius; // use mouse radius
672
sa_radius2 = sa_radius * sa_radius;
674
toparcx = Mxposn - sa_radius; // draw radius outline circle
675
toparcy = Myposn - sa_radius;
676
toparcw = toparch = 2 * sa_radius;
680
if (sa_stackdirec) zfree(sa_stackdirec); // allocate pixel search stack
681
if (sa_stackii) zfree(sa_stackii);
683
sa_stackdirec = zmalloc(cc,"sa_color");
684
sa_stackii = (int *) zmalloc(4*cc,"sa_color");
688
sa_mousex = sa_mousey = 0;
690
if (LMclick) { // get mouse position at click
694
sa_nextseq(); // set next sequence no. v.10.8
698
if ((Mxdrag || Mydrag) && Mbutton == 3) { // right drag, unselect within radius
699
sa_radius_mousefunc();
703
if ((Mxdrag || Mydrag) && Mbutton == 1) { // left drag, select matching colors
708
if (Mxdown != mxdown || Mydown != mydown) { // detect if new drag started
711
sa_nextseq(); // set next sequence no. v.10.8
714
else if (++drag > 30) { // limit work per sequence no.
715
sa_nextseq(); // set next sequence no. v.10.8
720
if (sa_mousex || sa_mousey) {
721
sa_find_color_pixels(); // accumulate pixels
727
sa_unselect_pixels(); // remove latest selection v.10.8
735
// find all contiguous pixels within the specified range of colors to match
737
void sa_find_color_pixels() // overhauled v.11.02
739
int ii, kk, cc, px, py, rx, ry, rad2;
740
int ppx, ppy, npx, npy;
742
double match1, match2, ff = 1.0 / 65536.0;
743
double dred, dgreen, dblue;
746
match1 = 0.01 * sa_colormatch; // color match level, 0.01 to 1.0
750
if (px < 0 || px > Fww-1) return; // mouse outside image
751
if (py < 0 || py > Fhh-1) return;
753
cc = Fww * Fhh; // allocate selection map
754
sa_pixselc = zmalloc(cc,"sa_color");
755
memset(sa_pixselc,0,cc);
757
sa_Nmatch = 0; // match color count
759
for (rx = -sa_radius; rx <= sa_radius; rx++) // loop every pixel in radius of mouse
760
for (ry = -sa_radius; ry <= sa_radius; ry++)
762
rad2 = rx * rx + ry * ry;
763
if (rad2 > sa_radius2) continue; // outside radius
766
if (px < 0 || px > Fww-1) continue; // off the image edge
767
if (py < 0 || py > Fhh-1) continue;
769
matchpix = PXMpix(Fpxm16,px,py); // get color at mouse position
771
for (ii = 0; ii < sa_Nmatch; ii++) // see if color is already included
773
dred = ff * abs(sa_matchRGB[ii][0] - matchpix[0]); // 0 = perfect match
774
dgreen = ff * abs(sa_matchRGB[ii][1] - matchpix[1]);
775
dblue = ff * abs(sa_matchRGB[ii][2] - matchpix[2]);
776
match2 = (1.0 - dred) * (1.0 - dgreen) * (1.0 - dblue); // 1 = perfect match
777
if (match2 >= match1) break; // matches close enough
780
if (ii == sa_Nmatch) { // no close match
781
sa_matchRGB[ii][0] = matchpix[0]; // add new match color to list
782
sa_matchRGB[ii][1] = matchpix[1];
783
sa_matchRGB[ii][2] = matchpix[2];
785
if (sa_Nmatch == 1000) goto startsearch; // capacity limit
791
sa_Ncurrseq = 0; // count newly selected pixels
793
px = sa_mousex; // pixel at mouse
796
sa_pixselc[ii] = 1; // pixel is in current selection
798
if (! sa_pixmap[ii]) { // if selected for the first time, v.10.12
799
sa_pixmap[ii] = sa_currseq; // map pixel to current sequence
800
sa_Ncurrseq++; // current sequence pixel count
803
sa_stackii[0] = ii; // put 1st pixel into stack
804
sa_stackdirec[0] = 'a'; // direction = ahead v.11.04
805
sa_Nstack = 1; // stack count
809
kk = sa_Nstack - 1; // get last pixel in stack
811
direc = sa_stackdirec[kk];
813
py = ii / Fww; // reconstruct px, py
816
if (direc == 'x') { // no neighbors left to check
822
ii = sa_Nstack - 2; // get prior pixel in stack
825
ppx = ii - ppy * Fww;
828
ppx = px - 1; // if only one, assume prior = left
832
if (direc == 'a') { // next ahead pixel v.11.04
835
sa_stackdirec[kk] = 'r'; // next search direction
838
else if (direc == 'r') { // next right pixel v.11.04
841
sa_stackdirec[kk] = 'l';
844
else { /* direc = 'l' */ // next left pixel v.11.04
847
sa_stackdirec[kk] = 'x';
850
if (npx < 0 || npx > Fww-1) continue; // pixel off the edge v.11.04
851
if (npy < 0 || npy > Fhh-1) continue;
853
rx = npx - Mxposn; // limit search to 3 * mouse radius
854
ry = npy - Myposn; // v.11.04
855
rad2 = rx * rx + ry * ry;
856
if (rad2 > 9 * sa_radius2) continue;
858
ii = npy * Fww + npx;
859
if (sa_pixselc[ii]) continue; // already in current selection v.10.8
861
if (sa_firewall && sa_pixmap[ii]) // aleady selected, firewall mode v.10.12
862
if (rad2 > sa_radius2) continue; // and pixel outside mouse radius
864
matchpix = PXMpix(Fpxm16,npx,npy);
865
for (kk = 0; kk < sa_Nmatch; kk++) { // compare pixel RGB to match colors
866
dred = ff * abs(sa_matchRGB[kk][0] - matchpix[0]); // v.10.8
867
dgreen = ff * abs(sa_matchRGB[kk][1] - matchpix[1]);
868
dblue = ff * abs(sa_matchRGB[kk][2] - matchpix[2]);
869
match2 = (1.0 - dred) * (1.0 - dgreen) * (1.0 - dblue); // 1 = perfect match
870
if (match2 >= match1) break; // within range
872
if (kk == sa_Nmatch) continue; // not within range of any color
874
sa_pixselc[ii] = 1; // map pixel to current selection v.10.8
876
if (! sa_pixmap[ii]) { // if selected for the first time, v.10.12
877
sa_pixmap[ii] = sa_currseq; // map pixel to current sequence
878
sa_Ncurrseq++; // current sequence pixel count
881
if (sa_Nstack == sa_maxstack) continue; // stack is full
882
kk = sa_Nstack++; // push pixel into stack
884
sa_stackdirec[kk] = 'a'; // direction = ahead v.11.04
887
zfree(sa_pixselc); // free memory
892
// select or un-select all pixels within radius - mouse function
894
void sa_radius_mousefunc()
896
int ii, px, py, rx, ry;
898
if (sa_stat != 1) return; // area gone? v.11.07
900
sa_radius = sa_mouseradius; // pixel selection radius
901
sa_radius2 = sa_radius * sa_radius;
903
toparcx = Mxposn - sa_radius; // draw radius outline circle
904
toparcy = Myposn - sa_radius;
905
toparcw = toparch = 2 * sa_radius;
909
if (LMclick || RMclick) { // mouse click
910
sa_nextseq(); // set next sequence no. v.10.8
911
LMclick = RMclick = 0;
914
if (Mbutton != 1 && Mbutton != 3) { // button released
915
sa_nextseq(); // set next sequence no. v.10.8
916
return; // (if some pixels mapped)
919
for (rx = -sa_radius; rx <= sa_radius; rx++) // loop every pixel in radius
920
for (ry = -sa_radius; ry <= sa_radius; ry++)
922
if (rx * rx + ry * ry > sa_radius2) continue; // outside radius
925
if (px < 0 || px > Fww-1) continue; // off the image edge
926
if (py < 0 || py > Fhh-1) continue;
930
if (Mbutton == 3) // right mouse button
931
sa_pixmap[ii] = 0; // remove pixel from select area
933
if (Mbutton == 1) // left mouse button
935
if (sa_pixmap[ii]) continue; // pixel already selected
937
if (sa_Ncurrseq > 1000) // start new sequence no.
938
sa_nextseq(); // after 1000 pixels
940
sa_pixmap[ii] = sa_currseq; // map pixel to current sequence
949
// set next sequence number for pixels about to be selected
951
void sa_nextseq() // v.10.8
953
if (sa_Ncurrseq > 0) sa_currseq++; // increase only if some pixels mapped
954
if (sa_currseq < sa_initseq) sa_currseq = sa_initseq; // start at initial value
960
// un-select all pixels mapped to current sequence number
961
// reduce sequence number and set pixel count = 1
963
void sa_unselect_pixels()
965
if (sa_stat != 1) return; // area gone? v.11.07
966
if (! sa_currseq) return; // no pixels mapped
968
for (int ii = 0; ii < Fww * Fhh; ii++)
969
if (sa_pixmap[ii] == sa_currseq) sa_pixmap[ii] = 0; // unmap current selection
971
if (sa_currseq > sa_initseq) { // reduce sequence no. v.10.8
973
sa_Ncurrseq = 1; // unknown but > 0
975
else sa_Ncurrseq = 0; // initial sequence no. reached
981
// Finish select area - map pixels enclosed by edge pixels
982
// into sa_pixmap[ii]: 0/1/2 = outside/edge/inside (ii=py*Fww+px)
983
// total count = sa_Npixel
985
zdialog *safinzd = 0;
987
void sa_finish() // overhauled v.11.02
989
void sa_finish_mousefunc();
990
int sa_finish_dialog_event(zdialog *, cchar *event);
992
cchar *fmess = ZTX("Click one time inside each enclosed area \n"
993
"(possible gaps in the outline will be found). \n"
994
"Press F1 for help.");
996
GtkWidget *pwin = zdialog_widget(zdsela,"dialog");
999
if (! sa_stat) return; // no area? v.11.07
1000
if (sa_fww != Fww || sa_fhh != Fhh) return; // area not valid for image v.11.08
1001
if (sa_mode == 7) return; // a whole image area
1003
sa_Npixel = Factivearea = 0; // area disabled, unfinished
1004
sa_hole = 0; // no hole detected yet
1005
sa_show(1); // show outline
1012
for (ii = 0; ii < Fww * Fhh; ii++) // get enclosing rectangle
1013
{ // for selected area
1014
if (! sa_pixmap[ii]) continue;
1017
if (px >= sa_maxx) sa_maxx = px+1; // like Fww, sa_maxx = last + 1
1018
if (px < sa_minx) sa_minx = px;
1019
if (py >= sa_maxy) sa_maxy = py+1;
1020
if (py < sa_miny) sa_miny = py;
1023
sa_minx -= 10; // add margins where possible
1024
if (sa_minx < 0) sa_minx = 0;
1026
if (sa_maxx > Fww) sa_maxx = Fww;
1028
if (sa_miny < 0) sa_miny = 0;
1030
if (sa_maxy > Fhh) sa_maxy = Fhh;
1032
sa_map_pixels(); // map edge and interior pixels
1033
if (sa_Npixel < 10) return; // ridiculous
1035
for (py = sa_miny; py < sa_maxy; py++) // loop pixels in rectangle v.10.12
1036
for (px = sa_minx; px < sa_maxx; px++)
1039
if (sa_pixmap[ii] == 2) sa_pixmap[ii] = 0; // eliminate interior pixels
1042
cc = (sa_maxx-sa_minx) * (sa_maxy-sa_miny); // allocate stack memory
1043
if (sa_stackdirec) zfree(sa_stackdirec);
1044
sa_stackdirec = zmalloc(cc,"sa_finish");
1045
if (sa_stackii) zfree(sa_stackii);
1046
sa_stackii = (int *) zmalloc(cc * 4,"sa_finish");
1049
safinzd = zdialog_new(ZTX("finish area"),pwin,Bdone,Bcancel,null); // dialog for user to click inside
1050
zdialog_add_widget(safinzd,"label","fmess","dialog",fmess,"space=5"); // each enclosed area
1051
zdialog_add_widget(safinzd,"hbox","hbstat","dialog");
1052
zdialog_add_widget(safinzd,"label","labstat","hbstat","status:","space=5");
1053
zdialog_add_widget(safinzd,"label","statmess","hbstat",0);
1055
takeMouse(safinzd,sa_finish_mousefunc,dragcursor); // connect mouse function v.11.03
1057
zdialog_run(safinzd,sa_finish_dialog_event,"save"); // run dialog, parallel v.11.07
1058
zdialog_wait(safinzd);
1063
// mouse function - get user clicks and perform pixel searches
1065
void sa_finish_mousefunc() // overhauled v.11.02
1068
int ppx, ppy, npx, npy;
1071
if (! LMclick) return;
1074
if (! sa_stat) return; // area gone? v.11.07
1076
ii = Fww * Myclick + Mxclick; // seed pixel from mouse click
1077
if (sa_pixmap[ii] == 1) return; // ignore if edge pixel v.11.07
1078
sa_pixmap[ii] = 2; // map the pixel, inside area
1079
sa_stackii[0] = ii; // put seed pixel into stack
1080
sa_stackdirec[0] = 'a'; // direction = ahead v.11.04
1081
sa_Nstack = 1; // stack count
1083
zdialog_stuff(safinzd,"statmess",ZTX("searching"));
1089
while (sa_Nstack) // find all pixels outside enclosed area(s)
1091
kk = sa_Nstack - 1; // get last pixel in stack
1092
ii = sa_stackii[kk];
1093
direc = sa_stackdirec[kk];
1095
py = ii / Fww; // reconstruct px, py
1098
if (px < sa_minx || px >= sa_maxx || py < sa_miny || py >= sa_maxy) // moved v.11.08
1100
sa_hole++; // ran off the edge, seed pixel was
1101
break; // not inside area or area has a hole
1104
if (direc == 'x') { // no neighbors left to check
1109
if (sa_Nstack > 1) {
1110
ii = sa_Nstack - 2; // get prior pixel in stack
1111
ii = sa_stackii[ii];
1113
ppx = ii - ppy * Fww;
1116
ppx = px - 1; // if only one, assume prior = left
1120
if (direc == 'a') { // next ahead pixel v.11.04
1121
npx = px + px - ppx;
1122
npy = py + py - ppy;
1123
sa_stackdirec[kk] = 'r'; // next search direction
1126
else if (direc == 'r') { // next right pixel v.11.04
1127
npx = px + py - ppy;
1128
npy = py + px - ppx;
1129
sa_stackdirec[kk] = 'l';
1132
else { /* direc = 'l' */ // next left pixel v.11.04
1133
npx = px + ppy - py;
1134
npy = py + ppx - px;
1135
sa_stackdirec[kk] = 'x';
1138
if (npx < 0 || npx > Fww-1) continue; // next pixel off the image edge
1139
if (npy < 0 || npy > Fhh-1) continue; // bugfix v.11.04
1141
ii = npy * Fww + npx;
1142
if (sa_pixmap[ii]) continue; // pixel already mapped
1144
sa_pixmap[ii] = 2; // map the pixel, inside area
1145
kk = sa_Nstack++; // put pixel into stack
1146
sa_stackii[kk] = ii;
1147
sa_stackdirec[kk] = 'a'; // direction = ahead v.11.04
1148
draw_pixel(npx,npy,sa_pixRGB); // color mapped pixels
1149
zmainloop(30); // let window update v.11.07
1155
zdialog_stuff(safinzd,"statmess",ZTX("outline has a gap"));
1157
zdialog_stuff(safinzd,"statmess",ZTX("success")); // all pixels found and mapped
1162
// dialog event and completion callback function
1164
int sa_finish_dialog_event(zdialog *zd, cchar *event)
1168
freeMouse(); // disconnect mouse
1170
zdialog_free(safinzd); // kill dialog
1172
if (! sa_stat) return 0; // area gone? v.11.07
1174
if (zstat != 1 || sa_hole) // user cancel or pixel search failure
1176
sa_unfinish(); // unmap interior pixels, set edit mode
1177
return 0; // v.11.07
1180
sa_map_pixels(); // count pixels, map interior pixels
1182
sa_stat = 3; // area is finished
1183
Factivearea = 1; // area is active by default
1184
sa_calced = sa_blend = 0; // edge calculation is missing
1190
// Finish select area automatically when the
1191
// interior selected pixels are already known.
1193
void sa_finish_auto()
1197
if (! sa_stat) return; // no area? v.11.07
1198
if (sa_fww != Fww || sa_fhh != Fhh) return; // area not valid for image v.11.08
1200
sa_Npixel = Factivearea = 0; // area disabled, unfinished
1207
for (ii = 0; ii < Fww * Fhh; ii++) // get enclosing rectangle
1208
{ // for selected area
1209
if (! sa_pixmap[ii]) continue;
1212
if (px >= sa_maxx) sa_maxx = px+1; // like Fww, sa_maxx = last + 1
1213
if (px < sa_minx) sa_minx = px;
1214
if (py >= sa_maxy) sa_maxy = py+1;
1215
if (py < sa_miny) sa_miny = py;
1218
sa_minx -= 10; // add margins where possible
1219
if (sa_minx < 0) sa_minx = 0;
1221
if (sa_maxx > Fww) sa_maxx = Fww;
1223
if (sa_miny < 0) sa_miny = 0;
1225
if (sa_maxy > Fhh) sa_maxy = Fhh;
1227
sa_map_pixels(); // count pixels, map interior pixels
1229
sa_stat = 3; // area is finished
1230
Factivearea = 1; // area is active by default
1231
sa_calced = sa_blend = 0; // edge calculation is missing
1238
// map edge and interior pixels (sa_pixmap[*] = 1 or 2)
1239
// set sa_Npixel = total pixel count
1241
void sa_map_pixels() // v.11.07
1243
int npix, px, py, ii, kk;
1245
if (! sa_stat) return; // no area?
1249
for (py = sa_miny; py < sa_maxy; py++) // find edge pixels
1250
for (px = sa_minx; px < sa_maxx; px++)
1253
if (! sa_pixmap[ii]) continue;
1256
if (px == 0 || px == Fww-1 || py == 0 || py == Fhh-1) // edge of image
1259
if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels
1261
if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix;
1263
if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix;
1265
sa_pixmap[ii] = 2; // interior pixel
1269
sa_pixmap[ii] = 1; // edge pixel
1272
sa_Npixel = npix; // total pixel count
1277
// unfinish an area - unmap interior pixels and put back in edit mode
1279
void sa_unfinish() // v.11.07
1283
if (! sa_stat) return; // no area?
1285
for (py = sa_miny; py < sa_maxy; py++) // loop pixels in rectangle
1286
for (px = sa_minx; px < sa_maxx; px++)
1288
ii = py * Fww + px; // clear interior pixels found
1289
if (sa_pixmap[ii] == 2) sa_pixmap[ii] = 0; // by finish search function
1292
sa_stat = 1; // resume edit mode
1294
sa_calced = sa_blend = 0;
1300
// menu function for show, hide, enable, disable, invert, unselect
1301
// (also implemented as buttons in select area dialog)
1303
void m_select_show(GtkWidget *, cchar *menu)
1305
zfuncs::F1_help_topic = "area_show_hide";
1311
void m_select_hide(GtkWidget *, cchar *menu)
1313
zfuncs::F1_help_topic = "area_show_hide";
1319
void m_select_enable(GtkWidget *, cchar *menu)
1321
zfuncs::F1_help_topic = "area_enable_disable";
1327
void m_select_disable(GtkWidget *, cchar *menu)
1329
zfuncs::F1_help_topic = "area_enable_disable";
1335
void m_select_invert(GtkWidget *, cchar *menu)
1337
zfuncs::F1_help_topic = "area_invert";
1343
void m_select_unselect(GtkWidget *, cchar *menu) // delete the area
1345
zfuncs::F1_help_topic = "area_unselect";
1351
// show or hide outline of select area
1352
// also called from mwpaint1() if Fshowarea = 1
1354
void sa_show(int flag)
1358
if (! sa_stat) return; // no area
1359
if (sa_fww != Fww || sa_fhh != Fhh) return; // area not valid for image
1360
if (sa_mode == 7) return; // a whole image area
1361
if (Fpreview) return; // preview mode, area ignored
1363
Fshowarea = flag; // flag for mwpaint1()
1366
mwpaint2(); // erase area outline v.10.8
1370
for (py = 0; py < Fhh; py++) // find pixels in area bugfix v.10.9
1371
for (px = 0; px < Fww; px++)
1374
if (! sa_pixmap[ii]) continue; // outside of area
1376
if (px == 0 || px == Fww-1 || py == 0 || py == Fhh-1) // edge of image
1379
if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels
1381
if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix;
1383
if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix;
1387
draw_fat_pixel(px,py,sa_pixRGB); // draw fat pixels v.11.04
1394
// enable select area that was disabled
1398
if (sa_fww != Fww || sa_fhh != Fhh) return; // area not valid for image v.11.08
1401
zmessageACK(mWin,ZTX("the area is not finished")); // v.11.08
1402
return; // v.11.06.1
1406
sa_show(1); // v.10.11
1411
// disable select area
1415
Factivearea = 0; // v.9.7
1416
sa_show(0); // v.10.11
1421
// invert a selected area
1425
int ii, jj, px, py, npix;
1427
if (sa_fww != Fww || sa_fhh != Fhh) return; // area not valid for image v.11.08
1429
if (sa_stat != 3) { // v.11.06.1
1430
zmessageACK(mWin,ZTX("the area is not finished"));
1434
if (sa_mode == 7) return; // a whole image area
1438
for (py = 0; py < Fhh; py++) // loop all pixels
1439
for (px = 0; px < Fww; px++)
1442
jj = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside
1444
if (px == 0 || px == Fww-1 || py == 0 || py == Fhh-1) // pixel on image edge v.10.12
1447
sa_pixmap[ii] = 1; // outside pixel >> edge pixel
1450
else sa_pixmap[ii] = 0; // edge pixel >> outside
1454
if (jj > 1) sa_pixmap[ii] = 0; // inside pixel (2+) >> outside (0)
1456
sa_pixmap[ii] = 2 - jj; // edge/outside (1/0) >> edge/inside (1/2)
1461
sa_Npixel = npix; // new select area pixel count
1462
sa_calced = sa_blend = 0; // edge calculation missing
1463
if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); // reset blend width v.11.01
1465
sa_minx = Fww; // new enclosing rectangle bugfix v.11.01
1470
for (ii = 0; ii < Fww * Fhh; ii++)
1472
if (! sa_pixmap[ii]) continue;
1475
if (px >= sa_maxx) sa_maxx = px + 1;
1476
if (px < sa_minx) sa_minx = px;
1477
if (py >= sa_maxy) sa_maxy = py + 1;
1478
if (py < sa_miny) sa_miny = py;
1485
// unselect current area (delete the area)
1489
sa_stat = sa_Npixel = sa_blend = sa_calced = Factivearea = 0;
1490
sa_currseq = sa_Ncurrseq = 0;
1491
sa_fww = sa_fhh = 0; // v.11.08
1492
if (sa_pixmap) zfree(sa_pixmap);
1493
if (sa_stackii) zfree(sa_stackii);
1494
if (sa_stackdirec) zfree(sa_stackdirec);
1498
if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); // reset blend width v.11.01
1504
// compute distance from all pixels in area to nearest edge
1505
// output: sa_pixmap[*] = 0/1/2+ = outside, edge, inside distance from edge
1507
namespace sa_edgecalc_names
1509
uint16 *sa_edgepx, *sa_edgepy, *sa_edgedist;
1515
using namespace sa_edgecalc_names;
1517
int edgecalc_dialog_event(zdialog*, cchar *event);
1518
void * edgecalc_thread(void *);
1520
int ii, nn, cc, px, py;
1522
cchar *zectext = ZTX("Edge calculation in progress");
1524
if (! sa_stat) return; // area gone? v.11.07
1525
if (sa_mode == 7) return; // a whole image area
1527
if (sa_calced) return; // done already
1528
if (! Factivearea) sa_finish(); // finish if needed
1529
if (! Factivearea) return; // no finished area
1531
zd = zdialog_new(ZTX("Area Edge Calc"),mWin,Bcancel,null); // start dialog for user cancel
1532
zdialog_add_widget(zd,"label","lab1","dialog",zectext,"space=10");
1533
zdialog_run(zd,edgecalc_dialog_event);
1536
cc = Fww * Fhh * sizeof(uint16); // allocate memory for calculations
1537
sa_edgedist = (uint16 *) zmalloc(cc,"sa_edgecalc");
1538
memset(sa_edgedist,0,cc);
1540
for (ii = nn = 0; ii < Fww * Fhh; ii++) // count edge pixels in select area
1541
if (sa_pixmap[ii] == 1) nn++;
1543
cc = nn * sizeof(uint16);
1544
sa_edgepx = (uint16 *) zmalloc(cc,"sa_edgecalc"); // allocate memory
1545
sa_edgepy = (uint16 *) zmalloc(cc,"sa_edgecalc");
1547
for (ii = nn = 0; ii < Fww * Fhh; ii++) // build list of edge pixels
1549
if (sa_pixmap[ii] != 1) continue;
1552
if (px < 3 || px > Fww-4) continue; // omit pixels < 3 from image edge
1553
if (py < 3 || py > Fhh-4) continue;
1561
SB_goal = sa_Npixel;
1564
for (ii = 0; ii < Nwt; ii++) // start worker threads to calculate
1565
start_wthread(edgecalc_thread,&wtnx[ii]); // sa_pixmap[] edge distances
1566
wait_wthreads(); // wait for completion
1570
if (! Fkillfunc) { // v.11.07
1571
for (ii = 0; ii < Fww * Fhh; ii++) // copy data from sa_edgedist[]
1573
if (sa_pixmap[ii] < 2) continue; // skip outside and edge pixels
1574
sa_pixmap[ii] = sa_edgedist[ii]; // interior pixel edge distance
1578
zdialog_free(zd); // kill dialog
1579
zfree(sa_edgedist); // free memory
1586
if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); // reset blend width v.11.01
1589
sa_calced = 1; // edge calculation available
1596
// dialog event and completion callback function
1598
int edgecalc_dialog_event(zdialog *zd, cchar *event) // respond to user cancel
1601
printf("edge calc killed \n");
1606
void * edgecalc_thread(void *arg) // worker thread function
1607
{ // new algorithm v.11.05
1608
using namespace sa_edgecalc_names;
1610
void edgecalc_f1(int px, int py);
1612
int index = *((int *) (arg));
1613
int midx, midy, radx, rady, rad;
1616
midx = (sa_maxx + sa_minx) / 2;
1617
midy = (sa_maxy + sa_miny) / 2;
1618
radx = (sa_maxx - sa_minx) / 2 + 1;
1619
rady = (sa_maxy - sa_miny) / 2 + 1;
1620
px = midx; // center of enclosing rectangle
1624
if (sa_pixmap[ii]) edgecalc_f1(px,py); // do center pixel first
1626
for (rad = 1; rad < radx || rad < rady; rad++) // expanding square from the center
1628
for (px = midx-rad; px <= midx+rad; px += 2 * rad) // process edges only, interior already done
1629
for (py = midy-rad+index; py <= midy+rad; py += Nwt)
1631
if (px < 0 || px > Fww-1) continue;
1632
if (py < 0 || py > Fhh-1) continue;
1634
if (! sa_pixmap[ii]) continue;
1635
if (sa_edgedist[ii]) continue;
1637
if (Fkillfunc) exit_wthread();
1640
for (py = midy-rad; py <= midy+rad; py += 2 * rad)
1641
for (px = midx-rad+index; px <= midx+rad; px += Nwt)
1643
if (px < 0 || px > Fww-1) continue;
1644
if (py < 0 || py > Fhh-1) continue;
1646
if (! sa_pixmap[ii]) continue;
1647
if (sa_edgedist[ii]) continue;
1649
if (Fkillfunc) exit_wthread();
1654
return 0; // not executed, stop gcc warning
1658
// Find the nearest edge pixel for a given pixel.
1659
// For all pixels in a line from the given pixel to the edge pixel,
1660
// the same edge pixel is used to compute edge distance.
1662
void edgecalc_f1(int px1, int py1)
1664
using namespace sa_edgecalc_names;
1666
int ii, px2, py2, mindist;
1667
uint dist2, mindist2;
1668
int epx, epy, pxm, pym, dx, dy, inc;
1672
mindist2 = mindist * mindist;
1675
for (ii = 0; ii < sa_Nedge; ii++) // loop all edge pixels
1677
px2 = sa_edgepx[ii];
1678
py2 = sa_edgepy[ii];
1680
if (dx >= mindist) continue; // speedup v.11.05
1682
if (dy >= mindist) continue;
1683
dist2 = dx*dx + dy*dy; // avoid sqrt()
1684
if (dist2 < mindist2) {
1685
mindist2 = dist2; // remember minimum
1686
epx = px2; // remember nearest edge pixel
1687
mindist = sqrt(dist2);
1692
if (abs(epy - py1) > abs(epx - px1)) { // find all pixels along a line
1693
slope = 1.0 * (epx - px1) / (epy - py1); // to the edge pixel
1694
if (epy > py1) inc = 1;
1696
for (pym = py1; pym != epy; pym += inc) {
1697
pxm = px1 + slope * (pym - py1);
1698
ii = pym * Fww + pxm;
1699
if (sa_edgedist[ii]) break;
1700
dx = epx - pxm; // calculate distance to edge
1702
dist2 = sqrt(dx*dx + dy*dy) + 0.5;
1703
sa_edgedist[ii] = dist2; // save
1704
SB_done++; // track progress v.11.06
1709
slope = 1.0 * (epy - py1) / (epx - px1);
1710
if (epx > px1) inc = 1;
1712
for (pxm = px1; pxm != epx; pxm += inc) {
1713
pym = py1 + slope * (pxm - px1);
1714
ii = pym * Fww + pxm;
1715
if (sa_edgedist[ii]) break;
1718
dist2 = sqrt(dx*dx + dy*dy) + 0.5;
1719
sa_edgedist[ii] = dist2;
1728
/**************************************************************************
1729
select area copy and paste menu functions
1730
***************************************************************************/
1732
PXM *sacp_image16 = 0; // select area pixmap image
1733
PXM *sacp_info16 = 0; // opacity and edge distance
1734
int sacp_ww, sacp_hh; // original dimensions
1736
PXM *sacpR_image16 = 0; // resized/rotated image
1737
PXM *sacpR_info16 = 0; // resized/rotated info
1738
int sacpR_ww, sacpR_hh; // resized/rotated dimensions
1740
double sacp_resize; // size, 1.0 = original size
1741
double sacp_angle; // angle of rotation, -180 to +180
1742
int sacp_orgx, sacp_orgy; // origin in target image
1743
double sacp_blend; // edge blend with target image
1746
// copy selected area, save in memory
1748
void m_select_copy(GtkWidget *, cchar *menu) // overhauled v.11.02
1751
int pxmin, pxmax, pymin, pymax;
1752
uint16 *pix1, *pix2;
1754
if (menu) zfuncs::F1_help_topic = "area_copy_paste";
1756
if (sa_mode == 7) return; // a whole image area v.11.01
1757
if (! Factivearea) sa_finish(); // finish area if not already
1758
if (! Factivearea) return;
1759
sa_edgecalc(); // do edge calc if not already
1767
for (ii = 0; ii < Fww * Fhh; ii++) // find pixels in select area
1769
if (! sa_pixmap[ii]) continue;
1772
if (px > pxmax) pxmax = px; // find enclosing rectangle
1773
if (px < pxmin) pxmin = px;
1774
if (py > pymax) pymax = py;
1775
if (py < pymin) pymin = py;
1778
PXM_free(sacp_image16); // free prior if any
1779
PXM_free(sacp_info16);
1780
PXM_free(sacpR_image16);
1781
PXM_free(sacpR_info16);
1783
sacp_ww = pxmax - pxmin + 1; // new area image PXM
1784
sacp_hh = pymax - pymin + 1;
1785
sacp_image16 = PXM_make(sacp_ww,sacp_hh,16);
1786
sacp_info16 = PXM_make(sacp_ww,sacp_hh,16); // new info PXM
1788
for (ii = 0; ii < Fww * Fhh; ii++) // find pixels in select area
1790
if (! sa_pixmap[ii]) continue; // 0/1/2+ = outside/edge/inside edge distance
1793
pix1 = PXMpix(Fpxm16,px,py); // copy pixels into image PXM
1796
pix2 = PXMpix(sacp_image16,px,py);
1800
pix2 = PXMpix(sacp_info16,px,py); // build info PXM v.11.02
1801
pix2[0] = 65535; // opacity
1802
pix2[1] = sa_pixmap[ii]; // edge distance
1810
// paste selected area into current image
1811
// this is an edit function - select area image is copied into main image
1813
int sacp_porg = 0; // pasted area is present
1814
int sacp_porgx, sacp_porgy; // pasted area origin in image
1815
int sacp_pww, sacp_phh; // pasted area dimensions
1819
void m_select_paste(GtkWidget *, cchar *menu) // menu function
1821
int select_paste_dialog_event(zdialog *, cchar *event);
1822
void select_paste_mousefunc();
1824
cchar *dragmess = ZTX("position with mouse click/drag");
1826
if (menu) zfuncs::F1_help_topic = "area_copy_paste";
1828
if (! sacp_image16) return; // nothing to paste
1829
sa_unselect(); // unselect area if present
1831
EFpaste.funcname = "paste";
1832
if (! edit_setup(EFpaste)) return; // setup edit for paste
1834
sacp_resize = 1.0; // size = 1x
1835
sacp_blend = 1; // edge blend = 1
1836
sacp_angle = 0; // angle = 0
1838
PXM_free(sacpR_image16); // free prior if any
1839
PXM_free(sacpR_info16);
1841
sacpR_ww = sacp_ww; // setup resized paste image
1842
sacpR_hh = sacp_hh; // (initially 1x, 0 rotation)
1843
sacpR_image16 = PXM_copy(sacp_image16);
1844
sacpR_info16 = PXM_copy(sacp_info16);
1846
sacp_porg = 0; // no image paste location yet
1848
CEF->zd = zdialog_new(ZTX("Paste Image"),mWin,Bdone,Bcancel,null);
1849
zdialog_add_widget(CEF->zd,"hbox","hb0","dialog",0,"space=8");
1850
zdialog_add_widget(CEF->zd,"label","lab1","hb0",dragmess,"space=8");
1851
zdialog_add_widget(CEF->zd,"check","mymouse","hb0",BmyMouse);
1853
zdialog_add_widget(CEF->zd,"hbox","hbres","dialog",0,"space=5");
1854
zdialog_add_widget(CEF->zd,"label","labres","hbres","resize");
1855
zdialog_add_widget(CEF->zd,"button","+.1%","hbres","+.1%");
1856
zdialog_add_widget(CEF->zd,"button","+1%","hbres","+1%");
1857
zdialog_add_widget(CEF->zd,"button","+10%","hbres","+10%");
1858
zdialog_add_widget(CEF->zd,"button","-.1%","hbres","-.1%");
1859
zdialog_add_widget(CEF->zd,"button","-1%","hbres","-1%");
1860
zdialog_add_widget(CEF->zd,"button","-10%","hbres","-10%");
1862
zdialog_add_widget(CEF->zd,"hbox","hbang","dialog",0,"space=5"); // rotation v.11.02
1863
zdialog_add_widget(CEF->zd,"label","labang","hbang",ZTX("angle"));
1864
zdialog_add_widget(CEF->zd,"button","+.1°","hbang","+.1°");
1865
zdialog_add_widget(CEF->zd,"button","+1°","hbang","+1°");
1866
zdialog_add_widget(CEF->zd,"button","+10°","hbang","+10°");
1867
zdialog_add_widget(CEF->zd,"button","-.1°","hbang","-.1°");
1868
zdialog_add_widget(CEF->zd,"button","-1°","hbang","-1°");
1869
zdialog_add_widget(CEF->zd,"button","-10°","hbang","-10°");
1871
zdialog_add_widget(CEF->zd,"hbox","hbbl","dialog",0,"space=5");
1872
zdialog_add_widget(CEF->zd,"label","lab2","hbbl","edge blend");
1873
zdialog_add_widget(CEF->zd,"hscale","blend","hbbl","0|20|0.3|0","expand"); // v.11.06
1875
zdialog_help(CEF->zd,"area_copy_paste"); // zdialog help topic v.11.08
1876
zdialog_run(CEF->zd,select_paste_dialog_event,"80/20"); // v.11.07
1878
takeMouse(CEF->zd,select_paste_mousefunc,0); // connect mouse function
1879
zdialog_stuff(CEF->zd,"mymouse",1); // v.11.03
1885
// Dialog event and completion callback function
1886
// Get dialog values and convert image. When done, commit edited image
1887
// (with pasted area) and set up a new select area for the pasted area,
1888
// allowing further editing of the area.
1890
int select_paste_dialog_event(zdialog *zd, cchar *event)
1892
void select_paste_mousefunc();
1893
void select_paste_pixmap();
1894
void select_paste_makearea();
1896
int mymouse, ww, hh;
1899
if (zd->zstat) // dialog completed
1901
freeMouse(); // disconnect mouse
1903
if (zd->zstat != 1 || ! sacp_porg) { // cancel paste
1904
edit_cancel(EFpaste); // cancel edit, restore image
1909
edit_done(EFpaste); // commit the edit (pasted image)
1910
select_paste_makearea(); // make equivalent select area
1911
PXM_free(sacpR_image16); // free memory
1912
PXM_free(sacpR_info16);
1916
if (strEqu(event,"mymouse")) { // toggle mouse capture v.10.12
1917
zdialog_fetch(zd,"mymouse",mymouse);
1918
if (mymouse) takeMouse(zd,select_paste_mousefunc,0);
1923
if (strstr(event,"%") || strstr(event,"°")) // new size or angle
1925
if (strEqu(event,"+.1%")) sacp_resize *= 1.001;
1926
if (strEqu(event,"+1%")) sacp_resize *= 1.01;
1927
if (strEqu(event,"+10%")) sacp_resize *= 1.10;
1928
if (strEqu(event,"-.1%")) sacp_resize *= 0.999001;
1929
if (strEqu(event,"-1%")) sacp_resize *= 0.990099;
1930
if (strEqu(event,"-10%")) sacp_resize *= 0.909091; // -10% is really 1.0/1.10
1932
if (strEqu(event,"+.1°")) sacp_angle += 0.1; // rotation v.11.02
1933
if (strEqu(event,"+1°")) sacp_angle += 1.0;
1934
if (strEqu(event,"+10°")) sacp_angle += 10.0;
1935
if (strEqu(event,"-.1°")) sacp_angle -= 0.1;
1936
if (strEqu(event,"-1°")) sacp_angle -= 1.0;
1937
if (strEqu(event,"-10°")) sacp_angle -= 10.0;
1939
PXM_free(sacpR_image16); // free prior if any
1940
PXM_free(sacpR_info16);
1942
ww = sacp_resize * sacp_ww; // new size
1943
hh = sacp_resize * sacp_hh;
1945
pxm_temp = PXM_rescale(sacp_image16,ww,hh); // resized area image
1946
sacpR_image16 = PXM_rotate(pxm_temp,sacp_angle); // rotated area image
1949
pxm_temp = PXM_rescale(sacp_info16,ww,hh); // resized area info
1950
sacpR_info16 = PXM_rotate(pxm_temp,sacp_angle); // rotated/resized area info
1953
sacpR_ww = sacpR_image16->ww; // size after resize/rotate
1954
sacpR_hh = sacpR_image16->hh;
1956
select_paste_pixmap(); // copy onto target image
1959
if (strEqu(event,"blend") && sacp_porg) {
1960
zdialog_fetch(zd,"blend",sacp_blend); // new edge blend distance
1961
select_paste_pixmap(); // copy onto target image
1964
CEF->Fmod = 1; // image is modified
1970
// convert the pasted image area into an equivalent select area by mouse
1972
void select_paste_makearea()
1975
int px1, py1, px2, py2;
1978
sa_unselect(); // unselect old area
1980
cc = Fww * Fhh * sizeof(uint16);
1981
sa_pixmap = (uint16 *) zmalloc(cc,"sa_paste"); // pixel map for new area
1982
memset(sa_pixmap,0,cc);
1984
for (py1 = 0; py1 < sacpR_hh; py1++) // map non-transparent pixels
1985
for (px1 = 0; px1 < sacpR_ww; px1++) // into sa_pixmap[]
1987
pix2 = PXMpix(sacpR_info16,px1,py1); // opacity and edge distance
1988
if (pix2[0] == 0) continue; // transparent
1989
px2 = px1 + sacp_orgx;
1990
py2 = py1 + sacp_orgy;
1991
if (px2 < 0 || px2 > Fww-1) continue; // parts may be beyond edges
1992
if (py2 < 0 || py2 > Fhh-1) continue;
1993
ii = py2 * Fww + px2;
1998
sa_mode = 6; // equivalent select-mouse area
1999
sa_fww = Fww; // v.11.08
2001
sa_finish_auto(); // v.11.04
2007
// mouse function - follow mouse drags and move pasted area accordingly
2009
void select_paste_mousefunc()
2011
void select_paste_pixmap();
2013
int mx1, my1, mx2, my2;
2014
static int mdx0, mdy0, mdx1, mdy1;
2016
if (LMclick) { // left mouse click
2018
sacp_orgx = Mxclick - sacpR_ww / 2; // position image at mouse v.10.11
2019
sacp_orgy = Myclick - sacpR_hh / 2;
2020
select_paste_pixmap();
2021
CEF->Fmod = 1; // image is modified
2024
if (! sacp_porg) return; // no select area paste yet
2026
if (Mxposn < sacp_orgx || Mxposn > sacp_orgx + sacpR_ww || // mouse outside select area
2027
Myposn < sacp_orgy || Myposn > sacp_orgy + sacpR_hh)
2028
gdk_window_set_cursor(drWin->window,0); // set normal cursor v.11.03
2030
gdk_window_set_cursor(drWin->window,dragcursor); // set drag cursor v.11.03
2032
if (Mxdrag + Mydrag == 0) return; // no drag underway
2034
if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated
2035
mdx0 = mdx1 = Mxdown;
2036
mdy0 = mdy1 = Mydown;
2039
mx1 = mdx1; // drag start
2041
mx2 = Mxdrag; // drag position
2043
mdx1 = mx2; // next drag start
2046
sacp_orgx += (mx2 - mx1); // move position of select area
2047
sacp_orgy += (my2 - my1); // by mouse drag amount
2048
select_paste_pixmap(); // re-copy area to new position
2049
CEF->Fmod = 1; // image is modified
2055
// copy select area into edit image, starting at sacp_orgx/y
2057
void select_paste_pixmap() // overhauled v.11.02
2059
int px1, py1, px3, py3, opac, dist;
2060
uint16 *pix1, *pix3;
2063
if (sacp_porg) // prior area overlap rectangle
2065
for (py1 = 0; py1 < sacp_phh; py1++) // restore original image pixels v.10.11
2066
for (px1 = 0; px1 < sacp_pww; px1++)
2068
px3 = px1 + sacp_porgx;
2069
py3 = py1 + sacp_porgy;
2070
if (px3 < 0 || px3 >= E3ww) continue; // parts may be beyond edges
2071
if (py3 < 0 || py3 >= E3hh) continue;
2072
pix1 = PXMpix(E1pxm16,px3,py3);
2073
pix3 = PXMpix(E3pxm16,px3,py3);
2080
for (py1 = 0; py1 < sacpR_hh; py1++) // copy paste area pixels to new
2081
for (px1 = 0; px1 < sacpR_ww; px1++) // image overlap rectangle
2083
pix1 = PXMpix(sacpR_info16,px1,py1); // opacity and edge distance
2086
if (opac == 0) continue; // skip transparent pixel
2087
px3 = px1 + sacp_orgx;
2088
py3 = py1 + sacp_orgy;
2089
if (px3 < 0 || px3 >= E3ww) continue; // parts may be beyond edges
2090
if (py3 < 0 || py3 >= E3hh) continue;
2092
pix1 = PXMpix(sacpR_image16,px1,py1); // paste image pixel
2093
pix3 = PXMpix(E3pxm16,px3,py3); // target image pixel
2094
if (dist > sacp_blend) f1 = 1.0; // minor bugfix v.11.06
2095
else f1 = 1.0 * dist / sacp_blend; // opacity reduction from edge blend
2096
f1 = f1 * opac / 65535.0; // opacity reduction from resize/rescale
2098
pix3[0] = f1 * pix1[0] + f2 * pix3[0]; // blend paste and target images
2099
pix3[1] = f1 * pix1[1] + f2 * pix3[1];
2100
pix3[2] = f1 * pix1[2] + f2 * pix3[2];
2103
mwpaint3(sacp_porgx,sacp_porgy,sacp_pww,sacp_phh); // update window for old overlap area
2104
mwpaint3(sacp_orgx,sacp_orgy,sacpR_ww,sacpR_hh); // update window for new overlap area
2106
sacp_porgx = sacp_orgx; // remember location for next call
2107
sacp_porgy = sacp_orgy;
2108
sacp_pww = sacpR_ww;
2109
sacp_phh = sacpR_hh;
2115
/**************************************************************************
2116
select area load from file and save to file functions v.9.9
2117
***************************************************************************/
2119
// Load a select area from a disk file and paste into current image.
2121
void m_select_open(GtkWidget *, cchar *)
2123
char *pfile1, *pfile2, *pp;
2125
zfuncs::F1_help_topic = "area_open_save"; // v.10.8
2127
pp = zgetfile1(ZTX("load select area from a file"),"open",saved_areas_dirk);
2130
pfile1 = strdupz(pp,8,"select_open");
2132
pp = strrchr(pfile1,'/');
2133
if (pp) pp = strrchr(pp,'.');
2134
if (pp) strcpy(pp,".tiff");
2135
else strcat(pfile1,".tiff");
2137
pfile2 = strdupz(pfile1,0,"select_open");
2138
pp = strrchr(pfile2,'.');
2141
PXM_free(sacp_image16); // free prior if any
2142
PXM_free(sacp_info16);
2144
sacp_image16 = TIFFread(pfile1); // .tiff file --> image PXM
2145
if (! sacp_image16) goto fail;
2147
sacp_info16 = TIFFread(pfile2); // .info file --> info PXM
2148
if (! sacp_info16) goto fail;
2153
sacp_ww = sacp_image16->ww; // paste into current image
2154
sacp_hh = sacp_image16->hh;
2155
m_select_paste(0,0);
2161
zmessageACK(mWin,ZTX("cannot open .tiff and .info files"));
2166
// save a select area as a disk file
2168
void m_select_save(GtkWidget *, cchar *)
2170
char *pfile1, *pfile2, *pp;
2172
zfuncs::F1_help_topic = "area_open_save"; // v.10.11
2174
if (sa_mode == 7) return; // a whole image area v.11.01
2175
if (! Factivearea) sa_finish(); // finish select area if not already
2176
if (! Factivearea) return; // v.11.06.1
2177
m_select_copy(0,0); // copy select area to memory
2178
if (! sacp_image16) return;
2180
pp = zgetfile1(ZTX("save select area to a file"),"save",saved_areas_dirk);
2183
pfile1 = strdupz(pp,8,"select_save");
2185
pp = strrchr(pfile1,'/');
2186
if (pp) pp = strrchr(pp,'.');
2187
if (pp) strcpy(pp,".tiff");
2188
else strcat(pfile1,".tiff");
2190
pfile2 = strdupz(pfile1,0,"select_save");
2191
pp = strrchr(pfile2,'.');
2194
TIFFwrite(sacp_image16,pfile1); // image PXM --> .tiff file
2195
TIFFwrite(sacp_info16,pfile2); // info PXM --> .info file
2201
/**************************************************************************/
2203
// Select the whole image as an area.
2204
// Set "edge distance" 1 to 255 from pixel or RGB color brightness.
2205
// Set "blend width" to 255
2206
// Edit function coefficient = edge distance / blend width.
2208
spldat * select_whole_image_curve;
2210
void m_select_whole_image(GtkWidget *, cchar *) // new v.11.01
2212
int select_whole_image_event(zdialog *, cchar *event); // dialog event and completion func
2213
void select_whole_image_curve_update(int spc); // curve update callback function
2215
cchar *title = ZTX("Select Whole Image");
2216
cchar *legend = ZTX("Edit Function Amplifier");
2218
zfuncs::F1_help_topic = "select_whole_image";
2220
if (! curr_file) return; // no image
2221
if (zdsela) return; // select dialog already active
2222
if (Fpreview) edit_fullsize(); // use full-size image
2225
Edit Function Amplifier
2226
------------------------------------------
2229
| curve drawing area |
2232
------------------------------------------
2233
darker areas lighter areas
2235
[+++] [---] [+ -] [- +] [+-+] [-+-]
2236
(o) Brightness (o) Red (o) Green (o) Blue
2237
Curve File: [ Open ] [ Save ]
2241
zdsela = zdialog_new(title,mWin,Bdone,null);
2243
zdialog_add_widget(zdsela,"label","labt","dialog",legend);
2244
zdialog_add_widget(zdsela,"frame","fr1","dialog",0,"expand");
2245
zdialog_add_widget(zdsela,"hbox","hba","dialog");
2246
zdialog_add_widget(zdsela,"label","labda","hba",Bdarker,"space=5");
2247
zdialog_add_widget(zdsela,"label","space","hba",0,"expand");
2248
zdialog_add_widget(zdsela,"label","labba","hba",Blighter,"space=5");
2249
zdialog_add_widget(zdsela,"hbox","hbb","dialog",0,"space=5");
2250
zdialog_add_widget(zdsela,"button","b +++","hbb","+++");
2251
zdialog_add_widget(zdsela,"button","b ---","hbb","‒ ‒ ‒");
2252
zdialog_add_widget(zdsela,"button","b +-", "hbb"," + ‒ ");
2253
zdialog_add_widget(zdsela,"button","b -+", "hbb"," ‒ + ");
2254
zdialog_add_widget(zdsela,"button","b +-+","hbb","+ ‒ +");
2255
zdialog_add_widget(zdsela,"button","b -+-","hbb","‒ + ‒");
2257
zdialog_add_widget(zdsela,"hbox","hbbr","dialog",0,"space=5");
2258
zdialog_add_widget(zdsela,"radio","bright","hbbr",Bbrightness,"space=5");
2259
zdialog_add_widget(zdsela,"radio","red","hbbr",Bred,"space=5");
2260
zdialog_add_widget(zdsela,"radio","green","hbbr",Bgreen,"space=5");
2261
zdialog_add_widget(zdsela,"radio","blue","hbbr",Bblue,"space=5");
2263
zdialog_add_widget(zdsela,"hbox","hbcf","dialog",0,"space=5");
2264
zdialog_add_widget(zdsela,"label","labcf","hbcf",Bcurvefile,"space=5");
2265
zdialog_add_widget(zdsela,"button","load","hbcf",Bopen,"space=5");
2266
zdialog_add_widget(zdsela,"button","save","hbcf",Bsave,"space=5");
2268
GtkWidget *frame = zdialog_widget(zdsela,"fr1"); // setup for curve editing
2269
spldat *sd = splcurve_init(frame,select_whole_image_curve_update); // v.11.01
2270
select_whole_image_curve = sd;
2274
sd->nap[0] = 3; // initial curve anchor points
2275
sd->apx[0][0] = 0.01;
2276
sd->apy[0][0] = 0.5;
2277
sd->apx[0][1] = 0.50;
2278
sd->apy[0][1] = 0.5;
2279
sd->apx[0][2] = 0.99;
2280
sd->apy[0][2] = 0.5;
2281
splcurve_generate(sd,0); // generate curve data
2283
zdialog_stuff(zdsela,"bright",1);
2284
zdialog_stuff(zdsela,"red",0);
2285
zdialog_stuff(zdsela,"green",0);
2286
zdialog_stuff(zdsela,"blue",0);
2287
zdialog_stuff(zdsela,"check",0);
2289
sa_unselect(); // unselect current area if any
2291
zdialog_resize(zdsela,0,360);
2292
zdialog_help(zdsela,"select_whole_image"); // zdialog help topic v.11.08
2293
zdialog_run(zdsela,select_whole_image_event,"80/20"); // run dialog - parallel v.11.07
2294
select_whole_image_event(zdsela,"init"); // initialize default params
2299
// dialog event and completion function
2301
int select_whole_image_event(zdialog *zd, cchar *event)
2303
int ii, kk, cc, base, pixbright, pixdist;
2304
double px, py, xval, yval;
2306
spldat *sd = select_whole_image_curve;
2308
if (zd->zstat) { // done, kill dialog
2309
zdialog_free(zdsela);
2310
zfree(sd); // free curve edit memory
2316
cc = Fww * Fhh * sizeof(uint16); // allocate sa_pixmap[] for new area
2317
sa_pixmap = (uint16 *) zmalloc(cc,"sa_select_image");
2319
sa_minx = 0; // enclosing rectangle
2324
sa_Npixel = Fww * Fhh;
2325
sa_stat = 3; // area status = complete
2326
sa_mode = 7; // area mode = whole image
2327
sa_calced = 1; // edge calculation complete
2328
sa_blend = 255; // "blend width" = 255
2329
sa_fww = Fww; // valid image dimensions v.11.08
2331
Factivearea = 1; // area is active
2334
if (strEqu(event,"load")) { // load saved curve v.11.02
2336
if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog
2340
if (strEqu(event,"save")) { // save curve to file v.11.02
2346
zdialog_fetch(zd,"bright",ii); // set brightness or color to be used
2348
zdialog_fetch(zd,"red",ii);
2350
zdialog_fetch(zd,"green",ii);
2352
zdialog_fetch(zd,"blue",ii);
2354
if (! base) return 0;
2356
if (strnEqu(event,"b ",2)) { // button to move entire curve
2357
for (ii = 0; ii < sd->nap[0]; ii++) {
2358
px = sd->apx[0][ii];
2359
py = sd->apy[0][ii];
2360
if (strEqu(event,"b +++")) py += 0.1;
2361
if (strEqu(event,"b ---")) py -= 0.1;
2362
if (strEqu(event,"b +-")) py += 0.1 - 0.2 * px;
2363
if (strEqu(event,"b -+")) py -= 0.1 - 0.2 * px;
2364
if (strEqu(event,"b +-+")) py -= 0.05 - 0.2 * fabs(px-0.5);
2365
if (strEqu(event,"b -+-")) py += 0.05 - 0.2 * fabs(px-0.5);
2368
sd->apy[0][ii] = py;
2372
splcurve_generate(sd,0); // regenerate the curve
2373
splcurve_draw(0,0,sd);
2375
pixel = (uint8 *) Fpxm8->bmp;
2377
for (ii = 0; ii < Fww * Fhh; ii++) // loop all pixels
2379
pixbright = pixel[0] + pixel[1] + pixel[2] + 1; // brightness of pixel, 1 - 766
2380
if (base == 1) pixbright = pixbright / 3; // brightness, 0 - 255 bugfix v.11.01.2
2381
else if (base == 2) pixbright = 255 * pixel[0] / pixbright; // red part: 0 - 255 = 100% red
2382
else if (base == 3) pixbright = 255 * pixel[1] / pixbright; // green part
2383
else if (base == 4) pixbright = 255 * pixel[2] / pixbright; // blue part
2384
xval = pixbright / 256.0; // curve x-value, 0 to 0.999
2385
kk = 1000 * xval; // speedup v.11.06
2386
if (kk > 999) kk = 999;
2387
yval = sd->yval[0][kk];
2388
pixdist = 255 * yval; // pixel "edge distance" 0 to 255
2389
sa_pixmap[ii] = pixdist | 1; // avoid 0 (pixel outside area)
2393
if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog
2399
// this function is called when curve is edited using mouse
2401
void select_whole_image_curve_update(int)
2403
select_whole_image_event(zdsela,"edit");
2408
/**************************************************************************/
2410
// select area and edit in parallel
2411
// current edit function is applied to areas painted with the mouse
2412
// mouse can be weak or strong, and edits are applied incrementally
2414
// entire image is a select area with all pixel edge distance = 0 (outside area)
2415
// blendwidth = 10000
2416
// pixels painted with mouse have increasing edge distance to amplify edits
2418
int select_edit_radius;
2419
int select_edit_cpower;
2420
int select_edit_epower;
2422
void m_select_edit(GtkWidget *, cchar *) // menu function v.11.02
2424
int select_edit_dialog_event(zdialog *, cchar *event); // dialog event function
2425
void select_edit_mousefunc(); // mouse function
2427
cchar *title = ZTX("Select Area for Edits");
2428
cchar *helptext = ZTX("Press F1 for help");
2431
zfuncs::F1_help_topic = "select_edit";
2433
if (! curr_file) return; // no image
2434
if (zdsela) return; // select area already active
2435
if (Fpreview) edit_fullsize(); // use full-size image
2437
if (! Fpxm16) { // create Fpxm16 if not already
2438
mutex_lock(&Fpixmap_lock);
2439
Fpxm16 = f_load(curr_file,16);
2440
mutex_unlock(&Fpixmap_lock);
2441
if (! Fpxm16) return;
2445
____________________________________________
2446
| Press F1 for help |
2448
| mouse radius [___|v] |
2449
| power: center [___|v] edge [___|v] |
2450
| [x] my mouse [reset area] |
2452
|____________________________________________|
2456
zdsela = zdialog_new(title,mWin,Bdone,null);
2457
zdialog_add_widget(zdsela,"label","labhelp","dialog",helptext,"space=5");
2458
zdialog_add_widget(zdsela,"hbox","hbr","dialog",0,"space=3");
2459
zdialog_add_widget(zdsela,"label","labr","hbr",ZTX("mouse radius"),"space=5");
2460
zdialog_add_widget(zdsela,"spin","radius","hbr","2|500|1|50");
2461
zdialog_add_widget(zdsela,"hbox","hbt","dialog",0,"space=3");
2462
zdialog_add_widget(zdsela,"label","labtc","hbt",ZTX("power: center"),"space=5");
2463
zdialog_add_widget(zdsela,"spin","center","hbt","0|100|1|50");
2464
zdialog_add_widget(zdsela,"label","labte","hbt",ZTX("edge"),"space=5");
2465
zdialog_add_widget(zdsela,"spin","edge","hbt","0|100|1|0");
2466
zdialog_add_widget(zdsela,"hbox","hbr","dialog",0,"space=5");
2467
zdialog_add_widget(zdsela,"check","mymouse","hbr",BmyMouse,"space=5");
2468
zdialog_add_widget(zdsela,"button","reset","hbr",ZTX("reset area"),"space=20");
2470
select_edit_radius = 50;
2471
select_edit_cpower = 50;
2472
select_edit_epower = 0;
2474
sa_unselect(); // unselect current area if any
2475
cc = Fww * Fhh * sizeof(uint16); // allocate sa_pixmap[] for new area
2476
sa_pixmap = (uint16 *) zmalloc(cc,"sa_select_edit");
2477
memset(sa_pixmap,0,cc); // edge distance = 0 for all pixels
2479
sa_minx = 0; // enclosing rectangle
2484
sa_Npixel = Fww * Fhh;
2485
sa_stat = 3; // area status = complete
2486
sa_mode = 7; // area mode = whole image
2487
sa_calced = 1; // edge calculation complete
2488
sa_blend = 10000; // "blend width"
2489
sa_fww = Fww; // valid image dimensions v.11.08
2491
Factivearea = 1; // area is active
2493
zdialog_help(zdsela,"select_edit"); // zdialog help topic v.11.08
2494
zdialog_run(zdsela,select_edit_dialog_event,"80/20"); // run dialog - parallel v.11.07
2499
// tailor whole image area to increase edit power for pixels within the mouse radius
2500
// sa_pixmap[*] = 0 = never touched by mouse
2501
// = 1 = minimum edit power (barely painted)
2502
// = sa_blend = maximum edit power (edit fully applied)
2504
int select_edit_dialog_event(zdialog *zd, cchar *event)
2506
void select_edit_mousefunc(); // mouse function
2509
if (! Factivearea) return 1; // area gone v.11.06.1
2511
if (zd->zstat) // done or cancel
2513
freeMouse(); // disconnect mouse function
2514
zdialog_free(zdsela); // kill dialog
2515
sa_unselect(); // unselect area
2519
if (strEqu(event,"mymouse")) { // toggle mouse capture
2520
zdialog_fetch(zd,"mymouse",mymouse);
2523
zmessageACK(mWin,ZTX("start edit function first")); // no edit function active
2524
zdialog_stuff(zd,"mymouse",0);
2527
takeMouse(zd,select_edit_mousefunc,0); // connect mouse function
2529
else freeMouse(); // disconnect mouse
2532
if (strEqu(event,"radius"))
2533
zdialog_fetch(zd,"radius",select_edit_radius); // set mouse radius
2535
if (strEqu(event,"center"))
2536
zdialog_fetch(zd,"center",select_edit_cpower); // set mouse center power
2538
if (strEqu(event,"edge"))
2539
zdialog_fetch(zd,"edge",select_edit_epower); // set mouse edge power
2541
if (strEqu(event,"reset")) {
2542
sa_unselect(); // unselect current area if any
2543
cc = Fww * Fhh * sizeof(uint16); // allocate sa_pixmap[] for new area
2544
sa_pixmap = (uint16 *) zmalloc(cc,"sa_select_edit");
2545
memset(sa_pixmap,0,cc); // edge distance = 0 for all pixels
2547
sa_minx = 0; // enclosing rectangle
2552
sa_Npixel = Fww * Fhh;
2553
sa_stat = 3; // area status = complete
2554
sa_mode = 7; // area mode = whole image
2555
sa_calced = 1; // edge calculation complete
2556
sa_blend = 10000; // "blend width"
2557
sa_fww = Fww; // valid image dimensions v.11.08
2559
Factivearea = 1; // area is active
2566
// mouse function - adjust edit strength for areas within mouse radius
2567
// "edge distance" is increased for more strength, decreased for less
2569
void select_edit_mousefunc()
2571
int ii, cc, px, py, rx, ry;
2572
int radius, radius2, cpower, epower;
2573
double rad, rad2, power; // v.11.06
2575
if (! CEF) { // no active edit
2580
if (! sa_stat) // area gone? v.11.07
2582
cc = Fww * Fhh * sizeof(uint16); // allocate sa_pixmap[] for new area
2583
sa_pixmap = (uint16 *) zmalloc(cc,"sa_select_edit");
2584
memset(sa_pixmap,0,cc); // clear to zeros
2586
sa_minx = 0; // enclosing rectangle
2591
sa_Npixel = Fww * Fhh;
2592
sa_stat = 3; // area status = complete
2593
sa_mode = 7; // area mode = whole image
2594
sa_calced = 1; // edge calculation complete
2595
sa_blend = 10000; // "blend width"
2596
sa_fww = Fww; // valid image dimensions v.11.08
2598
Factivearea = 1; // area is active
2601
radius = select_edit_radius; // pixel selection radius
2602
radius2 = radius * radius;
2603
cpower = select_edit_cpower;
2604
epower = select_edit_epower;
2606
toparcx = Mxposn - radius; // draw mouse outline circle
2607
toparcy = Myposn - radius;
2608
toparcw = toparch = 2 * radius;
2612
if (LMclick || RMclick) // mouse click
2613
LMclick = RMclick = 0;
2615
if (Mbutton != 1 && Mbutton != 3) // button released
2618
for (rx = -radius; rx <= radius; rx++) // loop every pixel in radius
2619
for (ry = -radius; ry <= radius; ry++)
2621
rad2 = rx * rx + ry * ry;
2622
if (rad2 > radius2) continue; // outside radius
2625
if (px < 0 || px > Fww-1) continue; // off the image edge
2626
if (py < 0 || py > Fhh-1) continue;
2630
power = cpower + rad / radius * (epower - cpower); // power at pixel radius v.11.06
2632
if (Mbutton == 1) // left mouse button
2633
{ // increase edit power
2634
sa_pixmap[ii] += power;
2635
if (sa_pixmap[ii] > sa_blend) sa_pixmap[ii] = sa_blend;
2638
if (Mbutton == 3) // right mouse button
2639
{ // weaken edit power
2640
if (sa_pixmap[ii] <= power) sa_pixmap[ii] = 0;
2641
else sa_pixmap[ii] -= power;
2645
sa_minx = Mxposn - radius; // set temp. smaller area around mouse
2646
if (sa_minx < 0) sa_minx = 0; // speedup v.11.06
2647
sa_maxx = Mxposn + radius;
2648
if (sa_maxx > Fww) sa_maxx = Fww;
2649
sa_miny = Myposn - radius;
2650
if (sa_miny < 0) sa_miny = 0;
2651
sa_maxy = Myposn + radius;
2652
if (sa_maxy > Fhh) sa_maxy = Fhh;
2653
sa_Npixel = (sa_maxx - sa_minx) * (sa_maxy - sa_miny);
2655
paint_toparc(2); // remove mouse outline
2657
zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog
2659
Ftoparc = 1; // restore mouse outline
2662
sa_minx = 0; // restore whole image area v.11.06
2666
sa_Npixel = Fww * Fhh;