diff --git a/arm9/source/common/screenshot.c b/arm9/source/common/screenshot.c index 0661cf1..93b0dc9 100644 --- a/arm9/source/common/screenshot.c +++ b/arm9/source/common/screenshot.c @@ -4,34 +4,24 @@ #include "vff.h" #include "png.h" -static void Screenshot_CvtAndTranspose(u8 *dest, u16 *fb, u32 w, u32 stride) +static void Screenshot_Transpose(u16 *dest, const u16 *fb, u32 w, u32 stride) { for (u32 y = 0; y < SCREEN_HEIGHT; y++) { - for (u32 x = 0; x < w; x++) { - u8 r, g, b; - u16 rgb_s = GetColor(fb, x, y); - - r = ((rgb_s >> 11) & 0x1F) << 3; - g = ((rgb_s >> 5) & 0x3F) << 2; - b = (rgb_s & 0x1F) << 3; - - *(dest++) = r; - *(dest++) = g; - *(dest++) = b; - } - + for (u32 x = 0; x < w; x++) + *(dest++) = GetColor(fb, x, y); dest += stride; } } void CreateScreenshot(void) { + u8 *png; + u16 *buffer; DsTime dstime; size_t png_size; - u8 *png, *buffer; char filename[64]; u32 snapbuf_size, snap_w, snap_h, bot_offset; - snapbuf_size = (SCREEN_WIDTH_TOP * SCREEN_HEIGHT * 3) * 2; + snapbuf_size = (SCREEN_WIDTH_TOP * SCREEN_HEIGHT * BYTES_PER_PIXEL) * 2; snap_w = SCREEN_WIDTH_TOP; snap_h = SCREEN_HEIGHT * 2; @@ -45,12 +35,13 @@ void CreateScreenshot(void) { buffer = malloc(snapbuf_size); if (!buffer) return; - memset(buffer, 0x1F, snapbuf_size); // gray background + for (unsigned i = snapbuf_size/4; i < snapbuf_size/2; i++) + buffer[i] = RGB(0x1F, 0x1F, 0x1F); // gray background - bot_offset = ((400 * SCREEN_HEIGHT) + 40) * 3; + bot_offset = (SCREEN_WIDTH_TOP * SCREEN_HEIGHT) + 40; - Screenshot_CvtAndTranspose(buffer, TOP_SCREEN, SCREEN_WIDTH_TOP, 0); - Screenshot_CvtAndTranspose(buffer + bot_offset, BOT_SCREEN, SCREEN_WIDTH_BOT, 80 * 3); + Screenshot_Transpose(buffer, TOP_SCREEN, SCREEN_WIDTH_TOP, 0); + Screenshot_Transpose(buffer + bot_offset, BOT_SCREEN, SCREEN_WIDTH_BOT, 80); png = PNG_Compress(buffer, snap_w, snap_h, &png_size); diff --git a/arm9/source/common/ui.c b/arm9/source/common/ui.c index 9896ace..e7a25e9 100644 --- a/arm9/source/common/ui.c +++ b/arm9/source/common/ui.c @@ -151,7 +151,7 @@ void ClearScreenF(bool clear_main, bool clear_alt, u32 color) if (clear_alt) ClearScreen(ALT_SCREEN, color); } -u16 GetColor(u16 *screen, int x, int y) +u16 GetColor(const u16 *screen, int x, int y) { return screen[PIXEL_OFFSET(x, y)]; } @@ -171,7 +171,7 @@ void DrawRectangle(u16 *screen, int x, int y, u32 width, u32 height, u32 color) } } -void DrawBitmap(u16 *screen, int x, int y, u32 w, u32 h, const u8* bitmap) +void DrawBitmap(u16 *screen, int x, int y, u32 w, u32 h, const u16* bitmap) { // on negative values: center the bitmap if (x < 0) x = (SCREEN_WIDTH(screen) - w) >> 1; @@ -183,10 +183,8 @@ void DrawBitmap(u16 *screen, int x, int y, u32 w, u32 h, const u8* bitmap) screen += PIXEL_OFFSET(x, y); while(h--) { - for (u32 i = 0; i < w; i++) { - screen[i * SCREEN_HEIGHT] = RGB(bitmap[2], bitmap[1], bitmap[0]); - bitmap += 3; - } + for (u32 i = 0; i < w; i++) + screen[i * SCREEN_HEIGHT] = *(bitmap++); screen--; } } @@ -392,7 +390,7 @@ void ShowString(const char *format, ...) } else ClearScreenF(true, false, COLOR_STD_BG); } -void ShowIconString(u8* icon, int w, int h, const char *format, ...) +void ShowIconString(u16* icon, int w, int h, const char *format, ...) { static const u32 icon_offset = 10; u32 str_width, str_height, tot_height; diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index dacfc86..15f5b83 100644 --- a/arm9/source/common/ui.h +++ b/arm9/source/common/ui.h @@ -48,13 +48,13 @@ bool ShowUnlockSequence(u32 seqlvl, const char *format, ...); u8* GetFontFromPbm(const void* pbm, const u32 pbm_size, u32* w, u32* h); bool SetFontFromPbm(const void* pbm, const u32 pbm_size); -u16 GetColor(u16 *screen, int x, int y); +u16 GetColor(const u16 *screen, int x, int y); void ClearScreen(u16 *screen, u32 color); void ClearScreenF(bool clear_main, bool clear_alt, u32 color); void DrawPixel(u16 *screen, int x, int y, u32 color); void DrawRectangle(u16 *screen, int x, int y, u32 width, u32 height, u32 color); -void DrawBitmap(u16 *screen, int x, int y, u32 w, u32 h, const u8* bitmap); +void DrawBitmap(u16 *screen, int x, int y, u32 w, u32 h, const u16* bitmap); void DrawQrCode(u16 *screen, const u8* qrcode); void DrawCharacter(u16 *screen, int character, int x, int y, u32 color, u32 bgcolor); @@ -74,7 +74,7 @@ void FormatNumber(char* str, u64 number); void FormatBytes(char* str, u64 bytes); void ShowString(const char *format, ...); -void ShowIconString(u8* icon, int w, int h, const char *format, ...); +void ShowIconString(u16* icon, int w, int h, const char *format, ...); bool ShowPrompt(bool ask, const char *format, ...); u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...); u32 ShowFileScrollPrompt(u32 n, const DirEntry** entries, bool hide_ext, const char *format, ...); diff --git a/arm9/source/game/nds.c b/arm9/source/game/nds.c index ec1d966..b2f669a 100644 --- a/arm9/source/game/nds.c +++ b/arm9/source/game/nds.c @@ -52,7 +52,7 @@ u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon) { } // TWL icon: 32x32 pixel, 8x8 tiles -u32 GetTwlIcon(u8* icon, const TwlIconData* twl_icon) { +u32 GetTwlIcon(u16* icon, const TwlIconData* twl_icon) { const u32 h = TWLICON_DIM_ICON; // fixed size const u32 w = TWLICON_DIM_ICON; // fixed size const u16* palette = twl_icon->palette; @@ -60,13 +60,17 @@ u32 GetTwlIcon(u8* icon, const TwlIconData* twl_icon) { for (u32 y = 0; y < h; y += 8) { for (u32 x = 0; x < w; x += 8) { for (u32 i = 0; i < 8*8; i++) { + u16 pix555; + u8 r, g, b; u32 ix = x + (i & 0x7); u32 iy = y + (i >> 3); - u16 pix555 = palette[((i%2) ? (*pix4 >> 4) : *pix4) & 0xF]; - u8* pix888 = icon + ((iy * w) + ix) * 3; - *(pix888++) = ((pix555 >> 10) & 0x1F) << 3; // B - *(pix888++) = ((pix555 >> 5) & 0x1F) << 3; // G - *(pix888++) = ((pix555 >> 0) & 0x1F) << 3; // R + + pix555 = palette[((i%2) ? (*pix4 >> 4) : *pix4) & 0xF]; + r = pix555 & 0x1F; + g = ((pix555 >> 5) & 0x1F) << 1; + g |= (g >> 1) & 1; + b = (pix555 >> 10) & 0x1F; + icon[(iy * w) + ix] = (r << 11) | (g << 5) | b; if (i % 2) pix4++; } } diff --git a/arm9/source/game/nds.h b/arm9/source/game/nds.h index 69d1516..abb133f 100644 --- a/arm9/source/game/nds.h +++ b/arm9/source/game/nds.h @@ -13,7 +13,7 @@ (v == 0x0003) ? 0x1240 : (v == 0x0103) ? 0x23C0 : 0x0000) #define TWLICON_SIZE_DESC 128 #define TWLICON_DIM_ICON 32 -#define TWLICON_SIZE_ICON (TWLICON_DIM_ICON * TWLICON_DIM_ICON * 3) // w * h * bpp (rgb888) +#define TWLICON_SIZE_ICON (TWLICON_DIM_ICON * TWLICON_DIM_ICON * 2) // w * h * bpp (rgb565) #define NDS_LOGO_CRC16 0xCF56 #define TWL_UNITCODE_NTR 0x00 @@ -126,7 +126,7 @@ typedef struct { u32 ValidateTwlHeader(TwlHeader* twl); u32 LoadTwlMetaData(const char* path, TwlHeader* hdr, TwlIconData* icon); u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon); -u32 GetTwlIcon(u8* icon, const TwlIconData* twl_icon); +u32 GetTwlIcon(u16* icon, const TwlIconData* twl_icon); u32 FindNitroRomDir(u32 dirid, u32* fileid, u8** fnt_entry, TwlHeader* hdr, u8* fnt, u8* fat); u32 NextNitroRomEntry(u32* fileid, u8** fnt_entry); diff --git a/arm9/source/game/smdh.c b/arm9/source/game/smdh.c index ea860c5..8ad0acd 100644 --- a/arm9/source/game/smdh.c +++ b/arm9/source/game/smdh.c @@ -8,7 +8,7 @@ 32, 33, 40, 41, 34, 35, 42, 43, 48, 49, 56, 57, 50, 51, 58, 59, \ 36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63 -u32 ConvertSmdhIcon(u8* icon, const u16* smdh_icon, u32 w, u32 h) { +u32 ConvertSmdhIcon(u16* icon, const u16* smdh_icon, u32 w, u32 h) { const u32 lut[8*8] = { SMDH_LUT }; u16* pix565 = (u16*) smdh_icon; for (u32 y = 0; y < h; y += 8) { @@ -16,11 +16,7 @@ u32 ConvertSmdhIcon(u8* icon, const u16* smdh_icon, u32 w, u32 h) { for (u32 i = 0; i < 8*8; i++) { u32 ix = x + (lut[i] & 0x7); u32 iy = y + (lut[i] >> 3); - u8* pix888 = icon + ((iy * w) + ix) * 3; - *(pix888++) = ((*pix565 >> 0) & 0x1F) << 3; // B - *(pix888++) = ((*pix565 >> 5) & 0x3F) << 2; // G - *(pix888++) = ((*pix565 >> 11) & 0x1F) << 3; // R - pix565++; + icon[(iy * w) + ix] = *(pix565++); } } } @@ -52,11 +48,11 @@ u32 GetSmdhPublisher(char* pub, const Smdh* smdh) { } // small icons are 24x24 => 0x6C0 byte in RGB888 -u32 GetSmdhIconSmall(u8* icon, const Smdh* smdh) { +u32 GetSmdhIconSmall(u16* icon, const Smdh* smdh) { return ConvertSmdhIcon(icon, smdh->icon_small, SMDH_DIM_ICON_SMALL, SMDH_DIM_ICON_SMALL); } // big icons are 48x48 => 0x1B00 byte in RGB888 -u32 GetSmdhIconBig(u8* icon, const Smdh* smdh) { +u32 GetSmdhIconBig(u16* icon, const Smdh* smdh) { return ConvertSmdhIcon(icon, smdh->icon_big, SMDH_DIM_ICON_BIG, SMDH_DIM_ICON_BIG); } diff --git a/arm9/source/game/smdh.h b/arm9/source/game/smdh.h index e1ce89e..1588218 100644 --- a/arm9/source/game/smdh.h +++ b/arm9/source/game/smdh.h @@ -9,8 +9,8 @@ #define SMDH_SIZE_PUBLISHER 64 #define SMDH_DIM_ICON_SMALL 24 #define SMDH_DIM_ICON_BIG 48 -#define SMDH_SIZE_ICON_SMALL (SMDH_DIM_ICON_SMALL * SMDH_DIM_ICON_SMALL * 3) // w * h * bpp (rgb888) -#define SMDH_SIZE_ICON_BIG (SMDH_DIM_ICON_BIG * SMDH_DIM_ICON_BIG * 3) // w * h * bpp (rgb888) +#define SMDH_SIZE_ICON_SMALL (SMDH_DIM_ICON_SMALL * SMDH_DIM_ICON_SMALL * 2) // w * h * bpp (rgb565) +#define SMDH_SIZE_ICON_BIG (SMDH_DIM_ICON_BIG * SMDH_DIM_ICON_BIG * 2) // w * h * bpp (rgb565) // see: https://www.3dbrew.org/wiki/SMDH#Application_Titles typedef struct { @@ -42,5 +42,5 @@ typedef struct { u32 GetSmdhDescShort(char* desc, const Smdh* smdh); u32 GetSmdhDescLong(char* desc, const Smdh* smdh); u32 GetSmdhPublisher(char* pub, const Smdh* smdh); -u32 GetSmdhIconSmall(u8* icon, const Smdh* smdh); -u32 GetSmdhIconBig(u8* icon, const Smdh* smdh); +u32 GetSmdhIconSmall(u16* icon, const Smdh* smdh); +u32 GetSmdhIconBig(u16* icon, const Smdh* smdh); diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index 786c1a8..5ca3dbf 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -115,10 +115,10 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) { } u32 SplashInit(const char* modestr) { + u16* bitmap; u64 splash_size; u32 splash_width, splash_height; u8* splash = FindVTarFileInfo(VRAM0_SPLASH_PNG, &splash_size); - u8* bitmap = NULL; const char* namestr = FLAVOR " " VERSION; const char* loadstr = "booting..."; const u32 pos_xb = 10; @@ -131,7 +131,10 @@ u32 SplashInit(const char* modestr) { if (splash) { bitmap = PNG_Decompress(splash, splash_size, &splash_width, &splash_height); if (bitmap) DrawBitmap(TOP_SCREEN, -1, -1, splash_width, splash_height, bitmap); - } else DrawStringF(TOP_SCREEN, 10, 10, COLOR_STD_FONT, COLOR_TRANSPARENT, "(" VRAM0_SPLASH_PNG " not found)"); + } else { + DrawStringF(TOP_SCREEN, 10, 10, COLOR_STD_FONT, COLOR_TRANSPARENT, "(" VRAM0_SPLASH_PNG " not found)"); + bitmap = NULL; + } if (modestr) DrawStringF(TOP_SCREEN, SCREEN_WIDTH_TOP - 10 - GetDrawStringWidth(modestr), SCREEN_HEIGHT - 10 - GetDrawStringHeight(modestr), COLOR_STD_FONT, COLOR_TRANSPARENT, modestr); @@ -144,7 +147,7 @@ u32 SplashInit(const char* modestr) { DrawStringF(BOT_SCREEN, pos_xu, pos_yu, COLOR_STD_FONT, COLOR_STD_BG, loadstr); DrawStringF(BOT_SCREEN, pos_xb, pos_yu, COLOR_STD_FONT, COLOR_STD_BG, "built: " DBUILTL); - if (bitmap) free(bitmap); + free(bitmap); return 0; } @@ -494,7 +497,7 @@ u32 SdFormatMenu(const char* slabel) { u32 FileGraphicsViewer(const char* path) { const u32 max_size = SCREEN_SIZE(ALT_SCREEN); u64 filetype = IdentifyFileType(path); - u8* bitmap = NULL; + u16* bitmap = NULL; u8* input = (u8*)malloc(max_size); u32 w = 0; u32 h = 0; @@ -519,8 +522,8 @@ u32 FileGraphicsViewer(const char* path) { ClearScreenF(true, true, COLOR_STD_BG); } else ret = 1; - if (bitmap) free(bitmap); - if (input) free(input); + free(bitmap); + free(input); return ret; } diff --git a/arm9/source/system/png.c b/arm9/source/system/png.c index c606e3c..da4b4e9 100644 --- a/arm9/source/system/png.c +++ b/arm9/source/system/png.c @@ -5,43 +5,68 @@ #include "png.h" #ifndef MONITOR_HEAP -static inline void _rgb_swap(u8 *img, size_t sz) +// dest and src can overlap +static inline void _rgb24_to_rgb565(u16 *dest, const u8 *src, size_t dim) { - // maybe process in batches of 3 pixels / 12 bytes at a time? - for (size_t i = 0; i < sz; i+=3) { - u8 c = img[i]; - img[i] = img[i + 2]; - img[i + 2] = c; + for (size_t i = 0; i < dim; i++) { + u8 r, g, b; + + r = *(src++) >> 3; + g = *(src++) >> 2; + b = *(src++) >> 3; + *(dest++) = r << 11 | g << 5 | b; } } -u8 *PNG_Decompress(const u8 *png, size_t png_len, u32 *w, u32 *h) +// dest and src CAN NOT overlap +static inline void _rgb565_to_rgb24(u8 *dest, const u16 *src, size_t dim) { - u8 *img; - u32 res; - size_t w_, h_; + for (size_t i = 0; i < dim; i++) { + u16 rgb = *(src++); + + *(dest++) = (rgb >> 11) << 3; + *(dest++) = ((rgb >> 5) & 0x3F) << 2; + *(dest++) = (rgb & 0x1F) << 3; + } +} + +u16 *PNG_Decompress(const u8 *png, size_t png_len, u32 *w, u32 *h) +{ + u16 *img; + unsigned res; + size_t width, height; img = NULL; - res = lodepng_decode24(&img, &w_, &h_, png, png_len); + res = lodepng_decode24((u8**)&img, &width, &height, png, png_len); if (res) { free(img); return NULL; } - _rgb_swap(img, w_ * h_ * 3); - if (w) *w = w_; - if (h) *h = h_; - return img; + _rgb24_to_rgb565(img, (const u8*)img, width * height); + if (w) *w = width; + if (h) *h = height; + + // the allocated buffer will be w*h*3 bytes long, but only w*h*2 bytes will be used + // however, this is not a problem and it'll all be freed with a regular free() call + return (u16*)img; } -u8 *PNG_Compress(const u8 *fb, u32 w, u32 h, size_t *png_sz) +u8 *PNG_Compress(const u16 *fb, u32 w, u32 h, size_t *png_sz) { - u8 *img; - u32 res; + u8 *img, *buf; + unsigned res; size_t png_size; img = NULL; - res = lodepng_encode24(&img, &png_size, fb, w, h); + + buf = malloc(w * h * 3); + if (!buf) return NULL; + + _rgb565_to_rgb24(buf, fb, w * h); + res = lodepng_encode24(&img, &png_size, buf, w, h); + free(buf); + if (res) { free(img); return NULL; diff --git a/arm9/source/system/png.h b/arm9/source/system/png.h index 06a02a5..a10562c 100644 --- a/arm9/source/system/png.h +++ b/arm9/source/system/png.h @@ -6,5 +6,5 @@ #define PNG_MAGIC 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A -u8 *PNG_Decompress(const u8 *png, size_t png_len, u32 *w, u32 *h); -u8 *PNG_Compress(const u8 *fb, u32 w, u32 h, size_t *png_sz); +u16 *PNG_Decompress(const u8 *png, size_t png_len, u32 *w, u32 *h); +u8 *PNG_Compress(const u16 *fb, u32 w, u32 h, size_t *png_sz); diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 8aff4ec..512ebde 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -1971,7 +1971,7 @@ u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { u32 ShowSmdhTitleInfo(Smdh* smdh) { const u8 smdh_magic[] = { SMDH_MAGIC }; const u32 lwrap = 24; - u8 icon[SMDH_SIZE_ICON_BIG]; + u16 icon[SMDH_SIZE_ICON_BIG]; char desc_l[SMDH_SIZE_DESC_LONG+1]; char desc_s[SMDH_SIZE_DESC_SHORT+1]; char pub[SMDH_SIZE_PUBLISHER+1]; @@ -1992,7 +1992,7 @@ u32 ShowSmdhTitleInfo(Smdh* smdh) { u32 ShowTwlIconTitleInfo(TwlIconData* twl_icon) { const u32 lwrap = 24; - u8 icon[TWLICON_SIZE_ICON]; + u16 icon[TWLICON_SIZE_ICON]; char desc[TWLICON_SIZE_DESC+1]; if ((GetTwlIcon(icon, twl_icon) != 0) || (GetTwlTitle(desc, twl_icon) != 0)) diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index e71edc0..bdda90e 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -1840,7 +1840,7 @@ bool ExecuteGM9Script(const char* path_script) { if (preview_mode > 2) { char* preview_str = get_var("PREVIEW_MODE", NULL); u32 bitmap_width, bitmap_height; - u8* bitmap = NULL; + u16* bitmap = NULL; u8* png = (u8*) malloc(SCREEN_SIZE_TOP); if (png) {