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