Sprites.Media.Image

A full-colour bitmap editor and its model. Image is a full-colour bitmap over a high resolution pixel grid, drawn on a canvas so it stays fast at large sizes. It works like the sprite editor, the same brushes, tools, palette and windows, but has layers and no frames, and holds its pixels as a Sprites.Grid.Tiled block, a grid of small tiles, so a paint copies one tile, not the whole image. A layer is { tiles, visible }; a pixel is a packed RGBA colour, transparent where alpha is zero. Every function composes reactive values; the raw pixel work lives in Sprites.Grid.Tiled and the raw drawing in Sprites.Ui.Image.Pixels, so this module holds no Calculate.

Sprites.Media.Image

Sprites.Media.Image.MinSide Number

The smallest a side may be, in pixels.

Value 1.

Sprites.Ui.Dom.Text('MinSide = ', Sprites.Media.Image.MinSide);
The lower bound on a side.

Sprites.Media.Image.MaxSide Number

The largest a side may be, in pixels. An image runs far larger than a sprite, so the maximum is high; the canvas draws it all the same, since it never makes one element per pixel.

Value 4096.

Sprites.Ui.Dom.Text('MaxSide = ', Sprites.Media.Image.MaxSide);
The upper bound on a side.

Sprites.Media.Image.Tile Number

The tile side the pixels are cut into, in pixels. A power of two, small enough that one tile copy on a paint is cheap and large enough that the tile count stays low.

Value 16.

Sprites.Ui.Dom.Text('Tile = ', Sprites.Media.Image.Tile);
The square block size the pixels are cut into.

Sprites.Media.Image.DefaultWidth Number

The default new image width, in pixels.

Value 256.

Sprites.Ui.Dom.Text('DefaultWidth = ', Sprites.Media.Image.DefaultWidth);
The width a new image takes.

Sprites.Media.Image.DefaultHeight Number

The default new image height, in pixels.

Value 256.

Sprites.Ui.Dom.Text('DefaultHeight = ', Sprites.Media.Image.DefaultHeight);
The height a new image takes.

Sprites.Media.Image.White Number

Opaque white as a packed RGBA integer, each byte a channel, the freeform brush default. A namespace constant holds a value, never a call, so the literal stands in place of a Pack.

Value 0xFFFFFFFF.

const css = Sprites.Colour.Css(Sprites.Media.Image.White);
Sprites.Ui.Dom.Text('White = ', css);
The packed white brush colour, shown as CSS.

Sprites.Media.Image.Transparent Number

The transparent pixel: a packed RGBA integer with every channel zero, so its alpha is zero. A tile holds this where nothing is painted, and an erase writes it. A typed pixel buffer cannot hold undefined, so zero alpha is the absent pixel here.

Value 0.

Sprites.Ui.Dom.Text('Transparent = ', Sprites.Media.Image.Transparent);
The absent pixel, packed zero.

Sprites.Media.Image.ClampSide Sprites.Reactive.Value

Sprites.Media.Image.ClampSide(n)

Clamp a side to the allowed range, as a whole number. A composite: round the value, then hold it between the smallest and largest a side may be.

InputTypeDescription
nNumber or Sprites.Reactive.ValueThe requested side, rounded and clamped.

Returns a Sprites.Reactive.Value holding the clamped whole side.

const asked = Sprites.Reactive.Value(8000);
const side = Sprites.Media.Image.ClampSide(asked);
Sprites.Ui.Input.Slider(asked, -100, 8000);
Sprites.Ui.Dom.Text(' clamped = ', side);
Slide past the bounds and the side settles at one or 4096.

Sprites.Media.Image.New Sprites.Reactive.Value

Sprites.Media.Image.New(width, height)

A new tiled pixel block at the given size, or the default size, all transparent. A composite: it clamps the sides, then builds the block with the tiled grid primitive.

InputTypeDescription
widthNumber or Sprites.Reactive.ValueThe pixel width, or absent for the default.
heightNumber or Sprites.Reactive.ValueThe pixel height, or absent for the default.

Returns a Sprites.Reactive.Value holding the blank tiled grid.

const width = Sprites.Reactive.Value(64);
const block = Sprites.Media.Image.New(width, 48);
const w = Sprites.Grid.Tiled.Width(block);
Sprites.Ui.Input.Slider(width, 16, 128);
Sprites.Ui.Dom.Text(' block width = ', w);
A blank transparent block, its width following the slider.

Sprites.Media.Image.NewDocument Object

Sprites.Media.Image.NewDocument(width, height, optionalMode)

A new image document at the given size in the given mode: the { media, w, h, tile, mode, layers } shape the store keeps, its one visible layer holding the blank tiles. A boot factory a page hands to the store, so it snapshots the blank block plain here rather than staying a live reactive.

InputTypeDescription
widthNumberThe pixel width.
heightNumberThe pixel height.
optionalModeStringThe mode name stored on the document, or absent.

Returns the plain { media, w, h, tile, mode, layers } document object.

const doc = Sprites.Media.Image.NewDocument(32, 24, 'Freeform');
const mode = Sprites.Object.Field(doc, 'mode');
const w = Sprites.Object.Field(doc, 'w');
const h = Sprites.Object.Field(doc, 'h');
const layers = Sprites.Object.Field(doc, 'layers');
const count = Sprites.Array.Length(layers);
Sprites.Ui.Dom.Text('mode ', mode, ', ', w, '×', h, ', layers ', count);
A fresh document, one visible layer of blank tiles.

Sprites.Media.Image.NewLayer Sprites.Reactive.Value

Sprites.Media.Image.NewLayer(width, height, tile)

A new blank layer at the given size: one visible layer of transparent tiles, its block built with the tiled grid primitive. A composite, so a click that adds a layer passes it as a fresh reactive value the array insert commits.

InputTypeDescription
widthNumber or Sprites.Reactive.ValueThe pixel width.
heightNumber or Sprites.Reactive.ValueThe pixel height.
tileNumber or Sprites.Reactive.ValueThe tile side.

Returns a Sprites.Reactive.Value holding the { tiles, visible } layer.

const layer = Sprites.Media.Image.NewLayer(32, 32, 16);
const visible = Sprites.Object.Field(layer, 'visible');
Sprites.Ui.Dom.Text('new layer visible = ', visible);
A fresh visible layer of transparent tiles.

Sprites.Media.Image.CloneLayer Sprites.Reactive.Value

Sprites.Media.Image.CloneLayer(layer)

A copy of a layer: a fresh layer object holding the same tiles and visible flag. Rebuilding the object keeps the copy independent, since a paint replaces the tiles rather than changing them in place, so editing one leaves the other be.

InputTypeDescription
layerObject or Sprites.Reactive.ValueA { tiles, visible } layer.

Returns a Sprites.Reactive.Value holding the copied layer.

const source = Sprites.Media.Image.NewLayer(16, 16, 16);
const copy = Sprites.Media.Image.CloneLayer(source);
const visible = Sprites.Object.Field(copy, 'visible');
Sprites.Ui.Dom.Text('copy visible = ', visible);
An independent copy carrying the same tiles and flag.

Sprites.Media.Image.LayerGrid Sprites.Reactive.Value

Sprites.Media.Image.LayerGrid(layer, width, height, tile)

The tiled grid of one layer at the document size: a { w, h, tile, tiles } block, for a thumbnail or a stamp. A composite over ReadOnly.

InputTypeDescription
layerObject or Sprites.Reactive.ValueA { tiles, visible } layer.
widthNumber or Sprites.Reactive.ValueThe document pixel width.
heightNumber or Sprites.Reactive.ValueThe document pixel height.
tileNumber or Sprites.Reactive.ValueThe tile side.

Returns a Sprites.Reactive.Value holding the { w, h, tile, tiles } grid.

const layer = Sprites.Media.Image.NewLayer(32, 32, 16);
const grid = Sprites.Media.Image.LayerGrid(layer, 32, 32, 16);
const pixel = Sprites.Grid.Tiled.At(grid, 0, 0);
Sprites.Ui.Dom.Text('top left pixel = ', pixel);
The layer as a tiled grid; a blank layer reads transparent zero.

Sprites.Media.Image.SelectedGrid Sprites.Reactive.Value

Sprites.Media.Image.SelectedGrid(data, layerIndex)

A two way { w, h, tile, tiles } grid over the layer at layerIndex. Reading composes the grid from the size, the tile and that layer's tiles, so the editor edits one flat layer. Writing lays the edited tiles back into that one layer through the layers list, keeping the layer's visible flag and every other layer, so a paint touches only the selected layer.

InputTypeDescription
dataSprites.Reactive.ValueThe two way stored document.
layerIndexNumber or Sprites.Reactive.ValueThe selected layer.

Returns a two-way Sprites.Reactive.Value over the selected layer's grid.

const document = Sprites.Media.Image.NewDocument(32, 32, 'Freeform');
const data = Sprites.Reactive.Binding(document);
const state = Sprites.Media.Image.SelectedGrid(data, 0);
const paint = Sprites.Reactive.Bindingundefined, () => {
  const next = Sprites.Grid.Tiled.FillRect(state, 0, 0, 4, 4, 0xff4444ff);
  Sprites.Reactive.Set(state, next);
});
const pixel = Sprites.Grid.Tiled.At(state, 0, 0);
Sprites.Ui.Button.Act(paint, 'Paint the layer');
Sprites.Ui.Dom.Text(' top left pixel = ', pixel);
A paint writes back into the selected layer, and the read follows.

Sprites.Media.Image.Display Sprites.Reactive.Value

Sprites.Media.Image.Display(data)

The image drawn as one tiled grid: the visible layers flattened bottom to top, a layer higher up over the ones below and a transparent pixel letting them show through. The canvas draws this, and a change repaints only the tiles it touched.

InputTypeDescription
dataObject or Sprites.Reactive.ValueThe document.

Returns a Sprites.Reactive.Value holding the flattened tiled grid.

const document = Sprites.Media.Image.NewDocument(32, 32, 'Freeform');
const data = Sprites.Reactive.Binding(document);
const state = Sprites.Media.Image.SelectedGrid(data, 0);
const paint = Sprites.Reactive.Bindingundefined, () => {
  const next = Sprites.Grid.Tiled.FillRect(state, 0, 0, 6, 6, 0x44ff44ff);
  Sprites.Reactive.Set(state, next);
});
const display = Sprites.Media.Image.Display(data);
const pixel = Sprites.Grid.Tiled.At(display, 2, 2);
Sprites.Ui.Button.Act(paint, 'Paint the layer');
Sprites.Ui.Dom.Text(' flattened pixel = ', pixel);
The flattened view follows the layer as it paints.

Sprites.Media.Image.ResizeAll Sprites.Reactive.Value

Sprites.Media.Image.ResizeAll(data, width, height, pad)

The same document resized so every layer is the new width and height, cropping or padding each layer's tiles with the padding colour. Width and height live on the document, shared by every layer. A composite keeping the tile, the mode and each layer's visible flag.

InputTypeDescription
dataObject or Sprites.Reactive.ValueThe document.
widthNumber or Sprites.Reactive.ValueThe new pixel width.
heightNumber or Sprites.Reactive.ValueThe new pixel height.
padNumber or Sprites.Reactive.ValueThe packed RGBA padding colour for new area.

Returns a Sprites.Reactive.Value holding the resized document.

const document = Sprites.Media.Image.NewDocument(32, 32, 'Freeform');
const data = Sprites.Reactive.Binding(document);
const width = Sprites.Reactive.Value(48);
const resized = Sprites.Media.Image.ResizeAll(data, width, 32, Sprites.Media.Image.Transparent);
const w = Sprites.Object.Field(resized, 'w');
Sprites.Ui.Input.Slider(width, 16, 64);
Sprites.Ui.Dom.Text(' document width = ', w);
Every layer resizes together to the new document width.

Sprites.Media.Image.Summary Sprites.Reactive.Value

Sprites.Media.Image.Summary(optionalModeName, width, height)

The meta info summary for an image: its mode and size as one short string, for the meta record a listing reads. The dimensions join, and the mode joins in front when there is one.

InputTypeDescription
optionalModeNameString or Sprites.Reactive.ValueThe mode name, or undefined for none.
widthNumber or Sprites.Reactive.ValueThe pixel width.
heightNumber or Sprites.Reactive.ValueThe pixel height.

Returns a Sprites.Reactive.Value holding the summary string.

const width = Sprites.Reactive.Value(128);
const summary = Sprites.Media.Image.Summary('Freeform', width, 64);
Sprites.Ui.Input.Slider(width, 16, 256);
Sprites.Ui.Dom.Text(' summary = ', summary);
The mode and size as one short string, following the width.

Sprites.Media.Image.Thumbnail Sprites.Reactive.Value

Sprites.Media.Image.Thumbnail(grid)

A PNG data URL of a tiled grid, transparent where a pixel's alpha is zero, as a reactive value that follows the pixels, for a saved preview or a layer card. It reads every pixel, so a page runs it for a preview, not on a paint.

InputTypeDescription
gridObject or Sprites.Reactive.ValueA { w, h, tile, tiles } tiled grid.

Returns a Sprites.Reactive.Value holding the PNG data URL string.

const grid = Sprites.Grid.Tiled.Blank(16, 16, 16, 0xff4444ff);
const url = Sprites.Media.Image.Thumbnail(grid);
Sprites.Ui.Dom.Tag('img', () => {
  Sprites.Ui.Dom.Attribute('src', url);
  Sprites.Ui.Dom.Style('width', '64px');
  Sprites.Ui.Dom.Style('image-rendering', 'pixelated');
});
The tiled grid encoded to a PNG data URL, shown as an image.

Sprites.Media.Image.SpriteToGrid Sprites.Reactive.Value

Sprites.Media.Image.SpriteToGrid(spriteData)

A sprite's pixels as a { w, h, cells } grid of packed RGBA colours, its first frame's visible layers flattened, so a picked sprite can stamp into an image. It reads the freeform sprite's packed cells straight, so a freeform sprite stamps true to colour.

InputTypeDescription
spriteDataObject or Sprites.Reactive.ValueA sprite document, its frames each with layers.

Returns a Sprites.Reactive.Value holding the { w, h, cells } grid.

const layer = { pixels: [0xff4444ff, undefined, undefined, 0x4444ffff], visible: true };
const frame = { layers: [layer] };
const sprite = { w: 2, h: 2, frames: [frame] };
const grid = Sprites.Media.Image.SpriteToGrid(sprite);
const cell = Sprites.Grid.Two.At(grid, 0, 0);
Sprites.Ui.Dom.Text('top left cell = ', cell);
The sprite's first frame flattened to a stampable grid.

Sprites.Media.Image.Palette Void

Sprites.Media.Image.Palette(hasSlots, slots, brush, visible)

The Palette window: a floating panel docked to the stage's bottom left. With platform slots it holds a swatch grid, a click setting the brush to that colour; freeform it holds an inline colour picker, so a paint colour is chosen in real time. hasSlots is a build time flag; slots is the packed colours; brush is the two way packed brush colour; visible is the show flag.

InputTypeDescription
hasSlotsBooleanTrue for a swatch grid, false for the freeform picker.
slotsArray or Sprites.Reactive.ValueThe packed palette colours.
brushSprites.Reactive.ValueThe two way packed brush colour.
visibleSprites.Reactive.ValueThe show flag the window follows.

Returns nothing; it builds a floating window.

const brush = Sprites.Reactive.Value(Sprites.Media.Image.White);
const visible = Sprites.Reactive.Value(false);
Sprites.Ui.Button.SetValue(visible, true, 'Open the palette');
Sprites.Media.Image.Palette(false, [], brush, visible);
const css = Sprites.Colour.Css(brush);
Sprites.Ui.Dom.Text(' brush = ', css);
Open the window and pick a colour with the freeform picker; the brush follows.

Sprites.Media.Image.Editor Void

Sprites.Media.Image.Editor(data, thumb, info, optionalModes, optionalModeName, optionalSlots)

The image editor. Builds the canvas view on the host, the pan and zoom overlay, the Tools window, each canvas tool's own window, the panel windows, and the Palette and Info windows. It builds interface and returns nothing; the doc fields it is given are where the edits land.

InputTypeDescription
dataSprites.Reactive.ValueThe document, a two way value of { media, w, h, tile, mode, layers }.
thumbSprites.Reactive.ValueA value the editor Sets to a fresh PNG data URL of the pixels at a stroke's end.
infoSprites.Reactive.ValueA value the editor Sets to a short mode and size summary.
optionalModesArrayThe platform's image modes, or absent for one freeform mode.
optionalModeNameSprites.Reactive.ValueA two way value of the current mode's name, or absent.
optionalSlotsArrayThe platform's palette as packed integers, or absent for the freeform picker.

Returns nothing; it builds the full editor interface. The example gates it behind a button so the windows do not cover the page on load.

const open = Sprites.Reactive.Value(false);
Sprites.Ui.Button.SetValue(open, true, 'Open the editor');
Sprites.Reactive.If(open, () => {
  const document = Sprites.Media.Image.NewDocument(64, 64, 'Freeform');
  const data = Sprites.Reactive.Binding(document);
  const thumb = Sprites.Reactive.Value(undefined);
  const info = Sprites.Reactive.Value(undefined);
  const modeName = Sprites.Object.Field(data, 'mode');
  Sprites.Ui.Dom.Tag('div', () => {
    Sprites.Ui.Dom.Class('editor-stage');
    Sprites.Ui.Dom.Style('height', '420px');
    Sprites.Media.Image.Editor(data, thumb, info, undefined, modeName);
  });
});
Open the editor: the canvas, tools, palette and info windows mount inside the stage.