|
Update: WHO'S A SUPERSTAR? I FIXED IT. I needed a pair of parentheses.
I spent weeks - weeks! working on a device independent pixel in C++
I finally built it, and it seems to work, but they're tricky because they store the pixel in the form of its final memory representation in a frame buffer, so if you defined a pixel as 24-bit color, there's a new pixel every 3 bytes, and if you defined a pixel as 18-bit color (6 bits each for R, G, and B - shockingly common) then there's a new pixel every 2.25 bytes!
So it gets ... tricky. There's bit shifting, masking and whatnot.
I'm not testing on an actual display device yet, so i have code that converts small bitmaps to monochrome and then renders them as "acii art"
My blting works, my filling works, and my clearing works, when the pixels are either byte-aligned (like 24 bit pixels) or 1 bit (monochrome), but the 18-bit one is weird, and clearing isn't working.
I think it's with my bit setting code, i thought, but also no, because it's clearing in the wrong place?
Each pixel is #, or black is just empty space. I have a hatch pattern i bltted and them i'm clearing a box inside of it. The clearing is what is failing.
Basically, this is what i get when it works:
# # # # # # # #
# # # # # # #
# # # # # # # #
# #
# # # #
# #
# # # #
# #
# # # #
# #
# # # #
# #
# # # # # # # #
# # # # # # #
# # # # # # # #
This works for rgb888 (24-bit), mono1 (1-bit) and rgb565 (16-bit). rgb666 (18-bit) however, is being truly evil:
# # #
# # # #
# # # # # # # #
# # # # # # #
# # # # # # # #
# # # # # # #
# # # # # # # #
# # # # # # #
# # # # # # # #
# # # # # # #
# # # # # # # #
# # # # # # #
# # # # # # # #
# # # # # # #
# # # # # # # #
What gets me is the bits that it *is* clearing. Look at the empty run above. How the elephant did it get that? It's not even starting in the right place.
So now I know where a spare day of my life is going.
Never make rgb666 pixels. They are truly satan's children.
Real programmers use butterflies
modified 12-Apr-21 10:09am.
|
|
|
|
|
The PDP-10 had 36-bit words.
|
|
|
|
|
That would actually make things easier in this one case. But because pixel definitions are completely arbitrary in terms of the channels they have and the bit depth of each, the total bit depth of the pixel can be anything more than 0 or less than 65, so it would only help in that one case.
It's just weird that it works with everything else but catastrophically fails on "satan's pixel"
Real programmers use butterflies
|
|
|
|
|
Example code? Does there really exist graphics hardware that has a pixel every 2,25 bytes in its framebuffer?
|
|
|
|
|
Wikipedia seem to think so. Mostly used in less expensive LCDs, which seems likely to be the displays attached to the types of devices the OP seems to have been targeting recently.
Keep Calm and Carry On
|
|
|
|
|
Yes indeed. In fact, as I recall the ILI9341 display adapter supports 18-bit color.
The place I had seen it before on the PC was the old VGA 320x200x8bpp mode where the 8 bit pixel was an indexed color into a palette. The palette colors were 18 bit.
Real programmers use butterflies
modified 12-Apr-21 11:01am.
|
|
|
|
|
Hi,I know 18bpp is used, I use it myself. The question was: Is there any hardware around that stores this information in a framebuffer every 2,25 bytes? I do not think so.
So I am not quite sure, what the use of this higly flexible pixel is. I can see a use for transforming of images. But the framebuffers I know are byte based and for performace reasons, aligned to byte, word or dword boundaries.
So when you do drawing, you can use an array (virtual area) of your pixels but I fear the performace would lack.
In a framebuffer, you normally have lots of pixels and it comes to processor cycles when you do drawing, copying, moving etc.. and want to get a useful frame rate So it is always nice if you can work with the register size of the CPU.
It sounds for me, that you plan to draw on a virtual area of pixels which are finally converted to the hardware equivalence in the framebuffer.
But maybe I did understand your problem wrong, that's why I asked for a code example.
|
|
|
|
|
I don't have the driver code built yet. I'm building the graphics end of it right now.
At any rate, supporting 18-bpp wasn't even my initial goal.
It's just that you can do this:
using bgr888 = pixel<
channel_traits<channel_name::B,8>,
channel_traits<channel_name::G,8>,
channel_traits<channel_name::R,8>
>;
using rgb565 = pixel<
channel_traits< channel_name::R,5>,
channel_traits<channel_name::G,6>,
channel_traits<channel_name::B,5>
>;
using gsc8 = pixel<
channel_traits<channel_name::L,8>
>;
using mono1 = pixel<
channel_traits<channel_name::L,1>
>;
using rgb888 = pixel<
channel_traits<channel_name::R,8>,
channel_traits<channel_name::G,8>,
channel_traits<channel_name::B,8>
>;
using rgb666 = pixel<
channel_traits<channel_name::R,6>,
channel_traits<channel_name::G,6>,
channel_traits<channel_name::B,6>
>;
using pixel_type = rgb666;
using bmp_type = bitmap<pixel_type>;
pixel_type p = color<pixel_type>::light_green;
uint8_t buf[bmp_type::sizeof_buffer(16,16)];
bmp_type bmp(16,16,buf);
bmp[point16(7,7)]=p;
p = bmp[point16(7,7)];
No matter how I declare a pixel, it has to work. The channel bit depths and even the number of channels and their types are arbitrary. The only thing above that even requires an RGB color model is the source colors like light_green above and they are convertible to the destination format in cases of black&white vs RGB
I think the ILI9341 doesn't align it's frame buffer in 18-bit mode, but I could be wrong.
Either way, it doesn't matter, since the above has to work.
(It's possible to add a NOP channel to a pixel, so I can pad out an 18-bit pixel to 24 bits)
Real programmers use butterflies
|
|
|
|
|
ok
|
|
|
|
|
The Univac 1100 series did, and 6 characters to a word.
|
|
|
|
|
Same with the PDP-10 when using SIXBIT[^]. I guess Univac did something similar.
|
|
|
|
|
What does that make a char in C?
6 bits?
I count on char being 8 bits in my code quite often.
Though to be fair when I do, it's usually uint8_t or int8_t that I'm using.
Who the elephant has time for a 6 bit character? I knew there were machines with unusual word sizes but this is ridiculous.
Real programmers use butterflies
|
|
|
|
|
Only those of us whose names end in -saurus wrote code for a PDP-10 or Univac.
The PDP-10's memory was limited to 256K words, so a little over 1MB. Hence things like SIXBIT.
I'm guessing that there were versions of C written for it while it was still in use, and a char must have been 8 bits. It did support ASCII, but I don't remember whether that was 7-bit or 8-bit.
|
|
|
|
|
Even in the gory days of hobby coding in the 80s I wasn't dealing with PDPs. Mostly it was 6502s or sometimes a fancy 16-bit 65c816.
Gotta be 8 bits or I'm done. I don't even want to deal with an architecture with a 6-bit word. Who designed it, the mayans?
Real programmers use butterflies
|
|
|
|
|
It was a DEC product. 8 bits hadn't become the de facto standard when it was developed.
|
|
|
|
|
Greg Utas wrote: Only those of us whose names end in -saurus wrote code for a PDP-10 or Univac.
Speak for yourself. I programmed a PDP-8 in University, and I'm not quite ready for the tar pits...
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
My university had one, but I never programmed it. A trophy for you because, if I remember correctly, PDP-8 was an appropriate name for a machine with only 8 opcodes!
|
|
|
|
|
IIRC, the minimum allowable character size in C is 5 bits. If your code relies on 'char' being 8 bits, you should really assert on CHAR_BIT (defined in limits.h).
Most programmers assume that "all the world's a Vax", and use 8-bit characters, 32-bit ints, etc. without a second thought.
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
Typically i use int8_t if i need an 8 bit int
Real programmers use butterflies
|
|
|
|
|
It looks to me like it is the code that multiplies up the y direction. It seems to be giving values about 1/16 as large as they should be.
Good luck, brave programmer.
|
|
|
|
|
It was kind of. I was doing an add after a multiply instead of before it because I forgot some parens.
const size_t offs = ( (dstr.top()+dy)*dimensions().width+dstr.left() ) *pixel_type::bit_depth;
Real programmers use butterflies
|
|
|
|
|
It's the little things in life.
Jack of all trades, master of none, though often times better than master of one.
|
|
|
|
|
That describes much of programming perfectly!
|
|
|
|
|
Blowing a bit of smoke (pun?):
A few posters have shown discomfort with the idea of 2.25 bytes/pixel - I don't really understand their discomfort - the pixels are just there. Indeed, they are most easily grouped by the hardware/firmware in powers of two but that's only an artifact.
The problem, then, is that the artifact of basically all of our tools and abilities is geared towards powers of two and the data is not. Shyte, right ?
So, and here's the blowing smoke part - since you're going to commit a day of life to this (or so you threatened yourself). It's a matter of a proper set of masks - perched in an array that matches the pixel layout - but maybe the mask sizing should be larger than is usually intuitive. Find the lowest common multiple of 8 and 18 - and make that your mask size (at least to start) and then you simply (?) need to iterate through that array to isolate pixel (and/or their components).
You're much better at this than I, for sure, so the forgoing is an "out of the mouths of babes" type of suggestion.
Ravings en masse^ |
---|
"The difference between genius and stupidity is that genius has its limits." - Albert Einstein | "If you are searching for perfection in others, then you seek disappointment. If you seek perfection in yourself, then you will find failure." - Balboos HaGadol Mar 2010 |
|
|
|
|
|
Here's the thing. The # of channels per pixel, and the bit depth of each channel is arbitrary.
I could turn around right now and define a 19 bit pixel and the thing still needs to work.
So I have shifty bits in my code to ... shift bits. Also to set bits on non-byte boundaries.
static void shift_left(void* bits,size_t offset_bits,size_t size_bits, size_t shift) {
if(nullptr==bits || 0==size_bits || 0==shift) {
return;
}
if(shift>=size_bits) {
set_bits(bits,offset_bits,size_bits,false);
return;
}
uint8_t* pbegin = ((uint8_t*)bits)+(offset_bits/8);
const size_t offset = offset_bits % 8;
const size_t shift_bytes = shift / 8;
const size_t shift_bits = shift % 8;
const size_t overhang = (size_bits+offset_bits) % 8;
const uint8_t left_mask = ((uint8_t)uint8_t(0xFF<<(8-offset)));
const uint8_t right_mask = 0!=overhang?uint8_t(0xFF>>overhang):0;
uint8_t* pend = pbegin+(size_t)((offset_bits+size_bits)/8.0+.999999);
uint8_t* plast = pend-1;
uint8_t* psrc = pbegin+shift_bytes;
uint8_t* pdst = pbegin;
if(pbegin+1==pend) {
uint8_t save_mask = left_mask|right_mask;
uint8_t tmp = *pbegin;
*pbegin = uint8_t(uint8_t(tmp<<shift_bits)&~save_mask)|
uint8_t(tmp&save_mask);
return;
}
uint8_t left = *pbegin;
uint8_t right = *(pend-1);
while(pdst!=pend) {
uint8_t src = psrc<pend?*psrc:0;
uint8_t src2 = (psrc+1)<pend?*(psrc+1):0;
*pdst = (src<<shift_bits)|(src2>>(8-shift_bits));
++psrc;
++pdst;
}
*pbegin=(left&left_mask)|uint8_t(*pbegin&~left_mask);
--pend;
*plast=uint8_t(right&right_mask)|uint8_t(*plast&uint8_t(~right_mask));
};
I'll exclude shift_right, plus all the templatized versions of these functions that the compiler computes at compile time instead of run time
and
inline static void set_bits(size_t offset_bits,size_t size_bits,void* dst,const void* src) {
const size_t offset_bytes = offset_bits / 8;
const size_t offset = offset_bits % 8;
const size_t total_size_bytes = (offset_bits+size_bits)/8.0+.999999999;
const size_t overhang = (offset_bits+size_bits) % 8;
uint8_t* pbegin = ((uint8_t*)dst)+offset_bytes;
uint8_t* psbegin = ((uint8_t*)src)+offset_bytes;
uint8_t* pend = ((uint8_t*)dst)+total_size_bytes;
uint8_t* plast = pend-(pbegin!=pend);
const uint8_t maskL = 0!=offset?
(uint8_t)((uint8_t(0xFF>>offset))):
uint8_t(0xff);
const uint8_t maskR = 0!=overhang?
(uint8_t)~((uint8_t(0xFF>>overhang))):
uint8_t(0xFF);
if(pbegin==plast) {
uint8_t v = *psbegin;
const uint8_t mask = maskL & maskR;
v&=mask;
*pbegin&=~mask;
*pbegin|=v;
return;
}
*pbegin&=~maskL;
*pbegin|=((*psbegin)&maskL);
*plast&=~maskR;
*plast|=((*(psbegin+total_size_bytes-1))&maskR);
if(pbegin+1<plast) {
const size_t len = plast-(pbegin+1);
if(0!=len&&len<=total_size_bytes)
memcpy(pbegin+1,psbegin+1,len);
}
}
Real programmers use butterflies
|
|
|
|
|