Smashmaster wrote:
Good afternoon everyone. I have a question:

...

How to draw 8-bit sprites with an alpha channel?


This should work:

Code:
void CopySpriteNbitMasked(const unsigned char* data, int x, int y, int width, int height, const color_t* palette, color_t maskColor, unsigned int bitwidth)
{
   color_t* VRAM = (color_t*)VRAM_LOC;
   VRAM += (LCD_WIDTH_PX*y + x);
   int offset = 0;
   unsigned char buf;
   for(int j=y; j<y+height; j++)
   {
      int availbits = 0;
      for(int i=x; i<x+width;  i++)
      {
         if (!availbits)
         {
            buf = data[offset++];
            availbits = 8;
         }
         color_t this = ((color_t)buf>>(8-bitwidth));
         color_t color = palette[this];
         if(color != maskColor)
         {
            *VRAM = color;
         }
         VRAM++;
         buf<<=bitwidth;
         availbits-=bitwidth;
      }
      VRAM += (LCD_WIDTH_PX-width);
   }
}
It's just Kerm's routine with some small modifications. I tested it with 8-bit sprites but the routine should still work with 4 and 2-bit sprites as well. Just make sure that your palette has the desired mask color in it.
Well, the terminology of "Alpha Channel" makes me think he wants different levels of transparency that can be applied to the sprite as it in turn is applied to the destination surface buffer. In which case, you're also going to have to meld what you have with a quick routine I wrote a while ago for dealing with transparency with a given alpha value: http://cemetech.net/forum/viewtopic.php?t=6114&postdays=0&postorder=asc&start=124
Suggestion: Since digging through an 8-page-long thread for a routine is not my idea of fun (nor anyone else's, I presume), why doesn't someone collect the procedures and stick them near the beginning of the thread, thus forming a good reference.
seana11 wrote:
Suggestion: Since digging through an 8-page-long thread for a routine is not my idea of fun (nor anyone else's, I presume), why doesn't someone collect the procedures and stick them near the beginning of the thread, thus forming a good reference.


We're actually in the effort of doing something even better: putting them on an organized wiki Smile Still a decent amount of work needs to be done to get these all on there, though.
Ashbad wrote:
seana11 wrote:
Suggestion: Since digging through an 8-page-long thread for a routine is not my idea of fun (nor anyone else's, I presume), why doesn't someone collect the procedures and stick them near the beginning of the thread, thus forming a good reference.


We're actually in the effort of doing something even better: putting them on an organized wiki :) Still a decent amount of work needs to be done to get these all on there, though.


But the thread is named "Useful Prizm Routines," so it should have routines accessible in a non-esoteric way.
seana11 wrote:
Ashbad wrote:
seana11 wrote:
Suggestion: Since digging through an 8-page-long thread for a routine is not my idea of fun (nor anyone else's, I presume), why doesn't someone collect the procedures and stick them near the beginning of the thread, thus forming a good reference.


We're actually in the effort of doing something even better: putting them on an organized wiki Smile Still a decent amount of work needs to be done to get these all on there, though.


But the thread is named "Useful Prizm Routines," so it should have routines accessible in a non-esoteric way.


Then if you have time, feel free to contribute some links to all of the routines with names, and someone can add it to the first post Wink I'm not one who cares too much about an in-thread directory of routines, so I'm not going to contribute to that effort at all.
Hello.

Thank you very much for your routine, it works great Smile !
Smashmaster wrote:
Hello.

Thank you very much for your routine, it works great Smile !
Great, glad to hear. Smile But yes, Seana, we're organizing them all onto WikiPrizm.
Apparently I never posted my sin/cos/tan implementations.


Code:
extern  float  __sinf(float);
extern  float  __tanf(float);
#define __cosf(x) __sinf((PIOVERTWO)+(x))

#define PIOVERTWO 1.570796327f
#define PI 3.141592654f
#define PISQUARED 9.869604401f
#define EXTRA_PRECISION

float __sinf(float x)
{
   while (x > PI) x-=(2.f*PI);
   while (x < -PI) x+=(2.f*PI);
    const float B = 4/PI;
    const float C = -4/(PISQUARED);

    float y = B * x + C * x * fabsf(x);

    #ifdef EXTRA_PRECISION
    //  const float Q = 0.7775;
        const float P = 0.224008178776;

        y = P * (y * fabsf(y) - y) + y;   // Q * y + P * y * abs(y)
    #endif
   
   return y;
}

float __tanf(float x) {
   float y = __sinf(x);
   return y/((PIOVERTWO)-y);
}
Been studying more of the mathematical sides of color theory lately, so I can understand how HSL/HSV, RGB, L*ab, etc. all relate between each other. I've found that HSL is a lot easier to use than RGB for quickly coming up for numbers that relate to a color you're thinking of, so in case anyone wants to use HSL on-prizm, here's something I just wrote up that takes H/S/L values and returns a standard color_t in R/G/B. Comments make it pretty self-explanitory.


Code:
float fabs(float f) { return f>=0?f:-f; }
int abs(int i) { return i>=0?i:-i; }

/*
h = hue degree, range [0,360)
s = saturation, range [0,1]
l = luminosity, range [0,1]
*/

color_t HSL_to_RGB(int h, float s, float l) {
   float r = 0.0f; float g = 0.0f; float b = 0.0f;
   float c = (1-fabs(2*l-1))*s;
   float tc = c*(1-abs(h%2-1));
   switch(h/60) {
      case 0: r = c; g = tc; break;
      case 1: g = c; r = tc; break;
      case 2: g = c; b = tc; break;
      case 3: b = c; g = tc; break;
      case 4: b = c; r = tc; break;
      case 5: r = c; b = tc; break;
      default: break;
   }
   float m = l-c/2;
   r += m; g += m; b += m;
   return (color_t)(((int)(r*32)<<11)|((int)(g*64)<<5)|((int)(b*32)));
}
I didnt make this, I got it from here.

Any way, its a C function that will make 5-6-5 colors.


Code:

uint16_t drawRGB24toRGB565(uint8_t r, uint8_t g, uint8_t b)
{
  return ((r / 8) << 11) | ((g / 4) << 5) | (b / 8);
}
Scrollable Menu
This displays a scrollable menu and returns the choice picked by the user.

Code:
#define MENU_WIDTH 180
#define TEXT_HEIGHT 18
#define MENU_Y_GAP 17
#define MAX_CHOICES_DISPLAYED (LCD_HEIGHT_PX - 2*MENU_Y_GAP)/(TEXT_HEIGHT+1)
int ShowMenuList(char** menuchoices, int numofitems, char* title, color_t fg, color_t bg, color_t titlecolor) //returns the choice picked by the user
{   
   int curchoice = 0;
   int curchoicex = 0;
   int startchoice = 0;
   int update = 1;
   while (!(keydownlast(KEY_PRGM_RETURN) && !keydownhold(KEY_PRGM_RETURN)))
   {
      keyupdate();
      if (update)
      {
         fillArea((LCD_WIDTH_PX-MENU_WIDTH)/2, MENU_Y_GAP, MENU_WIDTH, LCD_HEIGHT_PX-2*MENU_Y_GAP, bg);
         drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP,fg); //draw a rectangle
         drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX-MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
         drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
         drawLine((LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);         
         drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1,(LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1,fg);
         PrintMiniFix((LCD_WIDTH_PX-MENU_WIDTH)/2+1,MENU_Y_GAP+1,title,PMF_ALLOW_STATUSAREA,titlecolor,bg);      
         for (int x = 0; x < MAX_CHOICES_DISPLAYED && x < numofitems; x++)
         {
            PrintMiniFix((LCD_WIDTH_PX-MENU_WIDTH)/2+12,(x+1)*TEXT_HEIGHT+MENU_Y_GAP+2,menuchoices[startchoice+x],PMF_ALLOW_STATUSAREA,fg,bg);            
            if ((startchoice+x) == curchoice)
            {
               PrintMiniFix((LCD_WIDTH_PX-MENU_WIDTH)/2+2,(x+1)*TEXT_HEIGHT+MENU_Y_GAP+2,">",PMF_ALLOW_STATUSAREA,fg,bg);
            }      
         }                  
         Bdisp_PutDisp_DD();
         update = 0;
      }
      if (keydownlast(KEY_PRGM_DOWN) && !keydownhold(KEY_PRGM_DOWN))
      {
         if (curchoice < numofitems-1)
         {
            curchoice++;
            curchoicex++;
            if (curchoicex >= MAX_CHOICES_DISPLAYED)
            {
               curchoicex = MAX_CHOICES_DISPLAYED;
               startchoice++;
            }
            update = 1;
         }
      }
      if (keydownlast(KEY_PRGM_UP) && !keydownhold(KEY_PRGM_UP))
      {
         if (curchoice > 0)
         {
            curchoice--;
            curchoicex--;
            if (curchoicex <= 0)
            {
               curchoicex = 0;
               startchoice--;
            }
            update = 1;
         }
      }      
   }
   return curchoice;      
}

Example usage

Code:
const char* list[] = {"menuchoice1","menuchoice2","menuchoice3","menuchoice4","menuchoice5"};
ShowMenuList(list,5,"Title",COLOR_BLACK,COLOR_WHITE,COLOR_RED));
That's really useful, souvik1997, but what about the same thing but using PrintXY and a scrollbar, so it mimics the OS style?
gbl08ma wrote:
That's really useful, souvik1997, but what about the same thing but using PrintXY and a scrollbar, so it mimics the OS style?


Yeah, a scroolbar would be good, also, doesn't it use PrintXY already?
No, it uses PrintMiniFix. By the way, if we're going to use the Mini font type, I recommend you use PrintMini instead of the fixed-length PrintMiniFix.

(just don't forget PrintMini requires pointers instead of absolute values for x and y)
Here's a version with a scrollbar. It also fixes a bug with the scrolling.

Code:
#define MENU_WIDTH 180
#define TEXT_HEIGHT 18
#define MENU_Y_GAP 17
#define MAX_CHOICES_DISPLAYED (LCD_HEIGHT_PX - 2*MENU_Y_GAP)/(TEXT_HEIGHT+1)
int ShowMenuList(char** menuchoices, int numofitems, char* title, color_t fg, color_t bg, color_t titlecolor, int showscrollbar) //returns the choice picked by the user
{   
   int curchoice = 0;
   int curchoicex = 0;
   int startchoice = 0;
   int update = 1;
   int scrollbarheight = (LCD_HEIGHT_PX-2*MENU_Y_GAP-TEXT_HEIGHT)/(numofitems-MAX_CHOICES_DISPLAYED+1);
   while (!(keydownlast(KEY_PRGM_RETURN) && !keydownhold(KEY_PRGM_RETURN)))
   {
      keyupdate();
      if (update)
      {
         if (showscrollbar)
         {            
            fillArea((LCD_WIDTH_PX-MENU_WIDTH)/2, MENU_Y_GAP, MENU_WIDTH+1, LCD_HEIGHT_PX-2*MENU_Y_GAP, bg);
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2+1,MENU_Y_GAP,fg); //draw a rectangle
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX-MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2+1,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
            drawLine((LCD_WIDTH_PX+MENU_WIDTH)/2+1,MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2+1,LCD_HEIGHT_PX-MENU_Y_GAP,fg);   
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1,(LCD_WIDTH_PX+MENU_WIDTH)/2+1,MENU_Y_GAP+TEXT_HEIGHT+1,fg);
            if (startchoice < (numofitems-MAX_CHOICES_DISPLAYED))
               drawLine((LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1+(startchoice)*scrollbarheight+1,(LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1+(startchoice+1)*scrollbarheight+1,titlecolor);
            else
               drawLine((LCD_WIDTH_PX+MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP-scrollbarheight,(LCD_WIDTH_PX+MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP-1,titlecolor);

         }         
         else
         {
            fillArea((LCD_WIDTH_PX-MENU_WIDTH)/2, MENU_Y_GAP, MENU_WIDTH, LCD_HEIGHT_PX-2*MENU_Y_GAP, bg);
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP,fg); //draw a rectangle
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX-MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
            drawLine((LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP,(LCD_WIDTH_PX+MENU_WIDTH)/2,LCD_HEIGHT_PX-MENU_Y_GAP,fg);
            drawLine((LCD_WIDTH_PX-MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1,(LCD_WIDTH_PX+MENU_WIDTH)/2,MENU_Y_GAP+TEXT_HEIGHT+1,fg);
         }         
         PrintMiniFix((LCD_WIDTH_PX-MENU_WIDTH)/2+1,MENU_Y_GAP+1,title,PMF_ALLOW_STATUSAREA,titlecolor,bg);               
         for (int x = 0; x < MAX_CHOICES_DISPLAYED && x < numofitems; x++)
         {
            PrintMiniFix((LCD_WIDTH_PX-MENU_WIDTH)/2+14,(x+1)*TEXT_HEIGHT+MENU_Y_GAP+2,menuchoices[startchoice+x],PMF_ALLOW_STATUSAREA,fg,bg);            
            if ((startchoice+x) == curchoice)
            {
               PrintMiniFix((LCD_WIDTH_PX-MENU_WIDTH)/2+2,(x+1)*TEXT_HEIGHT+MENU_Y_GAP+2,">",PMF_ALLOW_STATUSAREA,fg,bg);
            }      
         }                  
         Bdisp_PutDisp_DD();
         update = 0;
      }
      if (keydownlast(KEY_PRGM_DOWN) && !keydownhold(KEY_PRGM_DOWN))
      {
         if (curchoice < numofitems-1)
         {
            curchoice++;
            curchoicex++;
            if (curchoicex >= MAX_CHOICES_DISPLAYED)
            {
               curchoicex = MAX_CHOICES_DISPLAYED;
               if (startchoice < (numofitems-MAX_CHOICES_DISPLAYED)) startchoice++;
            }
            update = 1;
         }
      }
      if (keydownlast(KEY_PRGM_UP) && !keydownhold(KEY_PRGM_UP))
      {
         if (curchoice > 0)
         {
            curchoice--;
            curchoicex--;
            if (curchoicex <= 0)
            {
               curchoicex = 0;
               if (startchoice > 0) startchoice--;
            }
            update = 1;
         }
      }      
   }
   return curchoice;      
}
Kerm asked for my input on an efficient implementation of floor() yesterday, and pasted this (which appears to work, but annoyed me with its complexity).
Code:
float floor(float x) {
   return (((x)<0 && (x)!=(float)(int)(x))?(((float)(int)(x))-1):((float)(int)(x)));
}

I came up with this earlier this morning, instead (which should be both smaller and faster).

Code:
float floor(x) {
    return ((int)x) - (x < 0);
}
Is this faster than using the floor provided in math.h? I assume that floor does all sorts of checking against for NaN and stuff, whereas yours doesn't.
There's no existing implementation. As is the case with many other functions, I added it to the header as a sort of note that it needs to be implemented.
I thought math.h was builtin?
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 9 of 12
» All times are GMT - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement