このページは、【JavaScript】 自力でCanvas画像をBMPに変換する
『パターン4:PNG/JPEGでデータ部を生成』のデモページです
ソースコード
HTML
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="./bitmap-ui.js" type="module"></script>
<style>
#inch{
width: 1in;
height: 1in;
padding: 0;
border: 0;
box-sizing: border-box;
}
</style>
</head>
<body>
<div id="inch"></div>
<p><input type="file" id="file_select"></p>
<p><label><input type="radio" name="type" value="PNG" checked>PNG</label>
<label><input type="radio" name="type" value="JPEG" >JPEG</label></p>
<p><button id="button">bmp生成</button></p>
<p><canvas id="canvas"></canvas></p>
</body>
</html>
bitmap-ui.js
import {imageDataToBmp} from "./imagedatatobmp.js";
(()=>{
window.addEventListener( "DOMContentLoaded" , ()=>{
// dpi取得
const dpi = document.getElementById("inch").offsetHeight;
start("file_select","canvas","button","type",dpi);
});
const start = (fileSelectId,canvasId,buttonId,typeName,dpm) =>{
const context = document.getElementById(canvasId).getContext("2d");
const type = document.getElementsByName(typeName);
const getType = ()=>type[ Array.prototype.findIndex.call(type, e=>e.checked ) ].value;
const image = new Image();
image.onload = ()=>{
context.canvas.width = image.width;
context.canvas.height = image.height;
context.drawImage(image, 0, 0, context.canvas.width, context.canvas.height);
};
image.onerror = ()=>alert("読み込めませんでした");
const reader = new FileReader();
reader.onload = () => image.src = reader.result
document.getElementById(fileSelectId)
.addEventListener("change",function(){
reader.readAsDataURL(this.files[0]);
});
document.getElementById(buttonId)
.addEventListener("click",()=>bitmapDownload(context,getType(),dpm));
};
// ビットマップファイルダウンロード
const bitmapDownload = (context,type,dpi) =>{
const {width,height} = context.canvas;
imageDataToBmp( context.canvas , width , height ,type , dpi )
.then( buffers => {
const blob = new Blob( buffers, { type: "octet/stream" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "image.bmp";
link.click();
URL.revokeObjectURL(link.href);
});
};
})();
imagedatatobmp.js
export {imageDataToBmp};
const imageDataToBmp = (()=>{
"use strict";
// ヘッダー生成用フォーマット定数
const [U8,U16,U32,I32] = (()=>{
const dp = DataView.prototype;
return [[dp.setUint8,1],[dp.setUint16,2],[dp.setUint32,4],[dp.setInt32,4]];
})();
// ヘッダー生成
const getHeader = (format,values)=> {
const size = format.reduce((a,b)=>a+b[1],0);
const buffer = new DataView(new ArrayBuffer( size ));
values.reduce((a,b,index)=>{
format[index][0].call(buffer,a,b,true);
return a + format[index][1];
},0);
return buffer;
};
const getImageBuffer = (canvas,type) =>{
const types = { "PNG":"image/png" , "JPEG" : "image/jpeg" };
return new Promise( resolve=>{
canvas.toBlob( blob =>resolve(blob.arrayBuffer()),types[type],0.7);
});
}
// BMPバイナリイメージの生成
return (canvas,width,height,type,dpi=96) => {
const dpm = Math.ceil((dpi === 0 ? 96 : dpi) * 39.3701 );
return getImageBuffer(canvas,type)
.then( imgBuffer =>{
// BITMAPFILEHEADERのセット
const dataOffset = 14 + 40;
const fileSize = imgBuffer.byteLength + dataOffset;
const fileHeader = getHeader( [U8,U8,U32,U16,U16,U32]
,["B".charCodeAt(0) , "M".charCodeAt(0) , fileSize , 0 , 0 ,dataOffset]);
const BI_TYPE = { "PNG":5 , "JPEG" : 4 };
const infoHeader = getHeader( [U32,U32,I32,U16,U16,U32,U32,U32,U32,U32,U32]
,[40 , width , height , 1 , 0
,BI_TYPE[type] , imgBuffer.byteLength , dpm , dpm , 0 , 0]);
return [fileHeader.buffer,infoHeader.buffer,imgBuffer];
});
};
})();