1 | /* |
2 | Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> |
3 | |
4 | This software is provided 'as-is', without any express or implied |
5 | warranty. In no event will the authors be held liable for any damages |
6 | arising from the use of this software. |
7 | |
8 | Permission is granted to anyone to use this software for any purpose, |
9 | including commercial applications, and to alter it and redistribute it |
10 | freely. |
11 | */ |
12 | |
13 | #include "SDL.h" |
14 | |
15 | #include "testyuv_cvt.h" |
16 | |
17 | |
18 | static float clip3(float x, float y, float z) |
19 | { |
20 | return ((z < x) ? x : ((z > y) ? y : z)); |
21 | } |
22 | |
23 | static void RGBtoYUV(Uint8 * rgb, int *yuv, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) |
24 | { |
25 | if (mode == SDL_YUV_CONVERSION_JPEG) { |
26 | /* Full range YUV */ |
27 | yuv[0] = (int)(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]); |
28 | yuv[1] = (int)((rgb[2] - yuv[0]) * 0.565 + 128); |
29 | yuv[2] = (int)((rgb[0] - yuv[0]) * 0.713 + 128); |
30 | } else { |
31 | // This formula is from Microsoft's documentation: |
32 | // https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx |
33 | // L = Kr * R + Kb * B + (1 - Kr - Kb) * G |
34 | // Y = floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5); |
35 | // U = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5)); |
36 | // V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5)); |
37 | float S, Z, R, G, B, L, Kr, Kb, Y, U, V; |
38 | |
39 | if (mode == SDL_YUV_CONVERSION_BT709) { |
40 | /* BT.709 */ |
41 | Kr = 0.2126f; |
42 | Kb = 0.0722f; |
43 | } else { |
44 | /* BT.601 */ |
45 | Kr = 0.299f; |
46 | Kb = 0.114f; |
47 | } |
48 | |
49 | S = 255.0f; |
50 | Z = 0.0f; |
51 | R = rgb[0]; |
52 | G = rgb[1]; |
53 | B = rgb[2]; |
54 | L = Kr * R + Kb * B + (1 - Kr - Kb) * G; |
55 | Y = (Uint8)SDL_floorf((219*(L-Z)/S + 16) + 0.5f); |
56 | U = (Uint8)clip3(0, 255, SDL_floorf((112.0f*(B-L) / ((1.0f-Kb)*S) + 128) + 0.5f)); |
57 | V = (Uint8)clip3(0, 255, SDL_floorf((112.0f*(R-L) / ((1.0f-Kr)*S) + 128) + 0.5f)); |
58 | |
59 | yuv[0] = (Uint8)Y; |
60 | yuv[1] = (Uint8)U; |
61 | yuv[2] = (Uint8)V; |
62 | } |
63 | |
64 | if (monochrome) { |
65 | yuv[1] = 128; |
66 | yuv[2] = 128; |
67 | } |
68 | |
69 | if (luminance != 100) { |
70 | yuv[0] = yuv[0] * luminance / 100; |
71 | if (yuv[0] > 255) |
72 | yuv[0] = 255; |
73 | } |
74 | } |
75 | |
76 | static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) |
77 | { |
78 | int x, y; |
79 | int yuv[4][3]; |
80 | Uint8 *Y1, *Y2, *U, *V; |
81 | Uint8 *rgb1, *rgb2; |
82 | int rgb_row_advance = (pitch - w*3) + pitch; |
83 | int UV_advance; |
84 | |
85 | rgb1 = src; |
86 | rgb2 = src + pitch; |
87 | |
88 | Y1 = out; |
89 | Y2 = Y1 + w; |
90 | switch (format) { |
91 | case SDL_PIXELFORMAT_YV12: |
92 | V = (Y1 + h * w); |
93 | U = V + ((h + 1)/2)*((w + 1)/2); |
94 | UV_advance = 1; |
95 | break; |
96 | case SDL_PIXELFORMAT_IYUV: |
97 | U = (Y1 + h * w); |
98 | V = U + ((h + 1)/2)*((w + 1)/2); |
99 | UV_advance = 1; |
100 | break; |
101 | case SDL_PIXELFORMAT_NV12: |
102 | U = (Y1 + h * w); |
103 | V = U + 1; |
104 | UV_advance = 2; |
105 | break; |
106 | case SDL_PIXELFORMAT_NV21: |
107 | V = (Y1 + h * w); |
108 | U = V + 1; |
109 | UV_advance = 2; |
110 | break; |
111 | default: |
112 | SDL_assert(!"Unsupported planar YUV format" ); |
113 | return; |
114 | } |
115 | |
116 | for (y = 0; y < (h - 1); y += 2) { |
117 | for (x = 0; x < (w - 1); x += 2) { |
118 | RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); |
119 | rgb1 += 3; |
120 | *Y1++ = (Uint8)yuv[0][0]; |
121 | |
122 | RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance); |
123 | rgb1 += 3; |
124 | *Y1++ = (Uint8)yuv[1][0]; |
125 | |
126 | RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance); |
127 | rgb2 += 3; |
128 | *Y2++ = (Uint8)yuv[2][0]; |
129 | |
130 | RGBtoYUV(rgb2, yuv[3], mode, monochrome, luminance); |
131 | rgb2 += 3; |
132 | *Y2++ = (Uint8)yuv[3][0]; |
133 | |
134 | *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1])/4.0f + 0.5f); |
135 | U += UV_advance; |
136 | |
137 | *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2])/4.0f + 0.5f); |
138 | V += UV_advance; |
139 | } |
140 | /* Last column */ |
141 | if (x == (w - 1)) { |
142 | RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); |
143 | rgb1 += 3; |
144 | *Y1++ = (Uint8)yuv[0][0]; |
145 | |
146 | RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance); |
147 | rgb2 += 3; |
148 | *Y2++ = (Uint8)yuv[2][0]; |
149 | |
150 | *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[2][1])/2.0f + 0.5f); |
151 | U += UV_advance; |
152 | |
153 | *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[2][2])/2.0f + 0.5f); |
154 | V += UV_advance; |
155 | } |
156 | Y1 += w; |
157 | Y2 += w; |
158 | rgb1 += rgb_row_advance; |
159 | rgb2 += rgb_row_advance; |
160 | } |
161 | /* Last row */ |
162 | if (y == (h - 1)) { |
163 | for (x = 0; x < (w - 1); x += 2) { |
164 | RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); |
165 | rgb1 += 3; |
166 | *Y1++ = (Uint8)yuv[0][0]; |
167 | |
168 | RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance); |
169 | rgb1 += 3; |
170 | *Y1++ = (Uint8)yuv[1][0]; |
171 | |
172 | *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1])/2.0f + 0.5f); |
173 | U += UV_advance; |
174 | |
175 | *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2])/2.0f + 0.5f); |
176 | V += UV_advance; |
177 | } |
178 | /* Last column */ |
179 | if (x == (w - 1)) { |
180 | RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); |
181 | *Y1++ = (Uint8)yuv[0][0]; |
182 | |
183 | *U = (Uint8)yuv[0][1]; |
184 | U += UV_advance; |
185 | |
186 | *V = (Uint8)yuv[0][2]; |
187 | V += UV_advance; |
188 | } |
189 | } |
190 | } |
191 | |
192 | static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) |
193 | { |
194 | int x, y; |
195 | int yuv[2][3]; |
196 | Uint8 *Y1, *Y2, *U, *V; |
197 | Uint8 *rgb; |
198 | int rgb_row_advance = (pitch - w*3); |
199 | |
200 | rgb = src; |
201 | |
202 | switch (format) { |
203 | case SDL_PIXELFORMAT_YUY2: |
204 | Y1 = out; |
205 | U = out+1; |
206 | Y2 = out+2; |
207 | V = out+3; |
208 | break; |
209 | case SDL_PIXELFORMAT_UYVY: |
210 | U = out; |
211 | Y1 = out+1; |
212 | V = out+2; |
213 | Y2 = out+3; |
214 | break; |
215 | case SDL_PIXELFORMAT_YVYU: |
216 | Y1 = out; |
217 | V = out+1; |
218 | Y2 = out+2; |
219 | U = out+3; |
220 | break; |
221 | default: |
222 | SDL_assert(!"Unsupported packed YUV format" ); |
223 | return; |
224 | } |
225 | |
226 | for (y = 0; y < h; ++y) { |
227 | for (x = 0; x < (w - 1); x += 2) { |
228 | RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance); |
229 | rgb += 3; |
230 | *Y1 = (Uint8)yuv[0][0]; |
231 | Y1 += 4; |
232 | |
233 | RGBtoYUV(rgb, yuv[1], mode, monochrome, luminance); |
234 | rgb += 3; |
235 | *Y2 = (Uint8)yuv[1][0]; |
236 | Y2 += 4; |
237 | |
238 | *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1])/2.0f + 0.5f); |
239 | U += 4; |
240 | |
241 | *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2])/2.0f + 0.5f); |
242 | V += 4; |
243 | } |
244 | /* Last column */ |
245 | if (x == (w - 1)) { |
246 | RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance); |
247 | rgb += 3; |
248 | *Y2 = *Y1 = (Uint8)yuv[0][0]; |
249 | Y1 += 4; |
250 | Y2 += 4; |
251 | |
252 | *U = (Uint8)yuv[0][1]; |
253 | U += 4; |
254 | |
255 | *V = (Uint8)yuv[0][2]; |
256 | V += 4; |
257 | } |
258 | rgb += rgb_row_advance; |
259 | } |
260 | } |
261 | |
262 | SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) |
263 | { |
264 | switch (format) |
265 | { |
266 | case SDL_PIXELFORMAT_YV12: |
267 | case SDL_PIXELFORMAT_IYUV: |
268 | case SDL_PIXELFORMAT_NV12: |
269 | case SDL_PIXELFORMAT_NV21: |
270 | ConvertRGBtoPlanar2x2(format, src, pitch, out, w, h, mode, monochrome, luminance); |
271 | return SDL_TRUE; |
272 | case SDL_PIXELFORMAT_YUY2: |
273 | case SDL_PIXELFORMAT_UYVY: |
274 | case SDL_PIXELFORMAT_YVYU: |
275 | ConvertRGBtoPacked4(format, src, pitch, out, w, h, mode, monochrome, luminance); |
276 | return SDL_TRUE; |
277 | default: |
278 | return SDL_FALSE; |
279 | } |
280 | } |
281 | |
282 | int CalculateYUVPitch(Uint32 format, int width) |
283 | { |
284 | switch (format) |
285 | { |
286 | case SDL_PIXELFORMAT_YV12: |
287 | case SDL_PIXELFORMAT_IYUV: |
288 | case SDL_PIXELFORMAT_NV12: |
289 | case SDL_PIXELFORMAT_NV21: |
290 | return width; |
291 | case SDL_PIXELFORMAT_YUY2: |
292 | case SDL_PIXELFORMAT_UYVY: |
293 | case SDL_PIXELFORMAT_YVYU: |
294 | return 4*((width + 1)/2); |
295 | default: |
296 | return 0; |
297 | } |
298 | } |
299 | |
300 | /* vi: set ts=4 sw=4 expandtab: */ |
301 | |