#!/usr/bin/perl

# inject( $message, \&getpixel, \&putpixel )
#    Insert the bits of message into a stream of pixels.
#    If getpixel/putpixel are not provided, they default
#    to reading stdin and writing stdout.  A pixel is
#    3 scalar values of 16-bit precision, red, green, blue.
sub inject {
    my $message = shift;
    my $getpixel = shift || \&stdinpixel;
    my $putpixel = shift || \&stdoutpixel;

    my $numbits = 8 * length( $message );
    my $curbit = 0;
    my ( @pixel );

    while ( @pixel = \&getpixel ) {
        if ( $curbit < $numbits ) {
            for $j (0..2) {
                if ( vec( $message, $curbit++, 1 ) ) {
                    $pixel[$j] |= 1;
                } else {
                    $pixel[$j] &= 65534;
                }
            }
        }
        &$putpixel( @pixel );
    }
}

# $message = extract( \&getpixel )
#    Extract a message from a stream of pixels.
#    If getpixel is not provided, it defaults to reading stdin.
sub extract {
    my $getpixel = shift || \&stdinpixel;
    my $message;
    my $curbit = 0;
    my @pixel;

    while ( @pixel = &$getpixel ) {
        for $j (0..2) {
            if ( $pixel[$j] & 1 ) {
                vec( $message, $curbit, 1 ) = 1;
            }
            ++$curbit;
        }
    }
}

sub stdinpixel {
    my $input;
    read STDIN, $input, 6 or return ();

    unpack "n3", $input;
}

sub stdoutpixel {
    my $output = pack "n3", @_;
    print STDOUT $output;
}
