A moderately unusual graphics module designed to compress images into blobs and store those blobs in a StringFilr. These tasks are performed and cached quickly enough for use in real-time environments, such as real-time video games.
At its core, PixelRendr is a library. It takes in sprites and string keys to store them under, and offers a fast lookup API. The internal folder structure storing images is at its core a tree, where strings are nodes similar to CSS classNames. See StringFilr for more information on storage, and ChangeLinr for the processing framework.
The most straightforward way is to use the encodeUri API. Rendering is done by loading an image onto an HTML element, so a browser is required; any image format supported by that browser may be provided.
MyPixelRender.encodeURI(
"http://my.image.url.gif",
function (results) {
console.log(results);
});
To start, each PixelRendr keeps a global "palette" as an Array[]:
[
[0, 0, 0, 0], // transparent
[255, 255, 255, 255], // white
[0, 0, 0, 255], // black
// ... and so on
]
Before compression is applied, sprites are stored as Strings containing series of numbers. For example:
"00000001112"
Using the above palette, this represents transparent pixels, three white pixels, and a black pixel. Most images are much larger and more complex than this, so a couple of compression techniques are applied:
-
Palette Mapping
It is necessary to have a consistent number of digits in images, as 010 could be [0, 1, 0], [0, 10], or etc. So, for palettes with more than ten colors, [1, 14, 1] would use ["01", "14", "01"]:
"011401011401011401011401011401011401011401"
We can avoid this wasted character space by instructing a sprite to only use a subset of the pre-defined palette:
"p[1,14]010010010010010010010"
The 'p[0,14]' tells the renderer that this sprite only uses colors 0 and 14, so the number 0 should refer to palette number 1, and the number 1 should refer to palette number 14.
-
Character Repetition
Take the following wasteful sprite:
"p[0]0000000000000000000000000000000000000000000000000"
We know the 0 should be printed 35 times, so the following notation is used to indicate "Print ('x') 0 35 times (','))":
"p[0]x035,"
-
Filters
Many sprites are different versions of other sprites, often simply identical or miscolored (the only two commands supported so far). So, a library may declare the following filter:
"Sample": [ "palette", { "00": "03" } ]
...along with a couple of sprites:
"foo": "p[0,7,14]000111222000111222000111222", "bar": [ "filter", ["foo"], "Sample"]
The "bar" sprite will be a filtered version of foo, using the Sample filter. The Sample filter instructs the sprite to replace all instances of "00" with "03", so "bar" will be equivalent to:
"bar": "p[3,7,14]000111222000111222000111222"
Another instruction you may use is "same", which is equivalent to directly copying a sprite with no changes:
"baz": [ "same", ["bar"] ]
-
"Multiple" sprites
Sprites are oftentimes of variable height. Pipes in Mario, for example, have a top opening and a shaft of potentially infinite height. Rather than use two objects to represent the two parts, sprites may be directed to have one sub-sprite for the top/bottom or left/right, with a single sub-sprite filling in the middle. Pipes, then, would use a top and middle.
[ "multiple", "vertical", { "top": "{upper image data}", "bottom": "{repeated image data}" } ]
-
BaseFiler
StringFilr
- A queryable interface on top of the base library. -
ProcessorBase
ChangeLinr
- Applies processing functions to turn raw strings into partial sprites. -
ProcessorDims
ChangeLinr
- Takes partial sprites and repeats rows, then checks for dimension flipping. -
ProcesserEncode
ChangeLinr
- Reverse of ProcessorBase: takes real images and compresses their data into sprites.
-
paletteDefault
Array[]
- The palette of colors to use for sprites. sprites. This should be an Array of Number[4]s representing rgba values. -
[library]
Object
- A library of sprites to process. -
[filters]
Object
- Filters that may be used by sprites in the library. -
[scale]
Number
- An amount to expand sprites by when processing (by default, 1 for not at all). -
[flipVert]
String
- What sub-class in decode keys should indicate a sprite is to be flipped ertically (by default, "flip-vert"). -
[flipHoriz]
String
- What sub-class in decode keys should indicate a sprite is to be flipped horizontally (by default, "flip-horiz"). -
[spriteWidth]
String
- What key in attributes should contain sprite widths (by default, "spriteWidth"). -
[spriteHeight]
String
- What key in attributes should contain sprite heights (by default, "spriteHeight"). -
[Uint8ClampedArray]
Function
- What internal storage container should be used for pixel data (by default, Uint8ClampedArray).
-
Creating and using a PixelRendr to create a simple black square.
var PixelRender = new PixelRendr({ "paletteDefault": [ [0, 0, 0, 255] // black ], "library": { "BlackSquare": "x064," } }), sizing = { "spriteWidth": 8, "spriteHeight": 8 }, sprite = PixelRender.decode("BlackSquare", sizing), canvas = document.createElement("canvas"), context = canvas.getContext("2d"), imageData; canvas.width = sizing.spriteWidth; canvas.height = sizing.spriteHeight; imageData = context.getImageData(0, 0, canvas.width, canvas.height); PixelRender.memcpyU8(sprite, imageData.data); context.putImageData(imageData, 0, 0);
-
Creating and using a PixelRendr to create a simple white square, using the simple black square as reference for a filter.
var PixelRender = new PixelRendr({ "paletteDefault": [ [0, 0, 0, 255], // black [255, 255, 255, 255] // white ], "library": { "BlackSquare": "x064,", "WhiteSquare": ["filter", ["BlackSquare"], "Invert"] }, "filters": { "Invert": ["palette", { "0": 1, "1": 0 }] } }), sizing = { "spriteWidth": 8, "spriteHeight": 8 }, sprite = PixelRender.decode("WhiteSquare", sizing), canvas = document.createElement("canvas"), context = canvas.getContext("2d"), imageData; canvas.width = sizing.spriteWidth; canvas.height = sizing.spriteHeight; imageData = context.getImageData(0, 0, canvas.width, canvas.height); PixelRender.memcpyU8(sprite, imageData.data); context.putImageData(imageData, 0, 0);