IMAGE! being moved to an extension, backed with BINARY!


#1

IMAGE! is not a Beta/One feature. More interesting would be a dialect for interacting with the HTML5 Canvas.

The WebAssembly Story hinges on a small download…to let JS programmers escape out into Rebol to run PARSE or whatever logic they want. More generally, Rebol itself hinges on being able to be small. We need to be able to build a core binary without the IMAGE! support code (or GOB! code, or VECTOR! code…). The important functions (e.g. PARSE) must be lightly embeddable on web pages and elsewhere.

The image code was pretty sloppy. But as tempting as it is to just delete IMAGE!, it has been kept around and working. Being able to read/write PNGs or JPGs or GIFs or BMPs is useful. And the PNG functionality has been updated and integrated/compacted in a fairly interesting way, which has exercised some advanced usages of the libRebol API. Complex examples like this and the FFI challenge the system’s ability to extend for practical needs.

So I’ve moved IMAGE! to an extension. It’s still built by default, so we can keep testing the encoders/decoders. But the JavaScript build does not include it.

One part of being in an extension means that the protocol for dealing with it needs to go through userspace. The LodePNG extension doesn’t get to fiddle the bits of an opaque image structure using system APIs, because IMAGE! has no system API. It’s a “user-defined-type” now…and has to be able to do what it needs by exchanging the currency of common Rebol values.

Being a user-defined type means IMAGE! becomes a victim of the weaknesses other user defined types have (e.g. no ability to define a meaning of FOR-EACH on them, because such an extension mechanism is not yet defined.) So IMAGE! will get such abilities if-and-when other user defined types do.

But you can still enumerate pixels in images, and what you can do for now is likely even better for most purposes, if anyone were using IMAGE! for anything real. The reason is that IMAGE! is now essentially an OBJECT! that aggregates a plain backing store BINARY! with size information.

MAKE IMAGE! […x… #{…}] now takes ownership of the BINARY!

This is to allow things like the PNG decoder to make a BINARY! and then not need to have that copied to MAKE IMAGE!.

The implication is that you now have to give it a binary of the exact number of bytes the RGBA data it wants is. So if your image is 10x10, the BINARY! you give it in MAKE IMAGE! must be 4 * 10 * 10 bytes long.

This isn’t actually taking away any intrinsic functionality. While it may have seemed you could make images without alpha channels before, they were always 4 RGBA bytes per pixel. It just gave the UI impression that it had only RGB bytes if all the alpha pixels were zero. (It should have been 255, because that’s opaque, but whatever.)

So if you create a helper like MAKE-RGB-IMAGE which produces a binary that adds the alpha channel, or fills in any missing data because your binary is too short…that helper won’t really be substantially less efficient than what the core could have done.

In return, you get a cool new feature: BYTES OF image

>> img: make image! 2x2

>> for-each [r g b a] bytes of img [print [r g b a]]
0 0 0 255
0 0 0 255
0 0 0 255
0 0 0 255

>> img/(2x1): 1.2.3.4
>> img/(2x2): 128.128.128

>> img
== make image! [2x2 #{
000000FF01020304000000FF808080FF
}]

>> for-each [r g b a] bytes of img [print [r g b a]]
0 0 0 255
1 2 3 4
0 0 0 255
128 128 128 255

For now, NEXT/SKIP/FOR-EACH won’t work on IMAGE! itself

Moving IMAGE! out of the core takes away its participation in the ANY-SERIES! category, and puts it on its own. And while extension types can add support for anything declared as a GENERIC (e.g. APPEND or COPY) as well as TO or MAKE, they can’t (easily) inject themselves as having behaviors for native functions (technically they could HIJACK and now RESKIN parameters if you were desperate, but we really need a better answer than that.)

Anyway, the priority was not to add features to images, but rather to make them optional in the build. But as it’s hard to know if things work or not unless you run tests, that means things are getting better in the process, even fixing bugs.