Skip to content

Instantly share code, notes, and snippets.

@profi200
Last active March 22, 2024 21:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save profi200/bfa7be60b3eecb8c43f59000f626c743 to your computer and use it in GitHub Desktop.
Save profi200/bfa7be60b3eecb8c43f59000f626c743 to your computer and use it in GitHub Desktop.
// License: Do what you want. I don't care.
#include <algorithm>
#include <math.h>
#include <cstdio>
#include <cinttypes>
#include "lodepng.h"
// 0 = libretro GBA color, 1 = mGBA GBA color, 2 = libretro DS phat, 3 = libretro DS phat white.
#define PROFILE_IDX (0)
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;
// 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 != 3)
{
puts("Usage: color_convert input_png output_png");
return 1;
}
unsigned char *buf;
u32 width, height;
u32 lpngErr = lodepng_decode32_file(&buf, &width, &height, argv[1]);
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;
}
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.
static constexpr float targetGammaTable[] = {2.5f, 2.7f, 2.f, 2.f};
r = powf(r, targetGammaTable[PROFILE_IDX]);
g = powf(g, targetGammaTable[PROFILE_IDX]);
b = powf(b, targetGammaTable[PROFILE_IDX]);
// Luminance.
static constexpr float luminanceTable[] = {0.93f, 0.99f, 1.f, 0.915f};
r = std::clamp(r * luminanceTable[PROFILE_IDX], 0.f, 1.f);
g = std::clamp(g * luminanceTable[PROFILE_IDX], 0.f, 1.f);
b = std::clamp(b * luminanceTable[PROFILE_IDX], 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.
#if (PROFILE_IDX == 0)
// libretro GBA color (sRGB). Credits: hunterk and Pokefan531.
// 0.800, 0.275, -0.075,
// 0.135, 0.640, 0.225,
// 0.195, 0.155, 0.650
float newR = 0.800f * r + 0.275f * g + -0.075f * b;
float newG = 0.135f * r + 0.640f * g + 0.225f * b;
float newB = 0.195f * r + 0.155f * g + 0.650f * b;
#elif (PROFILE_IDX == 1)
// mGBA GBA color (sRGB). Older version of the libretro shader.
// Credits: hunterk and Pokefan531.
// 0.84, 0.18, 0.00,
// 0.09, 0.67, 0.26,
// 0.15, 0.10, 0.73
float newR = 0.84f * r + 0.18f * g + 0.00f * b;
float newG = 0.09f * r + 0.67f * g + 0.26f * b;
float newB = 0.15f * r + 0.10f * g + 0.73f * b;
#elif (PROFILE_IDX == 2)
// libretro DS phat (sRGB). Credits: hunterk and Pokefan531.
// 0.7050, 0.2350, -0.0750,
// 0.0900, 0.5850, 0.2400,
// 0.1075, 0.1725, 0.7200
float newR = 0.7050f * r + 0.2350f * g + -0.0750f * b;
float newG = 0.0900f * r + 0.5850f * g + 0.2400f * b;
float newB = 0.1075f * r + 0.1725f * g + 0.7200f * b;
#else
// libretro DS phat white (sRGB). Credits: hunterk and Pokefan531.
// 0.8150, 0.2750, -0.0900,
// 0.1000, 0.6400, 0.2600,
// 0.1075, 0.1725, 0.7200
float newR = 0.8150f * r + 0.2750f * g + -0.0900f * b;
float newG = 0.1000f * r + 0.6400f * g + 0.2600f * b;
float newB = 0.1075f * r + 0.1725f * g + 0.7200f * b;
#endif
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.
static constexpr float displayGammaTable[] = {1.f / 2.f, 1.f / 2.5f + (0.5f * 0.125f), 1.f / 2.f, 1.f / 2.f};
newR = powf(newR, displayGammaTable[PROFILE_IDX]);
newG = powf(newG, displayGammaTable[PROFILE_IDX]);
newB = powf(newB, displayGammaTable[PROFILE_IDX]);
// 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[2], 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