8d36479ca1e3562c900492d46eadfc6e342ef28c
[vlc.git] / modules / video_filter / deinterlace / merge.c
1 /*****************************************************************************
2  * merge.c : Merge (line blending) routines for the VLC deinterlacer
3  *****************************************************************************
4  * Copyright (C) 2011 VLC authors and VideoLAN
5  *
6  * Author: Sam Hocevar <sam@zoy.org>                      (generic C routine)
7  *         Sigmund Augdal Helberg <sigmunau@videolan.org> (MMXEXT, 3DNow, SSE2)
8  *         Eric Petit <eric.petit@lapsus.org>             (Altivec)
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #   include "config.h"
27 #endif
28
29 #include <stdlib.h>
30 #include <stdint.h>
31
32 #include <vlc_common.h>
33 #include <vlc_cpu.h>
34 #include "merge.h"
35
36 #ifdef CAN_COMPILE_MMXEXT
37 #   include "mmx.h"
38 #endif
39
40 #ifdef HAVE_ALTIVEC_H
41 #   include <altivec.h>
42 #endif
43
44 /*****************************************************************************
45  * Merge (line blending) routines
46  *****************************************************************************/
47
48 void Merge8BitGeneric( void *_p_dest, const void *_p_s1,
49                        const void *_p_s2, size_t i_bytes )
50 {
51     uint8_t *p_dest = _p_dest;
52     const uint8_t *p_s1 = _p_s1;
53     const uint8_t *p_s2 = _p_s2;
54
55     for( ; i_bytes > 0; i_bytes-- )
56         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
57 }
58
59 void Merge16BitGeneric( void *_p_dest, const void *_p_s1,
60                         const void *_p_s2, size_t i_bytes )
61 {
62     uint16_t *p_dest = _p_dest;
63     const uint16_t *p_s1 = _p_s1;
64     const uint16_t *p_s2 = _p_s2;
65
66     for( size_t i_words = i_bytes / 2; i_words > 0; i_words-- )
67         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
68 }
69
70 #if defined(CAN_COMPILE_MMXEXT)
71 VLC_MMX
72 void MergeMMXEXT( void *_p_dest, const void *_p_s1, const void *_p_s2,
73                   size_t i_bytes )
74 {
75     uint8_t *p_dest = _p_dest;
76     const uint8_t *p_s1 = _p_s1;
77     const uint8_t *p_s2 = _p_s2;
78
79     for( ; i_bytes >= 8; i_bytes -= 8 )
80     {
81         __asm__  __volatile__( "movq %2,%%mm1;"
82                                "pavgb %1, %%mm1;"
83                                "movq %%mm1, %0" :"=m" (*p_dest):
84                                                  "m" (*p_s1),
85                                                  "m" (*p_s2) : "mm1" );
86         p_dest += 8;
87         p_s1 += 8;
88         p_s2 += 8;
89     }
90
91     for( ; i_bytes > 0; i_bytes-- )
92         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
93 }
94 #endif
95
96 #if defined(CAN_COMPILE_3DNOW)
97 VLC_MMX
98 void Merge3DNow( void *_p_dest, const void *_p_s1, const void *_p_s2,
99                  size_t i_bytes )
100 {
101     uint8_t *p_dest = _p_dest;
102     const uint8_t *p_s1 = _p_s1;
103     const uint8_t *p_s2 = _p_s2;
104
105     for( ; i_bytes >= 8; i_bytes -= 8 )
106     {
107         __asm__  __volatile__( "movq %2,%%mm1;"
108                                "pavgusb %1, %%mm1;"
109                                "movq %%mm1, %0" :"=m" (*p_dest):
110                                                  "m" (*p_s1),
111                                                  "m" (*p_s2) : "mm1" );
112         p_dest += 8;
113         p_s1 += 8;
114         p_s2 += 8;
115     }
116
117     for( ; i_bytes > 0; i_bytes-- )
118         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
119 }
120 #endif
121
122 #if defined(CAN_COMPILE_SSE)
123 VLC_SSE
124 void Merge8BitSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
125                     size_t i_bytes )
126 {
127     uint8_t *p_dest = _p_dest;
128     const uint8_t *p_s1 = _p_s1;
129     const uint8_t *p_s2 = _p_s2;
130
131     for( ; i_bytes > 0 && ((uintptr_t)p_s1 & 15); i_bytes-- )
132         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
133
134     for( ; i_bytes >= 16; i_bytes -= 16 )
135     {
136         __asm__  __volatile__( "movdqu %2,%%xmm1;"
137                                "pavgb %1, %%xmm1;"
138                                "movdqu %%xmm1, %0" :"=m" (*p_dest):
139                                                  "m" (*p_s1),
140                                                  "m" (*p_s2) : "xmm1" );
141         p_dest += 16;
142         p_s1 += 16;
143         p_s2 += 16;
144     }
145
146     for( ; i_bytes > 0; i_bytes-- )
147         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
148 }
149
150 VLC_SSE
151 void Merge16BitSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
152                      size_t i_bytes )
153 {
154     uint16_t *p_dest = _p_dest;
155     const uint16_t *p_s1 = _p_s1;
156     const uint16_t *p_s2 = _p_s2;
157
158     size_t i_words = i_bytes / 2;
159     for( ; i_words > 0 && ((uintptr_t)p_s1 & 15); i_words-- )
160         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
161
162     for( ; i_words >= 8; i_words -= 8 )
163     {
164         __asm__  __volatile__( "movdqu %2,%%xmm1;"
165                                "pavgw %1, %%xmm1;"
166                                "movdqu %%xmm1, %0" :"=m" (*p_dest):
167                                                  "m" (*p_s1),
168                                                  "m" (*p_s2) : "xmm1" );
169         p_dest += 8;
170         p_s1 += 8;
171         p_s2 += 8;
172     }
173
174     for( ; i_words > 0; i_words-- )
175         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
176 }
177
178 #endif
179
180 #ifdef CAN_COMPILE_C_ALTIVEC
181 void MergeAltivec( void *_p_dest, const void *_p_s1,
182                    const void *_p_s2, size_t i_bytes )
183 {
184     uint8_t *p_dest = _p_dest;
185     const uint8_t *p_s1 = _p_s1;
186     const uint8_t *p_s2 = _p_s2;
187     uint8_t *p_end  = p_dest + i_bytes - 15;
188
189     /* Use C until the first 16-bytes aligned destination pixel */
190     while( (uintptr_t)p_dest & 0xF )
191     {
192         *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
193     }
194
195     if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) )
196     {
197         /* Unaligned source */
198         vector unsigned char s1v, s2v, destv;
199         vector unsigned char s1oldv, s2oldv, s1newv, s2newv;
200         vector unsigned char perm1v, perm2v;
201
202         perm1v = vec_lvsl( 0, p_s1 );
203         perm2v = vec_lvsl( 0, p_s2 );
204         s1oldv = vec_ld( 0, p_s1 );
205         s2oldv = vec_ld( 0, p_s2 );
206
207         while( p_dest < p_end )
208         {
209             s1newv = vec_ld( 16, p_s1 );
210             s2newv = vec_ld( 16, p_s2 );
211             s1v    = vec_perm( s1oldv, s1newv, perm1v );
212             s2v    = vec_perm( s2oldv, s2newv, perm2v );
213             s1oldv = s1newv;
214             s2oldv = s2newv;
215             destv  = vec_avg( s1v, s2v );
216             vec_st( destv, 0, p_dest );
217
218             p_s1   += 16;
219             p_s2   += 16;
220             p_dest += 16;
221         }
222     }
223     else
224     {
225         /* Aligned source */
226         vector unsigned char s1v, s2v, destv;
227
228         while( p_dest < p_end )
229         {
230             s1v   = vec_ld( 0, p_s1 );
231             s2v   = vec_ld( 0, p_s2 );
232             destv = vec_avg( s1v, s2v );
233             vec_st( destv, 0, p_dest );
234
235             p_s1   += 16;
236             p_s2   += 16;
237             p_dest += 16;
238         }
239     }
240
241     p_end += 15;
242
243     while( p_dest < p_end )
244         *p_dest++ = ( *p_s1++ + *p_s2++ ) >> 1;
245 }
246 #endif
247
248 /*****************************************************************************
249  * EndMerge routines
250  *****************************************************************************/
251
252 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
253 void EndMMX( void )
254 {
255     __asm__ __volatile__( "emms" :: );
256 }
257 #endif
258
259 #if defined(CAN_COMPILE_3DNOW)
260 void End3DNow( void )
261 {
262     __asm__ __volatile__( "femms" :: );
263 }
264 #endif