1 /*
2   Copyright (C) 1997-2020 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 
clip3(float x,float y,float z)18 static float clip3(float x, float y, float z)
19 {
20     return ((z < x) ? x : ((z > y) ? y : z));
21 }
22 
RGBtoYUV(Uint8 * rgb,int * yuv,SDL_YUV_CONVERSION_MODE mode,int monochrome,int luminance)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 
ConvertRGBtoPlanar2x2(Uint32 format,Uint8 * src,int pitch,Uint8 * out,int w,int h,SDL_YUV_CONVERSION_MODE mode,int monochrome,int luminance)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 
ConvertRGBtoPacked4(Uint32 format,Uint8 * src,int pitch,Uint8 * out,int w,int h,SDL_YUV_CONVERSION_MODE mode,int monochrome,int luminance)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 
ConvertRGBtoYUV(Uint32 format,Uint8 * src,int pitch,Uint8 * out,int w,int h,SDL_YUV_CONVERSION_MODE mode,int monochrome,int luminance)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 
CalculateYUVPitch(Uint32 format,int width)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