#!/usr/bin/perl use Gimp::Feature qw(gtk perl-5.005); use Gimp 1.06 (':auto','__','N_'); use Gimp::Fu; use Gtk; BEGIN { eval "use Image::Magick 1.45"; $@ and Gimp::Feature::missing ("Image::Magick version 1.45 or higher") }; $VERSION = '0.2.1'; $preview_size = 160; # max. size for image preview # this funny little function parses the Magick.xs file sub reparse { my $res; $res.="%MagickTypes = (\n"; $xs = do { local(*XS,$/); open XS,"<$_[0]" or die; }; while($xs =~ /(\w+Types)\[\]\s=\s+\{([^}]+)\}/g) { my $name=$1; my @vals=$2=~/"([^"]+)"/g; shift @vals if $vals[0] eq "Undefined"; $res.=" $name => sub {\n my \$m = new Gtk::Menu;\n". join("",map " \$m->append(new Gtk::MenuItem '$_');\n",@vals). " my \$o = new Gtk::OptionMenu;\n". " \$o->set_menu(\$m);\n". " optionmenu_settext(\$o,\@_);\n". " \$o;\n". " },\n"; } $res.=");\n\n"; $res.="%MagickMethods = (\n"; $xs =~ /Methods\[\]\s=\s+\{\n(.+?)\s*\};/s or die; $methods=$1; while($methods =~ /(?:^|\n)(\s+)\{ "([^"]+)",(?: \{ (.*?))?\s*\},(?=\n\1\{|$)/gs) { my $method=$2; my @args=$3=~/\{"([^"]+)", (\w+)},?/gs; $res.=" $method => [".join(",",map "'$_'",@args)."],\n"; } $res.=");\n"; require IO::AtomicFile; { local $/; open X,"<$0" or die; $data=; $data=~s/(?<=\n#MAGICK#\n).*/sub magick {\n$res\n}\n/s; my $file=IO::AtomicFile->open($0,"w"); $file->print($data); $file->close; } } sub optionmenu_settext { my ($o,$ref) = @_; $o->signal_connect (clicked => sub { $arg{$ref} = $_[0]->get_menu->get_active->get; }); } sub new_entry { my ($re,$ref)=@_; my $e = new Gtk::Entry; $e->signal_connect(changed => sub { $arg{$ref}=$e->get_text; }); $e; } if ($ARGV[0] eq "--reparse") { reparse("/root/cvt/ImageMagick-4.2.0/PerlMagick/Magick.xs"); exit; } &magick; %MagickTypes = (%MagickTypes, 'StringReference' => sub { new_entry "",@_ }, 'DoubleReference' => sub { new_entry '^[0-9.E+-]+$',@_ }, 'IntegerReference' => sub { new_entry '^[0-9+-]+$',@_ }, 'ImageReference' => sub { new Gtk::Label "not yet supported" }, ); %MagickMethods = (%MagickMethods, ); sub check { for(values(%MagickMethods)) { my @a=@$_; while(@a) { shift @a; my $x = shift @a; print($x," <- does not exist\n") unless $MagickTypes{$x}; } } } #check; # read the image pixels into an imagemagick-image sub read_pixels { my($drawable,$im)=@_; my $th = Gimp->tile_height; Gimp->tile_cache_ntiles (1 + $drawable->width / Gimp->tile_width); my $type = $drawable->type; my $format; $format = "RGB" if $type == RGB_IMAGE; $format = "RGBA" if $type == RGBA_IMAGE; $format = "GRAY" if $type == GRAY_IMAGE; die "Indexed format and GRAYA not yet supported in GimpMagick!\n" unless $format; my $temp = Gimp->temp_name('raw'); open TEMP,">$temp\0" or die "unable to open temporary file '$temp' for writing\n"; my ($empty,$x1,$y1,$x2,$y2) = $drawable->mask_bounds; $x2-=$x1; $y2-=$y1; my $region = $drawable->pixel_rgn ($x1, $y1, $x2, $y2, 0, 0); Gimp->progress_init ("transferring image data"); for(my $y=0; $y<$y2; $y+=$th) { # calling internal function, sorry folks! Gimp->progress_update ($y/$y2*100); print TEMP $region->get_rect2(0,$y,$x2,$y2-$y > $th ? $th : $y2-$y); } close TEMP; $im->Set(size => $x2.'x'.$y2); $im->Read("$format:$temp"); unlink $temp; $format; } # read the image pixels back sub write_pixels { my($drawable,$im,$format)=@_; my $th = Gimp->tile_height; my $buf; my $temp = Gimp->temp_name('raw'); $im->Write("$format:$temp"); open TEMP,"<$temp\0" or die "unable to open temporary file '$temp' for writing\n"; unlink $temp; my ($empty,$x1,$y1,$x2,$y2) = $drawable->mask_bounds; $x2-=$x1; $y2-=$y1; if ($x2 ne $im->get('width') or $y2 ne $im->get('height')) { $drawable->resize ($im->get('width','height'),0,0); $drawable->image->selection_none; ($x1,$y1,$x2,$y2)=(0,0,$im->get('width','height')); } my $region = $drawable->get->pixel_rgn ($x1, $y1, $x2, $y2, 1, 1); Gimp->progress_init ("transferring image data"); my $stride = $x2*$region->bpp; for(my $y=0; $y<$y2; $y+=$th) { # calling internal function, sorry folks! Gimp->progress_update ($y/$y2*100); read TEMP,$buf,$stride*$th; $region->set_rect2($buf,0,$y); } close TEMP; undef $region; $drawable->merge_shadow (1); $drawable->update ($x1, $y1, $x2, $y2); Gimp->displays_flush; } sub update_preview { my ($im,$pre)=@_; $im=$im->clone; while($im->get('width') > $preview_size or $im->get('height') > $preview_size) { $im->Minify; } if(0==open BLOB,"-|") { $im->Write('RGB:-'); Gimp::_exit; } my($w,$h)=$im->get('width','height'); $pre->size($w,$h); for (0..$h-1) { read BLOB,$im,$w*3; $pre->draw_row($im,0,$_,$w); } close BLOB; $pre->draw(undef); } # Interactively apply Image::Magick sub gimp_magick { my ($drawable)=@_; Gimp::gtk_init; # generate main window my $im = new Image::Magick; my $format = read_pixels ($drawable, $im); my $w = new Gtk::Dialog; $w->set_title ("GimpMagick! $VERSION"); my $b = new Gtk::Button "Apply"; $b->signal_connect (clicked => sub { write_pixels ($drawable, $im, $format); main_quit Gtk }); $w->action_area->add ($b); $b = new Gtk::Button "Cancel"; $b->signal_connect (clicked => sub { main_quit Gtk }); $w->action_area->add ($b); $preview = new Gtk::Preview "color"; $w->vbox->add ($preview); my $frame = new Gtk::Frame "Arguments"; my $cbox = new Gtk::VBox 0,0; $frame->add($cbox); my $command = new Gtk::Combo; my %args; $command->set_popdown_strings (sort keys %MagickMethods); $command->set_case_sensitive (0); my $changed_command = sub { $method = $command->entry->get_text; return unless $MagickMethods{$method}; $frame->remove ($cbox); $cbox = new Gtk::VBox 0,5; my @args = @{$MagickMethods{$method}}; while(@args) { %arg=(); my($name,$type)=(shift @args,shift @args); my($hbox)=new Gtk::HBox 0,5; $hbox->add(new Gtk::Label "$name: "); my $widget = $MagickTypes{$type}->($name); $hbox->add($widget); $cbox->add($hbox); } $cbox->show_all; $frame->add($cbox); }; $command->entry->signal_connect(changed => $changed_command); my $execute = new Gtk::Button "Execute!"; $execute->signal_connect(clicked => sub { $im->$method(%arg); update_preview ($im, $preview); }); $w->vbox->add($command); $w->vbox->add($frame); $w->vbox->add($execute); update_preview ($im, $preview); $w->show_all; &$changed_command; main Gtk; (); } register "gimp_magick", "access to the Image::Magick-package", "Gimp::Magick gives you access to all methods in the Image::Magick-package. These methods often offer ". "higher quality than equivalent Gimp methods, as well as offering more methods than Gimp itself", "Marc Lehmann", "Marc Lehmann", $VERSION, N_"/Filters/Misc/Magick...", "*", [ ], sub { my($image,$drawable)=@_; gimp_magick ($drawable); $image; }; exit main; #MAGICK# sub magick { %MagickTypes = ( BooleanTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'False'); $m->append(new Gtk::MenuItem 'True'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, ClassTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'DirectClass'); $m->append(new Gtk::MenuItem 'PseudoClass'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, ColorspaceTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'RGB'); $m->append(new Gtk::MenuItem 'Gray'); $m->append(new Gtk::MenuItem 'Transparent'); $m->append(new Gtk::MenuItem 'OHTA'); $m->append(new Gtk::MenuItem 'XYZ'); $m->append(new Gtk::MenuItem 'YCbCr'); $m->append(new Gtk::MenuItem 'YCC'); $m->append(new Gtk::MenuItem 'YIQ'); $m->append(new Gtk::MenuItem 'YPbPr'); $m->append(new Gtk::MenuItem 'YUV'); $m->append(new Gtk::MenuItem 'CMYK'); $m->append(new Gtk::MenuItem 'sRGB'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, CompositeTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Over'); $m->append(new Gtk::MenuItem 'In'); $m->append(new Gtk::MenuItem 'Out'); $m->append(new Gtk::MenuItem 'Atop'); $m->append(new Gtk::MenuItem 'Xor'); $m->append(new Gtk::MenuItem 'Plus'); $m->append(new Gtk::MenuItem 'Minus'); $m->append(new Gtk::MenuItem 'Add'); $m->append(new Gtk::MenuItem 'Subtract'); $m->append(new Gtk::MenuItem 'Difference'); $m->append(new Gtk::MenuItem 'Bumpmap'); $m->append(new Gtk::MenuItem 'Replace'); $m->append(new Gtk::MenuItem 'ReplaceRed'); $m->append(new Gtk::MenuItem 'ReplaceGreen'); $m->append(new Gtk::MenuItem 'ReplaceBlue'); $m->append(new Gtk::MenuItem 'ReplaceMatte'); $m->append(new Gtk::MenuItem 'Blend'); $m->append(new Gtk::MenuItem 'Displace'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, CompressionTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'None'); $m->append(new Gtk::MenuItem 'BZip'); $m->append(new Gtk::MenuItem 'Fax'); $m->append(new Gtk::MenuItem 'Group4'); $m->append(new Gtk::MenuItem 'JPEG'); $m->append(new Gtk::MenuItem 'LZW'); $m->append(new Gtk::MenuItem 'Runlength'); $m->append(new Gtk::MenuItem 'Zip'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, FilterTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Point'); $m->append(new Gtk::MenuItem 'Box'); $m->append(new Gtk::MenuItem 'Triangle'); $m->append(new Gtk::MenuItem 'Hermite'); $m->append(new Gtk::MenuItem 'Hanning'); $m->append(new Gtk::MenuItem 'Hamming'); $m->append(new Gtk::MenuItem 'Blackman'); $m->append(new Gtk::MenuItem 'Gaussian'); $m->append(new Gtk::MenuItem 'Quadratic'); $m->append(new Gtk::MenuItem 'Cubic'); $m->append(new Gtk::MenuItem 'Catrom'); $m->append(new Gtk::MenuItem 'Mitchell'); $m->append(new Gtk::MenuItem 'Lanczos'); $m->append(new Gtk::MenuItem 'Bessel'); $m->append(new Gtk::MenuItem 'Sinc'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, GravityTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Forget'); $m->append(new Gtk::MenuItem 'NorthWest'); $m->append(new Gtk::MenuItem 'North'); $m->append(new Gtk::MenuItem 'NorthEast'); $m->append(new Gtk::MenuItem 'West'); $m->append(new Gtk::MenuItem 'Center'); $m->append(new Gtk::MenuItem 'East'); $m->append(new Gtk::MenuItem 'SouthWest'); $m->append(new Gtk::MenuItem 'South'); $m->append(new Gtk::MenuItem 'SouthEast'); $m->append(new Gtk::MenuItem 'Static'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, ImageTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Bilevel'); $m->append(new Gtk::MenuItem 'Grayscale'); $m->append(new Gtk::MenuItem 'Palette'); $m->append(new Gtk::MenuItem 'TrueColor'); $m->append(new Gtk::MenuItem 'Matte'); $m->append(new Gtk::MenuItem 'ColorSeparation'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, IntentTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Saturation'); $m->append(new Gtk::MenuItem 'Perceptual'); $m->append(new Gtk::MenuItem 'Absolute'); $m->append(new Gtk::MenuItem 'Relative'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, InterlaceTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'None'); $m->append(new Gtk::MenuItem 'Line'); $m->append(new Gtk::MenuItem 'Plane'); $m->append(new Gtk::MenuItem 'Partition'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, LayerTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Red'); $m->append(new Gtk::MenuItem 'Green'); $m->append(new Gtk::MenuItem 'Blue'); $m->append(new Gtk::MenuItem 'Matte'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, MethodTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Point'); $m->append(new Gtk::MenuItem 'Replace'); $m->append(new Gtk::MenuItem 'Floodfill'); $m->append(new Gtk::MenuItem 'FillToBorder'); $m->append(new Gtk::MenuItem 'Reset'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, ModeTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Frame'); $m->append(new Gtk::MenuItem 'Unframe'); $m->append(new Gtk::MenuItem 'Concatenate'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, NoiseTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Uniform'); $m->append(new Gtk::MenuItem 'Gaussian'); $m->append(new Gtk::MenuItem 'Multiplicative'); $m->append(new Gtk::MenuItem 'Impulse'); $m->append(new Gtk::MenuItem 'Laplacian'); $m->append(new Gtk::MenuItem 'Poisson'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, PreviewTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Rotate'); $m->append(new Gtk::MenuItem 'Shear'); $m->append(new Gtk::MenuItem 'Roll'); $m->append(new Gtk::MenuItem 'Hue'); $m->append(new Gtk::MenuItem 'Saturation'); $m->append(new Gtk::MenuItem 'Brightness'); $m->append(new Gtk::MenuItem 'Gamma'); $m->append(new Gtk::MenuItem 'Spiff'); $m->append(new Gtk::MenuItem 'Dull'); $m->append(new Gtk::MenuItem 'Grayscale'); $m->append(new Gtk::MenuItem 'Quantize'); $m->append(new Gtk::MenuItem 'Despeckle'); $m->append(new Gtk::MenuItem 'ReduceNoise'); $m->append(new Gtk::MenuItem 'AddNoise'); $m->append(new Gtk::MenuItem 'Sharpen'); $m->append(new Gtk::MenuItem 'Blur'); $m->append(new Gtk::MenuItem 'Threshold'); $m->append(new Gtk::MenuItem 'EdgeDetect'); $m->append(new Gtk::MenuItem 'Spread'); $m->append(new Gtk::MenuItem 'Solarize'); $m->append(new Gtk::MenuItem 'Shade'); $m->append(new Gtk::MenuItem 'Raise'); $m->append(new Gtk::MenuItem 'Segment'); $m->append(new Gtk::MenuItem 'Swirl'); $m->append(new Gtk::MenuItem 'Implode'); $m->append(new Gtk::MenuItem 'Wave'); $m->append(new Gtk::MenuItem 'OilPaint'); $m->append(new Gtk::MenuItem 'Charcoal'); $m->append(new Gtk::MenuItem 'JPEG'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, PrimitiveTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'Point'); $m->append(new Gtk::MenuItem 'Line'); $m->append(new Gtk::MenuItem 'Rectangle'); $m->append(new Gtk::MenuItem 'FillRectangle'); $m->append(new Gtk::MenuItem 'Circle'); $m->append(new Gtk::MenuItem 'FillCircle'); $m->append(new Gtk::MenuItem 'Ellipse'); $m->append(new Gtk::MenuItem 'FillEllipse'); $m->append(new Gtk::MenuItem 'Polygon'); $m->append(new Gtk::MenuItem 'FillPolygon'); $m->append(new Gtk::MenuItem 'Color'); $m->append(new Gtk::MenuItem 'Matte'); $m->append(new Gtk::MenuItem 'Text'); $m->append(new Gtk::MenuItem 'Image'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, ResolutionTypes => sub { my $m = new Gtk::Menu; $m->append(new Gtk::MenuItem 'PixelsPerInch'); $m->append(new Gtk::MenuItem 'PixelsPerCentimeter'); my $o = new Gtk::OptionMenu; $o->set_menu($m); optionmenu_settext($o,@_); $o; }, ); %MagickMethods = ( Comment => ['comment','StringReference'], Label => ['label','StringReference'], AddNoise => ['noise','NoiseTypes'], Colorize => ['color','StringReference','pen','StringReference'], Border => ['geom','StringReference','width','IntegerReference','height','IntegerReference','color','StringReference'], Blur => ['factor','DoubleReference'], Chop => ['geom','StringReference','width','IntegerReference','height','IntegerReference','x','IntegerReference','y','IntegerReference'], Crop => ['geom','StringReference','width','IntegerReference','height','IntegerReference','x','IntegerReference','y','IntegerReference'], Despeckle => [], Edge => ['factor','DoubleReference'], Emboss => [], Enhance => [], Flip => [], Flop => [], Frame => ['geom','StringReference','width','IntegerReference','height','IntegerReference','inner','IntegerReference','outer','IntegerReference','color','StringReference'], Implode => ['factor','DoubleReference'], Magnify => [], MedianFilter => [], Minify => [], OilPaint => ['radius','IntegerReference'], ReduceNoise => [], Roll => ['geom','StringReference','x','IntegerReference','y','IntegerReference'], Rotate => ['degree','DoubleReference','crop','BooleanTypes','sharpen','BooleanTypes'], Sample => ['geom','StringReference','width','IntegerReference','height','IntegerReference'], Scale => ['geom','StringReference','width','IntegerReference','height','IntegerReference'], Shade => ['geom','StringReference','azimuth','DoubleReference','elevat','DoubleReference','color','BooleanTypes'], Sharpen => ['factor','DoubleReference'], Shear => ['geom','StringReference','x','DoubleReference','y','DoubleReference','crop','BooleanTypes'], Spread => ['amount','IntegerReference'], Swirl => ['degree','DoubleReference'], Zoom => ['geom','StringReference','width','IntegerReference','height','IntegerReference','filter','FilterTypes'], IsGrayImage => [], Annotate => ['text','StringReference','font','StringReference','point','IntegerReference','density','StringReference','box','StringReference','pen','StringReference','geom','StringReference','server','StringReference','x','IntegerReference','y','IntegerReference','grav','GravityTypes'], ColorFloodfill => ['geom','StringReference','x','IntegerReference','y','IntegerReference','pen','StringReference','bordercolor','StringReference'], Composite => ['compos','CompositeTypes','image','ImageReference','geom','StringReference','x','IntegerReference','y','IntegerReference','grav','GravityTypes'], Contrast => ['sharp','BooleanTypes'], CycleColormap => ['amount','IntegerReference'], Draw => ['prim','PrimitiveTypes','points','StringReference','meth','MethodTypes','pen','StringReference','linew','IntegerReference','server','StringReference','borderc','StringReference'], Equalize => [], Gamma => ['gamma','StringReference','red','DoubleReference','green','DoubleReference','blue','DoubleReference'], Map => ['image','ImageReference','dither','BooleanTypes'], MatteFloodfill => ['geom','StringReference','x','IntegerReference','y','IntegerReference','matte','IntegerReference','bordercolor','StringReference'], Modulate => ['factor','StringReference','bright','DoubleReference','satur','DoubleReference','hue','DoubleReference'], Negate => ['gray','BooleanTypes'], Normalize => [], NumberColors => [], Opaque => ['color','StringReference','pen','StringReference'], Quantize => ['colors','IntegerReference','tree','IntegerReference','colorsp','ColorspaceTypes','dither','BooleanTypes','measure','BooleanTypes','global','BooleanTypes'], Raise => ['geom','StringReference','width','IntegerReference','height','IntegerReference','x','IntegerReference','y','IntegerReference','raise','BooleanTypes'], Segment => ['colorsp','ColorspaceTypes','verbose','BooleanTypes','clust','DoubleReference','smooth','DoubleReference'], Signature => [], Solarize => ['factor','DoubleReference'], Sync => [], Texture => ['texture','ImageReference'], Transform => ['crop','StringReference','geom','StringReference','filter','FilterTypes'], Transparent => ['color','StringReference'], Threshold => ['threshold','DoubleReference'], Charcoal => ['factor','StringReference'], Trim => [], Wave => ['geom','StringReference','ampli','DoubleReference','wave','DoubleReference'], Layer => ['layer','LayerTypes'], Condense => [], Stereo => ['image','ImageReference'], Stegano => ['image','ImageReference','offset','IntegerReference'], Coalesce => [], ); }