Last active
December 4, 2024 13:29
-
-
Save profi200/bfa7be60b3eecb8c43f59000f626c743 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // License: Do what you want. I don't care. | |
| #include <algorithm> | |
| #include <math.h> | |
| #include <cstdio> | |
| #include <cinttypes> | |
| #include "lodepng.h" | |
| typedef uint8_t u8; | |
| typedef uint16_t u16; | |
| typedef uint32_t u32; | |
| typedef uint64_t u64; | |
| typedef int8_t s8; | |
| typedef int16_t s16; | |
| typedef int32_t s32; | |
| typedef int64_t s64; | |
| typedef struct | |
| { | |
| float targetGamma; | |
| float lum; | |
| float r, gr, br; | |
| float rg, g, bg; | |
| float rb, gb, b; | |
| float displayGamma; | |
| } ColorProfile; | |
| // libretro shader values. Credits: hunterk and Pokefan531. | |
| // Last updated 2014-12-03. | |
| static const ColorProfile g_colorProfiles[7] = | |
| { | |
| { // libretro GBA color (sRGB). | |
| 2.2f + (0.f * 1.6f), // Darken screen. Default 0. | |
| 0.91f, | |
| 0.905f, 0.195f, -0.1f, | |
| 0.1f, 0.65f, 0.25f, | |
| 0.1575f, 0.1425f, 0.7f, | |
| 1.f / 2.2f | |
| }, | |
| { // libretro GB micro color (sRGB). | |
| 2.2f, | |
| 0.9f, | |
| 0.8025f, 0.31f, -0.1125f, | |
| 0.1f, 0.6875f, 0.2125f, | |
| 0.1225f, 0.1125f, 0.765f, | |
| 1.f / 2.2f | |
| }, | |
| { // libretro GBA SP (AGS-101) color (sRGB). | |
| 2.2f, | |
| 0.935f, | |
| 0.96f, 0.11f, -0.07f, | |
| 0.0325f, 0.89f, 0.0775f, | |
| 0.001f, -0.03f, 1.029f, | |
| 1.f / 2.2f | |
| }, | |
| { // libretro NDS color (sRGB). | |
| 2.2f, | |
| 0.905f, | |
| 0.835f, 0.27f, -0.105f, | |
| 0.1f, 0.6375f, 0.2625f, | |
| 0.105f, 0.175f, 0.72f, | |
| 1.f / 2.2f | |
| }, | |
| { // libretro NDS lite color (sRGB). | |
| 2.2f, | |
| 0.935f, | |
| 0.93f, 0.14f, -0.07f, | |
| 0.025f, 0.9f, 0.075f, | |
| 0.008f, -0.03f, 1.022f, | |
| 1.f / 2.2f | |
| }, | |
| { // libretro Nintendo Switch Online color (sRGB). | |
| 2.2f + 0.8f, // Darken screen. Default 0.8. | |
| 1.f, | |
| 0.865f, 0.1225f, 0.0125f, | |
| 0.0575f, 0.925f, 0.0125f, | |
| 0.0575f, 0.1225f, 0.82f, | |
| 1.f / 2.2f | |
| }, | |
| { // libretro Visual Boy Advance/No$GBA full color. | |
| 1.45f + 1.f, // Darken screen. Default 1. | |
| 1.f, | |
| 0.73f, 0.27f, 0.f, | |
| 0.0825f, 0.6775f, 0.24f, | |
| 0.0825f, 0.24f, 0.6775f, | |
| 1.f / 1.45f | |
| } | |
| }; | |
| static void printHelp(void) | |
| { | |
| puts("Usage: color_convert profile_index input_png output_png\n\n" | |
| "Available profiles (all sRGB):\n" | |
| "Index Description\n" | |
| "0 GBA\n" | |
| "1 GB micro\n" | |
| "2 GBA SP (AGS-101)\n" | |
| "3 DS phat\n" | |
| "4 DS lite\n" | |
| "5 Nintendo Switch Online\n" | |
| "6 Visual Boy Advance/No$GBA full"); | |
| } | |
| // Compile with "g++ -std=c++17 -s -flto -O2 -fstrict-aliasing -ffunction-sections -Wall -Wextra -I./lodepng -Wl,--gc-sections ./lodepng/lodepng.cpp ./color_convert.cpp -lm -o ./color_convert" | |
| int main(int argc, char const *argv[]) | |
| { | |
| if(argc != 4) | |
| { | |
| printHelp(); | |
| return 1; | |
| } | |
| // TODO: Arbitrary strings not representing a number should be treated as error. | |
| const u32 profileIdx = strtoul(argv[1], NULL, 10); | |
| if(profileIdx > 6) | |
| { | |
| printHelp(); | |
| return 1; | |
| } | |
| unsigned char *buf; | |
| u32 width, height; | |
| u32 lpngErr = lodepng_decode32_file(&buf, &width, &height, argv[2]); | |
| if(lpngErr) | |
| { | |
| fprintf(stderr, "lodepng error: %s", lodepng_error_text(lpngErr)); | |
| return 2; | |
| } | |
| if(width == 0 || width > 65535 || height == 0 || height > 65535) | |
| { | |
| fputs("Error: Input image too big.", stderr); | |
| free(buf); | |
| return 3; | |
| } | |
| const ColorProfile *const p = &g_colorProfiles[profileIdx]; | |
| u32 i = 0; | |
| do | |
| { | |
| // Normalize. | |
| float r = (float)buf[4 * i + 0] / 255; | |
| float g = (float)buf[4 * i + 1] / 255; | |
| float b = (float)buf[4 * i + 2] / 255; | |
| // Convert to linear gamma. | |
| const float targetGamma = p->targetGamma; | |
| r = powf(r, targetGamma); | |
| g = powf(g, targetGamma); | |
| b = powf(b, targetGamma); | |
| // Luminance. | |
| const float lum = p->lum; | |
| r = std::clamp(r * lum, 0.f, 1.f); | |
| g = std::clamp(g * lum, 0.f, 1.f); | |
| b = std::clamp(b * lum, 0.f, 1.f); | |
| /* | |
| * Input | |
| * [r] | |
| * [g] | |
| * [b] | |
| * | |
| * Profile Output | |
| * [ r][gr][br] [r] | |
| * [rg][ g][bg] [g] | |
| * [rb][gb][ b] [b] | |
| */ | |
| // Assuming alpha channel unused in original calculation. | |
| float newR = p->r * r + p->gr * g + p->br * b; | |
| float newG = p->rg * r + p->g * g + p->bg * b; | |
| float newB = p->rb * r + p->gb * g + p->b * b; | |
| newR = (newR < 0.f ? 0.f : newR); | |
| newG = (newG < 0.f ? 0.f : newG); | |
| newB = (newB < 0.f ? 0.f : newB); | |
| // Convert to display gamma. | |
| const float displayGamma = p->displayGamma; | |
| newR = powf(newR, displayGamma); | |
| newG = powf(newG, displayGamma); | |
| newB = powf(newB, displayGamma); | |
| // Denormalize, clamp and convert to RGB8. | |
| const u32 a = buf[4 * i + 3]; | |
| buf[3 * i + 0] = std::clamp<s32>(lroundf(newR * a), 0, 255); | |
| buf[3 * i + 1] = std::clamp<s32>(lroundf(newG * a), 0, 255); | |
| buf[3 * i + 2] = std::clamp<s32>(lroundf(newB * a), 0, 255); | |
| } while(++i < width * height); | |
| lpngErr = lodepng_encode24_file(argv[3], buf, width, height); | |
| if(lpngErr) | |
| { | |
| fprintf(stderr, "lodepng error: %s", lodepng_error_text(lpngErr)); | |
| free(buf); | |
| return 4; | |
| } | |
| free(buf); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment