noopkat / png-to-lcd

convert png image data into an lcd convertible framebuffer
22 stars 9 forks source link

directly support node-canvas? #9

Closed fredoche closed 9 years ago

fredoche commented 9 years ago

Hi, Thanks for building that project. I was wondering it, in you opinion, there would be a way to use your software as an alternate output for node-canvas. Currently node-canvas outputs directly to jpg or png, thanks to , I think, its cairo backend. But wouldn't it be cool if it could write directly a dithered image suitable for a tiny lcd screen? Let me know if you have any opinion about it, I'm trying to put together a hack but it doesnt come out easy since I dont know much about node-canvas and your codebase yet!

noopkat commented 9 years ago

Hi @fredoche !

Yeah this would be cool! So I think you might want to check out another one of my node packages, floyd-steinberg. png-to-lcd actually uses it to do the dithering thing.

I'll explain the difference between the two: png-to-lcd converts your png to monochrome. It then takes the pixel data and returns a buffer array of bytes designed to be sent straight to a physical LCD/OLED screen/chip driver that uses a bit for each pixel value.

floyd-steinberg converts your image to monochrome with dithering, and then returns the updated pixel data back to you. This data can be used with node-canvas or even piped back into a new png file. You could also convert the whole png object somehow to base64 encoding and plug it into the src attribute of an image tag.

Does this help? If not, I can provide an example to get you started :)

fredoche commented 9 years ago

It does help, thanks. I'm trying to write directly to a framebuffer device /dev/fb1, and, despite a monochrome screen, the pixel format is rgb565le , so 2 bytes per pixel. What is the pixel size in the dithered output buffer? Do you think I'll need to convert it so it matches the framebuffer's pixel size? Then again, so sorry if I ask the wrong questions :s it is all a bit new to me.

noopkat commented 9 years ago

Ah thanks for the extra details, this makes more sense now. As you mentioned, rgb565le is effectively 16 bits or 2 bytes - 5 bits for red, 6 for green and 5 for blue, right? Given that you're after monochrome still, it should be not too bad to convert it to match the framebuffer size.

I fell down a research rabbit hole, and this format is blowing my mind. I thought it was completely ridiculous at first and I'm not sure if we're being trolled, but I found this interesting explanation as to why 6 bits for green: http://stackoverflow.com/questions/25467682/rgb-565-why-6-bits-for-green-color?rq=1

And although unanswered, this stackoverflow might get us onto the right track with converting to this weirdo pixel format: http://stackoverflow.com/questions/27194568/rgb-color-converting-into-565-format?rq=1

I'm curious - what kind of framebuffer device or screen are you working with? I'm wondering if it's common enough, that it's a good idea to write support in png-to-lcd for rgb565le that's all. That, and blatant nosiness 😛

fredoche commented 9 years ago

I'm using the http://www.adafruit.com/products/938 , and access it with this driver https://github.com/notro/fbtft/wiki/LCD-Modules#adafruit-13-monochrome so that I end up with a /dev/fb1 device that I can read and write using standard shell (cat) and file API.That said, the screen is monochrome, so any value different than 0 will be white. Hence, an actual /color/ conversion may not be useful anyway. Having a compatible output though (2 bit per pixel ) is imho a nice option to have indeed. I'm trying to get to it, but my pixel math is reaaaally rusty, and I'm struggling to understand the

    // time to pack the buffer

part of the code, and may have gotten the previous parts wrong as well. At least I'm trying :)

noopkat commented 9 years ago

Fun fact - I originally wrote png-to-lcd for OLED devices exactly like the one you have (with SD1306 chip). So png-to-lcd technically outputs the correct sized framebuffer for your device, unless I'm missing something.

As for the // time to pack the buffer bit, what it's doing is taking straight up RGBA PNG pixel data, and compresses it into the 8 bit format for the screen. So if it's monochrome, it'll put a 0 for black and 1 for white etc etc as you said earlier. It reduces it from 1 byte per pixel to 1 bit per pixel. It also puts this into a node Buffer object, which is a correct binary format ideal for sending across a usb cable. I can explain this further if you like.

Was there a reason why you wanted to keep it as 2 byte rather than 1 byte? The link you provided says 565 / 16 bit, but if you scroll up to the table at the top of the page it says 8 bit, which is consistent with when I've interfaced with the same OLED that you have. Does it differ when you're using this library as a console for the device?

Are you using an Arduino to connect the screen to the computer? An alternative to the library you're using would be this one here (which can use png-to-lcd for bitmap displaying): https://github.com/noopkat/oled-js

fredoche commented 9 years ago

I came to that conclusion after this

[root@localhost frame-buf]# cat /dev/fb1 > anyframe
[root@localhost frame-buf]# ls -la anyframe 
-rw-r--r-- 1 root root 16384 Mar 24 16:21 anyframe

So yes I believe it is the pixel format of the framebuffer. Maybe, if the ssd1306 is used trough userland api, it may accept an API which works with 1byte per pixel, but it doesnt seem to be the case when directly using the linux framebuffer fbdev driver. And no, no arduino, direct wiring from a raspberry pi over spi bus. I'll check your links, thanks for your help.