mirror of
https://github.com/javl/image2cpp.git
synced 2026-07-28 04:05:35 +00:00
Somewhere (SPI Interface?) there is a MSB/LSB swap when using the converter with ESP32 and the U8g2 library which distorts the output. I checked with 1bit per pixel output but added an optional bitswap checkbox, a function to do optinal swap and added it to all output functions. The lvgl online converter has got the same problem. Maybe there is is something wrong with the u8g... Since endian is a traditional an generic issue I thought it's probably a nice feature inside the converter which at least works fine for me.
1406 lines
53 KiB
HTML
1406 lines
53 KiB
HTML
<!--
|
|
"image2cpp"
|
|
Original utility by:
|
|
https://jaspervanloenen.com
|
|
|
|
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 {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin: auto;
|
|
width: 900px;
|
|
}
|
|
|
|
.section {
|
|
margin: 10px 0;
|
|
}
|
|
|
|
.bottom-divider {
|
|
border-bottom: 2px solid #000000;
|
|
padding-bottom: 20px;
|
|
}
|
|
|
|
.sub-section {
|
|
clear: both;
|
|
margin-bottom: 1px;
|
|
}
|
|
|
|
.section,
|
|
.sub-section {
|
|
width: 100%;
|
|
}
|
|
|
|
.column {
|
|
float: left;
|
|
}
|
|
|
|
.column-center {
|
|
min-width: 160px;
|
|
text-align: center;
|
|
}
|
|
|
|
.column-right {
|
|
float: right;
|
|
}
|
|
|
|
.sub-section-title {
|
|
margin: 0 0 10px;
|
|
}
|
|
|
|
p {
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.table {
|
|
display: table;
|
|
margin: 10px 0 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.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 {
|
|
color: #666;
|
|
font-size: .9em;
|
|
width: 200px;
|
|
}
|
|
|
|
#format-caption-container div {
|
|
color: #505050;
|
|
display: none;
|
|
font-size: .9em;
|
|
line-height: 1.4em;
|
|
padding: 10px 0 15px;
|
|
width: 100%;
|
|
}
|
|
|
|
.byte-input {
|
|
min-height: 160px;
|
|
min-width: 360px;
|
|
}
|
|
|
|
.code-output {
|
|
height: 200px;
|
|
width: 100%;
|
|
}
|
|
|
|
.note {
|
|
color: #666666;
|
|
font-size: .9em;
|
|
line-height: 1.4em;
|
|
margin: 3px 0;
|
|
}
|
|
|
|
.file-input-entry {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
button,
|
|
input[type="file"] {
|
|
background: #00CB99;
|
|
border-radius: 3px;
|
|
border: none;
|
|
color: #fff;
|
|
font-size: .9em;
|
|
font-weight: bold;
|
|
margin: 10px 0;
|
|
padding: 4px 8px;
|
|
}
|
|
|
|
input[type="file"] {
|
|
font-size: 1.0em;
|
|
padding: 6px 20px;
|
|
}
|
|
|
|
.generate-button {
|
|
margin: 40px 0 20px;
|
|
}
|
|
|
|
.remove-button {
|
|
margin: 0 0 0 10px;
|
|
padding: 1px 4px;
|
|
}
|
|
|
|
.file-info {
|
|
color: #505050;
|
|
font-size: .7em;
|
|
margin-left: 20px;
|
|
max-width: 300px;
|
|
white-space: pre;
|
|
}
|
|
|
|
.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 #88DAC5;
|
|
margin: 10px 15px;
|
|
}
|
|
|
|
#images-canvas-container {
|
|
align-items: flex-start;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
#arduino-identifier,
|
|
#adafruit-gfx-settings,
|
|
#all-same-size {
|
|
display: none;
|
|
}
|
|
|
|
.msg {
|
|
font-size: 1.2em;
|
|
}
|
|
|
|
.error-msg {
|
|
color: #ff0000;
|
|
display: none;
|
|
}
|
|
|
|
h1{
|
|
padding: 10px;
|
|
color: white;
|
|
background-color: #00cb99;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrapper">
|
|
<section class="section">
|
|
<h1>image2cpp</h1>
|
|
<p>image2cpp is a simple tool to change images into byte arrays (or your array back into an image) for use with Arduino and (monochrome) displays such as OLEDs.
|
|
It was originally made to work with the Adafruit OLED library. An example sketch for Arduino and this library can be found <a href="https://github.com/javl/image2cpp/blob/master/oled_example/oled_example.ino" target="_blank">here</a>.</p>
|
|
<p>More info (and credits) can be found in the <a href="https://github.com/javl/image2cpp" target="_blank">Github repository</a>. This is also where you can report any <a href="https://github.com/javl/image2cpp/issues" target="_blank">issues</a> you might come across.</p>
|
|
<p>This tool also works offline. Simply save this page to your computer and open the file in your browser.</p>
|
|
</section>
|
|
|
|
<section class="section bottom-divider">
|
|
<section class="sub-section">
|
|
<div class="column" id="file-input-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 column-center">
|
|
<h2 class="sub-section-title">or</h2>
|
|
</div>
|
|
<div class="column column-right">
|
|
<h2 class="sub-section-title">1. Paste byte array</h2>
|
|
<textarea id="byte-input" class="byte-input"></textarea><br />
|
|
<div class="text-input-size">
|
|
<input type="number" min="0" id="text-input-width" class="size-input" value="128" /> x
|
|
<input type="number" min="0" id="text-input-height" class="size-input" value="64" /> px
|
|
</div>
|
|
<button onclick="handleTextInput('horizontal')">Read as horizontal</button>
|
|
<button onclick="handleTextInput('vertical')">Read as vertical</button>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
|
|
<section class="section bottom-divider">
|
|
<h2>2. Image Settings</h2>
|
|
<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="msg error-msg">Only images file type are allowed</div>
|
|
<div id="no-file-selected" class="msg">No files selected</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>
|
|
<input id="backgroundColorTransparent" type="radio" name="backgroundColor" value="transparent" onchange="updateRadio('backgroundColor')"/>
|
|
<label for="backgroundColorTransparent" class="smallLabel">Transparent</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 / alpha threshold: </label></div>
|
|
<div class="table-cell">
|
|
<input id="threshold" class="size-input" type="number" min="0" max="255" name="threshold" oninput="updateInteger('threshold')" value="128"/>
|
|
<div class="note">
|
|
<i>0 - 255; if the brightness of a pixel is above the given level the pixel becomes white, otherwise they become black. When using alpha, opaque and transparent are used instead.</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 class="table-row">
|
|
<div class="table-cell"><label for="invertColors">Rotate image:</label></div>
|
|
<div class="table-cell">
|
|
<input id="rotate180" type="checkbox" onchange="updateBoolean('rotate180')" />
|
|
<label for="rotate180">rotate 180 degrees</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-row">
|
|
<div class="table-cell"><label>Flip:</label></div>
|
|
<div class="table-cell">
|
|
<input id="flipHorizontally" type="checkbox" onchange="updateBoolean('flipHorizontally')" />
|
|
<label for="flipHorizontally">horizontally</label>
|
|
<input id="flipVertically" type="checkbox" onchange="updateBoolean('flipVertically')" />
|
|
<label for="flipVertically">vertically</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-row">
|
|
<div class="table-cell"><label for="bitswap">Swap Bits in Byte</label></div>
|
|
<div class="table-cell">
|
|
<input id="bitswap" type="checkbox" onchange="updateBoolean('bitswap')">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="note">
|
|
<i>Note: centering the image only works when using a canvas larger than the original image.</i>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
|
|
<section class="section bottom-divider">
|
|
<h2>3. Preview</h2>
|
|
<section class="sub-section">
|
|
<div id="images-canvas-container"></div>
|
|
</section>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2>4. Output</h2>
|
|
<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/Prefix:</label></div>
|
|
<div class="table-cell">
|
|
<input id="identifier" class="text-input" type="text" name="identifier" onchange="" value="epd_bitmap_"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-row">
|
|
<div class="table-cell"><label>Draw mode:</label></div>
|
|
<div class="table-cell">
|
|
<select id="drawMode" name="drawMode" onchange="updateDrawMode(this)">
|
|
<option value="horizontal1bit">Horizontal - 1 bit per pixel</option>
|
|
<option value="vertical1bit">Vertical - 1 bit per pixel</option>
|
|
<option value="horizontal565">Horizontal - 2 bytes per pixel (565)</option>
|
|
<option value="horizontalAlpha">Horizontal - 1 bit per pixel alpha map</option>
|
|
<option value="horizontal888">Horizontal - 3 bytes per pixel (rgb888</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="note1bit">
|
|
<div class="note">
|
|
<i>If your image looks all messed up on your display, like the image below, try using a different mode.</i>
|
|
</div>
|
|
<img class="inlineImg"
|
|
src="data:image/jpeg;base64,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"
|
|
width="150" height="64" alt="" />
|
|
</div>
|
|
</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">
|
|
var ConversionFunctions = {
|
|
// Output the image as a string for horizontally drawing displays
|
|
horizontal1bit: function (data, canvasWidth, canvasHeight){
|
|
var output_string = "";
|
|
var output_index = 0;
|
|
|
|
var byteIndex = 7;
|
|
var number = 0;
|
|
|
|
// format is RGBA, so move 4 steps per pixel
|
|
for(var index = 0; index < data.length; index += 4){
|
|
// Get the average of the RGB (we ignore A)
|
|
var avg = (data[index] + data[index + 1] + data[index + 2]) / 3;
|
|
if(avg > settings["threshold"]){
|
|
number += Math.pow(2, byteIndex);
|
|
}
|
|
byteIndex--;
|
|
|
|
// if this was the last pixel of a row or the last pixel of the
|
|
// image, fill up the rest of our byte with zeros so it always contains 8 bits
|
|
if ((index != 0 && (((index/4)+1)%(canvasWidth)) == 0 ) || (index == data.length-4)) {
|
|
// for(var i=byteIndex;i>-1;i--){
|
|
// number += Math.pow(2, i);
|
|
// }
|
|
byteIndex = -1;
|
|
}
|
|
|
|
// When we have the complete 8 bits, combine them into a hex value
|
|
if(byteIndex < 0){
|
|
var byteSet = bitswap(number).toString(16);
|
|
if(byteSet.length == 1){ byteSet = "0"+byteSet; }
|
|
var b = "0x"+byteSet;
|
|
output_string += b + ", ";
|
|
output_index++;
|
|
if(output_index >= 16){
|
|
output_string += "\n";
|
|
output_index = 0;
|
|
}
|
|
number = 0;
|
|
byteIndex = 7;
|
|
}
|
|
}
|
|
return output_string;
|
|
},
|
|
|
|
|
|
// Output the image as a string for vertically drawing displays
|
|
vertical1bit: function (data, canvasWidth, canvasHeight){
|
|
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++){
|
|
var byteIndex = 7;
|
|
var number = 0;
|
|
|
|
for (var y = 7; y >= 0; y--){
|
|
var index = ((p*8)+y)*(settings["screenWidth"]*4)+x*4;
|
|
var avg = (data[index] + data[index +1] + data[index +2]) / 3;
|
|
if (avg > settings["threshold"]){
|
|
number += Math.pow(2, byteIndex);
|
|
}
|
|
byteIndex--;
|
|
}
|
|
var byteSet = bitswap(number).toString(16);
|
|
if (byteSet.length == 1){ byteSet = "0"+byteSet; }
|
|
var b = "0x"+byteSet.toString(16);
|
|
output_string += b + ", ";
|
|
output_index++;
|
|
if(output_index >= 16){
|
|
output_string += "\n";
|
|
output_index = 0;
|
|
}
|
|
}
|
|
}
|
|
return output_string;
|
|
},
|
|
|
|
// Output the image as a string for 565 displays (horizontally)
|
|
horizontal565: function (data, canvasWidth, canvasHeight){
|
|
var output_string = "";
|
|
var output_index = 0;
|
|
|
|
// format is RGBA, so move 4 steps per pixel
|
|
for(var index = 0; index < data.length; index += 4){
|
|
// Get the RGB values
|
|
var r = data[index];
|
|
var g = data[index + 1];
|
|
var b = data[index + 2];
|
|
// calculate the 565 color value
|
|
var rgb = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | ((b & 0b11111000) >> 3);
|
|
// Split up the color value in two bytes
|
|
var firstByte = (rgb >> 8) & 0xff;
|
|
var secondByte = rgb & 0xff;
|
|
|
|
var byteSet = bitswap(rgb).toString(16);
|
|
while(byteSet.length < 4){ byteSet = "0" + byteSet; }
|
|
output_string += "0x" + byteSet + ", ";
|
|
|
|
// add newlines every 16 bytes
|
|
output_index++;
|
|
if(output_index >= 16){
|
|
output_string += "\n";
|
|
output_index = 0;
|
|
}
|
|
}
|
|
return output_string;
|
|
},
|
|
// Output the image as a string for rgb888 displays (horizontally)
|
|
horizontal888: function (data, canvasWidth, canvasHeight){
|
|
var output_string = "";
|
|
var output_index = 0;
|
|
|
|
// format is RGBA, so move 4 steps per pixel
|
|
for(var index = 0; index < data.length; index += 4){
|
|
// Get the RGB values
|
|
var r = data[index];
|
|
var g = data[index + 1];
|
|
var b = data[index + 2];
|
|
// calculate the 565 color value
|
|
var rgb = (r << 16) | (g << 8) | (b);
|
|
// Split up the color value in two bytes
|
|
var firstByte = (rgb >> 8) & 0xff;
|
|
var secondByte = rgb & 0xff;
|
|
|
|
var byteSet = bitswap(rgb).toString(16);
|
|
while(byteSet.length < 8){ byteSet = "0" + byteSet; }
|
|
output_string += "0x" + byteSet + ", ";
|
|
|
|
// add newlines every 16 bytes
|
|
output_index++;
|
|
if(output_index >= canvasWidth){
|
|
output_string += "\n";
|
|
output_index = 0;
|
|
}
|
|
}
|
|
return output_string;
|
|
},
|
|
// Output the alpha mask as a string for horizontally drawing displays
|
|
horizontalAlpha: function (data, canvasWidth, canvasHeight){
|
|
var output_string = "";
|
|
var output_index = 0;
|
|
|
|
var byteIndex = 7;
|
|
var number = 0;
|
|
|
|
// format is RGBA, so move 4 steps per pixel
|
|
for(var index = 0; index < data.length; index += 4){
|
|
// Get alpha part of the image data
|
|
var alpha = data[index + 3];
|
|
if(alpha > settings["threshold"]){
|
|
number += Math.pow(2, byteIndex);
|
|
}
|
|
byteIndex--;
|
|
|
|
// if this was the last pixel of a row or the last pixel of the
|
|
// image, fill up the rest of our byte with zeros so it always contains 8 bits
|
|
if ((index != 0 && (((index/4)+1)%(canvasWidth)) == 0 ) || (index == data.length-4)) {
|
|
byteIndex = -1;
|
|
}
|
|
|
|
// When we have the complete 8 bits, combine them into a hex value
|
|
if(byteIndex < 0){
|
|
var byteSet = bitswap(number).toString(16);
|
|
if(byteSet.length == 1){ byteSet = "0"+byteSet; }
|
|
var b = "0x"+byteSet;
|
|
output_string += b + ", ";
|
|
output_index++;
|
|
if(output_index >= 16){
|
|
output_string += "\n";
|
|
output_index = 0;
|
|
}
|
|
number = 0;
|
|
byteIndex = 7;
|
|
}
|
|
}
|
|
return output_string;
|
|
}
|
|
};
|
|
|
|
// 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.getByIndex = function(index) { return collection[index]; };
|
|
this.setByIndex = function(index, img) { collection[index] = img; };
|
|
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("file-input");
|
|
fileInput.addEventListener("click", function(){this.value = null;}, false);
|
|
fileInput.addEventListener("change", handleImageSelection, false);
|
|
|
|
var fileInputColumn = document.getElementById("file-input-column");
|
|
|
|
// Filetypes accepted by the file picker
|
|
var fileTypes = ["jpg", "jpeg", "png", "bmp", "gif", "svg"];
|
|
|
|
// The canvas we will draw on
|
|
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");
|
|
// initial message
|
|
var noFileSelected = document.getElementById("no-file-selected");
|
|
|
|
// 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 images = new Images();
|
|
|
|
// A bunch of settings used when converting
|
|
var settings = {
|
|
screenWidth: 128,
|
|
screenHeight: 64,
|
|
scaleToFit: true,
|
|
preserveRatio: true,
|
|
centerHorizontally: false,
|
|
centerVertically: false,
|
|
flipHorizontally: false,
|
|
flipVertically: false,
|
|
backgroundColor: "white",
|
|
scale: "1",
|
|
drawMode: "horizontal",
|
|
threshold: 128,
|
|
outputFormat: "plain",
|
|
invertColors: false,
|
|
rotate180: false,
|
|
conversionFunction: ConversionFunctions.horizontal1bit
|
|
};
|
|
|
|
// Variable name, when "arduino code" is required
|
|
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;
|
|
update();
|
|
}
|
|
|
|
// Easy way to update settings controlled by a checkbox
|
|
function updateBoolean(fieldName){
|
|
settings[fieldName] = document.getElementById(fieldName).checked;
|
|
update();
|
|
}
|
|
|
|
// Easy way to update settings controlled by a radiobutton
|
|
function updateRadio(fieldName){
|
|
var radioGroup = document.getElementsByName(fieldName);
|
|
for (var i = 0; i < radioGroup.length; i++) {
|
|
if (radioGroup[i].checked) {
|
|
settings[fieldName] = radioGroup[i].value;
|
|
}
|
|
}
|
|
update();
|
|
}
|
|
|
|
// Updates Arduino code check-box
|
|
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;
|
|
}
|
|
|
|
function updateDrawMode(elm) {
|
|
var note = document.getElementById("note1bit");
|
|
if(elm.value == "horizontal1bit" || elm.value == "vertical1bit") {
|
|
note.style.display = "block";
|
|
} else {
|
|
note.style.display = "none";
|
|
}
|
|
|
|
var conversionFunction = ConversionFunctions[elm.value];
|
|
if(conversionFunction) {
|
|
settings.conversionFunction = conversionFunction;
|
|
}
|
|
}
|
|
|
|
// Make the canvas black and white
|
|
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) {
|
|
var avg = (data[i] + data[i +1] + data[i +2]) / 3;
|
|
avg > settings["threshold"] ? avg = 255 : avg = 0;
|
|
data[i] = avg; // red
|
|
data[i + 1] = avg; // green
|
|
data[i + 2] = avg; // blue
|
|
}
|
|
ctx.putImageData(imageData, 0, 0);
|
|
}
|
|
|
|
// Invert the colors of the canvas
|
|
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) {
|
|
data[i] = 255 - data[i]; // red
|
|
data[i + 1] = 255 - data[i + 1]; // green
|
|
data[i + 2] = 255 - data[i + 2]; // blue
|
|
}
|
|
ctx.putImageData(imageData, 0, 0);
|
|
}
|
|
|
|
// Draw the image onto the canvas, taking into account color and scaling
|
|
function place_image(image){
|
|
|
|
var img = image.img;
|
|
var canvas = image.canvas;
|
|
var ctx = canvas.getContext("2d");
|
|
image.ctx = ctx;
|
|
|
|
// Invert background if needed
|
|
if (settings["backgroundColor"] == "transparent") {
|
|
ctx.fillStyle = "rgba(0,0,0,0.0)";
|
|
ctx.globalCompositeOperation = 'copy';
|
|
} else {
|
|
if (settings["invertColors"]){
|
|
settings["backgroundColor"] == "white" ? ctx.fillStyle = "black" : ctx.fillStyle = "white";
|
|
} else {
|
|
ctx.fillStyle = settings["backgroundColor"];
|
|
}
|
|
ctx.globalCompositeOperation = 'source-over';
|
|
}
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0); // start with identity matrix transform (no rotation).
|
|
if(settings["rotate180"]){
|
|
// Matrix transformation
|
|
ctx.translate(canvas.width/2.0, canvas.height/2.0);
|
|
ctx.rotate(Math.PI);
|
|
ctx.translate(-canvas.width/2.0, -canvas.height/2.0);
|
|
}
|
|
|
|
|
|
// Offset used for centering the image when requested
|
|
var offset_x = 0;
|
|
var offset_y = 0;
|
|
|
|
switch(settings["scale"]){
|
|
case "1": // Original
|
|
if(settings["centerHorizontally"]){ offset_x = Math.round((canvas.width - img.width) / 2); }
|
|
if(settings["centerVertically"]){ offset_y = Math.round((canvas.height - img.height) / 2); }
|
|
ctx.drawImage(img, 0, 0, img.width, img.height,
|
|
offset_x, offset_y, img.width, img.height);
|
|
break;
|
|
case "2": // Fit (make as large as possible without changing ratio)
|
|
var horRatio = canvas.width / img.width;
|
|
var verRatio = canvas.height / img.height;
|
|
var useRatio = Math.min(horRatio, verRatio);
|
|
|
|
if(settings["centerHorizontally"]){ offset_x = Math.round((canvas.width - img.width*useRatio) / 2); }
|
|
if(settings["centerVertically"]){ offset_y = Math.round((canvas.height - img.height*useRatio) / 2); }
|
|
ctx.drawImage(img, 0, 0, img.width, img.height,
|
|
offset_x, offset_y, img.width * useRatio, img.height * useRatio);
|
|
break;
|
|
case "3": // Stretch x+y (make as large as possible without keeping ratio)
|
|
ctx.drawImage(img, 0, 0, img.width, img.height,
|
|
offset_x, offset_y, canvas.width, canvas.height);
|
|
break;
|
|
case "4": // Stretch x (make as wide as possible)
|
|
offset_x = 0;
|
|
if(settings["centerVertically"]){ Math.round(offset_y = (canvas.height - img.height) / 2); }
|
|
ctx.drawImage(img, 0, 0, img.width, img.height,
|
|
offset_x, offset_y, canvas.width, img.height);
|
|
break;
|
|
case "5": // Stretch y (make as tall as possible)
|
|
if(settings["centerHorizontally"]){ offset_x = Math.round((canvas.width - img.width) / 2); }
|
|
offset_y = 0;
|
|
ctx.drawImage(img, 0, 0, img.width, img.height,
|
|
offset_x, offset_y, img.width, canvas.height);
|
|
break;
|
|
}
|
|
// Make sure the image is black and white
|
|
if(settings.conversionFunction == ConversionFunctions.horizontal1bit
|
|
|| settings.conversionFunction == ConversionFunctions.vertical1bit) {
|
|
blackAndWhite(canvas, ctx);
|
|
if(settings["invertColors"]){
|
|
invert(canvas, ctx);
|
|
}
|
|
}
|
|
|
|
// Flip image if needed
|
|
if (settings["flipHorizontally"]) {
|
|
ctx.save();
|
|
ctx.scale(-1, 1);
|
|
ctx.drawImage(canvas, -canvas.width, 0);
|
|
ctx.restore();
|
|
}
|
|
if (settings["flipVertically"]) {
|
|
ctx.save();
|
|
ctx.scale(1, -1);
|
|
ctx.drawImage(canvas, 0, -canvas.height);
|
|
ctx.restore();
|
|
}
|
|
}
|
|
|
|
// Handle inserting an image by pasting code
|
|
function handleTextInput(drawMode){
|
|
|
|
var canvas = document.createElement("canvas");
|
|
canvas.width = parseInt(document.getElementById("text-input-width").value);
|
|
canvas.height = parseInt(document.getElementById("text-input-height").value);
|
|
settings["screenWidth"] = canvas.width;
|
|
settings["screenHeight"] = canvas.height;
|
|
|
|
if(canvasContainer.children.length) {
|
|
canvasContainer.removeChild(canvasContainer.firstChild);
|
|
}
|
|
canvasContainer.appendChild(canvas);
|
|
|
|
var image = new Image();
|
|
images.setByIndex(0, {"img": image, "canvas" : canvas});
|
|
|
|
var input = document.getElementById("byte-input").value;
|
|
|
|
// Remove Arduino code
|
|
input = input.replace(/const\s+(unsigned\s+char|uint8_t)\s+[a-zA-Z0-9]+\s*\[\]\s*(PROGMEM\s*)?=\s*/g, "");
|
|
input = input.replace(/\};|\{/g, "");
|
|
|
|
// Convert newlines to comma (helps to remove comments later)
|
|
input = input.replace(/\r\n|\r|\n/g, ",");
|
|
// Convert multiple commas in a row into a single one
|
|
input = input.replace(/,{2,}/g, ",");
|
|
// Remove whitespace
|
|
input = input.replace(/\s/g, "");
|
|
//Remove comments
|
|
input = input.replace(/\/\/(.+?),/g, "");
|
|
// Remove "0x"
|
|
input = input.replace(/0[xX]/g, "");
|
|
// Split into list
|
|
var list = input.split(",");
|
|
console.log(list);
|
|
|
|
if(drawMode == "horizontal"){
|
|
listToImageHorizontal(list, canvas);
|
|
}else{
|
|
listToImageVertical(list, canvas);
|
|
}
|
|
}
|
|
|
|
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";
|
|
|
|
files.length > 0 ?
|
|
noFileSelected.style.display = "none" :
|
|
noFileSelected.style.display = "block";
|
|
|
|
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();
|
|
|
|
reader.onload = (function(file) {
|
|
return function(e) {
|
|
// Render thumbnail.
|
|
var img = new Image();
|
|
|
|
img.onload = function(){
|
|
const fileInputColumnEntry = document.createElement("div");
|
|
fileInputColumnEntry.className = "file-input-entry";
|
|
|
|
const fileInputColumnEntryLabel = document.createElement("span");
|
|
fileInputColumnEntryLabel.textContent = file.name;
|
|
|
|
const fileInputColumnEntryRemoveButton = document.createElement("button");
|
|
fileInputColumnEntryRemoveButton.className = "remove-button";
|
|
fileInputColumnEntryRemoveButton.innerHTML = "remove";
|
|
|
|
var canvas = document.createElement("canvas");
|
|
|
|
var imageEntry = document.createElement("li");
|
|
imageEntry.setAttribute("data-img", file.name);
|
|
|
|
var w = document.createElement("input");
|
|
w.type = "number";
|
|
w.name = "width";
|
|
w.id = "screenWidth";
|
|
w.min = 0;
|
|
w.className = "size-input";
|
|
w.value = img.width;
|
|
settings["screenWidth"] = img.width;
|
|
w.oninput = function() { canvas.width = this.value; update(); updateInteger('screenWidth'); };
|
|
|
|
var h = document.createElement("input");
|
|
h.type = "number";
|
|
h.name = "height";
|
|
h.id = "screenHeight";
|
|
h.min = 0;
|
|
h.className = "size-input";
|
|
h.value = img.height;
|
|
settings["screenHeight"] = img.height;
|
|
h.oninput = function() { canvas.height = this.value; update(); updateInteger('screenHeight'); };
|
|
|
|
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 + ")";
|
|
fn.innerHTML += "<br />";
|
|
|
|
var rb = document.createElement("button");
|
|
rb.className = "remove-button";
|
|
rb.innerHTML = "remove";
|
|
|
|
const removeButtonOnClick = function() {
|
|
var image = images.get(img);
|
|
canvasContainer.removeChild(image.canvas);
|
|
images.remove(image);
|
|
imageSizeSettings.removeChild(imageEntry);
|
|
fileInputColumn.removeChild(fileInputColumnEntry);
|
|
if(imageSizeSettings.children.length == 1) {
|
|
allSameSizeButton.style.display = "none";
|
|
}
|
|
if(images.length() == 0) noFileSelected.style.display = "block";
|
|
update();
|
|
};
|
|
|
|
rb.onclick = removeButtonOnClick;
|
|
fileInputColumnEntryRemoveButton.onclick = removeButtonOnClick;
|
|
|
|
fileInputColumnEntry.appendChild(fileInputColumnEntryLabel);
|
|
fileInputColumnEntry.appendChild(fileInputColumnEntryRemoveButton);
|
|
fileInputColumn.appendChild(fileInputColumnEntry);
|
|
|
|
imageEntry.appendChild(fn);
|
|
imageEntry.appendChild(w);
|
|
imageEntry.appendChild(document.createTextNode(" x "));
|
|
imageEntry.appendChild(h);
|
|
imageEntry.appendChild(gil);
|
|
imageEntry.appendChild(gi);
|
|
imageEntry.appendChild(rb);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
function imageToString(image){
|
|
// extract raw image data
|
|
var ctx = image.ctx;
|
|
var canvas = image.canvas;
|
|
|
|
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
var data = imageData.data;
|
|
return settings.conversionFunction(data, canvas.width, canvas.height);
|
|
}
|
|
|
|
// Get the custom arduino output variable name, if any
|
|
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 = "", count = 1;
|
|
var code = "";
|
|
|
|
switch(settings["outputFormat"]) {
|
|
|
|
case "arduino": {
|
|
let varQuickArray = [];
|
|
let bytesUsed = 0;
|
|
// --
|
|
images.each(function(image) {
|
|
code = imageToString(image);
|
|
|
|
// Trim whitespace from end and remove trailing comma
|
|
code = code.replace(/,\s*$/,"");
|
|
|
|
code = "\t" + code.split("\n").join("\n\t") + "\n";
|
|
var variableCount = images.length() > 1 ? count++ : "";
|
|
var comment = "// '" + image.glyph + "', "+image.canvas.width+"x"+image.canvas.height+"px\n";
|
|
bytesUsed += code.split("\n").length * 16; // 16 bytes per line.
|
|
console.log(image);
|
|
|
|
let varname = getIdentifier() + image.glyph.replace(/[^a-zA-Z0-9]/g,"_");
|
|
varQuickArray.push(varname);
|
|
code = comment + "const " + getType() + " " + varname + " [] PROGMEM = {\n" + code + "};\n";
|
|
output_string += code;
|
|
});
|
|
// --
|
|
varQuickArray.sort();
|
|
// --
|
|
output_string += "\n// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = "+bytesUsed+")\n"
|
|
output_string += "const int " + (getIdentifier()+"allArray_LEN") + " = " + varQuickArray.length + ";\n"
|
|
output_string += "const " + getType() + "* " + (getIdentifier()+"allArray") + "["+varQuickArray.length+"] = {\n\t"+varQuickArray.join(",\n\t")+"\n};\n";
|
|
// --
|
|
break;
|
|
}
|
|
|
|
case "arduino_single": {
|
|
var comment = "";
|
|
images.each(function(image) {
|
|
code = imageToString(image);
|
|
code = "\t" + code.split("\n").join("\n\t") + "\n";
|
|
comment = "\t// '" + image.glyph + ", " + image.canvas.width+"x"+image.canvas.height+"px\n";
|
|
output_string += comment + code;
|
|
});
|
|
|
|
output_string = output_string.replace(/,\s*$/,"");
|
|
|
|
output_string = "const " + getType() + " " +
|
|
+ getIdentifier()
|
|
+ " [] PROGMEM = {"
|
|
+ "\n" + output_string + "\n};";
|
|
break;
|
|
}
|
|
|
|
case "adafruit_gfx": { // bitmap
|
|
var comment = "";
|
|
var useGlyphs = 0;
|
|
images.each(function(image) {
|
|
code = imageToString(image);
|
|
code = "\t" + code.split("\n").join("\n\t") + "\n";
|
|
comment = "\t// '" + image.glyph + ", " + image.canvas.width+"x"+image.canvas.height+"px\n";
|
|
output_string += comment + code;
|
|
if(image.glyph.length == 1) useGlyphs++;
|
|
});
|
|
|
|
output_string = output_string.replace(/,\s*$/,"");
|
|
output_string = "const unsigned char "
|
|
+ getIdentifier()
|
|
+ "Bitmap"
|
|
+ " [] 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++)) + "'"
|
|
+ " }";
|
|
if(image != images.last()){ code += ","; }
|
|
code += "// '" + image.glyph + "'\n";
|
|
offset += image.canvas.width;
|
|
});
|
|
code += "};\n";
|
|
output_string += code;
|
|
|
|
// GFXbitmapFont
|
|
output_string += "\nconst GFXbitmapFont "
|
|
+ getIdentifier()
|
|
+ "Font PROGMEM = {\n"
|
|
+ "\t(uint8_t *)"
|
|
+ getIdentifier() + "Bitmap,\n"
|
|
+ "\t(GFXbitmapGlyph *)"
|
|
+ getIdentifier()
|
|
+ "Glyphs,\n"
|
|
+ "\t" + images.length()
|
|
+ "\n};\n";
|
|
break;
|
|
}
|
|
default: { // plain
|
|
images.each(function(image) {
|
|
code = imageToString(image);
|
|
var comment = image.glyph ? ("// '" + image.glyph + "', " + image.canvas.width+"x"+image.canvas.height+"px\n") : "";
|
|
if(image.img != images.first().img) comment = "\n" + comment;
|
|
code = comment + code;
|
|
output_string += code;
|
|
});
|
|
// Trim whitespace from end and remove trailing comma
|
|
output_string = output_string.replace(/,\s*$/g,"");
|
|
}
|
|
}
|
|
|
|
document.getElementById("code-output").value = output_string;
|
|
}
|
|
|
|
// Use the horizontally oriented list to draw the image
|
|
function listToImageHorizontal(list, canvas){
|
|
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
var imgData = ctx.createImageData(canvas.width, canvas.height);
|
|
|
|
var index = 0;
|
|
|
|
var page = 0;
|
|
var x = 0;
|
|
var y = 7;
|
|
// round the width up to the next byte
|
|
var widthRoundedUp = Math.floor(canvas.width / 8 + (canvas.width % 8 ? 1 : 0)) * 8;
|
|
var widthCounter = 0;
|
|
|
|
// Move the list into the imageData object
|
|
for (var i=0;i<list.length;i++){
|
|
|
|
var binString = hexToBinary(list[i]);
|
|
if(!binString.valid){
|
|
alert("Something went wrong converting the string. Did you forget to remove any comments from the input?");
|
|
console.log("invalid hexToBinary: ", binString.s);
|
|
return;
|
|
}
|
|
binString = binString.result;
|
|
if (binString.length == 4){
|
|
binString = binString + "0000";
|
|
}
|
|
|
|
// Check if pixel is white or black
|
|
for(var k=0; k<binString.length; k++, widthCounter++){
|
|
// if we've counted enough bits, reset counter for next line
|
|
if(widthCounter >= widthRoundedUp) {
|
|
widthCounter = 0;
|
|
}
|
|
// skip 'artifact' pixels due to rounding up to a byte
|
|
if(widthCounter >= canvas.width) {
|
|
continue;
|
|
}
|
|
var color = 0;
|
|
if(binString.charAt(k) == "1"){
|
|
color = 255;
|
|
}
|
|
imgData.data[index] = color;
|
|
imgData.data[index+1] = color;
|
|
imgData.data[index+2] = color;
|
|
imgData.data[index+3] = 255;
|
|
|
|
index += 4;
|
|
}
|
|
}
|
|
|
|
// Draw the image onto the canvas, then save the canvas contents
|
|
// inside the img object. This way we can reuse the img object when
|
|
// we want to scale / invert, etc.
|
|
ctx.putImageData(imgData, 0, 0);
|
|
var img = new Image();
|
|
img.src = canvas.toDataURL("image/png");
|
|
images.first().img = img;
|
|
}
|
|
|
|
// Use the vertically oriented list to draw the image
|
|
function listToImageVertical(list, canvas){
|
|
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
var index = 0;
|
|
|
|
var page = 0;
|
|
var x = 0;
|
|
var y = 7;
|
|
|
|
// Move the list into the imageData object
|
|
for (var i=0;i<list.length;i++){
|
|
|
|
var binString = hexToBinary(list[i]);
|
|
if(!binString.valid){
|
|
alert("Something went wrong converting the string. Did you forget to remove any comments from the input?");
|
|
console.log("invalid hexToBinary: ", binString.s);
|
|
return;
|
|
}
|
|
binString = binString.result;
|
|
if (binString.length == 4){
|
|
binString = binString + "0000";
|
|
}
|
|
|
|
// Check if pixel is white or black
|
|
for(var k=0; k<binString.length; k++){
|
|
var color = 0;
|
|
if(binString.charAt(k) == "1"){
|
|
color = 255;
|
|
}
|
|
drawPixel(ctx, x, (page*8)+y, color);
|
|
y--;
|
|
if(y < 0){
|
|
y = 7;
|
|
x++;
|
|
if(x >= settings["screenWidth"]){
|
|
x = 0;
|
|
page++;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
// Save the canvas contents inside the img object. This way we can
|
|
// reuse the img object when we want to scale / invert, etc.
|
|
var img = new Image();
|
|
img.src = canvas.toDataURL("image/png");
|
|
images.first().img = img;
|
|
}
|
|
|
|
// Convert hex to binary
|
|
function hexToBinary(s) {
|
|
|
|
var i, k, part, ret = "";
|
|
// lookup table for easier conversion. "0" characters are
|
|
// padded for "1" to "7"
|
|
var lookupTable = {
|
|
"0": "0000", "1": "0001", "2": "0010", "3": "0011", "4": "0100",
|
|
"5": "0101", "6": "0110", "7": "0111", "8": "1000", "9": "1001",
|
|
"a": "1010", "b": "1011", "c": "1100", "d": "1101", "e": "1110",
|
|
"f": "1111", "A": "1010", "B": "1011", "C": "1100", "D": "1101",
|
|
"E": "1110", "F": "1111"
|
|
};
|
|
for (i = 0; i < s.length; i += 1) {
|
|
if (lookupTable.hasOwnProperty(s[i])) {
|
|
ret += lookupTable[s[i]];
|
|
} else {
|
|
return { valid: false, s: s };
|
|
}
|
|
}
|
|
return { valid: true, result: ret };
|
|
}
|
|
|
|
// Quick and effective way to draw single pixels onto the canvas
|
|
// using a global 1x1px large canvas
|
|
function drawPixel(ctx, x, y, color) {
|
|
var single_pixel = ctx.createImageData(1,1);
|
|
var d = single_pixel.data;
|
|
|
|
d[0] = color;
|
|
d[1] = color;
|
|
d[2] = color;
|
|
d[3] = 255;
|
|
ctx.putImageData(single_pixel, x, y);
|
|
}
|
|
|
|
// get the type (in arduino code) of the output image
|
|
// this is a bit of a hack, it's better to make this a property of the conversion function (should probably turn it into objects)
|
|
function getType() {
|
|
if (settings.conversionFunction == ConversionFunctions.horizontal565) {
|
|
return "uint16_t";
|
|
}else if(settings.conversionFunction == ConversionFunctions.horizontal888){
|
|
return "unsigned long";
|
|
}else{
|
|
return "unsigned char";
|
|
}
|
|
}
|
|
function bitswap(b) {
|
|
if(settings["bitswap"])
|
|
{
|
|
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
|
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
|
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
|
}
|
|
return b;
|
|
}
|
|
// --
|
|
|
|
document.getElementById("outputFormat").value = "arduino";
|
|
document.getElementById("outputFormat").onchange();
|
|
</script>
|
|
</body>
|
|
</html>
|