1/*
2   Colour conversion routines (RGB <-> YUV) in plain C
3   (C) 2000 Nemosoft Unv.    nemosoft@smcc.demon.nl
4   (C) 2001 added 420p, LIMIT(x) jens@gecius.de
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19*/
20
21
22#include "ccvt.h"
23
24#define PUSH_RGB24	1
25#define PUSH_BGR24	2
26#define PUSH_RGB32	3
27#define PUSH_BGR32	4
28
29/* this is a shameless copy from ov511.c to limit the resulting rgb-values
30   to [0..255].
31*/
32
33#define LIMIT(x)  ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
34
35/* This is a really simplistic approach. Speedups are welcomed. */
36static void ccvt_420i(int width, int height, unsigned char *src, unsigned char *dst, int push)
37{
38	int line, col, linewidth;
39	int y, u, v, yy, vr, ug, vg, ub;
40	int r, g, b;
41	unsigned char *py, *pu, *pv;
42
43	linewidth = width + (width >> 1);
44	py = src;
45	pu = py + 4;
46	pv = pu + linewidth;
47
48	y = *py;
49	yy = y << 8;
50	u = *pu - 128;
51	ug =   88 * u;
52	ub =  454 * u;
53	v = *pv - 128;
54	vg =  183 * v;
55	vr =  359 * v;
56
57	/* The biggest problem is the interlaced data, and the fact that odd
58	   and even lines have V and U data, resp.
59	 */
60	for (line = 0; line < height; line++) {
61		for (col = 0; col < width; col++) {
62			r = LIMIT(yy +      vr);
63			g = LIMIT(yy - ug - vg);
64			b = LIMIT(yy + ub     );
65
66			switch(push) {
67			case PUSH_RGB24:
68				*dst++ = r;
69				*dst++ = g;
70				*dst++ = b;
71				break;
72
73			case PUSH_BGR24:
74				*dst++ = b;
75				*dst++ = g;
76				*dst++ = r;
77				break;
78
79			case PUSH_RGB32:
80				*dst++ = r;
81				*dst++ = g;
82				*dst++ = b;
83				*dst++ = 0;
84				break;
85
86			case PUSH_BGR32:
87				*dst++ = b;
88				*dst++ = g;
89				*dst++ = r;
90				*dst++ = 0;
91				break;
92			}
93
94			y = *py++;
95			yy = y << 8;
96			if ((col & 3) == 3)
97				py += 2; // skip u/v
98			if (col & 1) {
99				if ((col & 3) == 3) {
100					pu += 4; // skip y
101					pv += 4;
102				}
103				else {
104					pu++;
105					pv++;
106				}
107				u = *pu - 128;
108				ug =   88 * u;
109				ub =  454 * u;
110				v = *pv - 128;
111				vg =  183 * v;
112				vr =  359 * v;
113			}
114		} /* ..for col */
115		if (line & 1) { // odd line: go to next band
116			pu += linewidth;
117			pv += linewidth;
118		}
119		else { // rewind u/v pointers
120			pu -= linewidth;
121			pv -= linewidth;
122		}
123	} /* ..for line */
124}
125
126void ccvt_420i_rgb24(int width, int height, void *src, void *dst)
127{
128	ccvt_420i(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_RGB24);
129}
130
131void ccvt_420i_bgr24(int width, int height, void *src, void *dst)
132{
133	ccvt_420i(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_BGR24);
134}
135
136void ccvt_420i_rgb32(int width, int height, void *src, void *dst)
137{
138	ccvt_420i(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_RGB32);
139}
140
141void ccvt_420i_bgr32(int width, int height, void *src, void *dst)
142{
143	ccvt_420i(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_BGR32);
144}
145
146
147/* This is a really simplistic approach. Speedups are welcomed. */
148/* Derived this from the above 420i for 4:2:0 planar */
149static void ccvt_420p(int width, int height, unsigned char *src, unsigned char *srcu,
150		      unsigned char *srcv, unsigned char *dst, int push)
151{
152	int line, col, linewidth;
153	int y, yy;
154	int u, v;
155	int vr, ug, vg, ub;
156	int r, g, b;
157	unsigned char *py, *pu, *pv;
158
159	linewidth = width - (width >> 1);
160	py = src;
161	pu = srcu;
162	pv = srcv;
163
164	y = *py;
165	yy = y << 8;
166	u = *pu - 128;
167	ug =   88 * u;
168	ub =  454 * u;
169	v = *pv - 128;
170	vg =  183 * v;
171	vr =  359 * v;
172
173	for (line = 0; line < height; line++) {
174		for (col = 0; col < width; col++) {
175			r = LIMIT(yy +      vr);
176			g = LIMIT(yy - ug - vg);
177			b = LIMIT(yy + ub     );
178			switch(push) {
179			case PUSH_RGB24:
180				*dst++ = r;
181				*dst++ = g;
182				*dst++ = b;
183				break;
184
185			case PUSH_BGR24:
186				*dst++ = b;
187				*dst++ = g;
188				*dst++ = r;
189				break;
190
191			case PUSH_RGB32:
192				*dst++ = r;
193				*dst++ = g;
194				*dst++ = b;
195				*dst++ = 0;
196				break;
197
198			case PUSH_BGR32:
199				*dst++ = b;
200				*dst++ = g;
201				*dst++ = r;
202				*dst++ = 0;
203				break;
204			}
205
206			y = *py++;
207			yy = y << 8;
208			if ( (col & 1) == 1) {
209			  pu++; // increase u/v every second y
210			  pv++;
211			}
212			u = *pu - 128;
213			ug =   88 * u;
214			ub =  454 * u;
215			v = *pv - 128;
216			vg =  183 * v;
217			vr =  359 * v;
218		} // ..for col
219		if ( line & 1 ) { // even line: rewind u/v
220		  pu -= linewidth;
221		  pv -= linewidth;
222		}
223	} /* ..for line */
224}
225
226void ccvt_420p_rgb24(int width, int height, void *src, void *srcu, void *srcv, void *dst)
227{
228	ccvt_420p(width, height, (unsigned char *)src, (unsigned char *)srcu,
229		  (unsigned char *)srcv, (unsigned char *)dst, PUSH_RGB24);
230}
231
232void ccvt_420p_bgr24(int width, int height, void *src, void *srcu, void *srcv, void *dst)
233{
234	ccvt_420p(width, height, (unsigned char *)src, (unsigned char *)srcu,
235		  (unsigned char *)srcv, (unsigned char *)dst, PUSH_BGR24);
236}
237
238void ccvt_420p_rgb32(int width, int height, void *src, void *srcu, void *srcv, void *dst)
239{
240	ccvt_420p(width, height, (unsigned char *)src, (unsigned char *)srcu,
241		  (unsigned char *)srcv, (unsigned char *)dst, PUSH_RGB32);
242}
243
244void ccvt_420p_bgr32(int width, int height, void *src, void *srcu, void *srcv, void *dst)
245{
246	ccvt_420p(width, height, (unsigned char *)src, (unsigned char *)srcu,
247		  (unsigned char *)srcv, (unsigned char *)dst, PUSH_BGR32);
248}
249
250
251/* This is a really simplistic approach. Speedups are welcomed. */
252/* Derived this from the above 4:2:0 planar for yuyv            */
253/* Format: YUYV YUYV YUYV YUYV...                               */
254static void ccvt_yuyv(int width, int height, unsigned char *src, unsigned char *dst, int push)
255{
256	int line, col, linewidth;
257	int y, yy;
258	int u, v;
259	int vr, ug, vg, ub;
260	int r, g, b;
261	unsigned char *py, *pu, *pv;
262
263	linewidth = width - (width >> 1);
264	py = src;
265	pu = src + 1;
266	pv = src + 3;
267
268	y = *py;
269	yy = y << 8;
270	u = *pu - 128;
271	ug =   88 * u;
272	ub =  454 * u;
273	v = *pv - 128;
274	vg =  183 * v;
275	vr =  359 * v;
276
277	for (line = 0; line < height; line++) {
278		for (col = 0; col < width; col++) {
279			r = LIMIT(yy +      vr);
280			g = LIMIT(yy - ug - vg);
281			b = LIMIT(yy + ub     );
282			switch(push) {
283			case PUSH_RGB24:
284				*dst++ = r;
285				*dst++ = g;
286				*dst++ = b;
287				break;
288
289			case PUSH_BGR24:
290				*dst++ = b;
291				*dst++ = g;
292				*dst++ = r;
293				break;
294
295			case PUSH_RGB32:
296				*dst++ = r;
297				*dst++ = g;
298				*dst++ = b;
299				*dst++ = 0;
300				break;
301
302			case PUSH_BGR32:
303				*dst++ = b;
304				*dst++ = g;
305				*dst++ = r;
306				*dst++ = 0;
307				break;
308			}
309
310			py += 2;
311			y = *py;
312			yy = y << 8;
313			if ( (col & 1) == 1) {
314			  pu += 4; // skip yvy every second y
315			  pv += 4; // skip yuy every second y
316			}
317			u = *pu - 128;
318			ug =   88 * u;
319			ub =  454 * u;
320			v = *pv - 128;
321			vg =  183 * v;
322			vr =  359 * v;
323		} // ..for col
324	} /* ..for line */
325}
326
327void ccvt_yuyv_rgb24(int width, int height, void *src, void *dst)
328{
329	ccvt_yuyv(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_RGB24);
330}
331
332void ccvt_yuyv_bgr24(int width, int height, void *src, void *dst)
333{
334	ccvt_yuyv(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_BGR24);
335}
336
337void ccvt_yuyv_rgb32(int width, int height, void *src, void *dst)
338{
339	ccvt_yuyv(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_RGB32);
340}
341
342void ccvt_yuyv_bgr32(int width, int height, void *src, void *dst)
343{
344	ccvt_yuyv(width, height, (unsigned char *)src, (unsigned char *)dst, PUSH_BGR32);
345}
346
347
348void ccvt_420i_420p(int width, int height, void *src, void *dsty, void *dstu, void *dstv)
349{
350	short *s, *dy, *du, *dv;
351	int line, col;
352
353	s = (short *)src;
354	dy = (short *)dsty;
355	du = (short *)dstu;
356	dv = (short *)dstv;
357	for (line = 0; line < height; line++) {
358		for (col = 0; col < width; col += 4) {
359			*dy++ = *s++;
360			*dy++ = *s++;
361			if (line & 1)
362				*dv++ = *s++;
363			else
364				*du++ = *s++;
365		} /* ..for col */
366	} /* ..for line */
367}
368
369void ccvt_420i_yuyv(int width, int height, void *src, void *dst)
370{
371	int line, col, linewidth;
372	unsigned char *py, *pu, *pv, *d;
373
374	linewidth = width + (width >> 1);
375	py = (unsigned char *)src;
376	pu = src + 4;
377	pv = pu + linewidth;
378	d = (unsigned char *)dst;
379
380	for (line = 0; line < height; line++) {
381		for (col = 0; col < width; col += 4) {
382			/* four pixels in one go */
383			*d++ = *py++;
384			*d++ = *pu++;
385			*d++ = *py++;
386			*d++ = *pv++;
387
388			*d++ = *py++;
389			*d++ = *pu++;
390			*d++ = *py++;
391			*d++ = *pv++;
392
393			py += 2;
394			pu += 4;
395			pv += 4;
396		} /* ..for col */
397		if (line & 1) { // odd line: go to next band
398			pu += linewidth;
399			pv += linewidth;
400		}
401		else { // rewind u/v pointers
402			pu -= linewidth;
403			pv -= linewidth;
404		}
405	} /* ..for line */
406}
407
408