Script 1: Text Decoder
#!/usr/bin/perl
use strict;
use warnings;
use Image::PNG::Libpng ':all';
my $HEADER_SIZE = 2;
my $LENGTH_SIZE = 16;
my $png = create_read_struct();
open my $if, '<:raw', shift or die $!;
$png->init_io( $if );
$png->read_png;
$if->close;
my @bits;
for my $row ( $png->get_rows->@* ) {
while ( $row ) {
my ( $r, $g, $b ) = map { ord } unpack( 'aaa', $row );
push @bits, $r & 1,
$g & 1,
$b & 1;
$row = substr $row, 3;
}
}
my $header;
$header += shift @bits for 1 .. $HEADER_SIZE;
if ( $header != 0 ) {
die "invalid header";
}
my $length = 0;
for my $pos ( 1 .. $LENGTH_SIZE ) {
my $bit = shift @bits;
my $value = $bit << $LENGTH_SIZE - $pos;
$length += $value;
}
print "length: $length\n";
print "==========\n";
for ( 1 .. $length / 8 ) {
my $byte = 0;
my $pos = 1;
for my $bit ( splice @bits, 0, 8 ) {
my $value = $bit << 8 - $pos;
$byte += $value;
$pos ++;
}
print chr( $byte );
}
print "\n";
print "==========\n";
exit;
Script 2: Image Decoder
#!/usr/bin/perl
use strict;
use warnings;
use Image::PNG::Libpng ':all';
use Image::PNG::Const ':all';
my $HEADER_SIZE = 2;
my $ROWS_SIZE = 16;
my $COLS_SIZE = 16;
my $png = create_read_struct();
open my $if, '<:raw', shift or die $!;
$png->init_io( $if );
$png->read_png;
$if->close;
my @bits;
for my $row ( $png->get_rows->@* ) {
while ( $row ) {
my ( $r, $g, $b ) = map { ord } unpack( 'aaa', $row );
push @bits, $r & 1,
$g & 1,
$b & 1;
$row = substr $row, 3;
}
}
my $header = '';
$header .= shift @bits for 1 .. $HEADER_SIZE;
if ( $header ne '10' ) {
print "invalid header";
exit 1;
}
my $rows = 0;
for my $pos ( 1 .. $ROWS_SIZE ) {
my $bit = shift @bits;
my $value = $bit << $ROWS_SIZE - $pos;
$rows += $value;
}
print "rows: $rows\n";
my $cols = 0;
for my $pos ( 1 .. $COLS_SIZE ) {
my $bit = shift @bits;
my $value = $bit << $COLS_SIZE - $pos;
$cols += $value;
}
print "cols: $rows\n";
print "==========\n";
$png = create_write_struct();
open my $of, '>:raw', 'out.png';
$png->init_io( $of );
$png->set_IHDR( {
'height' => $rows,
'width' => $cols,
'bit_depth' => 1,
'color_type' => PNG_COLOR_TYPE_GRAY,
} );
$png->write_info;
my @rows;
for my $x ( 0 .. $rows - 1 ) {
my $row = '';
for my $y ( 0 .. $cols - 1 ) {
my $idx = $x * $rows + $y;
my $value = $bits[ $idx ];
$row .= $value;
}
push @rows, pack 'B*', $row;
}
$png->write_image( \@rows );
$png->write_end;
exit;
Answer 1: "Three may keep a secret, if two of them are dead." Benjamin Franklin
Answer 2: A key (and a kite).
At first I tried to do the image decoder by having something like if ( $bit ) print "X" else print " ", opening it in a text editor, and zooming out to create hi-res ASCII art. And this almost worked, I could tell what the image was but couldn't read the text. Otherwise libpng made this pretty simple. Took a couple tries to get the binary row format down.