Hiding information in Cats You are a secret agent trying to communicate with your fatherland. Of course the information needs to be hidden so no one eaves drops your message. What would be better suited than a cat? Everyone loves funny pictures of cats [citation needed], so they won't suspect secret information hiding in there!



Inspired by the algorithm the game Monaco uses to save the level information of shared levels it is your tasks to write a program that encoded information into the least significant bit of the colors of an image.

Encoding format:


The first 24 bits determine the length of the remaining encoded byte-string in bits
The image is read from left to right and from top to bottom, obviously starting in the upper left pixel
The channels are read from red to green to blue
The least significant bit from each channel is read
Bits are saved in Big Endian order


Rules:


Your program takes a single byte-string to be encoded and a single image filename for the base image
The resulting image must be come out as a true color PNG file
You may use I/O in whatever form you like (ARGV, STDIN, STDOUT, writing / reading from a file), as long as you state how to use your program
You must choose a random image of a funny cat and encode your program into it to show that your program works
You may assume that you are only provided valid input, if the amount of bits are not sufficient, the image is not in true color format, the image does not exist or similar problems you may do what you want
You may assume that the provided image does not contain any alpha channel
Length is counted in UTF-8 bytes without BOM


You may use this PHP script to test your solution, provide the name of the PNG file as the first command line argument:

&lt;?php
if ($argc === 1) die('Provide the filename of the PNG to read from');
$imageSize = @getimagesize($argv[1]);

if ($imageSize === false) die('Not a PNG file');
list($width, $height) = $imageSize;

$image = imagecreatefrompng($argv[1]);
$read = 0;
$bits = '';
for ($y = 0; $y &lt; $height; $y++) {
    for ($x = 0; $x &lt; $width; $x++) {
        $colorAt = imagecolorat($image, $x, $y);
        $red = ($colorAt &gt;&gt; 16) &amp; 0xFF;
        $green = ($colorAt &gt;&gt; 8) &amp; 0xFF;
        $blue = ($colorAt &gt;&gt; 0) &amp; 0xFF;

        $bits .= ($red &amp; 1).($green &amp; 1).($blue &amp; 1);
        $read += 3;
        if ($read == 24) {
            $length = (int) bindec($bits);
            $bits = '';
        }
        else if ($read &gt; 24 &amp;&amp; ($read - 24) &gt; $length) {
            $bits = substr($bits, 0, $length);
            break 2;
        }
    }
}
if (strlen($bits) !== $length) die('Not enough bits read to fulfill the length');
$parts = str_split($bits, 8);
foreach ($parts as $part) {
    echo chr(bindec($part));
}
Mathematica, 255 234 206 bytes

I've seen so many 255s while testing this, I'm unreasonably happy about the size of the code. :) And then my ambition to golf it down even further got the best of me...

f=(j=ImageData[Import@#2,t="Byte"];k=(d=IntegerDigits)[j,2,8]~Flatten~2;n=1;(k[[n++,-1]]=#)&amp;/@d[Length@#,2,24]~Join~#&amp;[Join@@d[ToCharacterCode@#,2,8]];ArrayReshape[#~FromDigits~2&amp;/@k,Dimensions@j]~Image~t)&amp;


It's technically a function and not a "program", but then again this is pretty much how you write "programs" in Mathematica, if that concept is even valid there. Call it like

f["my secret", "fully/qualified/cat.png"]


It will return an actual image expression (because that's the most natural way to return an image in Mathematica), so if you want a file, you need to export it:

Export["output-cat.png", f["my secret", "input-cat.png"]]


Here is the required example:



I'd love to show you the decoded message here, but it doesn't fit... so run it through the OP's decoder. ;)

Btw, I could make it work with UTF-8 secrets for just 7 bytes (change ToCharacterCode@# into #~ToCharacterCode~"utf8").

Ungolfed code:

f[secret_, filename_] := (
  bits = Join @@ IntegerDigits[ToCharacterCode[secret], 2, 8];
  bits = Join[d[Length @ bits, 2, 24], bits];
  data = ImageData[Import@#2, "Byte"];
  dims = Dimensions@data;
  data = Flatten[IntegerDigits[data, 2, 8], 2];
  m = n = 1;
  While[m &lt;= Length @ bits,
    data[[n++, -1]] = bits[[m++]]
  ];
  Image[ArrayReshape[FromDigits[#, 2] &amp; /@ data, dims], "Byte"]
)