diff --git a/README.md b/README.md index eebda01..ba1a2e6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ An online version of this tool is live at [http://javl.github.io/image2cpp/](http://javl.github.io/image2cpp/) -image2cpp is a simple tool to convert images into byte arrays (or your array back into an image) for use with (monochrome) displays suchs as OLEDs, like those from Adafruit or Sparkfun. While searching for a way to generate these arrays, I mostly found links to a piece of Windows software. Both the flakey results and the hassle of having to boot a virtual machine just to convert an image lead to me writing this pure html + javascript solution. +image2cpp is a simple tool to convert images into byte arrays (and vice versa) for use with (monochrome) displays suchs as OLEDs, like those from Adafruit or Sparkfun. While searching for a way to generate these arrays, I mostly found links to a piece of Windows software. Both the flakey results and the hassle of having to boot a virtual machine just to convert an image lead to me writing this pure html + javascript solution. Alternatively you can also enter a byte array as input to turn it back into an image. This might be useful for debugging, or when you want to write the byte array yourself. I don't know. diff --git a/css/style.css b/css/style.css index c9647c9..742d483 100644 --- a/css/style.css +++ b/css/style.css @@ -438,6 +438,11 @@ code { max-width: 320px; } +#continue-note { + display: none; + margin-top: 30px; +} + .msg { font-size: 16px; color: var(--text-muted); @@ -495,6 +500,12 @@ input[type="radio"] { line-height: 22px; resize: vertical; } +#text-input-error { + color: var(--error); + font-size: 14px; + margin-top: 8px; + display: none; +} .text-input-size { margin: 8px 0; @@ -506,6 +517,21 @@ input[type="radio"] { width: 72px; } +.sub-field { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; +} + +.sub-field:first-child { + margin-top: 0; +} + +.sub-field-label { + min-width: 160px; +} + .glyph-input { width: 80px; margin-left: 10px; @@ -542,7 +568,7 @@ button:active { input[type="file"] { background: var(--surface-container); color: var(--on-surface-variant); - border: 2px dashed var(--outline-variant); + /* border: 2px dashed var(--outline-variant); */ border-radius: var(--radius-lg); font-size: 14px; padding: 16px 20px; @@ -589,6 +615,9 @@ input[type="file"]::file-selector-button { color: var(--on-surface-variant); } +#no-images-error { + margin-bottom: 10px; +} /* ---- File info / size list ---- */ .file-input-entry { @@ -652,9 +681,9 @@ input[type="file"]::file-selector-button { .code-output { height: 400px; width: 100%; - margin-top: 16px; + /* margin-top: 16px; */ background: var(--editor-bg); - color: #dcdcaa; + color: #000000; border: 1px solid var(--on-surface-variant); border-radius: var(--radius-xl); font-family: var(--font-mono); diff --git a/index.html b/index.html index 5c2706c..f966083 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@
image2cpp
@@ -24,7 +24,7 @@ - +
@@ -42,8 +42,8 @@

image2cpp

-

image2cpp is a simple tool to convert images into byte arrays (or arrays back into an images) for use with (monochrome) displays such as OLEDs on your Arduino or Raspberry Pi.
- It was originally made to work with the Adafruit OLED library (for which your can find an example sketch for Arduino here) but +

image2cpp is a simple tool to convert images into byte arrays (and vice versa) for use with (monochrome) displays such as OLEDs on your Arduino or Raspberry Pi.
+ It was originally made to work with the Adafruit OLED library (for which your can find an example Arduino sketch here) but has been expanded by the community to be useful in all kind of (embedded) projects.

More info (and credits) can be found in the Github repository. This is also where you can report any issues you might come across.

@@ -58,23 +58,24 @@

Select image

-
All processing is done locally in your browser; your images are not uploaded or stored anywhere online.
+
You can import multiple images at once.
All processing is done locally in your browser: your images are not uploaded or stored anywhere online.

-
-
-

or

+
+
Select more images or continue to step 2
-

Paste byte array

+

or paste byte array


x px
+
Set this to the size of your image.
Common sizes: 32x32, 64x48, 128x32, 128x64.
+
Byte array is invalid (or empty).
Read images appear at step 3 below
@@ -91,13 +92,13 @@
    Only images file type are allowed
    -
    No files selected
    +
    Select one or more images at step 1 first.
    -
    +
    @@ -105,6 +106,9 @@ +
    + Used for parts of the canvas that are not covered by your image. +
    @@ -132,7 +136,8 @@
    - 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.
    + 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. Default is 128. +
    @@ -175,7 +180,7 @@ - + @@ -208,15 +213,42 @@
    -
    +
    - + + + + + + + +
    + If your image looks all messed up on your display, like the image below, try using a different mode. +
    + + +
    +
    + +
    +
    +
    + + + + + + + +
    +
    + Outputs just the raw bytes of the image(s). +
    Adds some extra Arduino code around the output for easy copy-paste into this example. @@ -260,28 +292,6 @@
    -
    -
    -
    - - -
    - If your image looks all messed up on your display, like the image below, try using a different mode. -
    - - -
    -
    -
    @@ -314,8 +324,9 @@
    - + +
    Select one or more images at step 1 first.
    diff --git a/js/script.js b/js/script.js index d69915b..dba19f1 100644 --- a/js/script.js +++ b/js/script.js @@ -639,6 +639,21 @@ function listToImageVertical(list, canvas) { img.src = canvas.toDataURL('image/png'); } +// Set width/height preset for byte array text input +// eslint-disable-next-line no-unused-vars +function setTextInputSize(width, height) { + document.getElementById('text-input-width').value = width; + document.getElementById('text-input-height').value = height; +} + +// Show/hide the "no images" error and report whether images are available +function checkImagesAvailable() { + const error = document.getElementById('no-images-error'); + const hasImages = images.length() > 0; + error.style.display = hasImages ? 'none' : 'block'; + return hasImages; +} + // Handle inserting an image by pasting code // eslint-disable-next-line no-unused-vars function handleTextInput(drawMode) { @@ -678,6 +693,13 @@ function handleTextInput(drawMode) { // Split into list const list = input.split(','); + if (list.length === 1 && list[0] === '') { + // eslint-disable-next-line no-alert + const errorEl = document.getElementById('text-input-error'); + errorEl.style.display = 'block'; + return; + } + if (drawMode === 'horizontal') { listToImageHorizontal(list, canvas); } else { @@ -726,6 +748,12 @@ function handleImageSelection(evt) { }); } + if (files.length > 0) { + document.getElementById('continue-note').style.display = 'block'; + } else { + document.getElementById('continue-note').style.display = 'none'; + } + for (let i = 0; files[i]; i++) { // Only process image files. if (!files[i].type.match('image.*')) { @@ -751,7 +779,7 @@ function handleImageSelection(evt) { const fileInputColumnEntryRemoveButton = document.createElement('button'); fileInputColumnEntryRemoveButton.className = 'remove-button'; - fileInputColumnEntryRemoveButton.innerHTML = 'remove'; + fileInputColumnEntryRemoveButton.innerHTML = 'remove image'; const canvas = document.createElement('canvas'); @@ -799,15 +827,7 @@ function handleImageSelection(evt) { image.glyph = gi.value; }; - const fn = document.createElement('span'); - fn.className = 'file-info file-name'; - fn.innerHTML = `${file.name} (file resolution: ${img.width} x ${img.height})`; - - const rb = document.createElement('button'); - rb.className = 'remove-button'; - rb.innerHTML = 'remove'; - - const fileInputColumn = document.getElementById('file-input-column'); + const fileInputColumn = document.getElementById('file-input-column-items'); const imageSizeSettings = document.getElementById('image-size-settings'); const canvasContainer = document.getElementById('images-canvas-container'); @@ -822,6 +842,7 @@ function handleImageSelection(evt) { document.getElementById('all-same-size').style.display = 'none'; } if (images.length() === 0) { + document.getElementById('file-input').value = ''; noFileSelected.forEach((el) => { // eslint-disable-next-line no-param-reassign el.style.display = 'block'; @@ -830,7 +851,18 @@ function handleImageSelection(evt) { updateAllImages(); }; + const rb = document.createElement('button'); + rb.className = 'remove-button'; + rb.innerHTML = 'remove image'; rb.onclick = removeButtonOnClick; + + const fn = document.createElement('span'); + fn.className = 'file-info file-name'; + fn.innerHTML = `${file.name} (file resolution: ${img.width} x ${img.height})`; + + + fn.appendChild(rb); + fileInputColumnEntryRemoveButton.onclick = removeButtonOnClick; fileInputColumnEntry.appendChild(fileInputColumnEntryLabel); @@ -843,7 +875,6 @@ function handleImageSelection(evt) { imageEntry.appendChild(h); imageEntry.appendChild(gil); imageEntry.appendChild(gi); - imageEntry.appendChild(rb); imageSizeSettings.appendChild(imageEntry); @@ -856,6 +887,7 @@ function handleImageSelection(evt) { document.getElementById('all-same-size').style.display = 'block'; } placeImage(images.last()); + checkImagesAvailable(); }; img.src = file.target.result; }; @@ -883,6 +915,8 @@ function getIdentifier() { // Output the image string to the textfield // eslint-disable-next-line no-unused-vars function generateOutputString() { + if (!checkImagesAvailable()) return; + let outputString = ''; let code = ''; @@ -1018,17 +1052,24 @@ function generateOutputString() { } document.getElementById('code-output').value = outputString; - document.getElementById('copy-button').disabled = false; } // Copy the final output to the clipboard // eslint-disable-next-line no-unused-vars function copyOutput() { - navigator.clipboard.writeText(document.getElementById('code-output').value); + if (!checkImagesAvailable()) return; + + const output = document.getElementById('code-output'); + if (!output.value) { + generateOutputString(); + } + navigator.clipboard.writeText(output.value); } // eslint-disable-next-line no-unused-vars function downloadBinFile() { + if (!checkImagesAvailable()) return; + let raw = []; images.each((image) => { const data = imageToString(image) @@ -1102,12 +1143,11 @@ function updateRadio(fieldName) { } window.onload = () => { - document.getElementById('copy-button').disabled = true; - // Add events to the file input button const fileInput = document.getElementById('file-input'); fileInput.addEventListener('click', () => { this.value = null; }, false); fileInput.addEventListener('change', handleImageSelection, false); - document.getElementById('outputFormat').value = 'arduino'; - document.getElementById('outputFormat').onchange(); + const outputFormatArduino = document.getElementById('outputFormatArduino'); + outputFormatArduino.checked = true; + outputFormatArduino.onchange(); };