1 #include <string.h>
2 #include "tiny-nv12-scale.h"
3 
4 /* TODO: optimize this stuff later, or replace with something better.  it's
5  * kind of garbage.  although normally it shouldn't be called that often.  plus
6  * it's nearest neighbor so not really a huge deal.  at the very least it
7  * should be sse2 at some point. */
8 
nv12_scale_init(nv12_scale_t * s,enum target_format format,int dst_cx,int dst_cy,int src_cx,int src_cy)9 void nv12_scale_init(nv12_scale_t *s, enum target_format format, int dst_cx,
10 		     int dst_cy, int src_cx, int src_cy)
11 {
12 	s->format = format;
13 
14 	s->src_cx = src_cx;
15 	s->src_cy = src_cy;
16 
17 	s->dst_cx = dst_cx;
18 	s->dst_cy = dst_cy;
19 }
20 
nv12_scale_nearest(nv12_scale_t * s,uint8_t * dst_start,const uint8_t * src)21 static void nv12_scale_nearest(nv12_scale_t *s, uint8_t *dst_start,
22 			       const uint8_t *src)
23 {
24 	register uint8_t *dst = dst_start;
25 	const int src_cx = s->src_cx;
26 	const int src_cy = s->src_cy;
27 	const int dst_cx = s->dst_cx;
28 	const int dst_cy = s->dst_cy;
29 
30 	/* lum */
31 	for (int y = 0; y < dst_cy; y++) {
32 		const int src_line = y * src_cy / dst_cy * s->src_cx;
33 
34 		for (int x = 0; x < dst_cx; x++) {
35 			const int src_x = x * src_cx / dst_cx;
36 
37 			*(dst++) = src[src_line + src_x];
38 		}
39 	}
40 
41 	src += src_cx * src_cy;
42 
43 	/* uv */
44 	const int dst_cx_d2 = dst_cx / 2;
45 	const int dst_cy_d2 = dst_cy / 2;
46 
47 	for (int y = 0; y < dst_cy_d2; y++) {
48 		const int src_line = y * src_cy / dst_cy * src_cx;
49 
50 		for (int x = 0; x < dst_cx_d2; x++) {
51 			const int src_x = x * src_cx / dst_cx * 2;
52 			const int pos = src_line + src_x;
53 
54 			*(dst++) = src[pos];
55 			*(dst++) = src[pos + 1];
56 		}
57 	}
58 }
59 
nv12_scale_nearest_to_i420(nv12_scale_t * s,uint8_t * dst_start,const uint8_t * src)60 static void nv12_scale_nearest_to_i420(nv12_scale_t *s, uint8_t *dst_start,
61 				       const uint8_t *src)
62 {
63 	register uint8_t *dst = dst_start;
64 	const int src_cx = s->src_cx;
65 	const int src_cy = s->src_cy;
66 	const int dst_cx = s->dst_cx;
67 	const int dst_cy = s->dst_cy;
68 	const int size = src_cx * src_cy;
69 
70 	/* lum */
71 	for (int y = 0; y < dst_cy; y++) {
72 		const int src_line = y * src_cy / dst_cy * s->src_cx;
73 
74 		for (int x = 0; x < dst_cx; x++) {
75 			const int src_x = x * src_cx / dst_cx;
76 
77 			*(dst++) = src[src_line + src_x];
78 		}
79 	}
80 
81 	src += size;
82 
83 	/* uv */
84 	const int dst_cx_d2 = dst_cx / 2;
85 	const int dst_cy_d2 = dst_cy / 2;
86 
87 	register uint8_t *dst2 = dst + dst_cx * dst_cy / 4;
88 
89 	for (int y = 0; y < dst_cy_d2; y++) {
90 		const int src_line = y * src_cy / dst_cy * src_cx;
91 
92 		for (int x = 0; x < dst_cx_d2; x++) {
93 			const int src_x = x * src_cx / dst_cx * 2;
94 			const int pos = src_line + src_x;
95 
96 			*(dst++) = src[pos];
97 			*(dst2++) = src[pos + 1];
98 		}
99 	}
100 }
101 
nv12_convert_to_i420(nv12_scale_t * s,uint8_t * dst_start,const uint8_t * src_start)102 static void nv12_convert_to_i420(nv12_scale_t *s, uint8_t *dst_start,
103 				 const uint8_t *src_start)
104 {
105 	const int size = s->src_cx * s->src_cy;
106 	const int size_d4 = size / 4;
107 
108 	memcpy(dst_start, src_start, size);
109 
110 	register uint8_t *dst1 = dst_start + size;
111 	register uint8_t *dst2 = dst1 + size_d4;
112 	register uint8_t *dst_end = dst2 + size_d4;
113 	register const uint8_t *src = src_start + size;
114 
115 	while (dst2 < dst_end) {
116 		*(dst1++) = *(src++);
117 		*(dst2++) = *(src++);
118 	}
119 }
120 
nv12_scale_nearest_to_yuy2(nv12_scale_t * s,uint8_t * dst_start,const uint8_t * src)121 static void nv12_scale_nearest_to_yuy2(nv12_scale_t *s, uint8_t *dst_start,
122 				       const uint8_t *src)
123 {
124 	register uint8_t *dst = dst_start;
125 	const int src_cx = s->src_cx;
126 	const int src_cy = s->src_cy;
127 	const int dst_cx = s->dst_cx;
128 	const int dst_cy = s->dst_cy;
129 	const int src_cx_d2 = src_cx / 2;
130 	const int src_cy_d2 = src_cy / 2;
131 	const int dst_cx_d2 = dst_cx / 2;
132 	const int dst_cy_d2 = dst_cy / 2;
133 	const int size = src_cx * src_cy;
134 
135 	const uint8_t *src_uv = src + size;
136 
137 	register int uv_flip = 0;
138 
139 	for (int y = 0; y < dst_cy; y++) {
140 		const int src_line = y * src_cy / dst_cy * s->src_cx;
141 		const int src_line_d2 =
142 			y / 2 * src_cy_d2 / dst_cy_d2 * s->src_cx;
143 
144 		for (int x = 0; x < dst_cx; x++) {
145 			const int src_x = x * src_cx / dst_cx;
146 			const int src_x_d2 = x / 2 * src_cx_d2 / dst_cx_d2;
147 			const int pos = src_line + src_x;
148 			const int pos_uv = src_line_d2 + src_x_d2 * 2 + uv_flip;
149 
150 			*(dst++) = src[pos];
151 			*(dst++) = src_uv[pos_uv];
152 
153 			uv_flip ^= 1;
154 		}
155 	}
156 }
157 
nv12_convert_to_yuy2(nv12_scale_t * s,uint8_t * dst_start,const uint8_t * src_start)158 static void nv12_convert_to_yuy2(nv12_scale_t *s, uint8_t *dst_start,
159 				 const uint8_t *src_start)
160 {
161 	const int size = s->src_cx * s->src_cy;
162 	const int size_dst_line = s->src_cx * 4;
163 
164 	register const uint8_t *src_y = src_start;
165 	register const uint8_t *src_uv = src_y + size;
166 	register uint8_t *dst = dst_start;
167 	register uint8_t *dst_end = dst + size * 2;
168 
169 	while (dst < dst_end) {
170 		register uint8_t *dst_line_end = dst + size_dst_line;
171 		const uint8_t *src_uv_start = src_uv;
172 		while (dst < dst_line_end) {
173 			*(dst++) = *(src_y++);
174 			*(dst++) = *(src_uv++);
175 			*(dst++) = *(src_y++);
176 			*(dst++) = *(src_uv++);
177 		}
178 
179 		dst_line_end = dst + size_dst_line;
180 		src_uv = src_uv_start;
181 		while (dst < dst_line_end) {
182 			*(dst++) = *(src_y++);
183 			*(dst++) = *(src_uv++);
184 			*(dst++) = *(src_y++);
185 			*(dst++) = *(src_uv++);
186 		}
187 	}
188 }
189 
nv12_do_scale(nv12_scale_t * s,uint8_t * dst,const uint8_t * src)190 void nv12_do_scale(nv12_scale_t *s, uint8_t *dst, const uint8_t *src)
191 {
192 	if (s->src_cx == s->dst_cx && s->src_cy == s->dst_cy) {
193 		if (s->format == TARGET_FORMAT_I420)
194 			nv12_convert_to_i420(s, dst, src);
195 		else if (s->format == TARGET_FORMAT_YUY2)
196 			nv12_convert_to_yuy2(s, dst, src);
197 		else
198 			memcpy(dst, src, s->src_cx * s->src_cy * 3 / 2);
199 	} else {
200 		if (s->format == TARGET_FORMAT_I420)
201 			nv12_scale_nearest_to_i420(s, dst, src);
202 		else if (s->format == TARGET_FORMAT_YUY2)
203 			nv12_scale_nearest_to_yuy2(s, dst, src);
204 		else
205 			nv12_scale_nearest(s, dst, src);
206 	}
207 }
208