gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Pixel Blending Routine

#44546 - telamon - Thu Jun 02, 2005 7:56 pm

Hi! I've kindof become stuck. I wrote this color blending routine, but it's spitting out completely wrong values. I've spent much about the whole day figureing out why it won't work but have had no sucess. :(

If anyone of you could help me getting this to work, i'd be extremely grateful.

Code:

u16 Blend_Normal(u16 c1,u16 c2,u8 a){
   // input: Color 1 , Color 2 , Alpha value.
   // 32 blending levels
   #define MAXBIT 5
   #define MAX (1<<(MAXBIT))
   /*
         u16
   0 00000 00000 00000
      B     G      R
   */
   if(c1 == Transparent_Color)
      c1 = Canvas_Color;
   if(c2 == Transparent_Color)
      return Transparent_Color;
      
   u8 rgb[3],i;
   for(i=0;i<3;i++){
      rgb[i]=(
      ((c1<<(i*5+1))>>11)*(MAX-a)
      +
      ((c2<<(i*5+1))>>11)*a
      )>>MAXBIT;
   }
   return RGB15(rgb[2],rgb[1],rgb[0]);
}

_________________
http://manifested.ath.cx

#44549 - TheMikaus - Thu Jun 02, 2005 8:14 pm

Are you sure that shifting erases the extras? I know that on some platforms it does but on other it doesn't.

maybe you should just mask the values instead

MaskA1 = cl && 0x1F;
MaskB1 = cl && (0x1F << 5);
MaskC1 = cl && (0x1F << 10);

Similar with the second color.

And then perform you blending operations

Also you'll probably want to check for value overflows.
If your code doesn't already.

---Edit---
Actually. Are you sure that in general it is correct?

Max * a is 'a' a fixed point value? if so I'm not sure that the multiplation would know the difference. You'd get over flows all over the place I would think. I mean .45 might be like 00111 or something which would be multiplication by 7. Of course I could be full of it since I know very little of what your blend function is actually doing. but that's what I got out of it.

#44552 - strager - Thu Jun 02, 2005 8:21 pm

telamon wrote:

Code:

u16 Blend_Normal(u16 c1,u16 c2,u8 a){
   // input: Color 1 , Color 2 , Alpha value.
   // 32 blending levels
   #define MAXBIT 5
   #define MAX (1<<(MAXBIT))
   /*
         u16
   0 00000 00000 00000
      B     G      R
   */
   if(c1 == Transparent_Color)
      c1 = Canvas_Color;
   if(c2 == Transparent_Color)
      return Transparent_Color;
      
   u8 rgb[3],i;
   for(i=0;i<3;i++){
      rgb[i]=(
      ((c1<<(i*5+1))>>11)*(MAX-a)
      +
      ((c2<<(i*5+1))>>11)*a
      )>>MAXBIT;
   }
   return RGB15(rgb[2],rgb[1],rgb[0]);
}


Maybe this'll fix it:
Code:

   u8 rgb[3]
   int channel;

   for(channel = 0; channel <= 3; channel++)
   {
      rgb[i] = ((((c1 >> (channel * 5)) & 31) * (31 - a) +
                (((c2 >> (channel * 5)) & 31) * a) / 2; /* Edit: May be / 31 !! */
   };

   return(RGB15(rgb[0], rgb[1], rgb[2]));


What I've fixed:
Changed i to channel, and made it an int.
Changed < to <= in the for loop.
Rewrote the rgb[i] = ... and added a / 2 (could change to >> 1).
Switched around the return.

#44555 - telamon - Thu Jun 02, 2005 8:35 pm

Oh yeah this gives me something to follow thanks alot.
Quote:
Are you sure that shifting erases the extras? I know that on some platforms it does but on other it doesn't.

Hmm i did't know about that. But seems strager figured out my problem. Gonna see if it works now.[/code]
_________________
http://manifested.ath.cx

#44563 - telamon - Thu Jun 02, 2005 10:37 pm

Ok i've got this stuff working, the masking seemed to have been the problem. Thanks to strager that's fixed =)
Here's the working function if anyone of you have use for it.

Code:

/** Input Color1 and Color 2 and Color2's opacity.
Returns blended color */
u16 Blend_Normal(u16 c1,u16 c2,u8 a){
   /*
         u16
   0 00000 00000 00000
      B     G      R
   */
   if(c1 == Transparent_Color)
      c1 = Canvas_Color;
   if(c2 == Transparent_Color)
      return Transparent_Color;
      
   u8 rgb[3],channel;
   for(channel=0;channel<3;channel++){
      rgb[channel] = (((c1 >> (channel * 5)) & 31) * (31 - a) +
        (((c2 >> (channel * 5)) & 31) * a)) >> 5;
   }
   return RGB15(rgb[0],rgb[1],rgb[2]);
}


Thanks for the help guys!
_________________
http://manifested.ath.cx

#44565 - strager - Thu Jun 02, 2005 10:42 pm

You are very welcome, and good luck in your project!

#44586 - Darkain - Fri Jun 03, 2005 3:15 am

this is what i'm using for alpha blending in DarkStar's gfx lib. maybe itll help ya out?

Code:
#define RGB(r,g,b)  ((r)+((g)<<5)+((b)<<10))
#define BGR(b,g,r)  ((r)+((g)<<5)+((b)<<10))
#define RGB16(r,g,b)  ((r)+((g)<<5)+((b)<<10)+0x8000)
#define BGR16(b,g,r)  ((r)+((g)<<5)+((b)<<10)+0x8000)

#define REDMASK   (0x7C00)
#define GREENMASK (0x03E0)
#define BLUEMASK  (0x001F)
#define ALPHAMASK (0x8000)
#define EXTENDEDMASK (0xF8000)

#define RGBRED(c)   (((c)>> 0) & 0x001F)
#define RGBGREEN(c) (((c)>> 5) & 0x001F)
#define RGBBLUE(c)  (((c)>>10) & 0x001F)

#define WHITE RGB(31,31,31)
#define BLACK RGB(0,0,0)

Code:
    inline u16 mixColors(u16 c1, u16 c2) { return ((c1>>1)&0x3DEF) + ((c2>>1)&0x3DEF) | 0x8000; }

    inline u16 alphaBlend(u16 c1, u16 c2, u8 trans) {
      if (c1 == c2) return c1;
      trans &= 0x1F;
      switch(trans) {
        case  0: return c1;
        case  7: return mixColors(c1, mixColors(c1, c2));
        case 15: return mixColors(c1, c2);
        case 23: return mixColors(c2, mixColors(c2, c1));
        case 31: return c2;
      }
      register u8 trans2 = 31-trans;
      return (u16)0x8000 |
        (u32)(((((c2&BLUEMASK) *trans)+((c1&BLUEMASK) *trans2))&GREENMASK)
            | ((((c2&GREENMASK)*trans)+((c1&GREENMASK)*trans2))&REDMASK)
            | ((((c2&REDMASK)  *trans)+((c1&REDMASK)  *trans2))&EXTENDEDMASK)) >> 5;
    }

_________________
-=- Darkain Dragoon -=-
http://www.darkain.com
DarkStar for Nintendo DS

#44601 - telamon - Fri Jun 03, 2005 10:10 am

Oh darkain! i was trying to contact you earlier. I was wondering if you stored alpha data for each pixel and calculated colors during the screen draw, or if you simply hard-painted alpha values during the penstrokes.

My app uses hardpainted alpha atm , but that ends up in ugly white edges when painting in a layer underneath. But having separate alpha values for each pixel will increase the bit size for my layer arrays. :/
And ofcourse having to re-calculate the alpha values for each frame will drain the resources too. :/ I wonder if there's anything in between or some nice alpha-hack.

Heh i used a few odd words to describe the problem. You can try this wip demo and see for yourselves what i mean :)
http://molested.ath.cx/code/nds/Artalot0.99-WIP.zip
Yep, i broke the brush again... :(
_________________
http://manifested.ath.cx

#44616 - Darkain - Fri Jun 03, 2005 6:43 pm

heh... i only work with a single buffer of memory right now, no layering at all. everything you see on the screen is always the same single bitmap. i dont do any fancy layering techniques at all.
_________________
-=- Darkain Dragoon -=-
http://www.darkain.com
DarkStar for Nintendo DS

#44621 - strager - Fri Jun 03, 2005 7:14 pm

What would be nice in a DS drawing program is that the colors are stored in 32-bits, and they are dithered (or whatever technique you chose) on-the-fly to the video buffer. This way, you could easily implement alpha to each pixel (using the unused 8 bits), and could access each channel with no hassle (assuming you are using memory where it is 8-bit RW).

#44666 - Darkain - Sat Jun 04, 2005 12:30 am

a) video ram is 16bit access *only* (32bit read/writes i do beleive become broken down into a pair of 16bit read/writes)

b) heh, dithering + downsampling from 32bit to 16bit isnt the fastest of processes either...
_________________
-=- Darkain Dragoon -=-
http://www.darkain.com
DarkStar for Nintendo DS

#44676 - strager - Sat Jun 04, 2005 1:32 am

Darkain wrote:
b) heh, dithering + downsampling from 32bit to 16bit isnt the fastest of processes either...


Dithering could be removed, and the channels just need to be shifted right 3 bits:
Code:
#define RGB32toRGB16(c) \
/* Red */ \
( ( (((c) & 0xFF) >> 3) | \
/* Green */ \
((((c) & 0xFF00) >> 3) << 5 | \
/* Blue */ \
((((c) & 0xFF0000) >> 3) << 10) ) | \
/* Alpha Mask */ \
(((c) & 0xFF000000) ? (1 << 15) : 0) )


I hope I have those parentheses correct...

#44680 - Darkain - Sat Jun 04, 2005 2:19 am

strager wrote:
Darkain wrote:
b) heh, dithering + downsampling from 32bit to 16bit isnt the fastest of processes either...


Dithering could be removed, and the channels just need to be shifted right 3 bits:
Code:
#define RGB32toRGB16(c) \
/* Red */ \
( ( (((c) & 0xFF) >> 3) | \
/* Green */ \
((((c) & 0xFF00) >> 3) << 5 | \
/* Blue */ \
((((c) & 0xFF0000) >> 3) << 10) ) | \
/* Alpha Mask */ \
(((c) & 0xFF000000) ? (1 << 15) : 0) )


I hope I have those parentheses correct...


either your code, or your comments may not be correct, because the DS, like the GBA, uses BGR instead of RGB. ;)
_________________
-=- Darkain Dragoon -=-
http://www.darkain.com
DarkStar for Nintendo DS

#44682 - strager - Sat Jun 04, 2005 2:38 am

Darkain wrote:
either your code, or your comments may not be correct, because the DS, like the GBA, uses BGR instead of RGB. ;)


Ah, but if the 32 bit colors were stored AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR, that wouldn't be the case. ;-)