mirror of
https://github.com/javl/image2cpp.git
synced 2026-07-27 19:56:07 +00:00
multiple-files
1. Re-arranged html structure, used html5 features. 2. Added multiple images capability, images can be added one-by-one or selecting them with ctrl/cmd from the file dialog. 4. Output for multiple images can be represented by a single byte array or with a byte array for each images, its identifier will be increased with a trailing counter. 3. Added ‘GFXfontBitmap’ support. 4. Other smaller improvement. NOTE: still under testing.
This commit is contained in:
+606
-191
@@ -3,161 +3,341 @@
|
||||
Original utility by:
|
||||
https://jaspervanloenen.com
|
||||
|
||||
This is a modified version by:
|
||||
https://wiredolphine.net
|
||||
This is the 2nd revision of the modified version by:
|
||||
https://wiredolphin.net
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>image2cpp</title>
|
||||
|
||||
<style type="text/css">
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body{
|
||||
font-family: arial;
|
||||
}
|
||||
.wrapper{
|
||||
.wrapper {
|
||||
width: 800px;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.numberInput{
|
||||
width: 50px;
|
||||
.section {
|
||||
margin: 10px 0;
|
||||
}
|
||||
label{
|
||||
width: 220px;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
.bottom-divider {
|
||||
border-bottom: 2px solid #000000;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
label.smallLabel{
|
||||
width: auto;
|
||||
.sub-section {
|
||||
clear: both;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
td{
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
td.or{
|
||||
padding: 20px;
|
||||
width: 100px;
|
||||
font-size: 30px;
|
||||
/*vertical-align: center;*/
|
||||
}
|
||||
hr{
|
||||
height: 3px;
|
||||
background-color: black;
|
||||
border: none;
|
||||
}
|
||||
img.inlineImg{
|
||||
display: inline-block;
|
||||
}
|
||||
textarea.fullWidth{
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
table{
|
||||
.section, .sub-section {
|
||||
width: 100%;
|
||||
}
|
||||
h1{
|
||||
margin: 0;
|
||||
.column {
|
||||
float: left;
|
||||
min-width: 100px;
|
||||
margin: 0 20px 0 0;
|
||||
}
|
||||
#variableNameContainer {
|
||||
.sub-section-title {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
p {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.table { display: table; width: 100%; margin: 10px 0 0;}
|
||||
.table-row { display: table-row; width: 100%; }
|
||||
.table-cell { display: table-cell; padding: 5px 0;}
|
||||
.table-cell:first-child { width: 30%; }
|
||||
.table-cell:last-child { width: 70%; }
|
||||
.table-cell:first-child label {font-weight: bold; }
|
||||
.table-cell:last-child label { margin-right: 10px; }
|
||||
.nested-table { margin: 0; }
|
||||
.nested-table .table-cell { font-size: .9em; color: #666; width: 200px; }
|
||||
.nested-table .table-cell:first-child { }
|
||||
#format-caption-container div {
|
||||
display: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding: 10px 0 15px;
|
||||
font-size: .9em;
|
||||
line-height: 1.4em;
|
||||
color: #505050;
|
||||
}
|
||||
.byte-input {
|
||||
min-width: 400px;
|
||||
min-height: 200px;
|
||||
}
|
||||
.code-output {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
.note { color: #666666; margin: 3px 0; font-size: .9em; line-height: 1.4em; }
|
||||
button {
|
||||
margin: 10px 0;
|
||||
background: #00CB99;
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
color: #fff;
|
||||
font-weight: 100;
|
||||
font-size: .9em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.generate-button {
|
||||
margin: 40px 0 20px;
|
||||
}
|
||||
.remove-button {
|
||||
margin: 0 0 0 10px;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
.file-info {
|
||||
font-size: .7em;
|
||||
color: #505050;
|
||||
margin-left: 20px;
|
||||
white-space: pre;
|
||||
max-width: 300px;
|
||||
}
|
||||
.size-input{ width: 45px; }
|
||||
.glyph-input { width: 80px; margin-left: 10px; }
|
||||
#image-size-settings { list-style-type: none; }
|
||||
#image-size-settings li { margin: 4px 0; }
|
||||
#images-canvas-container canvas {
|
||||
border: 3px solid #00CB99;
|
||||
margin: 10px 15px;
|
||||
}
|
||||
#images-canvas-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
#extra-settings-container { }
|
||||
#arduino-identifier, #adafruit-gfx-settings, #all-same-size { display: none; }
|
||||
.error-msg { color: #ff0000; font-size: 1.2em; display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<h1>image2cpp</h1>
|
||||
<p>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<h2>1. Select image</h2>
|
||||
<input type="file" id="fileInput" name="fileinput"/><br />
|
||||
<label>Selected image size:</label> <span id="imageSize">no image selected</span>
|
||||
</h4>
|
||||
<td class="or">or</td>
|
||||
<td>
|
||||
<h2>1. Paste byte array</h2>
|
||||
<textarea id="textInput" class="fullWidth"></textarea><br />
|
||||
<section class="section">
|
||||
<h1>image2cpp</h1>
|
||||
<p><a href="https://github.com/javl/image2cpp" target="_blank">Click here to visit the Github repo for this tool.</a></p>
|
||||
</section>
|
||||
<section class="section bottom-divider">
|
||||
<section class="sub-section">
|
||||
<div class="column">
|
||||
<h2 class="sub-section-title">1. Select image</h2>
|
||||
<input type="file" id="file-input" name="file-input" multiple/><br />
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="sub-section-title">or</h2>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="sub-section-title">1. Paste byte array</h2>
|
||||
<textarea id="byte-input" class="byte-input"></textarea><br />
|
||||
<button onclick="handleTextInput('horizontal')">Read as horizontal</button>
|
||||
<button onclick="handleTextInput('vertical')">Read as vertical</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section class="section bottom-divider">
|
||||
<h2>2. Image Settings</h2>
|
||||
<label>Canvas size: </label> <input id="screenWidth" class="numberInput" type="text" name="screenWidth" onchange="updateInteger('screenWidth')" value="128"/> * <input id="screenHeight" class="numberInput" type="text" name="screenHeight" onchange="updateInteger('screenHeight')" value="64"/> pixels<br />
|
||||
<label>Background color:</label>
|
||||
<input id="backgroundColorWhite" type="radio" name="backgroundColor" value="white" checked="checked" onchange="updateRadio('backgroundColor')"/><label for="backgroundColorWhite" class="smallLabel">White</label>
|
||||
<input id="backgroundColorBlack" type="radio" name="backgroundColor" value="black" onchange="updateRadio('backgroundColor')"/><label for="backgroundColorBlack" class="smallLabel">Black</label><br />
|
||||
<label>Brightness threshold: </label> <input id="threshold" class="numberInput" type="text" name="threshold" onchange="updateInteger('threshold')" value="128"/> <i>0 - 255; pixels with brightness above become white, below become black.</i><br/>
|
||||
<label for="scale">Scaling</label>
|
||||
<select id="scale" name="scale" onchange="updateInteger('scale')">
|
||||
<option value="1">original size</option>
|
||||
<option value="2">scale to fit, keeping proportions</option>
|
||||
<option value="3">stretch to fill canvas</option>
|
||||
<option value="4">stretch to fill canvas horizontally</option>
|
||||
<option value="5">stretch to fill canvas vertically</option>
|
||||
</select><br />
|
||||
|
||||
<label for="centerHorizontally">Center horizontally</label><input id="centerHorizontally" type="checkbox" onchange="updateBoolean('centerHorizontally')" /><br />
|
||||
<label for="centerVertically">Center vertically</label><input id="centerVertically" type="checkbox" onchange="updateBoolean('centerVertically')" /><br />
|
||||
<i>Centering the image only works when using a canvas larger than the selected image.</i><br />
|
||||
<label for="invertColors">Invert image colors</label><input id="invertColors" type="checkbox" onchange="updateBoolean('invertColors')" />
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<section class="sub-section">
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>Canvas size/s: </label></div>
|
||||
<div class="table-cell">
|
||||
<ul id="image-size-settings"></ul>
|
||||
<div id="only-images-file-error" class="error-msg">Only images file type are allowed</div>
|
||||
<button id="all-same-size">all same size</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>Background color:</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="backgroundColorWhite" type="radio" name="backgroundColor" value="white" checked="checked" onchange="updateRadio('backgroundColor')"/>
|
||||
<label for="backgroundColorWhite" class="smallLabel">White</label>
|
||||
<input id="backgroundColorBlack" type="radio" name="backgroundColor" value="black" onchange="updateRadio('backgroundColor')"/>
|
||||
<label for="backgroundColorBlack" class="smallLabel">Black</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label for="invertColors">Invert image colors</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="invertColors" type="checkbox" onchange="updateBoolean('invertColors')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>Brightness threshold: </label></div>
|
||||
<div class="table-cell">
|
||||
<input id="threshold" class="size-input" type="text" name="threshold" onchange="updateInteger('threshold')" value="128"/>
|
||||
<div class="note"><i>0 - 255; pixels with brightness above become white, below become black.</i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label for="scale">Scaling</label></div>
|
||||
<div class="table-cell">
|
||||
<select id="scale" name="scale" onchange="updateInteger('scale')">
|
||||
<option value="1">original size</option>
|
||||
<option value="2">scale to fit, keeping proportions</option>
|
||||
<option value="3">stretch to fill canvas</option>
|
||||
<option value="4">stretch to fill canvas horizontally</option>
|
||||
<option value="5">stretch to fill canvas vertically</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>Center:</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="centerHorizontally" type="checkbox" onchange="updateBoolean('centerHorizontally')" />
|
||||
<label for="centerHorizontally">horizontally</label>
|
||||
<input id="centerVertically" type="checkbox" onchange="updateBoolean('centerVertically')" />
|
||||
<label for="centerVertically">vertically</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note">
|
||||
<i>NOTE: Centering the image only works when using a canvas larger than the selected image.</i>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section class="section bottom-divider">
|
||||
<h2>3. Preview</h2>
|
||||
<canvas id="imageCanvas" style="border: 1px solid black;" width="128" height="64"></canvas>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
<section class="sub-section">
|
||||
<div id="images-canvas-container"></div>
|
||||
</section>
|
||||
</section>
|
||||
<section class="section">
|
||||
<h2>4. Output</h2>
|
||||
<label for="addArduinoCode">Add Arduino code</label><input id="addArduinoCode" type="checkbox" onchange="updateArduinoCode(this)" /><br />
|
||||
<i>Adds some extra Arduino code around the output for easy copy-paste into <a href="https://github.com/javl/image2cpp/blob/master/oled_example/oled_example.ino" target="_blank">this example</a>.</i><br />
|
||||
<p id="variableNameContainer">
|
||||
<label>Variable name:</label>
|
||||
<input id="variableName" class="textInput" type="text" name="variableName" onchange="" value="myBitmap"/>
|
||||
</p>
|
||||
<br />
|
||||
<label>Draw mode:</label>
|
||||
<input id="drawModeHorizontal" type="radio" name="drawMode" value="horizontal" checked="checked" onchange="updateRadio('drawMode')"/><label for="drawModeHorizontal" class="smallLabel">Horizontal</label>
|
||||
<input id="drawModeVertical" type="radio" name="drawMode" value="vertical" onchange="updateRadio('drawMode')"/><label for="drawModeVertical" class="smallLabel">Vertical</label><br />
|
||||
<i>If your image looks all messed up on your display, like the image below, try the other mode.</i><br />
|
||||
<br />
|
||||
<img class="inlineImg" src="img/error.jpg" width="150" height="64" alt="" /><br /><br />
|
||||
<button type="button" onclick="outputString()">Generate code</button><br /><br />
|
||||
<textarea id="outputField" class="fullWidth"></textarea>
|
||||
</p>
|
||||
<section class="sub-section">
|
||||
<div class="table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label for="outputFormat">Code output format</label></div>
|
||||
<div class="table-cell">
|
||||
<select id="outputFormat" name="outputFormat" onchange="updateOutputFormat(this)">
|
||||
<option value="plain">plain bytes</option>
|
||||
<option value="arduino">Arduino code</option>
|
||||
<option value="arduino_single">Arduino code, single bitmap</option>
|
||||
<option value="adafruit_gfx">Adafruit GFXbitmapFont</option>
|
||||
</select>
|
||||
<div id="format-caption-container">
|
||||
<div data-caption="arduino">
|
||||
Adds some extra Arduino code around the output for easy copy-paste into
|
||||
<a href="https://github.com/javl/image2cpp/blob/master/oled_example/oled_example.ino" target="_blank">this example</a>.
|
||||
If multiple images are loaded, generates a byte array for each and appends a counter to the identifier.
|
||||
</div>
|
||||
<div data-caption="arduino_single">
|
||||
Adds some extra Arduino code around the output for easy copy-paste.
|
||||
If multiple images are loaded, generates a single byte array.
|
||||
</div>
|
||||
<div data-caption="adafruit_gfx">
|
||||
Creates a <code>GFXbitmapFont</code> formatted ouput. Used by a modified version of the Adafruit GFX library.
|
||||
GitHub project and example <a href="https://github.com/wiredolphin/Adafruit-GFX-Library/tree/bitmap-font" target="_blank">here</a>.
|
||||
<br />
|
||||
<i>First ASCII character</i> value is used only if a glyph identifier of length equal to 1 is not provided for each image. The value itself will be incremented by 1 for each glyph.
|
||||
</div>
|
||||
</div>
|
||||
<div id="extra-settings-container">
|
||||
<div id="adafruit-gfx-settings" class="table nested-table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>First ASCII character (dec):</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="first-ascii-char" class="text-input" type="text" name="first-ascii-char" onchange="" value="48"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>x advance:</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="x-advance" class="text-input" type="text" name="x-advance" onchange="" value="0"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="arduino-identifier" class="table nested-table">
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>Identifier:</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="identifier" class="text-input" type="text" name="identifier" onchange="" value="myBitmap"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="table-cell"><label>Draw mode:</label></div>
|
||||
<div class="table-cell">
|
||||
<input id="drawModeHorizontal" type="radio" name="drawMode" value="horizontal" checked="checked" onchange="updateRadio('drawMode')"/>
|
||||
<label for="drawModeHorizontal" class="smallLabel">Horizontal</label>
|
||||
<input id="drawModeVertical" type="radio" name="drawMode" value="vertical" onchange="updateRadio('drawMode')"/>
|
||||
<label for="drawModeVertical" class="smallLabel">Vertical</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note">
|
||||
<i>If your image looks all messed up on your display, like the image below, try the other mode.</i>
|
||||
</div>
|
||||
<img class="inlineImg" src="img/error.jpg" width="150" height="64" alt="" />
|
||||
</section>
|
||||
<section class="sub-section">
|
||||
<button type="button" class="generate-button" onclick="outputString()">Generate code</button>
|
||||
<textarea id="code-output" class="code-output"></textarea>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
// An images collection with helper methods
|
||||
function Images() {
|
||||
var collection = [];
|
||||
this.push = function(img, canvas, glyph) {
|
||||
collection.push({ "img" : img, "canvas" : canvas, "glyph" : glyph });
|
||||
};
|
||||
this.remove = function(image) {
|
||||
var i = collection.indexOf(image);
|
||||
if(i != -1) collection.splice(i, 1);
|
||||
};
|
||||
this.each = function(f) { collection.forEach(f); };
|
||||
this.length = function() { return collection.length; };
|
||||
this.first = function() { return collection[0]; };
|
||||
this.last = function() { return collection[collection.length - 1]; };
|
||||
this.get = function(img) {
|
||||
if(img) {
|
||||
for(var i = 0; i < collection.length; i++) {
|
||||
if(collection[i].img == img) {
|
||||
return collection[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
// Add events to the file input button
|
||||
var fileInput = document.getElementById('fileInput');
|
||||
var fileInput = document.getElementById('file-input');
|
||||
fileInput.addEventListener('click', function(){this.value = null;}, false);
|
||||
fileInput.addEventListener('change', handleImageSelection, false);
|
||||
|
||||
|
||||
// Filetypes accepted by the file picker
|
||||
var fileTypes = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg'];
|
||||
|
||||
|
||||
// The canvas we will draw on
|
||||
var canvas = document.getElementById('imageCanvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var canvasContainer = document.getElementById('images-canvas-container');
|
||||
// multiple images settings container
|
||||
var imageSizeSettings = document.getElementById('image-size-settings');
|
||||
// all images same size button
|
||||
var allSameSizeButton = document.getElementById('all-same-size');
|
||||
// error message
|
||||
var onlyImagesFileError = document.getElementById("only-images-file-error");
|
||||
|
||||
|
||||
// The variable to hold our image. Global so we can easily reuse it when the
|
||||
// The variable to hold our images. Global so we can easily reuse it when the
|
||||
// user updates the settings (change canvas size, scale, invert, etc)
|
||||
var img = new Image();
|
||||
|
||||
var images;
|
||||
|
||||
// A bunch of settings used when converting
|
||||
var settings = {
|
||||
@@ -171,28 +351,29 @@
|
||||
scale: '1',
|
||||
drawMode: 'horizontal',
|
||||
threshold: 128,
|
||||
addArduinoCode: false,
|
||||
outputFormat: "plain",
|
||||
invertColors: false
|
||||
};
|
||||
|
||||
// Variable name, when 'arduino code' is required
|
||||
var variableName = 'myBitmap';
|
||||
var identifier = 'myBitmap';
|
||||
|
||||
function update() {
|
||||
images.each(function(image) { place_image(image); });
|
||||
}
|
||||
|
||||
// Easy way to update settings controlled by a textfield
|
||||
function updateInteger(fieldName){
|
||||
settings[fieldName] = document.getElementById(fieldName).value;
|
||||
place_image();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
// Easy way to update settings controlled by a checkbox
|
||||
function updateBoolean(fieldName){
|
||||
settings[fieldName] = document.getElementById(fieldName).checked;
|
||||
place_image();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
// Easy way to update settings controlled by a radiobutton
|
||||
function updateRadio(fieldName){
|
||||
var radioGroup = document.getElementsByName(fieldName);
|
||||
@@ -201,18 +382,30 @@
|
||||
settings[fieldName] = radioGroup[i].value;
|
||||
}
|
||||
}
|
||||
place_image();
|
||||
update();
|
||||
}
|
||||
|
||||
// Updates Arduino code check-box
|
||||
function updateArduinoCode(checkbox) {
|
||||
var c = document.getElementById('variableNameContainer');
|
||||
c && checkbox.checked ? c.style.display = "block" : c.style.display = "none";
|
||||
settings['addArduinoCode'] = checkbox.checked;
|
||||
function updateOutputFormat(elm) {
|
||||
|
||||
var caption = document.getElementById("format-caption-container");
|
||||
var adafruitGfx = document.getElementById("adafruit-gfx-settings");
|
||||
var arduino = document.getElementById('arduino-identifier');
|
||||
|
||||
for(var i = 0; i < caption.children.length; i++) {
|
||||
caption.children[i].style.display = "none";
|
||||
}
|
||||
caption = document.querySelector("div[data-caption='" + elm.value + "']");
|
||||
if(caption) caption.style.display = "block";
|
||||
|
||||
elm.value != "plain" ? arduino.style.display = "block" : arduino.style.display = "none";
|
||||
elm.value == "adafruit_gfx" ? adafruitGfx.style.display = "block" : adafruitGfx.style.display = " none";
|
||||
|
||||
settings['outputFormat'] = elm.value;
|
||||
}
|
||||
|
||||
// Make the canvas black and white
|
||||
function blackAndWhite(){
|
||||
function blackAndWhite(canvas, ctx){
|
||||
var imageData = ctx.getImageData(0,0,canvas.width, canvas.height);
|
||||
var data = imageData.data;
|
||||
for (var i = 0; i < data.length; i += 4) {
|
||||
@@ -227,7 +420,7 @@
|
||||
|
||||
|
||||
// Invert the colors of the canvas
|
||||
function invert() {
|
||||
function invert(canvas, ctx) {
|
||||
var imageData = ctx.getImageData(0,0,canvas.width, canvas.height);
|
||||
var data = imageData.data;
|
||||
for (var i = 0; i < data.length; i += 4) {
|
||||
@@ -238,13 +431,17 @@
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
};
|
||||
|
||||
|
||||
// Draw the image onto the canvas, taking into account color and scaling
|
||||
function place_image(){
|
||||
function place_image(image){
|
||||
|
||||
var img = image.img;
|
||||
var canvas = image.canvas;
|
||||
var ctx = canvas.getContext('2d');
|
||||
image.ctx = ctx;
|
||||
|
||||
// Make sure we're using the right canvas size
|
||||
canvas.width = settings['screenWidth'];
|
||||
canvas.height = settings['screenHeight'];
|
||||
//canvas.width = settings['screenWidth'];
|
||||
//canvas.height = settings['screenHeight'];
|
||||
|
||||
// Invert background if needed
|
||||
if (settings['invertColors']){
|
||||
@@ -293,9 +490,9 @@
|
||||
break;
|
||||
}
|
||||
// Make sure the image is black and white
|
||||
blackAndWhite();
|
||||
blackAndWhite(canvas, ctx);
|
||||
if(settings['invertColors']){
|
||||
invert();
|
||||
invert(canvas, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +500,7 @@
|
||||
// Handle inserting an image by pasting code
|
||||
function handleTextInput(drawMode){
|
||||
|
||||
var input = document.getElementById('textInput').value;
|
||||
var input = document.getElementById('byte-input').value;
|
||||
|
||||
// Remove Arduino code
|
||||
input = input.replace(/const unsigned char myBitmap \[\] PROGMEM = \{/g, "");
|
||||
@@ -326,36 +523,131 @@
|
||||
}
|
||||
|
||||
|
||||
// Handle selecting an image with the file picker
|
||||
function handleImageSelection(e){
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(event){
|
||||
// Crude check to see if the uploaded file is an image
|
||||
if (reader.result.substring (0, 10) != "data:image"){
|
||||
alert("Please select an image file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
img.onload = function(){
|
||||
// Show the size underneath the file picker
|
||||
document.getElementById('imageSize').innerHTML = img.width+" * " + img.height + " pixels";
|
||||
|
||||
// Resize the canvas and update the input fields
|
||||
document.getElementById('screenWidth').value = img.width;
|
||||
document.getElementById('screenHeight').value = img.height;
|
||||
settings['screenWidth'] = img.width;
|
||||
settings['screenHeight'] = img.height;
|
||||
|
||||
place_image();
|
||||
}
|
||||
img.src = event.target.result;
|
||||
}
|
||||
reader.readAsDataURL(e.target.files[0]);
|
||||
function allSameSize(images, files) {
|
||||
if(images.length() > 1 && images.length() == files.length) {
|
||||
var inputs = imageSizeSettings.querySelectorAll("input");
|
||||
allSameSizeButton.onclick = function() {
|
||||
for(var i = 2; i < inputs.length; i++) {
|
||||
if(inputs[i].name == "width") {
|
||||
inputs[i].value = inputs[0].value;
|
||||
inputs[i].oninput();
|
||||
}
|
||||
if(inputs[i].name == "height") {
|
||||
inputs[i].value = inputs[1].value;
|
||||
inputs[i].oninput();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
allSameSizeButton.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
// Handle selecting an image with the file picker
|
||||
function handleImageSelection(evt){
|
||||
|
||||
var files = evt.target.files;
|
||||
onlyImagesFileError.style.display = "none";
|
||||
|
||||
for (var i = 0, f; f = files[i]; i++) {
|
||||
|
||||
// Only process image files.
|
||||
if(!f.type.match('image.*')) {
|
||||
onlyImagesFileError.style.display = "block";
|
||||
continue;
|
||||
}
|
||||
|
||||
var reader = new FileReader();
|
||||
images = new Images();
|
||||
|
||||
reader.onload = (function(file) {
|
||||
return function(e) {
|
||||
// Render thumbnail.
|
||||
var img = new Image();
|
||||
|
||||
img.onload = function(){
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
var imageEntry = document.createElement('li');
|
||||
imageEntry.setAttribute('data-img', file.name);
|
||||
|
||||
var w = document.createElement('input');
|
||||
w.type = "text";
|
||||
w.name = "width";
|
||||
w.className = "size-input";
|
||||
w.value = img.width;
|
||||
w.oninput = function() { canvas.width = this.value; update(); };
|
||||
|
||||
var h = document.createElement('input');
|
||||
h.type = "text";
|
||||
h.name = "height";
|
||||
h.className = "size-input";
|
||||
h.value = img.height;
|
||||
h.oninput = function() { canvas.height = this.value; update(); };
|
||||
|
||||
var gil = document.createElement('span');
|
||||
gil.innerHTML = "glyph";
|
||||
gil.className = "file-info";
|
||||
|
||||
var gi = document.createElement('input');
|
||||
gi.type = "text";
|
||||
gi.name = "glyph";
|
||||
gi.className = "glyph-input";
|
||||
gi.onchange = function() {
|
||||
var image = images.get(img);
|
||||
image.glyph = gi.value;
|
||||
};
|
||||
|
||||
var fn = document.createElement('span');
|
||||
fn.className = "file-info";
|
||||
fn.innerHTML = file.name + " (file resolution: " + img.width + " x " + img.height + ")";
|
||||
|
||||
var rb = document.createElement('button');
|
||||
rb.className = "remove-button";
|
||||
rb.innerHTML = "remove";
|
||||
rb.onclick = function() {
|
||||
var image = images.get(img);
|
||||
canvasContainer.removeChild(image.canvas);
|
||||
images.remove(image);
|
||||
imageSizeSettings.removeChild(imageEntry);
|
||||
if(imageSizeSettings.children.length == 1) {
|
||||
allSameSizeButton.style.display = "none";
|
||||
}
|
||||
update();
|
||||
};
|
||||
|
||||
imageEntry.appendChild(w);
|
||||
imageEntry.appendChild(document.createTextNode(" x "));
|
||||
imageEntry.appendChild(h);
|
||||
imageEntry.appendChild(gil);
|
||||
imageEntry.appendChild(gi);
|
||||
imageEntry.appendChild(rb);
|
||||
imageEntry.appendChild(fn);
|
||||
|
||||
imageSizeSettings.appendChild(imageEntry);
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
canvasContainer.appendChild(canvas);
|
||||
|
||||
images.push(img, canvas, file.name.split('.')[0]);
|
||||
place_image(images.last());
|
||||
allSameSize(images, files);
|
||||
}
|
||||
img.src = e.target.result;
|
||||
};
|
||||
})(f);
|
||||
reader.readAsDataURL(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Output the image as a string for horizontally drawing displays
|
||||
function imageToStringHorizontal(){
|
||||
function imageToStringHorizontal(image){
|
||||
var ctx = image.ctx;
|
||||
var canvas = image.canvas;
|
||||
|
||||
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
var output_string = "";
|
||||
@@ -364,20 +656,20 @@
|
||||
var byteIndex = 7;
|
||||
var number = 0;
|
||||
|
||||
for (var index=0; index < imageData.data.length; index+=4){
|
||||
var avg = (imageData.data[index] + imageData.data[index +1] + imageData.data[index +2]) / 3;
|
||||
if (avg > settings['threshold']){
|
||||
for(var index = 0; index < imageData.data.length; index += 4){
|
||||
var avg = (imageData.data[index] + imageData.data[index + 1] + imageData.data[index + 2]) / 3;
|
||||
if(avg > settings['threshold']){
|
||||
number += Math.pow(2, byteIndex);
|
||||
}
|
||||
byteIndex--;
|
||||
|
||||
if(byteIndex < 0){
|
||||
var byteSet = number.toString(16);
|
||||
if (byteSet.length == 1){ byteSet = "0"+byteSet; }
|
||||
if(byteSet.length == 1){ byteSet = "0"+byteSet; }
|
||||
var b = "0x"+byteSet;
|
||||
output_string += b+", ";
|
||||
output_string += b + ", ";
|
||||
output_index++;
|
||||
if (output_index >= 16){
|
||||
if(output_index >= 16){
|
||||
output_string += "\n";
|
||||
output_index = 0;
|
||||
}
|
||||
@@ -390,10 +682,12 @@
|
||||
|
||||
|
||||
// Output the image as a string for vertically drawing displays
|
||||
function imageToStringVertical(){
|
||||
function imageToStringVertical(image){
|
||||
var x = 0;
|
||||
var y = 7;
|
||||
var page = 0;
|
||||
var ctx = image.ctx;
|
||||
var canvas = image.canvas;
|
||||
|
||||
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
var data = imageData.data;
|
||||
@@ -401,9 +695,8 @@
|
||||
var output_string = "";
|
||||
var output_index = 0;
|
||||
|
||||
|
||||
for (var p=0; p<Math.ceil(settings['screenHeight']/8);p++){
|
||||
for (var x=0;x<=settings['screenWidth'];x++){
|
||||
for(var p=0; p < Math.ceil(settings['screenHeight'] / 8); p++){
|
||||
for(var x = 0;x <= settings['screenWidth']; x++){
|
||||
// for (var x=0;x<1;x++){
|
||||
var byteIndex = 7;
|
||||
var number = 0;
|
||||
@@ -419,9 +712,9 @@
|
||||
var byteSet = number.toString(16);
|
||||
if (byteSet.length == 1){ byteSet = "0"+byteSet; }
|
||||
var b = "0x"+byteSet.toString(16);
|
||||
output_string += b+", ";
|
||||
output_string += b + ", ";
|
||||
output_index++;
|
||||
if (output_index >= 16){
|
||||
if(output_index >= 16){
|
||||
output_string += "\n";
|
||||
output_index = 0;
|
||||
}
|
||||
@@ -432,28 +725,147 @@
|
||||
|
||||
|
||||
// Get the custom arduino output variable name, if any
|
||||
function getVariableName() {
|
||||
var vn = document.getElementById('variableName');
|
||||
return vn && vn.value.length ? vn.value : variableName;
|
||||
function getIdentifier() {
|
||||
var vn = document.getElementById('identifier');
|
||||
return vn && vn.value.length ? vn.value : identifier;
|
||||
}
|
||||
|
||||
|
||||
// Output the image string to the textfield
|
||||
function outputString(){
|
||||
var output_string = "";
|
||||
if (settings['drawMode'] == 'horizontal'){
|
||||
output_string = imageToStringHorizontal();
|
||||
}else{
|
||||
output_string = imageToStringVertical();
|
||||
|
||||
var output_string = "", count = 1;
|
||||
var code = "";
|
||||
|
||||
switch(settings['outputFormat']) {
|
||||
|
||||
case "arduino": {
|
||||
|
||||
images.each(function(image) {
|
||||
if(settings['drawMode'] == 'horizontal'){
|
||||
code = imageToStringHorizontal(image);
|
||||
} else {
|
||||
code = imageToStringVertical(image);
|
||||
}
|
||||
// remove last comma and space chars
|
||||
code = code.substring(0, code.length - 3);
|
||||
|
||||
code = "\t" + code.split("\n").join("\n\t") + "\n";
|
||||
var variableCount = images.length() > 1 ? count++ : "";
|
||||
var comment = " // '" + image.glyph + "'\n";
|
||||
|
||||
code = comment + "const unsigned char "
|
||||
+ getIdentifier()
|
||||
+ variableCount
|
||||
+ " [] PROGMEM = {"
|
||||
+ "\n" + code + "};\n";
|
||||
|
||||
output_string += code;
|
||||
});
|
||||
break;
|
||||
};
|
||||
case "arduino_single": {
|
||||
var comment = "";
|
||||
images.each(function(image) {
|
||||
if(settings['drawMode'] == 'horizontal'){
|
||||
code = imageToStringHorizontal(image);
|
||||
} else {
|
||||
code = imageToStringVertical(image);
|
||||
}
|
||||
code = "\t" + code.split("\n").join("\n\t") + "\n";
|
||||
comment = "\t// '" + image.glyph + "'\n";
|
||||
output_string += comment + code;
|
||||
});
|
||||
|
||||
output_string = output_string.substring(0, output_string.length - 3);
|
||||
output_string = "const unsigned char "
|
||||
+ getIdentifier()
|
||||
+ " [] PROGMEM = {"
|
||||
+ "\n" + output_string + "\n};";
|
||||
break;
|
||||
};
|
||||
case "adafruit_gfx": {
|
||||
// bitmap
|
||||
var comment = "";
|
||||
var useGlyphs = 0;
|
||||
images.each(function(image) {
|
||||
if(settings['drawMode'] == 'horizontal'){
|
||||
code = imageToStringHorizontal(image);
|
||||
} else {
|
||||
code = imageToStringVertical(image);
|
||||
}
|
||||
code = "\t" + code.split("\n").join("\n\t") + "\n";
|
||||
comment = "\t// '" + image.glyph + "'\n";
|
||||
output_string += comment + code;
|
||||
if(image.glyph.length == 1) useGlyphs++;
|
||||
});
|
||||
|
||||
output_string = output_string.substring(0, output_string.length - 3);
|
||||
output_string = "const unsigned char "
|
||||
+ getIdentifier()
|
||||
+ " [] PROGMEM = {"
|
||||
+ "\n" + output_string + "\n};\n\n"
|
||||
+ "const GFXbitmapGlyph "
|
||||
+ getIdentifier()
|
||||
+ "Glyphs [] PROGMEM = {\n";
|
||||
|
||||
var firstAschiiChar = document.getElementById("first-ascii-char").value;
|
||||
var xAdvance = parseInt(document.getElementById("x-advance").value);
|
||||
var offset = 0;
|
||||
code = "";
|
||||
|
||||
// GFXbitmapGlyph
|
||||
images.each(function(image) {
|
||||
code += "\t{ "
|
||||
+ offset + ", "
|
||||
+ image.canvas.width + ", "
|
||||
+ image.canvas.height + ", "
|
||||
+ xAdvance + ", "
|
||||
+ "'" + (images.length() == useGlyphs ?
|
||||
image.glyph :
|
||||
String.fromCharCode(firstAschiiChar++)) + "'"
|
||||
+ " }"
|
||||
+ " // '" + image.glyph + "'"
|
||||
if(image != images.last()) code += ",";
|
||||
code += "\n";
|
||||
offset += image.canvas.width;
|
||||
});
|
||||
code += "};\n";
|
||||
output_string += code;
|
||||
|
||||
// GFXbitmapFont
|
||||
output_string += "\nconst GFXbitmapFont "
|
||||
+ getIdentifier()
|
||||
+ "Font PROGMEM = {\n"
|
||||
+ "\t(uint8_t *)"
|
||||
+ getIdentifier() + ",\n"
|
||||
+ "\t(GFXbitmapGlyph *)"
|
||||
+ getIdentifier()
|
||||
+ "Glyphs,\n"
|
||||
+ "\t" + images.length()
|
||||
+ "\n};\n"
|
||||
break;
|
||||
};
|
||||
default: {
|
||||
images.each(function(image) {
|
||||
if(settings['drawMode'] == 'horizontal'){
|
||||
code = imageToStringHorizontal(image);
|
||||
} else {
|
||||
code = imageToStringVertical(image);
|
||||
}
|
||||
|
||||
var comment = image.glyph ? (" // '" + image.glyph + "'\n") : "";
|
||||
if(image.img != images.first().img) comment = "\n" + comment;
|
||||
code = comment + code;
|
||||
output_string += code;
|
||||
});
|
||||
output_string = output_string.substring(0, output_string.length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
if(settings['addArduinoCode']){
|
||||
output_string = "const unsigned char " + getVariableName() + " [] PROGMEM = {\n" + output_string + "\n};";
|
||||
}
|
||||
document.getElementById('outputField').value = output_string;
|
||||
document.getElementById('code-output').value = output_string;
|
||||
}
|
||||
|
||||
|
||||
// Use the horizontally oriented list to draw the image
|
||||
function listToImageHorizontal(list){
|
||||
|
||||
@@ -574,15 +986,18 @@
|
||||
|
||||
// Quick and effective way to draw single pixels onto the canvas
|
||||
// using a global 1x1px large canvas
|
||||
var single_pixel = ctx.createImageData(1,1);
|
||||
var d = single_pixel.data;
|
||||
var ctx = images && images.last() ? images.last().ctx : undefined;
|
||||
if(ctx) {
|
||||
var single_pixel = ctx.createImageData(1,1);
|
||||
var d = single_pixel.data;
|
||||
|
||||
function drawPixel(x, y, color){
|
||||
d[0] = color;
|
||||
d[1] = color;
|
||||
d[2] = color;
|
||||
d[3] = 255;
|
||||
ctx.putImageData(single_pixel, x, y);
|
||||
function drawPixel(x, y, color){
|
||||
d[0] = color;
|
||||
d[1] = color;
|
||||
d[2] = color;
|
||||
d[3] = 255;
|
||||
ctx.putImageData(single_pixel, x, y);
|
||||
}/**/
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user