Homebrew Handy functions for your 3DS homebrews

  • Thread starter xem
  • Start date
  • Views 6,227
  • Replies 21
  • Likes 6

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
Hello,

In this thread, I will gather handy functions that are not present in ctrulib, or functions that are more handy or faster than those present in ctrulib.

"Handy" means that screen coordinates are more logic (i.e. X:0;Y:0 === top-left of the screen; X axis goes right and Y axis goes down).

"Faster" means that the code is optimized, and each function has an "in bounds" version that avoids doing all the tests and the crops.

Let's start with the ones written by Capz, MrJPGames and me.

Feel free to send your own and I'll update the list.

drawPixelInBounds
Code:
/*
* drawPixelInBounds v.0.0.1
* Draws a pixel on a screen
* The pixel coordinates must be in the bounds of the screen.
* @param fb: framebuffer
* @param x: pixel's X coordinate (0-399 on top screens / 0-319 on bottom screen)
* @param y: pixel's Y coordinate (0-239)
* @param r: red (0-255)
* @param g: green (0-255)
* @param b: blue (0-255)
*/
void drawPixelInBounds(u8* fb, s16 x, s16 y, u8 r, u8 g, u8 b){
  u32 pixel_index = 3 * (240 - y - 1 + x * 240);
  fb[pixel_index] = b;      // blue
  fb[pixel_index + 1] = g;  // green
  fb[pixel_index + 2] = r;  // red
}
drawPixel
Code:
/*
* drawPixel v.0.0.1
* Draws a pixel on a screen
* The pixel coordinates can be out of the screen
* @param fb: framebuffer
* @param x: pixel's X coordinate
* @param y: pixel's Y coordinate
* @param r: red (0-255)
* @param g: green (0-255)
* @param b: blue (0-255)
*/
void drawPixel(u8* fb, s16 x, s16 y, u8 r, u8 g, u8 b){
 
  // Set screen width
  u16 screen_width;
  if(fb == gfxGetFramebuffer(GFX_BOTTOM, 0, NULL, NULL)){
    screen_width = 320;
  }
  else{
    screen_width = 400;
  }
 
  // out-of-bounds
  if(x < 0) return;
  if(y < 0) return;
  if(x > screen_width) return;
  if(y > 239) return;
 
  u32 pixel_index = 3 * ((x + 1) * 240 - y - 1);
  fb[pixel_index] = b;      // blue
  fb[pixel_index + 1] = g;  // green
  fb[pixel_index + 2] = r;  // red
}
drawImageInBounds
Code:
/*
* drawImageInBounds v.0.0.2
* Draws an image on a screen
* The image's bin data needs to be rotated by 90 degrees.
* For example with this web tool: http://localhost/3DShomebrew/tools/image-to-bin.html
* You need to include <string.h>
* The image must be entirely visible on screen
* @param fb: framebuffer
* @param x: pixel's X coordinate (0-399 on top screens / 0-319 on bottom screen)
* @param y: pixel's Y coordinate (0-239)
* @param w: image's width (before rotation)
* @param h: image's height (before rotation)
* @param image: the image's bgr data
*/
void drawImageInBounds(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image){
 
  u32 i;
 
  // Loop on all the columns to display
  for(i = 0; i < w; i++){
 
    // Copy the whole column from the bin image to the framebuffer
    memcpy(
      &fb[3 * ((x + i + 1) * 240 - y - h)],
      &image[i * h * 3],
      h * 3
    );
  }
}
drawImage
Code:
/*
* drawImage v.0.0.2
* Draws an image on a screen
* The image's bin data needs to be rotated by 90 degrees.
* For example with this web tool: http://localhost/3DShomebrew/tools/image-to-bin.html
* The image can be partially or totally out of the screen.
* @param fb: framebuffer
* @param x: pixel's X coordinate
* @param y: pixel's Y coordinate
* @param w: image's width (before rotation)
* @param h: image's height (before rotation)
* @param image: the image's bgr data
*/
void drawImage(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image){
 
  // Set screen width
  u16 screen_width;
  if(fb == gfxGetFramebuffer(GFX_BOTTOM, 0, NULL, NULL)){
    screen_width = 320;
  }
  else{
    screen_width = 400;
  }
 
  // out-of-bounds
  if(x + w < 0) return;
  if(y + h < 0) return;
  if(x > screen_width) return;
  if(y > 239) return;
 
  // Overflow
  u16 image_x_left = 0;      // left X offset (first column to display)
  u16 image_x_right = w;      // right X offset (last column)
  u16 image_y_top = 0;        // top Y offset (first line)
  u16 image_y_bottom = h;    // bottom Y offset (last line)
 
  if(x + w > screen_width){
    image_x_right = screen_width - x;
  }
 
  if(y + h > 240){
    image_y_bottom = 240 - y;
  }
 
  if(x < 0){
    image_x_left = -x;
    x = 0;
  }
 
  if(y < 0){
    image_y_top = -y;
    y = 0;
  }
 
  u32 i;
 
  // Loop on the columns to display
  for(i = 0; i < image_x_right - image_x_left; i++){
   
    // copy
    memcpy(
     
      // on the framebuffer, at the column (x + i) and the line (240 - y - image_y_bottom + image_y_top)
      &fb[((x + i + 1) * 240 - y - image_y_bottom + image_y_top) * 3],
     
      // from the image, at the column (image_x_left + i) and the line (image_y_bottom)
      &image[((image_x_left + i + 1) * h - image_y_bottom) * 3],
     
      // the number of pixels to display in that column * 3bpp
      (image_y_bottom - image_y_top) * 3
    );
  }
}
drawImageWithAlphaInBounds
Code:
/*
* drawImageWithAlphaInBounds v.0.0.1
* Draws an image containing transparency on a screen
* Alpha blending is used on pixels having 0 < alpha < 255.
* The image's bin data needs to be rotated by 90 degrees.
* For example with this web tool: http://localhost/3DShomebrew/tools/image-to-bin.html
* NB: check the "preserve alpha transparency" option.
* You need to include <string.h>
* The image must be entirely visible on screen
* @param fb: framebuffer
* @param x: pixel's X coordinate (0-399 on top screens / 0-319 on bottom screen)
* @param y: pixel's Y coordinate (0-239)
* @param w: image's width (before rotation)
* @param h: image's height (before rotation)
* @param image: the image's abgr data
*/
void drawImageWithAlphaInBounds(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image){
 
  u32 i;
  u32 j;
  u8 alpha;
  u32 fb_index;
  u32 image_index;
  float alpha_ratio;
 
 
  // Loop on all the pixels to draw
  for(i = 0; i < w; i++){
    for(j = 0; j < h; j++){
 
      image_index = (i * h + j) * 4;
      alpha = image[image_index];
 
      // If alpha is not zero
      if(alpha != 0){
 
        // If alpha is 255, copy the pixels
        if(alpha == 255){
          fb_index = ((i + x + 1) * 240 - y + j) * 3;
          fb[fb_index] = image[image_index + 1];
          fb[fb_index + 1] = image[image_index + 2];
          fb[fb_index + 2] = image[image_index + 3];
        }
 
        // Else, alpha blending
        else {
          alpha_ratio = alpha / 255.0f;
          fb_index = ((i + x + 1) * 240 - y + j) * 3;
          fb[fb_index] = fb[fb_index] * (1 - alpha_ratio) + image[image_index + 1] * alpha_ratio;
          fb[fb_index + 1] = fb[fb_index + 1] * (1 - alpha_ratio) + image[image_index + 2] * alpha_ratio;
          fb[fb_index + 2] = fb[fb_index + 2] * (1 - alpha_ratio) + image[image_index + 3] * alpha_ratio;
        }
      }
    }
  }
}

Coming soon:

- drawFullScreenImage (to display images that are exactly 400*240 / 320*240)
- drawImageWithAlpha (like drawImage but with transparency)
- drawImageScaled (to draw zoomed images. 2x 3x 4x etc. Maybe scale down x2, x3, x4 ... too.)
- drawImageScaledWithAlpha
- drawSprite (to display only a part of an image)
- drawSpriteWithAlpha
- drawLine
- drawBox
- drawCircle

PS: you can find a quick tutorial explaining how to use your own images in homebrews here:
https://gbatemp.net/threads/handy-functions-for-your-3ds-homebrews.376679/#post-5221079
 

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
How can you use the drawImage methods? I mean how can you load the bin file into the memory to use it as u8*?

Hehe, sorry, I'll add this chapter to my tutorial soon:

0- use ctrulib's project template: https://github.com/smealum/ctrulib/tree/master/template

1- convert your image to a rotated bin with this tool: http://xem.github.io/3DShomebrew/tools/image-to-bin.html

2- Name (example: "img.bin") and place your bin file in the data folder of your project

3- On top of your program (main.c), do
Code:
#include "img_bin.h"
(i.e. the name of your bin file + "_bin.h")

4- In your program, before the main loop, initialize your framebuffers, like this:
Code:
    u8* fbTopLeft = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);
    u8* fbTopRight = gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL);
    u8* fbBottom = gfxGetFramebuffer(GFX_BOTTOM, 0, NULL, NULL);
    memset(fbTopLeft, 0, 240 * 400 * 3);
    memset(fbTopRight, 0, 240 * 400 * 3);
    memset(fbBottom, 0, 240 * 320 * 3);

5- In your program, in the main loop, call drawImage (or drawImageInBounds if you are sure that it won't overflow), like this:
Code:
drawImage(fbTopLeft, 100, 100, 200, 50, img_bin);
(200 and 50 are the dimensions of my test image, you may use your own)

6- build and play (or emulate)
 
  • Like
Reactions: Technicmaster0

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
Hey guys,
If you copied these functions in your project earlier, please update them now: I just finished optimizing and fixing many bugs.
Also, I added a version number in each function's description to help you keep track of the future updates (but we should be good now.)
Thanks!
 

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
Yay! There is now a working drawImageWithAlphaInBounds function! (v.0.0.1)

The out-of-bounds version will arrive in the next days
 
  • Like
Reactions: SLiV3R

Cid2mizard

Well-Known Member
Member
Joined
Aug 16, 2007
Messages
394
Trophies
0
Age
41
Location
Maubeuge
XP
1,766
Country
France
I'll test your work , thank you. Is it possible to ZOOM intergrer a variable to show the bigger picture ? This will avoid me to convert a pixel art image at size 16 * 16 and 64 * 64 ... it takes too much room for anything ...​


Edit : drawImageWithAlphaInBounds() have no gfx3dSide_t side for stereoscopie ?
 

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
I'll test your work , thank you. Is it possible to ZOOM intergrer a variable to show the bigger picture ? This will avoid me to convert a pixel art image at size 16 * 16 and 64 * 64 ... it takes too much room for anything ...​


Edit : drawImageWithAlphaInBounds() have no gfx3dSide_t side for stereoscopie ?

zoom is possible indeed, I'll make a function allowing that. :P

My functions take a framebuffer as parameter, so for stereoscopy you have to call the function twice, for the left and right framebuffers.
 

Cid2mizard

Well-Known Member
Member
Joined
Aug 16, 2007
Messages
394
Trophies
0
Age
41
Location
Maubeuge
XP
1,766
Country
France
This morning i tested "void drawImageInBounds(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image)", i think he've problem...

I use same bin file and same code for 2 tests.

Code:
for(lignes = 0; lignes < 15; lignes++)
    {
        for(colonnes = 0; colonnes < 20; colonnes++)
        {
            gfxDrawSprite(GFX_BOTTOM, GFX_BOTTOM, (u8*)TILES_bin, 16, 16, lignes*16, colonnes*16);
            //drawImageInBounds(screenBottom, colonnes*16, lignes*16, 16, 16, (u8*)TILES_bin);
        }
    }

With "void gfxDrawSprite(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y)" from Github :

sans_t12.png


With "void drawImageInBounds(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image)" from you :

bug11.png
 

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
This morning i tested "void drawImageInBounds(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image)", i think he've problem...

I use same bin file and same code for 2 tests.

Code:
for(lignes = 0; lignes < 15; lignes++)
    {
        for(colonnes = 0; colonnes < 20; colonnes++)
        {
            gfxDrawSprite(GFX_BOTTOM, GFX_BOTTOM, (u8*)TILES_bin, 16, 16, lignes*16, colonnes*16);
            //drawImageInBounds(screenBottom, colonnes*16, lignes*16, 16, 16, (u8*)TILES_bin);
        }
    }

With "void gfxDrawSprite(gfxScreen_t screen, gfx3dSide_t side, u8* spriteData, u16 width, u16 height, s16 x, s16 y)" from Github :

sans_t12.png


With "void drawImageInBounds(u8* fb, s16 x, s16 y, u16 w, u16 h, const u8* image)" from you :

bug11.png

woops, looks like a line is missing. Can you test if you have the same problem with drawSprite please?
 

xem

Well-Known Member
OP
Member
Joined
Nov 22, 2014
Messages
138
Trophies
0
Age
34
Location
Valbonne
XP
283
Country
France
  • Like
Reactions: Cid2mizard
General chit-chat
Help Users
    KenniesNewName @ KenniesNewName: Lol I cheated Amazon's 1 limit per item by placing additional orders 6 HDMI cables for like $3