ビットマップの動的作成

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>

この記事へのコメント