mirror of
https://github.com/LumaTeam/Luma3DS.git
synced 2026-02-22 01:44:38 +00:00
rosalina: implement full color space correction (to sRGB)
Add option to make the 3DS screens look like sRGB monitors. While this is not perfect (due to screen variance, and it may make dark tones slightly darker), this significantly improves the color fidelity of homebrew and custon HOME Menu themes, removing that "blueish, washed-out look". Do note that first-party party 3DS games are designed around the 3DS's "washed-out" color curve, and may not need that adjustment. (done in collaboration with @profi200)
This commit is contained in:
parent
7a259a5792
commit
db4564a3f5
Binary file not shown.
@ -501,6 +501,11 @@ static int configIniHandler(void* user, const char* section, const char* name, c
|
||||
CHECK_PARSE_OPTION(parseBoolOption(&opt, value));
|
||||
cfg->topScreenFilter.invert = opt;
|
||||
return 1;
|
||||
} else if (strcmp(name, "screen_filters_top_color_curve_adj") == 0) {
|
||||
s64 opt;
|
||||
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, 0, 2));
|
||||
cfg->topScreenFilter.colorCurveCorrection = (u8)opt;
|
||||
return 1;
|
||||
} else if (strcmp(name, "screen_filters_bot_cct") == 0) {
|
||||
s64 opt;
|
||||
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, 1000, 25100));
|
||||
@ -526,6 +531,11 @@ static int configIniHandler(void* user, const char* section, const char* name, c
|
||||
CHECK_PARSE_OPTION(parseBoolOption(&opt, value));
|
||||
cfg->bottomScreenFilter.invert = opt;
|
||||
return 1;
|
||||
} else if (strcmp(name, "screen_filters_bot_color_curve_adj") == 0) {
|
||||
s64 opt;
|
||||
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, 0, 2));
|
||||
cfg->bottomScreenFilter.colorCurveCorrection = (u8)opt;
|
||||
return 1;
|
||||
} else {
|
||||
CHECK_PARSE_OPTION(-1);
|
||||
}
|
||||
@ -666,6 +676,7 @@ static size_t saveLumaIniConfigToStr(char *out)
|
||||
(int)cfg->ntpTzOffetMinutes,
|
||||
|
||||
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,
|
||||
(int)cfg->topScreenFilter.colorCurveCorrection, (int)cfg->bottomScreenFilter.colorCurveCorrection,
|
||||
topScreenFilterGammaStr, bottomScreenFilterGammaStr,
|
||||
topScreenFilterContrastStr, bottomScreenFilterContrastStr,
|
||||
topScreenFilterBrightnessStr, bottomScreenFilterBrightnessStr,
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
#define CONFIG_FILE "config.ini"
|
||||
#define CONFIG_VERSIONMAJOR 3
|
||||
#define CONFIG_VERSIONMINOR 12
|
||||
#define CONFIG_VERSIONMINOR 13
|
||||
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 1)
|
||||
#define BOOTCFG_EMUINDEX BOOTCONFIG(1, 3)
|
||||
|
||||
@ -64,6 +64,7 @@ typedef volatile s64 vs64;
|
||||
typedef struct ScreenFiltersCfgData {
|
||||
u16 cct;
|
||||
bool invert;
|
||||
u8 colorCurveCorrection;
|
||||
s64 gammaEnc;
|
||||
s64 contrastEnc;
|
||||
s64 brightnessEnc;
|
||||
|
||||
@ -131,6 +131,7 @@ extern void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u
|
||||
typedef struct ScreenFiltersCfgData {
|
||||
u16 cct;
|
||||
bool invert;
|
||||
u8 colorCurveCorrection;
|
||||
s64 gammaEnc;
|
||||
s64 contrastEnc;
|
||||
s64 brightnessEnc;
|
||||
|
||||
@ -120,6 +120,12 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
||||
case 0x10C:
|
||||
*out = (s64)cfwInfo.bottomScreenFilter.invert;
|
||||
break;
|
||||
case 0x10D:
|
||||
*out = (s64)cfwInfo.topScreenFilter.colorCurveCorrection;
|
||||
break;
|
||||
case 0x10E:
|
||||
*out = (s64)cfwInfo.bottomScreenFilter.colorCurveCorrection;
|
||||
break;
|
||||
case 0x180:
|
||||
*out = cfwInfo.pluginLoaderFlags;
|
||||
break;
|
||||
|
||||
Binary file not shown.
@ -62,7 +62,7 @@ typedef struct MenuItem {
|
||||
typedef struct Menu {
|
||||
const char *title;
|
||||
|
||||
MenuItem items[16];
|
||||
MenuItem items[24];
|
||||
} Menu;
|
||||
|
||||
extern u32 menuCombo;
|
||||
|
||||
@ -33,6 +33,7 @@ extern Menu screenFiltersMenu;
|
||||
typedef struct ScreenFilter {
|
||||
u16 cct;
|
||||
bool invert;
|
||||
u8 colorCurveCorrection;
|
||||
float gamma;
|
||||
float contrast;
|
||||
float brightness;
|
||||
@ -56,4 +57,7 @@ void ScreenFiltersMenu_SetWarmIncandescent(void); // 2300K
|
||||
void ScreenFiltersMenu_SetCandle(void); // 1900K
|
||||
void ScreenFiltersMenu_SetEmber(void); // 1200K
|
||||
|
||||
void ScreenFiltersMenu_SetSrgbColorCurves(void);
|
||||
void ScreenFiltersMenu_RestoreColorCurves(void);
|
||||
|
||||
void ScreenFiltersMenu_AdvancedConfiguration(void);
|
||||
|
||||
1046
sysmodules/rosalina/include/menus/screen_filters_srgb_tables.h
Normal file
1046
sysmodules/rosalina/include/menus/screen_filters_srgb_tables.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -180,6 +180,7 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg)
|
||||
(int)cfg->ntpTzOffetMinutes,
|
||||
|
||||
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,
|
||||
(int)cfg->topScreenFilter.colorCurveCorrection, (int)cfg->bottomScreenFilter.colorCurveCorrection,
|
||||
topScreenFilterGammaStr, bottomScreenFilterGammaStr,
|
||||
topScreenFilterContrastStr, bottomScreenFilterContrastStr,
|
||||
topScreenFilterBrightnessStr, bottomScreenFilterBrightnessStr,
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "memory.h"
|
||||
#include "menu.h"
|
||||
#include "menus/screen_filters.h"
|
||||
#include "menus/screen_filters_srgb_tables.h"
|
||||
#include "draw.h"
|
||||
#include "redshift/colorramp.h"
|
||||
|
||||
@ -53,6 +54,7 @@ static inline bool ScreenFiltersMenu_IsDefaultSettingsFilter(const ScreenFilter
|
||||
ok = ok && filter->gamma == 1.0f;
|
||||
ok = ok && filter->contrast == 1.0f;
|
||||
ok = ok && filter->brightness == 0.0f;
|
||||
ok = ok && filter->colorCurveCorrection == 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
@ -89,7 +91,7 @@ static u8 ScreenFilterMenu_CalculatePolynomialColorLutComponent(const float coef
|
||||
return (u8)CLAMP(levelInt, 0, 255); // clamp again just to be sure
|
||||
}
|
||||
|
||||
static void ScreenFilterMenu_WritePolynomialColorLut(bool top, const float coeffs[][3], bool invert, float gamma, u32 dim)
|
||||
static void ScreenFilterMenu_WritePolynomialColorLut(bool top, u8 curveCorrection, const float coeffs[][3], bool invert, float gamma, u32 dim)
|
||||
{
|
||||
if (top)
|
||||
GPU_FB_TOP_COL_LUT_INDEX = 0;
|
||||
@ -99,9 +101,15 @@ static void ScreenFilterMenu_WritePolynomialColorLut(bool top, const float coeff
|
||||
for (int i = 0; i <= 255; i++) {
|
||||
Pixel px;
|
||||
int inLevel = invert ? 255 - i : i;
|
||||
px.r = ScreenFilterMenu_CalculatePolynomialColorLutComponent(coeffs, 0, gamma, dim, inLevel);
|
||||
px.g = ScreenFilterMenu_CalculatePolynomialColorLutComponent(coeffs, 1, gamma, dim, inLevel);
|
||||
px.b = ScreenFilterMenu_CalculatePolynomialColorLutComponent(coeffs, 2, gamma, dim, inLevel);
|
||||
const u8 (*tbl)[3] = curveCorrection == 2 ? ctrToSrgbTableTop : ctrToSrgbTableBottom;
|
||||
|
||||
u8 inLevelR = curveCorrection > 0 ? tbl[inLevel][0] : inLevel;
|
||||
u8 inLevelG = curveCorrection > 0 ? tbl[inLevel][1] : inLevel;
|
||||
u8 inLevelB = curveCorrection > 0 ? tbl[inLevel][2] : inLevel;
|
||||
|
||||
px.r = ScreenFilterMenu_CalculatePolynomialColorLutComponent(coeffs, 0, gamma, dim, inLevelR);
|
||||
px.g = ScreenFilterMenu_CalculatePolynomialColorLutComponent(coeffs, 1, gamma, dim, inLevelG);
|
||||
px.b = ScreenFilterMenu_CalculatePolynomialColorLutComponent(coeffs, 2, gamma, dim, inLevelB);
|
||||
px.z = 0;
|
||||
|
||||
if (top)
|
||||
@ -127,7 +135,7 @@ static void ScreenFiltersMenu_ApplyColorSettings(bool top)
|
||||
{ a * wp[0], a * wp[1], a * wp[2] }, // x^1
|
||||
};
|
||||
|
||||
ScreenFilterMenu_WritePolynomialColorLut(top, poly, inv, g, 1);
|
||||
ScreenFilterMenu_WritePolynomialColorLut(top, filter->colorCurveCorrection, poly, inv, g, 1);
|
||||
}
|
||||
|
||||
static void ScreenFiltersMenu_SetCct(u16 cct)
|
||||
@ -138,6 +146,30 @@ static void ScreenFiltersMenu_SetCct(u16 cct)
|
||||
ScreenFiltersMenu_ApplyColorSettings(false);
|
||||
}
|
||||
|
||||
static void ScreenFiltersMenu_UpdateEntries(void)
|
||||
{
|
||||
if (topScreenFilter.colorCurveCorrection == 0 || bottomScreenFilter.colorCurveCorrection == 0)
|
||||
{
|
||||
screenFiltersMenu.items[10].title = "Adjust both screens color curve to sRGB";
|
||||
screenFiltersMenu.items[10].method = &ScreenFiltersMenu_SetSrgbColorCurves;
|
||||
}
|
||||
else
|
||||
{
|
||||
screenFiltersMenu.items[10].title = "Restore both screens color curve";
|
||||
screenFiltersMenu.items[10].method = &ScreenFiltersMenu_RestoreColorCurves;
|
||||
}
|
||||
}
|
||||
static void ScreenFiltersMenu_SetColorCurveCorrection(bool top, u8 colorCurveCorrection)
|
||||
{
|
||||
if (top)
|
||||
topScreenFilter.colorCurveCorrection = colorCurveCorrection;
|
||||
else
|
||||
bottomScreenFilter.colorCurveCorrection = colorCurveCorrection;
|
||||
|
||||
ScreenFiltersMenu_ApplyColorSettings(top);
|
||||
ScreenFiltersMenu_UpdateEntries();
|
||||
}
|
||||
|
||||
Menu screenFiltersMenu = {
|
||||
"Screen filters menu",
|
||||
{
|
||||
@ -151,7 +183,8 @@ Menu screenFiltersMenu = {
|
||||
{ "[2300K] Warm Incandescent", METHOD, .method = &ScreenFiltersMenu_SetWarmIncandescent },
|
||||
{ "[1900K] Candle", METHOD, .method = &ScreenFiltersMenu_SetCandle },
|
||||
{ "[1200K] Ember", METHOD, .method = &ScreenFiltersMenu_SetEmber },
|
||||
{ "Advanced configuration", METHOD, .method = &ScreenFiltersMenu_AdvancedConfiguration },
|
||||
{ "Adjust both screen color curve to sRGB", METHOD, .method = &ScreenFiltersMenu_SetSrgbColorCurves },
|
||||
{ "Advanced configuration...", METHOD, .method = &ScreenFiltersMenu_AdvancedConfiguration },
|
||||
{},
|
||||
}
|
||||
};
|
||||
@ -162,6 +195,12 @@ void ScreenFiltersMenu_Set##name(void)\
|
||||
ScreenFiltersMenu_SetCct(temp);\
|
||||
}
|
||||
|
||||
#define DEF_SRGB_SETTER(top, profile, name)\
|
||||
void ScreenFiltersMenu_##name(void)\
|
||||
{\
|
||||
ScreenFiltersMenu_SetColorCurveCorrection(top, profile);\
|
||||
}
|
||||
|
||||
void ScreenFiltersMenu_RestoreSettings(void)
|
||||
{
|
||||
// Precondition: menu has not been entered
|
||||
@ -214,6 +253,9 @@ void ScreenFiltersMenu_LoadConfig(void)
|
||||
svcGetSystemInfo(&out, 0x10000, 0x107);
|
||||
topScreenFilter.invert = (bool)out;
|
||||
|
||||
svcGetSystemInfo(&out, 0x10000, 0x10D);
|
||||
topScreenFilter.colorCurveCorrection = (u8)out;
|
||||
|
||||
svcGetSystemInfo(&out, 0x10000, 0x108);
|
||||
bottomScreenFilter.cct = (u16)out;
|
||||
if (bottomScreenFilter.cct < 1000 || bottomScreenFilter.cct > 25100)
|
||||
@ -236,6 +278,11 @@ void ScreenFiltersMenu_LoadConfig(void)
|
||||
|
||||
svcGetSystemInfo(&out, 0x10000, 0x10C);
|
||||
bottomScreenFilter.invert = (bool)out;
|
||||
|
||||
svcGetSystemInfo(&out, 0x10000, 0x10E);
|
||||
bottomScreenFilter.colorCurveCorrection = (u8)out;
|
||||
|
||||
ScreenFiltersMenu_UpdateEntries();
|
||||
}
|
||||
|
||||
DEF_CCT_SETTER(6500, Default)
|
||||
@ -250,6 +297,18 @@ DEF_CCT_SETTER(2300, WarmIncandescent)
|
||||
DEF_CCT_SETTER(1900, Candle)
|
||||
DEF_CCT_SETTER(1200, Ember)
|
||||
|
||||
void ScreenFiltersMenu_SetSrgbColorCurves(void)
|
||||
{
|
||||
ScreenFiltersMenu_SetColorCurveCorrection(true, 1);
|
||||
ScreenFiltersMenu_SetColorCurveCorrection(false, 2);
|
||||
}
|
||||
|
||||
void ScreenFiltersMenu_RestoreColorCurves(void)
|
||||
{
|
||||
ScreenFiltersMenu_SetColorCurveCorrection(true, 0);
|
||||
ScreenFiltersMenu_SetColorCurveCorrection(false, 0);
|
||||
}
|
||||
|
||||
static void ScreenFiltersMenu_ClampFilter(ScreenFilter *filter)
|
||||
{
|
||||
filter->cct = CLAMP(filter->cct, 1000, 25100);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user