いっそこんな利用法はどうか。
404 Blog Not Found:Ajax - データ交換 via PNG使いどころが限定的すぎる。
Demo 0
アイコンの中にエンコーダーのソースコードを埋め込んである。
$('#demo0 input').click(function(){
$('#demo0dst').text(showTextInPNG($('#demo0img').get(0)));
});
Perlでテキストを取り出すソースコードは以下のとおり。
#!/usr/bin/env perl
use strict;
use warnings;
use Imager;
use autodie;
my $img = Imager->new();
$img->read( file => shift ) or die $img->errstr;
$img->write( data => \my $pnm, type => 'pnm' ) or die $img->errstr;
my $hdr = do {
$pnm =~ s/((?:[^\n]+\n){4})//;
$1;
};
my @pnm = map { ord } split //, $pnm;
my $txt = '';
for my $i (0..+@pnm / 3){
my $ord = (($pnm[3*$i] & 0b00000111)<<5)
+ (($pnm[3*$i+1] & 0b00000011)<<3)
+ ($pnm[3*$i+2] & 0b00000111);
last if ! $ord;
$txt .= chr $ord;
}
print $txt;
どうやってるの?
要するに、24bitカラーの元画像を16bit(RGB=565)に減色した上で、開いた8bitの中に1 byteのテキストを埋め込んでいる。ピクセル数以下のバイト数の任意のテキストが埋め込める。テキストを埋め込んだ部分は仮に元の色が#000000だとしても高々#070307にしかならず、見ての通り人の目ではほとんど区別がつかない。
Demo 1
- Get Image from URL:
- Size:
- 0x0=0bytes
- Text: 0
- and
$('#demo0 input').click(function(){
$('#demo0dst').text(showTextInPNG($('#demo0img').get(0)));
});
$('#demo1get').click(function(){
$.get('http://api.dan.co.jp/datauri/' + $('#demo1url').val(), function(txt){
$('#demo1img').attr('src', txt)
})
});
$('#demo1img').load(function(){
$('#demo1w').text(this.width);
$('#demo1h').text(this.height);
$('#demo1s').text(this.height*this.width);
});
$('#demo1txt').keyup(function(){
var text = this.value,
chars = text.length,
bytes = Base64.utob(text).length;
$('#demo1st').text([chars, 'chars,', bytes, 'bytes'].join(' '));
}).trigger('keyup');
$('#demo1embed').click(function(){
$('#demo1timg').attr('src',
hideTextInPNG($('#demo1img').get(0), $('#demo1txt').val())
);
});
$('#demo1extract').click(function(){
$('#demo1dst').text(showTextInPNG($('#demo1timg').get(0)));
});
JavaScriptによる実装
/*
* You need base64.js.
* cf: http://blog.livedoor.jp/dankogai/archives/51067688.html
*/
showTextInPNG = function(node) {
var el = document.createElement("canvas"),
ctx = el.getContext("2d"),
w = el.width = el.style.width = node.width,
h = el.height = el.style.height = node.height;
ctx.drawImage(node, 0, 0);
var d = ctx.getImageData(0, 0, w, h).data,
a = [];
for(var i = 0, l = d.length; i < l; i += 4) {
var c = ((d[i] & 0x7) << 5)
+ ((d[i+1] & 0x3) << 3)
+ (d[i+2] & 0x7);
if (!c) break;
a.push(String.fromCharCode(c));
}
return Base64.btou(a.join(''));
};
hideTextInPNG = function(node, txt) {
var el = document.createElement("canvas"),
ctx = el.getContext("2d"),
w = el.width = el.style.width = node.width,
h = el.height = el.style.height = node.height,
bin = Base64.utob(txt);
ctx.drawImage(node, 0, 0);
var imd = ctx.getImageData(0, 0, w, h),
d = imd.data,
a = [];
for(var i = 0, l = d.length; i < l; i += 4) {
var ord = bin.charCodeAt(i >> 2);
d[i] = (d[i] & 0xf8) + ((ord & 0xf8) >> 5);
d[i+1] = (d[i+1] & 0xfc) + ((ord & 0x18) >> 3);
d[i+2] = (d[i+2] & 0xf8) + (ord & 0x07);
}
ctx.putImageData(imd, 0, 0);
return el.toDataURL('image/png');
};
See Also:
Dan the Steganographer
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。