411351d1cd7955992e6e931dc174e15612b0e706
[vlc.git] / modules / video_filter / deinterlace / algo_x.c
1 /*****************************************************************************
2  * algo_x.c : "X" algorithm for vlc deinterlacer
3  *****************************************************************************
4  * Copyright (C) 2000-2011 VLC authors and VideoLAN
5  *
6  * Author: Laurent Aimar <fenrir@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 #   include "config.h"
25 #endif
26
27 #ifdef CAN_COMPILE_MMXEXT
28 #   include "mmx.h"
29 #endif
30
31 #include <stdint.h>
32
33 #include <vlc_common.h>
34 #include <vlc_cpu.h>
35 #include <vlc_picture.h>
36
37 #include "deinterlace.h" /* filter_sys_t */
38
39 #include "algo_x.h"
40
41 /*****************************************************************************
42  * Internal functions
43  *****************************************************************************/
44
45 /* XDeint8x8Detect: detect if a 8x8 block is interlaced.
46  * XXX: It need to access to 8x10
47  * We use more than 8 lines to help with scrolling (text)
48  * (and because XDeint8x8Frame use line 9)
49  * XXX: smooth/uniform area with noise detection doesn't works well
50  * but it's not really a problem because they don't have much details anyway
51  */
52 static inline int ssd( int a ) { return a*a; }
53 static inline int XDeint8x8DetectC( uint8_t *src, int i_src )
54 {
55     int y, x;
56     int ff, fr;
57     int fc;
58
59     /* Detect interlacing */
60     fc = 0;
61     for( y = 0; y < 7; y += 2 )
62     {
63         ff = fr = 0;
64         for( x = 0; x < 8; x++ )
65         {
66             fr += ssd(src[      x] - src[1*i_src+x]) +
67                   ssd(src[i_src+x] - src[2*i_src+x]);
68             ff += ssd(src[      x] - src[2*i_src+x]) +
69                   ssd(src[i_src+x] - src[3*i_src+x]);
70         }
71         if( ff < 6*fr/8 && fr > 32 )
72             fc++;
73
74         src += 2*i_src;
75     }
76
77     return fc < 1 ? false : true;
78 }
79 #ifdef CAN_COMPILE_MMXEXT
80 VLC_MMX
81 static inline int XDeint8x8DetectMMXEXT( uint8_t *src, int i_src )
82 {
83
84     int y, x;
85     int32_t ff, fr;
86     int fc;
87
88     /* Detect interlacing */
89     fc = 0;
90     pxor_r2r( mm7, mm7 );
91     for( y = 0; y < 9; y += 2 )
92     {
93         ff = fr = 0;
94         pxor_r2r( mm5, mm5 );
95         pxor_r2r( mm6, mm6 );
96         for( x = 0; x < 8; x+=4 )
97         {
98             movd_m2r( src[        x], mm0 );
99             movd_m2r( src[1*i_src+x], mm1 );
100             movd_m2r( src[2*i_src+x], mm2 );
101             movd_m2r( src[3*i_src+x], mm3 );
102
103             punpcklbw_r2r( mm7, mm0 );
104             punpcklbw_r2r( mm7, mm1 );
105             punpcklbw_r2r( mm7, mm2 );
106             punpcklbw_r2r( mm7, mm3 );
107
108             movq_r2r( mm0, mm4 );
109
110             psubw_r2r( mm1, mm0 );
111             psubw_r2r( mm2, mm4 );
112
113             psubw_r2r( mm1, mm2 );
114             psubw_r2r( mm1, mm3 );
115
116             pmaddwd_r2r( mm0, mm0 );
117             pmaddwd_r2r( mm4, mm4 );
118             pmaddwd_r2r( mm2, mm2 );
119             pmaddwd_r2r( mm3, mm3 );
120             paddd_r2r( mm0, mm2 );
121             paddd_r2r( mm4, mm3 );
122             paddd_r2r( mm2, mm5 );
123             paddd_r2r( mm3, mm6 );
124         }
125
126         movq_r2r( mm5, mm0 );
127         psrlq_i2r( 32, mm0 );
128         paddd_r2r( mm0, mm5 );
129         movd_r2m( mm5, fr );
130
131         movq_r2r( mm6, mm0 );
132         psrlq_i2r( 32, mm0 );
133         paddd_r2r( mm0, mm6 );
134         movd_r2m( mm6, ff );
135
136         if( ff < 6*fr/8 && fr > 32 )
137             fc++;
138
139         src += 2*i_src;
140     }
141     return fc;
142 }
143 #endif
144
145 static inline void XDeint8x8MergeC( uint8_t *dst,  int i_dst,
146                                     uint8_t *src1, int i_src1,
147                                     uint8_t *src2, int i_src2 )
148 {
149     int y, x;
150
151     /* Progressive */
152     for( y = 0; y < 8; y += 2 )
153     {
154         memcpy( dst, src1, 8 );
155         dst  += i_dst;
156
157         for( x = 0; x < 8; x++ )
158             dst[x] = (src1[x] + 6*src2[x] + src1[i_src1+x] + 4 ) >> 3;
159         dst += i_dst;
160
161         src1 += i_src1;
162         src2 += i_src2;
163     }
164 }
165
166 #ifdef CAN_COMPILE_MMXEXT
167 VLC_MMX
168 static inline void XDeint8x8MergeMMXEXT( uint8_t *dst,  int i_dst,
169                                          uint8_t *src1, int i_src1,
170                                          uint8_t *src2, int i_src2 )
171 {
172     static const uint64_t m_4 = INT64_C(0x0004000400040004);
173     int y, x;
174
175     /* Progressive */
176     pxor_r2r( mm7, mm7 );
177     for( y = 0; y < 8; y += 2 )
178     {
179         for( x = 0; x < 8; x +=4 )
180         {
181             movd_m2r( src1[x], mm0 );
182             movd_r2m( mm0, dst[x] );
183
184             movd_m2r( src2[x], mm1 );
185             movd_m2r( src1[i_src1+x], mm2 );
186
187             punpcklbw_r2r( mm7, mm0 );
188             punpcklbw_r2r( mm7, mm1 );
189             punpcklbw_r2r( mm7, mm2 );
190             paddw_r2r( mm1, mm1 );
191             movq_r2r( mm1, mm3 );
192             paddw_r2r( mm3, mm3 );
193             paddw_r2r( mm2, mm0 );
194             paddw_r2r( mm3, mm1 );
195             paddw_m2r( m_4, mm1 );
196             paddw_r2r( mm1, mm0 );
197             psraw_i2r( 3, mm0 );
198             packuswb_r2r( mm7, mm0 );
199             movd_r2m( mm0, dst[i_dst+x] );
200         }
201         dst += 2*i_dst;
202         src1 += i_src1;
203         src2 += i_src2;
204     }
205 }
206
207 #endif
208
209 /* XDeint8x8FieldE: Stupid deinterlacing (1,0,1) for block that miss a
210  * neighbour
211  * (Use 8x9 pixels)
212  * TODO: a better one for the inner part.
213  */
214 static inline void XDeint8x8FieldEC( uint8_t *dst, int i_dst,
215                                      uint8_t *src, int i_src )
216 {
217     int y, x;
218
219     /* Interlaced */
220     for( y = 0; y < 8; y += 2 )
221     {
222         memcpy( dst, src, 8 );
223         dst += i_dst;
224
225         for( x = 0; x < 8; x++ )
226             dst[x] = (src[x] + src[2*i_src+x] ) >> 1;
227         dst += 1*i_dst;
228         src += 2*i_src;
229     }
230 }
231
232 #ifdef CAN_COMPILE_MMXEXT
233 VLC_MMX
234 static inline void XDeint8x8FieldEMMXEXT( uint8_t *dst, int i_dst,
235                                           uint8_t *src, int i_src )
236 {
237     int y;
238
239     /* Interlaced */
240     for( y = 0; y < 8; y += 2 )
241     {
242         movq_m2r( src[0], mm0 );
243         movq_r2m( mm0, dst[0] );
244         dst += i_dst;
245
246         movq_m2r( src[2*i_src], mm1 );
247         pavgb_r2r( mm1, mm0 );
248
249         movq_r2m( mm0, dst[0] );
250
251         dst += 1*i_dst;
252         src += 2*i_src;
253     }
254 }
255 #endif
256
257 /* XDeint8x8Field: Edge oriented interpolation
258  * (Need -4 and +5 pixels H, +1 line)
259  */
260 static inline void XDeint8x8FieldC( uint8_t *dst, int i_dst,
261                                     uint8_t *src, int i_src )
262 {
263     int y, x;
264
265     /* Interlaced */
266     for( y = 0; y < 8; y += 2 )
267     {
268         memcpy( dst, src, 8 );
269         dst += i_dst;
270
271         for( x = 0; x < 8; x++ )
272         {
273             uint8_t *src2 = &src[2*i_src];
274             /* I use 8 pixels just to match the MMX version, but it's overkill
275              * 5 would be enough (less isn't good) */
276             const int c0 = abs(src[x-4]-src2[x-2]) + abs(src[x-3]-src2[x-1]) +
277                            abs(src[x-2]-src2[x+0]) + abs(src[x-1]-src2[x+1]) +
278                            abs(src[x+0]-src2[x+2]) + abs(src[x+1]-src2[x+3]) +
279                            abs(src[x+2]-src2[x+4]) + abs(src[x+3]-src2[x+5]);
280
281             const int c1 = abs(src[x-3]-src2[x-3]) + abs(src[x-2]-src2[x-2]) +
282                            abs(src[x-1]-src2[x-1]) + abs(src[x+0]-src2[x+0]) +
283                            abs(src[x+1]-src2[x+1]) + abs(src[x+2]-src2[x+2]) +
284                            abs(src[x+3]-src2[x+3]) + abs(src[x+4]-src2[x+4]);
285
286             const int c2 = abs(src[x-2]-src2[x-4]) + abs(src[x-1]-src2[x-3]) +
287                            abs(src[x+0]-src2[x-2]) + abs(src[x+1]-src2[x-1]) +
288                            abs(src[x+2]-src2[x+0]) + abs(src[x+3]-src2[x+1]) +
289                            abs(src[x+4]-src2[x+2]) + abs(src[x+5]-src2[x+3]);
290
291             if( c0 < c1 && c1 <= c2 )
292                 dst[x] = (src[x-1] + src2[x+1]) >> 1;
293             else if( c2 < c1 && c1 <= c0 )
294                 dst[x] = (src[x+1] + src2[x-1]) >> 1;
295             else
296                 dst[x] = (src[x+0] + src2[x+0]) >> 1;
297         }
298
299         dst += 1*i_dst;
300         src += 2*i_src;
301     }
302 }
303
304 #ifdef CAN_COMPILE_MMXEXT
305 VLC_MMX
306 static inline void XDeint8x8FieldMMXEXT( uint8_t *dst, int i_dst,
307                                          uint8_t *src, int i_src )
308 {
309     int y, x;
310
311     /* Interlaced */
312     for( y = 0; y < 8; y += 2 )
313     {
314         memcpy( dst, src, 8 );
315         dst += i_dst;
316
317         for( x = 0; x < 8; x++ )
318         {
319             uint8_t *src2 = &src[2*i_src];
320             int32_t c0, c1, c2;
321
322             movq_m2r( src[x-2], mm0 );
323             movq_m2r( src[x-3], mm1 );
324             movq_m2r( src[x-4], mm2 );
325
326             psadbw_m2r( src2[x-4], mm0 );
327             psadbw_m2r( src2[x-3], mm1 );
328             psadbw_m2r( src2[x-2], mm2 );
329
330             movd_r2m( mm0, c2 );
331             movd_r2m( mm1, c1 );
332             movd_r2m( mm2, c0 );
333
334             if( c0 < c1 && c1 <= c2 )
335                 dst[x] = (src[x-1] + src2[x+1]) >> 1;
336             else if( c2 < c1 && c1 <= c0 )
337                 dst[x] = (src[x+1] + src2[x-1]) >> 1;
338             else
339                 dst[x] = (src[x+0] + src2[x+0]) >> 1;
340         }
341
342         dst += 1*i_dst;
343         src += 2*i_src;
344     }
345 }
346 #endif
347
348 /* NxN arbitray size (and then only use pixel in the NxN block)
349  */
350 static inline int XDeintNxNDetect( uint8_t *src, int i_src,
351                                    int i_height, int i_width )
352 {
353     int y, x;
354     int ff, fr;
355     int fc;
356
357
358     /* Detect interlacing */
359     /* FIXME way too simple, need to be more like XDeint8x8Detect */
360     ff = fr = 0;
361     fc = 0;
362     for( y = 0; y < i_height - 2; y += 2 )
363     {
364         const uint8_t *s = &src[y*i_src];
365         for( x = 0; x < i_width; x++ )
366         {
367             fr += ssd(s[      x] - s[1*i_src+x]);
368             ff += ssd(s[      x] - s[2*i_src+x]);
369         }
370         if( ff < fr && fr > i_width / 2 )
371             fc++;
372     }
373
374     return fc < 2 ? false : true;
375 }
376
377 static inline void XDeintNxNFrame( uint8_t *dst, int i_dst,
378                                    uint8_t *src, int i_src,
379                                    int i_width, int i_height )
380 {
381     int y, x;
382
383     /* Progressive */
384     for( y = 0; y < i_height; y += 2 )
385     {
386         memcpy( dst, src, i_width );
387         dst += i_dst;
388
389         if( y < i_height - 2 )
390         {
391             for( x = 0; x < i_width; x++ )
392                 dst[x] = (src[x] + 2*src[1*i_src+x] + src[2*i_src+x] + 2 ) >> 2;
393         }
394         else
395         {
396             /* Blend last line */
397             for( x = 0; x < i_width; x++ )
398                 dst[x] = (src[x] + src[1*i_src+x] ) >> 1;
399         }
400         dst += 1*i_dst;
401         src += 2*i_src;
402     }
403 }
404
405 static inline void XDeintNxNField( uint8_t *dst, int i_dst,
406                                    uint8_t *src, int i_src,
407                                    int i_width, int i_height )
408 {
409     int y, x;
410
411     /* Interlaced */
412     for( y = 0; y < i_height; y += 2 )
413     {
414         memcpy( dst, src, i_width );
415         dst += i_dst;
416
417         if( y < i_height - 2 )
418         {
419             for( x = 0; x < i_width; x++ )
420                 dst[x] = (src[x] + src[2*i_src+x] ) >> 1;
421         }
422         else
423         {
424             /* Blend last line */
425             for( x = 0; x < i_width; x++ )
426                 dst[x] = (src[x] + src[i_src+x]) >> 1;
427         }
428         dst += 1*i_dst;
429         src += 2*i_src;
430     }
431 }
432
433 static inline void XDeintNxN( uint8_t *dst, int i_dst, uint8_t *src, int i_src,
434                               int i_width, int i_height )
435 {
436     if( XDeintNxNDetect( src, i_src, i_width, i_height ) )
437         XDeintNxNField( dst, i_dst, src, i_src, i_width, i_height );
438     else
439         XDeintNxNFrame( dst, i_dst, src, i_src, i_width, i_height );
440 }
441
442 /* XDeintBand8x8:
443  */
444 static inline void XDeintBand8x8C( uint8_t *dst, int i_dst,
445                                    uint8_t *src, int i_src,
446                                    const int i_mbx, int i_modx )
447 {
448     int x;
449
450     for( x = 0; x < i_mbx; x++ )
451     {
452         int s;
453         if( ( s = XDeint8x8DetectC( src, i_src ) ) )
454         {
455             if( x == 0 || x == i_mbx - 1 )
456                 XDeint8x8FieldEC( dst, i_dst, src, i_src );
457             else
458                 XDeint8x8FieldC( dst, i_dst, src, i_src );
459         }
460         else
461         {
462             XDeint8x8MergeC( dst, i_dst,
463                              &src[0*i_src], 2*i_src,
464                              &src[1*i_src], 2*i_src );
465         }
466
467         dst += 8;
468         src += 8;
469     }
470
471     if( i_modx )
472         XDeintNxN( dst, i_dst, src, i_src, i_modx, 8 );
473 }
474
475 #ifdef CAN_COMPILE_MMXEXT
476 VLC_MMX
477 static inline void XDeintBand8x8MMXEXT( uint8_t *dst, int i_dst,
478                                         uint8_t *src, int i_src,
479                                         const int i_mbx, int i_modx )
480 {
481     int x;
482
483     /* Reset current line */
484     for( x = 0; x < i_mbx; x++ )
485     {
486         int s;
487         if( ( s = XDeint8x8DetectMMXEXT( src, i_src ) ) )
488         {
489             if( x == 0 || x == i_mbx - 1 )
490                 XDeint8x8FieldEMMXEXT( dst, i_dst, src, i_src );
491             else
492                 XDeint8x8FieldMMXEXT( dst, i_dst, src, i_src );
493         }
494         else
495         {
496             XDeint8x8MergeMMXEXT( dst, i_dst,
497                                   &src[0*i_src], 2*i_src,
498                                   &src[1*i_src], 2*i_src );
499         }
500
501         dst += 8;
502         src += 8;
503     }
504
505     if( i_modx )
506         XDeintNxN( dst, i_dst, src, i_src, i_modx, 8 );
507 }
508 #endif
509
510 /*****************************************************************************
511  * Public functions
512  *****************************************************************************/
513
514 int RenderX( filter_t *p_filter, picture_t *p_outpic, picture_t *p_pic )
515 {
516     VLC_UNUSED(p_filter);
517     int i_plane;
518 #if defined (CAN_COMPILE_MMXEXT)
519     const bool mmxext = vlc_CPU_MMXEXT();
520 #endif
521
522     /* Copy image and skip lines */
523     for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
524     {
525         const int i_mby = ( p_outpic->p[i_plane].i_visible_lines + 7 )/8 - 1;
526         const int i_mbx = p_outpic->p[i_plane].i_visible_pitch/8;
527
528         const int i_mody = p_outpic->p[i_plane].i_visible_lines - 8*i_mby;
529         const int i_modx = p_outpic->p[i_plane].i_visible_pitch - 8*i_mbx;
530
531         const int i_dst = p_outpic->p[i_plane].i_pitch;
532         const int i_src = p_pic->p[i_plane].i_pitch;
533
534         int y, x;
535
536         for( y = 0; y < i_mby; y++ )
537         {
538             uint8_t *dst = &p_outpic->p[i_plane].p_pixels[8*y*i_dst];
539             uint8_t *src = &p_pic->p[i_plane].p_pixels[8*y*i_src];
540
541 #ifdef CAN_COMPILE_MMXEXT
542             if( mmxext )
543                 XDeintBand8x8MMXEXT( dst, i_dst, src, i_src, i_mbx, i_modx );
544             else
545 #endif
546                 XDeintBand8x8C( dst, i_dst, src, i_src, i_mbx, i_modx );
547         }
548
549         /* Last line (C only)*/
550         if( i_mody )
551         {
552             uint8_t *dst = &p_outpic->p[i_plane].p_pixels[8*y*i_dst];
553             uint8_t *src = &p_pic->p[i_plane].p_pixels[8*y*i_src];
554
555             for( x = 0; x < i_mbx; x++ )
556             {
557                 XDeintNxN( dst, i_dst, src, i_src, 8, i_mody );
558
559                 dst += 8;
560                 src += 8;
561             }
562
563             if( i_modx )
564                 XDeintNxN( dst, i_dst, src, i_src, i_modx, i_mody );
565         }
566     }
567
568 #ifdef CAN_COMPILE_MMXEXT
569     if( mmxext )
570         emms();
571 #endif
572     return VLC_SUCCESS;
573 }