Search code examples
imagefactor-lang

How to render a bitmap image in Factor?


In a Factor program I want to:

  1. programmatically draw an image.
  2. display it.
  3. save it in some file.

Preferably:

  1. portable.
  2. with no external dependencies.

If not portable, should work on Windows.
If with external dependencies, something freely distributable and easy to bundle (like a dll/so file).

If the image is tuple class image, then (2) (displaying) is covered by ui and images.viewer` vocabs.

But I can't find a straightforward way to create and draw to an image, or output it in a raster format.

I don't care about:

  • having to draw pixel by pixel, as for the time being it would just be drawing simple straight lines.
  • bad performance. (Completely Ridiculously Awful Performance might be an issue, though ;)

What is the easiest way to do this in Factor?


Solution

  • In order:

    1. Programmatically draw an image.

    We're talking about bitmaps, which means unfortunately you'll need to work with each byte individually, unless you'd rather work with the even-less-documented svg vocabulary, which seems under-featured.

    The good news is that Factor has lots of higher-order functions for working with arrays. Additionally, the promising-sounding images.processing vocab doesn't have very many words for this sort of thing.

    What you'll actually want to do (as far as I can tell) is make a valid file header, then write the image data and header to a .bmp file.

    1. Display it.

    Less easy. It seems like ui.images will take an image-name (whatever that is -- something that implements path>>, so maybe it's expecting a stream or file object?) and apparently display it in the current UI world, which is likely simpler if you have a UI already.

    Just look at world's constructor:

    TUPLE: world < track
        active? focused? grab-input? fullscreen? saved-position
        layers title status status-owner text-handle handle images
        window-loc pixel-format-attributes background-color promise
        window-controls window-resources ;
    

    Damn, son.

    1. Save it in some file.

    This one I can actually give an implementation for since it's pretty simple once you get the hang of it.

    You've got an image in a folder, and we're going to load it into the listener. (Again, displaying it is a whole 'nother story).

    USING: images.bitmap io.encodings.binary io.files ;
    
    : bmp-open ( path -- stream ) binary <file-reader> load-bitmap ;
    

    <file-reader> needs a path and an encoding (from io.encodings), and leaves an io.stream on the stack, which is what load-png, load-bitmap etc are looking for.

    That will give us a loading-bmp, which is more useful than it sounds.

    T{ loading-bitmap
        { file-header
            T{ file-header
                { size 12726 }
                { offset 54 }
                { header-length 40 }
            }
        }
        { header
            T{ v3-header
                { width 64 }
                { height 66 }
                { planes 1 }
                { bit-count 24 }
                { image-size 12672 }
            }
        }
        { color-index
            B{
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 255 255 255 255 255
                255 255 255 255 255 255 255 255 ~12573 more~
            }
        }
    }
    

    The data you're looking for is obviously in color-index>>.

    dup color-index>>
    
    --- Data stack:
    T{ loading-bitmap f ~file-header~ ~v3-header~ f ~byte-array~ f }
    B{ 255 255 255 255 255 255 255 255 255 255 255 255 255 255...
    

    You can mess with that array however you like, and then just do >>color-index (or mess with loading-bitmap's constructor), and write those bytes to a file.

    TUPLE: loading-bitmap
        file-header header color-palette color-index bitfields ;
    

    Something like loading-bitmap>bytes or bitmap>bytes should help you there.


    When I was writing this answer last night, I totally didn't think to look on RosettaCode for bitmap-related things.

    enter image description here