/** * photoeditor - v0.0.1 beta - 2012-04-30 * http://www.info-town.jp * * Copyright (c) 2012 Sawai Hiroshi */ // referred to the following site. // http://www.programmingmat.jp/webhtml_lab/canvas_image.html // http://www.programmingmat.jp/webhtml_lab/canvas_putdata.html // http://atomicrobotdesign.com/blog/javascript/draw-a-rectangle-using-the-mouse-on-the-canvas-in-less-than-40-lines-of-javascript/ // http://himaxoff.blog111.fc2.com/blog-entry-87.html // http://www.ajaxblender.com/howto-convert-image-to-grayscale-using-javascript.html // http://hooktail.org/computer/index.php?%A5%CF%A1%BC%A5%D5%A5%C8%A1%BC%A5%F3%BD%E8%CD%FD%28%A5%C7%A5%A3%A5%B6%CB%A1%29 // photoeditor aplication top level object (function() { /** * common private property */ var image, canvas, cxt, originalImageData, inputImageData, outputImageData; var checkImage = function() { if ($(image).length === 0) { appError({ message: 'not original image. drop original file' }); return false; } return true; }; var checkCanvas = function() { if ($('canvas').length === 0) { canvas = document.createElement('canvas'); $('#drop').append(canvas); cxt = canvas.getContext('2d'); } return true; }; /** * exception */ function appError(error) { alert(error.message); } /** * file open when file drag canvas * * drag handler * cancel default browser event */ $('#drop').get(0).ondragover = function(event) { return false; }; // drop handler // read file when file drag&drop to canvas $('#drop').get(0).ondrop = function(event) { var file, reader; if (!event.dataTransfer.files.length) { return false; } file = event.dataTransfer.files[0]; if (!/^image\/(png|jpeg|gif)$/.test(file.type)) { appError({ message: 'file type error when file drag open' }); return false; } reader = new FileReader(); reader.onload = function() { if ($(image).length === 0) { image = document.createElement('img'); image.draggable = false; $('#original').append(image); } image.setAttribute('src', reader.result); image.onload = function() { try { $('#org-width').val(image.width); $('#org-height').val(image.height); } catch (e) { appError({ message: 'error when load image' }); } }; }; // read image file reader.readAsDataURL(file); return false; }; /** * replace original */ $('#replace').click(function() { $(image).attr('src', canvas.toDataURL()); }); /** * grayscale * * grayscale image */ $('#grayscale').click(function(e) { var imageData, i, j, k, avg; if ($(image).length === 0) { appError({ message: 'grayscale: not original image. drop original file' }); return false; } if ($('canvas').length === 0) { canvas = document.createElement('canvas'); $('#canvas-container').append(canvas); cxt = canvas.getContext('2d'); } canvas.width = image.width; canvas.height = image.height; cxt.drawImage(image, 0, 0); imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); for (i = 0; i < imageData.height; i++) { for (j = 0; j < imageData.width; j++) { k = j * 4 + i * imageData.width * 4; avg = parseInt((imageData.data[k] + imageData.data[k + 1] + imageData.data[k + 2]) / 3, 10); imageData.data[k] = avg; imageData.data[k + 1] = avg; imageData.data[k + 2] = avg; } } cxt.putImageData(imageData, 0, 0, canvas.height, canvas.width); }); /** * mono * * most easy mono */ $('#mono').click(function(e) { var imageData, i, j, k, tmpData; if ($(image).length === 0) { appError({ message: 'mono function: not original image. drop imagefile file to File DropArea' }); return false; } if ($('canvas').length === 0) { canvas = document.createElement('canvas'); $('#canvas-container').append(canvas); cxt = canvas.getContext('2d'); } canvas.width = image.width; canvas.height = image.height; // draw image to canvas cxt.drawImage(image, 0, 0); // get ImageData object for image processing imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); // mono processing for (i = 0; i < imageData.height; i++) { for (j = 0; j < imageData.width; j++) { // index for target pixcil k = j * 4 + i * imageData.width * 4; tmpData = parseInt((imageData.data[k] + imageData.data[k + 1] + imageData.data[k + 2]) / 3, 10); // to mono tmpData = (tmpData < 128) ? 0 : 255; imageData.data[k] = tmpData; // R imageData.data[k + 1] = tmpData; // G imageData.data[k + 2] = tmpData; // B } } // draw mono to canvas cxt.putImageData(imageData, 0, 0, canvas.height, canvas.width); }); /** * halftone * * Bayer16 = [ 0, 8, 2, 10] = [B], [B'] = 16 * [B] + [4] * [12, 4, 14, 16] * [ 3, 11, 1, 9] * [15, 7, 13, 5] */ $('#halftone').click(function() { var f, g, i, j, k; if ($(image).length === 0) { appError({ message: 'grayscale: not original image. drop original file' }); return false; } if ($('canvas').length === 0) { canvas = document.createElement('canvas'); $('#canvas-container').append(canvas); cxt = canvas.getContext('2d'); } canvas.width = image.width; canvas.height = image.height; // first step: grayscale imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); for (i = 0; i < imageData.height; i++) { for (j = 0; j < imageData.width; j++) { k = j * 4 + i * imageData.width * 4; avg = parseInt((imageData.data[k] + imageData.data[k + 1] + imageData.data[k + 2]) / 3, 10); imageData.data[k] = avg; imageData.data[k + 1] = avg; imageData.data[k + 2] = avg; } } cxt.drawImage(image, 0, 0); // second step: dither imageData = cxt.getImageData(0, 0, canvas.width, canvas.height); var bayerpattern = []; bayerpattern[0] = [0, 8, 2, 10]; bayerpattern[1] = [12, 4, 14, 6]; bayerpattern[2] = [3, 11, 1, 9]; bayerpattern[3] = [15, 7, 13, 5]; for (i = 0; i < imageData.height; i++) { for (j = 0; j < imageData.width; j++) { k = j * 4 + i * imageData.width * 4; if ((bayerpattern[i % 4][j % 4] * 16 + 8) <= imageData.data[k]) { imageData.data[k] = imageData.data[k + 1] = imageData.data[k + 2] = 255; } else { imageData.data[k] = imageData.data[k + 1] = imageData.data[k + 2] = 0; } } } cxt.putImageData(imageData, 0, 0, imageData.width, imageData.height); }); }());
<h1>Photo</h1> <div id="tool-container"> <div id="drop">File Drop Area</div> <div class="panel"> オリジナル画像の情報<br> 横のサイズ: <input id="org-width" type="text" size="5" disabled="disabled"> 縦のサイズ: <input id="org-height" type="text" size="5" disabled="disabled"> </div><!-- /panel --> <div class="panel"> <button id="grayscale">グレースケール</button><br> <button id="mono">白黒</button><br> <button id="halftone">ハーフトーン</button> </div><!-- /panel --> <div class="panel"> <button id="replace">オリジナルに設定</button> </div><!-- /panel --> <div id="panel"> <a href="https://github.com/s-hiroshi/photoeditor">GitHub</a> </div> </div><!-- /tool-container --> <div id="image-container"> <div id="original"> <h3>オリジナル画像</h3> <div id="rectangle"></div> </div> <div id="canvas-container"> <h3>編集後の画像</h3> </div> </div>
@charset "utf-8"; /** * Copyright 2012 Sawai Hiroshi * http://www.info-town.jp * */ /** font size 10 77 11 85 12 93 (was 93) 13 100 14 108 (was 107) 15 116 (was 114) 16 123.1 (was 122) 17 131 (was 129) 18 138.5 (was 136) 19 146.5 (was 144) 20 153.9 (was 152) 21 161.6 (was 159) 22 167 23 174 24 182 25 189 26 197 */ /* ------------------------------------------------------------------- body ------------------------------------------------------------------- */ body { line-height: 1.5; } button { width: 140px; } /* ------------------------------------------------------------------- tool area ------------------------------------------------------------------- */ #tool-container { float: left; width: 28%; padding: 3px; margin-bottom: 10px; background: #DDD; border: 1px solid #BBB; } .panel { padding: 8px; margin: 3px; background: #EFEFEF; } #org-width, #org-height { background: #F4F4F4; } #drop { height: 100px; padding: 15px 8px; margin-bottom: 10px; background: #EFEFEF; border: 5px dotted #DDD; } /* ------------------------------------------------------------------- image area ------------------------------------------------------------------- */ #image-container { overflow: hidden; float: right; width: 70%; } #image-container h3 { margin: 10px; } #original { float: left; width: 45%; position: relative; overflow: auto; } #rectangle { z-index: 2; position: absolute; display: none; top: 0; left: 0; width: 10px; height: 10px; border: 5px solid #999; } #canvas-container { overflow: auto; width: 45%; float: right; }