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