Input needed: pixel value functions

Hello! On the topic of pixels, what would be the best way to access the current pixel state? We have super simple and direct rgb() and hsv() (and the niché hsv24()) APIs for setting the pixel, but none for getting it just yet.

All of these write to the current pixel that will head out to LEDs when the render function completes. If the pixel’s value were accessible, we could do more fun things and blend colors, convert hsv to rgb, etc.

If at some point a backing buffer was supported, this would let you access that pixel’s previous value as well. That plus some blending modes and an alpha setting would go far, but I’m getting ahead of myself!

What would be the best API for getting at the current pixel?

Pixel API
  • A new super-global array: pixel, an array(3) with RGB values. Could be read/write.
  • Getters: getRed(), getBlue(), getGreen()
  • Shorthand getters: red(), green(), blue()
  • Array copy/filler: getRgb(array) where you pass in an array to be set
  • Like above but renamed: getPixel(array) - still RGB

0 voters

What’s your long-term plan for evolving the API?

You could give FastLED-style low-level access to everything with a global pixel array, or you could keep the programming abstraction at a high level and add blending modes that are set in the preRender function and are applied to the framebuffer after the Render functions have all been called.

This topic is mainly about getting access to the current pixel’s RGB value after it has been set by hsv() or rgb().

There is not necessarily a backing buffer in Pixelblaze, which is a design goal from the start. I do intend to keep going in the direction of high level APIs.

Having an optional pixel buffer could be pretty handy though, and could make it easier to generate patterns that lend themselves to that approach. It’s not uncommon to make a buffer, draw into it, and fade them out a bit each frame. This would be a single pixel array, not a global array. Perhaps accessible via mapPixels() as well (see this post).

I like the idea of getRed(), getGreen()… If the compiler/VM works as I think it does, they should be close to as fast as array indexing, without requiring the user to allocate an array.
Using getters would allow you to have getHue(), getSaturation() and getBrightness() too. For almost all uses, I’ve come to greatly prefer working with HSV, and I also don’t want to give up the extra dynamic range on APA102 class LEDs.

I periodically think I might want direct access to buffered outgoing data, but thus far, not having it hasn’t stopped me from doing anything. (I also have occasional, “Wow, it would sure be easier if I could just write an OpenGL shader for this”, moments. At that point, I know it’s time to step away from the computer to clear my head…)

@zranger1 ,
I too love working in HSV, and I could add an RGB to HSV conversion tool, but otherwise wouldn’t keep the (h, s, v) values. Pretty much any blending modes would rely on working in RGB space. Plus I think getting the HSV of a post-blended pixel could lend itself to interesting filters, like hue shifts.

I intend to rework the LED driver stuff a bit so that there are fewer entanglements between the pattern and LED colorspace conversion, adding HDR support to rgb() or any supported pixel setting mechanisms. The backing pixel would have 16 bits per channel / 48-bit color which fits really well with the 0-1 range in the 16.16 fixed point number system. Also I have a bunch of really neat 48-bit LEDs sitting on my desk that will have range beyond APA102/SK9822 and can’t wait to unlock their potential!

Pixelblaze is definitely shader-like in ways. At some point I’d love to evolve the language to the point were you can use vectors, matrices, and the like with basic operator syntax. In my wildest dreams I imagine a GPU running parallel pixel renders, perhaps implemented on an FPGA.

1 Like

This is just my two cents, but the only reason I want to get the (previous) value of the current pixel is to do something meta to it like crossfading or anti-aliasing, in which case the preRender function does all the heavy lifting and the per-pixel Render function is just applying the values calculated in preRender (“hsv(pixelHues[index], pixelSats[index], pixelVals[index]);”).

If there was a backing framebuffer and I could set a blendMode in the preRender to determined how the pixels being rendered in the current loop were being applied, my code size would decrease by 60-80%.

Still, I’m curious to hear what uses other people would make of the current pixel value within the context of the Render function.

2 Likes

@zranger1,
I should also clarify, in the first option, the pixel super global array is managed by the engine. It’s pre-allocated, and the pixel global would likely get reset back to the proper array instance if it were accidentally changed (like pixelCount).

Each render call the array would get reset back to zeros (until some future date where a backing buffer is supported and enabled, in that case it gets updated with the last render’s data). Calls to rgb() or hsv() would write into that array. Code could also modify the array directly. At the end of render the contents of the pixel array would be sent to the LED driver.

There’s a small cost of array access, so e.g. getRed() might end up being slightly faster than pixel[0] - but my primary concern is which interface is nicer to have.

@pixie ,
Right, so you could implement cross-fading or color composition using this without pre-rendering by drawing the first color, getting the RGB, drawing the 2nd color, getting that RGB, blend both RGBs, and put back with rgb(). Once blending modes are implemented that would be even easier: draw the first color, set blend modes, draw the 2nd color. At that point getting the pixel value could still be interesting for creating additional effects or for storing the output pixel for later. Making patterns in HSV is easy, but blending 2 colors in HSV space is harder, see this post for some ideas there.

1 Like