#!/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);
}