GCC manual section 6.54 wrote:
GCC includes built-in versions of many of the functions in the standard C library. The versions prefixed with __builtin_ will always be treated as having the same meaning as the C library function even if you specify the -fno-builtin option. (see C Dialect Options) Many of these functions are only optimized in certain cases; if they are not optimized in a particular case, a call to the library function will be emitted.
Builtins are provided, but may not be used. I was testing a bit, and floor(1.5) got optimized to a constant load, while inhibiting optimizations plus moving data sources around got it to emit a call to the library function (all other options being the same).
Isn't that a good thing? What does double a = 1.5; floor(a); do?
Yes, it's good. But the point is that we cannot guarantee the builtins will be used, so there needs to be a library implementation for it to fall back to.
Tari wrote:
[...]
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);
}
Wouldn't this return the wrong result for integral negative x values? That was part of the complexity of my code.
Oh, so it would. Back to the drawing board (though I begin to suspect, then, that yours is about as good as it can get without weird bit manipulations).
Here's my full featured menu-system routine. It requires some other functions and data structures. Full documentation available in my example package. Description also available here

To get every required function, read the documentation. They are included in menu.hpp in the package
Data structures:

Code:
struct menu_item{
   int id;
   char *text;
};

struct menu_tab{
   char *label;
   unsigned char item_count;
   struct menu_item items[];
};

struct menu_page{
   char *label;
   int key;
   unsigned char tab_count;
   struct menu_tab *tabs[];
};

struct Tmenu{
   unsigned char page_count;
   struct menu_page *pages[];
};


Config:

Code:
#define MENU_X 6
#define MENU_Y 24
#define MENU_HEIGHT 191
#define MENU_WIDTH 326
#define MENU_HEADER_HEIGHT 27

#define MENU_TAB_INDENT 30
#define MENU_TAB_WIDTH 140
#define MENU_TAB_SPACE 4

#define MENU_COLOR COLOR_LIME

For example you can display more than 2 tabs on the same page, if you set a low value to MENU_TAB_WIDTH or a longer than 2 character long text in the upper left corner by changing MENU_TAB_INDENT. If its needed, you can transform this constants to function arguments with the same name.
If a label is longer than it's space then the rest of the string is not shown.


Function:

Code:
int menu(struct Tmenu *mymenu, unsigned char selected_page, unsigned char selected_tab){
   const color_t tab_palette[4] = {MENU_COLOR, COLOR_BLACK, COLOR_WHITE, MENU_COLOR};
   const unsigned char left_corner[18] = {1, 90, 129, 107, 241, 111, 252, 111, 255, 91, 255, 219, 255, 214, 255, 213, 191, 213};
   const unsigned char right_corner[18] = {169, 84, 63, 165, 79, 254, 83, 255, 229, 255, 249, 95, 255, 85, 255, 213, 111, 245};
   unsigned char go=1, page, tab, i, starti, selected_item, mehet, item_count;
   unsigned short tabplus=0;
   int x, y, lwidth, key, retval=0;
   unsigned char cheight = (MENU_HEIGHT-MENU_HEADER_HEIGHT-18)/24;
   unsigned char cwidth = (MENU_WIDTH-16)/18;
   char buff[cwidth+1];
   buff[1] = ':'; buff[cwidth] = 0;

   SaveVRAM_1();

   do{
      fillArea(MENU_X, MENU_Y, 1, MENU_HEIGHT, COLOR_BLACK);
      fillArea(MENU_X+1, MENU_Y+MENU_HEADER_HEIGHT+1, 1, MENU_HEIGHT-MENU_HEADER_HEIGHT-3, COLOR_WHITE);
      fillArea(MENU_X+1, MENU_Y, MENU_TAB_INDENT+4, 1, COLOR_BLACK);
      fillArea(MENU_X, MENU_Y+MENU_HEIGHT-2, MENU_WIDTH, 2, COLOR_BLACK);
      fillArea(MENU_X+MENU_WIDTH-2, MENU_Y+MENU_HEADER_HEIGHT+1, 2, MENU_HEIGHT-MENU_HEADER_HEIGHT-3, COLOR_BLACK);
      fillArea(MENU_X, MENU_Y+MENU_HEADER_HEIGHT, MENU_WIDTH, 1, COLOR_BLACK);
      fillArea(MENU_X+1, MENU_Y+MENU_HEADER_HEIGHT+1, MENU_WIDTH-3, 1, COLOR_WHITE);

      fillArea(MENU_X+2, MENU_Y+MENU_HEADER_HEIGHT+2, MENU_WIDTH-4, 4, MENU_COLOR);
      fillArea(MENU_X+2, MENU_Y+MENU_HEIGHT-6, MENU_WIDTH-4, 4, MENU_COLOR);
      fillArea(MENU_X+2, MENU_Y+MENU_HEADER_HEIGHT+6, 4, MENU_HEIGHT-MENU_HEADER_HEIGHT-12, MENU_COLOR);
      fillArea(MENU_WIDTH-MENU_X+6, MENU_Y+MENU_HEADER_HEIGHT+6, 4, MENU_HEIGHT-MENU_HEADER_HEIGHT-12, MENU_COLOR);

      fillArea(MENU_X+6, MENU_Y+MENU_HEADER_HEIGHT+6, 2, MENU_HEIGHT-MENU_HEADER_HEIGHT-13, COLOR_BLACK);
      fillArea(MENU_WIDTH-MENU_X+4, MENU_Y+MENU_HEADER_HEIGHT+6, 1, MENU_HEIGHT-MENU_HEADER_HEIGHT-13, COLOR_BLACK);
      fillArea(MENU_WIDTH-MENU_X+5, MENU_Y+MENU_HEADER_HEIGHT+6, 1, MENU_HEIGHT-MENU_HEADER_HEIGHT-12, COLOR_WHITE);
      fillArea(MENU_X+8, MENU_Y+MENU_HEADER_HEIGHT+6, MENU_WIDTH-16, 2, COLOR_BLACK);
      fillArea(MENU_X+8, MENU_Y+MENU_HEIGHT-8, MENU_WIDTH-16, 1, COLOR_BLACK);
      fillArea(MENU_X+6, MENU_Y+MENU_HEIGHT-7, MENU_WIDTH-13, 1, COLOR_WHITE);

      tabplus=0;
      for(tabplus=0; tabplus<mymenu->pages[selected_page]->tab_count*(MENU_TAB_WIDTH+MENU_TAB_SPACE); tabplus+=MENU_TAB_WIDTH+MENU_TAB_SPACE){
         CopySpriteMasked2bitR(left_corner, tabplus+MENU_X+MENU_TAB_INDENT, MENU_Y+1, 9, 8, tab_palette, 5*!tabplus);
         CopySpriteMasked2bitR(right_corner, tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-9, MENU_Y+1, 9, 8, tab_palette, 0);

         fillArea(tabplus+MENU_X+MENU_TAB_INDENT, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_BLACK);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+1, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_WHITE);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+2, MENU_Y+9, 4, MENU_HEADER_HEIGHT-9, MENU_COLOR);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+6, MENU_Y+9, 2, MENU_HEADER_HEIGHT-9, COLOR_BLACK);

         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-8, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_BLACK);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-7, MENU_Y+9, 1, MENU_HEADER_HEIGHT-9, COLOR_WHITE);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-6, MENU_Y+9, 4, MENU_HEADER_HEIGHT-9, MENU_COLOR);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-2, MENU_Y+9, 2, MENU_HEADER_HEIGHT-9, COLOR_BLACK);

         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+6, MENU_TAB_WIDTH-18, 2, COLOR_BLACK);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+2, MENU_TAB_WIDTH-18, 4, MENU_COLOR);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+1, MENU_TAB_WIDTH-18, 1, COLOR_WHITE);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+9, MENU_Y+8, MENU_TAB_WIDTH-18, 1, COLOR_WHITE);
         fillArea(tabplus+MENU_X+MENU_TAB_INDENT+5, MENU_Y, MENU_TAB_WIDTH-9, 1, COLOR_BLACK);
      }

      tabplus=selected_tab*(MENU_TAB_WIDTH+MENU_TAB_SPACE);
      fillArea(tabplus+MENU_X+MENU_TAB_INDENT+8, MENU_Y+MENU_HEADER_HEIGHT, MENU_TAB_WIDTH-16, 8, COLOR_WHITE);
      fillArea(tabplus+MENU_X+MENU_TAB_INDENT+6, MENU_Y+MENU_HEADER_HEIGHT+1, 2, 5, COLOR_BLACK);
      plot(tabplus+MENU_X+MENU_TAB_INDENT+1, MENU_Y+MENU_HEADER_HEIGHT, COLOR_WHITE);
      fillArea(tabplus+MENU_X+MENU_TAB_INDENT+2, MENU_Y+MENU_HEADER_HEIGHT, 4, 2, MENU_COLOR);
      fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-8, MENU_Y+MENU_HEADER_HEIGHT+1, 1, 5, COLOR_BLACK);
      fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-7, MENU_Y+MENU_HEADER_HEIGHT, 1, 6, COLOR_WHITE);
      fillArea(tabplus+MENU_X+MENU_TAB_INDENT+MENU_TAB_WIDTH-6, MENU_Y+MENU_HEADER_HEIGHT, 4, 2, MENU_COLOR);


      y=(MENU_HEADER_HEIGHT-27)/2 + 10;
      for(tab=0; tab<mymenu->pages[selected_page]->tab_count; tab++){
         x=MENU_X+MENU_TAB_INDENT+tab*(MENU_TAB_WIDTH+MENU_TAB_SPACE)+11;
         fillArea(x-3, MENU_Y+9, MENU_TAB_WIDTH-16, MENU_HEADER_HEIGHT-9, COLOR_WHITE);
         PrintMini(&x, &y, mymenu->pages[selected_page]->tabs[tab]->label, 0x02, x+MENU_TAB_WIDTH-24, 0,0,COLOR_BLACK, COLOR_WHITE, 1,0);
      }

      x=MENU_X+1; lwidth=x;
      y = MENU_HEADER_HEIGHT/2-6;
      PrintMini(&x, &y, mymenu->pages[selected_page]->label, 0, x+MENU_TAB_INDENT-1, 0,0,COLOR_BLACK, MENU_COLOR, 1,0);
      lwidth = x-lwidth;
      x = max(MENU_X + (MENU_TAB_INDENT-1-lwidth)/2 + 1, MENU_X+1);
      fillArea(MENU_X+1, MENU_Y+1, MENU_TAB_INDENT-1, MENU_HEADER_HEIGHT-1, MENU_COLOR);
      PrintMini(&x, &y, mymenu->pages[selected_page]->label, 0, x+MENU_TAB_INDENT-1, 0,0,COLOR_BLACK, MENU_COLOR, 1,0);
   
      fillArea(MENU_X+8, MENU_Y+MENU_HEADER_HEIGHT+8, MENU_WIDTH-16, MENU_HEIGHT-MENU_HEADER_HEIGHT-16, COLOR_WHITE);


      starti=0; selected_item=0;
      mehet=1;
      item_count = mymenu->pages[selected_page]->tabs[selected_tab]->item_count;
      do{   
         for(i=starti; i<starti+cheight && i<item_count && i<9; i++){
            buff[0] = '1' + i;
            sys_memset(buff+2, ' ', cwidth-2);
            memcpy(buff+2, mymenu->pages[selected_page]->tabs[selected_tab]->items[i].text,min(strlen(mymenu->pages[selected_page]->tabs[selected_tab]->items[i].text),cwidth-2));
      
            PrintCXY(MENU_X+8, MENU_Y+MENU_HEADER_HEIGHT-15+(i-starti)*24, buff, i==selected_item, -1, COLOR_BLACK, COLOR_WHITE, 1, 0);
            fillArea(MENU_X+8+cwidth*18, MENU_Y+MENU_HEADER_HEIGHT+9+(i-starti)*24, MENU_WIDTH-16-cwidth*18, 24, i==selected_item ? COLOR_BLACK : COLOR_WHITE);
         }

         if(starti>0){
            PrintCXY(MENU_X+MENU_WIDTH-26, MENU_Y+MENU_HEADER_HEIGHT-15, "\xE6\x92", TEXT_MODE_NORMAL, -1, starti==selected_item?COLOR_LIME:COLOR_FUCHSIA, starti==selected_item?COLOR_BLACK:COLOR_WHITE, 1, 0);
         }
         if(item_count>starti+cheight){
            PrintCXY(MENU_X+MENU_WIDTH-26, MENU_Y+MENU_HEADER_HEIGHT-15+(cheight-1)*24, "\xE6\x93", TEXT_MODE_NORMAL, -1, starti+cheight-1==selected_item?COLOR_LIME:COLOR_FUCHSIA, starti+cheight-1==selected_item?COLOR_BLACK:COLOR_WHITE, 1, 0);
         }

         GetKey(&key);
         switch(key){
            case KEY_CTRL_UP:
               selected_item = (selected_item+item_count-1)%item_count;
               break;
            case KEY_CTRL_DOWN:
               selected_item = (selected_item+1)%item_count;
               break;
            case KEY_CHAR_1:
            case KEY_CHAR_2:
            case KEY_CHAR_3:
            case KEY_CHAR_4:
            case KEY_CHAR_5:
            case KEY_CHAR_6:
            case KEY_CHAR_7:
            case KEY_CHAR_8:
            case KEY_CHAR_9:
               selected_item = min(key-KEY_CHAR_1, item_count-1);
               break;
            default:
               mehet=0;
               break;
         }

         if(starti > selected_item){starti=selected_item;}
         if(starti+cheight <= selected_item){starti=selected_item-cheight+1;}

      } while(mehet);

      switch(key){
         case KEY_CTRL_RIGHT:
            if(mymenu->pages[selected_page]->tab_count-1 == selected_tab){
               selected_page = (selected_page+1)%mymenu->page_count;
               selected_tab = 0;
            }else{
               selected_tab++;
            }
            break;
         case KEY_CTRL_LEFT:
            if(selected_tab == 0){
               selected_page = (selected_page+mymenu->page_count-1)%mymenu->page_count;
               selected_tab = mymenu->pages[selected_page]->tab_count-1;
            }else{
               selected_tab--;
            }
            break;
         case KEY_CTRL_EXIT:
            go=0;
            retval=-1;
            break;
         case KEY_CTRL_EXE:
            go=0;
            retval = mymenu->pages[selected_page]->tabs[selected_tab]->items[selected_item].id;
         default:
            for(page=0; page<mymenu->page_count; page++){
               if(mymenu->pages[page]->key == key){
                  selected_page = page;
                  selected_tab = 0;
               }
            }
            break;
      }


      LoadVRAM_1();

   } while(go);


   return retval;
}



Params:
    int menu(struct Tmenu *mymenu, unsigned char selected_page, unsigned char selected_tab)
    mymenu: the structure, which contains the menu system (pages, tabs and items)
    selected_page: the function draws this page
    selected_tab: the function draws this tab of the selected page
    return value: returns the ID of the selected menu item, or -1 if [EXIT] was pressed


Example initialization of a menu object (this is the copy of the Geometry plugin's menu system):

Code:
static struct menu_tab Tab0 = {"Option", 7, {{1, "Text"}, {2, "Expression"}, {3, "Number Format"}, {4, "Clr Constraint"}, {5, "Show All"}, {6, "Hide"}, {7, "Area Calc"}}};
   static struct menu_tab Tab1 = {"Properties", 5, {{11, "to the front"}, {12, "to the back"}, {13, "All Text"}, {14, "Fade I/O"}, {15, "Store Picture"}}};
   static struct menu_tab Tab2 = {"File", 4, {{21, "New"}, {22, "Open"}, {23, "Save as"}, {24, "Key Help"}}};
   static struct menu_tab Tab3 = {"View", 6, {{31, "Zoom Box"}, {32, "Pan"}, {33, "Scroll"}, {34, "Zoom In"}, {35, "Zoom Out"}, {36, "Zoom to Fit"}}};
   static struct menu_tab Tab4 = {"Edit", 6, {{41, "Undo/Redo"}, {42, "Select All"}, {43, "Deselect All"}, {44, "Select Figure"}, {45, "Delete"}, {46, "Clear All"}}};
   static struct menu_tab Tab5 = {"Draw", 8, {{51, "Point"}, {52, "Line Segment"}, {53, "Infinite Line"}, {54, "Ray"}, {55, "Vector"}, {56, "Circle"}, {57, "Arc"}, {58, "SemiCirc(Diam)"}}};
   static struct menu_tab Tab6 = {"Draw Spec", 7, {{61, "Triangle"}, {62, "Isocs Triangle"}, {63, "Rectangle"}, {64, "Square"}, {65, "Polygon"}, {66, "Regular n-gon"}, {67, "Function f(x)"}}};
   static struct menu_tab Tab7 = {"Construct", 8, {{71, "Perp Bisector"}, {72, "Perpendicular"}, {73, "Midpoint"}, {74, "Intersection"}, {75, "Angle Bisector"}, {76, "Parallel"}, {77, "Tangent"}, {78, "Attached Angle"}}};
   static struct menu_tab Tab8 = {"Transform", 6, {{81, "Reflection"}, {82, "Translation"}, {83, "Trans(Sel Vec)"}, {84, "Rotation"}, {85, "Dilation"}, {86, "Symmetry"}}};
   static struct menu_tab Tab9 = {"Animate", 8, {{91, "Add Animation"}, {92, "Replace Anima"}, {93, "Trace"}, {94, "Edit Animation"}, {95, "Go(once)"}, {96, "Go(repeat)"}, {97, "Add Table"}, {98, "Display Table"}}};

   static struct menu_page Page0 = {"OPT", KEY_CTRL_OPTN, 2, {&Tab0, &Tab1}};
   static struct menu_page Page1 = {"F1", KEY_CTRL_F1, 2, {&Tab2, &Tab3}};
   static struct menu_page Page2 = {"F2", KEY_CTRL_F2, 1, {&Tab4}};
   static struct menu_page Page3 = {"F3", KEY_CTRL_F3, 2, {&Tab5, &Tab6}};
   static struct menu_page Page4 = {"F4", KEY_CTRL_F4, 1, {&Tab7}};
   static struct menu_page Page5 = {"F5", KEY_CTRL_F5, 1, {&Tab8}};
   static struct menu_page Page6 = {"F6", KEY_CTRL_F6, 1, {&Tab9}};

   static struct Tmenu geometry = {7, {&Page0,&Page1,&Page2,&Page3,&Page4,&Page5,&Page6}};

   int choice = menu(&geometry, 3, 1);


Controls:
    Press keys UP and DOWN to select items
    Press keys LEFT and RIGHT to jump to the next or the previous tab
    Press EXE to select an item
    Press EXIT to close the menu without selecting an item
    Press your pre-defined keys to jump to page


Notices:
    This function uses SaveVRAM_1(). So your previously saved VRAM data will be overwritten.
    This function uses CopySpriteMasked2bitR() which is different from CopySpriteMasked2bit() and CopySpriteMaskedNbit()
    This function uses sys_memset() and PrintCXY(). They might not be in your libfxcg. You can download the newest version from here: https://github.com/Jonimoose/libfxcg
May I recommend that you make a page in the Useful Routines section of WikiPrizm for this excellent set of documentation? Great job!
Yes, I thought about that. I've sent you a PM
FOr Bust-a-Move, I used a sprite sheet for the launcher, because I didn't wanted to encode a lot of sprites.
So I made a Copy-Sprite derivated function who takes "angle" of the launcher, and choosing the right launcher.

With some modifications, It could become a "choose a sprite from a grid and display it" function...


Code:
void draw_launcher(unsigned char angle) {
   angle /=3;
   angle %=12;

   unsigned char x = (angle%4)*LAUNCHER_DOWN_WIDTH;
   unsigned char y = (angle/4)*LAUNCHER_DOWN_HEIGHT;
   unsigned short* VRAM = (unsigned short*)VRAM_ADRESS;
   unsigned short* ptr = VRAM + (LCD_HEIGHT_PX - LAUNCHER_DOWN_HEIGHT-8)*LCD_WIDTH_PX + (LCD_WIDTH_PX-LAUNCHER_DOWN_WIDTH)/2 -4;

   for(int i =y; i< y+LAUNCHER_DOWN_HEIGHT; i++) {
      for(int j = x; j< x+LAUNCHER_DOWN_WIDTH; j++) {
         unsigned char val = launcher_Down[i*(LAUNCHER_DOWN_SET_WIDTH) + j];
         if(val)   
            *ptr = launcher_Down_Palette[val];
         ptr++;
      }
      ptr += LCD_WIDTH_PX - LAUNCHER_DOWN_WIDTH;
   }
   CopySprite_Palette_Alpha(bar, bar_Palette, 128, (LCD_HEIGHT_PX - LAUNCHER_DOWN_HEIGHT - LAUNCHER_TOP_HEIGHT-4), 128, 4);
   CopySprite_Palette_Alpha(launcher_Top + (LAUNCHER_TOP_WIDTH * 16)*(angle%2), launcher_Top_Palette, (LCD_WIDTH_PX - LAUNCHER_TOP_WIDTH)/2, (LCD_HEIGHT_PX - LAUNCHER_DOWN_HEIGHT - LAUNCHER_TOP_HEIGHT-8), LAUNCHER_TOP_WIDTH, LAUNCHER_TOP_HEIGHT);
   CopySprite_Palette_Alpha(pipe, pipe_Palette, (LCD_WIDTH_PX - 15)/2, (LCD_HEIGHT_PX - LAUNCHER_DOWN_HEIGHT - LAUNCHER_TOP_HEIGHT-8 + 22), 13, 11);
}
I was worried that that would incur some significant CPU overhead and hence slow down the program, but after reading it throug ha few times I must say that I can't find a flaw in that that would make it slower than drawing a sprite from a converted sprite.
Really? I thought that the maths inlcued in the routine would make the routine slower...
Eiyeron wrote:
Really? I thought that the maths inlcued in the routine would make the routine slower...
They will, but an add and a multiply, performed once per pixel, and for a sprite drawn only once per frame, the addition is negligible.
Aaaaand the cleared routine, usable for 'any' sprite sheets... ('any' because if the sprite sheet is cut, the sprite will be messed up)

Code:
void draw_sprite_grid(unsigned char spr_grid[], unsigned char, spr_grid_Palette[], unsigned int x, unsigned int y, unsigned int sprite_width, unsigned int sprite_height, unsigned int index unsigned int num_spr_width, unsigned int num_spr_height) {
   unsigned int x = (index % num_spr_width) * sprite_width;
   unsigned int y = (index % num_spr_height) * sprite_height;
   
   const unsigned int sx = sprite_width*num_spr_width; // sprite grid width
   
   
   unsigned short* VRAM = (unsigned short*)VRAM_ADRESS;
   unsigned short* ptr = VRAM + (LCD_HEIGHT_PX - sprite_height-8)*LCD_WIDTH_PX + (LCD_WIDTH_PX-sprite_width)/2 -4;

   for(int i =y; i< y+sprite_height; i++) {
      for(int j = x; j< x+sprite_height; j++) {
         unsigned char val = spr_grid[i*(sx) + j];
         if(val)   
            *ptr = spr_grid_Palette[val];
         ptr++;
      }
      ptr += LCD_WIDTH_PX - sprite_width;
   }
}
Written based on a request from gbl08ma, this routine replaces every pixel with a given color inside a rectangular area with another color. One particularly useful potential application: change the background (or foreground) color on text or other complex shapes/sprites. Please tell me if you spot any problems.


Code:
void VRAMReplaceColorInRect(int x, int y, int width, int height, color_t color_old, color_t color_new) {
   color_t* VRAM = GetVRAMAddress();
   VRAM += (y*LCD_WIDTH_PX)+x;
   for(int j=0; j<height; VRAM += (LCD_WIDTH_PX-width), j++) {
      for(int i=0; i<width; VRAM++, i++) {
         if (*VRAM == color_old) *VRAM = color_new;
      }
   }
}
I was looking for Triangle routines and I modified one and got this:


Here is the routine:

Code:


void Triangle(int x, int y, int size, int color){
    int i = 0;
     
    while (s > 0)
    {
        for (i = 0; i < s; i++)
        {
            Bdisp_SetPoint_VRAM(i+x, s+y, color);
        }
    s--;
    }
}
Here is a FloodFill routine I ported from this website: http://cboard.cprogramming.com/brief-history-cprogramming-com/9676-floodfill-algorithm.html

Here is mine:

Code:
void FloodFill(int x, int y, int col)
{
   int thisColour = Bdisp_GetPoint_VRAM(x, y);
 
   Bdisp_SetPoint_VRAM(x, y, col);
 
   if (Bdisp_GetPoint_VRAM(x+1, y) == thisColour)
      FloodFill (x + 1, y, col);
 
   if (Bdisp_GetPoint_VRAM(x-1, y) == thisColour)
      FloodFill (x - 1, y, col);
 
   if (Bdisp_GetPoint_VRAM(x, y+1) == thisColour)
      FloodFill (x, y + 1, col);
 
   if (Bdisp_GetPoint_VRAM(x, y-1) == thisColour)
      FloodFill (x, y - 1, col);
}
As someone noted in your source, this is recursive so it may require a lot of stack. I estimate its stack frame (including the parameter area) is 6 words, so 24 bytes per recurse.
Worse-case memory needs there are just under two megabytes: 384 pixels * 216 pixels * 24 bytes/pixel = 1990656 byte-pixels (that's a silly unit). So if you're trying to fill a large region a bad situation is a crash (because you fill all of the available RAM), and the worst-case situation is silent corruption of your .data section (overflowing the stack but not RAM, since the stack and .data grow from opposite directions in the same memory region).

Basically, I wouldn't trust this to be robust, and I believe direct VRAM access would be appreciably faster than Bdisp_GetPoint_VRAM. There's no trivial way to convert this into a tail-recursive variant of the same algorithm, but there are indeed fixed-memory algorithms for flood-filling which would probably not be appreciably slower.
Tari wrote:
As someone noted in your source, this is recursive so it may require a lot of stack. I estimate its stack frame (including the parameter area) is 6 words, so 24 bytes per recurse.
Worse-case memory needs there are just under two megabytes: 384 pixels * 216 pixels * 24 bytes/pixel = 1990656 byte-pixels (that's a silly unit). So if you're trying to fill a large region a bad situation is a crash (because you fill all of the available RAM), and the worst-case situation is silent corruption of your .data section (overflowing the stack but not RAM, since the stack and .data grow from opposite directions in the same memory region).

Basically, I wouldn't trust this to be robust, and I believe direct VRAM access would be appreciably faster than Bdisp_GetPoint_VRAM. There's no trivial way to convert this into a tail-recursive variant of the same algorithm, but there are indeed fixed-memory algorithms for flood-filling which would probably not be appreciably slower.

So, you're saying I should make a new one with the link you sent me?
It would be a good idea to avoid memory corruption with big fills, since you have much less space for the stack than you would on a desktop.
KermMartian wrote:
It would be a good idea to avoid memory corruption with big fills, since you have much less space for the stack than you would on a desktop.


I could put a border around the screen so that it doesn't go on forever and ever.
  
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
» Goto page Previous  1, 2, 3 ... 9, 10, 11, 12  Next
» View previous topic :: View next topic  
Page 10 of 12
» All times are UTC - 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