Posted: 14th Jun 2007 19:26
Let's say I load an image on a 16 bit system, and turn it into a memblock. I do a bit of editing, and output a memblock in 32 bit format (bgra). Can I convert this 32 bit memblock into an image using 'make image from memblock'? Assuming I can, what will happen to the image - will dbpro automatically convert it into 16 bit format, as it does when it loads a 32 bit image on a 16 bit system, or does it stay as 32 bits? Can I then save the image?

The reason I ask, is because I can code a function to read 16 bit images (using Ian's functions), but I wouldn't know how to output a memblock that isn't in 24 or 32 bits - so the question is, is it ok to output it in 24 or 32 bits, or do I really need to ask the question, how do I create a 16 bit memblock.

(I'll see if anyone already knows the answer to this before I post a ton of code and ask people to test it.)

Cheers.
Posted: 14th Jun 2007 19:56
Yes:
+ Code Snippet
set window off
set display mode 640, 480, 16
make memblock 1, 16
write memblock dword 1, 0, 1
write memblock dword 1, 4, 1
write memblock dword 1, 8, 32
write memblock dword 1, 12, rgb(255, 0, 0)
make image from memblock 1, 1

paste image 1, 0, 0, 1

wait key


And it works in reverse too (16 bit image on 32 bit display).

I don't see what the difficulty is in editing a 16 bit image though. Use 5 bits for Red & Blue, 6 bits for Green, offset each pixel by 2 bytes instead of 4 bytes, and make sure your depth is 16 instead of 32. If you need help with it, just ask - I have the colour conversion routines laying around somewhere to make it easier.
Posted: 14th Jun 2007 20:21
Thanks Ian - I was avoiding 16 bit editing on your advice somewhere to somebody ... something like 'best avoid the whole mess'! The problem is, I don't know whether to use arrrrrgggggbbbbb or rrrrrggggggbbbbb - and I'm not sure if I can figure out how to test for which system is in operation.

Nevertheless - assuming I was able to figure out which 16 bit format to use, (or at the worst, guessed), how would I actually go about converting a 32 bit bgra pixel into 16 bits? Somehow I'd need to compose a dword out of odd numbers of bits - but there is no command to write single bits that I can find. This is what I have so far:

+ Code Snippet
    pos=(x * bpp) + (y * bpp * sizex) + 12
    if depth=32 or depth=24
      write memblock byte memblock,pos,blue
      write memblock byte memblock,pos+1,green
      write memblock byte memblock,pos+2,red
      write memblock byte memblock,pos+3,alpha
    endif
    if depth=16
      write memblock dword memblock,pos,convertto16bit(red,green,blue,alpha)

    endif

...............................................................

function convertto16bit(red,gree,blue,alpha)
result as dword

`what goes here?
`result=

endfunction result
Posted: 15th Jun 2007 22:52
Still scratching my head over this one. I'm trying to write a 16 bit image memblock. Even though Ian has shown above that a 32 bit one would still work in 16 bit mode, now that I've started, I'm curious to know how to do it. I have kind of figured out how to write odd numbers of bits to a 16 bit word, but it's not working for me. Here's my progress so far:

+ Code Snippet
`set a colour using rgba values as bytes
red=255
green=255
blue=255
alpha=255


`create 16 bit image memblock using arrrrrgggggbbbbb format
colour1 as word
colour1=assemble_pixel_arrrrrgggggbbbbb(red,green,blue,alpha)

make memblock 1,10*10*16+12
write memblock dword 1,0,10
write memblock dword 1,4,10
write memblock dword 1,8,16

for position=0 to 199
  write memblock word 1,position+12,colour1
next position

make image from memblock 1,1

`create 16 bit image memblock using rrrrrggggggbbbbb format
colour2 as word
colour2=assemble_pixel_rrrrrggggggbbbbb(red,green,blue)

make memblock 2,10*10*16+12
write memblock dword 2,0,10
write memblock dword 2,4,10
write memblock dword 2,8,16

for position=0 to 199
  write memblock word 2,position+12,colour2
next position

make image from memblock 2,2

`test images
paste image 1,0,0,1
text 20,0,bin$(colour1)

paste image 2,0,20,1
text 20,20,bin$(colour2)

wait key
end


function assemble_pixel_arrrrrgggggbbbbb(red as byte,green as byte,blue as byte,alpha as byte)

  rrrrr=int(red/8)
  ggggg=int(green/8)
  bbbbb=int(blue/8)
  a=int(0.5+alpha/255)

  arrrrrgggggbbbbb as word
  arrrrrgggggbbbbb=a<<15 || rrrrr<<10 || ggggg<<5 || bbbbb

endfunction arrrrrgggggbbbbb


function assemble_pixel_rrrrrggggggbbbbb(red as byte,green as byte,blue as byte)

  rrrrr=int(red/8)
  gggggg=int(green/4)
  bbbbb=int(blue/8)
  a=int(0.5+alpha/255)

  rrrrrggggggbbbbb=rrrrr<<11 || gggggg<<5 || bbbbb

endfunction rrrrrggggggbbbbb



In theory, it should create two 16 bit memblocks from the rgba values supplied and paste an image of each at the top left of the screen - one in arrrrrgggggbbbbb format, and another in rrrrrggggggbbbbb format. Problem is, it seems to work for white in both formats (rgb=255,255,255) but try somthing like magenta (rgb=255,0,255) and neither formats seem to work - I just get black. The binary values displayed seem to be what I expected though - so I don't know what's going wrong. Any ideas?

Also - question: one format has a single bit alpha channel ... I don't see what good that is, as it can only be 1 (opaque) or 0 (invisible) - and the other has no alpha channel at all. Do 16 bit graphics have no degrees of transparency other than on or off?
Posted: 16th Jun 2007 0:41
I think the 'avoid the whole mess' comment was for creating memblocks - if you are amending existing ones, then you haven't really got much of a choice.

Here are some functions I wrote some time back for 16 bit colour, with a small example snippet added to the top as an example:
+ Code Snippet
make memblock 1, 14
write memblock dword 1, 0, 1
write memblock dword 1, 4, 1
write memblock dword 1, 8, 16
write memblock word  1, 12, RGB16(255, 0, 0)
make image from memblock 1, 1
paste image 1, 0, 0
wait key
end


function RGB16(Red as dword, Green as dword, Blue as dword)
   local Colour as dword

   Colour = ((Red && 0xf8) << 7) || ((Green && 0xf8) << 2) || (Blue >> 3)
endfunction Colour

function RGBR16(Colour as dword)
   Colour = ((Colour >> 10) << 3) || (Colour >> 12)
endfunction Colour

function RGBG16(Colour as dword)
   local Green as dword

   Green = (Colour && 0x3e0) >> 2
   Green = Green || (Green >> 5)
endfunction Green

function RGBB16(Colour as dword)
   local Blue as dword

   Blue = (Colour && 0x1f) << 3
   Blue = Blue || (Blue >> 5)
endfunction Blue


RGB16 - Generate a 16 bit colour.
RGBR16 - Extract Red from 16 bit colour
RGBG16 - Extract Green from 16 bit colour
RGBB16 - Extract Blue from 16 bit colour

All numbers used are deliberately designed to be in the range 0 to 255 to be compatible with the standard 32 bit colours.

As far as I know, I don't think it's possible to generate a 16 bit image with an alpha bit using memblocks - perhaps someone else has done it? Ive tried the slightly-obvious step of putting '15' in the bit-depth position, but that doesn't work at all.

[EDIT]
Do 16 bit graphics have no degrees of transparency other than on or off?


No. That's why 32 bit is preferable when you are using transparency.
Posted: 16th Jun 2007 1:57
Thanks Ian - those functions will come in handy.

In fact, I tried swapping your functions for mine, and it helped me to spot the bug in my code - turns out that I'd made a silly mistake in calculating the memblock position.

As far as I know, I don't think it's possible to generate a 16 bit image with an alpha bit using memblocks - perhaps someone else has done it?


The following (which now works), shows that you can create a 16bit image in either arrrrrgggggbbbbb or rrrrrggggggbbbbb format - at least it works on my system - anyone care to check it works for them too?

The alpha value simply turns the pixels off (values 0-127) or on (values 128-255).

+ Code Snippet
`set a colour using rgba values as bytes
red=255
green=255
blue=0
alpha=255


`create 16 bit image memblock using arrrrrgggggbbbbb format
colour1 as word
colour1=assemble_pixel_arrrrrgggggbbbbb(red,green,blue,alpha)

make memblock 1,10*10*2+12
write memblock dword 1,0,10
write memblock dword 1,4,10
write memblock dword 1,8,16

for position=0 to 199 step 2
  write memblock word 1,position+12,colour1
next position

make image from memblock 1,1

`create 16 bit image memblock using rrrrrggggggbbbbb format
colour2 as word
colour2=assemble_pixel_rrrrrggggggbbbbb(red,green,blue)

make memblock 2,10*10*2+12
write memblock dword 2,0,10
write memblock dword 2,4,10
write memblock dword 2,8,16

for position=0 to 199 step 2
  write memblock word 2,position+12,colour2
next position

make image from memblock 2,2

`test images
paste image 1,0,0,1
text 20,0,bin$(colour1)

paste image 2,0,20,1
text 20,20,bin$(colour2)

wait key
end


function assemble_pixel_arrrrrgggggbbbbb(red as byte,green as byte,blue as byte,alpha as byte)

  rrrrr=int(red/8)
  ggggg=int(green/8)
  bbbbb=int(blue/8)
  a=int(0.5+alpha/255.0)

  arrrrrgggggbbbbb as word
  arrrrrgggggbbbbb=a<<15 || rrrrr<<10 || ggggg<<5 || bbbbb

endfunction arrrrrgggggbbbbb


function assemble_pixel_rrrrrggggggbbbbb(red as byte,green as byte,blue as byte)

  rrrrr=int(red/8)
  gggggg=int(green/4)
  bbbbb=int(blue/8)
  a=int(0.5+alpha/255)

  rrrrrggggggbbbbb=rrrrr<<11 || gggggg<<5 || bbbbb

endfunction rrrrrggggggbbbbb


The only thing left to do is figure out which format the input memblock is in .... working on something now .......
Posted: 16th Jun 2007 2:37
This tests the 16bit format - assuming the system is working in 16 bit:

+ Code Snippet
set window off
set display mode 800,600,16
dot 0,0,rgb(0,255,0)
get image 1,0,0,1,1,1
make memblock from image 1,1
depth=memblock dword(1,8)
if depth=16 then binary$=bin$(memblock word(1,12))
if depth=32 then binary$=bin$(memblock dword(1,12))
if depth=16 and mid$(binary$,23)="0" then format$="%arrrrrgggggbbbbb"
if depth=16 and mid$(binary$,23)="1" then format$="%rrrrrggggggbbbbb"
text 0,0,"binary green: "+binary$
text 0,20," format: "+format$
wait key
end


Probably not very efficient parsing the binary string - but it only needs to be called once. The 23rd character of the string is the key - for a green pixel it is 1 in rrrrrggggggbbbbb format and 0 in arrrrrgggggbbbbb format.

Interesting points:

1. simply setting the display mode to 16 bits does not actually change the grabbed image to 16 bits on my system - I have to change the graphics settings in the control panel. <edit> Ok, I just spotted in Ian's original code snippet the 'set window off' - now I know why that's there!

2. when in 32 bit format, the code returns a dword for a green pixel as binary %11111111000000001111111100000000 on my system - that's argb - not bgra as I expected. That's got me very worried now that all my 32 bit memblock routines might be wrong too!
Posted: 16th Jun 2007 2:43
So where exactly are you instructing DBPro that image 1 contains 1 bit of alpha with 5 bits of the other colours, and that image 2 contains 5 bits of red and blue with 6 bits of green?

In fact, it looks like the 1+5+5+5 version of colours is the one that DBPro uses in memblocks, not the 0+5+6+5. Hey, learn something new every day (Change the red value to 0, the alpha version goes green, the non-alpha version disappears)

[EDIT]
Remember that Intel processors are little-endian - when you write a 32 bit value, the low bytes are written first, so if you read bytes, they are in the order bgra, but if you read a dword they are interpreted as argb.
Posted: 16th Jun 2007 3:07
Change the red value to 0, the alpha version goes green, the non-alpha version disappears)


Your right - hadn't spotted that. Is it safe to say that this is the case for everybody though? If it is it makes the above 'test the green pixel' code redundant.

where exactly are you instructing DBPro that image 1 contains 1 bit of alpha with 5 bits of the other colours, and that image 2 contains 5 bits of red and blue with 6 bits of green?


If you have to ask, the chances are I've got it wrong then - I only learned what a bitshift was this evening! But this is where I thought I had done it (code commented):

+ Code Snippet
function assemble_pixel_arrrrrgggggbbbbb(red as byte,green as byte,blue as byte,alpha as byte)

`each colour as a byte has a max value of 2^8=256, but as 5 bits it has a max value of 2^5 = 32,
`so I'm dividing by 8 to get from 256 to 32, thus converting from a byte to 5 bits.

  rrrrr=int(red/8) 
  ggggg=int(green/8)
  bbbbb=int(blue/8)

`alpha as a byte has max value of 256, but as 1 bit it's max value is 1, so I'm
`dividing by 256 to get from 256 to 1 (+0.5 to round to an integer properly), thus converting a byte to a single bit

  a=int(0.5+alpha/255.0)

  arrrrrgggggbbbbb as word

  `the single alpha bit starts at the right end of the 16 bit word, so it needs shifting 15 places to the left

  `five red bits need shifting 10 places to the left, and 5 green bits shift 5 to the left ..

  arrrrrgggggbbbbb=a&lt;&lt;15 || rrrrr&lt;&lt;10 || ggggg&lt;&lt;5 || bbbbb

endfunction arrrrrgggggbbbbb


function assemble_pixel_rrrrrggggggbbbbb(red as byte,green as byte,blue as byte)

  rrrrr=int(red/8)

  `here, green is 6 bits, which has a max value of 2^6=64, so I'm dividing by 4 to get from 256 to 64, thus converting a byte into 6 bits.

  gggggg=int(green/4)
  bbbbb=int(blue/8)
  a=int(0.5+alpha/255)

  `and then shifting the relevant amounts here ..
  rrrrrggggggbbbbb=rrrrr&lt;&lt;11 || gggggg&lt;&lt;5 || bbbbb

endfunction rrrrrggggggbbbbb


so if you read bytes, they are in the order bgra, but if you read a dword they are interpreted as argb.


Ah - so the key is to stick to reading bytes. That's ok then.
Posted: 16th Jun 2007 12:30
Dividing by powers of two is messy anyway, what with the forcing it to round a higher number down, and using the int function. That works okay most of the time, but in the case of making 32-bit color values, bit shifting is the best way, I think. Signs are not a problem, and bit shift and masking are the preferred way to avoid problems. Divide by 256 = shr 8, or >> 8.

wrt the format, is the bitmap header preserved in the case of bmps, for example? The format of the pixels and color depth are wildly different, obviously.

rrrrr=int(red/8)
ggggg=int(green/8)
bbbbb=int(blue/8)

is equivalent to:

rrrrr = red >> 3
ggggg = green >> 3
bbbbb = blue >> 3

a=int(0.5+alpha/255.0)

is equivalent to:

a = alpha >> 8

There is no attendant rounding, sign issues, etc, and it is probably much faster, too. Promoting and demoting with math are problematic in many ways. The processor flags get involved, and then...things are dicey. Bit shifting does not affect the same flags as math does, which is more important to a high level language than it appears at first blush, I suspect.
Posted: 16th Jun 2007 13:01
Divide by 256 = shr 8, or >> 8.


Ah - I hadn't thought of that. So bit shifting right by n, is the same as dividing by 2^n? Just need to get it right in my head why that works .... as I don't ususally think in binary!!
Posted: 16th Jun 2007 13:03
yes, but I think it is supposed to be only 7 shifts, because the max value is 255, not 256...256 is a 16 bit value. It works both ways, shift left = multiply by powers of two.

edit: btw, signs are not accounted for in this! That is a little more involved, because you need to convert from/to two's complement before/after the shift.

+ Code Snippet
function jzS16DivByPow2(dividend as integer, power as integer)
   if dividend &amp;&amp; 0x8000
      dividend = dividend .. 0xffff
      inc dividend
      dividend = dividend &gt;&gt; power
      dividend = dividend .. 0xffff
      inc dividend
   else
      dividend = dividend &gt;&gt; power
   endif
endfunction dividend


That is not important to this discussion, but...be aware that signed values work a little differently.
Posted: 16th Jun 2007 20:43
Hmmm ... I hadn't considered the fact that colour values have a 0-255 range rather than 1-256. Can we just add 1 before dividing, then take one of again afterwards to solve the
Promoting and demoting with math are problematic in many ways.
problem? ie.

red(5bit) = ((red(8bit)+1) >> 3)-1

Or would that still be a bad idea (or just plain wrong!)?
Posted: 16th Jun 2007 22:46
I think that unnecessary is more appropos. The dword you are creating is actually a packed structure, not a value. Bitfields are really structures. There is an alpha channel, red, green and blue. There are two methods of extracting the individual fields. One is by math, the other is mask and shift. You are not using masking, but it is not necessary. Shifting will work by itself, but masking and shifting is more efficient and faster, I think.

To strip a byte out of a dword, you mask the byte's position with ones, and the rest are zeroes. I use hex for the mask because it looks more logical to me than using decimal. You use the and operator, and then you shift the result into a byte, like this:

To get the MSB out of a dword, mask it with 0xff000000 and shift right 24 times.
i.e, byteval1 = (dwval && 0xff000000) >> 24 rem takes the MSB only.
byteval2 = (dwval && 0x00ff0000) >> 16 rem takes the next 8
byteval3 = (dwval && 0x0000ff00) >> 8
byteval4 = dwval && 0x000000ff

That would get the bytes out correctly from a dword. It does not affect the variable at all, it simply takes what it want from it.

Now, if the red is packed to the left (msb, lower case means bit, as opposed to BYTE.) and is contained in 5 bits, it is only necessary to shift it right 3 positions. This is not really division, it is a bitfield, and it is packed, which means the position it occupies is not its value, merely its position. It remained very fuzzy to me for a very long time!

Look at this from Quake III, sometimes programmers are too cute! I guess John Carmack is no exception:

#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I')
// little-endian "IBSP"

IDBSPHEADER is now a dword, but it is really 'IBSP'. The code to check it only tests a dword against IDBSPHEADER...its not a string compare!
Posted: 17th Jun 2007 1:39
Right - thanks Jinzai, that explanation has really helped - it's all beginning to click now. I'm kind of glad I did it the long way around first using maths, because working something out for yourself from scratch - even in a cack-handed way, always gives you a clearer understanding of what's actually happening. I do think it's a bit confusing using hex values for the mask, then shift vales which apply to binary, though! I think I'll probably use binary masks in my code so I can see visually how much the shifts need to be.

Anyway - thanks to both Ian and Jinzai for helping on this one - my current project involves blending of images with different sizes and depths - and I can proceed with a little more confidence now!
Posted: 17th Jun 2007 1:54
That's not just 'cute' - that's a great idea for code that is written to be multi-platform. Reading the first dword of the data file will tell you straight away whether the values are stored little-endian or big-endian format within the file, and allow your code to adjust the values correctly or reject the file.

@Ric,
I also advise you to not use maths when manipulating at the bit level. The sign bit will screw you up every time.

There's no difference in speed in the processor between shift+mask, mask+shift, or shift+shift - ie, these are equivalent for getting the red value from a 32 bit colour:

byteval2 = (dwval && 0x00ff0000) >> 16 ` Mask + shift
byteval2 = (dwval >> 16) && 0xff ` Shift + Mask
byteval2 = (dwval << 8) >> 24 ` Shift + Shift

Also, you'll see that I did some or'ing in the code I posted above.

Green = (Colour && 0x3e0) >> 2 ` Mask + Shift
Green = Green || (Green >> 5) ` Fill in lowest 3 bits with the high 3 bits

That was because I wanted the highest 16 bit representation of green to give a green value of 255, not 248. Putting the top 3 bits of the value into the lower 3 bits gives a sliding adjustment to the value that will give 0 when you want darkest, and 255 when you want brightest.
Posted: 17th Jun 2007 2:24
np, I can't wait to see it!

IanM,
I meant cute in the way he uses the #define with literals and shifts, not numbers to create the value. About its use, I agree, especially when that is exactly what he does with it in his code.

I do not have x86 asm reference material at hand, but...aren't the two choices given by the processor an absolute shift of eax by n positions, or shift eax, edx? If you look at those instructions, the clock cycles should depend on the number of shifts, so there will be a difference in timing.

That's nice code, but...you knew that already. Since you are nearly broaching the subject anyway...do you have any 32-bit CRC code in your toolbelt?
tks, and cheers.
Posted: 17th Jun 2007 3:16
I meant cute in the way he uses the #define with literals and shifts


I get what you mean now

IIRC, DBPro never uses the absolute instructions - it always uses the variable instructions. DBPro doesn't do any constant folding or optimisation, so it doesn't always produce optimal code (that didn't really surprise you, did it? )

I don't have any CRC code written in DBPro as I've not had a need for it so far. It shouldn't be too hard or take too long to write a simple CRC generator though
Posted: 17th Jun 2007 3:19
Yes, but, you know...if you had already, I would not need to!!!!! Okay, I guess I have something to do tonight now.

Thank you for the hidden inner workings blurb. No, I wasn't too surprised, but you are teaching me alot these days, no doubt there.

Thanks again.