Last active
March 22, 2024 21:15
-
-
Save profi200/bfa7be60b3eecb8c43f59000f626c743 to your computer and use it in GitHub Desktop.
This file contains 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" | |
// 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