- Common Lisp 100%
|
All checks were successful
CI / test (push) Successful in 1m5s
To the latest commits of cl-threads, the API review's fixes, which this library's own commits of the review follow. |
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| tests | ||
| .gitattributes | ||
| .gitignore | ||
| cl-atlas.asd | ||
| CLAUDE.md | ||
| LICENSE | ||
| README.org | ||
| stiftfile | ||
cl-atlas
Texture atlases for Common Lisp: many small images, such as glyphs or icons, packed into a few large pages, which a renderer uploads as textures and draws from. cl-atlas packs the images, caches them by any key, and tells which part of each page changed since it was last uploaded. It knows nothing of GPUs or text.
(asdf:load-system :cl-atlas)
(defvar *atlas* (cl-atlas:make-atlas :page-size 512 :channels 1))
;; An image is a 2D array of octets, rows by bytes.
(defun square (size)
(make-array (list size size) :element-type '(unsigned-byte 8) :initial-element 255))
;; Packed the first time a key is asked for, the same region after.
(let ((region (cl-atlas:atlas-find-or-add *atlas* '(:square 12) (lambda () (square 12)))))
(list (cl-atlas:page-index (cl-atlas:region-page region))
(cl-atlas:region-x region) (cl-atlas:region-y region)
(multiple-value-list (cl-atlas:region-uv region))))
;; => (0 0 0 (0.0 0.0 0.0234375 0.0234375))
;; Before drawing: upload what changed in each page.
(dolist (page (cl-atlas:atlas-pages *atlas*))
(multiple-value-bind (x y width height) (cl-atlas:take-page-changes page)
(when x
(list x y width height (cl-atlas:copy-page-region page x y width height)))))
Atlases
make-atlas takes :page-size, the width and height of every page in
texels (1024 by default); :channels, the bytes of a texel, 1 for coverage
such as glyphs' or 4 for colour; and :padding, the empty texels kept right
of and below each image (1 by default), so that filtering one image never
reads its neighbour. :data rides along on the atlas, as atlas-data,
such as the font whose glyphs it holds.
atlas-addpacks an image and returns its region. An image is a 2D array of octets indexed by row, then byte: each row holds its texels from left to right,:channelsbytes each.:datarides along on the region, such as a glyph's offsets from its origin.atlas-find-or-addpacks the image a function makes the first time a key is asked for, comparing keys withequal, and returns the same region every time after.atlas-findonly looks. It allocates nothing and keeps no reference to the key, so a key allocated on the stack (dynamic-extent) looks a region up without allocating.atlas-clearforgets every page and region.
Images are packed by a skyline packer into the first page with room. When every page is full, the atlas adds a page. Pages never grow and regions never move, so what a renderer recorded about a region, such as its texture coordinates, stays right.
Regions and pages
A region is an image's place: region-page, region-x, region-y,
region-width and region-height in texels from the page's top left, and
region-uv as texture coordinates from 0 to 1. An empty image gets a
region with no page.
A page is page-size texels square, page-channels bytes each, and
page-index is its place among atlas-pages. page-atlas is the atlas it
belongs to, which a page keeps alive, so a renderer that holds a page
reaches the atlas's data. Before drawing, a renderer
asks each page what changed: take-page-changes returns the rectangle
covering every image packed since it was last asked, or NIL, and
copy-page-region reads a rectangle's texels row by row without padding,
as a texture upload takes them. Give it a vector to reuse and it
allocates nothing.
Threads
An atlas may be used from any number of threads at once: one lock guards its
pages and cache. The function atlas-find-or-add calls to make a missing
image runs without the lock, so threads can make images at the same time;
if two make one for the same key, the first packed is kept.
Errors
Errors are atlas-error conditions, such as an image larger than a page or an
image whose rows are no whole number of texels.
Tests
stift test
Licence
MIT. See LICENSE.