/* shifter.c Copyright (C) 2006 Mark Tyler This file is part of mtPaint. mtPaint is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. mtPaint is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mtPaint in the file COPYING. */ #include #include #include #include #include #include "global.h" #include "memory.h" #include "png.h" #include "mainwindow.h" #include "mygtk.h" #include "canvas.h" #include "shifter.h" static GtkWidget *shifter_window, *shifter_spin[8][3], *shifter_slider, *shifter_label; static png_color sh_old_pal[256]; static int shifter_in[8][3], shifter_pos, shifter_max, shift_play_state, shift_timer_state; static void pal_shift( int a, int b, int shift ) // Shift palette between a & b shift times { int dir = (a>b) ? -1 : 1, minab, maxab, i, j; if ( a == b ) return; // a=b => so nothing to do mtMIN(minab, a, b ) mtMAX(maxab, a, b ) shift = shift % (maxab-minab+1); j = minab + dir*shift; while ( j>maxab ) j = j - (maxab-minab+1); while ( jmaxab ) j=minab; } } static void shifter_set_palette(int pos) // Set current palette to a given position in cycle { int i, pos2; if ( pos<0 || pos>=shifter_max ) return; // Ensure sanity mem_pal_copy( mem_pal, sh_old_pal ); if ( pos==0 ) return; // pos=0 => original state for ( i=0; i<8; i++ ) // Implement each of the shifts { pos2 = pos / (shifter_in[i][2]+1); // Normalize the position shift for delay pal_shift( shifter_in[i][0], shifter_in[i][1], pos2 ); } } static gboolean shift_play_timer_call() { int i; if ( shift_play_state == 0 ) { shift_timer_state = 0; return FALSE; // Stop animating } else { i = mt_spinslide_get_value(shifter_slider); i++; if ( i>=shifter_max ) i=0; shifter_set_palette( i ); mt_spinslide_set_value(shifter_slider, i); return TRUE; } } static void shift_play_start() { if ( shift_play_state == 0 ) { shift_play_state = 1; if ( shift_timer_state == 0 ) shift_timer_state = g_timeout_add( 100, shift_play_timer_call, NULL ); } } static void shift_play_stop() { shift_play_state = 0; } static void shift_but_playstop( GtkWidget *widget ) { gboolean play = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if ( play ) shift_play_start(); else shift_play_stop(); } static void shifter_slider_moved() // Slider position changed { int pos = mt_spinslide_read_value(shifter_slider); if ( pos != shifter_pos ) { shifter_pos = pos; shifter_set_palette(pos); canvas_undo_chores(); } } static int gcd( int a, int b ) { int c; if ( b>a ) { c=a; a=b; b=c; } // Ensure a>=b while ( b>0 ) { c = b; b = a % b; a = c; } return a; } static void shifter_moved() // An input widget has changed in the dialog { char txt[130]; int i, j, fr[8], fs[8], tot, s1, s2, p, lcm, lcm2; for ( i=0; i<8; i++ ) { for ( j=0; j<3; j++ ) { shifter_in[i][j] = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(shifter_spin[i][j]) ); } if ( shifter_in[i][0] != shifter_in[i][1] ) fr[i] = (1 + abs(shifter_in[i][0] - shifter_in[i][1])) * (shifter_in[i][2] + 1); else fr[i] = 0; // Total frames needed for shifts, including delays } tot = 0; for ( i=0; i<8; i++ ) { if ( fr[i]>1 ) { fs[tot] = fr[i]; tot++; } } if ( tot<2 ) { if ( tot==1 ) lcm=fs[0]; else lcm=1; } else { // Calculate the lowest common multiple for all of the numbers i = 2; j = tot; s1 = fs[0]; s2 = fs[1]; p = s1 * s2; s1 = gcd( s1, s2 ); lcm = p / s1; while ( i < j ) { s2 = fs[i]; p = lcm * s2; s1 = gcd(s1, s2); lcm2 = gcd(lcm, s2); lcm = p / lcm2; i++; } } mt_spinslide_set_range(shifter_slider, 0, lcm-1); // Set min/max value of slider shifter_max = lcm; snprintf(txt, 128, "%s = %i", _("Frames"), lcm); gtk_label_set_text( GTK_LABEL(shifter_label), txt ); if ( shifter_pos >= lcm ) mt_spinslide_set_value(shifter_slider, 0); // Re-centre the slider if its out of range on the new scale } static void click_shift_fix() // Button to fix palette pressed { int i = mt_spinslide_get_value(shifter_slider); if ( i==0 || i>=shifter_max ) return; // Nothing to do mem_pal_copy( mem_pal, sh_old_pal ); spot_undo(UNDO_PAL); shifter_set_palette(i); mem_pal_copy( sh_old_pal, mem_pal ); mt_spinslide_set_value(shifter_slider, 0); canvas_undo_chores(); } static void click_shift_close() // Palette Shifter window closed by user or WM { mem_pal_copy( mem_pal, sh_old_pal ); canvas_undo_chores(); shift_play_stop(); gtk_widget_destroy( shifter_window ); } static void click_shift_clear() // Button to clear all of the values { int i, j; for ( i=0; i<8; i++ ) for ( j=0; j<3; j++ ) gtk_spin_button_set_value(GTK_SPIN_BUTTON(shifter_spin[i][j]),0); } static void click_shift_create() // Button to create a sequence of undo images { int i; if ( shifter_max<2 ) return; // Nothing to do for ( i=0; ientry ), "value_changed", GTK_SIGNAL_FUNC(shifter_moved), NULL); #else gtk_signal_connect( GTK_OBJECT( >K_SPIN_BUTTON(shifter_spin[i][j])->entry ), "changed", GTK_SIGNAL_FUNC(shifter_moved), NULL); #endif } } hbox = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); button = add_a_toggle(_("Play"), hbox, FALSE); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(shift_but_playstop), NULL); shift_play_state = FALSE; // Stopped shifter_label = gtk_label_new(""); gtk_widget_show( shifter_label ); gtk_misc_set_alignment( GTK_MISC(shifter_label), 0.5, 0.5 ); gtk_box_pack_start( GTK_BOX(hbox), shifter_label, TRUE, TRUE, 0 ); shifter_slider = mt_spinslide_new( -1, -1 ); mt_spinslide_set_range(shifter_slider, 0, 0); mt_spinslide_set_value(shifter_slider, 0); mt_spinslide_connect(shifter_slider, GTK_SIGNAL_FUNC(shifter_slider_moved), NULL); gtk_box_pack_start(GTK_BOX(vbox), shifter_slider, TRUE, TRUE, 5); add_hseparator( vbox, -2, 10 ); hbox = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); button = gtk_button_new_with_label(_("Clear")); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_shift_clear), NULL); button = gtk_button_new_with_label(_("Fix Palette")); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_shift_fix), NULL); button = gtk_button_new_with_label(_("Create Frames")); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_shift_create), NULL); button = gtk_button_new_with_label(_("Close")); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); gtk_widget_show(button); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(click_shift_close), NULL ); gtk_widget_add_accelerator (button, "clicked", ag, GDK_Escape, 0, (GtkAccelFlags) 0); gtk_signal_connect_object(GTK_OBJECT(shifter_window), "delete_event", GTK_SIGNAL_FUNC(click_shift_close), NULL ); gtk_widget_show(shifter_window); gtk_window_add_accel_group(GTK_WINDOW (shifter_window), ag); /* Reset shortened slider in GTK1 */ #if GTK_MAJOR_VERSION == 1 gtk_widget_queue_resize(shifter_window); #endif mem_pal_copy( sh_old_pal, mem_pal ); // Backup the current palette shifter_pos = 0; shifter_max = 0; shifter_moved(); // Initialize the input array }