1 /*	shifter.c
2 	Copyright (C) 2006-2016 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 
22 #include "mygtk.h"
23 #include "memory.h"
24 #include "vcode.h"
25 #include "png.h"
26 #include "mainwindow.h"
27 #include "canvas.h"
28 #include "shifter.h"
29 
30 #define NSHIFT 8
31 
32 typedef struct {
33 	int frame[3];
34 	int row, sfd[3];
35 	png_color old_pal[256];
36 	void **spinpack, **label, **slider;
37 	void **clear, **fix, **create;
38 } shifter_dd;
39 
40 
41 static int shift_play_state, shift_timer_state, spins[NSHIFT][3][3];
42 
43 
44 /* Shift palette between a & b shift times */
pal_shift(png_color * old_pal,int a,int b,int shift)45 static void pal_shift(png_color *old_pal, int a, int b, int shift)
46 {
47 	int i, j, d, dir = 1;
48 
49 	if (a == b) return;			// a=b => so nothing to do
50 	if (a > b) dir = a , a = b , b = dir , dir = -1;
51 	d = b - a + 1;
52 
53 	shift %= d;
54 	j = a + dir * shift;
55 	if (j < a) j += d;
56 
57 	for (i = a; i <= b; i++)
58 	{
59 		mem_pal[i] = old_pal[j];
60 		if (++j > b) j = a;
61 	}
62 }
63 
64 
65 /* Set current palette to a given position in cycle */
shifter_set_palette(shifter_dd * dt,int pos)66 static void shifter_set_palette(shifter_dd *dt, int pos)
67 {
68 	int i, pos2;
69 
70 	mem_pal_copy(mem_pal, dt->old_pal);
71 	if (!pos) return;			// pos=0 => original state
72 
73 	for (i = 0; i < NSHIFT; i++)		// Implement each of the shifts
74 	{
75 		// Normalize the position shift for delay
76 		pos2 = pos / (spins[i][2][0] + 1);
77 		pal_shift(dt->old_pal, spins[i][0][0], spins[i][1][0], pos2);
78 	}
79 }
80 
shift_play_timer_call(gpointer data)81 static gboolean shift_play_timer_call(gpointer data)
82 {
83 	if (!shift_play_state)
84 	{
85 		shift_timer_state = 0;
86 		return FALSE;			// Stop animating
87 	}
88 	else
89 	{
90 		shifter_dd *dt = data;
91 		int i;
92 
93 		cmd_read(dt->slider, dt);
94 		i = dt->frame[0] + 1;
95 		if (i > dt->frame[2]) i = dt->frame[1];
96 		cmd_set(dt->slider, i);
97 		return TRUE;
98 	}
99 }
100 
101 
shifter_slider_moved(shifter_dd * dt,void ** wdata,int what,void ** where)102 static void shifter_slider_moved(shifter_dd *dt, void **wdata, int what,
103 	void **where)
104 {
105 	cmd_read(where, dt);
106 
107 	shifter_set_palette(dt, dt->frame[0]);
108 	update_stuff(UPD_PAL);
109 }
110 
111 
gcd(int a,int b)112 static int gcd(int a, int b)
113 {
114 	int c;
115 
116 	while (b > 0)
117 	{
118 		c = b;
119 		b = a % b;
120 		a = c;
121 	}
122 
123 	return (a);
124 }
125 
reset_frames(shifter_dd * dt)126 static void reset_frames(shifter_dd *dt)
127 {
128 	char txt[130];
129 	int i, j, l, lcm;
130 
131 	for (lcm = 1 , i = 0; i < NSHIFT; i++)
132 	{
133 		l = spins[i][0][0] - spins[i][1][0];
134 		if (!l) continue;
135 		// Total frames needed for shifts, including delays
136 		l = (1 + abs(l)) * (spins[i][2][0] + 1);
137 		// Calculate the lowest common multiple for all of the numbers
138 		j = gcd(lcm, l);
139 		lcm = (lcm / j) * l;
140 	}
141 
142 	dt->frame[2] = lcm - 1;
143 	// Re-centre the slider if its out of range on the new scale
144 	if (dt->frame[0] >= lcm) dt->frame[0] = 0;
145 	cmd_setv(dt->slider, dt->frame, SPIN_ALL); // Set min/max value of slider
146 
147 	snprintf(txt, 128, "%s = %i", _("Frames"), lcm);
148 	cmd_setv(dt->label, txt, LABEL_VALUE);
149 }
150 
151 /* An input widget has changed in the dialog */
shifter_moved(shifter_dd * dt,void ** wdata,int what,void ** where)152 static void shifter_moved(shifter_dd *dt, void **wdata, int what, void **where)
153 {
154 	int i, j, *v = cmd_read(where, dt);
155 
156 	/* Scriptable parts */
157 	if (v == &dt->row) return;
158 	if (v && (v != spins[0][0]))
159 	{
160 		j = v - dt->sfd;
161 		i = spins[dt->row][j][2];
162 		spins[dt->row][j][0] = *v < i ? *v : i;
163 	}
164 
165 	reset_frames(dt);
166 }
167 
shift_all(shifter_dd * dt,void ** wdata,int what,void ** where,multi_ext * mx)168 static int shift_all(shifter_dd *dt, void **wdata, int what, void **where,
169 	multi_ext *mx)
170 {
171 	int i, j, rows = mx->nrows;
172 
173 	/* Sanity check */
174 	if ((rows > NSHIFT) || (mx->ncols > 3) || (mx->mincols < 3) ||
175 		(mx->fractcol >= 0)) return (0); // Error
176 
177 	for (i = 0; i < rows; i++)
178 	{
179 		int *row = mx->rows[i] + 1;
180 		for (j = 0; j < 3; j++)
181 			spins[i][j][0] = bounded(row[j], 0, spins[i][j][2]);
182 	}
183 	for (; i < NSHIFT; i++)
184 		spins[i][0][0] = spins[i][1][0] = spins[i][2][0] = 0;
185 
186 	reset_frames(dt);
187 
188 	return (1);
189 }
190 
shift_btn(shifter_dd * dt,void ** wdata,int what,void ** where)191 static void shift_btn(shifter_dd *dt, void **wdata, int what, void **where)
192 {
193 	int i;
194 
195 	if ((what == op_EVT_OK) || (what == op_EVT_CANCEL))
196 	{
197 		shift_play_state = FALSE; // Stop
198 
199 		mem_pal_copy(mem_pal, dt->old_pal);
200 		update_stuff(UPD_PAL);
201 
202 		run_destroy(wdata);
203 		return;
204 	}
205 
206 	if (what == op_EVT_CHANGE) // Play toggle
207 	{
208 		cmd_read(where, dt);
209 		if (shift_play_state && !shift_timer_state) // Start timer
210 			shift_timer_state = threads_timeout_add(100,
211 				shift_play_timer_call, dt);
212 		return;
213 	}
214 
215 	where = origin_slot(where);
216 
217 	if (where == dt->fix)	// Button to fix palette pressed
218 	{
219 		i = dt->frame[0];
220 		if (!i || (i > dt->frame[2])) return; // Nothing to do
221 
222 		mem_pal_copy(mem_pal, dt->old_pal);
223 		spot_undo(UNDO_PAL);
224 		shifter_set_palette(dt, i);
225 		mem_pal_copy(dt->old_pal, mem_pal);
226 		cmd_set(dt->slider, 0);
227 		update_stuff(UPD_PAL);
228 	}
229 
230 	else if (where == dt->clear)	// Button to clear all of the values
231 	{
232 		for (i = 0; i < NSHIFT; i++)
233 			spins[i][0][0] = spins[i][1][0] = spins[i][2][0] = 0;
234 		cmd_reset(dt->spinpack, dt);
235 		shifter_moved(dt, wdata, op_EVT_CHANGE, dt->spinpack);
236 	}
237 
238 	else if (where == dt->create)	// Button to create a sequence of undo images
239 	{
240 		if (!dt->frame[2]) return;	// Nothing to do
241 
242 		for (i = 0; i <= dt->frame[2]; i++)
243 		{
244 			shifter_set_palette(dt, i);
245 			spot_undo(UNDO_PAL);
246 		}
247 		shifter_set_palette(dt, dt->frame[0]);
248 		update_stuff(UPD_PAL);
249 	}
250 }
251 
252 
253 #undef _
254 #define _(X) X
255 
256 #define WBbase shifter_dd
257 static void *shifter_code[] = {
258 	WINDOWm(_("Palette Shifter")),
259 	XVBOXB, // !!! Originally the main vbox was that way
260 	TABLE(4, 9),
261 	BORDER(LABEL, 0),
262 	uSPIN(row, 0, 7), EVENT(CHANGE, shifter_moved), EVENT(MULTI, shift_all),
263 	TLLABELx(_("Start"), 1, 0, 0, 0, 5),
264 		uSPIN(sfd[0], 0, 255), EVENT(CHANGE, shifter_moved),
265 	TLLABELx(_("Finish"), 2, 0, 0, 0, 5),
266 		uSPIN(sfd[1], 0, 255), EVENT(CHANGE, shifter_moved),
267 	TLLABELx(_("Delay"), 3, 0, 0, 0, 5),
268 		uSPIN(sfd[2], 0, 255), EVENT(CHANGE, shifter_moved),
269 	DEFBORDER(LABEL),
270 	TLTEXT("0\n1\n2\n3\n4\n5\n6\n7", 0, 1),
271 	REF(spinpack), TLSPINPACKv(spins, 3 * NSHIFT, shifter_moved, 3, 1, 1),
272 	TRIGGER,
273 	WDONE,
274 	HBOX,
275 	UTOGGLEv(_("Play"), shift_play_state, shift_btn), UNNAME,
276 	BORDER(LABEL, 0),
277 	REF(label), XLABELcr(""),
278 	WDONE,
279 	REF(slider), SPINSLIDEa(frame), OPNAME("Play"),
280 	EVENT(CHANGE, shifter_slider_moved),
281 	HSEP,
282 	HBOX,
283 	REF(clear), BUTTONs(_("Clear"), shift_btn),
284 	REF(fix), BUTTONs(_("Fix Palette"), shift_btn),
285 	REF(create), BUTTONs(_("Create Frames"), shift_btn),
286 	CANCELBTN(_("Close"), shift_btn), uOKBTN(shift_btn),
287 	WDONE,
288 // !!! Transient windows cannot be minimized; don't know if that's desirable here
289 	ONTOP0,
290 	WSHOW
291 };
292 #undef WBbase
293 
294 #undef _
295 #define _(X) __(X)
296 
pressed_shifter()297 void pressed_shifter()
298 {
299 	shifter_dd tdata;
300 	int i;
301 
302 	memset(&tdata, 0, sizeof(tdata));
303 	mem_pal_copy(tdata.old_pal, mem_pal);
304 	for (i = 0; i < NSHIFT; i++)
305 	{
306 		spins[i][0][2] = spins[i][1][2] = mem_cols - 1;
307 		spins[i][2][2] = 255;
308 	}
309 
310 	shift_play_state = FALSE; // Stopped
311 
312 	run_create_(shifter_code, &tdata, sizeof(tdata), script_cmds);
313 }
314