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