summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pixeled.css177
-rw-r--r--pixeled.js318
-rw-r--r--ui/black-pen.pngbin0 -> 626 bytes
-rw-r--r--ui/blank-canvas.pngbin0 -> 645 bytes
-rw-r--r--ui/clear-screen.pngbin0 -> 760 bytes
-rw-r--r--ui/custom-color-mask.pngbin0 -> 1163 bytes
-rw-r--r--ui/custom-color.pngbin0 -> 773 bytes
-rw-r--r--ui/cyan-button-pressed.pngbin0 -> 654 bytes
-rw-r--r--ui/cyan-button.pngbin0 -> 7164 bytes
-rw-r--r--ui/darken-tool.pngbin0 -> 608 bytes
-rw-r--r--ui/eraser.pngbin0 -> 757 bytes
-rw-r--r--ui/green-button-pressed.pngbin0 -> 654 bytes
-rw-r--r--ui/green-button.pngbin0 -> 6899 bytes
-rw-r--r--ui/grid-toggle.pngbin0 -> 565 bytes
-rw-r--r--ui/lighten-tool.pngbin0 -> 625 bytes
-rw-r--r--ui/rainbow-pen.pngbin0 -> 694 bytes
-rw-r--r--ui/save-file.pngbin0 -> 797 bytes
-rw-r--r--ui/white-pen.pngbin0 -> 635 bytes
-rw-r--r--ui/yellow-button-pressed.pngbin0 -> 657 bytes
-rw-r--r--ui/yellow-button.pngbin0 -> 7348 bytes
20 files changed, 387 insertions, 108 deletions
diff --git a/pixeled.css b/pixeled.css
index 5f02f19..1cef83a 100644
--- a/pixeled.css
+++ b/pixeled.css
@@ -2,36 +2,26 @@ html, body {
margin: 0;
padding: 0;
user-select: none;
- font-size: 1vmin;
+ font-size: 10px;
color: #fff;
}
-button {
- border: 0;
- font-size: inherit;
- font-family: inherit;
- color: inherit;
- font-size: 3rem;
- margin-collapse: collapse;
- text-shadow: 0 0 3px #000;
+body {
+ overflow: hidden;
}
#app_area {
display: grid;
- grid-template-areas:
- "bar editor";
- grid-template-columns: minmax(50rem,30vw) auto;
height: 100vh;
width: 100vw;
}
/* The area behind the grid */
#workspace {
- background: #1e3526 linear-gradient(to right, #000000 -7%, transparent 8%);
- grid-area: editor;
+ background: #1e3526 linear-gradient(to right, #000000 -7%, transparent 8%, transparent 92%, #000000 107%);
display: grid;
overflow: scroll;
- grid-template-rows: calc(100% - 7rem) 1fr;
+ z-index: 1;
}
/* the canvas we're drawing on in the app */
@@ -43,7 +33,7 @@ button {
background: #888;
aspect-ratio: 1;
box-shadow: 0 0 3.2rem #000;
- z-index: 1;
+ z-index: 10;
height: 75vmin;
align-self: center;
justify-self: center;
@@ -61,93 +51,164 @@ button {
border-collapse: collapse;
}
-#grid_caption {
- align-self: center;
- justify-self: center;
- font-size: 4rem;
-}
-
#toolbox {
- grid-area: bar;
- background: #00762a;
- border-right: 3px solid #0b3a1d;
+ background: #00000088;
+ border: 1px solid #000;
+ border-radius: 8px;
display: grid;
box-sizing: border-box;
- padding: 0 1em 1em 1em;
- font-size: 3rem;
+ box-shadow: 0 0 16px #000;
+ padding: 6px;
+ font-size: 14px;
font-family: "PT Mono", "Envy Code R", "Liberation Mono", "DejaVu Sans", monospace;
font-weight: bold;
- z-index: 2;
- gap: 2rem;
- grid-template-rows: 10vh 15vh 20vh 1fr 1fr 1fr 1fr 1fr;
+ position: absolute;
+ z-index: 20;
+ gap: 8px;
+ grid-template-rows: 26px 1fr 60px;
+ top: 16px;
+ left: 16px;
+ width: 184px;
}
#toolbox > header {
margin: 0;
padding: 0;
- font-size: 3em;
+ font-size: 1.3em;
text-align: center;
text-shadow: 0 0 3px #000;
- align-self: center;
- justify-self: center;
+ line-height: 26px;
}
-#color_picker {
- display: grid;
- grid-template-columns: 1fr 1fr 1fr;
+#tool_palette {
+ display: flex;
+ flex-flow: row wrap;
+ gap: 3px;
}
-#color_picker > div {
+#tool_palette div {
+ background: url("./ui/green-button.png") no-repeat top left transparent;
display: grid;
- grid-template-rows: 70% 30%;
text-align: center;
font-weight: bold;
font-size: 1.3em;
+ image-rendering: crisp-edges;
+ flex-basis: 40px;
+ height: 48px;
+}
+
+#tool_palette div:hover {
+ background-image: url("./ui/cyan-button.png");
+}
+
+#tool_palette > div.selected {
+ background: url("./ui/green-button-pressed.png") no-repeat top left;
+ image-rendering: crisp-edges;
+}
+
+#tool_palette > div.active {
+ background: url("./ui/yellow-button-pressed.png") no-repeat top left;
+ image-rendering: crisp-edges;
}
-#color_picker > div.selected {
- background: rgba(255,255,255, 0.4);
+#tool_palette div.selected .swatch,
+#tool_palette div.active .swatch {
+ margin-top: 9px;
}
-#color_picker > div > .swatch {
+#tool_palette > div > .swatch {
aspect-ratio: 1;
- margin: 0.4em auto 0.2em auto;
+ margin: 4px auto 0 auto;
display: block;
- height: 4rem;
+ width: 32px;
+ height: 32px;
}
#black_pick > .swatch {
- background: #000;
+ background: url("./ui/black-pen.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
}
#white_pick > .swatch {
- background: #fff;
+ background: url("./ui/eraser.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
}
#custom_pick > .swatch {
+ mask: url("./ui/custom-color-mask.png") no-repeat center center;
+ mask-size: contain;
+ border: 0;
+ padding: 0;
}
-#canvas_opts {
- display: grid;
- grid-template-rows: 2.5rem 1fr 1fr;
+#custom_color::-moz-color-swatch {
+ background: url("./ui/custom-color.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+#rainbow > .swatch {
+ background: url("./ui/rainbow-pen.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
+}
+
+#darken > .swatch {
+ background: url("./ui/darken-tool.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
+}
+
+#lighten > .swatch {
+ background: url("./ui/lighten-tool.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
+}
+
+#clear_canvas > .swatch {
+ background: url("./ui/blank-canvas.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
+}
+
+#grid_toggle > .swatch {
+ background: url("./ui/grid-toggle.png") no-repeat center center;
+ background-size: contain;
+ image-rendering: crisp-edges;
}
-#rainbow {
- background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet);
- border: 2px solid rgba(0,0,0,0.4);
+#canvas_opts {
+ display: grid;
+ grid-template-areas:
+ "lbl lbl"
+ "num slider"
+ ;
+ grid-template-columns: 2.5em auto;
+ gap: 0 4px;
}
-#darken {
- background: #0b3a1d;
+#canvas_opts > label {
+ grid-area: lbl;
}
-#lighten {
- background: #20964a;
+#canvas_opts input[type=range] {
+ grid-area: slider;
+ width: 100%;
}
-button.selected {
- border: 4px solid #fff !important;
+#canvas_opts input[type=text] {
+ grid-area: num;
+ text-align: center;
+ background: #fff8;
+ border: 0;
+ border-radius: 5px;
}
-#btn_overlay {
+#canvas_opts input[type=text]:focus {
+ border: 1px solid #fff;
}
diff --git a/pixeled.js b/pixeled.js
index fd6a890..8bf3a94 100644
--- a/pixeled.js
+++ b/pixeled.js
@@ -30,10 +30,39 @@
* [*] click-and-drag to draw, not hover
* [*] right click to copy color under cursor (and switch to it)
*
- * STRETCH GOAL
+ * STRETCH GOALS
*
- * [ ] better UI layout
- * [ ] possible export to PNG or GIF format (!!! :D)
+ * [*] better UI layout
+ * [ ] possible export to GIF format (!!! :D)
+ */
+
+/* POST-COMPLETION
+ *
+ * This project has helped me understand what goes into making modern Web apps,
+ * and the value that some frameworks can bring when you're building something
+ * with somewhat complex UI language, like an editing tool. There is a lot of
+ * event and state syncing, for lack of a better explanation, which increases
+ * the complexity of the task you're carrying out. Web apps can be convenient
+ * to visit and use, but building them is a challenge. I think I would be more
+ * at home in a persistent ticking environment like a video game, or somewhat
+ * predictable backend code. The moving target of HTML5 and CSS3 and JS, while
+ * fun in some respects, adds to the ever-increasing complexity that developers
+ * face when building for the Web.
+ *
+ * I would prefer to build on a platform that is less prone to change. There's
+ * bound to be a middle ground between low-level tedium and a gargantuan
+ * virtual machine.
+ */
+
+/* CREDITS
+ *
+ * Wikipedia for the GIF information
+ * MDN for endless Web and Javascript info
+ * The Command Line Fanatic, who wrote two great articles on GIF:
+ * https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art010
+ * https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art011
+ *
+ * Implementing GIF would have been more of a hassle without these resources.
*/
/* disable right click's default behavior */
@@ -56,6 +85,7 @@ current_color = "#000000";
custom_color = "#2564b8";
draw_state = dstates["BLACK"];
do_draw = false;
+do_move = false;
function clean_element(node) {
while (node.firstChild) {
@@ -79,7 +109,6 @@ function rebuildCanvas(size) {
}
});
box.addEventListener("mousedown", function(e) {
- // console.log(e.target.style.backgroundColor);
if (e.which == 3 || e.button == 2) {
if (e.target.style.backgroundColor == 'rgb(0, 0, 0)') {
setBlack();
@@ -92,21 +121,23 @@ function rebuildCanvas(size) {
setCustom();
}
} else {
- do_draw = true;
+ // do_draw = true;
drawColor(e.target, current_color);
}
}, { "passive": false, "capture": true } );
- box.addEventListener("mouseup", function(e) {
- do_draw = false;
- }, { "passive": false } );
+ // box.addEventListener("mouseup", function(e) {
+ // do_draw = false;
+ // }, { "passive": false } );
}
}
function clearCanvas() {
- pixels = document.querySelectorAll("#grid_container div");
- pixels.forEach(function(i) {
- i.removeAttribute("style");
- });
+ if (window.confirm("Clear the entire canvas?")) {
+ pixels = document.querySelectorAll("#grid_container div");
+ pixels.forEach(function(i) {
+ i.removeAttribute("style");
+ });
+ }
}
function handler() {
@@ -122,15 +153,11 @@ function initialSetup() {
grid.id = ["grid_container"];
grid.classList = ["show_grid"];
/* turn the pen off when you leave the canvas */
- grid.addEventListener("mouseleave",
- (e) => {
- do_draw = false;
- }, { "passive": false }
- );
-
- grid_caption = document.createElement("div");
- grid_caption.id = ["grid_caption"];
- grid_caption.textContent = 'an app by ZLG';
+ // grid.addEventListener("mouseleave",
+ // (e) => {
+ // do_draw = false;
+ // }, { "passive": false }
+ // );
resetCanvas(16);
/* we should have 256 divs appended to the grid. */
@@ -141,18 +168,30 @@ function initialSetup() {
* the same thing in pure JS. It's all scaffolding anyway. */
toolbox.innerHTML = `
<header>pixelED</header>
- <section id="color_picker">
+ <section id="tool_palette">
<div id="black_pick">
<span class="swatch"></span>
- <span class="sw_label">black</span>
</div>
<div id="white_pick">
<span class="swatch"></span>
- <span class="sw_label">white</span>
</div>
<div id="custom_pick">
<input class="swatch" type="color" value="" name="custom_color" id="custom_color"></input>
- <span class="sw_label">custom</span>
+ </div>
+ <div id="rainbow">
+ <span class="swatch"></span>
+ </div>
+ <div id="darken">
+ <span class="swatch"></span>
+ </div>
+ <div id="lighten">
+ <span class="swatch"></span>
+ </div>
+ <div id="clear_canvas">
+ <span class="swatch"></span>
+ </div>
+ <div id="grid_toggle" class="active">
+ <span class="swatch"></span>
</div>
</section>
<section id="canvas_opts">
@@ -160,35 +199,46 @@ function initialSetup() {
<input type="range" min="8" max="100" value="16" name="canvas_size" id="canvas_size" list="sizes"></input>
<input type="text" value="16"></input>
</section>
- <button id="rainbow">rainbow</button>
- <button id="darken">darken</button>
- <button id="lighten">lighten</button>
- <button id="erase">erase</button>
- <button id="grid_toggle">grid on</button>
- <datalist id="sizes">
- <option value="16"></option>
- <option value="32"></option>
- <option value="48"></option>
- <option value="64"></option>
- <option value="80"></option>
- </datalist>
`;
/* elements must be added to the page in order to be queried */
root.appendChild(toolbox);
root.appendChild(workspace);
workspace.appendChild(grid);
- workspace.appendChild(grid_caption);
+
+ /* the area behind the grid */
+ workspace.addEventListener("mousedown", (e) => {
+ do_draw = true;
+ });
+ /* the area behind the grid */
+ workspace.addEventListener("mouseup", (e) => {
+ do_draw = false;
+ });
/* initial settings */
setBlack();
document.querySelector("#custom_color").value = custom_color;
+ /* click-and-drag for the toolbox */
+ document.querySelector("#toolbox header").addEventListener("mousedown", (e) => {
+ e.preventDefault();
+ x1 = e.clientX;
+ y1 = e.clientY;
+ xoff = e.clientX - e.target.parentNode.offsetLeft;
+ yoff = e.clientY - e.target.parentNode.offsetTop;
+ do_move = true;
+ document.addEventListener("mousemove", moveToolbox);
+ document.addEventListener("mouseup", (e) => {
+ e.preventDefault();
+ do_move = false;
+ }, { once: true });
+ });
+
/* add events, AFTER added to DOM above */
document.getElementById("rainbow").addEventListener("click", setRainbow);
document.getElementById("darken").addEventListener("click", setDarken);
document.getElementById("lighten").addEventListener("click", setLighten);
- document.getElementById("erase").addEventListener("click", clearCanvas);
+ document.getElementById("clear_canvas").addEventListener("click", clearCanvas);
/* The canvas slider */
document.querySelector("#canvas_size").addEventListener(
@@ -253,7 +303,6 @@ function initialSetup() {
/* The Custom color option */
document.querySelector('#custom_color').addEventListener("input", setCustom);
- document.querySelector('#custom_pick > span').addEventListener("click", setCustom);
document.querySelector('#custom_pick').addEventListener("click", setCustom);
/* The Toggle Grid option */
@@ -304,11 +353,7 @@ function setLighten() {
function toggleGrid(e) {
document.querySelector("#grid_container").classList.toggle("show_grid");
- if (document.querySelector("#grid_container").classList.contains("show_grid")) {
- e.target.textContent = "grid on";
- } else {
- e.target.textContent = "grid off";
- }
+ document.querySelector("#grid_toggle").classList.toggle("active");
}
function lintCanvasSize(s) {
@@ -329,11 +374,22 @@ function clearSelected() {
);
}
-function getRGBTriplet(str) {
- if (str[0] == "#") {
- return hex_to_rgb(str);
+function getRGBTriplet(s) {
+ console.log(s);
+ if (s.length == 0) {
+ return [255, 255, 255];
+ }
+ if (s[0] == "#" || s.length == 6) {
+ return hex_to_rgb(s);
}
- return str.match(new RegExp("rgb\\((.*)\\)"))[1].split(", ");
+ triplet = s.match(new RegExp("rgb\\((.*)\\)"))[1].split(", ");
+ nums = new Array();
+ triplet.forEach(
+ (c) => {
+ nums.push(Number.parseInt(c));
+ }
+ );
+ return nums;
}
/* transform a hex color into rgb(x, y, z) format */
@@ -346,7 +402,7 @@ function hex_to_rgb(str) {
*/
if (str[0] == "#") {
str = str.substr(1);
- console.log(str);
+ // console.log(str);
}
switch (str.length) {
case 3:
@@ -447,3 +503,165 @@ function gen_lighten(c) {
let is = nc_array.join(", ");
return `rgb(${is})`;
}
+
+function moveToolbox(e) {
+ if (!do_move) {
+ return 0;
+ }
+ e = e || window.event;
+ e.preventDefault();
+ let tb = document.querySelector("#toolbox");
+ // console.log(`toolbar at ${tb.offsetLeft},${tb.offsetTop}`);
+ tb.style.left = `${e.clientX - xoff}px`;
+ tb.style.top = `${(e.clientY - yoff)}px`;
+ boundToolbox();
+}
+
+function myMid(lowb, num, highb) {
+ return Math.min(Math.max(lowb, num), highb);
+}
+
+function boundToolbox() {
+ let tb = document.querySelector("#toolbox");
+ let tbh = document.querySelector("#toolbox header");
+ let gap = tbh.style.gap;
+ tb.style.left = `${myMid(0,tb.offsetLeft,(window.innerWidth - tb.offsetWidth))}px`;
+ tb.style.top = `${myMid(0,tb.offsetTop,(window.innerHeight - (tbh.offsetHeight + 16)))}px`;
+}
+
+/* Implement a simple GIF class to facilitate easy downloading of user's work
+ *
+ * Limitations include:
+ * * no animation
+ * * no sub-images for complex palettes
+ * * no transparency
+ * * no palette correction should the colorspace run out
+ *
+ * In short, please only use this as a way to get pixelED art into another
+ * program.
+ */
+class GIFImage {
+ #header = "GIF89a";
+ #width;
+ #height;
+ #pixel_data;
+ #pixel_view;
+ raw_binary;
+ theGCT;
+ theIDS;
+
+ constructor(height, width, pdata) {
+ this.#width = width;
+ this.#height = height;
+ this.#pixel_data = new Uint8Array(3*(this.#width*this.#height));
+ this.#pixel_view = new DataView(this.#pixel_data.buffer, 0);
+ this.fill_buffer(pdata);
+ this.build_gct();
+ }
+
+ // fill the data buffer with background color information from this element's children.
+ fill_buffer(grid_area) {
+ if (grid_area.children.length != (this.#pixel_data.length / 3) ) {
+ console.log("pixelED Error: Size mismatch between canvas and GIF buffer size!");
+ return;
+ } else {
+ // every pixel has three values, thus we can know offsets
+ for (i=0; i<this.#pixel_data.length; i+=3) {
+ let grid_index = i / 3;
+ let pixel = getRGBTriplet(grid_area.children[grid_index].style.backgroundColor);
+ this.#pixel_view.setUint8(i,pixel[0]);
+ this.#pixel_view.setUint8(i+1,pixel[1]);
+ this.#pixel_view.setUint8(i+2,pixel[2]);
+ }
+ }
+ }
+
+ // make the global color table, and add color indices to the image data section
+ build_gct() {
+ // setup the gct naively
+ this.theGCT = new Array();
+ this.theIDS = new Array();
+ grid.childNodes.forEach(
+ (p) => {
+ let hex = rgb_to_hex(p.style.backgroundColor).substring(1);
+ if (! this.theGCT.includes(hex)) {
+ this.theGCT.push(hex);
+ }
+ let ci = this.theGCT.indexOf(hex);
+ this.theIDS.push(ci);
+ }
+ );
+ }
+
+ gct_to_binary() {
+ let buf = new Uint8Array(128*3);
+ for (i=0; (i/3) <= this.theGCT.length; i+=3) {
+ let rgb = getRGBTriplet(this.theGCT[i/3]);
+ buf[i] = rgb[0];
+ buf[i+1] = rgb[1];
+ buf[i+2] = rgb[2];
+ }
+ return new DataView(buf.buffer, 0);
+ }
+
+ ids_to_binary() {
+ let buf = new Uint8Array(this.theIDS.length);
+ for (i=0; i < buf.byteLength; i++) {
+ buf[i] = this.theIDS[i];
+ }
+ return new DataView(buf.buffer, 0);
+ }
+
+ // don't hurt me i'm still a babby
+ writeData() {
+ // we need to figure out the length of our information before we allocate a buffer.
+ let size = 0;
+ size += 6; // header: 'GIF89a'
+ size += 2; // logical screen width
+ size += 2; // logical screen height
+ size += 1; // GCT (Global Color Table) information
+ // -- u are here
+ size += 1; // background color index
+ size += 1; // pixel aspect ratio
+ size += this.gct_to_binary().byteLength;
+ // we'll try omitting the GCE
+ size += 1; // image descriptor ','
+ size += 4; // upper-left coord of image in logical screen
+ size += 4; // image width and height
+ size += 1; // local color table bit
+ size += 1; // minimum LZW code size - 1 (7 for 8-bit indices)
+ size += 1; // CLEAR code (80)
+ size += this.ids_to_binary().byteLength;
+ size += 1; // STOP code (81)
+ size += 1; // last ';' in the file
+ console.log("we need "+size+" bytes");
+
+ // do the business
+ this.raw_binary = new Uint8Array(size);
+ let offset = 0;
+ // write header
+ for (i=0; i<this.#header.length; i++) {
+ this.raw_binary[i] = this.#header[i].charCodeAt(0);
+ offset++;
+ }
+ // write LS width and height
+ this.raw_binary[offset] = (this.#width & 255);
+ offset++;
+ this.raw_binary[offset] = (this.#width) >> 8;
+ offset++;
+ this.raw_binary[offset] = (this.#height & 255);
+ offset++;
+ this.raw_binary[offset] = (this.#height) >> 8;
+ offset++;
+
+ // write BG index and aspect ratio
+ this.raw_binary[offset++] = 0;
+ this.raw_binary[offset++] = 0;
+
+ let packed_field = 0b10110110;
+
+ console.log(offset);
+ }
+}
+
+/* vim: set foldmethod=syntax: */
diff --git a/ui/black-pen.png b/ui/black-pen.png
new file mode 100644
index 0000000..3d6af14
--- /dev/null
+++ b/ui/black-pen.png
Binary files differ
diff --git a/ui/blank-canvas.png b/ui/blank-canvas.png
new file mode 100644
index 0000000..b086ca7
--- /dev/null
+++ b/ui/blank-canvas.png
Binary files differ
diff --git a/ui/clear-screen.png b/ui/clear-screen.png
new file mode 100644
index 0000000..b88c30b
--- /dev/null
+++ b/ui/clear-screen.png
Binary files differ
diff --git a/ui/custom-color-mask.png b/ui/custom-color-mask.png
new file mode 100644
index 0000000..7eebcb3
--- /dev/null
+++ b/ui/custom-color-mask.png
Binary files differ
diff --git a/ui/custom-color.png b/ui/custom-color.png
new file mode 100644
index 0000000..c35ee4c
--- /dev/null
+++ b/ui/custom-color.png
Binary files differ
diff --git a/ui/cyan-button-pressed.png b/ui/cyan-button-pressed.png
new file mode 100644
index 0000000..13af424
--- /dev/null
+++ b/ui/cyan-button-pressed.png
Binary files differ
diff --git a/ui/cyan-button.png b/ui/cyan-button.png
new file mode 100644
index 0000000..4fe6b62
--- /dev/null
+++ b/ui/cyan-button.png
Binary files differ
diff --git a/ui/darken-tool.png b/ui/darken-tool.png
new file mode 100644
index 0000000..2bc005c
--- /dev/null
+++ b/ui/darken-tool.png
Binary files differ
diff --git a/ui/eraser.png b/ui/eraser.png
new file mode 100644
index 0000000..d4d01b0
--- /dev/null
+++ b/ui/eraser.png
Binary files differ
diff --git a/ui/green-button-pressed.png b/ui/green-button-pressed.png
new file mode 100644
index 0000000..054ab8d
--- /dev/null
+++ b/ui/green-button-pressed.png
Binary files differ
diff --git a/ui/green-button.png b/ui/green-button.png
new file mode 100644
index 0000000..d37ce9a
--- /dev/null
+++ b/ui/green-button.png
Binary files differ
diff --git a/ui/grid-toggle.png b/ui/grid-toggle.png
new file mode 100644
index 0000000..1844aac
--- /dev/null
+++ b/ui/grid-toggle.png
Binary files differ
diff --git a/ui/lighten-tool.png b/ui/lighten-tool.png
new file mode 100644
index 0000000..4a65072
--- /dev/null
+++ b/ui/lighten-tool.png
Binary files differ
diff --git a/ui/rainbow-pen.png b/ui/rainbow-pen.png
new file mode 100644
index 0000000..26ce0b8
--- /dev/null
+++ b/ui/rainbow-pen.png
Binary files differ
diff --git a/ui/save-file.png b/ui/save-file.png
new file mode 100644
index 0000000..2df1a01
--- /dev/null
+++ b/ui/save-file.png
Binary files differ
diff --git a/ui/white-pen.png b/ui/white-pen.png
new file mode 100644
index 0000000..ef36c29
--- /dev/null
+++ b/ui/white-pen.png
Binary files differ
diff --git a/ui/yellow-button-pressed.png b/ui/yellow-button-pressed.png
new file mode 100644
index 0000000..fe637d2
--- /dev/null
+++ b/ui/yellow-button-pressed.png
Binary files differ
diff --git a/ui/yellow-button.png b/ui/yellow-button.png
new file mode 100644
index 0000000..bb34269
--- /dev/null
+++ b/ui/yellow-button.png
Binary files differ