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.

C/C++ > Fading the BG palette....

#26313 - sfz989 - Sun Sep 12, 2004 8:29 pm

I want the screen to fade from black to my tiles. I've been tryin to get this working right for 3 days now......I know, I'm a n00b. Anyway can someone show me what is wrong with my code or give different code that will work? thanks in advance. Oh yeah, guess I should metion this is my first real project on gba, just to test my understanding of it....

Code:

#include "gba.h"

#define colors 0x100
#define cnt_max 0x800

u16 palette[] = {0x7FFF,0x03E0,0x0019,0x0000,};

void fade_in()
{
   u16 loop1, loop2, r[colors], g[colors], b[colors], rt[colors], gt[colors], bt[colors];
   u8 cnt, low_color, rd[colors], gd[colors], bd[colors], tempPalette[colors];
   s8 diff;

   for( loop1 = 0; loop1 < colors; loop1 ++ )
   {
      rt[loop1] = (palette[loop1] << 10) >> 10;  // get red
      gt[loop1] = (palette[loop1] << 5) >> 10;   // get green
      bt[loop1] = palette[loop1] >> 10;          // get blue
      tempPalette[loop1] = palette[loop1];   // put palette[] in tempPalette[]
      palette[loop1] = 0x0000;          // set palette[] to black
      BG_PaletteMem[loop1] = palette[loop1]; // set screen to black
   }
   
   for( loop2 = 0; loop2 < colors; loop2 ++ )
   {
      if( cnt < cnt_max ){         // slows fade down
         while( palette[loop2] < tempPalette[loop2] )
         {
            diff = -1;            // set to diff to -1
            rd[loop2] = rt[loop2]-r[loop2];      // diff between red
            gd[loop2] = gt[loop2]-g[loop2];      // diff between green
            bd[loop2] = bt[loop2]-b[loop2];      // diff between blue

            if( rd[loop2] > diff ){diff = rd[loop2]; low_color = 0;}
            if( gd[loop2] > diff ){diff = gd[loop2]; low_color = 1;}
            if( bd[loop2] > diff ){diff = bd[loop2]; low_color = 2;}

            if( low_color == 0 )
            {
               palette[loop2] += 0x0001; r[loop2] += 0x0001;
            }
            if( low_color == 1 )
            {
               palette[loop2] += 0x0020; g[loop2] += 0x0020;}
            if( low_color == 2 )
            {
               palette[loop2] += 0x0400; b[loop2] += 0x0400;
            }

            BG_PaletteMem[loop2] = palette[loop2];
            cnt = 0;
         }
      }else{
         cnt += 1;
      }
   }
}

int main()
{
   u8 i, x, y;
   u16* ScreenBB = (u16*)ScreenBaseBlock(8);
   u16* CharBB = (u16*)CharBaseBlock(0);
   REG_BG0CNT = BG_COLOR_256|TEXTBG_SIZE_256x256|(8<<SCREEN_SHIFT)|(0<<CHAR_SHIFT);
   
   SetMode(MODE_0|BG0_ENABLE);

   BG_PaletteMem[0] = RGB(31,31,31); //0x7FFF -white
   BG_PaletteMem[1] = RGB(0,31,0); //0x03E0 -green
   BG_PaletteMem[2] = RGB(0,0,25); //0x0019 -blue

   for(i=0;i<32;i++)
   {
      CharBB[i] = 0x0000;
   }

   for(i=32;i<64;i++)
   {
      CharBB[i] = 0x0101;
   }

   for(i=64;i<96;i++)
   {
      CharBB[i] = 0x0202;
   }

   x = 5;
   y = 3;
   ScreenBB[x+(32*y)] = 1;

   x = 15;
   y = 8;
   ScreenBB[x+(32*y)] = 2;
   
   fade_in();
   
   while(1){}
}


Last edited by sfz989 on Mon Sep 13, 2004 2:24 am; edited 1 time in total

#26317 - poslundc - Sun Sep 12, 2004 8:43 pm

It is helpful if you tell us what it is you are seeing vesus what you expect to see. But right off the bat I notice an error:

Code:
 ? ?? ?rt[l1]=(palette[l1]<<10)>>10;? // get red
 ? ?? ?gt[l1]=(palette[l1]<<5)>>10;? ?// get green
 ? ?? ?bt[l1]=palette[l1]>>10;? ?? ? ? ?// get blue


Shifting to get rid of the high bits in this fashion is unlikely to work in the first place, because the compiler will be treating the number as 32-bit instead of 16-bit. But even if it does, you would have to shift an extra bit in order properly zero 16 bits (you are shifting for 15 bits).

You are far better off using bitwise-and, as follows:

Code:
red = c & 0x1F;
green = (c >> 5 ) & 0x1F;
blue = (c >> 10) & 0x1F;


Dan.

#26322 - sajiimori - Sun Sep 12, 2004 9:19 pm

Style tips when posting code for others to read:

Get rid of all the magic numbers.

Variable names like "l1" are very hard to read because at first glance they almost look like numbers.

Whitespace around infix operators (= + - * / < > << >>) is helpful.

Put opening braces on their own lines, directly under the first character of the previous line. Use a different style privately if you want, but it's a very well established standard.

Use proper indentation.
Code:

// bad
for(l2=0;l2<256;l2++){                      // loop through all colors
   if(cnt<2048){

// good (besides l2 and the magic numbers)
for(l2 = 0; l2 < 256; l2++)  // loop through all colors
{
    if(cnt < 2048)
    {
Again, use whatever style you like in private, but try to do things the traditional way when asking others to read your code. =)

#26331 - sfz989 - Mon Sep 13, 2004 2:31 am

Thanks for the advice sajiimori. I think I have my code setup the right way now.

Here's what the code really does:
BG_PaletteMem[0] = RGB(0,0,1); //it should be RGB(31,31,31);
BG_PaletteMem[1] = RGB(0,1,1); //should be RGB(0,31,0);
BG_PaletteMem[2] = RGB(25,0,0); //should be RGB(0,0,25);

#26342 - FluBBa - Mon Sep 13, 2004 2:24 pm

There seem to be a lot of different erros in the code...
cnt is not initialized neither is the r array, the cnt/cnt_max check looks broken.
Try this instead:
Code:

#define colors 0x100

u16 palette[] = {0x7FFF,0x03E0,0x0019,0x0000,};

void fade_in()
{
    u32 luma;
    for( luma = 1 ; luma < 257 ; luma++)
    {
        fade(luma);
        VSyncWait();
     }
}
void fade(int luma)
{
   u32 loop1, loop2,r,g,b;

   for( loop1 = 0; loop1 < colors; loop1 ++ )
   {
      r = ((palette[loop1] & 0x1f) * luma)>>8;             // get red
      g = (((palette[loop1] >> 5) & 0x1f)*luma)>>8;    // get green
      b = (((palette[loop1] >> 10) & 0x1f)*luma)>>8;  // get blue
      BG_PaletteMem[loop1] = r | (g<<5) | (b<<10);  // set screen colors
   }
}

_________________
I probably suck, my not is a programmer.

#26349 - sfz989 - Mon Sep 13, 2004 5:07 pm

I never could figure out what was wrong with my code, but FluBBa's works perfectly. I'm not exactly sure how your code works, but Thanks a million!

#26358 - sajiimori - Mon Sep 13, 2004 6:16 pm

I'm not sure which part you don't understand, but splitting up the work might make each operation more clear:
Code:
#define COLORS 0x100

u16 palette[COLORS] = {0x7FFF,0x03E0,0x0019,0x0000,};

int make_rgb(int r, int g, int b)
{
    return r | (g << 5) | (b << 10);
}

int get_red(int color)
{
    return color & 0x1f;
}

int get_green(int color)
{
    return (color >> 5) & 0x1f;
}

int get_blue(int color)
{
    return (color >> 10) & 0x1f;
}

int dim_component(int component, int luma)
{
    return (component * luma) >> 8;
}

int dim_color(int color, int luma)
{
    return make_rgb(
        dim_component(get_red(color),   luma),
        dim_component(get_green(color), luma),
        dim_component(get_blue(color),  luma));
}

void dim_palette(int luma)
{
    int i;
    for(i = 0; i < COLORS; i++)
        BG_PaletteMem[i] = dim_color(palette[i], luma);
}

void fade_in()
{
    int luma;
    for(luma = 1; luma < COLORS + 1; luma++)
    {
        dim_palette(luma);
        VSyncWait();
    }
}
Inline as preferred.

#26377 - sfz989 - Tue Sep 14, 2004 4:24 am

Quote:

int dim_component(int component, int luma)
{
return (component * luma) >> 8;
}

I get everything else but there's this one part I still dont' quite understand. Why do you multiply the color by luma then shitf it by 8 bits, what does this do? Sorry if this is really simple, I just don't get it...

#26378 - sajiimori - Tue Sep 14, 2004 5:06 am

Well, luma is the numerator of a fraction whose denominator is 256. So, when luma=1 then the fraction is 1/256 and when luma is 256 the fraction is 256/256. The color component's value is reduced by multiplying by that fraction. Obviously, when the fraction is 256/256, multiplying by it won't affect the component at all.

And, in case you didn't know, shifting right by 8 bits is the same as dividing by 256. 256 was chosen as the denominator because it is a power of 2, which allows a shift instead of a divide.

#26381 - sgeos - Tue Sep 14, 2004 8:15 am

I think you people are completely insane... messing with individual palette entries to get a fade effect. Just use the blend registers.

Code:
/***   Bits
 */
#define BIT(x)   (1 << (x))

   // LCD I/O Color Special Effects
   #define REG_BLDCNT   (*(vu16*)0x4000050)   // Color Special Effects Selection (Read/Write)
      #define BLD_1ST(a)   BIT((a) + 0)
      #define BLD_1ST_BG0   BIT(0)
      #define BLD_1ST_BG1   BIT(1)
      #define BLD_1ST_BG2   BIT(2)
      #define BLD_1ST_BG3   BIT(3)
      #define BLD_1ST_OBJ   BIT(4)
      #define BLD_1ST_BD   BIT(5)
      #define BLD_1ST_ALL   0x003F
      #define BLD_OFF      0x0
      #define BLD_ALPHA   BIT(6)
      #define BLD_INC      BIT(7)
      #define BLD_DEC      (BIT(6) | BIT(7))
      #define BLD_MODE_MASK   (BIT(6) | BIT(7))
      #define BLD_2ND(a)   BIT((a) + 8)
      #define BLD_2ND_BG0   BIT(8)
      #define BLD_2ND_BG1   BIT(9)
      #define BLD_2ND_BG2   BIT(10)
      #define BLD_2ND_BG3   BIT(11)
      #define BLD_2ND_OBJ   BIT(12)
      #define BLD_2ND_BD   BIT(13)
      #define BLD_2ND_ALL   0x3F00
   #define REG_BLDALPHA   (*(vu16*)0x4000052)   // Alpha Blending Coefficients (Write)
      #define BLD_EVA(a)   ((a) << 0)
      #define BLD_EVB(a)   ((a) << 8)
      #define BLD_EVA_MASK   BLD_EVA(0x1F)
      #define BLD_EVB_MASK   BLD_EVB(0x1F)
   #define REG_BLDY   (*(vu16*)0x4000054)   // Brightness (Fade-In/Out) Coefficient (Write)
      #define BLD_EVY(a)   ((a) << 0)
      #define BLD_EVY_MASK   BLD_EVY(0x1F)


void vsync(void)
{
   increase_frame_count();
   /* If we are in the vblank, wait for vdraw */
   while (REG_VCOUNT > 159)
   {
      /* wait */
   }
   /* Wait for vblank to begin */
   while (REG_VCOUNT < 160)
   {
      /* wait */
   }
}

void fade_to_black(int frames)
{
   int i;

   REG_BLDCNT =   BLD_DEC | BLD_1ST_ALL;
   REG_BLDY =   BLD_EVY(0);
   for (i = 0; i < frames; i++) {
      vsync();
      REG_BLDY = BLD_EVY(16 * i / frames);
   }
   REG_BLDY =   BLD_EVY(16);
}

void fade_from_black(int frames)
{
   int i;

   REG_BLDCNT =   BLD_DEC | BLD_1ST_ALL;
   REG_BLDY =   BLD_EVY(16);
   for (i = 0; i < frames; i++) {
      vsync();
      REG_BLDY = BLD_EVY(16 - 16 * i / frames);
   }
   REG_BLDCNT =   BLD_OFF;
   REG_BLDY =   BLD_EVY(0);
}


-Brendan

#26382 - DekuTree64 - Tue Sep 14, 2004 9:30 am

One nice optimization for color blending is to do the red and blue in one pass. If you make your luma range from 0-31, and clear out the green bits, you can safely overflow into the hole, shift down, and re-clear the green bits. Green will have to be done by itself though, but that's still 1/3 of your work gone.

Code:
rb = originalColor & ~(31<<5); // clear green
rb *= luma;
rb >>= 5;
rb &= ~(31<<5);

g = originalColor & (31<<5); // get green
g *= luma;
g >>= 10;  // chop the lower bits off, as well as the 5 empty bits where red was

finalColor = rb | (g << 5); // this can be done in a single ARM instruction

That should be fairly quick. Probably couldn't do much better even with ASM, since each line there is more or less one ARM instruction anyway. It'll be a lot slower if compiled as THUMB though.

But yeah, I agree with sgeos, if you're not using the blending register for anything else, by all means use it for fading. The only reason to ever use a palette fade is if you want to only fade certain colors, or if the blending register is already tied up.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#26383 - Krakken - Tue Sep 14, 2004 10:06 am

I found that palette fading produces much smoother results than the BLD registers, that's why I dont use them for that.

#26385 - FluBBa - Tue Sep 14, 2004 12:28 pm

Actually I've never coded a palette fading routine for the GBA before as I've only used the BLD regs myself, but it could be better to use a homemade routine if you want better resolution (32 steps for the brigths instead of 16) or if you use the BLD reg for other things. Happy that it worked (you never know until it's tested ;).
_________________
I probably suck, my not is a programmer.

#26387 - tepples - Tue Sep 14, 2004 1:26 pm

sgeos wrote:
I think you people are completely insane... messing with individual palette entries to get a fade effect. Just use the blend registers.

And have your transparency effects suddenly turn off, right?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#26388 - dagamer34 - Tue Sep 14, 2004 2:39 pm

tepples wrote:
sgeos wrote:
I think you people are completely insane... messing with individual palette entries to get a fade effect. Just use the blend registers.

And have your transparency effects suddenly turn off, right?


For a programmer, it is better to learn the hard way, and then the easy way, than to learn the easy way, and never learn the hard way at all. It makes programming all the more worthwhile. :)
_________________
Little kids and Playstation 2's don't mix. :(

#26394 - sfz989 - Tue Sep 14, 2004 4:32 pm

^True true^

Thanks for breaking that code down sajiimori. I think I finally got it. Just to make sure let me try something....if that code makes it fade from black then all I would have to do to make it fade to black is this:
Code:
void fade_out()
{
    u32 luma;
    for( luma = 257 ; luma > 1 ; luma--)
    {
        fade(luma);
        WaitForVsync();
     }
}


And if I wanted it to fade faster I could do this:
Code:
void fade(int luma)
{
   u32 loop1,r,g,b;

   for( loop1 = 0; loop1 < colors; loop1 ++ )
   {
      r = ((palette[loop1] & 0x1f) * luma)>>7;             // get red
      g = (((palette[loop1] >> 5) & 0x1f)*luma)>>7;    // get green
      b = (((palette[loop1] >> 10) & 0x1f)*luma)>>7;  // get blue
      BG_PaletteMem[loop1] = r | (g<<5) | (b<<10);  // set screen colors
   }
}
void fade_in()
{
    u32 luma;
    for( luma = 1 ; luma < 129 ; luma++)
    {
        fade(luma);
        WaitForVsync();
     }
}

[edit] - Tested the code and it works, I finally got it. Thanks everyone!

#26399 - sajiimori - Tue Sep 14, 2004 6:00 pm

To fade at any speed, keep the denominator 256 and let fade_out take an argument that specifies the increment for luma at each iteration.

#26401 - sgeos - Tue Sep 14, 2004 8:06 pm

tepples wrote:
sgeos wrote:
I think you people are completely insane... messing with individual palette entries to get a fade effect. Just use the blend registers.
And have your transparency effects suddenly turn off, right?

I failed to recognize that requirement. If blending and fading need to be done at once, by all means use a palette fading routine. I personally think this would be a fantastic application of HSV routines.

sajiimori wrote:
To fade at any speed, keep the denominator 256 and let fade_out take an argument that specifies the increment for luma at each iteration.


I'd do something like this.
Code:
/*** MACROS
 *
 *  Magic numbers are evil.
 *  Figure out what the numbers represent and use macros instead.
 *  This makes reading and understanding code *much* easier!
 */
#define MAX_LUMINA    256
#define MIN_LUMINA    0

void fade_out(int p_frames)
{
    s32 luma;
    s32 increment;


    /*** Calculate increment from frames
     *
     *  Valid frame range:
     *      0 < frames <= MAX_LUMINA
     */
    increment = MAX_LUMINA / p_frames;

    /*** Main fade loop
     */
    for (luma = MAX_LUMINA; luma > MIN_LUMINA; luma -= increment)
    {
        fade(luma);
        WaitForVsync();
    }

    /*** Ensure we fade completely
     */
    fade(MIN_LUMINA);
    WaitForVsync();
}


-Brendan