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