Skip to content

Commit c291604

Browse files
committed
Initial commit
0 parents  commit c291604

File tree

8 files changed

+10331
-0
lines changed

8 files changed

+10331
-0
lines changed

README.md

494 Bytes

Turbografx Palette Generator

This program will take the lookup table extracted by Furrtek and apply math to generate an RGB color palette for the PC Engine. These colors differ from the RGB generated by the system, as the LUT is nonlinear.

bfbii.h

Lines changed: 1545 additions & 0 deletions
Large diffs are not rendered by default.

color_lib.h

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
#pragma once
2+
3+
// Mini-library of color conversion functions for older broadcast standards
4+
// Copyright Jamie Dickson, 2020.
5+
6+
#include <stdio.h>
7+
#include <stdint.h>
8+
#include <stdbool.h>
9+
#include <string.h>
10+
#include <math.h>
11+
12+
13+
// Use POSIX M_PI if available
14+
#ifndef M_PI
15+
#define M_PI 3.14159265358979323846
16+
#endif
17+
18+
// RGB Coefficients per specification are fixed precision
19+
// Fun trivia: CO_R + CO_G + CO_B == 1
20+
21+
#define CO_R_601 0.299
22+
#define CO_B_601 0.114
23+
#define CO_G_601 (1.0 - CO_R_601 - CO_B_601)
24+
25+
#define CO_R_709 0.2126
26+
#define CO_B_709 0.0722
27+
#define CO_G_709 (1.0 - CO_R_709 - CO_B_709)
28+
29+
#define CO_R_2020 0.2627
30+
#define CO_B_2020 0.0593
31+
#define CO_G_2020 (1.0 - CO_R_2020 - CO_B_2020)
32+
33+
typedef enum color_rec {
34+
REC_601 = 0,
35+
REC_709 = 1,
36+
REC_2020 = 2,
37+
} COLOR_REC_T;
38+
39+
struct color_coeff {
40+
double r;
41+
double g;
42+
double b;
43+
double u_max;
44+
double v_max;
45+
};
46+
47+
// Max and Min values as defined in BT 601 for Chroma. Luma may vary for NTSC-J.
48+
#define MAX_IRE 130.8333
49+
#define MIN_IRE 23.3025
50+
51+
// U V reduction factors for NTSC video
52+
#define U_REDUCTION 0.492111
53+
#define V_REDUCTION 0.877283
54+
55+
void pop_rec_const(COLOR_REC_T rec, struct color_coeff *co)
56+
{
57+
memset(co, 0, sizeof(struct color_coeff));
58+
59+
switch(rec) {
60+
case REC_709:
61+
co->r = CO_R_709;
62+
co->g = CO_G_709;
63+
co->b = CO_B_709;
64+
case REC_2020:
65+
co->r = CO_R_2020;
66+
co->g = CO_G_2020;
67+
co->b = CO_B_2020;
68+
default: // 601 as default
69+
co->r = CO_R_601;
70+
co->g = CO_G_601;
71+
co->b = CO_B_601;
72+
}
73+
74+
co->u_max = (co->g + co->r);
75+
co->v_max = (co->g + co->b);
76+
}
77+
78+
double ire_to_mv(double ire)
79+
{
80+
return (ire * 7.14);
81+
}
82+
83+
double mv_to_ire(double mv)
84+
{
85+
return (mv / 7.14);
86+
}
87+
88+
double deg_to_rad(double deg)
89+
{
90+
return deg / 180.0 * M_PI;
91+
}
92+
93+
double rad_to_deg(double rad)
94+
{
95+
return rad * 180.0 / M_PI;
96+
}
97+
98+
double uv_to_deg(double u, double v)
99+
{
100+
double ang = atan2(v, u);
101+
102+
return rad_to_deg(ang) + 180.0;
103+
}
104+
105+
// Quantifies a normalized y value to ntsc-j standards
106+
uint8_t y_quant_j(double y)
107+
{
108+
int32_t y1 = round(y * 235.0);
109+
if (y1 < 0) y1 = 0;
110+
if (y1 > 255) y1 = 255;
111+
112+
return y1;
113+
}
114+
115+
// Quantifies a normalized y value to ntsc standards
116+
uint8_t y_quant(double y)
117+
{
118+
int32_t y1 = round((y * 219.0) + 16.0);
119+
if (y1 < 0) y1 = 0;
120+
if (y1 > 255) y1 = 255;
121+
122+
return y1;
123+
}
124+
125+
// Quantifies a normalized pb or pr value to rec 601 standards
126+
uint8_t c_quant_601(double c)
127+
{
128+
int32_t c1 = round((c * 219.0) + 16.0);
129+
if (c1 < 0) c1 = 0;
130+
if (c1 > 255) c1 = 255;
131+
132+
return c1;
133+
}
134+
135+
// Quantifies a normalized pb or pr value to rec 709 standards
136+
uint8_t c_quant_709(double c)
137+
{
138+
int32_t c1 = round((c * 224.0) + 16.0);
139+
if (c1 < 0) c1 = 0;
140+
if (c1 > 255) c1 = 255;
141+
142+
return c1;
143+
}
144+
145+
// Standard split chroma to reduced u and v
146+
static void c_to_uv(double c, double *u, double *v)
147+
{
148+
*v = sin(c);
149+
*u = cos(c);
150+
}
151+
152+
// Standard RGB to luma generation for most color spaces
153+
static double rgb_to_y(struct color_coeff *co, double r, double g, double b)
154+
{
155+
return (co->r * r) + (co->g * g) + (co->b * b);
156+
}
157+
158+
// Convert standard RGB to un-reduced YUV
159+
static void rgb_to_yuv(struct color_coeff *co, double r, double g, double b, double *y, double *u, double *v)
160+
{
161+
162+
*y = rgb_to_y(co, r, g, b);
163+
164+
*u = (co->u_max * b) - (co->r * r) - (co->g * g);
165+
*v = (co->v_max * r) - (co->g * g) - (co->b * b);
166+
}
167+
168+
// Convert un-reduced YUV to RGB
169+
static void yuv_to_rgb(struct color_coeff *co, double y, double u, double v, double *r, double *g, double *b)
170+
{
171+
*r = y + v * (1.0 - co->r);
172+
*g = y - u * (1.0 - co->b) * co->b / co->g - v * (1.0 - co->r) * co->r / co->g;
173+
*b = y + u * (1.0 - co->b);
174+
}
175+
176+
// Convert RGB to YCbCr
177+
void rgb_to_ycbcr(struct color_coeff *co, double r, double g, double b, double *y, double *cb, double *cr)
178+
{
179+
*y = rgb_to_y(co, r, g, b);
180+
*cb = 0.5 * ((b - *y) / (1.0 - co->b));
181+
*cr = 0.5 * ((r - *y) / (1.0 - co->r));
182+
}
183+
184+
// This does not assume a bit size, so scaling and reductions should be done afterwards
185+
void ycbcr_to_rgb(struct color_coeff *co, double y, double cb, double cr, double *r, double *g, double *b)
186+
{
187+
*r = y + (co->v_max * 2.0) * cr;
188+
*g = y - ((co->u_max * 2.0) * co->b) / (co->g * cb) - ((co->v_max * 2.0) * co->r) / (co->g * cr);
189+
*b = y + (co->u_max * 2.0) * cb;
190+
}
191+
192+
// Creates a standard rec 601 U and V reduced value for composite video from (B-Y) and (R-Y)
193+
static void uv_reduce(double *u, double *v)
194+
{
195+
// For compatible with 50's era television recievers, it was anecodtally discovered that the
196+
// color difference signals must not have a possible excursion of greater than 33.3% above white
197+
// or below black. This means a maximum positive excursion of 100IRE + ((1/3) * 92.5 IRE) =
198+
// 130.8025 IRE and a minimum of 7.5IRE - ((1/3) * 92.5) = 23.3025IRE. The goal is to make the
199+
// yellow and cyan bars of 75% color bars equal to 100 IRE.
200+
*u = U_REDUCTION * *u;
201+
*v = V_REDUCTION * *v;
202+
}
203+
204+
static void uv_to_iq(double u, double v, double *i, double *q)
205+
{
206+
// IQ is effectively UV rotated about 33 degrees for ancient bandwidth and fidelity reasons,
207+
// with some attempts made to align the signal to have more fidelity with skin tones. Contrary
208+
// to popular belief, IQ is not used for most modern-ish composite signals, but rather UV is
209+
// instead.
210+
*i = (u * sin(deg_to_rad(33.0)) + (v * cos(deg_to_rad(33.0))));
211+
*q = (u * cos(deg_to_rad(33.0)) + (v * sin(deg_to_rad(33.0))));
212+
}
213+
214+
// Angle + Vector to X Y coordinates
215+
void av_to_xy(double angle, double vector, double *x, double *y)
216+
{
217+
*x = vector * cos(deg_to_rad(angle));
218+
*y = vector * sin(deg_to_rad(angle));
219+
}
220+
221+
// Generate the maximum possible vector for a given angle
222+
double angle_max_vector(struct color_coeff *co, double angle)
223+
{
224+
double u, v;
225+
226+
u = (255.0 * co->u_max) * cos(deg_to_rad(angle));
227+
v = (255.0 * co->v_max) * sin(deg_to_rad(angle));
228+
229+
u *= U_REDUCTION;
230+
v *= V_REDUCTION;
231+
232+
return sqrt((u * u) + (v * v));
233+
}
234+
235+
// Returns the reduced vector after applying NTSC U and V reduction for a given angle.
236+
double av_reduce(double angle, double vector)
237+
{
238+
double v1 = 0;
239+
double x, y;
240+
av_to_xy(angle, vector, &x, &y);
241+
242+
uv_reduce(&x, &y);
243+
244+
v1 = sqrt((x * x) + (y * y));
245+
246+
return v1;
247+
}

0 commit comments

Comments
 (0)