1 /*
2 * subsample.c: Routines to do chroma subsampling. ("Work In Progress")
3 *
4 *
5 * Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
6 *
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 */
23
24
25
26 #include <config.h>
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <mjpeg_types.h>
32
33 #include "subsample.h"
34
35
36
37
38
39
40
41
42
43 /*************************************************************************
44 * Chroma Subsampling
45 *************************************************************************/
46
47
48 /* vertical/horizontal interstitial siting
49 *
50 * Y Y Y Y
51 * C C
52 * Y Y Y Y
53 *
54 * Y Y Y Y
55 * C C
56 * Y Y Y Y
57 *
58 */
59
ss_444_to_420jpeg(uint8_t * buffer,int width,int height)60 static void ss_444_to_420jpeg(uint8_t *buffer, int width, int height)
61 {
62 uint8_t *in0, *in1, *out;
63 int x, y;
64
65 in0 = buffer;
66 in1 = buffer + width;
67 out = buffer;
68 for (y = 0; y < height; y += 2) {
69 for (x = 0; x < width; x += 2) {
70 *out = (in0[0] + in0[1] + in1[0] + in1[1]) >> 2;
71 in0 += 2;
72 in1 += 2;
73 out++;
74 }
75 in0 += width;
76 in1 += width;
77 }
78 }
79
80
81 /* vertical/horizontal interstitial siting
82 *
83 * Y Y Y Y
84 * C C C inm
85 * Y Y Y Y
86 *
87 * Y Y Y - Y out0
88 * C | C | C in0
89 * Y Y Y - Y out1
90 *
91 *
92 * C C C inp
93 *
94 *
95 * Each iteration through the loop reconstitutes one 2x2 block of
96 * pixels from the "surrounding" 3x3 block of samples...
97 * Boundary conditions are handled by cheap reflection; i.e. the
98 * center sample is simply reused.
99 *
100 */
101
102
103 #define BLANK_CRB in0[1]
104
105
106 #if 1 /* triangle/linear filter (otherwise, use the lame box filter) */
ss_420jpeg_to_444(uint8_t * buffer,int width,int height)107 static void ss_420jpeg_to_444(uint8_t *buffer, int width, int height)
108 {
109 uint8_t *inm, *in0, *inp, *out0, *out1;
110 uint8_t cmm, cm0, cmp, c0m, c00, c0p, cpm, cp0, cpp;
111 int x, y;
112 static uint8_t *saveme = NULL;
113 static int saveme_size = 0;
114
115 if (width > saveme_size) {
116 free(saveme);
117 saveme_size = width;
118 saveme = malloc(saveme_size * sizeof(saveme[0]));
119 assert(saveme != NULL);
120 }
121 memcpy(saveme, buffer, width);
122
123 in0 = buffer + (width * height / 4) - 2;
124 inm = in0 - width/2;
125 inp = in0 + width/2;
126 out1 = buffer + (width * height) - 1;
127 out0 = out1 - width;
128
129 for (y = height; y > 0; y -= 2) {
130 if (y == 2) {
131 in0 = saveme + width/2 - 2;
132 inp = in0 + width/2;
133 }
134 for (x = width; x > 0; x -= 2) {
135 #if 0
136 if ((x == 2) && (y == 2)) {
137 cmm = in0[1];
138 cm0 = in0[1];
139 cmp = in0[2];
140 c0m = in0[1];
141 c0p = in0[2];
142 cpm = inp[1];
143 cp0 = inp[1];
144 cpp = inp[2];
145 } else if ((x == 2) && (y == height)) {
146 cmm = inm[1];
147 cm0 = inm[1];
148 cmp = inm[2];
149 c0m = in0[1];
150 c0p = in0[2];
151 cpm = in0[1];
152 cp0 = in0[1];
153 cpp = in0[2];
154 } else if ((x == width) && (y == height)) {
155 cmm = inm[0];
156 cm0 = inm[1];
157 cmp = inm[1];
158 c0m = in0[0];
159 c0p = in0[1];
160 cpm = in0[0];
161 cp0 = in0[1];
162 cpp = in0[1];
163 } else if ((x == width) && (y == 2)) {
164 cmm = in0[0];
165 cm0 = (y == 2) ? BLANK_CRB : inm[1];
166 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
167 c0m = (x == 2) ? BLANK_CRB : in0[0];
168 c0p = (x == width) ? BLANK_CRB : in0[2];
169 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
170 cp0 = (y == height) ? BLANK_CRB : inp[1];
171 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
172 } else if (x == 2) {
173 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
174 cm0 = (y == 2) ? BLANK_CRB : inm[1];
175 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
176 c0m = (x == 2) ? BLANK_CRB : in0[0];
177 c0p = (x == width) ? BLANK_CRB : in0[2];
178 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
179 cp0 = (y == height) ? BLANK_CRB : inp[1];
180 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
181 } else if (y == 2) {
182 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
183 cm0 = (y == 2) ? BLANK_CRB : inm[1];
184 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
185 c0m = (x == 2) ? BLANK_CRB : in0[0];
186 c0p = (x == width) ? BLANK_CRB : in0[2];
187 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
188 cp0 = (y == height) ? BLANK_CRB : inp[1];
189 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
190 } else if (x == width) {
191 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
192 cm0 = (y == 2) ? BLANK_CRB : inm[1];
193 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
194 c0m = (x == 2) ? BLANK_CRB : in0[0];
195 c0p = (x == width) ? BLANK_CRB : in0[2];
196 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
197 cp0 = (y == height) ? BLANK_CRB : inp[1];
198 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
199 } else if (y == height) {
200 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
201 cm0 = (y == 2) ? BLANK_CRB : inm[1];
202 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
203 c0m = (x == 2) ? BLANK_CRB : in0[0];
204 c0p = (x == width) ? BLANK_CRB : in0[2];
205 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
206 cp0 = (y == height) ? BLANK_CRB : inp[1];
207 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
208 } else {
209 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
210 cm0 = (y == 2) ? BLANK_CRB : inm[1];
211 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
212 c0m = (x == 2) ? BLANK_CRB : in0[0];
213 c0p = (x == width) ? BLANK_CRB : in0[2];
214 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
215 cp0 = (y == height) ? BLANK_CRB : inp[1];
216 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
217 }
218 c00 = in0[1];
219
220 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
221 cm0 = (y == 2) ? BLANK_CRB : inm[1];
222 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
223 c0m = (x == 2) ? BLANK_CRB : in0[0];
224 c0p = (x == width) ? BLANK_CRB : in0[2];
225 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
226 cp0 = (y == height) ? BLANK_CRB : inp[1];
227 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
228 #else
229 cmm = ((x == 2) || (y == 2)) ? BLANK_CRB : inm[0];
230 cm0 = (y == 2) ? BLANK_CRB : inm[1];
231 cmp = ((x == width) || (y == 2)) ? BLANK_CRB : inm[2];
232 c0m = (x == 2) ? BLANK_CRB : in0[0];
233 c00 = in0[1];
234 c0p = (x == width) ? BLANK_CRB : in0[2];
235 cpm = ((x == 2) || (y == height)) ? BLANK_CRB : inp[0];
236 cp0 = (y == height) ? BLANK_CRB : inp[1];
237 cpp = ((x == width) || (y == height)) ? BLANK_CRB : inp[2];
238 #endif
239 inm--;
240 in0--;
241 inp--;
242
243 *(out1--) = (1*cpp + 3*(cp0+c0p) + 9*c00 + 8) >> 4;
244 *(out1--) = (1*cpm + 3*(cp0+c0m) + 9*c00 + 8) >> 4;
245 *(out0--) = (1*cmp + 3*(cm0+c0p) + 9*c00 + 8) >> 4;
246 *(out0--) = (1*cmm + 3*(cm0+c0m) + 9*c00 + 8) >> 4;
247 }
248 out1 -= width;
249 out0 -= width;
250 }
251 }
252
253 #else
ss_420jpeg_to_444(uint8_t * buffer,int width,int height)254 static void ss_420jpeg_to_444(uint8_t *buffer, int width, int height)
255 {
256 uint8_t *in, *out0, *out1;
257 int x, y;
258
259 in = buffer + (width * height / 4) - 1;
260 out1 = buffer + (width * height) - 1;
261 out0 = out1 - width;
262 for (y = height - 1; y >= 0; y -= 2) {
263 for (x = width - 1; x >= 0; x -=2) {
264 uint8_t val = *(in--);
265 *(out1--) = val;
266 *(out1--) = val;
267 *(out0--) = val;
268 *(out0--) = val;
269 }
270 out0 -= width;
271 out1 -= width;
272 }
273 }
274 #endif
275
276
277
278
279 /* vertical intersitial siting; horizontal cositing
280 *
281 * Y Y Y Y
282 * C C
283 * Y Y Y Y
284 *
285 * Y Y Y Y
286 * C C
287 * Y Y Y Y
288 *
289 * [1,2,1] kernel for horizontal subsampling:
290 *
291 * inX[0] [1] [2]
292 * | | |
293 * C C C C
294 * \ | /
295 * \ | /
296 * C
297 */
298
ss_444_to_420mpeg2(uint8_t * buffer,int width,int height)299 static void ss_444_to_420mpeg2(uint8_t *buffer, int width, int height)
300 {
301 uint8_t *in0, *in1, *out;
302 int x, y;
303
304 in0 = buffer; /* points to */
305 in1 = buffer + width; /* second of pair of lines */
306 out = buffer;
307 for (y = 0; y < height; y += 2) {
308 /* first column boundary condition -- just repeat it to right */
309 *out = (in0[0] + (2 * in0[0]) + in0[1] +
310 in1[0] + (2 * in1[0]) + in1[1]) >> 3;
311 out++;
312 in0++;
313 in1++;
314 /* rest of columns just loop */
315 for (x = 2; x < width; x += 2) {
316 *out = (in0[0] + (2 * in0[1]) + in0[2] +
317 in1[0] + (2 * in1[1]) + in1[2]) >> 3;
318 in0 += 2;
319 in1 += 2;
320 out++;
321 }
322 in0 += width + 1;
323 in1 += width + 1;
324 }
325 }
326
327
328
329
330
chroma_sub_implemented(int mode)331 int chroma_sub_implemented(int mode)
332 {
333 switch (mode) {
334 case Y4M_CHROMA_420JPEG:
335 case Y4M_CHROMA_420MPEG2:
336 case Y4M_CHROMA_444:
337 return 1; /* yes, supported */
338 case Y4M_CHROMA_420PALDV:
339 case Y4M_CHROMA_422:
340 case Y4M_CHROMA_411:
341 case Y4M_CHROMA_444ALPHA:
342 case Y4M_CHROMA_MONO:
343 default:
344 return 0; /* no, unsupported */
345 }
346 }
347
348
chroma_subsample(int mode,uint8_t * ycbcr[],int width,int height)349 void chroma_subsample(int mode, uint8_t *ycbcr[], int width, int height)
350 {
351 switch (mode) {
352 case Y4M_CHROMA_444:
353 case Y4M_CHROMA_444ALPHA:
354 case Y4M_CHROMA_MONO:
355 break;
356 case Y4M_CHROMA_420JPEG:
357 ss_444_to_420jpeg(ycbcr[1], width, height);
358 ss_444_to_420jpeg(ycbcr[2], width, height);
359 break;
360 case Y4M_CHROMA_420MPEG2:
361 ss_444_to_420mpeg2(ycbcr[1], width, height);
362 ss_444_to_420mpeg2(ycbcr[2], width, height);
363 break;
364 default:
365 break;
366 }
367 }
368
369
370
chroma_super_implemented(int mode)371 int chroma_super_implemented(int mode)
372 {
373 switch (mode) {
374 case Y4M_CHROMA_420JPEG:
375 case Y4M_CHROMA_444:
376 return 1; /* yes, supported */
377 case Y4M_CHROMA_420MPEG2:
378 case Y4M_CHROMA_420PALDV:
379 case Y4M_CHROMA_422:
380 case Y4M_CHROMA_411:
381 case Y4M_CHROMA_444ALPHA:
382 case Y4M_CHROMA_MONO:
383 default:
384 return 0; /* no, unsupported */
385 }
386 }
387
388
chroma_supersample(int mode,uint8_t * ycbcr[],int width,int height)389 void chroma_supersample(int mode, uint8_t *ycbcr[], int width, int height)
390 {
391 switch (mode) {
392 case Y4M_CHROMA_444:
393 case Y4M_CHROMA_444ALPHA:
394 case Y4M_CHROMA_MONO:
395 break;
396 case Y4M_CHROMA_420JPEG:
397 ss_420jpeg_to_444(ycbcr[1], width, height);
398 ss_420jpeg_to_444(ycbcr[2], width, height);
399 break;
400 case Y4M_CHROMA_420MPEG2:
401 // ss_420mpeg2_to_444(ycbcr[1], width, height);
402 // ss_420mpeg2_to_444(ycbcr[2], width, height);
403 // exit(4);
404 // break;
405 default:
406 break;
407 }
408 }
409
410
411