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