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