A PICO-8 app for creating and sharing pixel paintings.
https://evs-dev.github.io/SharePaint
- Arrow Keys: Move selection
- X: Paint
- C: Change mode (painting, colour selection, tool selection)
- Enter: Open pause menu
- Paint Brush: Paints a single pixel
- Paint Bucket: Flood fills all the pixels of the same colour connected to the current pixel
- Undo: Reverts the most recent change
- Redo: Reverts the most recent reversion
To save a painting, open the pause menu (Enter or the small rectangular button on mobile), select "SAVE PIXELS
", and press enter/X. The page will reload and the URL will be updated with your painting. You can now copy the URL to share.
Here are the current URL parameters:
pixels
- the pixel data encoded with run-length encoding. Letters correspond to PICO-8 colours (A = 0 = black, H = 7 = white, etc.). Numbers are the run lengths. If there is no number following a letter, the number is presumed to be 1 unless the letter is the last character, in which case it stretches to the end of the canvas.outline
- whether or not the selection outline is visible by default (0 = invisible, >0 = visible). The outline hides itself automatically after 4 seconds of inactivity, and reappears as soon as a button is pressed. This parameter only affects the start state.
Here is an example of a URL:
which results in this:
SharePaint is a combination of JavaScript and PICO-8 Lua. pixelsLoader.js
uses the flexible and well-documented aspects of the save format of PICO-8 to modify the array _cartdat
in sharepaint.js
(the file generated by PICO-8 that contains all cartridge data) as the cartridge loads on the website using the URL parameters.
index.html
is generated by PICO-8 and is responsible for running sharepaint.js
, as well as site responsiveness, on-screen controls for mobile, etc. It is modified only to run the loadPixels()
function (from pixelsLoader.js
) as it loads the cartridge.
Images are stored in a run-length format, where c = 0 = black, h = 7 = white, and so on (corresponding with PICO-8 colours). The save_pixels()
function in sharepaint.p8
turns the user's drawing into a string and copies it to the clipboard. The clipboard is stored in a hidden element in index.html
, from which sharepaint_checkPixelsSaved()
in index.html
gets the string. This function appends the string to the URL and reloads the page, so SharePaint is reloaded with the same image. The URL can subsequently be copied and shared.
An example of a run-length encoded image is this:
c36o2c4o2c7o4c2o4c5o8h2o2c4o9ho2c5o10c6io9c6io9c7io7c9io4ic11i4c
which creates the heart in the SharePaint logo.
c = 2 = dark red, so the image begins as such. The following number, 36, dictates the number of pixels that are dark red. So if you count the number of dark red pixels (starting at the top left, going from left to right) until the first non-dark red one, you will get 36. This continues.
Sometimes, there are no numbers after a letter (e.g. i followed by o in the above string). This is assumed by the parser to mean 1 pixel of the colour; omitting the number simply saves space. The final letter in a string will have its colour continue until the end of the image.
The loadPixels()
function in pixelsLoader.js
turns the run-length string in the pixels
parameter of the URL into numbers that PICO-8 can understand. A certain part of the _cartdat
array in sharepaint.p8
is used to store the PICO-8 map (which is actually what is rendered in SharePaint). Setting the correct indices of this array corresponding to the values PICO-8 uses for colours is how PICO-8 can display the URL data.
SharePaint implements a recursive flood fill algorithm for the Paint Bucket tool in flood_fill()
in sharepaint.p8
. It tests neighbouring pixels in the cardinal directions for a match to the selected pixel's colour, and sets those matching pixels to be the end colour. Each set pixel then does the same thing, and so on. This of course finishes when there are no more connected pixels of the starting colour.
It's always useful to be able to reverse a Paint Bucket spillage or reverse that reversal. The Undo/Redo tools allow this. Before a change is made to the painting (e.g. the Paint Brush or Paint Bucket is used), all the pixels are temporarily saved. Then after the change, the new pixels are compared to those old saved ones, and all pixels that changed, along with their old and new colours, are added to a new table in the history
list. Using the Undo tool cycles back in history, applying the old colour (old_c
in the code). Using the Redo tool cycles forward in history, applying the new colour of the change (new_c
). When back in history, i.e. there are 'future' that could be redone into, and a change is made, all of those future changes are discarded and the most recent change becomes the latest one. There is a limit of 256 changes in history
to prevent running out of memory.