1 /* otherwindow.c
2 Copyright (C) 2004-2021 Mark Tyler and Dmitry Groshev
3
4 This file is part of mtPaint.
5
6 mtPaint is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 mtPaint is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with mtPaint in the file COPYING.
18 */
19
20 #include "global.h"
21 #undef _
22 #define _(X) X
23
24 #include "mygtk.h"
25 #include "memory.h"
26 #include "vcode.h"
27 #include "otherwindow.h"
28 #include "ani.h"
29 #include "png.h"
30 #include "mainwindow.h"
31 #include "viewer.h"
32 #include "inifile.h"
33 #include "canvas.h"
34 #include "layer.h"
35 #include "wu.h"
36 #include "channels.h"
37 #include "toolbar.h"
38 #include "csel.h"
39 #include "font.h"
40 #include "icons.h"
41
42 /// NEW IMAGE WINDOW
43
reset_tools()44 void reset_tools()
45 {
46 if (!mem_img[mem_channel]) mem_channel = CHN_IMAGE; // Safety first
47 pressed_select(FALSE); // To prevent automatic paste
48 change_to_tool(DEFAULT_TOOL_ICON);
49
50 init_istate(&mem_state, &mem_image);
51 memset(channel_col_A, 255, NUM_CHANNELS);
52 memset(channel_col_B, 0, NUM_CHANNELS);
53 tool_opacity = 255; // Set opacity to 100% to start with
54
55 if (inifile_get_gboolean("zoomToggle", FALSE))
56 can_zoom = 1; // Always start at 100%
57
58 update_stuff(UPD_RESET | UPD_ALL);
59 }
60
do_new_chores(int undo)61 void do_new_chores(int undo)
62 {
63 set_new_filename(layer_selected, NULL);
64 if (layers_total) layers_notify_changed();
65
66 // No reason to reset tools in undoable mode
67 if (!undo) reset_tools();
68 else update_stuff(UPD_ALL);
69 }
70
do_new_one(int nw,int nh,int nc,png_color * pal,int bpp,int undo)71 int do_new_one(int nw, int nh, int nc, png_color *pal, int bpp, int undo)
72 {
73 int res = 0;
74
75 nw = nw < MIN_WIDTH ? MIN_WIDTH : nw > MAX_WIDTH ? MAX_WIDTH : nw;
76 nh = nh < MIN_HEIGHT ? MIN_HEIGHT : nh > MAX_HEIGHT ? MAX_HEIGHT : nh;
77 mem_cols = nc < 2 ? 2 : nc > 256 ? 256 : nc;
78
79 /* Check memory for undo */
80 if (undo) undo = !undo_next_core(UC_CREATE | UC_GETMEM, nw, nh, bpp, CMASK_IMAGE);
81 /* Create undo frame if requested */
82 if (undo)
83 {
84 undo_next_core(UC_DELETE, nw, nh, bpp, CMASK_ALL);
85 undo = !!(mem_img[CHN_IMAGE] = calloc(1, nw * nh * bpp));
86 }
87 /* Create image anew if all else fails */
88 if (!undo)
89 {
90 res = mem_new( nw, nh, bpp, CMASK_IMAGE );
91 if (res) memory_errors(1); // Not enough memory!
92 }
93 /* *Now* prepare and update palette */
94 if (pal) mem_pal_copy(mem_pal, pal);
95 else mem_bw_pal(mem_pal, 0, nc - 1);
96 update_undo(&mem_image);
97
98 do_new_chores(undo);
99
100 return (res);
101 }
102
clip_to_layer(int layer)103 static int clip_to_layer(int layer)
104 {
105 image_info *img;
106 image_state *state;
107 int cmask, undo = undo_load;
108
109 cmask = cmask_from(mem_clip.img);
110 if (layer == layer_selected)
111 {
112 if (undo) undo = !undo_next_core(UC_CREATE | UC_GETMEM,
113 mem_clip_w, mem_clip_h, mem_clip_bpp, cmask);
114 if (undo) undo_next_core(UC_DELETE, mem_clip_w, mem_clip_h,
115 mem_clip_bpp, CMASK_ALL);
116 else mem_free_image(&mem_image, FREE_IMAGE);
117 img = &mem_image;
118 state = &mem_state;
119 }
120 else
121 {
122 img = &layer_table[layer].image->image_;
123 state = &layer_table[layer].image->state_;
124 *state = mem_state;
125 mem_free_image(img, FREE_IMAGE);
126 mem_pal_copy(img->pal, mem_pal);
127 img->cols = mem_cols;
128 img->trans = mem_xpm_trans;
129 }
130 if (!mem_alloc_image(AI_COPY, img, 0, 0, 0, 0, &mem_clip)) return (0);
131 update_undo(img);
132 state->channel = CHN_IMAGE;
133 return (1);
134 }
135
136 typedef struct {
137 int type, w, h, c, undo, delay, im_type;
138 } newwin_dd;
139
create_new(newwin_dd * dt,void ** wdata)140 static void create_new(newwin_dd *dt, void **wdata)
141 {
142 png_color *pal;
143 int im_type, new_window_type = dt->type;
144 int nw, nh, nc, err = 1, bpp;
145
146
147 run_query(wdata);
148 im_type = dt->im_type;
149 pal = im_type == 1 ? NULL : mem_pal_def;
150
151 nw = dt->w; nh = dt->h; nc = dt->c;
152 if (!new_window_type) undo_load = dt->undo;
153
154 if (im_type == 4) /* Screenshot */
155 {
156 // Ensure that both this window and the main one are offscreen
157 cmd_setv(wdata, (void *)(TRUE), WINDOW_DISAPPEAR);
158
159 if (dt->delay) sleep(dt->delay);
160
161 // Use current layer
162 if (!new_window_type)
163 {
164 err = load_image(NULL, FS_PNG_LOAD, undo_load ?
165 FT_PIXMAP | FTM_UNDO : FT_PIXMAP);
166 if (err == 1)
167 {
168 do_new_chores(undo_load);
169 notify_changed();
170 }
171 }
172 // Add new layer
173 else if (layer_add(0, 0, 1, 0, mem_pal_def, 0))
174 {
175 err = load_image(NULL, FS_LAYER_LOAD, FT_PIXMAP);
176 if (err == 1) layer_show_new();
177 else layer_delete(layers_total);
178 }
179
180 // Let main window onscreen again
181 cmd_setv(main_window_, (void *)(FALSE), WINDOW_DISAPPEAR);
182 }
183
184 if (im_type == 3) /* Clipboard */
185 {
186 // Use current layer
187 if (!new_window_type)
188 {
189 err = import_clipboard(FS_PNG_LOAD);
190 if ((err != 1) && mem_clipboard)
191 err = clip_to_layer(layer_selected);
192 if (err == 1)
193 {
194 do_new_chores(undo_load);
195 notify_changed();
196 }
197 }
198 // Add new layer
199 else if (layer_add(0, 0, 1, 0, mem_pal_def, 0))
200 {
201 err = import_clipboard(FS_LAYER_LOAD);
202 if ((err != 1) && mem_clipboard)
203 err = clip_to_layer(layers_total);
204 if (err == 1) layer_show_new();
205 else layer_delete(layers_total);
206 }
207 }
208
209 /* Fallthrough if error */
210 if (err != 1) im_type = 0;
211
212 /* RGB / Greyscale / Indexed */
213 bpp = im_type == 0 ? 3 : 1;
214 if (im_type > 2); // Successfully done above
215 else if (new_window_type == 1) // Layer
216 layer_new(nw, nh, bpp, nc, pal, CMASK_IMAGE);
217 else // Image
218 {
219 /* Nothing to undo if image got deleted already */
220 err = do_new_one(nw, nh, nc, pal, bpp, undo_load && mem_img[CHN_IMAGE]);
221 if (err > 0)
222 {
223 /* System was unable to allocate memory for
224 * image, using 8x8 instead */
225 nw = mem_width;
226 nh = mem_height;
227 }
228
229 inifile_set_gint32("lastnewWidth", nw );
230 inifile_set_gint32("lastnewHeight", nh );
231 inifile_set_gint32("lastnewCols", nc );
232 inifile_set_gint32("lastnewType", im_type );
233 }
234
235 run_destroy(wdata);
236 }
237
238 static char *newwin_txt[] = { _("24 bit RGB"), _("Greyscale"),
239 _("Indexed Palette"), _("From Clipboard"), _("Grab Screenshot") };
240
241 #define WBbase newwin_dd
242 static void *newwin_code[] = {
243 IF(type), WINDOWm(_("New Layer")), // modal
244 UNLESS(type), WINDOWm(_("New Image")), // modal
245 TABLE2(3), OPNAME0,
246 TSPIN(_("Width"), w, MIN_WIDTH, MAX_WIDTH),
247 TSPIN(_("Height"), h, MIN_HEIGHT, MAX_HEIGHT),
248 TSPIN(_("Colours"), c, 2, 256),
249 WDONE,
250 BORDER(RPACK, 0),
251 // !!! Commandline mode leaves GDK uninitialized, but screenshot needs it
252 UNLESSv(cmd_mode), RPACK(newwin_txt, 5, 0, im_type),
253 IFv(cmd_mode), RPACK(newwin_txt, 4, 0, im_type),
254 OPNAME(""),
255 uSPIN(delay, 0, 100), OPNAME("Delay"), // For screenshots from scripts
256 UNLESS(type), CHECK(_("Undoable"), undo),
257 HSEPl(200),
258 OKBOXB(_("Create"), create_new, _("Cancel"), NULL),
259 WSHOW
260 };
261 #undef WBbase
262
generic_new_window(int type)263 void generic_new_window(int type) // 0=New image, 1=New layer
264 {
265 newwin_dd tdata = { type, mem_width, mem_height, mem_cols, undo_load, 0 };
266 int im_type = 3 - mem_img_bpp;
267
268 if (!type)
269 {
270 if ((!script_cmds || !undo_load) &&
271 (check_for_changes() == 1)) return;
272
273 tdata.w = inifile_get_gint32("lastnewWidth", DEFAULT_WIDTH);
274 tdata.h = inifile_get_gint32("lastnewHeight", DEFAULT_HEIGHT);
275 tdata.c = inifile_get_gint32("lastnewCols", 256);
276 im_type = inifile_get_gint32("lastnewType", 2);
277 if ((im_type < 0) || (im_type > 2)) im_type = 0;
278 }
279 tdata.im_type = im_type;
280
281 run_create_(newwin_code, &tdata, sizeof(tdata), script_cmds);
282 }
283
284
285 /// PATTERN & BRUSH CHOOSER WINDOW
286
287 static void **pat_window;
288
289 typedef struct {
290 int mode;
291 int wh[3];
292 int xs, ys, xw, max;
293 unsigned char *rgb;
294 } pattern_dd;
295
296 #define PAL_SLOT_SIZE 10
297
make_crgb(unsigned char * tmp,int channel)298 static void make_crgb(unsigned char *tmp, int channel)
299 {
300 int col, k, r, g, b;
301
302 if (channel <= CHN_IMAGE) /* Palette as such */
303 {
304 pal2rgb(tmp, mem_pal, mem_cols, 256);
305 return;
306 }
307
308 if (channel < NUM_CHANNELS) /* Utility */
309 {
310 r = channel_rgb[channel][0];
311 g = channel_rgb[channel][1];
312 b = channel_rgb[channel][2];
313 }
314 else r = g = b = 255; /* Opacity */
315
316 for (col = 0; col < 256; col++)
317 {
318 k = r * col;
319 *tmp++ = (k + (k >> 8) + 1) >> 8;
320 k = g * col;
321 *tmp++ = (k + (k >> 8) + 1) >> 8;
322 k = b * col;
323 *tmp++ = (k + (k >> 8) + 1) >> 8;
324 }
325 }
326
render_color_grid(unsigned char * rgb,int w,int h,int cellsize,unsigned char * pp)327 static void render_color_grid(unsigned char *rgb, int w, int h, int cellsize,
328 unsigned char *pp)
329 {
330 unsigned char *tmp;
331 int i, j, k, row = w * 3;
332
333
334 for (i = 0; i < h; i += cellsize)
335 {
336 tmp = rgb + i * row;
337 for (j = 0; j < row; j += cellsize * 3 , pp += 3)
338 {
339 tmp[j + 0] = pp[0];
340 tmp[j + 1] = pp[1];
341 tmp[j + 2] = pp[2];
342 for (k = j + 3; k < j + cellsize * 3 - 3; k++)
343 tmp[k] = tmp[k - 3];
344 }
345 for (j = i + 1; j < i + cellsize - 1; j++)
346 memcpy(rgb + j * row, tmp, row);
347 }
348 }
349
delete_pat(pattern_dd * dt,void ** wdata)350 static int delete_pat(pattern_dd *dt, void **wdata)
351 {
352 run_destroy(wdata);
353 pat_window = NULL;
354
355 return (FALSE);
356 }
357
key_pat(pattern_dd * dt,void ** wdata,int what,void ** where,key_ext * key)358 static int key_pat(pattern_dd *dt, void **wdata, int what, void **where,
359 key_ext *key)
360 {
361 /* xine-ui sends bogus keypresses so don't delete on this */
362 if (!XINE_FAKERY(key->key)) delete_pat(dt, wdata);
363
364 return (TRUE);
365 }
366
click_pat(pattern_dd * dt,void ** wdata,int what,void ** where,mouse_ext * mouse)367 static int click_pat(pattern_dd *dt, void **wdata, int what, void **where,
368 mouse_ext *mouse)
369 {
370 int pat_no, mx = mouse->x, my = mouse->y;
371
372 pat_no = mx / dt->xs + dt->xw * (my / dt->ys);
373 pat_no = pat_no < 0 ? 0 : pat_no > dt->max ? dt->max : pat_no;
374
375 // if (mouse->count != 1) return (FALSE); // only single click
376 if (dt->mode == CHOOSE_COLOR)
377 {
378 int ab;
379
380 if (!(ab = mouse->button == 3) && (mouse->button != 1))
381 return (FALSE); // Only left or right click
382 mem_col_[ab] = pat_no;
383 mem_col_24[ab] = mem_pal[pat_no];
384 update_stuff(UPD_AB);
385 }
386 else if (dt->mode == CHOOSE_PATTERN)
387 {
388 if (mouse->button == 1) mem_tool_pat = pat_no; // Left
389 else if (pattern_B && (mouse->button == 3)) // Right
390 mem_tool_pat_B = pat_no;
391 else return (FALSE);
392 update_stuff(UPD_PAT);
393 }
394 else if (mouse->button != 1) return (FALSE); // Left click only
395 else /* if (dt->mode == CHOOSE_BRUSH) */
396 {
397 mem_set_brush(pat_no);
398 change_to_tool(TTB_PAINT);
399 update_stuff(UPD_BRUSH);
400 }
401
402 return (TRUE);
403 }
404
405 #define WBbase pattern_dd
406 void *pattern_code[] = {
407 BORDER(POPUP, 4),
408 WPMOUSE, POPUP(_("Pattern Chooser")),
409 EVENT(KEY, key_pat),
410 /* !!! Given the window is modal, this makes it closeable by button
411 * release anywhere over mtPaint's main window - WJ */
412 EVENT(RMOUSE, delete_pat),
413 TALLOC(rgb, wh[2]),
414 RGBIMAGE(rgb, wh), EVENT(MOUSE, click_pat),
415 WEND
416 };
417 #undef WBbase
418
choose_pattern(int typ)419 void choose_pattern(int typ) // Bring up pattern chooser (0) or brush (1)
420 {
421 unsigned char pp[768];
422 pattern_dd tdata, *dt;
423
424 if (pat_window) return; // Already displayed
425
426 tdata.mode = typ;
427 tdata.wh[2] = 3; // bpp
428 if (typ == CHOOSE_PATTERN)
429 {
430 tdata.wh[0] = patterns_grid_w * PATTERN_CELL;
431 tdata.wh[1] = patterns_grid_h * PATTERN_CELL;
432 tdata.xs = tdata.ys = PATTERN_CELL;
433 tdata.xw = patterns_grid_w;
434 tdata.max = patterns_grid_w * patterns_grid_h - 1;
435 }
436 else if (typ == CHOOSE_BRUSH)
437 {
438 tdata.rgb = mem_brushes;
439 tdata.wh[0] = PATCH_WIDTH;
440 tdata.wh[1] = PATCH_HEIGHT;
441 tdata.wh[2] = 0; // no allocation
442 tdata.xs = tdata.ys = BRUSH_CELL;
443 tdata.xw = BRUSH_GRID_W;
444 tdata.max = NUM_BRUSHES - 1;
445 }
446 else /* if (typ == CHOOSE_COLOR) */
447 {
448 tdata.wh[0] = tdata.wh[1] = 16 * PAL_SLOT_SIZE - 1;
449 tdata.xs = tdata.ys = PAL_SLOT_SIZE;
450 tdata.xw = 16;
451 tdata.max = mem_cols - 1;
452 }
453
454 tdata.wh[2] *= tdata.wh[0] * tdata.wh[1]; // alloc size
455
456 pat_window = run_create(pattern_code, &tdata, sizeof(tdata));
457
458 /* Prepare image */
459 dt = GET_DDATA(pat_window);
460 if (typ == CHOOSE_PATTERN) render_patterns(dt->rgb);
461 else if (typ == CHOOSE_COLOR)
462 {
463 make_crgb(pp, CHN_IMAGE);
464 render_color_grid(dt->rgb, dt->wh[0], dt->wh[1],
465 PAL_SLOT_SIZE, pp);
466 }
467
468 cmd_showhide(pat_window, TRUE);
469 }
470
471
472 /// ADD COLOURS TO PALETTE WINDOW
473
do_add_cols(spin1_dd * dt,void ** wdata)474 static int do_add_cols(spin1_dd *dt, void **wdata)
475 {
476 int i;
477
478 run_query(wdata);
479 i = dt->n[0];
480 if (i != mem_cols)
481 {
482 spot_undo(UNDO_PAL);
483
484 if (i > mem_cols) memset(mem_pal + mem_cols, 0,
485 (i - mem_cols) * sizeof(png_color));
486
487 mem_cols = i;
488 update_stuff(UPD_PAL);
489 }
490 return (TRUE);
491 }
492
pressed_add_cols()493 void pressed_add_cols()
494 {
495 static spin1_dd tdata = {
496 { _("Set Palette Size"), spin1_code, FW_FN(do_add_cols) },
497 { 256, 2, 256 } };
498 run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
499 }
500
501 /* Generic code to handle UI needs of common image transform tasks */
502
do_filterwindow(filterwindow_dd * dt,void ** wdata)503 static void do_filterwindow(filterwindow_dd *dt, void **wdata)
504 {
505 if (dt->evt(dt, wdata) || script_cmds) run_destroy(wdata);
506 update_stuff(UPD_IMG);
507 }
508
509 #define WBbase filterwindow_dd
510 void *filterwindow_code[] = {
511 WINDOWpm(name), // modal
512 DEFW(300),
513 HSEP,
514 CALLp(code),
515 HSEP,
516 OKBOX(_("Apply"), do_filterwindow, _("Cancel"), NULL),
517 WSHOW
518 };
519 #undef WBbase
520
521 #define WBbase spin1_dd
522 void *spin1_code[] = { SPINa(n), RET };
523 #undef WBbase
524
525 /// BACTERIA EFFECT
526
do_bacteria(spin1_dd * dt,void ** wdata)527 static int do_bacteria(spin1_dd *dt, void **wdata)
528 {
529 run_query(wdata);
530 spot_undo(UNDO_FILT);
531 mem_bacteria(dt->n[0]);
532 mem_undo_prepare();
533 return (FALSE);
534 }
535
pressed_bacteria()536 void pressed_bacteria()
537 {
538 static spin1_dd tdata = {
539 { _("Bacteria Effect"), spin1_code, FW_FN(do_bacteria) },
540 { 10, 1, 100 } };
541 run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
542 }
543
544
545 /// SORT PALETTE COLOURS
546
547 int spal_mode;
548
549 typedef struct {
550 int rgb, rev, start[3], end[3];
551 } spal_dd;
552
spal_evt(spal_dd * dt,void ** wdata,int what)553 static void spal_evt(spal_dd *dt, void **wdata, int what)
554 {
555 int index1, index2;
556
557 run_query(wdata);
558 index1 = dt->start[0];
559 index2 = dt->end[0];
560
561 if (index1 != index2)
562 {
563 spot_undo(UNDO_XPAL);
564 mem_pal_sort(spal_mode, index1, index2, dt->rev);
565 mem_undo_prepare();
566 update_stuff(UPD_TPAL);
567 }
568 if (what == op_EVT_OK) run_destroy(wdata);
569 }
570
571 static char *spal_txt[] = {
572 _("Hue"), _("Saturation"), _("Luminance"), _("Brightness"),
573 _("Distance to A"),
574 _("Red"), _("Green"), _("Blue"), _("Projection to A->B"),
575 _("Frequency") };
576
577 #define WBbase spal_dd
578 static void *spal_code[] = {
579 WINDOWm(_("Sort Palette Colours")), // modal
580 TABLE2(2), OPNAME0,
581 TSPINa(_("Start Index"), start),
582 TSPINa(_("End Index"), end),
583 WDONE,
584 BORDER(RPACK, 0),
585 IF(rgb), RPACKv(spal_txt, 9, 5, spal_mode),
586 UNLESS(rgb), RPACKv(spal_txt, 10, 5, spal_mode),
587 OPNAME(""), // default
588 CHECKb(_("Reverse Order"), rev, "palrevSort"),
589 HSEPl(200),
590 OKBOX3B(_("OK"), spal_evt, _("Cancel"), NULL, _("Apply"), spal_evt),
591 WSHOW
592 };
593 #undef WBbase
594
pressed_sort_pal()595 void pressed_sort_pal()
596 {
597 spal_dd tdata = { mem_img_bpp == 3, FALSE, { 0, 0, mem_cols - 1 },
598 { mem_cols - 1, 0, mem_cols - 1 } };
599 run_create_(spal_code, &tdata, sizeof(tdata), script_cmds);
600 }
601
602
603 /// BRIGHTNESS-CONTRAST-SATURATION WINDOW
604
605 transform_state def_bcsp = { { 0, 0, 0, 8, 100, 0 }, { TRUE, TRUE, TRUE } };
606
607 int mem_preview, mem_preview_clip, brcosa_auto;
608
609 typedef struct {
610 transform_state t;
611 int rgb, show, tmode;
612 int rgbclip, pflag;
613 int c01[3 * 2];
614 void **xb; // Transform mode toggle
615 void **sss[BRCOSA_ITEMS]; // Spinsliders
616 void **buttons[4]; // Preview/Reset/Apply/OK
617 void **xtra; // "Details" area
618 png_color pal[256];
619 } brcosa_dd;
620
check_transform_defaults(transform_state * t)621 static int check_transform_defaults(transform_state *t)
622 {
623 int i;
624
625 for (i = 0; i < BRCOSA_ITEMS; i++)
626 if (t->bcsp[i] != (i != BRCOSA_POSTERIZE ? def_bcsp.bcsp[i] :
627 DEF_POSTERIZE(t->pmode))) return (TRUE);
628 return (FALSE);
629 }
630
brcosa_preview(brcosa_dd * dt,void * cause)631 static void brcosa_preview(brcosa_dd *dt, void *cause)
632 {
633 int j, update = UPD_PAL;
634 int do_pal = TRUE; // palette processing
635
636 mem_pal_copy(mem_pal, dt->pal); // Get back normal palette
637
638 mem_bcsp[0] = dt->t;
639
640 if (mem_img_bpp == 3)
641 {
642 update = UPD_RENDER | UPD_PAL;
643 do_pal = dt->pflag;
644 // Unless user has just cleared toggle
645 if (!do_pal && (cause != &dt->pflag)) update = UPD_RENDER;
646 }
647 if (do_pal)
648 {
649 j = dt->c01[0] > dt->c01[3] ? 3 : 0;
650 transform_pal(mem_pal, dt->pal, dt->c01[j], dt->c01[j ^ 3]);
651 }
652 update_stuff(update);
653 }
654
brcosa_changed(brcosa_dd * dt,void ** wdata,int what,void ** where)655 static void brcosa_changed(brcosa_dd *dt, void **wdata, int what, void **where)
656 {
657 brcosa_preview(dt, cmd_read(where, dt)); // Update everything
658 }
659
brcosa_moved(brcosa_dd * dt,void ** wdata,int what,void ** where)660 static void brcosa_moved(brcosa_dd *dt, void **wdata, int what, void **where)
661 {
662 int i, state;
663 void *cause = cmd_read(where, dt);
664
665 if (script_cmds) return; // Useless in scripted mode
666 if (brcosa_auto) brcosa_preview(dt, NULL);
667 if (cause == &brcosa_auto) cmd_showhide(dt->buttons[0], !brcosa_auto);
668
669 // Set 3 brcosa button as sensitive if the user has assigned changes
670 state = check_transform_defaults(&dt->t);
671 for (i = 1; i < 4; i++) cmd_sensitive(dt->buttons[i], state);
672 }
673
brcosa_btn(brcosa_dd * dt,void ** wdata,int what)674 static void brcosa_btn(brcosa_dd *dt, void **wdata, int what)
675 {
676 unsigned char *mask, *mask0, *tmp, *xbuf;
677 int i;
678
679 mem_pal_copy(mem_pal, dt->pal);
680
681 if (what == op_EVT_CANCEL);
682 else if (!dt->tmode) // OK/Apply
683 {
684 // !!! Buttons disabled for default values
685 spot_undo(UNDO_COL);
686
687 run_query(wdata); // This may modify palette if preview active
688
689 brcosa_preview(dt, NULL); // This definitely modifies it
690 while (mem_preview && (mem_img_bpp == 3)) // This modifies image
691 {
692 mask = malloc(mem_width * 4);
693 if (!mask) break;
694 xbuf = mask + mem_width;
695 mask0 = NULL;
696 if (!channel_dis[CHN_MASK]) mask0 = mem_img[CHN_MASK];
697 tmp = mem_img[CHN_IMAGE];
698 for (i = 0; i < mem_height; i++)
699 {
700 prep_mask(0, 1, mem_width, mask, mask0, tmp);
701 do_transform(0, 1, mem_width, mask, xbuf, tmp, 255);
702 process_img(0, 1, mem_width, mask, tmp, tmp, xbuf,
703 NULL, 3, BLENDF_SET | BLENDF_INVM);
704 if (mask0) mask0 += mem_width;
705 tmp += mem_width * 3;
706 }
707 free(mask);
708 break;
709 }
710 if (mem_preview_clip && (mem_img_bpp == 3) && (mem_clip_bpp == 3))
711 {
712 // This modifies clipboard
713 do_transform(0, 1, mem_clip_w * mem_clip_h, NULL,
714 mem_clipboard, mem_clipboard, 0);
715 }
716 mem_undo_prepare();
717 }
718 else // OK/Apply for transform mode
719 {
720 run_query(wdata); // May modify palette, through preview
721 mem_bcsp[1] = dt->t; // Safe: buttons disabled for default values
722 mem_pal_copy(mem_pal, dt->pal); // Restore palette
723 if (mem_blend) update_stuff(UPD_MODE);
724 }
725
726 // Disable preview for final update
727 if (what != op_EVT_CLICK) mem_preview = mem_preview_clip = FALSE;
728
729 update_stuff(UPD_PAL);
730
731 if (what != op_EVT_CLICK) // OK/Cancel
732 {
733 void **xb = dt->xb;
734
735 run_destroy(wdata);
736 if (xb) // Transform mode
737 {
738 // Toggle transform off if still uninitialized
739 if (!mem_bcsp[1].bcsp[BRCOSA_POSTERIZE])
740 cmd_set(xb, FALSE);
741 // Show its dialog
742 cmd_showhide(GET_WINDOW(wdata_slot(xb)), TRUE);
743 }
744 }
745 else if (!dt->tmode) // Apply
746 {
747 // Reload palette and redo preview
748 mem_pal_copy(dt->pal, mem_pal);
749 brcosa_preview(dt, NULL);
750 }
751 }
752
click_brcosa_show_toggle(brcosa_dd * dt,void ** wdata,int what,void ** where)753 static void click_brcosa_show_toggle(brcosa_dd *dt, void **wdata, int what,
754 void **where)
755 {
756 cmd_read(where, dt);
757 cmd_showhide(dt->xtra, dt->show);
758 }
759
click_brcosa_reset(brcosa_dd * dt)760 static void click_brcosa_reset(brcosa_dd *dt)
761 {
762 int i;
763
764 mem_pal_copy(mem_pal, dt->pal);
765
766 for (i = 0; i < BRCOSA_ITEMS; i++)
767 cmd_set(dt->sss[i], i != BRCOSA_POSTERIZE ? def_bcsp.bcsp[i] :
768 DEF_POSTERIZE(dt->t.pmode));
769
770 update_stuff(UPD_PAL);
771 }
772
brcosa_posterize_changed(brcosa_dd * dt,void ** wdata,int what,void ** where)773 static void brcosa_posterize_changed(brcosa_dd *dt, void **wdata, int what,
774 void **where)
775 {
776 int vvv[3];
777 int i, v, m;
778
779 cmd_read(where, dt);
780 m = dt->t.pmode;
781 if (!def_bcsp.pmode ^ !m) // From/to bitwise
782 {
783 cmd_read(dt->sss[BRCOSA_POSTERIZE], dt);
784 v = dt->t.bcsp[BRCOSA_POSTERIZE];
785 if (!m) // To bitwise
786 {
787 for (i = 0 , v -= 1; v ; i++ , v >>= 1);
788 vvv[0] = i;
789 vvv[1] = 1;
790 vvv[2] = 8;
791 }
792 else // From bitwise
793 {
794 vvv[0] = 1 << v;
795 vvv[1] = 2;
796 vvv[2] = 256;
797 }
798 cmd_setv(dt->sss[BRCOSA_POSTERIZE], vvv, SPIN_ALL);
799 }
800 else if (brcosa_auto) brcosa_preview(dt, NULL);
801 def_bcsp.pmode = m; // Remember
802 }
803
804 static char *pos_txt[] = { _("Bitwise"), _("Truncated"), _("Rounded") };
805
806 #define WBbase brcosa_dd
807 static void *brcosa_code[] = {
808 WPMOUSE, WINDOWm(_("Transform Colour")), NORESIZE,
809 BORDER(TABLE, 10), BORDER(SPINSLIDE, 0),
810 TABLE2(6), OPNAME0,
811 REF(sss[4]), TSPINSLIDE(_("Gamma"), t.bcsp[4], 20, 500),
812 EVENT(CHANGE, brcosa_moved),
813 REF(sss[0]), TSPINSLIDE(_("Brightness"), t.bcsp[0], -255, 255),
814 EVENT(CHANGE, brcosa_moved),
815 REF(sss[1]), TSPINSLIDE(_("Contrast"), t.bcsp[1], -100, 100),
816 EVENT(CHANGE, brcosa_moved),
817 REF(sss[2]), TSPINSLIDE(_("Saturation"), t.bcsp[2], -100, 100),
818 EVENT(CHANGE, brcosa_moved),
819 REF(sss[5]), TSPINSLIDE(_("Hue"), t.bcsp[5], -1529, 1529),
820 EVENT(CHANGE, brcosa_moved),
821 TLABEL(_("Posterize")),
822 REF(sss[3]),
823 UNLESS(t.pmode), T1SPINSLIDE(t.bcsp[3], 1, 8),
824 IF(t.pmode), T1SPINSLIDE(t.bcsp[3], 2, 256),
825 EVENT(CHANGE, brcosa_moved),
826 WDONE,
827 /// MIDDLE SECTION
828 HSEP,
829 HBOX,
830 CHECKb(_("Show Detail"), show, "transcol_show"), UNNAME,
831 EVENT(CHANGE, click_brcosa_show_toggle), TRIGGER,
832 WDONE,
833 BORDER(TABLE, 0),
834 REF(xtra), TABLEr(4, 4),
835 TLABEL(_("Posterize type")),
836 BORDER(OPT, 0),
837 TLOPTle(pos_txt, 3, t.pmode, brcosa_posterize_changed, 1, 0, 2),
838 UNLESSx(tmode, 1),
839 UNLESS(rgb), TLLABEL(_("Palette"), 0, 2),
840 IFx(rgb, 2),
841 TLCHECK(_("Palette"), pflag, 0, 2),
842 EVENT(CHANGE, brcosa_changed),
843 TLCHECKv(_("Image"), mem_preview, 0, 1),
844 EVENT(CHANGE, brcosa_changed),
845 IFx(rgbclip, 3),
846 TLCHECKvl(_("Clipboard"), mem_preview_clip, 1, 1, 2),
847 EVENT(CHANGE, brcosa_changed),
848 ENDIF(3),
849 ENDIF(2),
850 TLHBOXl(1, 2, 2),
851 BORDER(SPIN, 0),
852 XSPINa(c01[0]), EVENT(CHANGE, brcosa_changed), OPNAME("from"),
853 XSPINa(c01[3]), EVENT(CHANGE, brcosa_changed), OPNAME("to"),
854 WDONE,
855 ENDIF(1),
856 TLCHECKv(_("Auto-Preview"), brcosa_auto, 0, 3), UNNAME,
857 EVENT(CHANGE, brcosa_moved), TRIGGER,
858 TLCHECK(_("Red"), t.allow[0], 1, 3), EVENT(CHANGE, brcosa_changed),
859 TLCHECK(_("Green"), t.allow[1], 2, 3), EVENT(CHANGE, brcosa_changed),
860 TLCHECK(_("Blue"), t.allow[2], 3, 3), EVENT(CHANGE, brcosa_changed),
861 WDONE,
862 /// BOTTOM AREA
863 HSEP,
864 EQBOXB,
865 BORDER(BUTTON, 4),
866 CANCELBTN(_("Cancel"), brcosa_btn),
867 REF(buttons[0]), BUTTON(_("Preview"), brcosa_changed),
868 REF(buttons[1]), BUTTON(_("Reset"), click_brcosa_reset),
869 REF(buttons[2]), BUTTON(_("Apply"), brcosa_btn),
870 REF(buttons[3]), OKBTN(_("OK"), brcosa_btn),
871 WSHOW
872 };
873 #undef WBbase
874
pressed_brcosa(void ** xb)875 void pressed_brcosa(void **xb)
876 {
877 brcosa_dd tdata = { def_bcsp, mem_img_bpp == 3, FALSE, !!xb,
878 mem_clipboard && (mem_clip_bpp == 3), FALSE,
879 { 0, 0, mem_cols - 1, mem_cols - 1, 0, mem_cols - 1 }, xb };
880
881 tdata.t.bcsp[BRCOSA_POSTERIZE] = DEF_POSTERIZE(tdata.t.pmode);
882 /* Remember settings for transform mode if initialized */
883 if (xb && mem_bcsp[1].bcsp[BRCOSA_POSTERIZE]) tdata.t = mem_bcsp[1];
884 mem_pal_copy(tdata.pal, mem_pal); // Remember original palette
885 mem_bcsp[0] = tdata.t; // !!! In case a redraw happens inside run_create()
886
887 mem_preview = TRUE; // Enable live preview in RGB mode
888
889 run_create_(brcosa_code, &tdata, sizeof(tdata), script_cmds);
890 }
891
892
893 /// RESIZE/RESCALE WINDOWS
894
895 typedef struct {
896 int script, mode, rgb;
897 int fix, gamma;
898 int w, h, x, y;
899 void **spin[4]; // w, h, x, y
900 void **book;
901 } sisca_dd;
902
903
sisca_moved(sisca_dd * dt,void ** wdata,int what,void ** where)904 static void sisca_moved(sisca_dd *dt, void **wdata, int what, void **where)
905 {
906 int w, h, nw, idx;
907
908 idx = cmd_read(where, dt) != &dt->h; // _other_ spin index: w/h
909 if (!dt->fix) return;
910 w = dt->w; h = dt->h;
911 if (!idx)
912 {
913 nw = (h * mem_width * 2 + mem_height) / (mem_height * 2);
914 nw = nw < 1 ? 1 : nw > MAX_WIDTH ? MAX_WIDTH : nw;
915 if (nw == w) return;
916 }
917 else
918 {
919 nw = (w * mem_height * 2 + mem_width) / (mem_width * 2);
920 nw = nw < 1 ? 1 : nw > MAX_HEIGHT ? MAX_HEIGHT : nw;
921 if (nw == h) return;
922 }
923 dt->fix = FALSE; // Prevent readjusting the original
924 cmd_set(dt->spin[idx], nw); /* Other coordinate */
925 dt->fix = TRUE;
926 }
927
alert_same_geometry()928 static void alert_same_geometry()
929 {
930 alert_box(_("Error"), _("New geometry is the same as now - nothing to do."), NULL);
931 }
932
933 static int scale_mode = 6;
934 static int resize_mode = 0;
935 static int boundary_mode = BOUND_MIRROR;
936 int sharper_reduce;
937
click_sisca_ok(sisca_dd * dt,void ** wdata)938 static void click_sisca_ok(sisca_dd *dt, void **wdata)
939 {
940 int nw, nh, ox, oy, res, scale_type = 0, gcor = FALSE;
941
942 run_query(wdata);
943 nw = dt->w; nh = dt->h; ox = dt->x; oy = dt->y;
944
945 if (!((nw ^ mem_width) | (nh ^ mem_height) | ox | oy))
946 {
947 if (!script_cmds) alert_same_geometry();
948 else // Allow a noop in script mode
949 {
950 spot_undo(UNDO_PAL);
951 run_destroy(wdata);
952 }
953 return;
954 }
955
956 if (dt->mode)
957 {
958 if (mem_img_bpp == 3)
959 {
960 scale_type = scale_mode;
961 gcor = dt->gamma;
962 }
963 res = mem_image_scale(nw, nh, scale_type, gcor, sharper_reduce,
964 boundary_mode);
965 }
966 else res = mem_image_resize(nw, nh, ox, oy, resize_mode);
967
968 if (!res)
969 {
970 update_stuff(UPD_GEOM);
971 run_destroy(wdata);
972 }
973 else memory_errors(res);
974 }
975
memory_errors(int type)976 void memory_errors(int type)
977 {
978 if ( type == 1 )
979 alert_box(_("Error"), _("The operating system cannot allocate the memory for this operation."), NULL);
980 if ( type == 2 )
981 alert_box(_("Error"), _("You have not allocated enough memory in the Preferences window for this operation."), NULL);
982 }
983
click_sisca_centre(sisca_dd * dt,void ** wdata)984 static void click_sisca_centre(sisca_dd *dt, void **wdata)
985 {
986 int nw = dt->w, nh = dt->h; // !!! sisca_moved() keeps these updated
987
988 cmd_set(dt->spin[2], (nw - mem_width) / 2);
989 cmd_set(dt->spin[3], (nh - mem_height) / 2);
990 }
991
992 static char *bound_modes[] = { _("Mirror"), _("Tile"), _("Void") };
993 static char *resize_modes[] = { _("Clear"), _("Tile"), _("Mirror tile"), NULL };
994 static char *scale_modes[] = {
995 _("Nearest Neighbour"),
996 _("Bilinear / Area Mapping"),
997 _("Bicubic"),
998 _("Bicubic edged"),
999 _("Bicubic better"),
1000 _("Bicubic sharper"),
1001 _("Blackman-Harris"),
1002 NULL
1003 };
1004
1005 #define WBbase sisca_dd
1006 static void *sisca_code[] = {
1007 IF(mode), WINDOWm(_("Scale Canvas")),
1008 UNLESS(mode), WINDOWm(_("Resize Canvas")),
1009 TABLE(3, 3), // !!! in fact 5 rows in resize mode
1010 BORDER(LABEL, 0),
1011 TLLABEL(_("Original "), 0, 1), TLNOSPIN(w, 1, 1), TLNOSPIN(h, 2, 1),
1012 TLABEL(_("New")),
1013 TLLABEL(_("Width "), 1, 0),
1014 REF(spin[0]), UNLESS(script), TLSPIN(w, 1, MAX_WIDTH, 1, 2),
1015 IF(script), uSCALE(w, 1, MAX_WIDTH),
1016 EVENT(CHANGE, sisca_moved),
1017 TLLABEL(_("Height "), 2, 0),
1018 REF(spin[1]), UNLESS(script), TLSPIN(h, 1, MAX_HEIGHT, 2, 2),
1019 IF(script), uSCALE(h, 1, MAX_HEIGHT),
1020 EVENT(CHANGE, sisca_moved),
1021 UNLESSx(mode, 1),
1022 TLABEL(_("Offset")),
1023 REF(spin[2]), TLSPIN(x, -MAX_WIDTH, MAX_WIDTH, 1, 3), OPNAME("X"),
1024 REF(spin[3]), TLSPIN(y, -MAX_HEIGHT, MAX_HEIGHT, 2, 3), OPNAME("Y"),
1025 TLBUTTONs(_("Centre"), click_sisca_centre, 0, 4),
1026 ENDIF(1),
1027 WDONE,
1028 HSEP,
1029 BORDER(RPACK, 0),
1030 IF(rgb), HBOX, IF(rgb), VBOX,
1031 IFx(mode, 1),
1032 CHECKb(_("Fix Aspect Ratio"), fix, "scaleAspect"),
1033 EVENT(CHANGE, sisca_moved),
1034 ENDIF(1),
1035 IFx(rgb, 1),
1036 CHECK(_("Gamma corrected"), gamma),
1037 WDONE, EVBOX,
1038 BOOKBTN(_("Settings"), book),
1039 WDONE, WDONE,
1040 HSEP,
1041 REF(book), PLAINBOOK, // pages 0 and 1 are both stacked
1042 RPACKv(scale_modes, 0, 0, scale_mode), OPNAME(""),
1043 HSEP,
1044 WDONE, // page 0
1045 CHECKv(_("Sharper image reduction"), sharper_reduce),
1046 FRPACKv(_("Boundary extension"), bound_modes, 3, 0,
1047 boundary_mode),
1048 WDONE, // page 1
1049 ENDIF(1),
1050 UNLESSx(mode, 1),
1051 CHECKb(_("Fix Aspect Ratio"), fix, "resizeAspect"),
1052 EVENT(CHANGE, sisca_moved),
1053 HSEP,
1054 RPACKv(resize_modes, 0, 0, resize_mode), OPNAME(""),
1055 ENDIF(1),
1056 UNLESS(rgb), HSEP,
1057 OKBOXB(_("OK"), click_sisca_ok, _("Cancel"), NULL),
1058 WSHOW
1059 };
1060 #undef WBbase
1061
pressed_scale_size(int mode)1062 void pressed_scale_size(int mode)
1063 {
1064 sisca_dd tdata = { !!script_cmds, mode, mode && (mem_img_bpp == 3),
1065 TRUE, use_gamma, mem_width, mem_height, 0, 0 };
1066 run_create_(sisca_code, &tdata, sizeof(tdata), script_cmds);
1067 }
1068
1069
1070 /// PALETTE EDITOR WINDOW
1071
1072 enum {
1073 CHOOK_CANCEL = 0,
1074 CHOOK_PREVIEW,
1075 CHOOK_OK,
1076 CHOOK_SELECT,
1077 CHOOK_SET,
1078 CHOOK_UNVIEW,
1079 CHOOK_CHANGE
1080 };
1081
1082 typedef struct {
1083 unsigned char rgb[768]; // everything needs it
1084 unsigned char opac[256]; // only EDIT_OVERLAYS uses it
1085 int AB[4][NUM_CHANNELS]; // for EDIT_AB
1086 int csrange, csinv, csmode; // for EDIT_CSEL
1087 int cgrid, tgrid, grid_min, tgrid_dx, tgrid_dy; // for GRID
1088 } csdata_dd;
1089
1090 typedef struct colsel_dd colsel_dd;
1091 typedef void (*colsel_fn)(colsel_dd *dt, int what);
1092
1093 struct colsel_dd {
1094 colsel_fn select;
1095 char *name, **cnames;
1096 int opflag, mpflag;
1097 int cnt, idx;
1098
1099 int preview;
1100 int rflag; // recursion
1101
1102 int color[2];
1103 void **clist, **csel;
1104
1105 int is_pal;
1106 int n0, n1, scale;
1107 void **fbutton, **fspin, **tspin;
1108
1109 int is_AB;
1110 int pos_AB, v_AB[NUM_CHANNELS];
1111 void **xtbox, **pspin_AB, **spin_AB[NUM_CHANNELS];
1112
1113 int is_csel;
1114 void **fspin_csel;
1115
1116 int is_grid;
1117
1118 csdata_dd v, v0; // primary and backup
1119 };
1120
1121
1122 /* Put current color in list into selector */
colsel_show_idx(colsel_dd * dt)1123 static void colsel_show_idx(colsel_dd *dt)
1124 {
1125 unsigned char *c = dt->v.rgb + dt->idx * 3;
1126 int color[4];
1127
1128 color[2] = color[0] = MEM_2_INT(c, 0);
1129 color[3] = color[1] = dt->v.opac[dt->idx];
1130 dt->rflag = TRUE; // prevent recursion
1131 cmd_setv(dt->csel, color, COLOR_ALL);
1132 dt->rflag = FALSE;
1133 }
1134
color_refresh(colsel_dd * dt)1135 static void color_refresh(colsel_dd *dt)
1136 {
1137 colsel_show_idx(dt);
1138 cmd_repaint(dt->clist);
1139 }
1140
1141
do_allcol(csdata_dd * v)1142 static void do_allcol(csdata_dd *v)
1143 {
1144 rgb2pal(mem_pal, v->rgb, mem_cols);
1145 update_stuff(UPD_PAL);
1146 }
1147
do_allover(csdata_dd * v)1148 static void do_allover(csdata_dd *v)
1149 {
1150 unsigned char *rgb = v->rgb;
1151 int i;
1152
1153 for (i = 0; i < NUM_CHANNELS; i++)
1154 {
1155 channel_rgb[i][0] = rgb[0];
1156 channel_rgb[i][1] = rgb[1];
1157 channel_rgb[i][2] = rgb[2];
1158 channel_opacity[i] = v->opac[i];
1159 rgb += 3;
1160 }
1161 update_stuff(UPD_RENDER);
1162 }
1163
do_AB(csdata_dd * v)1164 static void do_AB(csdata_dd *v)
1165 {
1166 png_color *A0, *B0;
1167 A0 = mem_img_bpp == 1 ? &mem_pal[mem_col_A] : &mem_col_A24;
1168 B0 = mem_img_bpp == 1 ? &mem_pal[mem_col_B] : &mem_col_B24;
1169
1170 A0->red = v->rgb[0]; A0->green = v->rgb[1]; A0->blue = v->rgb[2];
1171 B0->red = v->rgb[3]; B0->green = v->rgb[4]; B0->blue = v->rgb[5];
1172 mem_pal_ab_c = MEM_2_INT(v->rgb, 6);
1173 mem_pal_id_c = MEM_2_INT(v->rgb, 9);
1174 update_stuff(mem_img_bpp == 1 ? UPD_PAL : UPD_AB);
1175 }
1176
set_csel(csdata_dd * v)1177 static void set_csel(csdata_dd *v)
1178 {
1179 unsigned char *rgb = v->rgb;
1180 csel_data->center = MEM_2_INT(rgb, 0);
1181 csel_data->limit = MEM_2_INT(rgb, 3);
1182 csel_preview = MEM_2_INT(rgb, 6);
1183 /* !!! Disabled for now - later will be opacity */
1184 // csel_preview_a = v->opac[2];
1185 csel_data->mode = v->csmode;
1186 csel_data->range = v->csrange / 100.0;
1187 csel_data->invert = v->csinv;
1188 }
1189
set_grid(csdata_dd * v)1190 static void set_grid(csdata_dd *v)
1191 {
1192 unsigned char *rgb = v->rgb;
1193 int i;
1194
1195 for (i = 0; i < GRID_MAX; i++)
1196 {
1197 grid_rgb[i] = MEM_2_INT(rgb, 0);
1198 rgb += 3;
1199 }
1200 color_grid = v->cgrid;
1201 show_tile_grid = v->tgrid;
1202 mem_grid_min = v->grid_min;
1203 tgrid_dx = v->tgrid_dx;
1204 tgrid_dy = v->tgrid_dy;
1205 }
1206
select_colour(colsel_dd * dt,int what)1207 static void select_colour(colsel_dd *dt, int what)
1208 {
1209 switch (what)
1210 {
1211 case CHOOK_UNVIEW: /* Disable preview */
1212 case CHOOK_CANCEL: /* Cancel */
1213 do_allcol(&dt->v0);
1214 break;
1215 case CHOOK_SET: /* Set */
1216 if (!dt->preview) break;
1217 case CHOOK_PREVIEW: /* Preview */
1218 do_allcol(&dt->v);
1219 break;
1220 case CHOOK_OK: /* OK */
1221 do_allcol(&dt->v0);
1222 spot_undo(UNDO_PAL);
1223 do_allcol(&dt->v);
1224 break;
1225 }
1226 }
1227
click_colour(colsel_dd * dt,void ** wdata,int what,void ** where,colorlist_ext * xdata)1228 static void click_colour(colsel_dd *dt, void **wdata, int what, void **where,
1229 colorlist_ext *xdata)
1230 {
1231 /* Middle click sets "from" */
1232 if (xdata->button == 2) cmd_set(dt->fspin, xdata->idx);
1233
1234 /* Right click sets "to" */
1235 if (xdata->button == 3) cmd_set(dt->tspin, xdata->idx);
1236 }
1237
set_range_spin(colsel_dd * dt,void ** wdata,int what,void ** where)1238 static void set_range_spin(colsel_dd *dt, void **wdata, int what, void **where)
1239 {
1240 cmd_set(origin_slot(where) == dt->fbutton ? dt->fspin : dt->tspin, dt->idx);
1241 }
1242
make_cscale(colsel_dd * dt,void ** wdata)1243 static void make_cscale(colsel_dd *dt, void **wdata)
1244 {
1245 int i, n, start, stop, start0, mode;
1246 unsigned char *c0, *c1, *lc;
1247 double d;
1248
1249 run_query(wdata);
1250 mode = dt->scale;
1251 start = start0 = dt->n0;
1252 stop = dt->n1;
1253
1254 if (mode <= 2) /* RGB/sRGB/HSV */
1255 {
1256 if (start > stop) { i = start; start = stop; stop = i; }
1257 if (stop < start + 2) return;
1258 }
1259 else if (stop == start) return; /* Gradient */
1260
1261 c0 = lc = dt->v.rgb + start * 3;
1262 c1 = dt->v.rgb + stop * 3;
1263 d = n = stop - start;
1264
1265 switch (mode)
1266 {
1267 case 0: /* RGB */
1268 {
1269 double r0, g0, b0, dr, dg, db;
1270
1271 dr = ((int)c1[0] - c0[0]) / d;
1272 r0 = c0[0];
1273 dg = ((int)c1[1] - c0[1]) / d;
1274 g0 = c0[1];
1275 db = ((int)c1[2] - c0[2]) / d;
1276 b0 = c0[2];
1277
1278 for (i = 1; i < n; i++)
1279 {
1280 lc += 3;
1281 lc[0] = rint(r0 + dr * i);
1282 lc[1] = rint(g0 + dg * i);
1283 lc[2] = rint(b0 + db * i);
1284 }
1285 break;
1286 }
1287 case 1: /* sRGB */
1288 {
1289 double r0, g0, b0, dr, dg, db, rr, gg, bb;
1290
1291 dr = (gamma256[c1[0]] - (r0 = gamma256[c0[0]])) / d;
1292 dg = (gamma256[c1[1]] - (g0 = gamma256[c0[1]])) / d;
1293 db = (gamma256[c1[2]] - (b0 = gamma256[c0[2]])) / d;
1294
1295 for (i = 1; i < n; i++)
1296 {
1297 lc += 3;
1298 rr = r0 + dr * i;
1299 lc[0] = UNGAMMA256(rr);
1300 gg = g0 + dg * i;
1301 lc[1] = UNGAMMA256(gg);
1302 bb = b0 + db * i;
1303 lc[2] = UNGAMMA256(bb);
1304 }
1305 break;
1306 }
1307 case 2: /* HSV */
1308 {
1309 int t;
1310 double h0, dh, s0, ds, v0, dv, hsv[6], hh, ss, vv;
1311
1312 rgb2hsv(c0, hsv + 0);
1313 rgb2hsv(c1, hsv + 3);
1314 /* Grey has no hue */
1315 if (hsv[1] == 0.0) hsv[0] = hsv[3];
1316 if (hsv[4] == 0.0) hsv[3] = hsv[0];
1317
1318 /* Always go from 1st to 2nd hue in ascending order */
1319 t = start == start0 ? 0 : 3;
1320 if (hsv[t] > hsv[t ^ 3]) hsv[t] -= 6.0;
1321
1322 dh = (hsv[3] - hsv[0]) / d;
1323 h0 = hsv[0];
1324 ds = (hsv[4] - hsv[1]) / d;
1325 s0 = hsv[1];
1326 dv = (hsv[5] - hsv[2]) / d;
1327 v0 = hsv[2];
1328
1329 for (i = 1; i < n; i++)
1330 {
1331 vv = v0 + dv * i;
1332 ss = vv - vv * (s0 + ds * i);
1333 hh = h0 + dh * i;
1334 if (hh < 0.0) hh += 6.0;
1335 t = hh;
1336 hh = (hh - t) * (vv - ss);
1337 if (t & 1) { vv -= hh; hh += vv; }
1338 else hh += ss;
1339 t >>= 1;
1340 lc += 3;
1341 lc[t] = rint(vv);
1342 lc[(t + 1) % 3] = rint(hh);
1343 lc[(t + 2) % 3] = rint(ss);
1344 }
1345 break;
1346 }
1347 default: /* Gradient */
1348 {
1349 int j, op, c[NUM_CHANNELS + 3];
1350
1351 j = start < stop ? 1 : -1;
1352 for (i = 0; i != n + j; i += j , lc += j * 3)
1353 {
1354 op = grad_value(c, 0, i / d);
1355 /* Zero opacity - empty slot */
1356 if (!op) lc[0] = lc[1] = lc[2] = 0;
1357 else
1358 {
1359 lc[0] = (c[0] + 128) >> 8;
1360 lc[1] = (c[1] + 128) >> 8;
1361 lc[2] = (c[2] + 128) >> 8;
1362 }
1363 }
1364 break;
1365 }
1366 }
1367 color_refresh(dt);
1368 select_colour(dt, CHOOK_SET);
1369 }
1370
select_overlay(colsel_dd * dt,int what)1371 static void select_overlay(colsel_dd *dt, int what)
1372 {
1373 char txt[64];
1374 int i, j;
1375
1376 switch (what)
1377 {
1378 case CHOOK_UNVIEW: /* Disable preview */
1379 case CHOOK_CANCEL: /* Cancel */
1380 do_allover(&dt->v0); // Restore original values
1381 break;
1382 case CHOOK_SET: /* Set */
1383 if (!dt->preview) break;
1384 case CHOOK_PREVIEW: /* Preview */
1385 do_allover(&dt->v);
1386 break;
1387 case CHOOK_OK: /* OK */
1388 do_allover(&dt->v);
1389 for (i = 0; i < NUM_CHANNELS; i++) // Save all settings to ini file
1390 {
1391 for (j = 0; j < 4; j++)
1392 {
1393 sprintf(txt, "overlay%i%i", i, j);
1394 inifile_set_gint32(txt, j < 3 ? channel_rgb[i][j] :
1395 channel_opacity[i]);
1396 }
1397 }
1398 break;
1399 }
1400 }
1401
select_AB(colsel_dd * dt,int what)1402 static void select_AB(colsel_dd *dt, int what)
1403 {
1404 int i, *v;
1405
1406 switch (what)
1407 {
1408 case CHOOK_UNVIEW: /* Disable preview */
1409 case CHOOK_CANCEL: /* Cancel */
1410 do_AB(&dt->v0);
1411 break;
1412 case CHOOK_OK: /* OK */
1413 do_AB(&dt->v0);
1414 /* Palette gets modified in indexed mode */
1415 if (mem_img_bpp == 1) spot_undo(UNDO_PAL);
1416 for (i = CHN_ALPHA; i < NUM_CHANNELS; i++)
1417 {
1418 channel_col_A[i] = dt->v.AB[0][i];
1419 channel_col_B[i] = dt->v.AB[1][i];
1420 }
1421 do_AB(&dt->v);
1422 break;
1423 case CHOOK_SET: /* Set */
1424 if (!dt->preview) break;
1425 case CHOOK_PREVIEW: /* Preview */
1426 do_AB(&dt->v);
1427 break;
1428 case CHOOK_SELECT: /* Select */
1429 v = dt->v.AB[dt->idx];
1430 for (i = CHN_ALPHA; i < NUM_CHANNELS; i++)
1431 cmd_set(dt->spin_AB[i], v[i]);
1432 cmd_sensitive(dt->xtbox, dt->idx < 2);
1433 break;
1434 }
1435 }
1436
AB_spin_moved(colsel_dd * dt,void ** wdata,int what,void ** where)1437 static void AB_spin_moved(colsel_dd *dt, void **wdata, int what, void **where)
1438 {
1439 int *w = cmd_read(where, dt);
1440 dt->v.AB[dt->idx][w - dt->v_AB] = *w;
1441 }
1442
posterize_AB(colsel_dd * dt,void ** wdata)1443 static void posterize_AB(colsel_dd *dt, void **wdata)
1444 {
1445 static const int posm[8] = {0, 0xFF00, 0x5500, 0x2480, 0x1100,
1446 0x0840, 0x0410, 0x0204};
1447 unsigned char *lc = dt->v.rgb;
1448 int i, pm, ps;
1449
1450 cmd_read(dt->pspin_AB, dt);
1451 inifile_set_gint32("posterizeInt", ps = dt->pos_AB);
1452 if (ps >= 8) return;
1453 pm = posm[ps]; ps = 8 - ps;
1454
1455 for (i = 0; i < 6; i++) lc[i] = ((lc[i] >> ps) * pm) >> 8;
1456 color_refresh(dt);
1457 select_AB(dt, CHOOK_SET);
1458 }
1459
select_csel(colsel_dd * dt,int what)1460 static void select_csel(colsel_dd *dt, int what)
1461 {
1462 int old_over = csel_overlay;
1463
1464 switch (what)
1465 {
1466 case CHOOK_CANCEL: /* Cancel */
1467 set_csel(&dt->v0);
1468 csel_reset(csel_data);
1469 case CHOOK_UNVIEW: /* Disable preview */
1470 csel_overlay = 0;
1471 if (old_over) update_stuff(UPD_RENDER);
1472 break;
1473 case CHOOK_PREVIEW: /* Preview */
1474 csel_overlay = 1;
1475 case CHOOK_CHANGE: /* Range/mode/invert controls changed */
1476 if (!csel_overlay) break; // No preview
1477 case CHOOK_OK: /* OK */
1478 set_csel(&dt->v);
1479 csel_reset(csel_data);
1480 if (what == CHOOK_OK)
1481 {
1482 csel_overlay = 0;
1483 update_stuff(UPD_RENDER | UPD_MODE);
1484 }
1485 else update_stuff(UPD_RENDER);
1486 break;
1487 case CHOOK_SET: /* Set */
1488 set_csel(&dt->v);
1489 if (dt->idx == 1) /* Limit color changed */
1490 {
1491 // This triggers CHOOK_CHANGE which will redraw
1492 cmd_set(dt->fspin_csel, rint(csel_eval(csel_data->mode,
1493 csel_data->center, csel_data->limit) * 100));
1494 }
1495 else if (csel_overlay)
1496 {
1497 /* Center color changed */
1498 if (!dt->idx) csel_reset(csel_data);
1499 /* Else, overlay color changed */
1500 update_stuff(UPD_RENDER);
1501 }
1502 break;
1503 }
1504 }
1505
csel_controls_changed(colsel_dd * dt,void ** wdata,int what,void ** where)1506 static void csel_controls_changed(colsel_dd *dt, void **wdata, int what, void **where)
1507 {
1508 if (cmd_read(where, dt) == &dt->v.csmode)
1509 {
1510 set_csel(&dt->v);
1511 // This triggers CHOOK_CHANGE which will redraw
1512 cmd_set(dt->fspin_csel, rint(csel_eval(csel_data->mode,
1513 csel_data->center, csel_data->limit) * 100));
1514 }
1515 else select_csel(dt, CHOOK_CHANGE);
1516 }
1517
select_grid(colsel_dd * dt,int what)1518 static void select_grid(colsel_dd *dt, int what)
1519 {
1520 switch (what)
1521 {
1522 case CHOOK_UNVIEW: /* Disable preview */
1523 case CHOOK_CANCEL: /* Cancel */
1524 // Restore original values
1525 set_grid(&dt->v0);
1526 break;
1527 case CHOOK_CHANGE: /* Grid controls changed */
1528 case CHOOK_SET: /* Set */
1529 if (!dt->preview) return;
1530 case CHOOK_PREVIEW: /* Preview */
1531 case CHOOK_OK: /* OK */
1532 set_grid(&dt->v);
1533 break;
1534 default: return;
1535 }
1536 update_stuff(UPD_RENDER);
1537 }
1538
grid_controls_changed(colsel_dd * dt,void ** wdata,int what,void ** where)1539 static void grid_controls_changed(colsel_dd *dt, void **wdata, int what, void **where)
1540 {
1541 cmd_read(where, dt);
1542 select_grid(dt, CHOOK_CHANGE);
1543 }
1544
1545
colsel_evt(colsel_dd * dt,void ** wdata,int what,void ** where)1546 static void colsel_evt(colsel_dd *dt, void **wdata, int what, void **where)
1547 {
1548 void *cause;
1549
1550 if (dt->rflag) return; // skip recursive calls
1551 cause = cmd_read(where, dt);
1552
1553 if (cause == &dt->idx)
1554 {
1555 colsel_show_idx(dt); // Set selected color
1556 dt->select(dt, CHOOK_SELECT);
1557 }
1558 else if (cause == &dt->color)
1559 {
1560 unsigned char *c = dt->v.rgb + dt->idx * 3;
1561 int col = dt->color[0];
1562 /* Opacity */
1563 dt->v.opac[dt->idx] = dt->color[1];
1564 /* Color */
1565 c[0] = INT_2_R(col);
1566 c[1] = INT_2_G(col);
1567 c[2] = INT_2_B(col);
1568 cmd_setv(dt->clist, (void *)dt->idx, COLORLIST_RESET_ROW);
1569 dt->select(dt, CHOOK_SET);
1570 }
1571 else if (cause == &dt->preview)
1572 dt->select(dt, dt->preview ? CHOOK_PREVIEW : CHOOK_UNVIEW);
1573 else if (what == op_EVT_OK)
1574 {
1575 run_query(wdata); // for final updating, and for extra part
1576 dt->select(dt, CHOOK_OK);
1577 run_destroy(wdata);
1578 }
1579 else if (what == op_EVT_CANCEL)
1580 {
1581 dt->select(dt, CHOOK_CANCEL);
1582 run_destroy(wdata);
1583 }
1584 }
1585
colsel_pick_pixel(colsel_dd * dt,void ** wdata,int what,void ** where,multi_ext * mx)1586 static int colsel_pick_pixel(colsel_dd *dt, void **wdata, int what, void **where,
1587 multi_ext *mx)
1588 {
1589 int pix, *row = mx->rows[0] + 1;
1590
1591 /* Sanity check */
1592 if ((mx->ncols != 2) || (mx->nrows != 1) || (mx->fractcol >= 0))
1593 return (0);
1594
1595 if ((row[0] < 0) || (row[1] < 0) ||
1596 (row[0] >= mem_width) || (row[1] >= mem_height))
1597 pix = mem_background * 0x010101; // Grey outside image
1598 else pix = get_pixel_RGB(row[0], row[1]);
1599 cmd_set(origin_slot(where), pix);
1600 return (1);
1601 }
1602
1603 static char *scales_txt[] = { _("RGB"), _("sRGB"), _("HSV"), _("Gradient") };
1604 static char *AB_txt[] = { "A", "B", _("Highlight"), _("Index"), NULL };
1605 static char *csel_txt[] = { _("Centre"), _("Limit"), _("Preview"), NULL };
1606 static char *csel_modes[] = { _("Sphere"), _("Angle"), _("Cube"), NULL };
1607 static char *grid_txt[GRID_MAX + 1] = { _("Opaque"), _("Border"),
1608 _("Transparent"), _("Tile "), _("Segment"), NULL };
1609 /* !!! "Tile " has a trailing space to be distinct from "Tile" in "Resize Canvas" */
1610
1611 #if NUM_CHANNELS > CHN_MASK + 1
1612 #error "Not all channels listed in dialog"
1613 #endif
1614
1615 #define WBbase colsel_dd
1616 static void *colsel_code[] = {
1617 IF(mpflag), WPMOUSE,
1618 WINDOWpm(name),
1619 BORDER(SCROLL, 0), BORDER(BUTTON, 4),
1620 REF(clist),
1621 IFx(is_pal, 1), // long-list form - for now only palette needs it
1622 XHBOXS,
1623 SCROLL(0, 1), // never/auto
1624 COLORLISTN(cnt, idx, v.rgb, colsel_evt, click_colour),
1625 XVBOXBS,
1626 ENDIF(1),
1627 UNLESSx(is_pal, 1), // short-list form (6 items or less)
1628 XVBOXBS,
1629 XHBOXbp(10, 0, 0),
1630 SCROLL(0, 1), // never/auto
1631 COLORLIST(cnames, idx, v.rgb, colsel_evt, NULL),
1632 ENDIF(1),
1633 TRIGGER, // colorlist SELECT
1634 REF(csel),
1635 IF(opflag), TCOLOR(color), // with opacity
1636 UNLESS(opflag), COLOR(color), // solid colors
1637 EVENT(CHANGE, colsel_evt), OPNAME("Colour"),
1638 EVENT(MULTI, colsel_pick_pixel),
1639 UNLESS(is_pal), WDONE,
1640 /* Task-specific part */
1641 BORDER(TABLE, 0),
1642 IFx(is_pal, 1),
1643 BORDER(SPIN, 0),
1644 BORDER(LABEL, 0),
1645 HBOX,
1646 MLABEL(_("Scale")),
1647 REF(fbutton), BUTTON(_("From"), set_range_spin),
1648 REF(fspin), SPIN(n0, 0, 255),
1649 BUTTON(_("To"), set_range_spin),
1650 REF(tspin), SPIN(n1, 0, 255),
1651 BORDER(OPT, 0),
1652 XVBOXbp(0, 4, 0),
1653 OPT(scales_txt, 4, scale),
1654 EVENT(SCRIPT, make_cscale), OPNAME("Scale"),
1655 WDONE,
1656 BUTTON(_("Create"), make_cscale),
1657 WDONE,
1658 ENDIF(1),
1659 IFx(is_AB, 1),
1660 BORDER(SPINSLIDE, 0),
1661 BORDER(LABEL, 0),
1662 REF(xtbox), VBOXr,
1663 HBOX,
1664 BUTTON(_("Posterize"), posterize_AB),
1665 REF(pspin_AB), SPIN(pos_AB, 1, 8),
1666 EVENT(SCRIPT, posterize_AB),
1667 WDONE,
1668 TABLE(3, 2),
1669 TXLABEL(_("Alpha"), 0, 0),
1670 REF(spin_AB[CHN_ALPHA]),
1671 TLSPINSLIDEx(v_AB[CHN_ALPHA], 0, 255, 0, 1),
1672 EVENT(CHANGE, AB_spin_moved),
1673 TXLABEL(_("Selection"), 1, 0),
1674 REF(spin_AB[CHN_SEL]),
1675 TLSPINSLIDEx(v_AB[CHN_SEL], 0, 255, 1, 1),
1676 EVENT(CHANGE, AB_spin_moved),
1677 TXLABEL(_("Mask"), 2, 0),
1678 REF(spin_AB[CHN_MASK]),
1679 TLSPINSLIDEx(v_AB[CHN_MASK], 0, 255, 2, 1),
1680 EVENT(CHANGE, AB_spin_moved),
1681 WDONE,
1682 WDONE, // VBOXr
1683 ENDIF(1),
1684 IFx(is_csel, 1),
1685 BORDER(SPIN, 0),
1686 BORDER(LABEL, 0),
1687 BORDER(RPACK, 0),
1688 HBOX,
1689 MLABEL(_("Range")),
1690 REF(fspin_csel),
1691 FSPIN(v.csrange, 0, 76500), EVENT(CHANGE, csel_controls_changed),
1692 CHECK(_("Inverse"), v.csinv), EVENT(CHANGE, csel_controls_changed),
1693 RPACKe(csel_modes, 0, 1, v.csmode, csel_controls_changed), FLATTEN,
1694 OPNAME("Mode"),
1695 WDONE,
1696 ENDIF(1),
1697 IFx(is_grid, 1),
1698 BORDER(SPIN, 0),
1699 TABLE(6, 2),
1700 TLCHECK(_("Smart grid"), v.cgrid, 0, 0),
1701 EVENT(CHANGE, grid_controls_changed),
1702 TLCHECKl(_("Tile grid"), v.tgrid, 1, 0, 2),
1703 EVENT(CHANGE, grid_controls_changed),
1704 TLLABEL(_("Minimum grid zoom"), 0, 1),
1705 TLXSPIN(v.grid_min, 2, 12, 1, 1),
1706 EVENT(CHANGE, grid_controls_changed),
1707 TLLABEL(_("Tile width"), 2, 1),
1708 TLXSPIN(v.tgrid_dx, 2, MAX_WIDTH, 3, 1),
1709 EVENT(CHANGE, grid_controls_changed),
1710 TLLABEL(_("Tile height"), 4, 1),
1711 TLXSPIN(v.tgrid_dy, 2, MAX_HEIGHT, 5, 1),
1712 EVENT(CHANGE, grid_controls_changed),
1713 WDONE,
1714 ENDIF(1),
1715 HBOX, MINWIDTH(260), EEQBOX,
1716 DEFBORDER(BUTTON),
1717 CANCELBTN(_("Cancel"), colsel_evt),
1718 TOGGLE(_("Preview"), preview, colsel_evt),
1719 OKBTN(_("OK"), colsel_evt),
1720 WSHOW
1721 };
1722 #undef WBbase
1723
colour_selector(int cs_type)1724 void colour_selector(int cs_type)
1725 {
1726 colsel_dd tdata;
1727 unsigned char *rgb = tdata.v.rgb;
1728 int i;
1729
1730 memset(&tdata, 0, sizeof(tdata));
1731 if (cs_type >= COLSEL_EDIT_ALL)
1732 {
1733 tdata.select = select_colour;
1734 tdata.name = _("Palette Editor");
1735 tdata.mpflag = TRUE;
1736 tdata.cnt = mem_cols;
1737 tdata.idx = cs_type - COLSEL_EDIT_ALL;
1738 tdata.is_pal = TRUE;
1739
1740 pal2rgb(rgb, mem_pal, mem_cols, 0);
1741 // Opacity (unused)
1742 // memset(tdata.v.opac, 255, mem_cols);
1743 }
1744 else if (cs_type == COLSEL_OVERLAYS)
1745 {
1746 tdata.select = select_overlay;
1747 tdata.name = _("Configure Overlays");
1748 tdata.cnames = channames_;
1749 tdata.opflag = TRUE;
1750 tdata.cnt = NUM_CHANNELS;
1751
1752 for (i = 0; i < NUM_CHANNELS; i++)
1753 {
1754 tdata.v.opac[i] = channel_opacity[i];
1755 rgb[0] = channel_rgb[i][0];
1756 rgb[1] = channel_rgb[i][1];
1757 rgb[2] = channel_rgb[i][2];
1758 rgb += 3;
1759 }
1760 }
1761 else if (cs_type == COLSEL_EDIT_AB)
1762 {
1763 png_color *A0 = &mem_col_A24, *B0 = &mem_col_B24;
1764
1765 tdata.select = select_AB;
1766 tdata.name = _("Colour Editor");
1767 tdata.cnames = AB_txt;
1768 tdata.mpflag = TRUE; // at cursor
1769 tdata.cnt = 4;
1770 tdata.is_AB = TRUE;
1771
1772 tdata.pos_AB = inifile_get_gint32("posterizeInt", 1);
1773
1774 if (mem_img_bpp == 1)
1775 A0 = &mem_pal[mem_col_A] , B0 = &mem_pal[mem_col_B];
1776
1777 rgb[0] = A0->red; rgb[1] = A0->green; rgb[2] = A0->blue;
1778 rgb[3] = B0->red; rgb[4] = B0->green; rgb[5] = B0->blue;
1779 rgb[6] = INT_2_R(mem_pal_ab_c);
1780 rgb[7] = INT_2_G(mem_pal_ab_c);
1781 rgb[8] = INT_2_B(mem_pal_ab_c);
1782 rgb[9] = INT_2_R(mem_pal_id_c);
1783 rgb[10] = INT_2_G(mem_pal_id_c);
1784 rgb[11] = INT_2_B(mem_pal_id_c);
1785 for (i = CHN_ALPHA; i < NUM_CHANNELS; i++)
1786 {
1787 tdata.v.AB[0][i] = tdata.v_AB[i] = channel_col_A[i];
1788 tdata.v.AB[1][i] = channel_col_B[i];
1789 }
1790 // Opacity (unused)
1791 // tdata.v.opac[0] = tdata.v.opac[1] = 255;
1792 }
1793 else if (cs_type == COLSEL_EDIT_CSEL)
1794 {
1795 if (!csel_data)
1796 {
1797 csel_init();
1798 if (!csel_data) return;
1799 }
1800
1801 tdata.select = select_csel;
1802 tdata.name = _("Colour-Selective Mode");
1803 tdata.cnames = csel_txt;
1804 tdata.cnt = 3;
1805 tdata.is_csel = TRUE;
1806
1807 tdata.v.csrange = rint(csel_data->range * 100);
1808 tdata.v.csinv = csel_data->invert;
1809 tdata.v.csmode = csel_data->mode;
1810
1811 rgb[0] = INT_2_R(csel_data->center);
1812 rgb[1] = INT_2_G(csel_data->center);
1813 rgb[2] = INT_2_B(csel_data->center);
1814 rgb[3] = INT_2_R(csel_data->limit);
1815 rgb[4] = INT_2_G(csel_data->limit);
1816 rgb[5] = INT_2_B(csel_data->limit);
1817 rgb[6] = INT_2_R(csel_preview);
1818 rgb[7] = INT_2_G(csel_preview);
1819 rgb[8] = INT_2_B(csel_preview);
1820 // Opacity (unused)
1821 // tdata.v.opac[0] = tdata.v.opac[1] = 255;
1822 // tdata.v.opac[2] = csel_preview_a;
1823 }
1824 else if (cs_type == COLSEL_GRID)
1825 {
1826 tdata.select = select_grid;
1827 tdata.name = _("Configure Grid");
1828 tdata.cnames = grid_txt;
1829 tdata.cnt = GRID_MAX;
1830 tdata.is_grid = TRUE;
1831
1832 tdata.v.cgrid = color_grid;
1833 tdata.v.tgrid = show_tile_grid;
1834 tdata.v.grid_min = mem_grid_min;
1835 tdata.v.tgrid_dx = tgrid_dx;
1836 tdata.v.tgrid_dy = tgrid_dy;
1837
1838 for (i = 0; i < GRID_MAX; i++)
1839 {
1840 rgb[0] = INT_2_R(grid_rgb[i]);
1841 rgb[1] = INT_2_G(grid_rgb[i]);
1842 rgb[2] = INT_2_B(grid_rgb[i]);
1843 // Opacity (unused)
1844 // tdata.v.opac[i] = 255;
1845 rgb += 3;
1846 }
1847 }
1848
1849 tdata.v0 = tdata.v; // Save original values
1850 run_create_(colsel_code, &tdata, sizeof(tdata), script_cmds);
1851 }
1852
1853 /// QUANTIZE WINDOW
1854
1855 #define QUAN_EXACT 0
1856 #define QUAN_CURRENT 1
1857 #define QUAN_PNN 2
1858 #define QUAN_WU 3
1859 #define QUAN_MAXMIN 4
1860 #define QUAN_MAX 5
1861
1862 #define DITH_NONE 0
1863 #define DITH_FS 1
1864 #define DITH_STUCKI 2
1865 #define DITH_ORDERED 3
1866 #define DITH_DUMBFS 4
1867 #define DITH_OLDDITHER 5
1868 #define DITH_OLDSCATTER 6
1869 #define DITH_MAX 7
1870
1871 typedef struct {
1872 int pflag;
1873 int cols, cols0;
1874 int err;
1875 png_color newpal[256];
1876 char **qtxt;
1877 void **dith, **colspin, **errspin;
1878 void **book, **qbook;
1879 } quantize_dd;
1880
1881 /* Quantization & dither settings - persistent */
1882 static int quantize_mode = -1, dither_mode = -1;
1883 static int quantize_tp;
1884 static int dither_cspace = CSPACE_SRGB, dither_dist = DIST_L2, dither_limit;
1885 static int dither_scan = TRUE, dither_8b, dither_sel;
1886 static int dither_pfract[2] = { 100, 85 };
1887
click_quantize_radio(quantize_dd * dt,void ** wdata,int what,void ** where)1888 static void click_quantize_radio(quantize_dd *dt, void **wdata, int what, void **where)
1889 {
1890 int vvv[3] = { 1, 1, 256 }, n;
1891
1892 cmd_read(where, dt);
1893 n = quantize_mode;
1894 cmd_set(dt->qbook, (n == QUAN_PNN) || (n == QUAN_WU) ? 2 :
1895 n == QUAN_CURRENT ? 1 : 0);
1896
1897 if (n == QUAN_EXACT) vvv[1] = vvv[2] = dt->cols0;
1898 else if (n == QUAN_CURRENT) vvv[2] = mem_cols;
1899 cmd_read(dt->colspin, dt);
1900 vvv[0] = dt->cols; // !!! gets bounded when setting
1901 cmd_setv(dt->colspin, vvv, SPIN_ALL);
1902
1903 /* No dither for exact conversion */
1904 if (!dt->pflag) cmd_sensitive(dt->dith, n != QUAN_EXACT);
1905 }
1906
click_quantize_ok(quantize_dd * dt,void ** wdata)1907 static void click_quantize_ok(quantize_dd *dt, void **wdata)
1908 {
1909 int i, dither, new_cols, have_image = !dt->pflag, err = 0;
1910 int quantize_cols = dt->cols0, efrac = 0;
1911 png_color newpal[256];
1912 unsigned char *old_image = mem_img[CHN_IMAGE];
1913
1914 /* Dithering filters */
1915 /* Floyd-Steinberg dither */
1916 static short fs_dither[16] =
1917 { 16, 0, 0, 0, 7, 0, 0, 3, 5, 1, 0, 0, 0, 0, 0, 0 };
1918 /* Stucki dither */
1919 static short s_dither[16] =
1920 { 42, 0, 0, 0, 8, 4, 2, 4, 8, 4, 2, 1, 2, 4, 2, 1 };
1921
1922 run_query(wdata);
1923 dither = quantize_mode != QUAN_EXACT ? dither_mode : DITH_NONE;
1924 new_cols = dt->cols;
1925 mem_pal_copy(newpal, dt->newpal);
1926 if (have_image) /* Work on image */
1927 dither_pfract[dither_sel ? 1 : 0] = efrac = dt->err;
1928
1929 /* !!! No touching dt past this point */
1930 run_destroy(wdata);
1931
1932 /* Paranoia */
1933 if ((quantize_mode >= QUAN_MAX) || (dither >= DITH_MAX)) return;
1934 if (!have_image && (quantize_mode == QUAN_CURRENT)) return;
1935
1936 i = undo_next_core(UC_NOCOPY, mem_width, mem_height,
1937 have_image ? 1 : mem_img_bpp, have_image ? CMASK_IMAGE : CMASK_NONE);
1938 if (i)
1939 {
1940 memory_errors(i);
1941 return;
1942 }
1943
1944 switch (quantize_mode)
1945 {
1946 case QUAN_EXACT: /* Use image colours */
1947 new_cols = quantize_cols;
1948 if (have_image) err = mem_convert_indexed(mem_img[CHN_IMAGE],
1949 old_image, mem_width * mem_height, new_cols, newpal);
1950 dither = DITH_MAX;
1951 break;
1952 default:
1953 case QUAN_CURRENT: /* Use current palette */
1954 break;
1955 case QUAN_PNN: /* PNN quantizer */
1956 err = pnnquan(old_image, mem_width, mem_height, new_cols, newpal);
1957 break;
1958 case QUAN_WU: /* Wu quantizer */
1959 err = wu_quant(old_image, mem_width, mem_height, new_cols, newpal);
1960 break;
1961 case QUAN_MAXMIN: /* Max-Min quantizer */
1962 err = maxminquan(old_image, mem_width, mem_height, new_cols, newpal);
1963 break;
1964 }
1965
1966 if (err) dither = DITH_MAX;
1967 else if (quantize_mode != QUAN_CURRENT)
1968 {
1969 memcpy(mem_pal, newpal, new_cols * sizeof(*mem_pal));
1970 mem_cols = new_cols;
1971 }
1972 else if (quantize_tp) mem_cols = new_cols; // Truncate palette
1973
1974 if (!have_image) /* Palette only */
1975 {
1976 if (err < 0) memory_errors(1);
1977 update_stuff(UPD_PAL | CF_MENU);
1978 return;
1979 }
1980
1981 switch (dither)
1982 {
1983 case DITH_NONE:
1984 case DITH_FS:
1985 case DITH_STUCKI:
1986 err = mem_dither(old_image, new_cols, dither == DITH_NONE ?
1987 NULL : dither == DITH_FS ? fs_dither : s_dither,
1988 dither_cspace, dither_dist, dither_limit, dither_sel,
1989 dither_scan, dither_8b, efrac * 0.01);
1990 break;
1991
1992 // !!! No code yet - temporarily disabled !!!
1993 // case DITH_ORDERED:
1994
1995 case DITH_DUMBFS:
1996 err = mem_dumb_dither(old_image, mem_img[CHN_IMAGE],
1997 mem_pal, mem_width, mem_height, new_cols, TRUE);
1998 break;
1999 case DITH_OLDDITHER:
2000 err = mem_quantize(old_image, new_cols, 2);
2001 break;
2002 case DITH_OLDSCATTER:
2003 err = mem_quantize(old_image, new_cols, 3);
2004 break;
2005 case DITH_MAX: /* Stay silent unless a memory error happened */
2006 err = err < 0;
2007 break;
2008 }
2009 if (err) memory_errors(1);
2010
2011 /* Image was converted */
2012 mem_col_A = mem_cols > 1 ? 1 : 0;
2013 mem_col_B = 0;
2014 update_stuff(UPD_2IDX);
2015 }
2016
choose_selective(quantize_dd * dt,void ** wdata,int what,void ** where)2017 static void choose_selective(quantize_dd *dt, void **wdata, int what, void **where)
2018 {
2019 int i = dither_sel;
2020
2021 cmd_read(where, dt);
2022
2023 /* Selectivity state toggled */
2024 if ((i = !i) ^ !dither_sel)
2025 {
2026 cmd_read(dt->errspin, dt);
2027 dither_pfract[i ^ 1] = dt->err;
2028 cmd_set(dt->errspin, dither_pfract[i]);
2029 }
2030 }
2031
2032 static char *quan_txt[] = { _("Exact Conversion"), _("Use Current Palette"),
2033 _("PNN Quantize (slow, better quality)"),
2034 _("Wu Quantize (fast)"),
2035 _("Max-Min Quantize (best for small palettes and dithering)"), NULL };
2036 static char *dith_txt[] = { _("None"), _("Floyd-Steinberg"), _("Stucki"),
2037 // !!! "Ordered" not done yet !!!
2038 /* _("Ordered") */ "",
2039 _("Floyd-Steinberg (quick)"), _("Dithered (effect)"),
2040 _("Scattered (effect)"), NULL };
2041 static char *clamp_txt[] = { _("Gamut"), _("Weakly"), _("Strongly") };
2042 static char *err_txt[] = { _("Off"), _("Separate/Sum"), _("Separate/Split"),
2043 _("Length/Sum"), _("Length/Split"), NULL };
2044 static char *dist_txt[] = { _("Largest (Linf)"), _("Sum (L1)"), _("Euclidean (L2)") };
2045
2046 #define WBbase quantize_dd
2047 static void *quantize_code[] = {
2048 IF(pflag), WINDOWm(_("Create Quantized")),
2049 UNLESS(pflag), WINDOWm(_("Convert To Indexed")),
2050 BORDER(RPACK, 0),
2051 BORDER(LABEL, 0),
2052 HBOXbp(5, 10, 0), MLABEL(_("Indexed Colours To Use")),
2053 DEFBORDER(LABEL),
2054 BORDER(SPIN, 0),
2055 REF(colspin), XSPIN(cols, 1, 256),
2056 DEFBORDER(SPIN),
2057 UNLESS(pflag), BOOKBTN(_("Settings"), book),
2058 WDONE,
2059 REF(book), UNLESS(pflag), PLAINBOOK,
2060 /* Main page - Palette frame */
2061 FVBOX(_("Palette")),
2062 RPACKDve(qtxt, 0, quantize_mode, click_quantize_radio), TRIGGER,
2063 REF(qbook), PLAINBOOKn(3),
2064 WDONE, // empty page 0
2065 CHECKv(_("Truncate palette"), quantize_tp), WDONE, // page 1
2066 CHECKv(_("Diameter based weighting"), quan_sqrt), WDONE, // page 2
2067 WDONE,
2068 UNLESSx(pflag, 1),
2069 /* Main page - Dither frame */
2070 REF(dith),
2071 FRPACKv(_("Dither"), dith_txt, 0, DITH_MAX / 2, dither_mode),
2072 WDONE,
2073 /* Settings page */
2074 FRPACKv(_("Colour space"), cspnames_, NUM_CSPACES, 1, dither_cspace),
2075 FRPACKv(_("Difference measure"), dist_txt, 3, 1, dither_dist),
2076 FRPACKv(_("Reduce colour bleed"), clamp_txt, 3, 1, dither_limit),
2077 CHECKv(_("Serpentine scan"), dither_scan),
2078 TABLE2(2),
2079 REF(errspin), TSPIN(_("Error propagation, %"), err, 0, 100),
2080 TLABEL(_("Selective error propagation")),
2081 BORDER(OPT, 0),
2082 TLOPTve(err_txt, 0, dither_sel, choose_selective, 1, 1),
2083 WDONE,
2084 CHECKv(_("Full error precision"), dither_8b),
2085 WDONE,
2086 ENDIF(1),
2087 /* OK / Cancel */
2088 OKBOX(_("OK"), click_quantize_ok, _("Cancel"), NULL),
2089 WSHOW
2090 };
2091 #undef WBbase
2092
pressed_quantize(int palette)2093 void pressed_quantize(int palette)
2094 {
2095 char *qnames[sizeof(quan_txt) / sizeof(quan_txt[0])];
2096 quantize_dd tdata;
2097
2098 tdata.pflag = palette;
2099 tdata.cols = tdata.cols0 = mem_cols_used(tdata.newpal);
2100 tdata.qtxt = qnames;
2101
2102 memcpy(qnames, quan_txt, sizeof(qnames));
2103 /* No exact transfer if too many colours */
2104 if (tdata.cols > 256) qnames[QUAN_EXACT] = "";
2105 if (palette) qnames[QUAN_CURRENT] = "";
2106 if ((quantize_mode < 0) || !qnames[quantize_mode][0]) // Use default mode
2107 {
2108 quantize_mode = palette || (tdata.cols > 256) ? QUAN_WU : QUAN_EXACT;
2109 if (!palette) dither_mode = -1; // Reset dither too
2110 }
2111
2112 if (!palette)
2113 {
2114 if (dither_mode < 0) dither_mode = tdata.cols > 256 ?
2115 DITH_DUMBFS : DITH_NONE;
2116 tdata.err = dither_pfract[dither_sel ? 1 : 0];
2117 }
2118
2119 run_create_(quantize_code, &tdata, sizeof(tdata), script_cmds);
2120 }
2121
2122 /// GRADIENT WINDOW
2123
2124 #define PPAD_SLOT 11
2125 #define PPAD_XSZ 32
2126 #define PPAD_YSZ 8
2127 #define PPAD_WIDTH (PPAD_XSZ * PPAD_SLOT - 1)
2128 #define PPAD_HEIGHT (PPAD_YSZ * PPAD_SLOT - 1)
2129 #define PPAD_C ((PPAD_SLOT >> 1) - 1)
2130
2131 #define NUM_GTYPES 7
2132 #define NUM_OTYPES 3
2133
2134 typedef struct {
2135 int pmouse;
2136 int channel, nchan;
2137 int len, rep, ofs;
2138 int type, bound, opac;
2139 int gtype, grev;
2140 int otype, orev;
2141 void **opt, **gbut, **obut, **group;
2142 char **gpp, *gp[NUM_GTYPES + 1];
2143 grad_info temps[NUM_CHANNELS];
2144 grad_map tmaps[NUM_CHANNELS + 1];
2145 grad_store tbytes;
2146 } grad_dd;
2147
2148 typedef struct {
2149 void **xw; // parent widget-map
2150 int lock; // prevent nested calls
2151 int interp, crgb[4], cidx[3];
2152 int cnt, slot, mode;
2153 int gsize[3], gxy[2];
2154 void **ppad;
2155 void **col, **opt;
2156 void **spin, **chk;
2157 void **pspin, **gbar;
2158 unsigned char *pgrid;
2159 unsigned char rgb[768];
2160 unsigned char pad[GRAD_POINTS * 3], mpad[GRAD_POINTS];
2161 } ged_dd;
2162
ged_event(ged_dd * dt,void ** wdata,int what,void ** where)2163 static void ged_event(ged_dd *dt, void **wdata, int what, void **where)
2164 {
2165 int slot, *cause;
2166
2167 if (what == op_EVT_OK)
2168 {
2169 grad_dd *gdt = GET_DDATA(dt->xw);
2170 int idx = (gdt->channel == CHN_IMAGE) && (mem_img_bpp == 3) ? 0 :
2171 gdt->channel + 1;
2172
2173 run_query(wdata);
2174 if (dt->mode > NUM_CHANNELS) /* Opacity */
2175 {
2176 memcpy(gdt->tbytes + GRAD_CUSTOM_OPAC(idx), dt->pad,
2177 GRAD_POINTS);
2178 memcpy(gdt->tbytes + GRAD_CUSTOM_OMAP(idx), dt->mpad,
2179 GRAD_POINTS);
2180 gdt->tmaps[idx].coplen = dt->cnt;
2181 }
2182 else /* Gradient */
2183 {
2184 memcpy(gdt->tbytes + GRAD_CUSTOM_DATA(idx), dt->pad,
2185 idx ? GRAD_POINTS : GRAD_POINTS * 3);
2186 memcpy(gdt->tbytes + GRAD_CUSTOM_DMAP(idx), dt->mpad,
2187 GRAD_POINTS);
2188 gdt->tmaps[idx].cvslen = dt->cnt;
2189 }
2190 run_destroy(wdata);
2191 return;
2192 }
2193
2194 cause = cmd_read(where, dt);
2195 if (dt->lock) return;
2196 dt->lock = TRUE; /* Block circular signal calls */
2197
2198 slot = dt->slot;
2199 if (cause == &dt->slot) /* Select slot */
2200 {
2201 if (slot >= dt->cnt) goto done; /* Empty slot */
2202 if (!dt->mode) /* RGB */
2203 {
2204 unsigned char *gp = dt->pad + slot * 3;
2205
2206 dt->crgb[0] = dt->crgb[2] = MEM_2_INT(gp, 0);
2207 cmd_setv(dt->col, dt->crgb, COLOR_ALL);
2208 cmd_set(dt->opt, dt->mpad[slot]);
2209 }
2210 else /* Indexed / utility / opacity */
2211 {
2212 cmd_set(dt->chk, dt->mpad[slot] == GRAD_TYPE_CONST);
2213 dt->lock = FALSE;
2214 cmd_set(dt->spin, dt->pad[slot]);
2215 }
2216 }
2217 else if (cause == &dt->cnt) /* Set length */
2218 cmd_repaint(dt->gbar);
2219 else
2220 {
2221 int n = *cause;
2222 if (cause == &dt->interp) /* Select mode */
2223 {
2224 if (dt->mode) n = n ? GRAD_TYPE_CONST : GRAD_TYPE_RGB;
2225 dt->mpad[slot] = n;
2226 }
2227 else if (cause == dt->cidx)
2228 {
2229 int xy[2];
2230 xy[0] = (n % PPAD_XSZ) * PPAD_SLOT + PPAD_C;
2231 xy[1] = (n / PPAD_XSZ) * PPAD_SLOT + PPAD_C;
2232 cmd_setv(dt->ppad, xy, FCIMAGE_XY);
2233 if (!dt->mode) /* RGB */
2234 {
2235 dt->crgb[0] = MEM_2_INT(dt->rgb, n * 3);
2236 cmd_setv(dt->col, dt->crgb, COLOR_RGBA);
2237 }
2238 else dt->pad[slot] = n; /* Indexed / utility / opacity */
2239 }
2240
2241 if (!dt->mode) /* RGB */
2242 {
2243 unsigned char *gp = dt->pad + slot * 3;
2244 int rgb = dt->crgb[0];
2245 gp[0] = INT_2_R(rgb);
2246 gp[1] = INT_2_G(rgb);
2247 gp[2] = INT_2_B(rgb);
2248 }
2249
2250 if (dt->cnt <= slot) // Extend as needed
2251 cmd_set(dt->pspin, dt->cnt = slot + 1);
2252
2253 cmd_repaint(dt->gbar);
2254 }
2255 done: dt->lock = FALSE;
2256 }
2257
ged_pkey(ged_dd * dt,void ** wdata,int what,void ** where,key_ext * key)2258 static int ged_pkey(ged_dd *dt, void **wdata, int what, void **where,
2259 key_ext *key)
2260 {
2261 int x, y;
2262
2263 if (!arrow_key_(key->key, key->state, &x, &y, 0)) return (FALSE);
2264 x += dt->gxy[0] / PPAD_SLOT;
2265 y += dt->gxy[1] / PPAD_SLOT;
2266 y = y < 0 ? 0 : y >= PPAD_YSZ ? PPAD_YSZ - 1 : y;
2267 y = y * PPAD_XSZ + x;
2268 y = y < 0 ? 0 : y > 255 ? 255 : y;
2269 cmd_set(dt->spin, y);
2270
2271 return (TRUE);
2272 }
2273
ged_pclick(ged_dd * dt,void ** wdata,int what,void ** where,mouse_ext * mouse)2274 static int ged_pclick(ged_dd *dt, void **wdata, int what, void **where,
2275 mouse_ext *mouse)
2276 {
2277 int x, y;
2278
2279 /* Only single clicks */
2280 if (mouse->count != 1) return (TRUE);
2281 x = mouse->x / PPAD_SLOT;
2282 y = mouse->y / PPAD_SLOT;
2283 y = y * PPAD_XSZ + x;
2284 cmd_set(dt->spin, y);
2285
2286 return (TRUE);
2287 }
2288
2289 static char *interp_txt[] = { _("RGB"), _("sRGB"), _("HSV"), _("Backward HSV"),
2290 _("Constant") };
2291
2292 #define WBbase ged_dd
2293 static void *ged_code[] = {
2294 ONTOP(xw), WINDOWm(_("Edit Gradient")),
2295 XVBOXBS, // !!! Originally the main vbox was that way
2296 /* Palette pad */
2297 TALLOC(pgrid, gsize[2]),
2298 REF(ppad), FCIMAGEP(pgrid, gxy, gsize),
2299 EVENT(KEY, ged_pkey), EVENT(MOUSE, ged_pclick),
2300 HSEP,
2301 /* Editor widgets */
2302 BORDER(SPINSLIDE, 0),
2303 REF(spin), IF(mode), SPINSLIDEa(cidx), UNLESS(mode), uSPINa(cidx),
2304 EVENT(CHANGE, ged_event), OPNAME("Value"),
2305 UNLESSx(mode, 1), /* RGB */
2306 REF(col), COLOR(crgb), EVENT(CHANGE, ged_event), OPNAME("Colour"),
2307 EVENT(MULTI, colsel_pick_pixel),
2308 EQBOXS, BORDER(OPT, 0),
2309 REF(opt), XOPTe(interp_txt, 5, interp, ged_event), OPNAME("Type"),
2310 ENDIF(1),
2311 IFx(mode, 1), /* Indexed / utility / opacity */
2312 EQBOXS,
2313 REF(chk), XCHECK(_("Constant"), interp), EVENT(CHANGE, ged_event),
2314 ENDIF(1),
2315 BORDER(LABEL, 0),
2316 XHBOXS, MLABEL(_("Points:")),
2317 REF(pspin), SPIN(cnt, 2, GRAD_POINTS), EVENT(CHANGE, ged_event),
2318 WDONE, WDONE,
2319 /* Gradient bar */
2320 uSPIN(slot, 0, GRAD_POINTS - 1), EVENT(CHANGE, ged_event), TRIGGER,
2321 OPNAME(""),
2322 REF(gbar), GRADBAR(mode, slot, cnt, GRAD_POINTS, pad, rgb, ged_event),
2323 OKBOX(_("OK"), ged_event, _("Cancel"), NULL),
2324 WEND
2325 };
2326 #undef WBbase
2327
grad_edit(void ** wdata,int opac)2328 static void grad_edit(void **wdata, int opac)
2329 {
2330 ged_dd tdata, *ddt;
2331 grad_dd *dt = GET_DDATA(wdata);
2332 void **dd;
2333 int idx;
2334
2335 memset(&tdata, 0, sizeof(tdata));
2336 tdata.xw = wdata;
2337 idx = (dt->channel == CHN_IMAGE) && (mem_img_bpp == 3) ? 0 :
2338 dt->channel + 1;
2339
2340 /* Copy to temp */
2341 if (opac)
2342 {
2343 memcpy(tdata.pad, dt->tbytes + GRAD_CUSTOM_OPAC(idx), GRAD_POINTS);
2344 memcpy(tdata.mpad, dt->tbytes + GRAD_CUSTOM_OMAP(idx), GRAD_POINTS);
2345 tdata.cnt = dt->tmaps[idx].coplen;
2346 tdata.mode = NUM_CHANNELS + 1;
2347 }
2348 else
2349 {
2350 memcpy(tdata.pad, dt->tbytes + GRAD_CUSTOM_DATA(idx),
2351 idx ? GRAD_POINTS : GRAD_POINTS * 3);
2352 memcpy(tdata.mpad, dt->tbytes + GRAD_CUSTOM_DMAP(idx), GRAD_POINTS);
2353 tdata.cnt = dt->tmaps[idx].cvslen;
2354 tdata.mode = idx;
2355 }
2356 if (tdata.cnt < 2) tdata.cnt = 2;
2357 tdata.crgb[1] = tdata.crgb[3] = 255;
2358 tdata.cidx[2] = tdata.mode <= CHN_IMAGE + 1 ? mem_cols - 1 : 255;
2359
2360 make_crgb(tdata.rgb, tdata.mode - 1);
2361
2362 if (!script_cmds)
2363 {
2364 tdata.gsize[2] = (tdata.gsize[0] = PPAD_WIDTH) *
2365 (tdata.gsize[1] = PPAD_HEIGHT) * 3;
2366 tdata.gxy[0] = tdata.gxy[1] = PPAD_C;
2367 }
2368
2369 dd = run_create_(ged_code, &tdata, sizeof(tdata), script_cmds);
2370 if (!script_cmds)
2371 {
2372 ddt = GET_DDATA(dd);
2373 render_color_grid(ddt->pgrid, PPAD_WIDTH, PPAD_HEIGHT,
2374 PPAD_SLOT, ddt->rgb);
2375 }
2376
2377 cmd_showhide(dd, TRUE);
2378 }
2379
2380 static const char gtmap[NUM_GTYPES * 2] = { GRAD_TYPE_RGB, 1, GRAD_TYPE_RGB, 2,
2381 GRAD_TYPE_SRGB, 2, GRAD_TYPE_HSV, 2, GRAD_TYPE_BK_HSV, 2,
2382 GRAD_TYPE_CONST, 3, GRAD_TYPE_CUSTOM, 3 };
2383 static const char opmap[NUM_OTYPES] = { GRAD_TYPE_RGB, GRAD_TYPE_CONST,
2384 GRAD_TYPE_CUSTOM };
2385
store_channel_gradient(grad_dd * dt)2386 static void store_channel_gradient(grad_dd *dt)
2387 {
2388 int channel = dt->channel;
2389 grad_info *grad = dt->temps + channel;
2390 grad_map *gmap = dt->tmaps + channel + 1;
2391
2392 if ((channel == CHN_IMAGE) && (mem_img_bpp == 3)) gmap = dt->tmaps;
2393
2394 grad->len = dt->len;
2395 grad->gmode = dt->type + GRAD_MODE_LINEAR;
2396 grad->rep = dt->rep;
2397 grad->rmode = dt->bound;
2398 grad->ofs = dt->ofs;
2399
2400 gmap->gtype = gtmap[2 * dt->gtype];
2401 gmap->grev = dt->grev;
2402 gmap->otype = opmap[dt->otype];
2403 gmap->orev = dt->orev;
2404 }
2405
2406 static char *gradtypes_txt[NUM_GTYPES];
2407
show_channel_gradient(grad_dd * dt,void ** wdata)2408 static void show_channel_gradient(grad_dd *dt, void **wdata)
2409 {
2410 int channel = dt->channel;
2411 grad_info *grad = dt->temps + channel;
2412 grad_map *gmap;
2413 int i, j, k, idx = channel + 1, bpp = BPP(channel);
2414
2415 if (bpp == 3) --idx;
2416 gmap = dt->tmaps + idx;
2417
2418 /* Reconfigure gradient selector */
2419 dt->gpp = dt->gp;
2420 i = bpp == 1 ? 1 : 2;
2421 for (k = -1 , j = 0; j < NUM_GTYPES; j++)
2422 {
2423 dt->gp[j] = ""; // hide
2424 if (!(gtmap[j * 2 + 1] & i)) continue;
2425 dt->gp[j] = gradtypes_txt[j]; // show
2426 if ((gtmap[j * 2] == gmap->gtype) || (k < 0)) k = j; // select
2427 }
2428 dt->gtype = k;
2429
2430 /* Opacity gradient */
2431 for (i = NUM_OTYPES - 1; (i >= 0) && (opmap[i] != gmap->otype); i--);
2432 dt->otype = i;
2433
2434 /* Parameters */
2435 dt->len = grad->len;
2436 dt->rep = grad->rep;
2437 dt->ofs = grad->ofs;
2438 dt->type = grad->gmode - GRAD_MODE_LINEAR;
2439 dt->bound = grad->rmode;
2440 dt->grev = gmap->grev;
2441 dt->orev = gmap->orev;
2442
2443 /* Display all that */
2444 cmd_reset(dt->group, dt);
2445 }
2446
grad_evt(grad_dd * dt,void ** wdata,int what,void ** where)2447 static void grad_evt(grad_dd *dt, void **wdata, int what, void **where)
2448 {
2449 run_query(wdata);
2450 if (what == op_EVT_SELECT) // channel
2451 {
2452 /* If same channel, it means doing a reset */
2453 if (dt->nchan != dt->channel) store_channel_gradient(dt);
2454 dt->channel = dt->nchan;
2455 show_channel_gradient(dt, wdata);
2456 return;
2457 }
2458 where = origin_slot(where); // button
2459 if ((where == dt->gbut) || (where == dt->obut))
2460 grad_edit(wdata, where == dt->obut); // value / opacity
2461 else // OK/Apply
2462 {
2463 int i;
2464
2465 store_channel_gradient(dt);
2466 memcpy(gradient, dt->temps, sizeof(dt->temps));
2467 memcpy(graddata, dt->tmaps, sizeof(dt->tmaps));
2468 memcpy(gradbytes, dt->tbytes, sizeof(dt->tbytes));
2469
2470 grad_opacity = dt->opac;
2471
2472 for (i = 0; i < NUM_CHANNELS; i++) grad_update(gradient + i);
2473 for (i = 0; i <= NUM_CHANNELS; i++)
2474 gmap_setup(graddata + i, gradbytes, i);
2475 update_stuff(UPD_GRAD);
2476 }
2477 if (what == op_EVT_OK) run_destroy(wdata);
2478 }
2479
2480 static char *gtypes_txt[] = {_("Linear"), _("Bilinear"), _("Radial"),
2481 _("Square"), _("Angular"), _("Conical")};
2482 static char *rtypes_txt[] = {_("None"), _("Level "), _("Repeat"), _("Mirror")};
2483 static char *gradtypes_txt[NUM_GTYPES] = {_("A to B"), _("A to B (RGB)"),
2484 _("A to B (sRGB)"), _("A to B (HSV)"), _("A to B (backward HSV)"),
2485 _("A only"), _("Custom")};
2486 static char *optypes_txt[NUM_OTYPES] = {_("Current to 0"), _("Current only"),
2487 _("Custom")};
2488
2489 #define WBbase grad_dd
2490 static void *grad_code[] = {
2491 IF(pmouse), WPMOUSE, WINDOWm(_("Configure Gradient")),
2492 /* Channel box */
2493 BORDER(RPACK, 0),
2494 FRPACKe(_("Channel"), channames_, NUM_CHANNELS, 1, nchan, grad_evt),
2495 TRIGGER,
2496 /* Setup block */
2497 TABLE(4, 4),
2498 REF(group), GROUPR,
2499 TSPIN(_("Length"), len, 0, MAX_GRAD),
2500 TSPIN(_("Repeat length"), rep, 0, MAX_GRAD),
2501 TSPIN(_("Offset"), ofs, -MAX_GRAD, MAX_GRAD),
2502 TLLABEL(_("Gradient type"), 2, 0), TLOPT(gtypes_txt, 6, type, 3, 0),
2503 TLLABEL(_("Extension type"), 2, 1), TLOPT(rtypes_txt, 4, bound, 3, 1),
2504 TLLABEL(_("Distance type"), 2, 2), TLOPTv(dist_txt, 3, sb_dist, 3, 2),
2505 TLABEL(_("Preview opacity")),
2506 MINWIDTH(200), TLSPINSLIDExl(opac, 0, 255, 1, 3, 3), UNNAME,
2507 WDONE,
2508 /* Select page */
2509 EQBOX,
2510 BORDER(OPT, 0),
2511 FXVBOX(_("Gradient")),
2512 VBOXB, REF(opt), OPTD(gpp, gtype), WDONE,
2513 GROUPN, EQBOX,
2514 CHECK(_("Reverse"), grev),
2515 REF(gbut), BUTTONs(_("Edit Custom"), grad_evt),
2516 WDONE, GROUP0, WDONE,
2517 FXVBOX(_("Opacity")),
2518 VBOXB, OPT(optypes_txt, NUM_OTYPES, otype), WDONE,
2519 GROUPN, EQBOX,
2520 CHECK(_("Reverse"), orev),
2521 REF(obut), BUTTONs(_("Edit Custom"), grad_evt),
2522 WDONE, GROUP0, WDONE,
2523 WDONE,
2524 /* Cancel / Apply / OK */
2525 OKBOX3(_("OK"), grad_evt, _("Cancel"), NULL, _("Apply"), grad_evt),
2526 WSHOW
2527 };
2528 #undef WBbase
2529
gradient_setup(int mode)2530 void gradient_setup(int mode)
2531 {
2532 grad_dd tdata;
2533
2534 memset(&tdata, 0, sizeof(tdata));
2535 tdata.pmouse = !mode && !inifile_get_gboolean("centerSettings", TRUE);
2536 tdata.channel = tdata.nchan = mem_channel;
2537 tdata.opac = grad_opacity;
2538 tdata.gpp = tdata.gp; // Gradient menu is empty initially
2539 memcpy(tdata.temps, gradient, sizeof(tdata.temps));
2540 memcpy(tdata.tmaps, graddata, sizeof(tdata.tmaps));
2541 memcpy(tdata.tbytes, gradbytes, sizeof(tdata.tbytes));
2542 run_create_(grad_code, &tdata, sizeof(tdata), script_cmds);
2543 }
2544
2545 /// GRADIENT PICKER
2546
2547 static int pickg_grad = GRAD_TYPE_RGB, pickg_cspace = CSPACE_LXN;
2548
do_pick_gradient(filterwindow_dd * dt,void ** wdata)2549 static int do_pick_gradient(filterwindow_dd *dt, void **wdata)
2550 {
2551 unsigned char buf[256];
2552 int len;
2553
2554 run_query(wdata);
2555
2556 len = mem_pick_gradient(buf, pickg_cspace, pickg_grad);
2557
2558 mem_clip_new(len, 1, 1, CMASK_IMAGE, NULL);
2559 if (mem_clipboard) memcpy(mem_clipboard, buf, len);
2560
2561 update_stuff(UPD_XCOPY);
2562 pressed_paste(TRUE);
2563
2564 return TRUE;
2565 }
2566
2567 #define WBbase filterwindow_dd
2568 static void *gp_code[] = {
2569 TABLE2(2), OPNAME0,
2570 TOPTv(_("Gradient"), interp_txt, 4, pickg_grad),
2571 TOPTv(_("Colour space"), cspnames_, NUM_CSPACES, pickg_cspace),
2572 WDONE, RET
2573 };
2574 #undef WBbase
2575
pressed_pick_gradient()2576 void pressed_pick_gradient()
2577 {
2578 static filterwindow_dd tdata = {
2579 _("Pick Gradient"), gp_code, FW_FN(do_pick_gradient) };
2580 run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
2581 }
2582
2583 /// SKEW WINDOW
2584
2585 typedef struct {
2586 int rgb, gamma;
2587 int angle[2], ofs[2], dist[2];
2588 int angles, lock;
2589 char **ftxt;
2590 void **aspin[2], **ospin[2];
2591 } skew_dd;
2592
2593 static int skew_mode = 6;
2594
click_skew_ok(skew_dd * dt,void ** wdata)2595 static void click_skew_ok(skew_dd *dt, void **wdata)
2596 {
2597 double xskew, yskew;
2598 int res;
2599
2600 run_query(wdata);
2601 if (dt->angles & 1) xskew = tan(dt->angle[0] * (M_PI / 18000.0));
2602 else xskew = dt->ofs[0] / (double)(dt->dist[0] * 100);
2603 if (dt->angles & 2) yskew = tan(dt->angle[1] * (M_PI / 18000.0));
2604 else yskew = dt->ofs[1] / (double)(dt->dist[1] * 100);
2605
2606 if (!xskew && !yskew)
2607 {
2608 alert_same_geometry();
2609 return;
2610 }
2611
2612 res = mem_skew(xskew, yskew, skew_mode, dt->gamma);
2613 if (!res)
2614 {
2615 update_stuff(UPD_GEOM);
2616 run_destroy(wdata);
2617 }
2618 else memory_errors(res);
2619 }
2620
skew_moved(skew_dd * dt,void ** wdata,int what,void ** where)2621 static void skew_moved(skew_dd *dt, void **wdata, int what, void **where)
2622 {
2623 void *cause = cmd_read(where, dt);
2624 int i;
2625
2626 if (dt->lock) return; // Avoid recursion
2627 dt->lock = TRUE;
2628
2629 for (i = 0; i < 2; i++)
2630 {
2631 /* Offset for angle */
2632 if (cause == &dt->angle[i])
2633 {
2634 cmd_set(dt->ospin[i], rint(dt->dist[i] * 100 *
2635 tan(dt->angle[i] * (M_PI / 18000.0))));
2636 dt->angles |= 1 << i;
2637 }
2638 /* Angle for offset */
2639 else if ((cause == &dt->ofs[i]) || (cause == &dt->dist[i]))
2640 {
2641 cmd_set(dt->aspin[i], rint(atan(dt->ofs[i] /
2642 (dt->dist[i] * 100)) * (18000.0 / M_PI)));
2643 dt->angles &= ~(1 << i);
2644 }
2645 }
2646
2647 dt->lock = FALSE;
2648 }
2649
2650 #define WBbase skew_dd
2651 static void *skew_code[] = {
2652 WINDOWm(_("Skew")),
2653 TABLE(4, 3),
2654 BORDER(LABEL, 0),
2655 TLLABEL(_("Horizontal "), 0, 1), GROUPN,
2656 REF(aspin[0]), TLFSPIN(angle[0], -8999, 8999, 1, 1),
2657 EVENT(CHANGE, skew_moved), TLLABEL(_("Angle"), 1, 0),
2658 REF(ospin[0]), TLFSPIN(ofs[0], -MAX_WIDTH * 100, MAX_WIDTH * 100, 2, 1),
2659 EVENT(CHANGE, skew_moved), TLLABEL(_("Offset"), 2, 0),
2660 TLSPIN(dist[0], 1, MAX_HEIGHT, 3, 1),
2661 EVENT(CHANGE, skew_moved), TLLABEL(_("At distance"), 3, 0),
2662 TLLABEL(_("Vertical"), 0, 2), GROUPN,
2663 REF(aspin[1]), TLFSPIN(angle[1], -8999, 8999, 1, 2),
2664 EVENT(CHANGE, skew_moved), OPNAME("Angle"),
2665 REF(ospin[1]), TLFSPIN(ofs[1], -MAX_HEIGHT * 100, MAX_HEIGHT * 100, 2, 2),
2666 EVENT(CHANGE, skew_moved), OPNAME("Offset"),
2667 TLSPIN(dist[1], 1, MAX_WIDTH, 3, 2),
2668 EVENT(CHANGE, skew_moved), OPNAME("At distance"),
2669 WDONE, GROUP0,
2670 HSEP,
2671 IFx(rgb, 1),
2672 CHECK(_("Gamma corrected"), gamma),
2673 HSEP,
2674 BORDER(RPACK, 0),
2675 RPACKDv(ftxt, 0, skew_mode), OPNAME(""),
2676 HSEP,
2677 ENDIF(1),
2678 OKBOXB(_("OK"), click_skew_ok, _("Cancel"), NULL),
2679 WSHOW
2680 };
2681 #undef WBbase
2682
pressed_skew()2683 void pressed_skew()
2684 {
2685 char *fnames[sizeof(scale_modes) / sizeof(scale_modes[0])];
2686 skew_dd tdata;
2687
2688
2689 memcpy(fnames, scale_modes, sizeof(fnames));
2690 fnames[1] = _("Bilinear");
2691
2692 memset(&tdata, 0, sizeof(tdata));
2693 tdata.rgb = mem_img_bpp == 3;
2694 tdata.gamma = use_gamma;
2695 tdata.dist[0] = mem_height;
2696 tdata.dist[1] = mem_width;
2697 tdata.ftxt = fnames;
2698
2699 run_create_(skew_code, &tdata, sizeof(tdata), script_cmds);
2700 }
2701
2702 /// TRACING IMAGE WINDOW
2703
2704 typedef struct {
2705 int src;
2706 int w, h;
2707 int x, y;
2708 int scale, state;
2709 void **wspin, **hspin;
2710 } bkg_dd;
2711
bkg_evt(bkg_dd * dt,void ** wdata,int what)2712 static void bkg_evt(bkg_dd *dt, void **wdata, int what)
2713 {
2714 run_query(wdata);
2715 if (what == op_EVT_SELECT) // set source
2716 {
2717 int w = 0, h = 0;
2718
2719 switch (dt->src)
2720 {
2721 case 0: w = bkg_w; h = bkg_h; break;
2722 case 1: break;
2723 case 2: w = mem_width; h = mem_height; break;
2724 case 3: if (mem_clipboard) w = mem_clip_w , h = mem_clip_h;
2725 break;
2726 }
2727
2728 cmd_set(dt->wspin, w);
2729 cmd_set(dt->hspin, h);
2730 }
2731 else // OK/Apply
2732 {
2733 bkg_x = dt->x;
2734 bkg_y = dt->y;
2735 bkg_scale = dt->scale;
2736 bkg_flag = dt->state;
2737 if (!config_bkg(dt->src)) memory_errors(1);
2738 update_stuff(UPD_RENDER);
2739 }
2740 if (what == op_EVT_OK) run_destroy(wdata);
2741 }
2742
2743 static char *srcs_txt[4] = { _("Unchanged"), _("None"), _("Image"),
2744 _("Clipboard") };
2745
2746 #define WBbase bkg_dd
2747 static void *bkg_code[] = {
2748 WINDOWm(_("Tracing Image")),
2749 TABLE(3, 4),
2750 TLABEL(_("Source")),
2751 TLOPTle(srcs_txt, 4, src, bkg_evt, 1, 0, 2), TRIGGER,
2752 TLABEL(_("Size")),
2753 REF(wspin), TLNOSPINr(w, 1, 1),
2754 REF(hspin), TLNOSPINr(h, 2, 1),
2755 TLABEL(_("Origin")),
2756 TLSPIN(x, -MAX_WIDTH, MAX_WIDTH, 1, 2),
2757 TLSPIN(y, -MAX_HEIGHT, MAX_HEIGHT, 2, 2),
2758 TLABEL(_("Relative scale")),
2759 TLSPIN(scale, 1, MAX_ZOOM, 1, 3),
2760 WDONE,
2761 CHECK(_("Display"), state),
2762 HSEP,
2763 OKBOX3B(_("OK"), bkg_evt, _("Cancel"), NULL, _("Apply"), bkg_evt),
2764 WSHOW
2765 };
2766 #undef WBbase
2767
bkg_setup()2768 void bkg_setup()
2769 {
2770 bkg_dd tdata = { 0, 0, 0, bkg_x, bkg_y, bkg_scale, bkg_flag };
2771 run_create(bkg_code, &tdata, sizeof(tdata));
2772 }
2773
2774 /// SEGMENTATION WINDOW
2775
2776 seg_state *seg_preview;
2777
2778 typedef struct {
2779 int cspace, dist;
2780 int threshold, rank, size[3];
2781 int preview, progress;
2782 int step;
2783 void **tspin, **pbutton;
2784 seg_state *s;
2785 } seg_dd;
2786
2787 static int seg_cspace = CSPACE_LXN, seg_dist = DIST_LINF;
2788 static int seg_rank = 4, seg_minsize = 1;
2789 static guint seg_idle;
2790
2791 /* Change colorspace or distance measure, causing full recalculation */
seg_mode_toggled(seg_dd * dt,void ** wdata,int what,void ** where)2792 static void seg_mode_toggled(seg_dd *dt, void **wdata, int what, void **where)
2793 {
2794 cmd_read(where, dt);
2795 mem_seg_prepare(dt->s, mem_img[CHN_IMAGE], mem_width, mem_height,
2796 dt->progress, dt->cspace, dt->dist);
2797 /* Disable preview if cancelled, change threshold if not */
2798 if (!dt->s->phase) cmd_set(dt->pbutton, FALSE);
2799 else cmd_set(dt->tspin, rint(mem_seg_threshold(dt->s) * 100));
2800 }
2801
2802 /* Do phase 2 (segmentation) in the background */
seg_process_idle(void ** wdata)2803 static gboolean seg_process_idle(void **wdata)
2804 {
2805 seg_dd *dt = GET_DDATA(wdata);
2806
2807 if (seg_preview && (dt->s->phase == 1))
2808 {
2809 #define SEG_STEP 100000
2810 dt->step = mem_seg_process_chunk(dt->step, SEG_STEP, dt->s);
2811 #undef SEG_STEP
2812 if (!(dt->s->phase & 2)) return (TRUE); // Not yet completed
2813 cmd_cursor(GET_WINDOW(wdata), NULL);
2814 seg_idle = 0; // In case update_stuff() ever calls main loop
2815 update_stuff(UPD_RENDER);
2816 }
2817 seg_idle = 0;
2818 return (FALSE);
2819 }
2820
2821 /* Change segmentation limits, causing phase 2 restart */
seg_spin_changed(seg_dd * dt,void ** wdata,int what,void ** where)2822 static void seg_spin_changed(seg_dd *dt, void **wdata, int what, void **where)
2823 {
2824 void *cause = cmd_read(where, dt);
2825 seg_state *s = dt->s;
2826
2827 if (cause == &dt->threshold) s->threshold = dt->threshold * 0.01;
2828 else if (cause == &dt->rank) s->minrank = dt->rank;
2829 else s->minsize = dt->size[0];
2830 s->phase &= 1; // Need phase 2 rerun
2831 dt->step = 0; // Restart phase 2 afresh
2832 if (seg_preview)
2833 {
2834 if (dt->progress) cmd_cursor(GET_WINDOW(wdata), busy_cursor);
2835 if (!seg_idle) seg_idle = threads_idle_add_priority(
2836 GTK_PRIORITY_REDRAW + 5, (GtkFunction)seg_process_idle, wdata);
2837 }
2838 }
2839
2840 /* Finish all calculations (preparation and segmentation) */
seg_process(seg_dd * dt)2841 static int seg_process(seg_dd *dt)
2842 {
2843 /* Run phase 1 if necessary */
2844 if (!dt->s->phase) mem_seg_prepare(dt->s, mem_img[CHN_IMAGE],
2845 mem_width, mem_height, dt->progress, dt->cspace, dt->dist);
2846 /* Run phase 2 if possible & necessary */
2847 if (dt->s->phase == 1) mem_seg_process(dt->s);
2848 /* Return whether job is done */
2849 return (dt->s->phase > 1);
2850 }
2851
seg_evt(seg_dd * dt,void ** wdata,int what,void ** where)2852 static void seg_evt(seg_dd *dt, void **wdata, int what, void **where)
2853 {
2854 seg_state *oldp = seg_preview;
2855 int update = 0;
2856
2857 if (what == op_EVT_CHANGE) // Toggle preview
2858 {
2859 cmd_read(where, dt);
2860 if (dt->preview ^ !oldp) return; // Nothing to do
2861 if (!oldp) // Enable
2862 {
2863 /* Do segmentation conspicuously at first */
2864 if (!seg_process(dt)) cmd_set(dt->pbutton, FALSE);
2865 else seg_preview = dt->s;
2866 if (seg_preview) update_stuff(UPD_RENDER);
2867 return;
2868 }
2869 }
2870
2871 /* First, disable preview */
2872 if (oldp)
2873 {
2874 if (seg_idle) gtk_idle_remove(seg_idle);
2875 seg_idle = 0;
2876 seg_preview = NULL;
2877 update = UPD_RENDER;
2878 }
2879
2880 if (what == op_EVT_OK)
2881 {
2882 /* Update parameters */
2883 run_query(wdata);
2884 seg_cspace = dt->cspace;
2885 seg_dist = dt->dist;
2886 seg_rank = dt->rank;
2887 seg_minsize = dt->size[0];
2888
2889 /* Now, finish segmentation & render results */
2890 if (seg_process(dt))
2891 {
2892 spot_undo(UNDO_FILT);
2893 mem_seg_render(mem_img[CHN_IMAGE], dt->s);
2894 mem_undo_prepare();
2895 update |= UPD_IMG;
2896 }
2897 }
2898
2899 if (what != op_EVT_CHANGE) // Finished
2900 {
2901 oldp = dt->s;
2902 run_destroy(wdata);
2903 free(oldp);
2904 }
2905 // !!! Maybe better to add & use an integrated progressbar?
2906 else cmd_cursor(GET_WINDOW(wdata), NULL);
2907
2908 update_stuff(update);
2909 }
2910
2911 #define WBbase seg_dd
2912 static void *seg_code[] = {
2913 WINDOWm(_("Segment Image")),
2914 BORDER(RPACK, 0),
2915 FRPACKe(_("Colour space"), cspnames_, NUM_CSPACES, 1, cspace, seg_mode_toggled),
2916 FRPACKe(_("Difference measure"), dist_txt, 3, 1, dist, seg_mode_toggled),
2917 TABLE2(3),
2918 REF(tspin), TFSPIN(_("Threshold"), threshold, 0, 500000),
2919 EVENT(CHANGE, seg_spin_changed),
2920 TSPIN(_("Level"), rank, 0, 30), EVENT(CHANGE, seg_spin_changed),
2921 TSPINa(_("Minimum size"), size), EVENT(CHANGE, seg_spin_changed),
2922 WDONE,
2923 EQBOX,
2924 CANCELBTN(_("Cancel"), seg_evt),
2925 REF(pbutton), TOGGLE(_("Preview"), preview, seg_evt),
2926 OKBTN(_("Apply"), seg_evt),
2927 WSHOW
2928 };
2929 #undef WBbase
2930
pressed_segment()2931 void pressed_segment()
2932 {
2933 seg_dd tdata;
2934 seg_state *s;
2935 int progress = 0, sz = mem_width * mem_height;
2936
2937
2938 if (sz == 1) return; /* 1 pixel in image is trivial - do nothing */
2939 if (sz >= 1024 * 1024) progress = SEG_PROGRESS;
2940
2941 s = mem_seg_prepare(NULL, mem_img[CHN_IMAGE], mem_width, mem_height,
2942 progress, seg_cspace, seg_dist);
2943 if (!s)
2944 {
2945 memory_errors(1);
2946 return;
2947 }
2948 if (!s->phase) return; // Terminated by user
2949 s->threshold = mem_seg_threshold(s);
2950
2951 tdata.s = s;
2952 tdata.progress = progress;
2953
2954 tdata.cspace = seg_cspace;
2955 tdata.dist = seg_dist;
2956 tdata.threshold = rint(s->threshold * 100);
2957 tdata.rank = s->minrank = seg_rank;
2958 tdata.size[0] = s->minsize = seg_minsize;
2959 tdata.size[1] = 1;
2960 tdata.size[2] = sz;
2961 tdata.preview = FALSE;
2962 tdata.step = 0;
2963
2964 run_create_(seg_code, &tdata, sizeof(tdata), script_cmds);
2965 }
2966
2967 /// THRESHOLDING WINDOW
2968
2969 int xhold_preview;
2970
2971 typedef struct {
2972 int lo[3], hi[3], mode, preview;
2973 int rgb;
2974 void **lspin, **hspin, **pbutton;
2975 } xhold_dd;
2976
xhold_changed(xhold_dd * dt,void ** wdata,int what,void ** where)2977 static void xhold_changed(xhold_dd *dt, void **wdata, int what, void **where)
2978 {
2979 void *cause = cmd_read(where, dt);
2980
2981 if (dt->lo[0] > dt->hi[0]) // Inversion tried to happen
2982 {
2983 if (cause == dt->hspin) cmd_set(dt->lspin, dt->hi[0]);
2984 else cmd_set(dt->hspin, dt->lo[0]);
2985 return; // Changed slider did the update
2986 }
2987 mem_ts.lo = dt->lo[0];
2988 mem_ts.hi = dt->hi[0];
2989 mem_ts.mode = dt->mode;
2990 if (xhold_preview) update_stuff(UPD_RENDER);
2991 }
2992
xhold_evt(xhold_dd * dt,void ** wdata,int what,void ** where)2993 static void xhold_evt(xhold_dd *dt, void **wdata, int what, void **where)
2994 {
2995 int update = 0;
2996
2997 if (what == op_EVT_CHANGE) // Toggle preview
2998 {
2999 cmd_read(where, dt);
3000 if (dt->preview ^ !xhold_preview) return; // Nothing to do
3001 xhold_preview = dt->preview;
3002 update_stuff(UPD_RENDER);
3003 return;
3004 }
3005
3006 /* First, disable preview */
3007 if (xhold_preview) update = UPD_RENDER;
3008 xhold_preview = FALSE;
3009
3010 while (what == op_EVT_OK)
3011 {
3012 unsigned char *dest, *xbuf, *mask;
3013 int i, bpp = MEM_BPP;
3014
3015 run_query(wdata); // Update parameters
3016 mask = malloc(mem_width * (bpp + 1));
3017 if (!mask) break;
3018 spot_undo(UNDO_FILT);
3019 xbuf = mask + mem_width;
3020
3021 for (i = 0; i < mem_height; i++)
3022 {
3023 row_protected(0, i, mem_width, mask);
3024 dest = mem_img[mem_channel] + i * mem_width * bpp;
3025 do_xhold(0, 1, mem_width, mask, xbuf, dest);
3026 process_img(0, 1, mem_width, mask, dest, dest, xbuf,
3027 NULL, bpp, BLENDF_SET | BLENDF_INVM);
3028 }
3029
3030 free(mask);
3031 mem_undo_prepare();
3032 update |= UPD_IMG;
3033 break;
3034 }
3035
3036 run_destroy(wdata); // Finished
3037 update_stuff(update);
3038 }
3039
3040 static char *xh_txt[] = {_("Max"), _("Min"), _("Red"), _("Green"), _("Blue")};
3041
3042 #define WBbase xhold_dd
3043 static void *xhold_code[] = {
3044 WINDOWm(_("Threshold Image")), DEFW(300),
3045 TABLE2(2), OPNAME0,
3046 REF(lspin), TSPINSLIDEa(_("From"), lo),
3047 EVENT(CHANGE, xhold_changed), TRIGGER,
3048 REF(hspin), TSPINSLIDEa(_("To"), hi), EVENT(CHANGE, xhold_changed),
3049 WDONE,
3050 IFx(rgb, 1),
3051 BORDER(RPACK, 0),
3052 RPACKe(xh_txt, XHOLD_NMODES, 0, mode, xhold_changed),
3053 OPNAME(""), // default
3054 ENDIF(1),
3055 HSEP,
3056 EQBOX,
3057 CANCELBTN(_("Cancel"), xhold_evt),
3058 REF(pbutton), TOGGLE(_("Preview"), preview, xhold_evt),
3059 OKBTN(_("OK"), xhold_evt),
3060 WSHOW
3061 };
3062 #undef WBbase
3063
pressed_xhold()3064 void pressed_xhold()
3065 {
3066 xhold_dd tdata = { { 128, 0, 255 }, { 255, 0, 255 }, 0, FALSE, MEM_BPP == 3 };
3067
3068 if (IS_INDEXED)
3069 {
3070 tdata.lo[0] = mem_cols / 2;
3071 tdata.hi[0] = tdata.hi[2] = tdata.lo[2] = mem_cols - 1;
3072 }
3073 run_create_(xhold_code, &tdata, sizeof(tdata), script_cmds);
3074 }
3075
3076 /// NOISE WINDOW
3077
3078 int noise_preview;
3079 static int noise_seed, noise_map;
3080 static int noise_xs = 256, noise_ys = 256, noise_lvl = 1;
3081
3082 typedef struct {
3083 int xs, ys, lvl, seed, map, preview;
3084 int rgb;
3085 char **ncp, *nc[5];
3086 void **sspin, **xspin, **yspin, **lspin, **copt, **pbutton;
3087 } noise_dd;
3088
noise_changed(noise_dd * dt,void ** wdata,int what,void ** where)3089 static void noise_changed(noise_dd *dt, void **wdata, int what, void **where)
3090 {
3091 cmd_read(where, dt);
3092 if (!noise_preview) return;
3093 init_perlin(dt->seed, dt->xs, dt->ys, dt->lvl, dt->map);
3094 update_stuff(UPD_RENDER);
3095 }
3096
noise_evt(noise_dd * dt,void ** wdata,int what,void ** where)3097 static void noise_evt(noise_dd *dt, void **wdata, int what, void **where)
3098 {
3099 int update = 0;
3100
3101 if (what == op_EVT_OK)
3102 {
3103 /* Update parameters */
3104 run_query(wdata);
3105 noise_seed = dt->seed;
3106 noise_map = dt->map;
3107 noise_xs = dt->xs;
3108 noise_ys = dt->ys;
3109 noise_lvl = dt->lvl;
3110 }
3111
3112 if (what != op_EVT_CANCEL)
3113 init_perlin(dt->seed, dt->xs, dt->ys, dt->lvl, dt->map);
3114
3115 if (what == op_EVT_CHANGE) // Toggle preview
3116 {
3117 cmd_read(where, dt);
3118 if (dt->preview ^ !noise_preview) return; // Nothing to do
3119 noise_preview = dt->preview;
3120 update_stuff(UPD_RENDER);
3121 return;
3122 }
3123
3124 /* First, disable preview */
3125 if (noise_preview) update = UPD_RENDER;
3126 noise_preview = FALSE;
3127
3128 while (what == op_EVT_OK)
3129 {
3130 spot_undo(UNDO_FILT);
3131 mem_perlin();
3132 mem_undo_prepare();
3133 update |= UPD_IMG;
3134 break;
3135 }
3136
3137 run_destroy(wdata); // Finished
3138 update_stuff(update);
3139 }
3140
3141 #define WBbase noise_dd
3142 static void *noise_code[] = {
3143 WINDOWm(_("Solid Noise")), DEFW(300),
3144 TABLE2(5),
3145 REF(sspin), TSPIN(_("Seed"), seed, 0, INT_MAX),
3146 EVENT(CHANGE, noise_changed), TRIGGER,
3147 REF(xspin), TSPIN(_("X span"), xs, 1, MAX_WIDTH), EVENT(CHANGE, noise_changed),
3148 REF(yspin), TSPIN(_("Y span"), ys, 1, MAX_HEIGHT), EVENT(CHANGE, noise_changed),
3149 REF(lspin), TSPIN(_("Octaves"), lvl, 1, 8), EVENT(CHANGE, noise_changed),
3150 IFx(rgb, 1),
3151 REF(copt), TOPTDe(_("Colours"), ncp, map, noise_changed),
3152 ENDIF(1),
3153 WDONE,
3154 HSEP,
3155 EQBOX,
3156 CANCELBTN(_("Cancel"), noise_evt),
3157 REF(pbutton), TOGGLE(_("Preview"), preview, noise_evt),
3158 OKBTN(_("OK"), noise_evt),
3159 WSHOW
3160 };
3161 #undef WBbase
3162
pressed_noise()3163 void pressed_noise()
3164 {
3165 noise_dd tdata = { noise_xs, noise_ys, noise_lvl, noise_seed, noise_map,
3166 FALSE, MEM_BPP == 3, tdata.nc, { _("Greyscale"), _("Gradient"),
3167 _("Palette"), mem_clipboard ? _("Clipboard") : "", NULL } };
3168
3169 run_create_(noise_code, &tdata, sizeof(tdata), script_cmds);
3170 }
3171