SSブログ

ビットマップの動的作成 [JavaScript]

createbmp.png
canvas 使えば!?っていうことなんですけど。

・ビットマップをバイナリーファイルの配列として作る
・ファイルイメージ上で画像を編集
・Base64でエンコードしてテキストとしてソースを指定

というゴリゴリした処理で canvas を使うことなく画像を表示します。
ネットで拾った情報をアレンジして何とか形にしたものです。
JavaScriptのオブジェクトとか、よくわかってないので間違ってたらすみません。
HTA形式なら、ファイル保存もできます。
ソースは長いですが、フルカラー24bitとモノクロ1bitの2つと、base64エンコードと、HTA用ファイル保存などの組み合わせだけで、やってることは単純です。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ビットマップの動的作成 Dynamic creation of bitmap</title>
<script>
var Bitmap = function() {       // full color (24bit)
    // Property
    this.width     = 0;
    this.height    = 0;
    this.lineBytes = 0;
    this.fileData  = [ 66,77, 54,0,0,0, 0,0, 0,0, 54,0,0,0,     // Bitmap File Header (14 bytes)
                       40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0, 0,0,0,0, 0,0,0,0,
                        0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];    // Information Header (40 bytes)
    // Method
    this.setSize = function(w, h) {
        this.width     = w;
        this.height    = h;
        this.lineBytes = ((w * 3 + 3) >> 2) << 2;
        setArrayLong( this.fileData,  2, 54 + this.lineBytes * h );
        setArrayLong( this.fileData, 18, w );
        setArrayLong( this.fileData, 22, h );
        this.fileData.length = 54 + this.lineBytes * h;
    } 
    this.setPixel = function(x, y, r, g, b) {
        var index = (this.height - y - 1) * this.lineBytes  +  x * 3;
        this.fileData[ index + 54 ] = b;
        this.fileData[ index + 55 ] = g;
        this.fileData[ index + 56 ] = r;
    }
    this.fillRect = function(x1, y1, x2, y2, r, g, b) {
        for(var x=x1; x<=x2; x++) for(var y=y1; y<=y2; y++) this.setPixel(x, y, r, g, b);
    }
    // Local Scope Function
    function setArrayLong(arr, index, num) {
        for(var i=0; i<4; i++)  arr[index + i] = ( num >> ( i * 8 ) ) & 0xff;
    }
}

var BitmapMono = function() {   // 2color monochrome (1bit)
    // Property
    this.width     = 0;
    this.height    = 0;
    this.lineBytes = 0;
    this.fileData  = [ 66,77, 62,0,0,0, 0,0, 0,0, 62,0,0,0,     // Bitmap File Header (14 bytes)
                       40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 1,0, 0,0,0,0, 0,0,0,0,
                        0,0,0,0, 0,0,0,0, 2,0,0,0, 0,0,0,0,     // Information Header (40 bytes)
                       255,255,255,0,  0,0,0,0 ];               // Color palette       (8 bytes)
    // Method
    this.setSize = function(w, h) {
        this.width     = w;
        this.height    = h;
        this.lineBytes = ((w + 31) >> 5) << 2;
        setArrayLong( this.fileData,  2, 62 + this.lineBytes * h );
        setArrayLong( this.fileData, 18, w );
        setArrayLong( this.fileData, 22, h );
        this.fileData.length = 62 + this.lineBytes * h;
    } 
    this.setPixel = function(x, y, pal) {
        var index = (this.height - y - 1) * this.lineBytes  + (x >> 3);
        if( pal ) this.fileData[ index + 62 ] |=  (128 >> (x % 8)); // 128 = 0b10000000
        else      this.fileData[ index + 62 ] &= ~(128 >> (x % 8));
    }
    this.fillRect = function(x1, y1, x2, y2, pal) {
        for(var x=x1; x<=x2; x++) for(var y=y1; y<=y2; y++) this.setPixel(x, y, pal);
    }
    // Local Scope Function
    function setArrayLong(arr, index, num) {
        for(var i=0; i<4; i++)  arr[index + i] = ( num >> ( i * 8 ) ) & 0xff;
    }
}

function saveFile( bytes, filename ) {  // File writing (* Only for HTA)
    var stream = new ActiveXObject('ADODB.Stream'); 
    stream.Type = 2;                // 1:adTypeBinary  2:adTypeText
    stream.Charset = 'iso-8859-1';  // To get as a byte type array
    stream.Open();
    for(var i=0; i<bytes.length; i++) stream.WriteText( String.fromCharCode( bytes[i] ) );
    stream.SaveToFile( filename, 2 );
    stream.Close();
}

function encodeBase64( bytes ) {        // Encode to Base64
    var i, len, table = [], base64 = '';
    for (i=65; i< 91; i++)  table.push(String.fromCharCode(i));
    for (i=97; i<123; i++)  table.push(String.fromCharCode(i));
    for (i= 0; i< 10; i++)  table.push(i.toString(10));
    table.push('+');
    table.push('/');
    for (i=0, len=bytes.length; i<len-2; i+=3) {
        base64 += table[  (bytes[i]   & 0xfc) >> 2  ];
        base64 += table[ ((bytes[i]   & 0x03) << 4) | ((bytes[i+1] & 0xf0) >> 4) ];
        base64 += table[ ((bytes[i+1] & 0x0f) << 2) | ((bytes[i+2] & 0xc0) >> 6) ];
        base64 += table[   bytes[i+2] & 0x3f        ];
    }
    if (len === i+1) {          // last 1 byte
        base64 += table[  (bytes[i]   & 0xfc) >> 2  ];
        base64 += table[ ((bytes[i]   & 0x03) << 4) ] + '==';
    }else if (len === i+2) {    // last 2 bytes
        base64 += table[  (bytes[i]   & 0xfc) >> 2  ];
        base64 += table[ ((bytes[i]   & 0x03) << 4) | ((bytes[i+1] & 0xf0) >> 4) ];
        base64 += table[ ((bytes[i+1] & 0x0f) << 2) ] + '=';
    }
    return base64;
}

function main() {
    var bmp1 = new BitmapMono();
    var bmp2 = new Bitmap();
    bmp1.setSize( 32, 32);
    for(var i=0; i<32; i++)  bmp1.setPixel( i, i, 1);
    document.getElementById('bmp').innerHTML  = '<img src="data:image/bmp;base64,'+ encodeBase64( bmp1.fileData ) + '" >';
    bmp2.setSize( 32, 32);
    for(var i=0; i<32; i++)  bmp2.setPixel( i, i, i*8, 255-i*8, 0);
    document.getElementById('bmp').innerHTML += '<img src="data:image/bmp;base64,'+ encodeBase64( bmp2.fileData ) + '" >';
    // * Can be saved for HTA
    // saveFile(bmp1.fileData, 'test1.bmp');
    // saveFile(bmp2.fileData, 'test2.bmp');
}
</script>
</head>
<body>
<h1>ビットマップの動的作成</h1>
<p>
<ul>
  <li>ビットマップをバイナリーファイルの配列として作る
  <li>ファイルイメージ上で画像を編集
  <li>Base64でエンコードしてテキストとしてソースを指定
</ul>
<p><button onclick="main()">create Bitmap</button>
<p id="bmp"></p>
</body>
</html>

タグ:HTA
nice!(0)  コメント(0)