1 /*	canvas.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 "ani.h"
28 #include "png.h"
29 #include "mainwindow.h"
30 #include "otherwindow.h"
31 #include "inifile.h"
32 #include "canvas.h"
33 #include "viewer.h"
34 #include "layer.h"
35 #include "polygon.h"
36 #include "wu.h"
37 #include "prefs.h"
38 #include "spawn.h"
39 #include "channels.h"
40 #include "toolbar.h"
41 #include "font.h"
42 
43 float can_zoom = 1;				// Zoom factor 1..MAX_ZOOM
44 int margin_main_xy[2];				// Top left of image from top left of canvas
45 int margin_view_xy[2];
46 int marq_status = MARQUEE_NONE, marq_xy[4] = { -1, -1, -1, -1 };	// Selection marquee
47 int marq_drag_x, marq_drag_y;						// Marquee dragging offset
48 int line_status = LINE_NONE, line_xy[4];				// Line tool
49 int poly_status = POLY_NONE;						// Polygon selection tool
50 int clone_status, clone_x, clone_y, clone_dx, clone_dy;			// Clone tool state
51 int clone_mode = TRUE, clone_x0 = -1, clone_y0 = -1,			// Clone settings
52 	clone_dx0, clone_dy0;
53 
54 int recent_files;					// Current recent files setting
55 char *recent_filenames[MAX_RECENT];			// Recent filenames themselves
56 
57 int	show_paste,					// Show contents of clipboard while pasting
58 	col_reverse,					// Painting with right button
59 	status_on[STATUS_ITEMS],			// Show status bar items?
60 	text_paste,					// Are we pasting text?
61 	canvas_image_centre,				// Are we centering the image?
62 	chequers_optimize,				// Are we optimizing the chequers for speed?
63 	cursor_zoom					// Are we zooming at cursor position?
64 	;
65 
66 int brush_spacing;	// Step in non-continuous mode; 0 means use event coords
67 int lasso_sel;		// Lasso by selection channel (just trim it) if present
68 
69 int preserved_gif_delay = 10, undo_load;
70 
71 static int update_later;
72 
73 
74 ///	STATUS BAR
75 
76 void **label_bar[STATUS_ITEMS];
77 
update_image_bar()78 static void update_image_bar()
79 {
80 	char txt[128], txt2[16], *tmp = cspnames[CSPACE_RGB];
81 
82 
83 	if (!status_on[STATUS_GEOMETRY]) return;
84 
85 	if (mem_img_bpp == 1) sprintf(tmp = txt2, "%i", mem_cols);
86 
87 	tmp = txt + snprintf(txt, 80, "%s %i x %i x %s",
88 		channames[mem_channel], mem_width, mem_height, tmp);
89 
90 	if ( mem_img[CHN_ALPHA] || mem_img[CHN_SEL] || mem_img[CHN_MASK] )
91 	{
92 		strcpy(tmp, " + "); tmp += 3;
93 		if (mem_img[CHN_ALPHA]) *tmp++ = 'A';
94 		if (mem_img[CHN_SEL])   *tmp++ = 'S';
95 		if (mem_img[CHN_MASK])  *tmp++ = 'M';
96 	// !!! String not NUL-terminated at this point
97 	}
98 
99 	if ( layers_total>0 )
100 		tmp += sprintf(tmp, "  (%i/%i)", layer_selected, layers_total);
101 	if ( mem_xpm_trans>=0 )
102 		tmp += sprintf(tmp, "  (T=%i)", mem_xpm_trans);
103 	strcpy(tmp, "  ");
104 	cmd_setv(label_bar[STATUS_GEOMETRY], txt, LABEL_VALUE);
105 }
106 
update_sel_bar(int now)107 void update_sel_bar(int now)		// Update selection stats on status bar
108 {
109 	char txt[64] = "";
110 	int rect[4];
111 	float lang, llen;
112 	grad_info *grad = gradient + mem_channel;
113 
114 
115 	if (!status_on[STATUS_SELEGEOM]) return;
116 	update_later |= CF_SELBAR;
117 	if (script_cmds && !now) return;
118 	update_later &= ~CF_SELBAR;
119 
120 	if ((((tool_type == TOOL_SELECT) || (tool_type == TOOL_POLYGON)) &&
121 		(marq_status > MARQUEE_NONE)) ||
122 		((tool_type == TOOL_GRADIENT) && (grad->status != GRAD_NONE)) ||
123 		((tool_type == TOOL_LINE) && (line_status != LINE_NONE)))
124 	{
125 		if (tool_type == TOOL_GRADIENT)
126 		{
127 			copy4(rect, grad->xy);
128 			rect[2] -= rect[0];
129 			rect[3] -= rect[1];
130 			lang = (180.0 / M_PI) * atan2(rect[2], -rect[3]);
131 		}
132 		else if (tool_type == TOOL_LINE)
133 		{
134 			copy4(rect, line_xy);
135 			rect[2] -= rect[0];
136 			rect[3] -= rect[1];
137 			lang = (180.0 / M_PI) * atan2(rect[2], -rect[3]);
138 		}
139 		else
140 		{
141 			marquee_at(rect);
142 			lang = (180.0 / M_PI) * atan2(marq_x2 - marq_x1,
143 				marq_y1 - marq_y2);
144 		}
145 		llen = sqrt(rect[2] * rect[2] + rect[3] * rect[3]);
146 		snprintf(txt, 60, "  %i,%i : %i x %i   %.1f' %.1f\"",
147 			rect[0], rect[1], rect[2], rect[3], lang, llen);
148 	}
149 
150 	else if (tool_type == TOOL_POLYGON)
151 	{
152 		snprintf(txt, 60, "  (%i)%c", poly_points,
153 			poly_status != POLY_DONE ? '+' : '\0');
154 	}
155 
156 	cmd_setv(label_bar[STATUS_SELEGEOM], txt, LABEL_VALUE);
157 }
158 
chan_txt_cat(char * txt,int chan,int x,int y)159 static char *chan_txt_cat(char *txt, int chan, int x, int y)
160 {
161 	if (!mem_img[chan]) return (txt);
162 	return (txt + sprintf(txt, "%i", mem_img[chan][x + mem_width*y]));
163 }
164 
update_xy_bar(int x,int y)165 void update_xy_bar(int x, int y)
166 {
167 	char txt[96], *tmp = txt;
168 	int pixel;
169 
170 	if (status_on[STATUS_CURSORXY])
171 	{
172 		snprintf(txt, 60, "%i,%i", x, y);
173 		cmd_setv(label_bar[STATUS_CURSORXY], txt, LABEL_VALUE);
174 	}
175 
176 	if (!status_on[STATUS_PIXELRGB]) return;
177 	*tmp = '\0';
178 	if ((x >= 0) && (x < mem_width) && (y >= 0) && (y < mem_height))
179 	{
180 		pixel = get_pixel_img(x, y);
181 		if (mem_img_bpp == 1)
182 			tmp += sprintf(tmp, "[%u] = {%i,%i,%i}", pixel,
183 				mem_pal[pixel].red, mem_pal[pixel].green,
184 				mem_pal[pixel].blue);
185 		else
186 			tmp += sprintf(tmp, "{%i,%i,%i}", INT_2_R(pixel),
187 				INT_2_G(pixel), INT_2_B(pixel));
188 		if (mem_img[CHN_ALPHA] || mem_img[CHN_SEL] || mem_img[CHN_MASK])
189 		{
190 			strcpy(tmp, " + {"); tmp += 4;
191 			tmp = chan_txt_cat(tmp, CHN_ALPHA, x, y);
192 			*tmp++ = ',';
193 			tmp = chan_txt_cat(tmp, CHN_SEL, x, y);
194 			*tmp++ = ',';
195 			tmp = chan_txt_cat(tmp, CHN_MASK, x, y);
196 			strcpy(tmp, "}");
197 		}
198 	}
199 	cmd_setv(label_bar[STATUS_PIXELRGB], txt, LABEL_VALUE);
200 }
201 
update_undo_bar()202 static void update_undo_bar()
203 {
204 	char txt[32];
205 
206 	if (status_on[STATUS_UNDOREDO])
207 	{
208 		sprintf(txt, "%i+%i", mem_undo_done, mem_undo_redo);
209 		cmd_setv(label_bar[STATUS_UNDOREDO], txt, LABEL_VALUE);
210 	}
211 }
212 
init_status_bar()213 void init_status_bar()
214 {
215 	int i;
216 
217 	for (i = 0; i < STATUS_ITEMS; i++)
218 		cmd_showhide(label_bar[i], status_on[i]);
219 	update_image_bar();
220 	update_undo_bar();
221 }
222 
223 
commit_paste(int swap,int * update)224 void commit_paste(int swap, int *update)
225 {
226 	image_info ti;
227 	unsigned char *image, *xbuf, *mask, *alpha = NULL;
228 	unsigned char *old_image, *old_alpha;
229 	int op = 255, opacity = tool_opacity, bpp = MEM_BPP;
230 	int fx, fy, fw, fh, fx2, fy2;		// Screen coords
231 	int i, ua, cmask, ofs, iofs, upd = UPD_IMGP, fail = TRUE;
232 
233 
234 	fx = marq_x1 > 0 ? marq_x1 : 0;
235 	fy = marq_y1 > 0 ? marq_y1 : 0;
236 	fx2 = marq_x2 < mem_width ? marq_x2 : mem_width - 1;
237 	fy2 = marq_y2 < mem_height ? marq_y2 : mem_height - 1;
238 
239 	fw = fx2 - fx + 1;
240 	fh = fy2 - fy + 1;
241 
242 	mask = multialloc(MA_SKIP_ZEROSIZE, &mask, fw, &alpha,
243 		((mem_channel == CHN_IMAGE) && RGBA_mode && mem_img[CHN_ALPHA] &&
244 		!mem_clip_alpha && !channel_dis[CHN_ALPHA]) * fw,
245 		&xbuf, NEED_XBUF_PASTE * fw * bpp, NULL);
246 	if (!mask) goto quit; // Not enough memory
247 	if (alpha) memset(alpha, channel_col_A[CHN_ALPHA], fw);
248 
249 	/* Ignore clipboard alpha if disabled */
250 	ua = channel_dis[CHN_ALPHA] | !mem_clip_alpha;
251 
252 	if (swap) /* Prepare to convert image contents into new clipboard */
253 	{
254 		cmask = CMASK_IMAGE | CMASK_SEL;
255 		if ((mem_channel == CHN_IMAGE) && mem_img[CHN_ALPHA] &&
256 			 !channel_dis[CHN_ALPHA]) cmask |= CMASK_ALPHA;
257 		if (!mem_alloc_image(AI_CLEAR | AI_NOINIT, &ti, fw, fh, MEM_BPP,
258 			cmask, NULL)) goto quit;
259 		copy_area(&ti, &mem_image, fx, fy);
260 	}
261 
262 	/* Offset in memory */
263 	ofs = (fy - marq_y1) * mem_clip_w + (fx - marq_x1);
264 	image = mem_clipboard + ofs * mem_clip_bpp;
265 	iofs = fy * mem_width + fx;
266 
267 	mem_undo_next(UNDO_PASTE);	// Do memory stuff for undo
268 
269 	old_image = mem_img[mem_channel];
270 	old_alpha = mem_img[CHN_ALPHA];
271 	if (mem_undo_opacity)
272 	{
273 		old_image = mem_undo_previous(mem_channel);
274 		old_alpha = mem_undo_previous(CHN_ALPHA);
275 	}
276 	if (IS_INDEXED) op = opacity = 0;
277 
278 	for (i = 0; i < fh; i++)
279 	{
280 		unsigned char *wa = ua ? alpha : mem_clip_alpha + ofs;
281 		unsigned char *wm = mem_clip_mask ? mem_clip_mask + ofs : NULL;
282 		unsigned char *img = image;
283 
284 		row_protected(fx, fy + i, fw, mask);
285 		if (swap)
286 		{
287 			unsigned char *ws = ti.img[CHN_SEL] + i * fw;
288 
289 			memcpy(ws, mask, fw);
290 			process_mask(0, 1, fw, ws, NULL, NULL,
291 				ti.img[CHN_ALPHA] ? NULL : wa, wm, op, 0);
292 		}
293 
294 		process_mask(0, 1, fw, mask, mem_img[CHN_ALPHA] && wa ?
295 			mem_img[CHN_ALPHA] + iofs : NULL, old_alpha + iofs,
296 			wa, wm, opacity, 0);
297 
298 		if (mem_clip_bpp < bpp)
299 		{
300 			/* Convert paletted clipboard to RGB */
301 			do_convert_rgb(0, 1, fw, xbuf, img,
302 				mem_clip_paletted ? mem_clip_pal : mem_pal);
303 			img = xbuf;
304 		}
305 
306 		process_img(0, 1, fw, mask, mem_img[mem_channel] + iofs * bpp,
307 			old_image + iofs * bpp, img, xbuf, bpp, 0);
308 
309 		image += mem_clip_w * mem_clip_bpp;
310 		ofs += mem_clip_w;
311 		iofs += mem_width;
312 	}
313 
314 	if (swap)
315 	{
316 		if ((fw - mem_clip_w) | (fh - mem_clip_h))
317 			upd |= UPD_CGEOM & ~UPD_IMGMASK;
318 		/* Remove new mask if it's all 255 */
319 		if (is_filled(ti.img[CHN_SEL], 255, fw * fh))
320 		{
321 			free(ti.img[CHN_SEL]);
322 			ti.img[CHN_SEL] = NULL;
323 		}
324 		mem_clip_new(fw, fh, MEM_BPP, 0, NULL);
325 		memcpy(mem_clip.img, ti.img, sizeof(chanlist));
326 		// !!! marq_x2, marq_y2 will be set by update_stuff()
327 		mem_clip_x = marq_x1 = fx;
328 		mem_clip_y = marq_y1 = fy;
329 	}
330 
331 	fail = FALSE;
332 quit:	free(mask);
333 
334 	if (fail) memory_errors(1); /* Warn and not update */
335 	else if (!update) /* Update right now */
336 	{
337 		update_stuff(upd);
338 		lr_update_area(layer_selected, fx, fy, fw, fh);
339 	}
340 	else /* Accumulate update area for later */
341 	{
342 	/* !!! Swap does not use this branch, and isn't supported here */
343 		fw += fx; fh += fy;
344 		if (fx < update[0]) update[0] = fx;
345 		if (fy < update[1]) update[1] = fy;
346 		if (fw > update[2]) update[2] = fw;
347 		if (fh > update[3]) update[3] = fh;
348 	}
349 }
350 
iso_trans(int mode)351 void iso_trans(int mode)
352 {
353 	int i = mem_isometrics(mode);
354 
355 	if (!i) update_stuff(UPD_GEOM);
356 	else if (i == -5) alert_box(_("Error"),
357 		_("The image is too large to transform."), NULL);
358 	else memory_errors(i);
359 }
360 
pressed_inv(int what)361 void pressed_inv(int what)
362 {
363 	spot_undo(UNDO_INV);
364 
365 	if (what == FILT_INVERT) mem_invert();
366 	else mem_normalize();
367 
368 	mem_undo_prepare();
369 	update_stuff(UPD_COL);
370 }
371 
372 static int map_from, map_to;
373 
374 typedef struct {
375 	filterwindow_dd fw;
376 	int noclip;
377 } map_dd;
378 
do_map(map_dd * dt,void ** wdata)379 static int do_map(map_dd *dt, void **wdata)
380 {
381 	unsigned char map[768];
382 
383 	run_query(wdata);
384 
385 	spot_undo(UNDO_FILT);
386 	mem_prepare_map(map, map_from + MAP_GRAD);
387 	mem_remap_rgb(map, map_to);
388 	mem_undo_prepare();
389 
390 	return TRUE;
391 }
392 
393 static char *map_from_txt[3] = { _("Gradient"), _("Palette"), _("Clipboard") };
394 static char *map_to_txt[4] = { _("Value"), _("Red"), _("Green"), _("Blue") };
395 
396 #define WBbase map_dd
397 static void *map_code[] = {
398 	TABLE2(2),
399 	TLABEL(_("From ")),
400 	UNLESS(noclip), TLOPTv(map_from_txt, 3, map_from, 1, 0),
401 	IF(noclip), TLOPTv(map_from_txt, 2, map_from, 1, 0),
402 		ALTNAME(""), // default
403 	TOPTv(_("To "), map_to_txt, 4, map_to),
404 	WDONE, RET
405 };
406 #undef WBbase
407 
pressed_map()408 void pressed_map()
409 {
410 	map_dd tdata = { { _("Map"), map_code, FW_FN(do_map) }, !mem_clipboard };
411 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
412 }
413 
414 static int edge_mode;
415 
do_edge(filterwindow_dd * dt,void ** wdata)416 static int do_edge(filterwindow_dd *dt, void **wdata)
417 {
418 	static const unsigned char fxmap[] = { FX_EDGE, FX_SOBEL, FX_PREWITT,
419 		FX_KIRSCH, FX_GRADIENT, FX_ROBERTS, FX_LAPLACE, FX_MORPHEDGE };
420 
421 	run_query(wdata);
422 	spot_undo(UNDO_FILT);
423 	do_effect(fxmap[edge_mode], 0);
424 	mem_undo_prepare();
425 
426 	return TRUE;
427 }
428 
429 static char *fnames_txt[] = { _("MT"), _("Sobel"), _("Prewitt"), _("Kirsch"),
430 	_("Gradient"), _("Roberts"), _("Laplace"), _("Morphological"), NULL };
431 
432 #define WBbase filterwindow_dd
433 static void *edge_code[] = {
434 	BORDER(RPACK, 0),
435 	RPACKv(fnames_txt, 0, 4, edge_mode), RET
436 };
437 #undef WBbase
438 
pressed_edge_detect()439 void pressed_edge_detect()
440 {
441 	static filterwindow_dd tdata = {
442 		_("Edge Detect"), edge_code, FW_FN(do_edge) };
443 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
444 }
445 
446 typedef struct {
447 	spin1_dd s1;
448 	int fx;
449 } spin1f_dd;
450 
do_fx(spin1f_dd * dt,void ** wdata)451 static int do_fx(spin1f_dd *dt, void **wdata)
452 {
453 	run_query(wdata);
454 	spot_undo(UNDO_FILT);
455 	do_effect(dt->fx, dt->s1.n[0]);
456 	mem_undo_prepare();
457 
458 	return TRUE;
459 }
460 
pressed_sharpen()461 void pressed_sharpen()
462 {
463 	static spin1f_dd tdata = { {
464 		{ _("Edge Sharpen"), spin1_code, FW_FN(do_fx) },
465 		{ 50, 1, 100 } }, FX_SHARPEN };
466 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
467 }
468 
pressed_soften()469 void pressed_soften()
470 {
471 	static spin1f_dd tdata = { {
472 		{ _("Edge Soften"), spin1_code, FW_FN(do_fx) },
473 		{ 50, 1, 100 } }, FX_SOFTEN };
474 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
475 }
476 
pressed_fx(int what)477 void pressed_fx(int what)
478 {
479 	spot_undo(UNDO_FILT);
480 	do_effect(what, 0);
481 	mem_undo_prepare();
482 	update_stuff(UPD_IMG);
483 }
484 
485 typedef struct {
486 	filterwindow_dd fw;
487 	int rgb;
488 	int x, y, xy, gamma;
489 	void **yspin;
490 } gauss_dd;
491 
do_gauss(gauss_dd * dt,void ** wdata)492 static int do_gauss(gauss_dd *dt, void **wdata)
493 {
494 	int radiusX, radiusY, gcor = FALSE;
495 
496 	run_query(wdata);
497 	if (mem_channel == CHN_IMAGE) gcor = dt->gamma;
498 
499 	radiusX = radiusY = dt->x;
500 	if (dt->xy) radiusY = dt->y;
501 
502 	spot_undo(UNDO_DRAW);
503 	mem_gauss(radiusX * 0.01, radiusY * 0.01, gcor);
504 	mem_undo_prepare();
505 
506 	return TRUE;
507 }
508 
gauss_xy_click(gauss_dd * dt,void ** wdata,int what,void ** where)509 static void gauss_xy_click(gauss_dd *dt, void **wdata, int what, void **where)
510 {
511 	cmd_read(where, dt);
512 	cmd_sensitive(dt->yspin, dt->xy);
513 }
514 
515 #define WBbase gauss_dd
516 static void *gauss_code[] = {
517 	VBOXPS,
518 	BORDER(SPIN, 0),
519 	FSPIN(x, 0, 20000), ALTNAME("X"),
520 	REF(yspin), FSPIN(y, 0, 20000), INSENS, OPNAME("Y"),
521 	CHECK(_("Different X/Y"), xy), EVENT(CHANGE, gauss_xy_click),
522 	IF(rgb), CHECK(_("Gamma corrected"), gamma),
523 	WDONE, RET
524 };
525 #undef WBbase
526 
pressed_gauss()527 void pressed_gauss()
528 {
529 	gauss_dd tdata = {
530 		{ _("Gaussian Blur"), gauss_code, FW_FN(do_gauss) },
531 		mem_channel == CHN_IMAGE, 100, 100, FALSE, use_gamma };
532 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
533 }
534 
535 typedef struct {
536 	filterwindow_dd fw;
537 	int rgb;
538 	int radius, amount, threshold, gamma;
539 } unsharp_dd;
540 
do_unsharp(unsharp_dd * dt,void ** wdata)541 static int do_unsharp(unsharp_dd *dt, void **wdata)
542 {
543 	run_query(wdata);
544 	// !!! No RGBA mode for now, so UNDO_DRAW isn't needed
545 	spot_undo(UNDO_FILT);
546 	mem_unsharp(dt->radius * 0.01, dt->amount * 0.01, dt->threshold,
547 		(mem_channel == CHN_IMAGE) && dt->gamma);
548 	mem_undo_prepare();
549 
550 	return TRUE;
551 }
552 
553 #define WBbase unsharp_dd
554 static void *unsharp_code[] = {
555 	VBOXPS,
556 	BORDER(TABLE, 0),
557 	TABLE2(3), OPNAME0,
558 	TFSPIN(_("Radius"), radius, 0, 20000),
559 	TFSPIN(_("Amount"), amount, 0, 1000),
560 	TSPIN(_("Threshold "), threshold, 0, 255),
561 	WDONE,
562 	IF(rgb), CHECK(_("Gamma corrected"), gamma),
563 	WDONE, RET
564 };
565 #undef WBbase
566 
pressed_unsharp()567 void pressed_unsharp()
568 {
569 	unsharp_dd tdata = {
570 		{ _("Unsharp Mask"), unsharp_code, FW_FN(do_unsharp) },
571 		mem_channel == CHN_IMAGE, 500, 50, 0, use_gamma };
572 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
573 }
574 
575 typedef struct {
576 	filterwindow_dd fw;
577 	int rgb;
578 	int outer, inner, norm, gamma;
579 } dog_dd;
580 
do_dog(dog_dd * dt,void ** wdata)581 static int do_dog(dog_dd *dt, void **wdata)
582 {
583 	run_query(wdata);
584 	if (dt->outer <= dt->inner) return (FALSE); /* Invalid parameters */
585 
586 	spot_undo(UNDO_FILT);
587 	mem_dog(dt->outer * 0.01, dt->inner * 0.01, dt->norm,
588 		(mem_channel == CHN_IMAGE) && dt->gamma);
589 	mem_undo_prepare();
590 
591 	return TRUE;
592 }
593 
594 #define WBbase dog_dd
595 static void *dog_code[] = {
596 	VBOXPS,
597 	BORDER(TABLE, 0),
598 	TABLE2(2), OPNAME0,
599 	TFSPIN(_("Outer radius"), outer, 0, 20000),
600 	TFSPIN(_("Inner radius"), inner, 0, 20000),
601 	WDONE,
602 	CHECK(_("Normalize"), norm),
603 	IF(rgb), CHECK(_("Gamma corrected"), gamma),
604 	WDONE, RET
605 };
606 #undef WBbase
607 
pressed_dog()608 void pressed_dog()
609 {
610 	dog_dd tdata = {
611 		{ _("Difference of Gaussians"), dog_code, FW_FN(do_dog) },
612 		mem_channel == CHN_IMAGE, 300, 100, TRUE, use_gamma };
613 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
614 }
615 
616 typedef struct {
617 	filterwindow_dd fw;
618 	int r, detail, gamma;
619 } kuw_dd;
620 
do_kuwahara(kuw_dd * dt,void ** wdata)621 static int do_kuwahara(kuw_dd *dt, void **wdata)
622 {
623 	run_query(wdata);
624 	spot_undo(UNDO_COL); // Always processes RGB image channel
625 	mem_kuwahara(dt->r, dt->gamma, dt->detail);
626 	mem_undo_prepare();
627 
628 	return (TRUE);
629 }
630 
631 #define WBbase kuw_dd
632 static void *kuw_code[] = {
633 	VBOXPS,
634 	BORDER(SPIN, 0),
635 	SPIN(r, 1, 127),
636 	CHECK(_("Protect details"), detail),
637 	CHECK(_("Gamma corrected"), gamma),
638 	WDONE, RET
639 };
640 #undef WBbase
641 
pressed_kuwahara()642 void pressed_kuwahara()
643 {
644 	kuw_dd tdata = {
645 		{ _("Kuwahara-Nagao Blur"), kuw_code, FW_FN(do_kuwahara) },
646 		1, FALSE, use_gamma };
647 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
648 }
649 
pressed_convert_rgb()650 void pressed_convert_rgb()
651 {
652 	unsigned char *old_img = mem_img[CHN_IMAGE];
653 	int res = undo_next_core(UC_NOCOPY, mem_width, mem_height, 3, CMASK_IMAGE);
654 	if (res) memory_errors(res);
655 	else
656 	{
657 		do_convert_rgb(0, 1, mem_width * mem_height, mem_img[CHN_IMAGE],
658 			old_img, mem_pal);
659 		update_stuff(UPD_2RGB);
660 	}
661 }
662 
pressed_greyscale(int mode)663 void pressed_greyscale(int mode)
664 {
665 	spot_undo(UNDO_COL);
666 
667 	mem_greyscale(mode);
668 	mem_undo_prepare();
669 
670 	update_stuff(UPD_COL);
671 }
672 
pressed_rotate_image(int dir)673 void pressed_rotate_image(int dir)
674 {
675 	int i = mem_image_rot(dir);
676 	if (i) memory_errors(i);
677 	else update_stuff(UPD_GEOM);
678 }
679 
pressed_rotate_sel(int dir)680 void pressed_rotate_sel(int dir)
681 {
682 	if (mem_sel_rot(dir)) memory_errors(1);
683 	else update_stuff(UPD_CGEOM);
684 }
685 
686 static int angle = 4500;
687 
688 typedef struct {
689 	filterwindow_dd fw;
690 	int rgb;
691 	int smooth, gamma;
692 } rfree_dd;
693 
do_rotate_free(rfree_dd * dt,void ** wdata)694 static int do_rotate_free(rfree_dd *dt, void **wdata)
695 {
696 	int j, smooth = 0, gcor = 0;
697 
698 	run_query(wdata);
699 	if (mem_img_bpp == 3)
700 	{
701 		gcor = dt->gamma;
702 		smooth = dt->smooth;
703 	}
704 	j = mem_rotate_free(angle * 0.01, smooth, gcor, FALSE);
705 	if (!j) update_stuff(UPD_GEOM);
706 	else
707 	{
708 		if (j == -5) alert_box(_("Error"),
709 			_("The image is too large for this rotation."), NULL);
710 		else memory_errors(j);
711 	}
712 
713 	return TRUE;
714 }
715 
716 #define WBbase rfree_dd
717 static void *rfree_code[] = {
718 	VBOXPS,
719 	BORDER(SPIN, 0),
720 	FSPINv(angle, -36000, 36000),
721 	IFx(rgb, 1),
722 		CHECK(_("Gamma corrected"), gamma),
723 		CHECK(_("Smooth"), smooth),
724 	ENDIF(1),
725 	WDONE, RET
726 };
727 #undef WBbase
728 
pressed_rotate_free()729 void pressed_rotate_free()
730 {
731 	rfree_dd tdata = {
732 		{ _("Free Rotate"), rfree_code, FW_FN(do_rotate_free) },
733 		mem_img_bpp == 3, TRUE, use_gamma };
734 	run_create_(filterwindow_code, &tdata, sizeof(tdata), script_cmds);
735 }
736 
737 
pressed_clip_mask(int val)738 void pressed_clip_mask(int val)
739 {
740 	int i;
741 
742 	if ( mem_clip_mask == NULL )
743 	{
744 		i = mem_clip_mask_init(val ^ 255);
745 		if (i)
746 		{
747 			memory_errors(1);	// Not enough memory
748 			return;
749 		}
750 	}
751 	mem_clip_mask_set(val);
752 	update_stuff(UPD_CLIP);
753 }
754 
do_clip_alphamask()755 static int do_clip_alphamask()
756 {
757 	unsigned char *old_mask = mem_clip_mask;
758 	int i, j = mem_clip_w * mem_clip_h, k;
759 
760 	if (!mem_clipboard || !mem_clip_alpha) return FALSE;
761 
762 	mem_clip_mask = mem_clip_alpha;
763 	mem_clip_alpha = NULL;
764 
765 	if (old_mask)
766 	{
767 		for (i = 0; i < j; i++)
768 		{
769 			k = old_mask[i] * mem_clip_mask[i];
770 			mem_clip_mask[i] = (k + (k >> 8) + 1) >> 8;
771 		}
772 		free(old_mask);
773 	}
774 
775 	return TRUE;
776 }
777 
pressed_clip_alphamask()778 void pressed_clip_alphamask()
779 {
780 	if (do_clip_alphamask()) update_stuff(UPD_CLIP);
781 }
782 
pressed_clip_alpha_scale()783 void pressed_clip_alpha_scale()
784 {
785 	if (!mem_clipboard || (mem_clip_bpp != 3)) return;
786 	if (!mem_clip_mask) mem_clip_mask_init(255);
787 	if (!mem_clip_mask) return;
788 
789 	mem_scale_alpha(mem_clipboard, mem_clip_mask, mem_clip_w, mem_clip_h, TRUE);
790 
791 	update_stuff(UPD_CLIP);
792 }
793 
pressed_clip_mask_all()794 void pressed_clip_mask_all()
795 {
796 	if (mem_clip_mask_init(0))
797 		memory_errors(1);	// Not enough memory
798 	else update_stuff(UPD_CLIP);
799 }
800 
pressed_clip_mask_clear()801 void pressed_clip_mask_clear()
802 {
803 	if (!mem_clip_mask) return;
804 	mem_clip_mask_clear();
805 	update_stuff(UPD_CLIP);
806 }
807 
pressed_flip_image_v()808 void pressed_flip_image_v()
809 {
810 	int i;
811 	unsigned char *temp;
812 
813 	temp = malloc(mem_width * mem_img_bpp);
814 	if (!temp) return; /* Not enough memory for temp buffer */
815 	spot_undo(UNDO_XFORM);
816 	for (i = 0; i < NUM_CHANNELS; i++)
817 	{
818 		if (!mem_img[i]) continue;
819 		mem_flip_v(mem_img[i], temp, mem_width, mem_height, BPP(i));
820 	}
821 	free(temp);
822 	mem_undo_prepare();
823 	update_stuff(UPD_IMG);
824 }
825 
pressed_flip_image_h()826 void pressed_flip_image_h()
827 {
828 	int i;
829 
830 	spot_undo(UNDO_XFORM);
831 	for (i = 0; i < NUM_CHANNELS; i++)
832 	{
833 		if (!mem_img[i]) continue;
834 		mem_flip_h(mem_img[i], mem_width, mem_height, BPP(i));
835 	}
836 	mem_undo_prepare();
837 	update_stuff(UPD_IMG);
838 }
839 
pressed_flip_sel_v()840 void pressed_flip_sel_v()
841 {
842 	unsigned char *temp;
843 	int i, bpp = mem_clip_bpp;
844 
845 	temp = malloc(mem_clip_w * mem_clip_bpp);
846 	if (!temp) return; /* Not enough memory for temp buffer */
847 	for (i = 0; i < NUM_CHANNELS; i++ , bpp = 1)
848 	{
849 		if (!mem_clip.img[i]) continue;
850 		mem_flip_v(mem_clip.img[i], temp, mem_clip_w, mem_clip_h, bpp);
851 	}
852 	update_stuff(UPD_CLIP);
853 }
854 
pressed_flip_sel_h()855 void pressed_flip_sel_h()
856 {
857 	int i, bpp = mem_clip_bpp;
858 	for (i = 0; i < NUM_CHANNELS; i++ , bpp = 1)
859 	{
860 		if (!mem_clip.img[i]) continue;
861 		mem_flip_h(mem_clip.img[i], mem_clip_w, mem_clip_h, bpp);
862 	}
863 	update_stuff(UPD_CLIP);
864 }
865 
866 static void locate_marquee(int *xy, int snap);
867 
868 #define MIN_VISIBLE 16 /* No less than a square this large must be visible */
869 
pressed_paste(int centre)870 void pressed_paste(int centre)
871 {
872 	if (!mem_clipboard) return;
873 
874 	pressed_select(FALSE);
875 	change_to_tool(TTB_SELECT);
876 
877 	marq_status = MARQUEE_PASTE;
878 	cursor_corner = -1;
879 	marq_x1 = mem_clip_x;
880 	marq_y1 = mem_clip_y;
881 	while (centre)
882 	{
883 		int marq0[4], mxy[4], vxy[4];
884 
885 		canvas_center(mem_ic);
886 		marq_x1 = mem_width * mem_icx - mem_clip_w * 0.5;
887 		marq_y1 = mem_height * mem_icy - mem_clip_h * 0.5;
888 		if (!tgrid_snap) break;
889 		/* Snap to grid */
890 		copy4(marq0, marq_xy);
891 		locate_marquee(mxy, TRUE);
892 		if (script_cmds) break; // Scripting must behave consistently
893 		/* Undo snap if not enough of paste area is left visible */
894 		// !!! Could use CSCROLL_XYSIZE here instead
895 		cmd_peekv(drawing_canvas, vxy, sizeof(vxy), CANVAS_VPORT);
896 		if (!clip(vxy, vxy[0] - margin_main_x, vxy[1] - margin_main_y,
897 			vxy[2] - margin_main_x, vxy[3] - margin_main_y, mxy) ||
898 			((vxy[2] - vxy[0] < MIN_VISIBLE) &&
899 			 (vxy[2] - vxy[0] < mxy[2] - mxy[0])) ||
900 			((vxy[3] - vxy[1] < MIN_VISIBLE) &&
901 			 (vxy[3] - vxy[1] < mxy[3] - mxy[1])))
902 			copy4(marq_xy, marq0);
903 		break;
904 	}
905 	// !!! marq_x2, marq_y2 will be set by update_stuff()
906 	update_stuff(UPD_PASTE);
907 }
908 
909 #undef MIN_VISIBLE
910 
pressed_rectangle(int filled)911 void pressed_rectangle(int filled)
912 {
913 	int sb;
914 
915 	spot_undo(UNDO_DRAW);
916 
917 	/* Shapeburst mode */
918 	sb = STROKE_GRADIENT;
919 
920 	if ( tool_type == TOOL_POLYGON )
921 	{
922 		if (sb)
923 		{
924 			int l2, l3, ixy[4] = { 0, 0, mem_width, mem_height };
925 
926 			l2 = l3 = filled ? 1 : tool_size;
927 			l2 >>= 1; l3 -= l2;
928 			clip(sb_rect, poly_min_x - l2, poly_min_y - l2,
929 				poly_max_x + l3, poly_max_y + l3, ixy);
930 			sb_rect[2] -= sb_rect[0];
931 			sb_rect[3] -= sb_rect[1];
932 			sb = init_sb();
933 		}
934 		if (!filled) poly_outline();
935 		else poly_paint();
936 	}
937 	else
938 	{
939 		int l2 = 2 * tool_size, rect[4];
940 
941 		marquee_at(rect);
942 		if (sb)
943 		{
944 			copy4(sb_rect, rect);
945 			sb = init_sb();
946 		}
947 
948 		if (filled || (l2 >= rect[2]) || (l2 >= rect[3]))
949 			f_rectangle(rect[0], rect[1], rect[2], rect[3]);
950 		else
951 		{
952 			f_rectangle(rect[0], rect[1],
953 				rect[2], tool_size);
954 			f_rectangle(rect[0], rect[1] + rect[3] - tool_size,
955 				rect[2], tool_size);
956 			f_rectangle(rect[0], rect[1] + tool_size,
957 				tool_size, rect[3] - l2);
958 			f_rectangle(rect[0] + rect[2] - tool_size, rect[1] + tool_size,
959 				tool_size, rect[3] - l2);
960 		}
961 	}
962 
963 	if (sb) render_sb(NULL);
964 
965 	mem_undo_prepare();
966 	update_stuff(UPD_IMG);
967 }
968 
pressed_ellipse(int filled)969 void pressed_ellipse(int filled)
970 {
971 	spot_undo(UNDO_DRAW);
972 	mem_ellipse(marq_x1, marq_y1, marq_x2, marq_y2, filled ? 0 : tool_size);
973 	mem_undo_prepare();
974 	update_stuff(UPD_IMG);
975 }
976 
copy_clip()977 static int copy_clip()
978 {
979 	int rect[4], bpp = MEM_BPP, cmask = CMASK_IMAGE;
980 
981 
982 	if ((mem_channel == CHN_IMAGE) && mem_img[CHN_ALPHA] &&
983 		 !channel_dis[CHN_ALPHA]) cmask = CMASK_RGBA;
984 	marquee_at(rect);
985 	mem_clip_new(rect[2], rect[3], bpp, cmask, NULL);
986 
987 	if (!mem_clipboard)
988 	{
989 		alert_box(_("Error"), _("Not enough memory to create clipboard"), NULL);
990 		return (FALSE);
991 	}
992 
993 	mem_clip_x = rect[0];
994 	mem_clip_y = rect[1];
995 
996 	copy_area(&mem_clip, &mem_image, mem_clip_x, mem_clip_y);
997 
998 	return (TRUE);
999 }
1000 
channel_mask()1001 static int channel_mask()
1002 {
1003 	int i, j, ofs, delta;
1004 
1005 	if (!mem_img[CHN_SEL] || channel_dis[CHN_SEL]) return (FALSE);
1006 	if (mem_channel > CHN_ALPHA) return (FALSE);
1007 
1008 	if (!mem_clip_mask) mem_clip_mask_init(255);
1009 	if (!mem_clip_mask) return (FALSE);
1010 
1011 	ofs = mem_clip_y * mem_width + mem_clip_x;
1012 	delta = 0;
1013 	for (i = 0; i < mem_clip_h; i++)
1014 	{
1015 		for (j = 0; j < mem_clip_w; j++)
1016 			mem_clip_mask[delta + j] &= mem_img[CHN_SEL][ofs + j];
1017 		ofs += mem_width;
1018 		delta += mem_clip_w;
1019 	}
1020 	return (TRUE);
1021 }
1022 
cut_clip()1023 static void cut_clip()
1024 {
1025 	int i, sb = 0;
1026 
1027 	spot_undo(UNDO_DRAW);
1028 	/* Shapeburst mode */
1029 	if (STROKE_GRADIENT)
1030 	{
1031 		sb_rect[0] = mem_clip_x;
1032 		sb_rect[1] = mem_clip_y;
1033 		sb_rect[2] = mem_clip_w;
1034 		sb_rect[3] = mem_clip_h;
1035 		sb = init_sb();
1036 	}
1037 	for (i = 0; i < mem_clip_h; i++)
1038 	{
1039 		put_pixel_row(mem_clip_x, mem_clip_y + i, mem_clip_w,
1040 			mem_clip_mask ? mem_clip_mask + i * mem_clip_w : NULL);
1041 	}
1042 	if (sb) render_sb(mem_clip_mask);
1043 	mem_undo_prepare();
1044 }
1045 
trim_clip()1046 static void trim_clip()
1047 {
1048 	chanlist old_img;
1049 	unsigned char *tmp;
1050 	int i, j, k, offs, offd, maxx, maxy, minx, miny, nw, nh;
1051 
1052 	minx = MAX_WIDTH; miny = MAX_HEIGHT; maxx = maxy = 0;
1053 
1054 	/* Find max & min values for shrink wrapping */
1055 	for (j = 0; j < mem_clip_h; j++)
1056 	{
1057 		offs = mem_clip_w * j;
1058 		for (i = 0; i < mem_clip_w; i++)
1059 		{
1060 			if (!mem_clip_mask[offs + i]) continue;
1061 			if (i < minx) minx = i;
1062 			if (i > maxx) maxx = i;
1063 			if (j < miny) miny = j;
1064 			if (j > maxy) maxy = j;
1065 		}
1066 	}
1067 
1068 	/* No live pixels found */
1069 	if (minx > maxx) return;
1070 
1071 	nw = maxx - minx + 1;
1072 	nh = maxy - miny + 1;
1073 
1074 	/* No decrease so no resize either */
1075 	if ((nw == mem_clip_w) && (nh == mem_clip_h)) return;
1076 
1077 	/* Pack data to front */
1078 	for (j = miny; j <= maxy; j++)
1079 	{
1080 		offs = j * mem_clip_w + minx;
1081 		offd = (j - miny) * nw;
1082 		memmove(mem_clipboard + offd * mem_clip_bpp,
1083 			mem_clipboard + offs * mem_clip_bpp, nw * mem_clip_bpp);
1084 		for (k = 1; k < NUM_CHANNELS; k++)
1085 		{
1086 			if (!(tmp = mem_clip.img[k])) continue;
1087 			memmove(tmp + offd, tmp + offs, nw);
1088 		}
1089 	}
1090 
1091 	/* Try to realloc memory for smaller clipboard */
1092 	tmp = realloc(mem_clipboard, nw * nh * mem_clip_bpp);
1093 	if (tmp) mem_clipboard = tmp;
1094 	for (k = 1; k < NUM_CHANNELS; k++)
1095 	{
1096 		if (!(tmp = mem_clip.img[k])) continue;
1097 		tmp = realloc(tmp, nw * nh);
1098 		if (tmp) mem_clip.img[k] = tmp;
1099 	}
1100 
1101 	/* Reset clipboard to new size */
1102 	mem_clip_new(nw, nh, mem_clip_bpp, 0, old_img);
1103 	memcpy(mem_clip.img, old_img, sizeof(chanlist));
1104 	mem_clip_x += minx;
1105 	mem_clip_y += miny;
1106 
1107 	if (marq_status >= MARQUEE_PASTE) // We're trimming live paste area
1108 	{
1109 		marq_x2 = (marq_x1 += minx) + nw - 1;
1110 		marq_y2 = (marq_y1 += miny) + nh - 1;
1111 	}
1112 }
1113 
pressed_copy(int cut)1114 void pressed_copy(int cut)
1115 {
1116 	if (!copy_clip()) return;
1117 	if (tool_type == TOOL_POLYGON) poly_mask();
1118 	channel_mask();
1119 	if (cut) cut_clip();
1120 	update_stuff(cut ? UPD_CUT : UPD_COPY);
1121 }
1122 
pressed_lasso(int cut)1123 void pressed_lasso(int cut)
1124 {
1125 	/* Lasso a new selection */
1126 	if (((marq_status > MARQUEE_NONE) && (marq_status < MARQUEE_PASTE)) ||
1127 		(poly_status == POLY_DONE))
1128 	{
1129 		if (!copy_clip()) return;
1130 		if (tool_type == TOOL_POLYGON) poly_mask();
1131 		else mem_clip_mask_init(255);
1132 		if (!lasso_sel || !channel_mask())
1133 		{
1134 			poly_lasso(poly_status == POLY_DONE);
1135 			channel_mask();
1136 		}
1137 		trim_clip();
1138 		if (cut) cut_clip();
1139 		pressed_paste(TRUE);
1140 	}
1141 	/* Trim an existing clipboard */
1142 	else
1143 	{
1144 		if (!lasso_sel || !mem_clip_mask)
1145 		{
1146 			unsigned char *oldmask = mem_clip_mask;
1147 
1148 			mem_clip_mask = NULL;
1149 			mem_clip_mask_init(255);
1150 			poly_lasso(FALSE);
1151 			if (mem_clip_mask && oldmask)
1152 			{
1153 				int i, j = mem_clip_w * mem_clip_h;
1154 				for (i = 0; i < j; i++)
1155 					oldmask[i] &= mem_clip_mask[i];
1156 				mem_clip_mask_clear();
1157 			}
1158 			if (!mem_clip_mask) mem_clip_mask = oldmask;
1159 		}
1160 		trim_clip();
1161 		update_stuff(UPD_CGEOM);
1162 	}
1163 }
1164 
update_menus()1165 void update_menus()			// Update edit/undo menu
1166 {
1167 	static int memwarn;
1168 	unsigned int statemap;
1169 	int i, j;
1170 
1171 	if (mem_undo_fail && !memwarn) memwarn = alert_box(_("Warning"),
1172 		_("You have not allocated enough memory in the Preferences window to undo this action."),
1173 		"", NULL); // Not an error
1174 	update_undo_bar();
1175 
1176 	statemap = mem_img_bpp == 3 ? NEED_24 | NEED_NOIDX : NEED_IDX;
1177 	if (mem_channel != CHN_IMAGE) statemap |= NEED_NOIDX;
1178 	if ((mem_img_bpp == 3) && mem_img[CHN_ALPHA]) statemap |= NEED_RGBA;
1179 
1180 	if (mem_clipboard) statemap |= NEED_PCLIP;
1181 	if (mem_clipboard && (mem_clip_bpp == 3)) statemap |= NEED_ACLIP;
1182 
1183 	if ( marq_status == MARQUEE_NONE )
1184 	{
1185 //		statemap &= ~(NEED_SEL | NEED_CROP);
1186 		if (poly_status == POLY_DONE) statemap |= NEED_MARQ | NEED_LASSO;
1187 	}
1188 	else
1189 	{
1190 		statemap |= NEED_MARQ;
1191 
1192 		/* If we are pasting disallow copy/cut/crop */
1193 		if (marq_status < MARQUEE_PASTE)
1194 			statemap |= NEED_SEL | NEED_CROP | NEED_LASSO;
1195 
1196 		/* Only offer the crop option if the user hasn't selected everything */
1197 		if (!((abs(marq_x1 - marq_x2) < mem_width - 1) ||
1198 			(abs(marq_y1 - marq_y2) < mem_height - 1)))
1199 			statemap &= ~NEED_CROP;
1200 	}
1201 
1202 	/* Forbid RGB-to-indexed paste, but allow indexed-to-RGB */
1203 	if (mem_clipboard && (mem_clip_bpp <= MEM_BPP)) statemap |= NEED_CLIP;
1204 
1205 	if (mem_undo_done) statemap |= NEED_UNDO;
1206 	if (mem_undo_redo) statemap |= NEED_REDO;
1207 
1208 	cmd_set(menu_slots[MENU_CHAN0 + mem_channel], TRUE);
1209 	cmd_set(menu_slots[MENU_DCHAN0], hide_image);
1210 	cmd_set(menu_slots[MENU_OALPHA], overlay_alpha);
1211 
1212 	for (i = j = 0; i < NUM_CHANNELS; i++)	// Enable/disable channel enable/disable
1213 	{
1214 		if (mem_img[i]) j++;
1215 		cmd_sensitive(menu_slots[MENU_DCHAN0 + i], !!mem_img[i]);
1216 	}
1217 	if (j > 1) statemap |= NEED_CHAN;
1218 
1219 	cmd_setv(main_window_, (void *)statemap, WDATA_ACTMAP);
1220 
1221 	/* Switch to default tool if active smudge tool got disabled */
1222 	if ((tool_type == TOOL_SMUDGE) &&
1223 		!cmd_checkv(icon_buttons[SMUDGE_TOOL_ICON], SLOT_SENSITIVE))
1224 		change_to_tool(DEFAULT_TOOL_ICON);
1225 }
1226 
update_stuff(int flags)1227 void update_stuff(int flags)
1228 {
1229 	/* Always check current channel first */
1230 	if (!mem_img[mem_channel])
1231 	{
1232 		mem_channel = CHN_IMAGE;
1233 		flags |= UPD_CHAN;
1234 	}
1235 
1236 	/* And check paste validity too */
1237 	if ((marq_status >= MARQUEE_PASTE) &&
1238 		(!mem_clipboard || (mem_clip_bpp > MEM_BPP)))
1239 		pressed_select(FALSE);
1240 
1241 	if (flags & CF_CAB)
1242 		flags |= mem_channel == CHN_IMAGE ? UPD_AB : UPD_GRAD;
1243 	if (flags & CF_GEOM)
1244 	{
1245 		int wh[2];
1246 		canvas_size(wh + 0, wh + 1);
1247 		cmd_setv(drawing_canvas, wh, CANVAS_SIZE);
1248 	}
1249 	if (flags & CF_CGEOM)
1250 		if (marq_status >= MARQUEE_PASTE) flags |= CF_DRAW;
1251 	if (flags & (CF_GEOM | CF_CGEOM))
1252 		check_marquee();
1253 	if (flags & CF_PAL)
1254 	{
1255 		int wh[2] = { PALETTE_WIDTH,
1256 			mem_cols * PALETTE_SWATCH_H + PALETTE_SWATCH_Y * 2 };
1257 
1258 		if (mem_col_A >= mem_cols) mem_col_A = 0;
1259 		if (mem_col_B >= mem_cols) mem_col_B = 0;
1260 		mem_mask_init();	// Reinit RGB masks
1261 		cmd_setv(drawing_palette, wh, CANVAS_SIZE);
1262 	}
1263 	if (flags & CF_AB)
1264 	{
1265 		mem_pat_update();
1266 		if (text_paste && (marq_status >= MARQUEE_PASTE))
1267 		{
1268 			if (text_paste == TEXT_PASTE_FT) ft_render_text();
1269 			else /* if ( text_paste == TEXT_PASTE_GTK ) */
1270 				render_text();
1271 			check_marquee();
1272 			flags |= CF_PMODE;
1273 		}
1274 	}
1275 	if (flags & CF_GRAD)
1276 		grad_def_update(-1);
1277 	if (flags & CF_PREFS)
1278 	{
1279 		update_undo_depth();	// If undo depth was changed
1280 		update_recent_files(FALSE);
1281 		init_status_bar();	// Takes care of all statusbar parts
1282 	}
1283 	if (flags & CF_MENU)
1284 		update_menus();
1285 	if (flags & CF_SET)
1286 		toolbar_update_settings();
1287 #if 0
1288 // !!! Too risky for now - need a safe path which only calls update_xy_bar()
1289 	if (flags & CF_PIXEL)
1290 		move_mouse(0, 0, 0);	// To cause update of XY bar
1291 #endif
1292 	if (flags & CF_PMODE)
1293 		if ((marq_status >= MARQUEE_PASTE) && show_paste) flags |= CF_DRAW;
1294 	if (flags & CF_GMODE)
1295 		if ((tool_type == TOOL_GRADIENT) && grad_opacity) flags |= CF_DRAW;
1296 
1297 	/* The next parts can be done later in a cumulative update */
1298 	flags |= update_later;
1299 	if (script_cmds && !(flags & CF_NOW)) flags ^= update_later = flags &
1300 		(CF_NAME | CF_IMGBAR | CF_SELBAR | CF_TRANS | CF_CURSOR |
1301 		 CF_DRAW | CF_VDRAW | CF_PDRAW | CF_TDRAW |
1302 		 CF_ALIGN | CF_VALIGN);
1303 	update_later &= ~flags;
1304 
1305 	if (flags & CF_NAME)
1306 		update_titlebar();
1307 	if (flags & CF_IMGBAR)
1308 		update_image_bar();
1309 	if (flags & CF_SELBAR)
1310 		update_sel_bar(TRUE);
1311 	if (flags & CF_CURSOR)
1312 		set_cursor(NULL);
1313 	if (flags & CF_DRAW)
1314 		if (drawing_canvas) cmd_repaint(drawing_canvas);
1315 	if (flags & CF_VDRAW)
1316 		if (view_showing && vw_drawing) cmd_repaint(vw_drawing);
1317 	if (flags & CF_PDRAW)
1318 	{
1319 		mem_pal_init();		// Update palette RGB on screen
1320 		cmd_repaint(drawing_palette);
1321 	}
1322 	if (flags & CF_TDRAW)
1323 	{
1324 		update_top_swatch();
1325 		cmd_repaint(drawing_col_prev);
1326 	}
1327 	if (flags & CF_ALIGN)
1328 		realign_size();
1329 	if (flags & CF_VALIGN)
1330 		vw_realign();
1331 	if (flags & CF_TRANS)
1332 		layer_show_trans();
1333 }
1334 
pressed_do_undo(int redo,int cnt)1335 void pressed_do_undo(int redo, int cnt)
1336 {
1337 	while (cnt--) mem_do_undo(redo);
1338 	update_stuff(UPD_ALL | CF_NAME);
1339 }
1340 
load_pal(char * filename)1341 static int load_pal(char *filename)		// Load palette file
1342 {
1343 	int ftype, res = -1;
1344 
1345 	ftype = detect_palette_format(filename);
1346 	if (ftype < 0) return (-1); /* Silently fail if no file */
1347 
1348 	if (ftype != FT_NONE) res = load_image(filename, FS_PALETTE_LOAD, ftype);
1349 
1350 	/* Successful... */
1351 	if (res == 1) update_stuff(UPD_UPAL);
1352 	/* ...or not */
1353 	else alert_box(_("Error"), _("Invalid palette file"), NULL);
1354 
1355 	return (res == 1 ? 0 : -1);
1356 }
1357 
1358 
set_new_filename(int layer,char * fname)1359 void set_new_filename(int layer, char *fname)
1360 {
1361 	mem_replace_filename(layer, fname);
1362 	if (layer == layer_selected) update_stuff(UPD_NAME);
1363 }
1364 
populate_channel(char * filename)1365 static int populate_channel(char *filename)
1366 {
1367 	int ftype, res = -1;
1368 
1369 	ftype = detect_image_format(filename);
1370 	if (ftype < 0) return (-1); /* Silently fail if no file */
1371 
1372 	/* Don't bother with mismatched formats */
1373 	if (file_formats[ftype].flags & (MEM_BPP == 1 ? FF_IDX : FF_RGB))
1374 		res = load_image(filename, FS_CHANNEL_LOAD, ftype);
1375 
1376 	/* Successful */
1377 	if (res == 1) update_stuff(UPD_UIMG);
1378 
1379 	/* Not enough memory available */
1380 	else if (res == FILE_MEM_ERROR) memory_errors(1);
1381 
1382 	/* Unspecified error */
1383 	else alert_box(_("Error"), _("Invalid channel file."), NULL);
1384 
1385 	return (res == 1 ? 0 : -1);
1386 }
1387 
1388 
1389 
1390 ///	FILE SELECTION WINDOW
1391 
1392 static int anim_mode = ANM_COMP - ANM_RAW;
1393 
1394 typedef struct {
1395 	int is_anim;
1396 	char *what;
1397 	void **lload, **load1, **explode;
1398 	void **res;
1399 } animfile_dd;
1400 
1401 static char *modes_txt[] = { _("Raw frames"), _("Composited frames"),
1402 		_("Composited frames with nonzero delay") };
1403 
1404 #define WBbase animfile_dd
1405 static void *animfile_code[] = {
1406 	WINDOWm(_("Load Frames")),
1407 	MLABELcp(what),
1408 	EQBOXB, // !!! why not HBOXB ?
1409 	REF(lload), BUTTON(_("Load into Layers"), dialog_event), FOCUS,
1410 	WDONE,
1411 	BORDER(RPACK, 0),
1412 	IF(is_anim), RPACKv(modes_txt, 3, 0, anim_mode),
1413 	HSEP,
1414 	EQBOXB,
1415 	REF(load1), CANCELBTN(_("Load First Frame"), dialog_event),
1416 	REF(explode), BUTTON(_("Explode Frames"), dialog_event),
1417 #ifndef WIN32
1418 	IF(is_anim), BUTTON(_("View Animation"), dialog_event),
1419 #endif
1420 	WDONE,
1421 	RAISED, WDIALOG(res)
1422 };
1423 #undef WBbase
1424 
anim_file_dialog(int ftype,int is_anim)1425 static int anim_file_dialog(int ftype, int is_anim)
1426 {
1427 	animfile_dd tdata, *dt;
1428 	void **res, **where;
1429 	char *tmp;
1430 	int i;
1431 
1432 
1433 	tdata.is_anim = is_anim;
1434 	tmp = g_strdup_printf(is_anim ? __("This is an animated %s file.") :
1435 		__("This is a multipage %s file."),
1436 		file_formats[ftype & FTM_FTYPE].name);
1437 	tdata.what = tmp;
1438 	res = run_create(animfile_code, &tdata, sizeof(tdata)); // run dialog
1439 	g_free(tmp);
1440 
1441 	/* Retrieve results */
1442 	run_query(res);
1443 	dt = GET_DDATA(res);
1444 	where = origin_slot(dt->res);
1445 	i = where == dt->lload ? 3 : where == dt->load1 ? 0 :
1446 		where == dt->explode ? 1 : 2;
1447 	run_destroy(res);
1448 
1449 	return (i);
1450 }
1451 
handle_file_error(int res)1452 static void handle_file_error(int res)
1453 {
1454 	char mess[256], *txt = NULL;
1455 
1456 	/* Image was too large for OS */
1457 	if (res == FILE_MEM_ERROR) memory_errors(1);
1458 	else if (res == TOO_BIG)
1459 		snprintf(txt = mess, 250, __("File is too big, must be <= to width=%i height=%i"), MAX_WIDTH, MAX_HEIGHT);
1460 	else if (res == EXPLODE_FAILED)
1461 		txt = _("Unable to explode frames");
1462 	else if (res <= 0)
1463 		txt = _("Unable to load file");
1464 	else if (res == FILE_LIB_ERROR)
1465 		txt = _("The file import library had to terminate due to a problem with the file (possibly corrupt image data or a truncated file). I have managed to load some data as the header seemed fine, but I would suggest you save this image to a new file to ensure this does not happen again.");
1466 	else if (res == FILE_TOO_LONG)
1467 		txt = _("The animation is too long to load all of it into layers.");
1468 	else if (res == FILE_EXP_BREAK)
1469 		txt = _("Could not explode all the frames in the animation.");
1470 	if (txt) alert_box(_("Error"), txt, NULL);
1471 }
1472 
1473 typedef struct {
1474 	char *what;
1475 	void **ok, **res;
1476 	int w, h;
1477 } scale_dd;
1478 
1479 #define WBbase scale_dd
1480 static void *scale_code[] = {
1481 	WINDOWm(_("Size")),
1482 	MLABELpx(what, 5, 5, 4),
1483 	TABLE2(2),
1484 	TSPIN(_("Width"), w, 0, MAX_WIDTH),
1485 	TSPIN(_("Height"), h, 0, MAX_HEIGHT),
1486 	WDONE,
1487 	HSEP,
1488 	EQBOXB,
1489 	CANCELBTN(_("Cancel"), dialog_event),
1490 	REF(ok), OKBTN(_("OK"), dialog_event),
1491 	WDONE,
1492 	RAISED, WDIALOG(res)
1493 };
1494 #undef WBbase
1495 
scale_file_dialog(int ftype,int * w,int * h)1496 static void scale_file_dialog(int ftype, int *w, int *h)
1497 {
1498 	scale_dd tdata, *dt;
1499 	void **res;
1500 	char *tmp;
1501 
1502 
1503 	memset(&tdata, 0, sizeof(tdata));
1504 	tdata.what = tmp = g_strdup_printf(__("This is a scalable %s file."),
1505 		file_formats[ftype & FTM_FTYPE].name);
1506 	res = run_create(scale_code, &tdata, sizeof(tdata)); // run dialog
1507 	g_free(tmp);
1508 
1509 	/* Retrieve results */
1510 	run_query(res);
1511 	dt = GET_DDATA(res);
1512 	if (origin_slot(dt->res) == dt->ok) *w = dt->w , *h = dt->h;
1513 	run_destroy(res);
1514 }
1515 
1516 typedef struct {
1517 	void **pathbox;
1518 	char *title, **ftnames, **tiffnames;
1519 	char *frames_name, *ex_path;
1520 	int frames_ftype, frames_anim, load_lr;
1521 	int mode, fpmode;
1522 	int fmask, ftype, ftypes[NUM_FTYPES];
1523 	int need_save, need_anim, need_undo, need_icc, script;
1524 	int jpeg_c, png_c, tga_c, jp2_c, xtrans[3], xx[3], xy[3];
1525 	int tiff_m, lzma_c, zstd_c;
1526 	int webp_p, webp_q, webp_c;
1527 	int lbm_c, lbm_p;
1528 	int gif_delay, undo, icc;
1529 	int w, h;
1530 	/* Note: filename is in system encoding */
1531 	char filename[PATHBUF];
1532 } fselector_dd;
1533 
do_a_load_x(char * fname,int undo,void * v)1534 int do_a_load_x(char *fname, int undo, void *v)
1535 {
1536 	char real_fname[PATHBUF];
1537 	int res, ftype, mult = 0, w = 0, h = 0, got_frame1 = FALSE;
1538 
1539 
1540 	resolve_path(real_fname, PATHBUF, fname);
1541 	ftype = detect_image_format(real_fname);
1542 
1543 	if ((ftype < 0) || (ftype == FT_NONE))
1544 	{
1545 		alert_box(_("Error"), ftype < 0 ? _("Cannot open file") :
1546 			_("Unsupported file format"), NULL);
1547 		return (1);
1548 	}
1549 
1550 	set_image(FALSE);
1551 
1552 	if (ftype == FT_LAYERS1) mult = res = load_layers(real_fname);
1553 	else
1554 	{
1555 		if (script_cmds && v)
1556 		{
1557 			fselector_dd *dt = v;
1558 			w = dt->w;
1559 			h = dt->h;
1560 		}
1561 		else if (!script_cmds && (file_formats[ftype].flags & FF_SCALE))
1562 			scale_file_dialog(ftype, &w, &h);
1563 		res = load_image_scale(real_fname, FS_PNG_LOAD,
1564 			ftype | (undo ? FTM_UNDO : 0), w, h);
1565 	}
1566 
1567 	/* Multiframe file was loaded so tell user */
1568 	if ((res == FILE_HAS_FRAMES) || (res == FILE_HAS_ANIM))
1569 	{
1570 		int i, is_anim = res == FILE_HAS_ANIM;
1571 
1572 		/* First frame was loaded OK */
1573 		got_frame1 = TRUE;
1574 		res = 1;
1575 
1576 		/* Don't ask user in viewer mode */
1577 // !!! When implemented, load as frameset & run animation in that case instead
1578 		if (viewer_mode && view_image_only) i = 0;
1579 		/* Don't ask in script mode too */
1580 		else if (script_cmds && v)
1581 		{
1582 			fselector_dd *dt = v;
1583 			i = dt->ex_path ? -1 : dt->load_lr ? 3 : 0;
1584 		}
1585 		else if (script_cmds) i = 0;
1586 		else i = anim_file_dialog(ftype, is_anim);
1587 		is_anim = is_anim ? anim_mode + ANM_RAW : ANM_PAGE;
1588 
1589 		if (i == 3)
1590 		{
1591 			/* Make current layer, with first frame in it, background */
1592 			if (layer_selected)
1593 			{
1594 				/* Simply swap layer data pointers */
1595 				layer_image *tip = layer_table[layer_selected].image;
1596 				layer_table[layer_selected].image = layer_table[0].image;
1597 				layer_table[0].image = tip;
1598 				layer_selected = 0;
1599 			}
1600 			mult = res = load_to_layers(real_fname, ftype, is_anim);
1601 		}
1602 		else if (i == -1) /* Explode frames into preset directory */
1603 		{
1604 			fselector_dd *dt = v;
1605 			i = dt->ftypes[dt->ftype];
1606 			res = explode_frames(dt->ex_path, is_anim, real_fname,
1607 				ftype, i == FT_NONE ? ftype : i);
1608 		}
1609 		else if (i == 1) /* Ask for directory to explode frames to */
1610 		{
1611 			void *xdata[3];
1612 
1613 			/* Needed when starting new mtpaint process later */
1614 			xdata[0] = real_fname;
1615 			xdata[1] = (void *)ftype;
1616 			xdata[2] = (void *)is_anim;
1617 			file_selector_x(FS_EXPLODE_FRAMES, xdata);
1618 		}
1619 		else if (i == 2) run_def_action(ftype == FT_WEBP ? DA_WEBP_PLAY :
1620 			ftype == FT_PNG ? DA_APNG_PLAY : DA_GIF_PLAY, real_fname, NULL, 0);
1621 	}
1622 
1623 	/* An error happened */
1624 	if (res != 1)
1625 	{
1626 		handle_file_error(res);
1627 		if ((res <= 0) && !got_frame1) // Hard error
1628 		{
1629 			set_image(TRUE);
1630 			return (1);
1631 		}
1632 	}
1633 
1634 	/* Whether we loaded something or failed to, old image is gone anyway */
1635 	if (!script_cmds) register_file(real_fname); // Ignore what scripts do
1636 	if (mult <= 0) /* A single image */
1637 	{
1638 		/* To prevent 1st frame overwriting a multiframe file */
1639 		char *nm = g_strconcat(real_fname, got_frame1 ? ".000" : NULL, NULL);
1640 		set_new_filename(layer_selected, nm);
1641 		g_free(nm);
1642 
1643 		if ( layers_total>0 )
1644 			layers_notify_changed(); // We loaded an image into the layers, so notify change
1645 	}
1646 	else /* A whole bunch of layers */
1647 	{
1648 //		pressed_layers();
1649 		// We have just loaded a layers file so ensure view window is open
1650 		view_show();
1651 	}
1652 
1653 	/* Show new image */
1654 	if (!undo) reset_tools();
1655 	else // No reason to reset tools in undoable mode
1656 	{
1657 		notify_unchanged(NULL);
1658 		update_stuff(UPD_ALL);
1659 	}
1660 
1661 	set_image(TRUE);
1662 	return (0);
1663 }
1664 
check_file(char * fname)1665 int check_file( char *fname )		// Does file already exist?  Ask if OK to overwrite
1666 {
1667 	char *msg, *f8;
1668 	int res = 0;
1669 
1670 	if ( valid_file(fname) == 0 )
1671 	{
1672 		f8 = gtkuncpy(NULL, fname, 0);
1673 		msg = g_strdup_printf(__("File: %s already exists. Do you want to overwrite it?"), f8);
1674 		res = alert_box(_("File Found"), msg, _("NO"), _("YES"), NULL) != 2;
1675 		g_free(msg);
1676 		g_free(f8);
1677 	}
1678 
1679 	return (res);
1680 }
1681 
change_image_format(fselector_dd * dt,void ** wdata,int what,void ** where)1682 static void change_image_format(fselector_dd *dt, void **wdata, int what,
1683 	void **where)
1684 {
1685 	int ftype;
1686 	unsigned int xflags;
1687 
1688 	if (!dt->need_save) return; // no use
1689 	cmd_read(where, dt);
1690 	ftype = dt->ftypes[dt->ftype];
1691 	xflags = ftype != FT_TIFF ? file_formats[ftype].xflags :
1692 		tiff_formats[dt->tiff_m].xflags;
1693 	if (ftype == FT_WEBP) xflags |= dt->webp_p ? XF_COMPV8 : XF_COMPV8L;
1694 	if ((ftype == FT_LBM) && (dt->fmask & FF_IDX)) xflags |= XF_PBM;
1695 
1696 	/* Hide/show name/value widget pairs */
1697 	cmd_setv(wdata, (void *)xflags, WDATA_ACTMAP);
1698 }
1699 
ftype_selector(int mask,char * ext,int def,char ** names,int * ftypes)1700 int ftype_selector(int mask, char *ext, int def, char **names, int *ftypes)
1701 {
1702 	fformat *ff;
1703 	int i, j, k, l, ft_sort[NUM_FTYPES], ft_key[NUM_FTYPES];
1704 
1705 	ft_key[0] = ft_sort[0] = 0;
1706 	k = def == FT_NONE;	// Include FT_NONE if default
1707 	for (i = 0; i < NUM_FTYPES; i++)	// Populate sorted filetype list
1708 	{
1709 		ff = file_formats + i;
1710 		if (ff->flags & FF_NOSAVE) continue;
1711 		if (!(ff->flags & mask)) continue;
1712 		l = (ff->name[0] << 16) + (ff->name[1] << 8) + ff->name[2];
1713 		for (j = k; j > 0; j--)
1714 		{
1715 			if (ft_key[j - 1] < l) break;
1716 			ft_sort[j] = ft_sort[j - 1];
1717 			ft_key[j] = ft_key[j - 1];
1718 		}
1719 		ft_key[j] = l;
1720 		ft_sort[j] = i;
1721 		k++;
1722 	}
1723 	j = -1;
1724 	for (l = 0; l < k; l++)		// Prepare to generate option menu
1725 	{
1726 		i = ft_sort[l];
1727 		if ((j < 0) && (i == def)) j = l;	// Default type if not saved yet
1728 		ff = file_formats + i;
1729 		if (!strncasecmp(ext, ff->ext, LONGEST_EXT) || (ff->ext2[0] &&
1730 			!strncasecmp(ext, ff->ext2, LONGEST_EXT))) j = l;
1731 		names[l] = ff->name;
1732 	}
1733 
1734 	names[k] = NULL;
1735 	memcpy(ftypes, ft_sort, sizeof(int) * k);
1736 	return (j);
1737 }
1738 
tiff_type_selector(int mask,int def,char ** names)1739 int tiff_type_selector(int mask, int def, char **names)
1740 {
1741 	int i, n;
1742 
1743 	for (n = i = 0; tiff_formats[i].name; i++)
1744 	{
1745 		names[i] = "";
1746 		if (tiff_formats[i].flags & mask)
1747 		{
1748 			names[i] = tiff_formats[i].name;
1749 			if (i == def) n = i;
1750 		}
1751 	}
1752 	names[i] = NULL;
1753 	return (n);
1754 }
1755 
init_ls_settings(ls_settings * settings,void ** wdata)1756 void init_ls_settings(ls_settings *settings, void **wdata)
1757 {
1758 	png_color *pal = mem_pal;
1759 
1760 	/* Set defaults */
1761 	memset(settings, 0, sizeof(ls_settings));
1762 	settings->ftype = FT_NONE;
1763 	settings->xpm_trans = mem_xpm_trans;
1764 	settings->hot_x = mem_xbm_hot_x;
1765 	settings->hot_y = mem_xbm_hot_y;
1766 	settings->req_w = settings->req_h = 0;
1767 	settings->jpeg_quality = jpeg_quality;
1768 	settings->png_compression = png_compression;
1769 	settings->lzma_preset = lzma_preset;
1770 	settings->zstd_level = zstd_level;
1771 	settings->tiff_type = -1; /* Use default */
1772 	settings->tga_RLE = tga_RLE;
1773 	settings->jp2_rate = jp2_rate;
1774 	settings->webp_preset = webp_preset;
1775 	settings->webp_quality = webp_quality;
1776 	settings->webp_compression = webp_compression;
1777 	settings->lbm_pack = lbm_pack;
1778 	settings->lbm_pbm = lbm_pbm;
1779 	settings->gif_delay = preserved_gif_delay;
1780 
1781 	/* Read in settings */
1782 	if (wdata)
1783 	{
1784 		fselector_dd *dt = GET_DDATA(wdata);
1785 		settings->xpm_trans = dt->xtrans[0];
1786 		settings->hot_x = dt->xx[0];
1787 		settings->hot_y = dt->xy[0];
1788 		settings->jpeg_quality = dt->jpeg_c;
1789 		settings->png_compression = dt->png_c;
1790 		settings->lzma_preset = dt->lzma_c;
1791 		settings->zstd_level = dt->zstd_c;
1792 		settings->tiff_type = dt->tiff_m;
1793 		settings->tga_RLE = dt->tga_c;
1794 		settings->jp2_rate = dt->jp2_c;
1795 		settings->webp_preset = dt->webp_p;
1796 		settings->webp_quality = dt->webp_q;
1797 		settings->webp_compression = dt->webp_c;
1798 		settings->lbm_pack = dt->lbm_c;
1799 		settings->lbm_pbm = dt->lbm_p;
1800 		settings->gif_delay = dt->gif_delay;
1801 
1802 		settings->mode = dt->mode;
1803 		settings->ftype = dt->fmask ? dt->ftypes[dt->ftype] :
1804 			/* For animation, hardcoded GIF format for now */
1805 			dt->mode == FS_EXPORT_GIF ? FT_GIF : FT_NONE;
1806 
1807 		undo_load = dt->undo;
1808 #ifdef U_LCMS
1809 		apply_icc = dt->icc;
1810 #endif
1811 		// Use background's palette
1812 		if ((dt->mode == FS_COMPOSITE_SAVE) && layer_selected)
1813 			pal = layer_table[0].image->image_.pal;
1814 	}
1815 
1816 	/* Default expansion of xpm_trans */
1817 	settings->rgb_trans = settings->xpm_trans < 0 ? -1 :
1818 		PNG_2_INT(pal[settings->xpm_trans]);
1819 }
1820 
store_ls_settings(ls_settings * settings)1821 static void store_ls_settings(ls_settings *settings)
1822 {
1823 	int ftype = settings->ftype, ttype = settings->tiff_type;
1824 	unsigned int xflags = (ftype == FT_TIFF) && (ttype >= 0) ?
1825 		tiff_formats[ttype].xflags : file_formats[ftype].xflags;
1826 
1827 	switch (settings->mode)
1828 	{
1829 	case FS_PNG_SAVE:
1830 		if (xflags & XF_TRANS)
1831 			mem_set_trans(settings->xpm_trans);
1832 		if (xflags & XF_SPOT)
1833 		{
1834 			mem_xbm_hot_x = settings->hot_x;
1835 			mem_xbm_hot_y = settings->hot_y;
1836 		}
1837 		// Fallthrough
1838 	case FS_CHANNEL_SAVE:
1839 	case FS_COMPOSITE_SAVE:
1840 		if (xflags & XF_COMPJ)
1841 			jpeg_quality = settings->jpeg_quality;
1842 		if (xflags & (XF_COMPZ | XF_COMPZT))
1843 			png_compression = settings->png_compression;
1844 		if (xflags & XF_COMPLZ)
1845 			lzma_preset = settings->lzma_preset;
1846 		if (xflags & XF_COMPZS)
1847 			zstd_level = settings->zstd_level;
1848 		if (xflags & XF_COMPR)
1849 			tga_RLE = settings->tga_RLE;
1850 		if (xflags & XF_COMPJ2)
1851 			jp2_rate = settings->jp2_rate;
1852 		if (xflags & XF_COMPW)
1853 		{
1854 			webp_preset = settings->webp_preset;
1855 			if (webp_preset) webp_quality = settings->webp_quality;
1856 			else webp_compression = settings->webp_compression;
1857 		}
1858 		if (xflags & XF_COMPWT) webp_quality = settings->webp_quality;
1859 		if ((xflags & XF_COMPT) && (ttype >= 0))
1860 		{
1861 			/* Remember selection for incompatible types separately;
1862 			 * image is not set up yet, so use the mode instead */
1863 			*(settings->mode == FS_COMPOSITE_SAVE ? &tiff_rtype :
1864 				(settings->mode == FS_CHANNEL_SAVE) &&
1865 				(mem_channel != CHN_IMAGE) ? &tiff_itype :
1866 				mem_img_bpp == 3 ? &tiff_rtype :
1867 				mem_cols > 2 ? &tiff_itype : &tiff_btype) = ttype;
1868 		}
1869 		if (xflags & XF_COMPRL) lbm_pack = settings->lbm_pack;
1870 		/* !!! XF_PBM is only for indexed LBMs, but a complicated check
1871 		 * for that like for TIFFs above would add nothing of value here */
1872 		if (ftype == FT_LBM) lbm_pbm = settings->lbm_pbm;
1873 		break;
1874 	case FS_EXPORT_GIF:
1875 		preserved_gif_delay = settings->gif_delay;
1876 		break;
1877 	}
1878 }
1879 
fs_ok(fselector_dd * dt,void ** wdata)1880 static void fs_ok(fselector_dd *dt, void **wdata)
1881 {
1882 	ls_settings settings;
1883 	char fname[PATHTXT], *msg, *f8;
1884 	char *c, *ext, *ext2, *gif, *gif2;
1885 	int i, j, res, redo = 1;
1886 
1887 	run_query(wdata);
1888 	/* Pick up extra info */
1889 	init_ls_settings(&settings, wdata);
1890 
1891 	/* Looks better if no dialog under progressbar */
1892 	cmd_showhide(GET_WINDOW(wdata), FALSE);
1893 
1894 	/* File extension */
1895 	cmd_peekv(GET_WINDOW(wdata), fname, PATHTXT, FPICK_RAW); // raw filename
1896 	c = strrchr(fname, '.');
1897 	while (TRUE)
1898 	{
1899 		/* Cut the extension off */
1900 		if ((settings.mode == FS_CLIP_FILE) ||
1901 			(settings.mode == FS_EXPLODE_FRAMES) ||
1902 			(settings.mode == FS_EXPORT_UNDO) ||
1903 			(settings.mode == FS_EXPORT_UNDO2))
1904 		{
1905 			if (!c) break;
1906 			*c = '\0';
1907 		}
1908 		/* Modify the file extension if needed */
1909 		else if (settings.mode == FS_PNG_LOAD) break;
1910 		else
1911 		{
1912 			ext = file_formats[settings.ftype].ext;
1913 			if (!ext[0]) break;
1914 
1915 			if (c) /* There is an extension */
1916 			{
1917 				/* Same extension? */
1918 				if (!strncasecmp(c + 1, ext, 256)) break;
1919 				/* Alternate extension? */
1920 				ext2 = file_formats[settings.ftype].ext2;
1921 				if (ext2[0] && !strncasecmp(c + 1, ext2, 256))
1922 					break;
1923 				/* Another file type? */
1924 				for (i = 0; i < NUM_FTYPES; i++)
1925 				{
1926 					if (strncasecmp(c + 1, file_formats[i].ext, 256) &&
1927 						strncasecmp(c + 1, file_formats[i].ext2, 256))
1928 						continue;
1929 					/* Truncate */
1930 					*c = '\0';
1931 					break;
1932 				}
1933 			}
1934 			i = strlen(fname);
1935 			j = strlen(ext);
1936 			if (i + j >= PATHTXT - 1) break; /* Too long */
1937 			fname[i] = '.';
1938 			strncpy(fname + i + 1, ext, j + 1);
1939 		}
1940 		cmd_setv(GET_WINDOW(wdata), fname, FPICK_RAW); // raw filename
1941 		break;
1942 	}
1943 
1944 	/* Get filename the proper way, in system filename encoding */
1945 	cmd_read(GET_WINDOW(wdata), dt);
1946 
1947 	switch (settings.mode)
1948 	{
1949 	case FS_PNG_LOAD:
1950 		if (do_a_load_x(dt->filename, undo_load, dt) == 1) break;
1951 		redo = 0;
1952 		break;
1953 	case FS_PNG_SAVE:
1954 		if (check_file(dt->filename)) break;
1955 		store_ls_settings(&settings);	// Update data in memory
1956 		if (gui_save(dt->filename, &settings) < 0) break;
1957 		if (layers_total > 0)
1958 		{
1959 			/* Filename has changed so layers file needs re-saving to be correct */
1960 			if (!mem_filename || strcmp(dt->filename, mem_filename))
1961 				layers_notify_changed();
1962 		}
1963 		set_new_filename(layer_selected, dt->filename);
1964 		redo = 0;
1965 		break;
1966 	case FS_PALETTE_LOAD:
1967 		if (load_pal(dt->filename)) break;
1968 		notify_changed();
1969 		redo = 0;
1970 		break;
1971 	case FS_PALETTE_SAVE:
1972 		if (check_file(dt->filename)) break;
1973 		settings.pal = mem_pal;
1974 		settings.colors = mem_cols;
1975 		redo = 2;
1976 		if (save_image(dt->filename, &settings)) break;
1977 		redo = 0;
1978 		break;
1979 	case FS_CLIP_FILE:
1980 	case FS_SELECT_FILE:
1981 	case FS_SELECT_DIR:
1982 		if (dt->pathbox) cmd_setv(dt->pathbox, dt->filename, PATH_VALUE);
1983 		redo = 0;
1984 		break;
1985 	case FS_EXPORT_UNDO:
1986 	case FS_EXPORT_UNDO2:
1987 		if (export_undo(dt->filename, &settings))
1988 			alert_box(_("Error"), _("Unable to export undo images"), NULL);
1989 		redo = 0;
1990 		break;
1991 	case FS_EXPORT_ASCII:
1992 		if (check_file(dt->filename)) break;
1993 		if (export_ascii(dt->filename))
1994 			alert_box(_("Error"), _("Unable to export ASCII file"), NULL);
1995 		redo = 0;
1996 		break;
1997 	case FS_LAYER_SAVE:
1998 		if (check_file(dt->filename)) break;
1999 		if (save_layers(dt->filename) != 1) break;
2000 		redo = 0;
2001 		break;
2002 	case FS_EXPLODE_FRAMES:
2003 		gif = dt->frames_name;
2004 		res = dt->frames_ftype;
2005 		i = dt->frames_anim;
2006 		res = explode_frames(dt->filename, i, gif, res, settings.ftype);
2007 		if (res != 1) handle_file_error(res);
2008 		if (res > 0) // Success or nonfatal error
2009 		{
2010 			c = strrchr(gif, DIR_SEP);
2011 			if (!c) c = gif;
2012 			else c++;
2013 			c = file_in_dir(NULL, dt->filename, c, PATHBUF);
2014 			run_def_action(DA_GIF_EDIT, c, NULL, preserved_gif_delay);
2015 			free(c);
2016 			redo = 0;
2017 		}
2018 		break;
2019 	case FS_EXPORT_GIF:
2020 		if (check_file(dt->filename)) break;
2021 		store_ls_settings(&settings);	// Update data in memory
2022 		gif2 = g_strdup(mem_filename);	// Guaranteed to be non-NULL
2023 		for (i = strlen(gif2) - 1; i >= 0; i--)
2024 		{
2025 			if (gif2[i] == DIR_SEP) break;
2026 			if ((unsigned char)(gif2[i] - '0') <= 9) gif2[i] = '?';
2027 		}
2028 		if (!run_def_action(DA_GIF_CREATE, gif2, dt->filename, settings.gif_delay) &&
2029 			!cmd_mode) /* Don't launch GUI from commandline */
2030 			run_def_action(DA_GIF_PLAY, dt->filename, NULL, 0);
2031 		g_free(gif2);
2032 		redo = 0;
2033 		break;
2034 	case FS_CHANNEL_LOAD:
2035 		if (populate_channel(dt->filename)) break;
2036 		redo = 0;
2037 		break;
2038 	case FS_CHANNEL_SAVE:
2039 		if (check_file(dt->filename)) break;
2040 		store_ls_settings(&settings);	// Update data in memory
2041 		settings.img[CHN_IMAGE] = mem_img[mem_channel];
2042 		settings.width = mem_width;
2043 		settings.height = mem_height;
2044 		if (mem_channel == CHN_IMAGE)
2045 		{
2046 			settings.pal = mem_pal;
2047 			settings.bpp = mem_img_bpp;
2048 			settings.colors = mem_cols;
2049 		}
2050 		else
2051 		{
2052 			settings.pal = NULL; /* Greyscale one 'll be created */
2053 			settings.bpp = 1;
2054 			settings.colors = 256;
2055 			settings.xpm_trans = -1;
2056 		}
2057 		redo = 2;
2058 		if (save_image(dt->filename, &settings)) break;
2059 		redo = 0;
2060 		break;
2061 	case FS_COMPOSITE_SAVE:
2062 		if (check_file(dt->filename)) break;
2063 		store_ls_settings(&settings);	// Update data in memory
2064 		redo = 2;
2065 		if (layer_save_composite(dt->filename, &settings)) break;
2066 		redo = 0;
2067 		break;
2068 	default: // Paranoia
2069 		redo = 0;
2070 		break;
2071 	}
2072 
2073 	if (redo > 1) /* Report error */
2074 	{
2075 		f8 = gtkuncpy(NULL, dt->filename, 0);
2076 		msg = g_strdup_printf(__("Unable to save file: %s"), f8);
2077 		alert_box(_("Error"), msg, NULL);
2078 		g_free(msg);
2079 		g_free(f8);
2080 	}
2081 	if (redo && !script_cmds) /* Redo */
2082 	{
2083 		cmd_showhide(GET_WINDOW(wdata), TRUE);
2084 		return;
2085 	}
2086 	/* Done */
2087 	user_break |= redo; // Paranoia
2088 	update_menus();
2089 	run_destroy(wdata);
2090 }
2091 
2092 #define WBbase fselector_dd
2093 static void *fselector_code[] = {
2094 	FPICKpm(title, fpmode, filename, fs_ok, NULL),
2095 	IFx(fmask, 1),
2096 		MLABEL(_("File Format")),
2097 		OPTDe(ftnames, ftype, change_image_format), TRIGGER,
2098 	ENDIF(1),
2099 	IFx(need_save, 1),
2100 		VISMASK(~0), HEIGHTBAR,
2101 		MLABELr(_("Transparency index")), ACTMAP(XF_TRANS),
2102 			SPINa(xtrans), ACTMAP(XF_TRANS),
2103 		MLABELr(_("TIFF Compression")), ACTMAP(XF_COMPT),
2104 		OPTDe(tiffnames, tiff_m, change_image_format), ACTMAP(XF_COMPT),
2105 		MLABELr(_("JPEG Save Quality (100=High)")), ACTMAP(XF_COMPJ),
2106 			SPIN(jpeg_c, 0, 100), ACTMAP(XF_COMPJ),
2107 		MLABELr(_("ZIP Compression (0=None)")), ACTMAP(XF_COMPZT),
2108 		MLABELr(_("PNG Compression (0=None)")), ACTMAP(XF_COMPZ),
2109 			SPIN(png_c, 0, 9), ACTMAP(XF_COMPZ | XF_COMPZT),
2110 			ALTNAME("ZIP Compression (0=None)"),
2111 		MLABELr(_("LZMA2 Compression (0=None)")), ACTMAP(XF_COMPLZ),
2112 			SPIN(lzma_c, 0, 9), ACTMAP(XF_COMPLZ),
2113 		MLABELr(_("ZSTD Compression Level")), ACTMAP(XF_COMPZS),
2114 			SPIN(zstd_c, ZSTD_MIN, ZSTD_MAX), ACTMAP(XF_COMPZS),
2115 		CHECK(_("TGA RLE Compression"), tga_c), ACTMAP(XF_COMPR),
2116 		CHECK("PBM", lbm_p), ACTMAP(XF_PBM),
2117 		CHECK(_("LBM PackBits Compression"), lbm_c), ACTMAP(XF_COMPRL),
2118 		MLABELr(_("JPEG2000 Compression (0=Lossless)")), ACTMAP(XF_COMPJ2),
2119 			SPIN(jp2_c, 0, 100), ACTMAP(XF_COMPJ2),
2120 		MLABELr(_("WebP Compression")), ACTMAP(XF_COMPW),
2121 		OPTe(webp_presets, 0, webp_p, change_image_format), ACTMAP(XF_COMPW),
2122 		MLABELr(_("WebP Save Quality (100=High)")), ACTMAP(XF_COMPWT),
2123 		MLABELr(_("V8 Save Quality (100=High)")), ACTMAP(XF_COMPV8),
2124 			SPIN(webp_q, 0, 100), ACTMAP(XF_COMPV8 | XF_COMPWT),
2125 		MLABELr(_("V8L Compression (0=None)")), ACTMAP(XF_COMPV8L),
2126 			SPIN(webp_c, 0, 9), ACTMAP(XF_COMPV8L),
2127 		MLABELr(_("Hotspot at X =")), ACTMAP(XF_SPOT),
2128 			SPINa(xx), ACTMAP(XF_SPOT),
2129 		MLABELr(_("Y =")), ACTMAP(XF_SPOT),
2130 			SPINa(xy), ACTMAP(XF_SPOT),
2131 	ENDIF(1),
2132 	IFx(need_anim, 1),
2133 		MLABEL(_("Animation delay")),
2134 		SPIN(gif_delay, 1, MAX_DELAY),
2135 	ENDIF(1),
2136 	IF(need_undo), CHECK(_("Undoable"), undo),
2137 #ifdef U_LCMS
2138 	IF(need_icc), CHECK(_("Apply colour profile"), icc),
2139 #endif
2140 	WDONE,
2141 	IFx(script, 1),
2142 		/* For loading multiframe files */
2143 		CHECK("Load into Layers", load_lr),
2144 		uPATHSTR(ex_path), OPNAME("Explode Frames"),
2145 		OPTv(modes_txt, 3, anim_mode), OPNAME("Frames"),
2146 		/* For loading scalable images */
2147 		SPIN(w, 0, MAX_WIDTH), OPNAME("Width"),
2148 		SPIN(h, 0, MAX_HEIGHT), OPNAME("Height"),
2149 	ENDIF(1),
2150 	WXYWH("fs_window", 550, 500),
2151 	CLEANUP(frames_name),
2152 	RAISED, WSHOW
2153 };
2154 #undef WBbase
2155 
file_selector_x(int action_type,void ** xdata)2156 void file_selector_x(int action_type, void **xdata)
2157 {
2158 	fselector_dd tdata;
2159 	char *names[NUM_FTYPES + 1], *tiffnames[TIFF_MAX_TYPES], *ext = NULL;
2160 	int def = FT_PNG;
2161 
2162 
2163 	memset(&tdata, 0, sizeof(tdata));
2164 	tdata.script = !!script_cmds;
2165 	tdata.mode = action_type;
2166 	tdata.fpmode = FPICK_ENTRY;
2167 	tdata.xtrans[0] = mem_xpm_trans;
2168 	tdata.xtrans[1] = -1;
2169 	tdata.xtrans[2] = mem_cols - 1;
2170 	switch (action_type)
2171 	{
2172 	case FS_PNG_LOAD:
2173 		if ((!script_cmds || !undo_load) && ((layers_total ?
2174 			check_layers_for_changes() : check_for_changes()) == 1))
2175 			return;
2176 		tdata.title = _("Load Image File");
2177 		tdata.fpmode = FPICK_LOAD;
2178 		tdata.need_undo = TRUE;
2179 #ifdef U_LCMS
2180 		tdata.need_icc = TRUE;
2181 #endif
2182 		if (script_cmds)
2183 		{
2184 			tdata.fmask = FF_IMAGE;
2185 			ext = "";
2186 			def = FT_NONE;
2187 		}
2188 		break;
2189 	case FS_PNG_SAVE:
2190 		tdata.fmask = FF_SAVE_MASK;
2191 		tdata.title = _("Save Image File");
2192 		if (mem_filename) strncpy(tdata.filename, mem_filename, PATHBUF);
2193 		tdata.need_save = TRUE;
2194 		break;
2195 	case FS_PALETTE_LOAD:
2196 		tdata.title = _("Load Palette File");
2197 		tdata.fpmode = FPICK_LOAD;
2198 		break;
2199 	case FS_PALETTE_SAVE:
2200 		tdata.fmask = FF_PALETTE;
2201 		def = FT_GPL;
2202 		tdata.title = _("Save Palette File");
2203 		break;
2204 	case FS_EXPORT_UNDO:
2205 		if (!mem_undo_done) return;
2206 		tdata.fmask = FF_IMAGE;
2207 		tdata.title = _("Export Undo Images");
2208 		break;
2209 	case FS_EXPORT_UNDO2:
2210 		if (!mem_undo_done) return;
2211 		tdata.fmask = FF_IMAGE;
2212 		tdata.title = _("Export Undo Images (reversed)");
2213 		break;
2214 	case FS_EXPORT_ASCII:
2215 		if (mem_cols > 16)
2216 		{
2217 			alert_box( _("Error"), _("You must have 16 or fewer palette colours to export ASCII art."), NULL);
2218 			return;
2219 		}
2220 		tdata.title = _("Export ASCII Art");
2221 		break;
2222 	case FS_LAYER_SAVE: /* !!! No selectable layer file format yet */
2223 		if (check_layers_all_saved()) return;
2224 		tdata.title = _("Save Layer Files");
2225 		strncpy(tdata.filename, layers_filename, PATHBUF);
2226 		break;
2227 	case FS_EXPLODE_FRAMES:
2228 		tdata.frames_name = strdup(xdata[0]);
2229 		tdata.frames_ftype = (int)xdata[1];
2230 		tdata.frames_anim = (int)xdata[2];
2231 
2232 		ext = file_formats[tdata.frames_ftype].ext;
2233 		tdata.fmask = FF_IMAGE;
2234 		tdata.title = _("Choose frames directory");
2235 		tdata.fpmode = FPICK_DIRS_ONLY;
2236 		break;
2237 	case FS_EXPORT_GIF: /* !!! No selectable formats yet */
2238 		if (!mem_filename)
2239 		{
2240 			alert_box(_("Error"), _("You must save at least one frame to create an animated GIF."), NULL);
2241 			return;
2242 		}
2243 		tdata.title = _("Export GIF animation");
2244 		tdata.need_anim = TRUE;
2245 		break;
2246 	case FS_CHANNEL_LOAD:
2247 		tdata.title = _("Load Channel");
2248 		tdata.fpmode = FPICK_LOAD;
2249 #ifdef U_LCMS
2250 		tdata.need_icc = MEM_BPP == 3;
2251 #endif
2252 		break;
2253 	case FS_CHANNEL_SAVE:
2254 		tdata.fmask = mem_channel != CHN_IMAGE ? FF_256 : FF_SAVE_MASK;
2255 		tdata.title = _("Save Channel");
2256 		tdata.need_save = TRUE;
2257 		break;
2258 	case FS_COMPOSITE_SAVE:
2259 		tdata.fmask = FF_RGB;
2260 		tdata.title = _("Save Composite Image");
2261 		tdata.need_save = TRUE;
2262 		if (layer_selected) // Use background's transparency
2263 		{
2264 			image_info *image = &layer_table[0].image->image_;
2265 			tdata.xtrans[0] = image->trans;
2266 			tdata.xtrans[2] = image->cols - 1;
2267 		}
2268 		break;
2269 	case FS_CLIP_FILE:
2270 	case FS_SELECT_FILE:
2271 	case FS_SELECT_DIR:
2272 		tdata.fpmode = action_type == FS_SELECT_DIR ?
2273 			FPICK_LOAD | FPICK_DIRS_ONLY : FPICK_LOAD;
2274 		tdata.title = xdata[0];
2275 		tdata.pathbox = xdata[1]; // pathbox slot
2276 		cmd_peekv(xdata[1], tdata.filename, PATHBUF, PATH_VALUE);
2277 		break;
2278 	default: /*
2279 	FS_LAYER_LOAD,
2280 	FS_PATTERN_LOAD,
2281 	FS_CLIPBOARD,
2282 	FS_PALETTE_DEF */
2283 		return;	/* These are not for here */
2284 	}
2285 
2286 	/* Default filename */
2287 	if (!tdata.filename[0]) file_in_dir(tdata.filename,
2288 		inifile_get("last_dir", get_home_directory()),
2289 		action_type == FS_LAYER_SAVE ? "layers.txt" : "", PATHBUF);
2290 	if (!ext)
2291 	{
2292 		ext = strrchr(tdata.filename, '.');
2293 		ext = ext ? ext + 1 : "";
2294 	}
2295 
2296 	/* Filetype selectors */
2297 	if (tdata.fmask)
2298 	{
2299 		tdata.ftype = ftype_selector(tdata.fmask, ext, def,
2300 			tdata.ftnames = names, tdata.ftypes);
2301 		tdata.tiff_m = tiff_type_selector(tdata.fmask,
2302 			tdata.fmask & FF_RGB ? tiff_rtype :
2303 			tdata.fmask & FF_BW ? tiff_btype : tiff_itype,
2304 			tdata.tiffnames = tiffnames);
2305 	}
2306 
2307 	tdata.xx[0] = mem_xbm_hot_x;
2308 	tdata.xx[1] = -1;
2309 	tdata.xx[2] = mem_width - 1;
2310 	tdata.xy[0] = mem_xbm_hot_y;
2311 	tdata.xy[1] = -1;
2312 	tdata.xy[2] = mem_height - 1;
2313 
2314 	tdata.jpeg_c = jpeg_quality;
2315 	tdata.png_c = png_compression;
2316 	tdata.tga_c = tga_RLE;
2317 	tdata.jp2_c = jp2_rate;
2318 	tdata.lzma_c = lzma_preset;
2319 	tdata.zstd_c = zstd_level;
2320 	tdata.webp_p = webp_preset;
2321 	tdata.webp_q = webp_quality;
2322 	tdata.webp_c = webp_compression;
2323 
2324 	tdata.gif_delay = preserved_gif_delay;
2325 	tdata.undo = undo_load;
2326 #ifdef U_LCMS
2327 	tdata.icc = apply_icc;
2328 #endif
2329 
2330 	run_create_(fselector_code, &tdata, sizeof(tdata), script_cmds);
2331 }
2332 
file_selector(int action_type)2333 void file_selector(int action_type)
2334 {
2335 	file_selector_x(action_type, NULL);
2336 }
2337 
canvas_center(float ic[2])2338 void canvas_center(float ic[2])		// Center of viewable area
2339 {
2340 	int w, h, xyhv[4];
2341 
2342 	ic[0] = ic[1] = 0.5;
2343 	if (script_cmds) return;
2344 	cmd_peekv(scrolledwindow_canvas, xyhv, sizeof(xyhv), CSCROLL_XYSIZE);
2345 	canvas_size(&w, &h);
2346 	if (xyhv[2] < w) ic[0] = (xyhv[0] + xyhv[2] * 0.5) / w;
2347 	if (xyhv[3] < h) ic[1] = (xyhv[1] + xyhv[3] * 0.5) / h;
2348 }
2349 
align_size(float new_zoom)2350 void align_size(float new_zoom)		// Set new zoom level
2351 {
2352 	static int zoom_flag;
2353 
2354 	if (zoom_flag) return;		// Needed as we could be called twice per iteration
2355 
2356 	if (new_zoom < MIN_ZOOM) new_zoom = MIN_ZOOM;
2357 	if (new_zoom > MAX_ZOOM) new_zoom = MAX_ZOOM;
2358 	if (new_zoom == can_zoom) return;
2359 
2360 	zoom_flag = 1;
2361 	if (!mem_ics && !cmd_mode)
2362 	{
2363 		int xc, yc, dx, dy, w, h, x, y, xyhv[4];
2364 
2365 		cmd_peekv(scrolledwindow_canvas, xyhv, sizeof(xyhv), CSCROLL_XYSIZE);
2366 		xc = xyhv[0] * 2 + (w = xyhv[2]);
2367 		yc = xyhv[1] * 2 + (h = xyhv[3]);
2368 		dx = dy = 0;
2369 
2370 		if (cursor_zoom)
2371 		{
2372 			mouse_ext m;
2373 			cmd_peekv(drawing_canvas, &m, sizeof(m), CANVAS_FIND_MOUSE);
2374 			x = m.x - xyhv[0];
2375 			y = m.y - xyhv[1];
2376 			if ((x >= 0) && (x < w) && (y >= 0) && (y < h))
2377 				dx = x * 2 - w , dy = y * 2 - h;
2378 		}
2379 
2380 		mem_icx = ((xc + dx - margin_main_x * 2) / can_zoom -
2381 			dx / new_zoom) / (mem_width * 2);
2382 		mem_icy = ((yc + dy - margin_main_y * 2) / can_zoom -
2383 			dy / new_zoom) / (mem_height * 2);
2384 	}
2385 	mem_ics = 0;
2386 
2387 	can_zoom = new_zoom;
2388 	realign_size();
2389 	zoom_flag = 0;
2390 }
2391 
realign_size()2392 void realign_size()		// Reapply old zoom
2393 {
2394 	int xyhv[4], xywh[4];
2395 
2396 	if (cmd_mode) return;
2397 	cmd_peekv(scrolledwindow_canvas, xyhv, sizeof(xyhv), CSCROLL_XYSIZE);
2398 	canvas_size(xywh + 2, xywh + 3);
2399 	xywh[0] = xywh[1] = 0;	// New positions of scrollbar
2400 	if (xyhv[2] < xywh[2]) xywh[0] = rint(xywh[2] * mem_icx - xyhv[2] * 0.5);
2401 	if (xyhv[3] < xywh[3]) xywh[1] = rint(xywh[3] * mem_icy - xyhv[3] * 0.5);
2402 
2403 	/* !!! CSCROLL's self-updating for CANVAS resize is delayed in GTK+, as
2404 	 * is the actual resize; so to communicate new position to that latter
2405 	 * resize, I preset both position and range of CSCROLL here - WJ */
2406 	cmd_setv(scrolledwindow_canvas, xywh, CSCROLL_XYRANGE);
2407 
2408 	cmd_setv(drawing_canvas, xywh + 2, CANVAS_SIZE);
2409 	vw_focus_view();	// View window position may need updating
2410 	toolbar_zoom_update();	// Zoom factor may have been reset
2411 }
2412 
2413 /* This tool is seamless: doesn't draw pixels twice if not requested to - WJ */
2414 // !!! With GCC inlining this, weird size fluctuations can happen
rec_continuous(int nx,int ny,int w,int h)2415 void rec_continuous(int nx, int ny, int w, int h)
2416 {
2417 	linedata line1, line2, line3, line4;
2418 	int ws2 = w >> 1, hs2 = h >> 1;
2419 	int i, j, i2, j2, *xv;
2420 	int dx[3] = {-ws2, w - ws2 - 1, -ws2};
2421 	int dy[3] = {-hs2, h - hs2 - 1, -hs2};
2422 
2423 	i = nx < tool_ox;
2424 	j = ny < tool_oy;
2425 
2426 	/* Redraw starting square only if need to fill in possible gap when
2427 	 * size changes, or to draw stroke gradient in the proper direction */
2428 	if (!tablet_working && !script_cmds &&
2429 		((tool_type == TOOL_CLONE) || !STROKE_GRADIENT))
2430 	{
2431 		i2 = tool_ox + dx[i + 1] + 1 - i * 2;
2432 		j2 = tool_oy + dy[j + 1] + 1 - j * 2;
2433 		xv = &line3[0];
2434 	}
2435 	else
2436 	{
2437 		i2 = tool_ox + dx[i];
2438 		j2 = tool_oy + dy[j];
2439 		xv = &i2;
2440 	}
2441 
2442 	if (tool_ox == nx)
2443 	{
2444 		line_init(line1, tool_ox + dx[i], j2,
2445 			tool_ox + dx[i], ny + dy[j + 1]);
2446 		line_init(line3, tool_ox + dx[i + 1], j2,
2447 			tool_ox + dx[i + 1], ny + dy[j + 1]);
2448 		line2[2] = line4[2] = -1;
2449 	}
2450 	else
2451 	{
2452 		line_init(line2, tool_ox + dx[i], tool_oy + dy[j + 1],
2453 			nx + dx[i], ny + dy[j + 1]);
2454 		line_nudge(line2, i2, j2);
2455 		line_init(line3, tool_ox + dx[i + 1], tool_oy + dy[j],
2456 			nx + dx[i + 1], ny + dy[j]);
2457 		line_nudge(line3, i2, j2);
2458 		line_init(line1, *xv, line3[1], *xv, line2[1]);
2459 		line_init(line4, nx + dx[i + 1], ny + dy[j],
2460 			nx + dx[i + 1], ny + dy[j + 1]);
2461 	}
2462 
2463 	/* Prevent overwriting clone source */
2464 	while (tool_type == TOOL_CLONE)
2465 	{
2466 		if ((j * 2 - 1) * clone_dy <= 0) break; // Ahead of dest
2467 		if (mem_undo_opacity && (mem_undo_previous(mem_channel) !=
2468 			mem_img[mem_channel])) break; // In another frame
2469 #if 0 /* No real reason to spend code on avoiding the flip */
2470 		i = abs(ny - tool_oy) + h - 1;
2471 		if ((clone_dy < -i) || (clone_dy > i)) break; // Out Y range
2472 		i = abs(nx - tool_ox) + w - 1;
2473 		if ((clone_dx < -i) || (clone_dx > i)) break; // Out X range
2474 #endif
2475 		line_flip(line1);
2476 		line_flip(line3);
2477 		if (tool_ox == nx) break; // Only 2 lines
2478 		line_flip(line2);
2479 		line_flip(line4);
2480 		draw_quad(line2, line1, line4, line3);
2481 		return;
2482 	}
2483 
2484 	draw_quad(line1, line2, line3, line4);
2485 }
2486 
2487 
2488 static struct {
2489 	float c_zoom;
2490 	int points;
2491 	int xy[2 * MAX_POLY + 2];
2492 //	int step[MAX_POLY + 1];
2493 } poly_cache;
2494 
poly_update_cache()2495 static void poly_update_cache()
2496 {
2497 //	int dx, dy, *ps;
2498 	int i, i0, last, *pxy, ds, zoom = 1, scale = 1;
2499 
2500 	i0 = poly_cache.c_zoom == can_zoom ? poly_cache.points : 0;
2501 	last = poly_points + (poly_status == POLY_DONE);
2502 	if (i0 >= last) return; // Up to date
2503 
2504 	/* !!! This uses the fact that zoom factor is either N or 1/N !!! */
2505 	if (can_zoom < 1.0) zoom = rint(1.0 / can_zoom);
2506 	else scale = rint(can_zoom);
2507 	ds = scale >> 1;
2508 
2509 	poly_cache.c_zoom = can_zoom;
2510 	poly_cache.points = last;
2511 	/* Get locations */
2512 	pxy = poly_cache.xy + i0 * 2;
2513 	for (i = i0; i < poly_points; i++)
2514 	{
2515 		*pxy++ = floor_div(poly_mem[i][0] * scale, zoom) + ds;
2516 		*pxy++ = floor_div(poly_mem[i][1] * scale, zoom) + ds;
2517 	}
2518 	/* Join 1st & last point if finished */
2519 	if (poly_status == POLY_DONE)
2520 	{
2521 		*pxy++ = poly_cache.xy[0];
2522 		*pxy++ = poly_cache.xy[1];
2523 	}
2524 #if 0 /* No need for now */
2525 	/* Get distances */
2526 	poly_cache.step[0] = 0;
2527 	if (!i0) i0 = 1;
2528 	ps = poly_cache.step + i0 - 1;
2529 	pxy = poly_cache.xy + i0 * 2 - 2;
2530 	for (i = i0; i < last; i++ , pxy += 2 , ps++)
2531 	{
2532 		dx = abs(pxy[2] - pxy[0]);
2533 		dy = abs(pxy[3] - pxy[1]);
2534 		ps[1] = ps[0] + (dx > dy ? dx : dy);
2535 	}
2536 #endif
2537 }
2538 
stretch_poly_line(int x,int y)2539 void stretch_poly_line(int x, int y)			// Clear old temp line, draw next temp line
2540 {
2541 	int old[4];
2542 
2543 	if (!poly_points || (poly_points >= MAX_POLY)) return;
2544 	if ((line_x2 == x) && (line_y2 == y)) return;	// This check reduces flicker
2545 
2546 	copy4(old, line_xy);
2547 	line_x2 = x;
2548 	line_y2 = y;
2549 	line_x1 = poly_mem[poly_points - 1][0];
2550 	line_y1 = poly_mem[poly_points - 1][1];
2551 	repaint_line(old);
2552 }
2553 
2554 static void refresh_lines(const int xy0[4], const int xy1[4]);
2555 
poly_conclude()2556 static void poly_conclude()
2557 {
2558 	if (!poly_points) poly_status = POLY_NONE;
2559 	else
2560 	{
2561 		int n = poly_points - 1, edge[4] = {
2562 			poly_mem[0][0], poly_mem[0][1],
2563 			poly_mem[n][0], poly_mem[n][1] };
2564 
2565 		poly_status = POLY_DONE;
2566 		poly_bounds();
2567 		check_marquee();
2568 		poly_update_cache();
2569 		/* Combine erasing old line with drawing final segment:
2570 		 * there is no new line to be drawn */
2571 		refresh_lines(line_xy, edge);
2572 	}
2573 	update_stuff(UPD_PSEL);
2574 }
2575 
poly_add_po(int x,int y)2576 static void poly_add_po(int x, int y)
2577 {
2578 	if (!poly_points) poly_cache.c_zoom = 0; // Invalidate
2579 	else if (!((x - poly_mem[poly_points - 1][0]) |
2580 		(y - poly_mem[poly_points - 1][1]))) return; // Never stack
2581 	poly_add(x, y);
2582 	if (poly_points >= MAX_POLY) poly_conclude();
2583 	else
2584 	{
2585 		int n = poly_points > 1 ? poly_points - 2 : 0;
2586 		int old[4], edge[4] = {	poly_mem[n][0], poly_mem[n][1], x, y };
2587 
2588 		poly_update_cache();
2589 		copy4(old, line_xy);
2590 		line_x1 = line_x2 = x;
2591 		line_y1 = line_y2 = y;
2592 		/* Combine erasing old line with redrawing new segment:
2593 		 * the 1-point new line will be drawn as part of it */
2594 		refresh_lines(old, edge);
2595 		update_sel_bar(FALSE);
2596 	}
2597 }
2598 
poly_delete_po(int x,int y)2599 static void poly_delete_po(int x, int y)
2600 {
2601 	if ((poly_status != POLY_SELECTING) && (poly_status != POLY_DONE))
2602 		return; // Do nothing
2603 	if (poly_points < 2) // Remove the only point
2604 	{
2605 		pressed_select(FALSE);
2606 		return;
2607 	}
2608 	if (poly_status == POLY_SELECTING) // Delete last
2609 	{
2610 		poly_points--;
2611 		/* New place for line */
2612 		line_x2 = x;
2613 		line_y2 = y;
2614 		line_x1 = poly_mem[poly_points - 1][0];
2615 		line_y1 = poly_mem[poly_points - 1][1];
2616 	}
2617 	else // Delete nearest
2618 	{
2619 		int i, ll, idx = 0, l = INT_MAX;
2620 
2621 		for (i = 0; i < poly_points; i++)
2622 		{
2623 			ll = (poly_mem[i][0] - x) * (poly_mem[i][0] - x) +
2624 				(poly_mem[i][1] - y) * (poly_mem[i][1] - y);
2625 			if (ll < l) idx = i , l = ll;
2626 		}
2627 		memmove(poly_mem[idx], poly_mem[idx + 1], sizeof(poly_mem[0]) *
2628 			(--poly_points - idx));
2629 		poly_bounds();
2630 		check_marquee();
2631 	}
2632 	poly_cache.c_zoom = 0; // Invalidate
2633 	update_stuff(UPD_PSEL | UPD_RENDER);
2634 }
2635 
tool_draw(int x,int y,int first_point,int * update)2636 static int tool_draw(int x, int y, int first_point, int *update)
2637 {
2638 	static int ncx, ncy;
2639 	int minx, miny, xw, yh, ts2, tr2;
2640 	int i, j, k, ox, oy, px, py, rx, ry, sx, sy, off1, off2;
2641 
2642 	ts2 = tool_size >> 1;
2643 	tr2 = tool_size - ts2 - 1;
2644 	minx = x - ts2;
2645 	miny = y - ts2;
2646 	xw = yh = tool_size;
2647 
2648 	/* Save the brush coordinates for next step */
2649 	ox = ncx; oy = ncy;
2650 	ncx = x; ncy = y;
2651 
2652 	switch (tool_type)
2653 	{
2654 	case TOOL_SQUARE: case TOOL_CLONE:
2655 		f_rectangle(x - ts2, y - ts2, tool_size, tool_size);
2656 		break;
2657 	case TOOL_CIRCLE:
2658 		f_circle(x, y, tool_size);
2659 		break;
2660 	case TOOL_HORIZONTAL:
2661 		miny = y; yh = 1;
2662 		sline(x - ts2, y, x + tr2, y);
2663 		break;
2664 	case TOOL_VERTICAL:
2665 		minx = x; xw = 1;
2666 		sline(x, y - ts2, x, y + tr2);
2667 		break;
2668 	case TOOL_SLASH:
2669 		sline(x + tr2, y - ts2, x - ts2, y + tr2);
2670 		break;
2671 	case TOOL_BACKSLASH:
2672 		sline(x - ts2, y - ts2, x + tr2, y + tr2);
2673 		break;
2674 	case TOOL_SPRAY:
2675 		for (j = 0; j < tool_flow; j++)
2676 		{
2677 			rx = x - ts2 + rand() % tool_size;
2678 			ry = y - ts2 + rand() % tool_size;
2679 			IF_IN_RANGE(rx, ry) put_pixel(rx, ry);
2680 		}
2681 		break;
2682 	case TOOL_SHUFFLE:
2683 		for (j = 0; j < tool_flow; j++)
2684 		{
2685 			rx = x - ts2 + rand() % tool_size;
2686 			ry = y - ts2 + rand() % tool_size;
2687 			sx = x - ts2 + rand() % tool_size;
2688 			sy = y - ts2 + rand() % tool_size;
2689 			IF_IN_RANGE(rx, ry) IF_IN_RANGE(sx, sy)
2690 			{
2691 		/* !!! Or do something for partial mask too? !!! */
2692 				if (pixel_protected(rx, ry) ||
2693 					pixel_protected(sx, sy))
2694 					continue;
2695 				off1 = rx + ry * mem_width;
2696 				off2 = sx + sy * mem_width;
2697 				if ((mem_channel == CHN_IMAGE) &&
2698 					RGBA_mode && mem_img[CHN_ALPHA])
2699 				{
2700 					px = mem_img[CHN_ALPHA][off1];
2701 					py = mem_img[CHN_ALPHA][off2];
2702 					mem_img[CHN_ALPHA][off1] = py;
2703 					mem_img[CHN_ALPHA][off2] = px;
2704 				}
2705 				k = MEM_BPP;
2706 				off1 *= k; off2 *= k;
2707 				for (i = 0; i < k; i++)
2708 				{
2709 					px = mem_img[mem_channel][off1];
2710 					py = mem_img[mem_channel][off2];
2711 					mem_img[mem_channel][off1++] = py;
2712 					mem_img[mem_channel][off2++] = px;
2713 				}
2714 			}
2715 		}
2716 		break;
2717 	case TOOL_SMUDGE:
2718 		if (first_point) return (FALSE);
2719 		mem_smudge(ox, oy, x, y);
2720 		break;
2721 	default: return (FALSE); /* Stop this nonsense now! */
2722 	}
2723 
2724 	/* Accumulate update info */
2725 	if (minx < update[0]) update[0] = minx;
2726 	if (miny < update[1]) update[1] = miny;
2727 	xw += minx; yh += miny;
2728 	if (xw > update[2]) update[2] = xw;
2729 	if (yh > update[3]) update[3] = yh;
2730 
2731 	return (TRUE);
2732 }
2733 
line_to_gradient()2734 void line_to_gradient()
2735 {
2736 	if (STROKE_GRADIENT)
2737 	{
2738 		grad_info *grad = gradient + mem_channel;
2739 		grad->gmode = GRAD_MODE_LINEAR;
2740 		grad->status = GRAD_DONE;
2741 		copy4(grad->xy, line_xy);
2742 		grad_update(grad);
2743 	}
2744 }
2745 
do_tool_action(int cmd,int x,int y,int pressure)2746 void do_tool_action(int cmd, int x, int y, int pressure)
2747 {
2748 	static double lstep;
2749 	linedata ncline;
2750 	double len1;
2751 	int update_area[4];
2752 	int minx = -1, miny = -1, xw = -1, yh = -1;
2753 	tool_info o_tool = tool_state;
2754 	int i, j, ts2, tr2, ox, oy;
2755 	int oox, ooy;	// Continuous smudge stuff
2756 	int first_point, pswap = FALSE;
2757 
2758 	// Only do something with a new point
2759 	if (!(first_point = !pen_down) && (cmd & TCF_ONCE) &&
2760 		(tool_ox == x) && (tool_oy == y)) return;
2761 
2762 	// Apply pressure
2763 	if ((cmd & TCF_PRES) && (tablet_working || script_cmds))
2764 	{
2765 // !!! Later maybe switch the calculations to integer
2766 		double p = pressure <= (MAX_PRESSURE * 2 / 10) ? -1.0 :
2767 			(pressure - MAX_PRESSURE) * (10.0 / (8 * MAX_PRESSURE));
2768 		p /= MAX_TF;
2769 
2770 		for (i = 0; i < 3; i++)
2771 		{
2772 			if (!tablet_tool_use[i]) continue;
2773 			tool_state.var[i] *= (tablet_tool_factor[i] > 0) +
2774 				tablet_tool_factor[i] * p;
2775 			if (tool_state.var[i] < 1) tool_state.var[i] = 1;
2776 		}
2777 	}
2778 
2779 	ts2 = tool_size >> 1;
2780 	tr2 = tool_size - ts2 - 1;
2781 
2782 	switch (cmd &= TC_OPMASK)
2783 	{
2784 	case TC_LINE_START: case TC_LINE_ARROW:
2785 		line_x2 = x;
2786 		line_y2 = y;
2787 		if (line_status == LINE_NONE)
2788 		{
2789 			line_x1 = x;
2790 			line_y1 = y;
2791 		}
2792 
2793 		// Draw circle at x, y
2794 		if (line_status == LINE_LINE)
2795 		{
2796 			grad_info svgrad = gradient[mem_channel];
2797 
2798 			/* If not called from draw_arrow() */
2799 			if (cmd != TC_LINE_ARROW) line_to_gradient();
2800 
2801 			mem_undo_next(UNDO_TOOL);
2802 			if (tool_size > 1)
2803 			{
2804 				int oldmode = mem_undo_opacity;
2805 				mem_undo_opacity = TRUE;
2806 				f_circle(line_x1, line_y1, tool_size);
2807 				f_circle(line_x2, line_y2, tool_size);
2808 				// Draw tool_size thickness line from 1-2
2809 				tline(line_x1, line_y1, line_x2, line_y2, tool_size);
2810 				mem_undo_opacity = oldmode;
2811 			}
2812 			else sline(line_x1, line_y1, line_x2, line_y2);
2813 
2814 			minx = (line_x1 < line_x2 ? line_x1 : line_x2) - ts2;
2815 			miny = (line_y1 < line_y2 ? line_y1 : line_y2) - ts2;
2816 			xw = abs( line_x2 - line_x1 ) + 1 + tool_size;
2817 			yh = abs( line_y2 - line_y1 ) + 1 + tool_size;
2818 
2819 			line_x1 = line_x2;
2820 			line_y1 = line_y2;
2821 
2822 			gradient[mem_channel] = svgrad;
2823 		}
2824 		line_status = LINE_START;
2825 		break;
2826 	case TC_LINE_NEXT:
2827 		line_status = LINE_LINE;
2828 		repaint_line(NULL);
2829 		break;
2830 	case TC_LINE_STOP:
2831 		stop_line();
2832 		break;
2833 	case TC_SEL_CLEAR:
2834 		pressed_select(FALSE);
2835 		break;
2836 	case TC_SEL_START:
2837 	case TC_SEL_SET_0: case TC_SEL_SET_1:
2838 	case TC_SEL_SET_2: case TC_SEL_SET_3:
2839 		if (cmd == TC_SEL_START)
2840 		{
2841 			marq_x1 = x;
2842 			marq_y1 = y;
2843 		}
2844 		else
2845 		{
2846 			paint_marquee(MARQ_HIDE, 0, 0, NULL);
2847 			i = cmd - TC_SEL_SET_0;
2848 			if (!(i & 1) ^ (marq_x1 > marq_x2))
2849 				marq_x1 = marq_x2;
2850 			if (!(i & 2) ^ (marq_y1 > marq_y2))
2851 				marq_y1 = marq_y2;
2852 			set_cursor(NULL);
2853 		}
2854 		marq_x2 = x;
2855 		marq_y2 = y;
2856 		marq_status = MARQUEE_SELECTING;
2857 		paint_marquee(MARQ_SNAP, 0, 0, NULL);
2858 		break;
2859 	case TC_SEL_TO:
2860 		paint_marquee(MARQ_SIZE, x, y, NULL);
2861 		break;
2862 	case TC_SEL_STOP:
2863 		marq_status = marq_status == MARQUEE_PASTE_DRAG ? MARQUEE_PASTE :
2864 			MARQUEE_DONE;
2865 		cursor_corner = -1;
2866 // !!! Here fallthrough to setting cursor
2867 		break;
2868 	case TC_POLY_START: case TC_POLY_START_D:
2869 		poly_status = cmd == TC_POLY_START ? POLY_SELECTING : POLY_DRAGGING;
2870 		// Fallthrough
2871 	case TC_POLY_ADD:
2872 		poly_add_po(x, y);
2873 		break;
2874 	case TC_POLY_DEL:
2875 		poly_delete_po(x, y);
2876 		break;
2877 	case TC_POLY_CLOSE:
2878 		poly_conclude();
2879 		break;
2880 	case TC_PASTE_PSWAP:
2881 		pswap = TRUE;
2882 		// Fallthrough
2883 	case TC_PASTE_DRAG:
2884 	case TC_PASTE_PAINT:
2885 		if (marq_status != MARQUEE_PASTE_DRAG)
2886 		{
2887 			marq_status = MARQUEE_PASTE_DRAG;
2888 			marq_drag_x = x - marq_x1;
2889 			marq_drag_y = y - marq_y1;
2890 		}
2891 		else paint_marquee(MARQ_MOVE, x - marq_drag_x, y - marq_drag_y, NULL);
2892 		if (cmd == TC_PASTE_DRAG) break;
2893 		cmd = TC_PASTE_COMMIT;
2894 		// Fallthrough
2895 	case TC_PAINT_B:
2896 		if (cmd == TC_PAINT_B)
2897 		{
2898 			tint_mode[2] = 3; /* Swap tint +/- */
2899 			if (first_point && !tint_mode[0])
2900 			{
2901 				col_reverse = TRUE;
2902 				mem_swap_cols(FALSE);
2903 			}
2904 		}
2905 		// Fallthrough
2906 	case TC_PAINT: case TC_PASTE_COMMIT:
2907 		// Update stroke gradient
2908 		if (STROKE_GRADIENT) grad_stroke(x, y);
2909 
2910 		/* Handle floodfill here, as too irregular a non-continuous tool */
2911 		if (tool_type == TOOL_FLOOD)
2912 		{
2913 			/* Non-masked start point, and working fill */
2914 			if ((pixel_protected(x, y) < 255) &&
2915 				flood_fill(x, y, get_pixel(x, y)))
2916 			{
2917 				// All pixels could change
2918 				minx = miny = 0;
2919 				xw = mem_width;
2920 				yh = mem_height;
2921 			}
2922 			/* Undo the color swap if fill failed */
2923 			if (!pen_down && col_reverse)
2924 			{
2925 				col_reverse = FALSE;
2926 				mem_swap_cols(FALSE);
2927 			}
2928 			break;
2929 		}
2930 
2931 		// Relativize source coords if that isn't yet done
2932 		if (tool_type == TOOL_CLONE)
2933 		{
2934 			if ((clone_status == CLONE_ABS) ||
2935 				(clone_status == (CLONE_REL | CLONE_TRACK)))
2936 				clone_dx = clone_x - x , clone_dy = clone_y - y;
2937 			clone_status = (clone_status & CLONE_ABS) | CLONE_DRAG;
2938 		}
2939 
2940 		// Do memory stuff for undo
2941 		mem_undo_next(UNDO_TOOL);
2942 
2943 		/* Handle continuous mode */
2944 		if (mem_continuous && !first_point)
2945 		{
2946 			minx = tool_ox < x ? tool_ox : x;
2947 			xw = (tool_ox > x ? tool_ox : x) - minx + tool_size;
2948 			minx -= ts2;
2949 
2950 			miny = tool_oy < y ? tool_oy : y;
2951 			yh = (tool_oy > y ? tool_oy : y) - miny + tool_size;
2952 			miny -= ts2;
2953 
2954 			if ((tool_type == TOOL_CLONE) ||
2955 				(ts2 ? tool_type == TOOL_SQUARE : tool_type < TOOL_SPRAY))
2956 			{
2957 				rec_continuous(x, y, tool_size, tool_size);
2958 				break;
2959 			}
2960 			if (tool_type == TOOL_CIRCLE)
2961 			{
2962 				/* Redraw stroke gradient in proper direction */
2963 				if (STROKE_GRADIENT)
2964 					f_circle(tool_ox, tool_oy, tool_size);
2965 				tline(tool_ox, tool_oy, x, y, tool_size);
2966 				f_circle(x, y, tool_size);
2967 				break;
2968 			}
2969 			if (tool_type == TOOL_HORIZONTAL)
2970 			{
2971 				miny += ts2; yh -= tool_size - 1;
2972 				rec_continuous(x, y, tool_size, 1);
2973 				break;
2974 			}
2975 			if (tool_type == TOOL_VERTICAL)
2976 			{
2977 				minx += ts2; xw -= tool_size - 1;
2978 				rec_continuous(x, y, 1, tool_size);
2979 				break;
2980 			}
2981 			if (tool_type == TOOL_SLASH)
2982 			{
2983 				g_para(x + tr2, y - ts2, x - ts2, y + tr2,
2984 					tool_ox - x, tool_oy - y);
2985 				break;
2986 			}
2987 			if (tool_type == TOOL_BACKSLASH)
2988 			{
2989 				g_para(x - ts2, y - ts2, x + tr2, y + tr2,
2990 					tool_ox - x, tool_oy - y);
2991 				break;
2992 			}
2993 			if (tool_type == TOOL_SMUDGE)
2994 			{
2995 				linedata line;
2996 
2997 				line_init(line, tool_ox, tool_oy, x, y);
2998 				while (TRUE)
2999 				{
3000 					oox = line[0];
3001 					ooy = line[1];
3002 					if (line_step(line) < 0) break;
3003 					mem_smudge(oox, ooy, line[0], line[1]);
3004 				}
3005 				break;
3006 			}
3007 			xw = yh = -1; /* Nothing was done */
3008 		}
3009 
3010 		/* Handle non-continuous mode & tools */
3011 		update_area[0] = MAX_WIDTH;
3012 		update_area[1] = MAX_HEIGHT;
3013 		update_area[2] = update_area[3] = 0;
3014 
3015 		if (first_point) lstep = 0.0;
3016 
3017 		if (first_point || !brush_spacing) /* Single point */
3018 		{
3019 			if (cmd == TC_PASTE_COMMIT)
3020 				commit_paste(pswap, update_area); // At marquee
3021 			else tool_draw(x, y, first_point, update_area);
3022 		}
3023 		else /* Multiple points */
3024 		{
3025 			/* Use marquee coords for paste */
3026 			i = j = 0;
3027 			ox = marq_x1;
3028 			oy = marq_y1;
3029 			if (cmd == TC_PASTE_COMMIT)
3030 			{
3031 				i = marq_x1 - x;
3032 				j = marq_y1 - y;
3033 			}
3034 
3035 			line_init(ncline, tool_ox + i, tool_oy + j, x + i, y + j);
3036 			i = abs(x - tool_ox);
3037 			j = abs(y - tool_oy);
3038 			len1 = sqrt(i * i + j * j) / (i > j ? i : j);
3039 
3040 			while (TRUE)
3041 			{
3042 				if (lstep + (1.0 / 65536.0) >= brush_spacing)
3043 				{
3044 					/* Drop error for 1-pixel step */
3045 					lstep = brush_spacing == 1 ? 0.0 :
3046 						lstep - brush_spacing;
3047 					if (cmd == TC_PASTE_COMMIT)
3048 					{
3049 						/* Adjust paste location */
3050 						marq_x2 += ncline[0] - marq_x1;
3051 						marq_y2 += ncline[1] - marq_y1;
3052 						marq_x1 = ncline[0];
3053 						marq_y1 = ncline[1];
3054 						commit_paste(pswap, update_area);
3055 					}
3056 					else if (!tool_draw(ncline[0], ncline[1],
3057 						first_point, update_area)) break;
3058 				}
3059 				if (line_step(ncline) < 0) break;
3060 				lstep += len1;
3061 			}
3062 			marq_x2 += ox - marq_x1;
3063 			marq_y2 += oy - marq_y1;
3064 			marq_x1 = ox;
3065 			marq_y1 = oy;
3066 		}
3067 
3068 		/* Convert update limits */
3069 		minx = update_area[0];
3070 		miny = update_area[1];
3071 		xw = update_area[2] - minx;
3072 		yh = update_area[3] - miny;
3073 		break;
3074 	}
3075 
3076 	if ((xw > 0) && (yh > 0)) /* Some drawing action */
3077 	{
3078 		if (xw + minx > mem_width) xw = mem_width - minx;
3079 		if (yh + miny > mem_height) yh = mem_height - miny;
3080 		if (minx < 0) xw += minx , minx = 0;
3081 		if (miny < 0) yh += miny , miny = 0;
3082 
3083 		if ((xw > 0) && (yh > 0))
3084 			lr_update_area(layer_selected, minx, miny, xw, yh);
3085 	}
3086 	tool_ox = x;	// Remember the coords just used as they are needed in continuous mode
3087 	tool_oy = y;
3088 
3089 	tint_mode[2] = 0; /* Default */
3090 	if (tablet_working || script_cmds) tool_state = o_tool;
3091 }
3092 
tool_action(int count,int button,int x,int y)3093 int tool_action(int count, int button, int x, int y)
3094 {
3095 	int cmd = TC_NONE;
3096 
3097 	/* Handle "exceptional" tools */
3098 	if (tool_type == TOOL_LINE)
3099 // !!! Is pressure sensitivity even useful here, or is it a misfeature?
3100 		cmd = button == 1 ? TC_LINE_START | TCF_PRES : TC_LINE_STOP;
3101 	else if ((tool_type == TOOL_SELECT) || (tool_type == TOOL_POLYGON))
3102 	{
3103 		// User wants to drag the paste box
3104 		if ((button == 1) || (button == 13) || (button == 2))
3105 		{
3106 			if ((marq_status == MARQUEE_PASTE_DRAG) ||
3107 				((marq_status == MARQUEE_PASTE) &&
3108 				(x >= marq_x1) && (x <= marq_x2) &&
3109 				(y >= marq_y1) && (y <= marq_y2)))
3110 				cmd = TC_PASTE_DRAG;
3111 		}
3112 		// User wants to commit the paste
3113 		if (((marq_status == MARQUEE_PASTE_DRAG) || (marq_status == MARQUEE_PASTE)) &&
3114 			(((button == 3) && (count == 1)) || ((button == 13) && !count)))
3115 			cmd = cmd == TC_PASTE_DRAG ? TC_PASTE_PAINT | TCF_PRES :
3116 				TC_PASTE_COMMIT | TCF_PRES;
3117 
3118 		if ((tool_type == TOOL_SELECT) && (button == 3) && (marq_status == MARQUEE_DONE))
3119 			cmd = TC_SEL_CLEAR;
3120 		if ((tool_type == TOOL_SELECT) && (button == 1) &&
3121 			((marq_status == MARQUEE_NONE) || (marq_status == MARQUEE_DONE)))
3122 			// Starting a selection
3123 		{
3124 			if (marq_status == MARQUEE_DONE)
3125 				cmd = TC_SEL_SET_0 + close_to(x, y);
3126 			else cmd = TC_SEL_START;
3127 		}
3128 		else
3129 		{
3130 			if (marq_status == MARQUEE_SELECTING)
3131 				cmd = TC_SEL_TO; // Continuing to make a selection
3132 		}
3133 
3134 		if (tool_type == TOOL_POLYGON)
3135 		{
3136 			if ((poly_status == POLY_NONE) && (marq_status == MARQUEE_NONE))
3137 			{
3138 				// Start doing something
3139 				if (button == 1) cmd = TC_POLY_START;
3140 				else if (button) cmd = TC_POLY_START_D;
3141 			}
3142 			if (poly_status == POLY_SELECTING)
3143 			{
3144 				/* Add another point to polygon */
3145 				if (button == 1) cmd = TC_POLY_ADD;
3146 				/* Stop adding points */
3147 				else if (button == 3) cmd = TC_POLY_CLOSE;
3148 			}
3149 			// Add point to polygon
3150 			if (poly_status == POLY_DRAGGING) cmd = TC_POLY_ADD;
3151 		}
3152 	}
3153 	else /* Some other kind of tool */
3154 	{
3155 		if (button == 1) cmd = TC_PAINT | TCF_PRES;
3156 		/* Does tool draw with color B when right button pressed? */
3157 		else if ((button == 3) && ((tool_type <= TOOL_SPRAY) ||
3158 			(tool_type == TOOL_FLOOD))) cmd = TC_PAINT_B | TCF_PRES;
3159 	}
3160 	return (cmd);
3161 }
3162 
check_marquee()3163 void check_marquee()	// Check marquee boundaries are OK - may be outside limits via arrow keys
3164 {
3165 	if (marq_status >= MARQUEE_PASTE)
3166 	{
3167 		marq_x1 = marq_x1 < 1 - mem_clip_w ? 1 - mem_clip_w :
3168 			marq_x1 > mem_width - 1 ? mem_width - 1 : marq_x1;
3169 		marq_y1 = marq_y1 < 1 - mem_clip_h ? 1 - mem_clip_h :
3170 			marq_y1 > mem_height - 1 ? mem_height - 1 : marq_y1;
3171 		marq_x2 = marq_x1 + mem_clip_w - 1;
3172 		marq_y2 = marq_y1 + mem_clip_h - 1;
3173 		return;
3174 	}
3175 	/* Reinit marquee from polygon bounds */
3176 	if (poly_status == POLY_DONE)
3177 		copy4(marq_xy, poly_xy);
3178 	else if (marq_status == MARQUEE_NONE) return;
3179 	/* Selection mode in operation */
3180 	marq_x1 = marq_x1 < 0 ? 0 : marq_x1 >= mem_width ? mem_width - 1 : marq_x1;
3181 	marq_x2 = marq_x2 < 0 ? 0 : marq_x2 >= mem_width ? mem_width - 1 : marq_x2;
3182 	marq_y1 = marq_y1 < 0 ? 0 : marq_y1 >= mem_height ? mem_height - 1 : marq_y1;
3183 	marq_y2 = marq_y2 < 0 ? 0 : marq_y2 >= mem_height ? mem_height - 1 : marq_y2;
3184 }
3185 
paint_poly_marquee(rgbcontext * ctx)3186 void paint_poly_marquee(rgbcontext *ctx)	// Paint polygon marquee
3187 {
3188 	if ((tool_type != TOOL_POLYGON) || !poly_points) return;
3189 // !!! Maybe check boundary clipping too
3190 	poly_update_cache();
3191 	draw_poly(poly_cache.xy, poly_cache.points, 0, margin_main_x, margin_main_y, ctx);
3192 }
3193 
3194 
repaint_clipped(int x0,int y0,int x1,int y1,const int * vxy)3195 static void repaint_clipped(int x0, int y0, int x1, int y1, const int *vxy)
3196 {
3197 	int rxy[4];
3198 
3199 	if (clip(rxy, x0, y0, x1, y1, vxy))
3200 		repaint_canvas(margin_main_x + rxy[0], margin_main_y + rxy[1],
3201 			rxy[2] - rxy[0], rxy[3] - rxy[1]);
3202 }
3203 
marquee_at(int * rect)3204 void marquee_at(int *rect)			// Read marquee location & size
3205 {
3206 	rect[0] = marq_x1 < marq_x2 ? marq_x1 : marq_x2;
3207 	rect[1] = marq_y1 < marq_y2 ? marq_y1 : marq_y2;
3208 	rect[2] = abs(marq_x2 - marq_x1) + 1;
3209 	rect[3] = abs(marq_y2 - marq_y1) + 1;
3210 }
3211 
locate_marquee(int * xy,int snap)3212 static void locate_marquee(int *xy, int snap)
3213 {
3214 	int rxy[4];
3215 	int x1, y1, x2, y2, w, h, zoom = 1, scale = 1;
3216 
3217 	if (snap && tgrid_snap)
3218 	{
3219 		copy4(rxy, marq_xy);
3220 		snap_xy(marq_xy);
3221 		if (marq_status < MARQUEE_PASTE)
3222 		{
3223 			snap_xy(marq_xy + 2);
3224 			marq_xy[(rxy[2] >= rxy[0]) * 2 + 0] += tgrid_dx - 1;
3225 			marq_xy[(rxy[3] >= rxy[1]) * 2 + 1] += tgrid_dy - 1;
3226 		}
3227 	}
3228 	check_marquee();
3229 
3230 	/* !!! This uses the fact that zoom factor is either N or 1/N !!! */
3231 	if (can_zoom < 1.0) zoom = rint(1.0 / can_zoom);
3232 	else scale = rint(can_zoom);
3233 
3234 	/* Get onscreen coords */
3235 	x1 = floor_div(marq_x1 * scale, zoom);
3236 	y1 = floor_div(marq_y1 * scale, zoom);
3237 	x2 = floor_div(marq_x2 * scale, zoom);
3238 	y2 = floor_div(marq_y2 * scale, zoom);
3239 	w = abs(x2 - x1) + scale;
3240 	h = abs(y2 - y1) + scale;
3241 	xy[2] = (xy[0] = x1 < x2 ? x1 : x2) + w;
3242 	xy[3] = (xy[1] = y1 < y2 ? y1 : y2) + h;
3243 }
3244 
3245 
paint_marquee(int action,int new_x,int new_y,rgbcontext * ctx)3246 void paint_marquee(int action, int new_x, int new_y, rgbcontext *ctx)
3247 {
3248 	int xy[4], vxy[4], nxy[4], rxy[4], clips[4 * 3];
3249 	int i, nc, rgb, rw, rh, offx, offy, wx, wy, mst = marq_status;
3250 
3251 
3252 	vxy[0] = vxy[1] = 0;
3253 	canvas_size(vxy + 2, vxy + 3);
3254 
3255 	locate_marquee(xy, action == MARQ_SNAP);
3256 	copy4(nxy, xy);
3257 	copy4(clips, xy);
3258 	nc = action < MARQ_HIDE ? 0 : 4; // No clear if showing anew
3259 
3260 	/* Determine which parts moved outside */
3261 	while (action >= MARQ_MOVE)
3262 	{
3263 		if (action == MARQ_MOVE) // Move
3264 		{
3265 			marq_x2 += new_x - marq_x1;
3266 			marq_x1 = new_x;
3267 			marq_y2 += new_y - marq_y1;
3268 			marq_y1 = new_y;
3269 		}
3270 		else marq_x2 = new_x , marq_y2 = new_y; // Resize
3271 		locate_marquee(nxy, TRUE);
3272 
3273 		/* No intersection? */
3274 		if (!clip(rxy, xy[0], xy[1], xy[2], xy[3], nxy)) break;
3275 
3276 		/* Horizontal slab */
3277 		if (rxy[1] > xy[1]) clips[3] = rxy[1]; // Top
3278 		else if (rxy[3] < xy[3]) clips[1] = rxy[3]; // Bottom
3279 		else nc = 0; // None
3280 
3281 		/* Inside area, if left unfilled */
3282 		if (!(show_paste && (mst >= MARQUEE_PASTE)))
3283 		{
3284 			clips[nc + 0] = nxy[0] + 1;
3285 			clips[nc + 1] = nxy[1] + 1;
3286 			clips[nc + 2] = nxy[2] - 1;
3287 			clips[nc + 3] = nxy[3] - 1;
3288 			nc += 4;
3289 		}
3290 
3291 		/* Vertical block */
3292 		if (rxy[0] > xy[0]) // Left
3293 			clips[nc + 0] = xy[0] , clips[nc + 2] = rxy[0];
3294 		else if (rxy[2] < xy[2]) // Right
3295 			clips[nc + 0] = rxy[2] , clips[nc + 2] = xy[2];
3296 		else break; // None
3297 		clips[nc + 1] = rxy[1]; clips[nc + 3] = rxy[3];
3298 		nc += 4;
3299 		break;
3300 	}
3301 
3302 	/* Clear - only happens in void context */
3303 	marq_status = 0;
3304 	for (i = 0; i < nc; i += 4)
3305 	{
3306 		/* Clip to visible portion */
3307 		if (!clip(rxy, clips[i + 0], clips[i + 1],
3308 			clips[i + 2], clips[i + 3], vxy)) continue;
3309 		/* Redraw entire area */
3310 		if (show_paste && (mst >= MARQUEE_PASTE))
3311 			repaint_clipped(xy[0], xy[1], xy[2], xy[3], rxy);
3312 		/* Redraw only borders themselves */
3313 		else
3314 		{
3315 			repaint_clipped(xy[0], xy[1] + 1, xy[0] + 1, xy[3] - 1, rxy);
3316 			repaint_clipped(xy[2] - 1, xy[1] + 1, xy[2], xy[3] - 1, rxy);
3317 			repaint_clipped(xy[0], xy[1], xy[2], xy[1] + 1, rxy);
3318 			repaint_clipped(xy[0], xy[3] - 1, xy[2], xy[3], rxy);
3319 		}
3320 	}
3321 	marq_status = mst;
3322 	if (action == MARQ_HIDE) return; // All done for clear
3323 
3324 	/* Determine visible area */
3325 	if (ctx) clip(vxy, ctx->xy[0] - margin_main_x, ctx->xy[1] - margin_main_y,
3326 		ctx->xy[2] - margin_main_x, ctx->xy[3] - margin_main_y, vxy);
3327 	if (!clip(rxy, nxy[0], nxy[1], nxy[2], nxy[3], vxy)) return;
3328 
3329 	/* Draw */
3330 	rgb = RGB_2_INT(255, 0, 0); /* Draw in red */
3331 	if (marq_status >= MARQUEE_PASTE)
3332 	{
3333 		/* Display paste RGB, only if not being called from repaint_canvas */
3334 		if (show_paste && !ctx) repaint_clipped(nxy[0] + 1, nxy[1] + 1,
3335 			nxy[2] - 1, nxy[3] - 1, vxy);
3336 		rgb = RGB_2_INT(0, 0, 255); /* Draw in blue */
3337 	}
3338 
3339 	rw = rxy[2] - rxy[0];
3340 	rh = rxy[3] - rxy[1];
3341 	wx = margin_main_x + rxy[0];
3342 	wy = margin_main_y + rxy[1];
3343 	offx = rxy[0] - nxy[0];
3344 	offy = rxy[1] - nxy[1];
3345 
3346 	if ((nxy[0] >= rxy[0]) && (marq_x1 >= 0) && (marq_x2 >= 0))
3347 		draw_dash(rgb, RGB_2_INT(255, 255, 255), offy,
3348 			wx, wy, 1, rh, ctx);
3349 
3350 	if ((nxy[2] <= rxy[2]) && (marq_x1 < mem_width) && (marq_x2 < mem_width))
3351 		draw_dash(rgb, RGB_2_INT(255, 255, 255), offy,
3352 			wx + rw - 1, wy, 1, rh, ctx);
3353 
3354 	if ((nxy[1] >= rxy[1]) && (marq_y1 >= 0) && (marq_y2 >= 0))
3355 		draw_dash(rgb, RGB_2_INT(255, 255, 255), offx,
3356 			wx, wy, rw, 1, ctx);
3357 
3358 	if ((nxy[3] <= rxy[3]) && (marq_y1 < mem_height) && (marq_y2 < mem_height))
3359 		draw_dash(rgb, RGB_2_INT(255, 255, 255), offx,
3360 			wx, wy + rh - 1, rw, 1, ctx);
3361 }
3362 
3363 
close_to(int x1,int y1)3364 int close_to( int x1, int y1 )		// Which corner of selection is coordinate closest to?
3365 {
3366 	return ((x1 + x1 <= marq_x1 + marq_x2 ? 0 : 1) +
3367 		(y1 + y1 <= marq_y1 + marq_y2 ? 0 : 2));
3368 }
3369 
3370 
line_get_xy(int xy[4],linedata line,int y1)3371 static void line_get_xy(int xy[4], linedata line, int y1)
3372 {
3373 	int k, x0, x1, y;
3374 
3375 	xy[1] = y = line[1];
3376 	x0 = x1 = line[0];
3377 	while ((line[1] < y1) && (line_step(line) >= 0))
3378 		x1 = line[0] , y = line[1];
3379 	xy[3] = y;
3380 	k = (x0 > x1) * 2; xy[k] = x0; xy[k ^ 2] = x1;
3381 }
3382 
repaint_xy(int xy[4],int scale)3383 static void repaint_xy(int xy[4], int scale)
3384 {
3385 	repaint_canvas(margin_main_x + xy[0] * scale, margin_main_y + xy[1] * scale,
3386 		(xy[2] - xy[0] + 1) * scale, (xy[3] - xy[1] + 1) * scale);
3387 }
3388 
3389 // !!! For now, this function is hardcoded to merge 2 areas
merge_xy(int cnt,int * xy,int step)3390 static int merge_xy(int cnt, int *xy, int step)
3391 {
3392 	if ((xy[0 + 0] > xy[4 + 2] + step + 1) ||
3393 		(xy[4 + 0] > xy[0 + 2] + step + 1)) return (2);
3394 	if (xy[0 + 0] > xy[4 + 0]) xy[0 + 0] = xy[4 + 0];
3395 	if (xy[0 + 1] > xy[4 + 1]) xy[0 + 1] = xy[4 + 1];
3396 	if (xy[0 + 2] < xy[4 + 2]) xy[0 + 2] = xy[4 + 2];
3397 	if (xy[0 + 3] < xy[4 + 3]) xy[0 + 3] = xy[4 + 3];
3398 	return (1);
3399 }
3400 
3401 /* Only 2 line-quads for now, but will be extended to 4 for line-join drag */
refresh_lines(const int xy0[4],const int xy1[4])3402 static void refresh_lines(const int xy0[4], const int xy1[4])
3403 {
3404 	linedata ll1, ll2;
3405 	int ixy[4], getxy[8], *lines[2] = { ll1, ll2 };
3406 	int i, j, y, y1, y2, cnt, step, zoom = 1, scale = 1;
3407 
3408 	if (cmd_mode) return;
3409 	/* !!! This uses the fact that zoom factor is either N or 1/N !!! */
3410 	if (can_zoom < 1.0) zoom = rint(1.0 / can_zoom);
3411 	else scale = rint(can_zoom);
3412 
3413 	// !!! Could use CSCROLL_XYSIZE here instead
3414 	cmd_peekv(drawing_canvas, ixy, sizeof(ixy), CANVAS_VPORT);
3415 	prepare_line_clip(ixy, ixy, scale);
3416 
3417 	for (i = j = 0; j < 2; j++)
3418 	{
3419 		const int *xy;
3420 		int tmp;
3421 
3422 		xy = j ? xy1 : xy0;
3423 		if (!xy) continue;
3424 		line_init(lines[i],
3425 			floor_div(xy[0], zoom), floor_div(xy[1], zoom),
3426 			floor_div(xy[2], zoom), floor_div(xy[3], zoom));
3427 		if (line_clip(lines[i], ixy, &tmp) < 0) continue;
3428 		if (lines[i][9] < 0) line_flip(lines[i]);
3429 		i++;
3430 	}
3431 
3432 	step = scale < 8 ? (16 + scale - 1) / scale : 2;
3433 	for (cnt = i , y1 = ixy[1]; cnt; y1 += step)
3434 	{
3435 		y2 = ixy[3] + 1;
3436 		for (j = i = 0; i < cnt; i++)
3437 		{
3438 			y = lines[i][1];
3439 			if (lines[i][2] < 0) // Remove used-up line
3440 			{
3441 				lines[i--] = lines[--cnt];
3442 			}
3443 			else if (y >= y1) // Remember not-yet-started line
3444 			{
3445 				if (y2 > y) y2 = y;
3446 			}
3447 			else line_get_xy(getxy + j++ * 4, lines[i], y1);
3448 		}
3449 		if (j)
3450 		{
3451 			if (j > 1) j = merge_xy(j, getxy, step);
3452 			for (i = 0; i < j; i++)
3453 				repaint_xy(getxy + i * 4, scale);
3454 		}
3455 		else y1 = y2;
3456 	}
3457 }
3458 
render_line(int mode,linedata line,int ofs,rgbcontext * ctx)3459 static void render_line(int mode, linedata line, int ofs, rgbcontext *ctx)
3460 {
3461 	int rxy[4], cxy[4];
3462 	int i, j, x, y, tx, ty, w3, rgb = 0, scale = 1;
3463 
3464 	/* !!! This uses the fact that zoom factor is either N or 1/N !!! */
3465 	if (can_zoom > 1.0) scale = rint(can_zoom);
3466 
3467 	copy4(cxy, ctx->xy);
3468 	w3 = (cxy[2] - cxy[0]) * 3;
3469 	for (i = ofs; line[2] >= 0; line_step(line) , i++)
3470 	{
3471 		x = (tx = line[0]) * scale + margin_main_x;
3472 		y = (ty = line[1]) * scale + margin_main_y;
3473 
3474 		if (mode == 1) /* Drawing */
3475 		{
3476 			j = ((ty & 7) * 8 + (tx & 7)) * 3;
3477 			rgb = MEM_2_INT(mem_col_pat24, j);
3478 		}
3479 		else if (mode == 2) /* Tracking */
3480 		{
3481 			rgb = ((i >> 2) & 1) * 0xFFFFFF;
3482 		}
3483 		else if (mode == 3) /* Gradient */
3484 		{
3485 			rgb = ((i >> 2) & 1) * 0xFFFFFF ^
3486 				((i >> 1) & 1) * 0x00FF00;
3487 		}
3488 		if (clip(rxy, x, y, x + scale, y + scale, cxy))
3489 		{
3490 			unsigned char *dest, *tmp;
3491 			int i, h, l;
3492 
3493 			tmp = dest = ctx->rgb + (rxy[1] - cxy[1]) * w3 +
3494 				(rxy[0] - cxy[0]) * 3;
3495 			*tmp++ = INT_2_R(rgb);
3496 			*tmp++ = INT_2_G(rgb);
3497 			*tmp++ = INT_2_B(rgb);
3498 			l = (rxy[2] - rxy[0]) * 3;
3499 			for (i = l - 3; i; i-- , tmp++)
3500 				*tmp = *(tmp - 3);
3501 			tmp = dest;
3502 			for (h = rxy[3] - rxy[1] - 1; h; h--)
3503 			{
3504 				tmp += w3;
3505 				memcpy(tmp, dest, l);
3506 			}
3507 		}
3508 	}
3509 }
3510 
repaint_grad(const int * old)3511 void repaint_grad(const int *old)
3512 {
3513 	refresh_lines(gradient[mem_channel].xy, old);
3514 }
3515 
repaint_line(const int * old)3516 void repaint_line(const int *old)
3517 {
3518 	refresh_lines(line_xy, old);
3519 }
3520 
3521 /* lxy is ctx's bounds scaled to "line space" (unchanged for zoom < 0, image
3522  * coordinates otherwise) */
refresh_line(int mode,const int * lxy,rgbcontext * ctx)3523 void refresh_line(int mode, const int *lxy, rgbcontext *ctx)
3524 {
3525 	linedata line;
3526 	int j, zoom = 1, *xy;
3527 
3528 	/* !!! This uses the fact that zoom factor is either N or 1/N !!! */
3529 	if (can_zoom < 1.0) zoom = rint(1.0 / can_zoom);
3530 
3531 	xy = mode == 3 ? gradient[mem_channel].xy : line_xy;
3532 	line_init(line, floor_div(xy[0], zoom), floor_div(xy[1], zoom),
3533 		floor_div(xy[2], zoom), floor_div(xy[3], zoom));
3534 	if (line_clip(line, lxy, &j) >= 0) render_line(mode, line, j, ctx);
3535 }
3536 
update_recent_files(int save)3537 void update_recent_files(int save)		// Update the menu items
3538 {
3539 	char txt[64], *t, txt2[PATHTXT];
3540 	int i, j;
3541 
3542 	/* Init from inifile, skipping empty lines */
3543 	if (!recent_filenames[0])
3544 	{
3545 		for (i = j = 0; i < MAX_RECENT; i++)
3546 		{
3547 			sprintf(txt, "file%i", i + 1);
3548 			t = inifile_get(txt, "");
3549 			if (t[0]) recent_filenames[j++] = strdup(t);
3550 		}
3551 		while (j < MAX_RECENT) recent_filenames[j++] = "";
3552 	}
3553 
3554 	if (save) /* Store to inifile */
3555 	{
3556 // !!! Or maybe set everything from recent_files to ""?
3557 		for (i = 0; i < MAX_RECENT; i++)
3558 		{
3559 			sprintf(txt, "file%i", i + 1);
3560 			inifile_set(txt, recent_filenames[i]);
3561 		}
3562 		return;
3563 	}
3564 
3565 	/* Display recent filenames */
3566 	for (i = 0; i < recent_files; i++)
3567 	{
3568 		if (!recent_filenames[i][0]) break; // Should only be at end
3569 		gtkuncpy(txt2, recent_filenames[i], PATHTXT);
3570 		cmd_setv(menu_slots[MENU_RECENT1 + i], txt2, LABEL_VALUE);
3571 		cmd_showhide(menu_slots[MENU_RECENT1 + i], TRUE); // Show
3572 	}
3573 	/* Hide separator if not needed */
3574 	cmd_showhide(menu_slots[MENU_RECENT_S], i);
3575 	/* Hide extra items */
3576 	for (; i < MAX_RECENT; i++) cmd_showhide(menu_slots[MENU_RECENT1 + i], FALSE);
3577 }
3578 
register_file(char * filename)3579 void register_file(char *filename)	// Called after successful load/save
3580 {
3581 	char *c;
3582 	int i;
3583 
3584 	if (!filename[0]) return;	// Paranoia
3585 	c = strrchr(filename, DIR_SEP);
3586 	if (c)
3587 	{
3588 		i = *c;
3589 		*c = '\0';		// Strip off filename
3590 		inifile_set("last_dir", filename);
3591 		*c = i;
3592 	}
3593 
3594 	/* Is it already in used file list? */
3595 	for (i = 0; i < MAX_RECENT; i++)
3596 		if (!strcmp(recent_filenames[i], filename)) break;
3597 	/* Copy it into LAST slot if not */
3598 	if (i >= MAX_RECENT)
3599 	{
3600 		i = MAX_RECENT - 1;
3601 		if (recent_filenames[i][0]) free(recent_filenames[i]);
3602 		recent_filenames[i] = strdup(filename);
3603 	}
3604 	/* Now rotate it to the top if not already there */
3605 	if (i > 0)
3606 	{
3607 		filename = recent_filenames[i];
3608 		memmove(recent_filenames + 1, recent_filenames,
3609 			i * sizeof(recent_filenames[0]));
3610 		recent_filenames[0] = filename;
3611 		update_recent_files(FALSE);
3612 	}
3613 }
3614 
create_default_image()3615 void create_default_image()			// Create default new image
3616 {
3617 	int	nw = inifile_get_gint32("lastnewWidth", DEFAULT_WIDTH ),
3618 		nh = inifile_get_gint32("lastnewHeight", DEFAULT_HEIGHT ),
3619 		nc = inifile_get_gint32("lastnewCols", 256 ),
3620 		nt = inifile_get_gint32("lastnewType", 2 );
3621 
3622 	do_new_one(nw, nh, nc, nt == 1 ? NULL : mem_pal_def,
3623 		(nt == 0) || (nt > 2) ? 3 : 1, FALSE);
3624 }
3625