#!/usr/bin/perl -w #d

# GUI front-end to jpegtran to rotate and flip jpegs without loss
#
# 2000-06-14 Dobrica Pavlinusic <dpavlin@rot13.org>

use Gtk;
use strict;

init Gtk;
set_locale Gtk;

my $DEBUG = 1;
sub pdebug { print STDERR @_,"\n" if ($DEBUG); }

my $false = 0;
my $true = 1;

# $X[0](dir|filename|w|h|pix)
my @X;

my $xv_pic_dir=".xvpics";
#$xv_pic_dir="/mnt/f/tmp2/PICS/.xvpics" if ($DEBUG);

if (! $ARGV[0] && !-d ".xvpics") {
	print "Usage: $0 [path to directory with pictures (in which is .xvpics dir)]\n";
	exit 1;
}

$xv_pic_dir = $ARGV[0]."/.xvpics" if ($ARGV[0]);

if (! -d $xv_pic_dir) {
	print "$xv_pic_dir is not directory!";
}

#--------------------------------------------------------------------
# scan directory

pdebug "reading dir $xv_pic_dir";

opendir(DIR, $xv_pic_dir) || die "can't opendir $xv_pic_dir: $!";
my @pic_filenames = grep { !/^\./ && -f "$xv_pic_dir/$_" } readdir(DIR);
closedir(DIR);

pdebug "found $#pic_filenames files in $xv_pic_dir";

my $dir=$xv_pic_dir;
$dir=~s/\.xvpics//;

foreach my $filename (@pic_filenames) {
	pdebug "gronk $filename";
	my ($w,$h,$pix) = xvpic_read("$xv_pic_dir/$filename");
	my (%tmp) = ( dir=>$dir, filename=>$filename, w=>$w, h=>$h, pix=>$pix );
	push @X,\%tmp;
}

#--------------------------------------------------------------------
# read xvpic from disk
#	xvpic_read($filename)
#	returns:
#		($w,$h,$xv_pix)

sub xvpic_read {
	my ($xv_pic) = @_;

	my ($w,$h,$cols) = (0,0,0);

	open(XV,$xv_pic) || die "$xv_pic: $!";
	my $l=<XV>; chomp $l;
	if ($l!~m/^P7 332$/) {
		warn "$xv_pic not XV thumbnail file ($l)";
		return;
	}
	while(<XV>) {
		chomp;
		next if (/^#/);
		if (/^(\d+) (\d+) (\d+)$/) {
			($w,$h,$cols) = ($1,$2,$3);
			last;
		}
		warn "expected w,h,cols, got: $_";
	}

	warn "$xv_pic number of colors is not 255 (strange)" if ($cols != 255);

	my $buf;	# tmp buffer for read;

	# read pixels
	read XV,$buf,($w*$h);

	return ($w,$h,$buf);
}

#--------------------------------------------------------------------
# Generate 332 colour-cube colourmap

sub generate_xpm_colmap {
	my @xpm_colmap;
	for (my $i=0; $i<256; $i++) {

		push(@xpm_colmap,sprintf ("%02x c #%02x%02x%02x",$i,
			(255*(($i&(7<<5))>>5))/7,
			(255*(($i&(7<<2))>>2))/7,
			(255*(($i&(3<<0))>>0))/3
			));
	}
	return @xpm_colmap;
}

#--------------------------------------------------------------------
# main

my ($w,$h) = (0,0);
my $xv_pix;
my @xpm_colmap=generate_xpm_colmap();

#($w,$h,@xv_pix) = xvpic_read($xv_pic);
my $xv_pic = $X[0]{filename};
$w = $X[0]{w};
$h = $X[0]{h};
$xv_pix = $X[0]{pix};

#--------------------------------------------------------------------
# make rotated pictures

#sub make_rotated {
#	my ($w,$h,$xv_pix)

#/* width height ncolors chars_per_pixel */
#my @xpm_0= ( "$w $h 256 2" );
my @xpm_180= ( "$w $h 256 2" );
my @xpm_90= ( "$h $w 256 2" );
my @xpm_270= ( "$h $w 256 2" );
my @xpm_h= ( "$w $h 256 2" );
my @xpm_v= ( "$w $h 256 2" );

#push @xpm_0,@xpm_colmap;
push @xpm_180,@xpm_colmap;
push @xpm_90,@xpm_colmap;
push @xpm_270,@xpm_colmap;
push @xpm_h,@xpm_colmap;
push @xpm_v,@xpm_colmap;

my @pix_0;
my @pix_180;
my @pix_90;
my @pix_270;
my @pix_h;
my @pix_v;

my @xv_pix=unpack("C*",$xv_pix);

for(my $y=0; $y<$h; $y++) {
	for (my $x=0; $x<$w; $x++) {
		if (defined($xv_pix[$y*$w+$x])) {
			$pix_0[$y*$w+$x] = sprintf("%02x",$xv_pix[$y*$w+$x]);
			$pix_180[($h-$y-1)*$w+($w-$x-1)] = sprintf("%02x",$xv_pix[$y*$w+$x]);
			$pix_270[($w-$x-1)*$h+$y] = sprintf("%02x",$xv_pix[$y*$w+$x]);
			$pix_90[$x*$h+$y] = sprintf("%02x",$xv_pix[$y*$w+$x]);
			$pix_h[$y*$w+($w-$x-1)] = sprintf("%02x",$xv_pix[$y*$w+$x]);
			$pix_v[($h-$y-1)*$w+$x] = sprintf("%02x",$xv_pix[$y*$w+$x]);
		} else {
			warn "undef pixel $x,$y!\n";
		}
	}
}


# fill in pixels now

my $tmp;

# portrait
my $lw=$w*2;
#$tmp=join('',@pix_0); while ($tmp=~s/^(.{$lw})//) { push(@xpm_0,$1); }
$tmp=join('',@pix_180); while ($tmp=~s/^(.{$lw})//) { push(@xpm_180,$1); }
$tmp=join('',@pix_v); while ($tmp=~s/^(.{$lw})//) { push(@xpm_v,$1); }
$tmp=join('',@pix_h); while ($tmp=~s/^(.{$lw})//) { push(@xpm_h,$1); }

# landscape
$lw=$h*2;
$tmp=join('',@pix_90); while ($tmp=~s/^(.{$lw})//) { push(@xpm_90,$1); }
$tmp=join('',@pix_270); while ($tmp=~s/^(.{$lw})//) { push(@xpm_270,$1); }

#--------------------------------------------------------------------
# GTK GUI

my $window;
my ($pixmapwid_0, $pixmapwid_180, $pixmapwid_90, $pixmapwid_270, $pixmapwid_v, $pixmapwid_h);
my ($button_0, $button_180, $button_90, $button_270, $button_v, $button_h);
my ($pixmap_0, $pixmap_180, $pixmap_90, $pixmap_270, $pixmap_v, $pixmap_h);
my $mask;
my $style;

$window = new Gtk::Window( "toplevel" );
$window->signal_connect( "delete_event", sub { Gtk->exit( 0 ); } );
$window->border_width( 10 );
$window->set_title( "GTK jpegtran" );
$window->realize();

# now for the pixmap from gdk
$style = $window->get_style()->bg( 'normal' );

#( $pixmap_0, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm_0 );
#
## a pixmap widget to contain the pixmap
#$pixmapwid_0 = new Gtk::Pixmap( $pixmap_0, $mask );
#$pixmapwid_0->show();
#
## a button to contain the pixmap widget
#$button_0 = new Gtk::Button();
#$button_0->add( $pixmapwid_0 );
#$button_0->show();
##$button_0->signal_connect( "clicked", \&jpegtran, "-optimize" );
## make button_0 inactive
#$button_0->set_sensitive( $false );

# rotate 180
( $pixmap_180, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm_180 );

$pixmapwid_180 = new Gtk::Pixmap( $pixmap_180, $mask );
$pixmapwid_180->show();

$button_180 = new Gtk::Button();
$button_180->add( $pixmapwid_180 );
$button_180->show();
$button_180->signal_connect( "clicked", \&jpegtran, "-rotate 180" );

# rotate 90
( $pixmap_90, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm_90 );
$pixmapwid_90 = new Gtk::Pixmap( $pixmap_90, $mask );
$pixmapwid_90->show();

$button_90 = new Gtk::Button();
$button_90->add( $pixmapwid_90 );
$button_90->show();
$button_90->signal_connect( "clicked", \&jpegtran, "-rotate 90" );

# rotate 270
( $pixmap_270, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm_270 );
$pixmapwid_270 = new Gtk::Pixmap( $pixmap_270, $mask );
$pixmapwid_270->show();

$button_270 = new Gtk::Button();
$button_270->add( $pixmapwid_270 );
$button_270->show();
$button_270->signal_connect( "clicked", \&jpegtran, "-rotate 270" );

# rotate v
( $pixmap_v, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm_v );
$pixmapwid_v = new Gtk::Pixmap( $pixmap_v, $mask );
$pixmapwid_v->show();

$button_v = new Gtk::Button();
$button_v->add( $pixmapwid_v );
$button_v->show();
$button_v->signal_connect( "clicked", \&jpegtran, "-flip vertical" );

# rotate h
( $pixmap_h, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @xpm_h );
$pixmapwid_h = new Gtk::Pixmap( $pixmap_h, $mask );
$pixmapwid_h->show();

$button_h = new Gtk::Button();
$button_h->add( $pixmapwid_h );
$button_h->show();
$button_h->signal_connect( "clicked", \&jpegtran, "-flip horizontal" );

# pack, show, draw

my $hbox_top = new Gtk::HBox( $false, 0 );
my $hbox_mid = new Gtk::HBox( $true, 0 );
my $hbox_bot = new Gtk::HBox( $true, 0 );


#--------------------------------------------------------------------
# 

sub pix2xpm {
	my ($nr,$w,$h,$xv_pix) = @_;

	pdebug("pix2xpm w:$w h:$w pix:",length($xv_pix));

	my @xpm=( "$w $h 256 2" );
	push @xpm,@xpm_colmap;

	my @xv_pix=unpack("C*",$xv_pix);
	my $pix;

	for(my $i=0; $i<$#xv_pix; $i++) {
		$pix .= sprintf("%02x",$xv_pix[$i]);
	}
	my $lw=$w*2;
	while ($pix=~s/^(.{$lw})//) { push(@xpm,$1); }

	pdebug "pix2xpm retured ",length(join("",@xpm))," bytes, $#xpm lines";
	return (\@xpm);
}

sub xpm_button {
	my ($nr) = @_;

	pdebug "xpm_button($nr): xpm/pix len: ", length $X[$nr]{xpm},"/",length $X[$nr]{pix};

	( $X[$nr]{pixmap}, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm_d( $window->window, $style, @{$X[$nr]{xpm}} );
#	( $pixmap, $mask ) = Gtk::Gdk::Pixmap->create_from_xpm( $window->window, $style, "test.xpm" );
	$X[$nr]{pixmapwid} = new Gtk::Pixmap( $X[$nr]{pixmap}, $mask );
	$X[$nr]{pixmapwid}->show();

	$X[$nr]{button} = new Gtk::Button();
	$X[$nr]{button}->add( $X[$nr]{pixmapwid} );
	$X[$nr]{button}->show();
	$X[$nr]{button}->signal_connect( "clicked", \&select_pic, $nr );

	return ($X[$nr]{button});
}

my $arr_l = create_arrow_button( 'left', 'in' );
$hbox_top->pack_start( $arr_l, $false, $false, 0 );
#$hbox_top->pack_start( $button_0, $false, $false, 0 );


for (my $i=0; $i<$#X; $i++) {
#for (my $i=0; $i<0; $i++) {
	$X[$i]{xpm} = pix2xpm($i,$X[$i]{w},$X[$i]{h},$X[$i]{pix});
	$X[$i]{button}=xpm_button($window, $i );
	$hbox_top->pack_start( $X[$i]{button}, $false, $false, 0 );
}

my $arr_r = create_arrow_button( 'right', 'in' );
$hbox_top->pack_start( $arr_r, $false, $false, 0 );

$hbox_mid->pack_start( $button_90, $false, $false, 0 );
$hbox_mid->pack_start( $button_180, $false, $false, 0 );
$hbox_mid->pack_start( $button_270, $false, $false, 0 );

$hbox_bot->pack_start( $button_v, $false, $false, 0 );
$hbox_bot->pack_start( $button_h, $false, $false, 0 );

$hbox_top->show();
$hbox_mid->show();
$hbox_bot->show();

sub create_arrow_button
{
	my ( $arrow_type, $shadow_type ) = @_;
	
	my $button;
	my $arrow;
		    
	$button = new Gtk::Button();
	$arrow = new Gtk::Arrow( $arrow_type, $shadow_type );
			        
	$button->add( $arrow );
	$button->show();
	$arrow->show();  

	return ( $button );
}


my $hbox = new Gtk::HBox( $true, 0 );
my $filename_label = new Gtk::Label($xv_pic);
$filename_label->show();
$hbox->pack_start( $filename_label, $false, $false, 0 );
$hbox->show();

my $hseparator = new Gtk::HSeparator();
$hseparator->show();
my $hseparator2 = new Gtk::HSeparator();
$hseparator2->show();

my $vbox = new Gtk::VBox( $false, 5 );
$vbox->pack_start( $hbox_top, $false, $false, 0 );
$vbox->pack_start( $hseparator, $false, $false, 0 );
$vbox->pack_start( $hbox, $false, $false, 0 );
$vbox->pack_start( $hseparator2, $false, $false, 0 );
$vbox->pack_start( $hbox_mid, $false, $false, 0 );
$vbox->pack_start( $hbox_bot, $false, $false, 0 );
$vbox->show();

$window->add($vbox);
$window->show();

main Gtk;
exit( 0 );

#--------------------------------------------------------------------
# call jpegtran command

sub jpegtran {
	my ( $widget, $data ) = @_;
	print "jpegtran $data\n";
}

#--------------------------------------------------------------------
# select picture

sub select_pic {
	my ( $widget, $data ) = @_;
	my $xvpic=$X[$data]{filename};
	print "picture selected: $data ($xvpic)\n";
	$filename_label->set_text($xvpic);
}
