Pure JS PNG Encoder imaya Follow 2013-05-13 17:35:12 License: MIT License Fork4 Fav2 View8500 Play Stop Reload Fullscreen Smart Phone Fork tree Readme JavaScript 354 lines HTML 154 lines CSS 0 lines ■ はじめに Pure JS の PNG エンコーダです。 "file drop here!" と書いてあるところに画像ファイルをD&Dして「実行」ボタンを押すとエンコードします。 ■ その他 Web Workers でエンコード処理をしています。 仕様にあるチャンクは全て実装しています。 Zlib, Deflate もフル実装していますが、他の実装と比べて若干効率が落ちることがあります。 ビルド前のソースコードは下記においてあります。 https://github.com/imaya/CanvasTool.PngEncoder 使い方書きました。 http://blog.livedoor.jp/imaya_js/archives/4883165.html Pure JS PNG Encoder CanvasTool.PngEncoder var url, prevurl; window.addEventListener('DOMContentLoaded', function(ev) { setDragAndDropEvent(); }, false); function setDragAndDropEvent() { var droparea = id_('droparea'); droparea.addEventListener('drop', function(e) { var file = e.dataTransfer.files[0]; var reader = new FileReader(); reader.onload = function () { url = reader.result; }; reader.readAsDataURL(file); e.preventDefault(); }, false); droparea.addEventListener('dragover', function(e) { e.preventDefault(); }, false); } function makeParam() { var param = {}, elem, elemId = { bitDepth: 'bitdepth', colourType: 'colourtype', filterType: 'filtertype', interlaceMethod: 'interlace' }; for (var key in elemId) { param[key] = id_(elemId[key]).value | 0; } if (id_('gamma_on').checked) { param['gamma'] = id_('gamma').valueAsNumber; } if (id_('chrm_on').checked) { param['chrm'] = {}; param['chrm']['whitePointX'] = id_('chrm_white_x').valueAsNumber; param['chrm']['whitePointY'] = id_('chrm_white_y').valueAsNumber; param['chrm']['redX'] = id_('chrm_red_x').valueAsNumber; param['chrm']['redY'] = id_('chrm_red_y').valueAsNumber; param['chrm']['greenX'] = id_('chrm_green_x').valueAsNumber; param['chrm']['greenY'] = id_('chrm_green_y').valueAsNumber; param['chrm']['blueX'] = id_('chrm_blue_x').valueAsNumber; param['chrm']['blueY'] = id_('chrm_blue_y').valueAsNumber; } if (id_('splt_on').checked) { param['splt'] = {}; param['splt']['name'] = id_('splt_name').value; param['splt']['num'] = id_('splt_num').valueAsNumber; } if (id_('srgb_on').checked) { param['srgb'] = id_('srgb_ri').value | 0; } if (id_('sbit_on').checked) { switch (param.colourType) { case CanvasTool.PngEncoder.ColourType.GRAYSCALE: param['sbit'] = [8]; break; case CanvasTool.PngEncoder.ColourType.TRUECOLOR: param['sbit'] = [8, 8, 8]; break; case CanvasTool.PngEncoder.ColourType.INDEXED_COLOR: param['sbit'] = [8, 8, 8]; break; case CanvasTool.PngEncoder.ColourType.GRAYSCALE_WITH_ALPHA: param['sbit'] = [8, 8]; break; case CanvasTool.PngEncoder.ColourType.TRUECOLOR_WITH_ALPHA: param['sbit'] = [8, 8, 8, 8]; break; default: throw new Error('unknown colour type'); } } if (id_('phys_on').checked) { param['phys'] = {}; param['phys']['x'] = id_('phys_x').valueAsNumber; param['phys']['y'] = id_('phys_y').valueAsNumber; param['phys']['unit'] = id_('phys_unit').value | 0; console.log(param['phys']); } if (id_('text_on').checked) { param['text'] = {}; param['text']['keyword'] = id_('text_keyword').value; param['text']['text'] = id_('text_text').value; } if (id_('ztxt_on').checked) { param['ztxt'] = {}; param['ztxt']['keyword'] = id_('ztxt_keyword').value; param['ztxt']['text'] = id_('ztxt_text').value; param['ztxt']['compressionMethod'] = CanvasTool.PngEncoder.CompressionMethod.DEFLATE; } if (id_('itxt_on').checked) { param['itxt'] = {}; param['itxt']['keyword'] = id_('itxt_keyword').value; param['itxt']['text'] = id_('itxt_text').value; param['itxt']['lang'] = id_('itxt_lang').value; param['itxt']['translatedKeyword'] = id_('itxt_translated_keyword').value; if ((id_('itxt_compression_flag').value | 0) === CanvasTool.PngEncoder.CompressionFlag.COMPRESSED) { param['itxt']['compressionMethod'] = CanvasTool.PngEncoder.CompressionMethod.DEFLATE; } } if (id_('histogram_on').checked) { param['hist'] = true; } if (id_('time_on').checked) { param['time'] = new Date(); } return param; } function fire(url, param) { var canvas = id_('canvas'); var ctx = canvas.getContext('2d'); var source = id_('source'); var test = { bitDepth: [1, 2, 4, 8, 16], colourType: CanvasTool.PngEncoder.ColourType, compressionMethod: CanvasTool.PngEncoder.CompressionMethod, filterMethod: CanvasTool.PngEncoder.FilterMethod.BASIC, filterType: CanvasTool.PngEncoder.BasicFilterType, interlaceMethod: CanvasTool.PngEncoder.InterlaceMethod }; if (url === void 0) { return; } if (prevurl === url) { listener(); } else { source.src = url; prevurl = url; } // イベントの登録 source.onload = listener; function listener() { var key, val, t = test; var result = id_('result'); result.innerHTML = ''; // Canvas に貼り付け canvas.width = source.naturalWidth; canvas.height = source.naturalHeight; ctx.drawImage(source, 0, 0, canvas.width, canvas.height); // toDataURL showResultToDataURL(); run(param); } } /** * テストを実行する */ function run(param) { var canvas = id_('canvas'); var error = false; resource = {}; // リソースの作成 resource.colourType = reverseKeyValue(CanvasTool.PngEncoder.ColourType); resource.compressionMethod = reverseKeyValue(CanvasTool.PngEncoder.CompressionMethod); resource.filterMethod = reverseKeyValue(CanvasTool.PngEncoder.FilterMethod); resource.filterType = reverseKeyValue(CanvasTool.PngEncoder.BasicFilterType); resource.interlaceMethod = reverseKeyValue(CanvasTool.PngEncoder.InterlaceMethod); // PNG作成 width = canvas.width; height = canvas.height; param.width = width; param.height = height; // clone var param2 = {}; for (var key in param) { param2[key] = param[key]; } run.param = param; var ar = canvas.getContext('2d').getImageData(0, 0, width, height).data; var message = { data: arraylike2array(ar), param: param2 }; run.worker.postMessage(message); console.time("worker"); console.log("hoge", run.worker); } run.worker = new Worker('http://jsrun.it/imaya/canvastool.pngencoder.demo.worker/js'); run.worker.addEventListener('message', function(event) { console.timeEnd("worker"); showResult(event.data, run.param); }, true); run.worker.addEventListener('error', function(event) { showResult('', run.param, event.message); }, true); run.param = {}; showResult.count = 0; function showResult(png, param, error) { var result = id_('result'), id = 'test' + (showResult.count++), tmp = [], key, img, paramname; // head tmp.push('<table style="border: 1px solid black; float: left; margin-top: 1em;">'); // image if (!error) { tmp.push('<tr><td colspan="2"><img id="', id, '" /></td></tr>'); } // param for (key in param) { paramname = (resource[key]) ? resource[key][param[key]] : param[key]; tmp.push( '<tr>', '<th>', key, '</th>', '<td>', paramname, '</td>', '</tr>' ); } // result if (error) { tmp.push( '<tr>', '<th>Error</th>', '<th>', error, '</th>', '</tr>' ); } else { tmp.push( '<tr>', '<th>Size</th>', '<td>', png.length, '</td>', '</tr>' ); } tmp.push('</table>'); setTimeout(function(){ result.innerHTML += tmp.join(''); if (!error) { img = id_(id); img.src = 'data:image/png;base64,' + base64encode(png); } }, 0); } /** * オブジェクトの逆引きを作る */ function reverseKeyValue(obj) { var key, ret = {}; for (key in obj) { ret[obj[key]] = key; } return ret; } /** * toDataURL の結果を表示 */ function showResultToDataURL() { var result = id_('result'); var current = result.innerHTML, tmp = [], size, dataurl, png64, png; var canvas = id_('canvas'); dataurl = canvas.toDataURL('image/png'); png64 = dataurl.slice('data:image/png;base64'.length); png = base64decode(png64); size = png.length; tmp.push('<table style="border: 1px solid black; float: left;">'); tmp.push('<tr><td colspan="2"><img src="' + dataurl + '"><td></tr>'); tmp.push('<tr><th>canvas.toDataURL() size</th><td>', size, '<td></tr>'); tmp.push('</table>'); result.innerHTML += tmp.join(''); } /** * arraylike to array * @param {Object} arraylike arraylike object. * @return {Array} converted array. */ function arraylike2array(arraylike) { var array = [], i = 0, l = arraylike.length; for (; i < l; i++) { array.push(arraylike[i]); } return array; } /** * id_ shortcut. * @param {string} id id string. * @return {Element} element. * @private */ function id_(id) { return document.getElementById(id); } /** * display toggle * @param {Element} elem target element. * @return {Element} target element. * @private */ function toggle_(elem) { if (elem.style.display === 'none') { elem.style.display = 'block'; } else { elem.style.display = 'none'; } return elem; } <script src="http://www.onicos.com/staff/iz/amuse/javascript/expert/base64.txt"></script> <div id="droparea" style="width:100%; border-radius: 10px; border: 3px dotted #888; text-align: center; font-size: 48px; color: #888">drop area</div> <br /> <fieldset> <legend>Critical Chunks</legend> Bit Depth: <select id="bitdepth"> <optgroup label="Bit Depth"> <option value="1">1</option> <option value="2">2</option> <option value="4">4</option> <option value="8">8</option> <option value="16">16</option> </optgroup> </select><br /> Colour Type: <select id="colourtype"> <optgroup label="Colour Type"> <option value="0">Grayscale</option> <option value="2">True Color</option> <option value="3">Indexed Color</option> <option value="4">Grayscale with Alpha</option> <option value="6">True Color with Alpha</option> </optgroup> </select><br /> Filter Type: <select id="filtertype"> <optgroup label="Filter Type"> <option value="0">None</option> <option value="1">Sub</option> <option value="2">Up</option> <option value="3">Average</option> <option value="4">Paeth</option> </optgroup> </select><br /> Interlace Method: <select id="interlace"> <optgroup label="Interlace Method"> <option value="0">None</option> <option value="1">Adam7</option> </optgroup> </select> </fieldset> <br /> <fieldset> <legend>Ancillary Chunks</legend> <input id="gamma_on" type="checkbox" /> <label for="gamma_on">Gamma:</label> <input id="gamma" type="number" value="1.0" /><br /> <input id="chrm_on" type="checkbox" onchange="toggle_(id_('chrm_list'));" /> <label for="chrm_on">Primary chromaticities and white point:</label> <ul id="chrm_list" style="margin: 0px; display: none;"> <li>white x: <input id="chrm_white_x" type="number" /></li> <li>white y: <input id="chrm_white_y" type="number" /></li> <li>red x: <input id="chrm_red_x" type="number" /></li> <li>red y: <input id="chrm_red_y" type="number" /></li> <li>green x: <input id="chrm_green_x" type="number" /></li> <li>green y: <input id="chrm_green_y" type="number" /></li> <li>blue x: <input id="chrm_blue_x" type="number" /></li> <li>blue y: <input id="chrm_blue_y" type="number" /></li> </ul> <br /> <input id="splt_on" type="checkbox" onchange="toggle_(id_('splt_list'));" /> <label for="splt_on">Suggested palette:</label> <ul id="splt_list" style="margin: 0px; display: none;"> <li>Palette name: <input id="splt_name" type="text" /></li> <li>Entries: <input id="splt_num" type="number" /></li> </ul> <br /> <input id="srgb_on" type="checkbox" onchange="toggle_(id_('srgb_ri'));" /> <label for="srgb_on">Standard RGB colour space:</label> <select id="srgb_ri" style="display: none;"> <optgroup label="Rendering intent"> <option value="0">Perceptual</option> <option value="1">Relative colorimetric</option> <option value="2">Saturation</option> <option value="3">Absolute colorimetric</option> </optgroup> </select> <br /> <input id="sbit_on" type="checkbox" /> <label for="sbit_on">save significant bits:</label> <br /> <input id="phys_on" type="checkbox" onchange="toggle_(id_('phys_list'));" /> <label for="phys_on">Physical pixel dimensions</label> <ul id="phys_list" style="margin: 0px; display: none;"> <li>Pixels per unit, X axis: <input id="phys_x" type="number" /></li> <li>Pixels per unit, Y axis: <input id="phys_y" type="number" /></li> <li>Unit specifier: <select id="phys_unit"> <optgroup label="Unit specifier"> <option value="0">unit is unknown</option> <option value="1">unit is the metre</option> </optgroup> </select> </li> </ul> <br /> <input id="text_on" type="checkbox" onchange="toggle_(id_('text_list'));" /> <label for="text_on">Textual data:</label> <ul id="text_list" style="margin: 0px; display: none;"> <li>Keyword: <input id="text_keyword" type="text" /></li> <li>Text: <input id="text_text" type="text" /></li> </ul> <br /> <input id="ztxt_on" type="checkbox" onchange="toggle_(id_('ztxt_list'));" /> <label for="ztxt_on">Compressed Textual data:</label> <ul id="ztxt_list" style="margin: 0px; display: none;"> <li>Keyword: <input id="ztxt_keyword" type="text" /></li> <li>Text: <input id="ztxt_text" type="text" /></li> </ul> <br /> <input id="itxt_on" type="checkbox" onchange="toggle_(id_('itxt_list'));" /> <label for="itxt_on">International Textual data:</label> <ul id="itxt_list" style="margin: 0px; display: none;"> <li>Keyword: <input id="itxt_keyword" type="text" /></li> <li>Language: <input id="itxt_lang" type="text" /></li> <li>Translated Keyword: <input id="itxt_translated_keyword" type="text" /></li> <li>Compression: <select id="itxt_compression_flag"> <optgroup label="Compression Flag"> <option value="0">Uncompress</option> <option value="1">Compress</option> </optgroup> </select></li> <li>Text: <input id="itxt_text" type="text" /></li> </ul> <br /> <input id="histogram_on" type="checkbox" /> <label for="histogram_on">Save histogram</label> <br /> <input id="time_on" type="checkbox" /> <label for="time_on">Save current time</label> <br /> </fieldset> <input type="button" value="実行" onClick="fire(url, makeParam()); return false;" /> </form> <hr /> <img id="source" style="display: none;"/> <canvas id="canvas" style="display: none;"></canvas> <div id="result"></div> Pure JS PNG Encoder ■ はじめに Pure JS の PNG エンコーダです。 "file drop here!" と書いてあるところに画像ファイルをD&Dして「実行」ボタンを押すとエンコードします。 ■ その他 Web Workers でエンコード処理をしています。 仕様にあるチャンクは全て実装しています。 Zlib, Deflate もフル実装していますが、他の実装と比べて若干効率が落ちることがあります。 ビルド前のソースコードは下記においてあります。 https://github.com/imaya/CanvasTool.PngEncoder 使い方書きました。 http://blog.livedoor.jp/imaya_js/archives/4883165.html var url, prevurl; window.addEventListener('DOMContentLoaded', function(ev) { setDragAndDropEvent(); }, false); function setDragAndDropEvent() { var droparea = id_('droparea'); droparea.addEventListener('drop', function(e) { var file = e.dataTransfer.files[0]; var reader = new FileReader(); reader.onload = function () { url = reader.result; }; reader.readAsDataURL(file); e.preventDefault(); }, false); droparea.addEventListener('dragover', function(e) { e.preventDefault(); }, false); } function makeParam() { var param = {}, elem, elemId = { bitDepth: 'bitdepth', colourType: 'colourtype', filterType: 'filtertype', interlaceMethod: 'interlace' }; for (var key in elemId) { param[key] = id_(elemId[key]).value | 0; } if (id_('gamma_on').checked) { param['gamma'] = id_('gamma').valueAsNumber; } if (id_('chrm_on').checked) { param['chrm'] = {}; param['chrm']['whitePointX'] = id_('chrm_white_x').valueAsNumber; param['chrm']['whitePointY'] = id_('chrm_white_y').valueAsNumber; param['chrm']['redX'] = id_('chrm_red_x').valueAsNumber; param['chrm']['redY'] = id_('chrm_red_y').valueAsNumber; param['chrm']['greenX'] = id_('chrm_green_x').valueAsNumber; param['chrm']['greenY'] = id_('chrm_green_y').valueAsNumber; param['chrm']['blueX'] = id_('chrm_blue_x').valueAsNumber; param['chrm']['blueY'] = id_('chrm_blue_y').valueAsNumber; } if (id_('splt_on').checked) { param['splt'] = {}; param['splt']['name'] = id_('splt_name').value; param['splt']['num'] = id_('splt_num').valueAsNumber; } if (id_('srgb_on').checked) { param['srgb'] = id_('srgb_ri').value | 0; } if (id_('sbit_on').checked) { switch (param.colourType) { case CanvasTool.PngEncoder.ColourType.GRAYSCALE: param['sbit'] = [8]; break; case CanvasTool.PngEncoder.ColourType.TRUECOLOR: param['sbit'] = [8, 8, 8]; break; case CanvasTool.PngEncoder.ColourType.INDEXED_COLOR: param['sbit'] = [8, 8, 8]; break; case CanvasTool.PngEncoder.ColourType.GRAYSCALE_WITH_ALPHA: param['sbit'] = [8, 8]; break; case CanvasTool.PngEncoder.ColourType.TRUECOLOR_WITH_ALPHA: param['sbit'] = [8, 8, 8, 8]; break; default: throw new Error('unknown colour type'); } } if (id_('phys_on').checked) { param['phys'] = {}; param['phys']['x'] = id_('phys_x').valueAsNumber; param['phys']['y'] = id_('phys_y').valueAsNumber; param['phys']['unit'] = id_('phys_unit').value | 0; console.log(param['phys']); } if (id_('text_on').checked) { param['text'] = {}; param['text']['keyword'] = id_('text_keyword').value; param['text']['text'] = id_('text_text').value; } if (id_('ztxt_on').checked) { param['ztxt'] = {}; param['ztxt']['keyword'] = id_('ztxt_keyword').value; param['ztxt']['text'] = id_('ztxt_text').value; param['ztxt']['compressionMethod'] = CanvasTool.PngEncoder.CompressionMethod.DEFLATE; } if (id_('itxt_on').checked) { param['itxt'] = {}; param['itxt']['keyword'] = id_('itxt_keyword').value; param['itxt']['text'] = id_('itxt_text').value; param['itxt']['lang'] = id_('itxt_lang').value; param['itxt']['translatedKeyword'] = id_('itxt_translated_keyword').value; if ((id_('itxt_compression_flag').value | 0) === CanvasTool.PngEncoder.CompressionFlag.COMPRESSED) { param['itxt']['compressionMethod'] = CanvasTool.PngEncoder.CompressionMethod.DEFLATE; } } if (id_('histogram_on').checked) { param['hist'] = true; } if (id_('time_on').checked) { param['time'] = new Date(); } return param; } function fire(url, param) { var canvas = id_('canvas'); var ctx = canvas.getContext('2d'); var source = id_('source'); var test = { bitDepth: [1, 2, 4, 8, 16], colourType: CanvasTool.PngEncoder.ColourType, compressionMethod: CanvasTool.PngEncoder.CompressionMethod, filterMethod: CanvasTool.PngEncoder.FilterMethod.BASIC, filterType: CanvasTool.PngEncoder.BasicFilterType, interlaceMethod: CanvasTool.PngEncoder.InterlaceMethod }; if (url === void 0) { return; } if (prevurl === url) { listener(); } else { source.src = url; prevurl = url; } // イベントの登録 source.onload = listener; function listener() { var key, val, t = test; var result = id_('result'); result.innerHTML = ''; // Canvas に貼り付け canvas.width = source.naturalWidth; canvas.height = source.naturalHeight; ctx.drawImage(source, 0, 0, canvas.width, canvas.height); // toDataURL showResultToDataURL(); run(param); } } /** * テストを実行する */ function run(param) { var canvas = id_('canvas'); var error = false; resource = {}; // リソースの作成 resource.colourType = reverseKeyValue(CanvasTool.PngEncoder.ColourType); resource.compressionMethod = reverseKeyValue(CanvasTool.PngEncoder.CompressionMethod); resource.filterMethod = reverseKeyValue(CanvasTool.PngEncoder.FilterMethod); resource.filterType = reverseKeyValue(CanvasTool.PngEncoder.BasicFilterType); resource.interlaceMethod = reverseKeyValue(CanvasTool.PngEncoder.InterlaceMethod); // PNG作成 width = canvas.width; height = canvas.height; param.width = width; param.height = height; // clone var param2 = {}; for (var key in param) { param2[key] = param[key]; } run.param = param; var ar = canvas.getContext('2d').getImageData(0, 0, width, height).data; var message = { data: arraylike2array(ar), param: param2 }; run.worker.postMessage(message); console.time("worker"); console.log("hoge", run.worker); } run.worker = new Worker('http://jsrun.it/imaya/canvastool.pngencoder.demo.worker/js'); run.worker.addEventListener('message', function(event) { console.timeEnd("worker"); showResult(event.data, run.param); }, true); run.worker.addEventListener('error', function(event) { showResult('', run.param, event.message); }, true); run.param = {}; showResult.count = 0; function showResult(png, param, error) { var result = id_('result'), id = 'test' + (showResult.count++), tmp = [], key, img, paramname; // head tmp.push('<table style="border: 1px solid black; float: left; margin-top: 1em;">'); // image if (!error) { tmp.push('<tr><td colspan="2"><img id="', id, '" /></td></tr>'); } // param for (key in param) { paramname = (resource[key]) ? resource[key][param[key]] : param[key]; tmp.push( '<tr>', '<th>', key, '</th>', '<td>', paramname, '</td>', '</tr>' ); } // result if (error) { tmp.push( '<tr>', '<th>Error</th>', '<th>', error, '</th>', '</tr>' ); } else { tmp.push( '<tr>', '<th>Size</th>', '<td>', png.length, '</td>', '</tr>' ); } tmp.push('</table>'); setTimeout(function(){ result.innerHTML += tmp.join(''); if (!error) { img = id_(id); img.src = 'data:image/png;base64,' + base64encode(png); } }, 0); } /** * オブジェクトの逆引きを作る */ function reverseKeyValue(obj) { var key, ret = {}; for (key in obj) { ret[obj[key]] = key; } return ret; } /** * toDataURL の結果を表示 */ function showResultToDataURL() { var result = id_('result'); var current = result.innerHTML, tmp = [], size, dataurl, png64, png; var canvas = id_('canvas'); dataurl = canvas.toDataURL('image/png'); png64 = dataurl.slice('data:image/png;base64'.length); png = base64decode(png64); size = png.length; tmp.push('<table style="border: 1px solid black; float: left;">'); tmp.push('<tr><td colspan="2"><img src="' + dataurl + '"><td></tr>'); tmp.push('<tr><th>canvas.toDataURL() size</th><td>', size, '<td></tr>'); tmp.push('</table>'); result.innerHTML += tmp.join(''); } /** * arraylike to array * @param {Object} arraylike arraylike object. * @return {Array} converted array. */ function arraylike2array(arraylike) { var array = [], i = 0, l = arraylike.length; for (; i < l; i++) { array.push(arraylike[i]); } return array; } /** * id_ shortcut. * @param {string} id id string. * @return {Element} element. * @private */ function id_(id) { return document.getElementById(id); } /** * display toggle * @param {Element} elem target element. * @return {Element} target element. * @private */ function toggle_(elem) { if (elem.style.display === 'none') { elem.style.display = 'block'; } else { elem.style.display = 'none'; } return elem; } <script src="http://www.onicos.com/staff/iz/amuse/javascript/expert/base64.txt"></script> <div id="droparea" style="width:100%; border-radius: 10px; border: 3px dotted #888; text-align: center; font-size: 48px; color: #888">drop area</div> <br /> <fieldset> <legend>Critical Chunks</legend> Bit Depth: <select id="bitdepth"> <optgroup label="Bit Depth"> <option value="1">1</option> <option value="2">2</option> <option value="4">4</option> <option value="8">8</option> <option value="16">16</option> </optgroup> </select><br /> Colour Type: <select id="colourtype"> <optgroup label="Colour Type"> <option value="0">Grayscale</option> <option value="2">True Color</option> <option value="3">Indexed Color</option> <option value="4">Grayscale with Alpha</option> <option value="6">True Color with Alpha</option> </optgroup> </select><br /> Filter Type: <select id="filtertype"> <optgroup label="Filter Type"> <option value="0">None</option> <option value="1">Sub</option> <option value="2">Up</option> <option value="3">Average</option> <option value="4">Paeth</option> </optgroup> </select><br /> Interlace Method: <select id="interlace"> <optgroup label="Interlace Method"> <option value="0">None</option> <option value="1">Adam7</option> </optgroup> </select> </fieldset> <br /> <fieldset> <legend>Ancillary Chunks</legend> <input id="gamma_on" type="checkbox" /> <label for="gamma_on">Gamma:</label> <input id="gamma" type="number" value="1.0" /><br /> <input id="chrm_on" type="checkbox" onchange="toggle_(id_('chrm_list'));" /> <label for="chrm_on">Primary chromaticities and white point:</label> <ul id="chrm_list" style="margin: 0px; display: none;"> <li>white x: <input id="chrm_white_x" type="number" /></li> <li>white y: <input id="chrm_white_y" type="number" /></li> <li>red x: <input id="chrm_red_x" type="number" /></li> <li>red y: <input id="chrm_red_y" type="number" /></li> <li>green x: <input id="chrm_green_x" type="number" /></li> <li>green y: <input id="chrm_green_y" type="number" /></li> <li>blue x: <input id="chrm_blue_x" type="number" /></li> <li>blue y: <input id="chrm_blue_y" type="number" /></li> </ul> <br /> <input id="splt_on" type="checkbox" onchange="toggle_(id_('splt_list'));" /> <label for="splt_on">Suggested palette:</label> <ul id="splt_list" style="margin: 0px; display: none;"> <li>Palette name: <input id="splt_name" type="text" /></li> <li>Entries: <input id="splt_num" type="number" /></li> </ul> <br /> <input id="srgb_on" type="checkbox" onchange="toggle_(id_('srgb_ri'));" /> <label for="srgb_on">Standard RGB colour space:</label> <select id="srgb_ri" style="display: none;"> <optgroup label="Rendering intent"> <option value="0">Perceptual</option> <option value="1">Relative colorimetric</option> <option value="2">Saturation</option> <option value="3">Absolute colorimetric</option> </optgroup> </select> <br /> <input id="sbit_on" type="checkbox" /> <label for="sbit_on">save significant bits:</label> <br /> <input id="phys_on" type="checkbox" onchange="toggle_(id_('phys_list'));" /> <label for="phys_on">Physical pixel dimensions</label> <ul id="phys_list" style="margin: 0px; display: none;"> <li>Pixels per unit, X axis: <input id="phys_x" type="number" /></li> <li>Pixels per unit, Y axis: <input id="phys_y" type="number" /></li> <li>Unit specifier: <select id="phys_unit"> <optgroup label="Unit specifier"> <option value="0">unit is unknown</option> <option value="1">unit is the metre</option> </optgroup> </select> </li> </ul> <br /> <input id="text_on" type="checkbox" onchange="toggle_(id_('text_list'));" /> <label for="text_on">Textual data:</label> <ul id="text_list" style="margin: 0px; display: none;"> <li>Keyword: <input id="text_keyword" type="text" /></li> <li>Text: <input id="text_text" type="text" /></li> </ul> <br /> <input id="ztxt_on" type="checkbox" onchange="toggle_(id_('ztxt_list'));" /> <label for="ztxt_on">Compressed Textual data:</label> <ul id="ztxt_list" style="margin: 0px; display: none;"> <li>Keyword: <input id="ztxt_keyword" type="text" /></li> <li>Text: <input id="ztxt_text" type="text" /></li> </ul> <br /> <input id="itxt_on" type="checkbox" onchange="toggle_(id_('itxt_list'));" /> <label for="itxt_on">International Textual data:</label> <ul id="itxt_list" style="margin: 0px; display: none;"> <li>Keyword: <input id="itxt_keyword" type="text" /></li> <li>Language: <input id="itxt_lang" type="text" /></li> <li>Translated Keyword: <input id="itxt_translated_keyword" type="text" /></li> <li>Compression: <select id="itxt_compression_flag"> <optgroup label="Compression Flag"> <option value="0">Uncompress</option> <option value="1">Compress</option> </optgroup> </select></li> <li>Text: <input id="itxt_text" type="text" /></li> </ul> <br /> <input id="histogram_on" type="checkbox" /> <label for="histogram_on">Save histogram</label> <br /> <input id="time_on" type="checkbox" /> <label for="time_on">Save current time</label> <br /> </fieldset> <input type="button" value="実行" onClick="fire(url, makeParam()); return false;" /> </form> <hr /> <img id="source" style="display: none;"/> <canvas id="canvas" style="display: none;"></canvas> <div id="result"></div> use an iframe compat browser, deer Play on jsdo.it games Author Share ブログに埋め込む QR Tag Download Complete! Description What kind of game? ■ はじめに Pure JS の PNG エンコーダです。 "file drop here!" と書いてあるところに画像ファイルをD&Dして「実行」ボタンを押すとエンコードします。 ■ その他 Web Workers でエンコード処理をしています。 仕様にあるチャンクは全て実装しています。 Zlib, Deflate もフル実装していますが、他の実装と比べて若干効率が落ちることがあります。 ビルド前のソースコードは下記においてあります。 https://github.com/imaya/CanvasTool.PngEncoder 使い方書きました。 http://blog.livedoor.jp/imaya_js/archives/4883165.html Control Device Smartphone Controllerjsdo.it WebSocket Controller» Mouse Keyboard Touch Device Fullscreen Activated Inactivated jsdo.it games から削除する Submit Author imaya URLhttp://twitter.com/y_imaya Tweet Default Panel Auto play Screenshot Readme JavaScript HTML CSS Size Width: px Height: px code <script type="text/javascript" src="http://jsdo.it/blogparts/R7YM/js"></script> application canvas png Discussion Questions on this code? Tags application canvas png Favorite by Yukisuke xui Forked sort by latest page views favorite forked pixelart minifier.js(減色機のようなもの 59naga 00 1355 0/45/13 application canvas png forked: Pure JS PNG Encoder mudcube 00 1086 355/154/10 application canvas png forked: Pure JS PNG Encoder imaya 00 1290 342/159/1 canvas png forked: Pure JS PNG Encoder Gabriel.Sixe 00 1497 342/159/1 canvas png