#!/usr/local/bin/perl ## Make a gif "transparent" ## ## Jeffrey Friedl ## jfriedl@omrongw.wg.omron.co.jp ## 15 July 1994 ## ## I wrote this because people ask for something like this all the time. ## I just learned the format of GIFs a week ago, so this will likely be ## lacking in many respects. ## ## Usage: ## ## transgif [colornum] regular.gif > transparent.gif ## or ## cat regular.gif | transgif [colornum] transparent.gif ## ## COLORNUM is the index of the color entry to make transparent, and ## defaults to zero. For those that like the looks of it, you can put ## a leading '-'. ## ## Idea for the future: let the color be defined as R-G-B and select the ## closest color from the colormap and use that. ## $color = 0; if (@ARGV && $ARGV[0] =~ m/^-?(\d+)/) { $color = $1; shift; } die "too many args; usage: $0 [colornum] [file]\n" if @ARGV > 1; if (@ARGV == 0) { &giftrans(*STDIN, *STDOUT, $color); } else { open(INPUT, $file =shift) || die "$0: couldn't open [$file] for input\n"; &giftrans(*INPUT, *STDOUT, $color); close(INPUT); } ## ## Given indirect references to two filehandles, pass the file from ## one to the other, changing nothing unless it's a GIF that we know ## how to deal with, and if so do so. ## ## This is written rather verbosely for the sake of clarity... speed not ## much of an issue for something like this, and the difference is minimal ## anyway. ## sub giftrans { local(*IN, *OUT, $color) = @_; $color = 0 if !defined $color; local($header, $color_table, $nextblock, $buffer) = ('') x 4; ## The header looks like: ## byte 0 - 5: "GIF89a" or "GIF87a" ## byte 6, 7: width (low order first) ## byte 8, 9: height (low order first) ## byte 10: various flags ## byte 11: background color index ## byte 12: aspect ratio sysread(IN, $header, 13) || die "sysread header"; substr($header, 0, 6) = 'GIF89a' if substr($header,0,6) eq 'GIF87a'; print OUT $header; if (substr($header, 0, 6) ne 'GIF89a') { print STDERR "don't know input filetype, passing unchanged\n"; } else { ## ## Look at flags: ## High bit is global colormap indicator. ## Value_in_next_three_bits + 1 == number of bits per pixel. ## Next bit indicates if color map is sorted by importance. ## (1 + 2 ** (Value_in_final_three_bits + 1)) == size of colormap. ## local($flags) = ord(substr($header, 10, 1)); local($has_global_colormap) = $flags & 0x80; ## Copy over the colormap if need be. if ($has_global_colormap) { local($bits_per_color) = 1 + ($flags & 0x07); local($color_tbl_size) = 3 * (1 << $bits_per_color); sysread(IN, $color_table, $color_tbl_size) || die "sysread color"; print OUT $color_table; } ## ## The next 8 bytes will either be an already-there graphic-extension ## block, or something else that we'll not care about. In the latter ## case, we'll add a graphic-extension block saying "color such-and- ## such is transparent". If there's already one there, we'll just ## ensure that it says that. ## sysread(IN, $nextblock, 8) || die "sysread nextblock"; local($extension, $label) = unpack('CC', $nextblock); ## If extension is 0x21 and label is 0xf9, that's the magic tha means ## there's already a graphic extension there. if ($extension == 0x21 && $label == 0xf9) { substr($nextblock, 3, 1) = pack('C', 1|substr($nextblock, 3, 1)); substr($nextblock, 6, 1) = pack('C', $color); } else { print OUT pack('CCC CCCC C', 0x21, ## magic: "Extension Introducer" 0xf9, ## magic: "Graphic Control Label" 4, ## bytes in block (between here and terminator) 0x01, ## indicates that 'transparet index' is given 0, 0, ## delay time. $color, ## index of "transparent" color. 0x00); ## terminator. } print OUT $nextblock; } ## Now just pass the rest of the file over unchanged. print OUT $buffer while sysread(IN, $buffer, 4096); close(IN); close(OUT); }