/*
 * generic alpha renderers for all YUV modes and RGB depths
 * Optimized by Nick and Michael.
 *
 * This file is part of MPlayer.
 *
 * MPlayer is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * MPlayer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#undef PREFETCH
#undef EMMS
#undef PREFETCHW
#undef PAVGB

#if HAVE_AMD3DNOW
#define PREFETCH  "prefetch"
#define PREFETCHW "prefetchw"
#define PAVGB	  "pavgusb"
#elif HAVE_MMX2
#define PREFETCH "prefetchnta"
#define PREFETCHW "prefetcht0"
#define PAVGB	  "pavgb"
#else
#define PREFETCH " # nop"
#define PREFETCHW " # nop"
#endif

#if HAVE_AMD3DNOW
/* On K6 femms is faster of emms. On K7 femms is directly mapped on emms. */
#define EMMS     "femms"
#else
#define EMMS     "emms"
#endif

#if HAVE_SSE2
ATTR_TARGET_SSE2
static inline __m128i muladd_src_unpacked(__m128i dstlo, __m128i dsthi, __m128i src, __m128i srcalo, __m128i srcahi)
{
    // (mmhigh,mmlow) = (dst * (srca * 256)) / 65536 (= (dst * srca) >> 8)
    __m128i mmlow = _mm_mulhi_epu16(dstlo, srcalo);
    __m128i mmhigh = _mm_mulhi_epu16(dsthi, srcahi);

    __m128i res = _mm_packus_epi16(mmlow, mmhigh);

    return _mm_add_epi8(res, src);
}

ATTR_TARGET_SSE2
static inline __m128i muladd_src(__m128i dst, __m128i src, __m128i srca)
{
    __m128i zero = _mm_setzero_si128();
    __m128i dstlo = _mm_unpacklo_epi8(dst, zero);
    __m128i dsthi = _mm_unpackhi_epi8(dst, zero);
    __m128i srcalo = _mm_unpacklo_epi8(zero, srca);
    __m128i srcahi = _mm_unpackhi_epi8(zero, srca);
    return muladd_src_unpacked(dstlo, dsthi, src, srcalo, srcahi);
}

ATTR_TARGET_SSE2
static inline __m128i alphamask(__m128i orig, __m128i blended, __m128i srca)
{
    __m128i zero = _mm_setzero_si128();
    // if (!srca) res |= dst --- assumes srca == 0 implies src == 0,
    // thus no need to mask res
    __m128i mask = _mm_cmpeq_epi8(srca, zero);
    orig = _mm_and_si128(orig, mask);
    return _mm_or_si128(blended, orig);
}

// Special version that compares alpha in 16 bit chunks instead of bytewise
ATTR_TARGET_SSE2
static inline __m128i alphamask16(__m128i orig, __m128i blended, __m128i srca)
{
    __m128i zero = _mm_setzero_si128();
    // if (!srca) res |= dst --- assumes srca == 0 implies src == 0,
    // thus no need to mask res
    __m128i mask = _mm_cmpeq_epi16(srca, zero);
    orig = _mm_and_si128(orig, mask);
    return _mm_or_si128(blended, orig);
}
#endif

#if HAVE_SSE2
ATTR_TARGET_SSE2
#endif
static inline void RENAME(vo_draw_alpha_yv12)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
    int y;
#if defined(FAST_OSD) && !HAVE_MMX && !HAVE_SSE2
    w=w>>1;
#endif
#if HAVE_MMX
    __asm__ volatile(
        "pcmpeqb %%mm5, %%mm5\n\t" // F..F
        "movq %%mm5, %%mm4\n\t"
        "movq %%mm5, %%mm7\n\t"
        "psllw $8, %%mm5\n\t" //FF00FF00FF00
        "psrlw $8, %%mm4\n\t" //00FF00FF00FF
        ::);
#endif
    for(y=0;y<h;y++){
        register int x;
#if HAVE_MMX
    __asm__ volatile(
	PREFETCHW" %0\n\t"
	PREFETCH" %1\n\t"
	PREFETCH" %2\n\t"
	::"m"(*dstbase),"m"(*srca),"m"(*src):"memory");
    for(x=0;x<w;x+=8){
	__asm__ volatile(
		"movl %1, %%eax\n\t"
		"orl 4%1, %%eax\n\t"
		" jz 1f\n\t"
		PREFETCHW" 32%0\n\t"
		PREFETCH" 32%1\n\t"
		PREFETCH" 32%2\n\t"
		"movq	%0, %%mm0\n\t" // dstbase
		"movq	%%mm0, %%mm1\n\t"
		"pand %%mm4, %%mm0\n\t" 	//0Y0Y0Y0Y
		"psrlw $8, %%mm1\n\t"		//0Y0Y0Y0Y
		"movq	%1, %%mm2\n\t" 		//srca HGFEDCBA
		"paddb	%%mm7, %%mm2\n\t"
		"movq %%mm2, %%mm3\n\t"
		"pand %%mm4, %%mm2\n\t" 	//0G0E0C0A
		"psrlw $8, %%mm3\n\t"		//0H0F0D0B
		"pmullw	%%mm2, %%mm0\n\t"
		"pmullw	%%mm3, %%mm1\n\t"
		"psrlw	$8, %%mm0\n\t"
		"pand %%mm5, %%mm1\n\t"
		"por %%mm1, %%mm0\n\t"
		"paddb	%2, %%mm0\n\t"
		"movq	%%mm0, %0\n\t"
		"1:\n\t"
		:: "m" (dstbase[x]), "m" (srca[x]), "m" (src[x])
		: "%eax");
	}
#elif HAVE_SSE2
        __m128i zero = _mm_setzero_si128();
        for(x=0;x+15<w;x+=16){
            __m128i mmsrc, mmdst, res;
            __m128i mmsrca = _mm_load_si128((const __m128i *)(srca + x));

            int alpha = _mm_movemask_epi8(_mm_cmpeq_epi8(mmsrca, zero));
            if (alpha == 0xffff) continue;

            mmdst = _mm_loadu_si128((const __m128i *)(dstbase + x));
            mmsrc = _mm_load_si128((const __m128i *)(src + x));

            res = muladd_src(mmdst, mmsrc, mmsrca);

            // _mm_maskmoveu_s128 would be an alternative but slower
            res = alphamask(mmdst, res, mmsrca);
            _mm_storeu_si128((__m128i *)(dstbase + x), res);
        }
        for(;x<w;x++){
            if(srca[x]) dstbase[x]=((dstbase[x]*srca[x])>>8)+src[x];
        }
#else /* HAVE_SSE2 */
        for(x=0;x<w;x++){
#ifdef FAST_OSD
            if(srca[2*x+0]) dstbase[2*x+0]=src[2*x+0];
            if(srca[2*x+1]) dstbase[2*x+1]=src[2*x+1];
#else
            if(srca[x]) dstbase[x]=((dstbase[x]*srca[x])>>8)+src[x];
#endif
        }
#endif
        src+=srcstride;
        srca+=srcstride;
        dstbase+=dststride;
    }
#if HAVE_MMX
	__asm__ volatile(EMMS:::"memory");
#endif
    return;
}

#if HAVE_SSE2
ATTR_TARGET_SSE2
#endif
static inline void RENAME(vo_draw_alpha_yuy2)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
    int y;
#if defined(FAST_OSD) && !HAVE_MMX
    w=w>>1;
#endif
#if HAVE_MMX
    __asm__ volatile(
        "pxor %%mm7, %%mm7\n\t"
        "pcmpeqb %%mm5, %%mm5\n\t" // F..F
        "movq %%mm5, %%mm6\n\t"
        "movq %%mm5, %%mm4\n\t"
        "psllw $8, %%mm5\n\t" //FF00FF00FF00
        "psrlw $8, %%mm4\n\t" //00FF00FF00FF
        ::);
#endif
    for(y=0;y<h;y++){
        register int x;
#if HAVE_MMX
    __asm__ volatile(
	PREFETCHW" %0\n\t"
	PREFETCH" %1\n\t"
	PREFETCH" %2\n\t"
	::"m"(*dstbase),"m"(*srca),"m"(*src));
    for(x=0;x<w;x+=4){
	__asm__ volatile(
		"movl %1, %%eax\n\t"
		"orl %%eax, %%eax\n\t"
		" jz 1f\n\t"
		PREFETCHW" 32%0\n\t"
		PREFETCH" 32%1\n\t"
		PREFETCH" 32%2\n\t"
		"movq	%0, %%mm0\n\t" // dstbase
		"movq	%%mm0, %%mm1\n\t"
		"pand %%mm4, %%mm0\n\t" 	//0Y0Y0Y0Y
		"movd	%%eax, %%mm2\n\t"	//srca 0000DCBA
		"paddb	%%mm6, %%mm2\n\t"
		"punpcklbw %%mm7, %%mm2\n\t"	//srca 0D0C0B0A
		"pmullw	%%mm2, %%mm0\n\t"
		"psrlw	$8, %%mm0\n\t"
		"pand %%mm5, %%mm1\n\t" 	//U0V0U0V0
		"movd %2, %%mm2\n\t"		//src 0000DCBA
		"punpcklbw %%mm7, %%mm2\n\t"	//src 0D0C0B0A
		"por %%mm1, %%mm0\n\t"
		"paddb	%%mm2, %%mm0\n\t"
		"movq	%%mm0, %0\n\t"
		"1:\n\t"
		:: "m" (dstbase[x*2]), "m" (srca[x]), "m" (src[x])
		: "%eax");
	}
#elif HAVE_SSE2
        __m128i zero = _mm_setzero_si128();
        __m128i ymask = _mm_set1_epi16(0xff);
        __m128i uvofs = _mm_set1_epi16(0x8000);
        for(x=0;x+15<w;x+=16){
            __m128i mmsrc, mmsrcalo, mmsrcahi, mmdst, mmdst2, mmlow, mmhigh, mmy, mmuv, res;
            __m128i mmsrca = _mm_load_si128((const __m128i *)(srca + x));
            int alpha = _mm_movemask_epi8(_mm_cmpeq_epi8(mmsrca, zero));
            if (alpha == 0xffff) continue;

            mmdst = _mm_loadu_si128((const __m128i *)(dstbase + 2*x));
            mmdst2 = _mm_loadu_si128((const __m128i *)(dstbase + 2*x + 16));

            // convert UV to signed
            mmdst = _mm_xor_si128(mmdst, uvofs);
            mmdst2 = _mm_xor_si128(mmdst2, uvofs);

            mmsrc = _mm_load_si128((const __m128i *)(src + x));
            mmsrcalo = _mm_unpacklo_epi8(zero, mmsrca);
            mmsrcahi = _mm_unpackhi_epi8(zero, mmsrca);

            mmy = muladd_src_unpacked(_mm_and_si128(mmdst, ymask), _mm_and_si128(mmdst2, ymask), mmsrc, mmsrcalo, mmsrcahi);

            // mmuv = ((dst(uv) ^ 128) * (srca * 256)) / 65536 ^ 128 (= (((dst - 128) * srca) >> 8)) + 128
            mmlow = _mm_srai_epi16(mmdst, 8);
            mmlow = _mm_mulhi_epi16(mmlow, mmsrcalo);
            mmhigh = _mm_srai_epi16(mmdst2, 8);
            mmhigh = _mm_mulhi_epi16(mmhigh, mmsrcahi);

            mmuv = _mm_packs_epi16(mmlow, mmhigh);

            res = _mm_unpacklo_epi8(mmy, mmuv);
            res = alphamask16(mmdst, res, mmsrcalo);
            // convert UV to unsigned
            res = _mm_xor_si128(res, uvofs);
            _mm_storeu_si128((__m128i *)(dstbase + 2 * x), res);

            res = _mm_unpackhi_epi8(mmy, mmuv);
            res = alphamask16(mmdst2, res, mmsrcahi);
            // convert UV to unsigned
            res = _mm_xor_si128(res, uvofs);
            _mm_storeu_si128((__m128i *)(dstbase + 2 * x + 16), res);
        }
        for(;x<w;x++){
            if(srca[x]) {
               dstbase[2*x]=((dstbase[2*x]*srca[x])>>8)+src[x];
               dstbase[2*x+1]=((((signed)dstbase[2*x+1]-128)*srca[x])>>8)+128;
            }
        }
#else /* HAVE_SSE2 */
        for(x=0;x<w;x++){
#ifdef FAST_OSD
            if(srca[2*x+0]) dstbase[4*x+0]=src[2*x+0];
            if(srca[2*x+1]) dstbase[4*x+2]=src[2*x+1];
#else
            if(srca[x]) {
               dstbase[2*x]=((dstbase[2*x]*srca[x])>>8)+src[x];
               dstbase[2*x+1]=((((signed)dstbase[2*x+1]-128)*srca[x])>>8)+128;
           }
#endif
        }
#endif
	src+=srcstride;
        srca+=srcstride;
        dstbase+=dststride;
    }
#if HAVE_MMX
	__asm__ volatile(EMMS:::"memory");
#endif
    return;
}

#if HAVE_SSE2
ATTR_TARGET_SSE2
#endif
static inline void RENAME(vo_draw_alpha_uyvy)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
  int y;
#if defined(FAST_OSD)
  w=w>>1;
#endif
  for(y=0;y<h;y++){
    register int x;
#if HAVE_SSE2
        __m128i zero = _mm_setzero_si128();
        __m128i uvofs = _mm_set1_epi16(0x80);
        for(x=0;x+15<w;x+=16){
            __m128i mmsrc, mmsrcalo, mmsrcahi, mmdst, mmdst2, mmlow, mmhigh, mmy, mmuv, res;
            __m128i mmsrca = _mm_load_si128((const __m128i *)(srca + x));
            int alpha = _mm_movemask_epi8(_mm_cmpeq_epi8(mmsrca, zero));
            if (alpha == 0xffff) continue;

            mmdst = _mm_loadu_si128((const __m128i *)(dstbase + 2*x));
            mmdst2 = _mm_loadu_si128((const __m128i *)(dstbase + 2*x + 16));

            // convert UV to signed
            mmdst = _mm_xor_si128(mmdst, uvofs);
            mmdst2 = _mm_xor_si128(mmdst2, uvofs);

            mmsrc = _mm_load_si128((const __m128i *)(src + x));
            mmsrcalo = _mm_unpacklo_epi8(zero, mmsrca);
            mmsrcahi = _mm_unpackhi_epi8(zero, mmsrca);

            mmy = muladd_src_unpacked(_mm_srli_epi16(mmdst, 8), _mm_srli_epi16(mmdst2, 8), mmsrc, mmsrcalo, mmsrcahi);

            // mmuv = ((dst(uv) ^ 128) * (srca * 256)) / 65536 ^ 128 (= (((dst - 128) * srca) >> 8)) + 128
            // sign-extend and multiply
            mmlow = _mm_slli_epi16(mmdst, 8);
            mmlow = _mm_srai_epi16(mmlow, 8);
            mmlow = _mm_mulhi_epi16(mmlow, mmsrcalo);
            mmhigh = _mm_slli_epi16(mmdst2, 8);
            mmhigh = _mm_srai_epi16(mmhigh, 8);
            mmhigh = _mm_mulhi_epi16(mmhigh, mmsrcahi);

            mmuv = _mm_packs_epi16(mmlow, mmhigh);

            res = _mm_unpacklo_epi8(mmuv, mmy);
            res = alphamask16(mmdst, res, mmsrcalo);
            // convert UV to unsigned
            res = _mm_xor_si128(res, uvofs);
            _mm_storeu_si128((__m128i *)(dstbase + 2 * x), res);

            res = _mm_unpackhi_epi8(mmuv, mmy);
            res = alphamask16(mmdst2, res, mmsrcahi);
            // convert UV to unsigned
            res = _mm_xor_si128(res, uvofs);
            _mm_storeu_si128((__m128i *)(dstbase + 2 * x + 16), res);
        }
        for(;x<w;x++){
            if(srca[x]) {
	       dstbase[2*x+1]=((dstbase[2*x+1]*srca[x])>>8)+src[x];
               dstbase[2*x]=((((signed)dstbase[2*x]-128)*srca[x])>>8)+128;
            }
        }
#else /* HAVE_SSE2 */
    for(x=0;x<w;x++){
#ifdef FAST_OSD
      if(srca[2*x+0]) dstbase[4*x+2]=src[2*x+0];
      if(srca[2*x+1]) dstbase[4*x+0]=src[2*x+1];
#else
      if(srca[x]) {
	dstbase[2*x+1]=((dstbase[2*x+1]*srca[x])>>8)+src[x];
	dstbase[2*x]=((((signed)dstbase[2*x]-128)*srca[x])>>8)+128;
      }
#endif
    }
#endif
    src+=srcstride;
    srca+=srcstride;
    dstbase+=dststride;
  }
}

#define REPL3X(out, sd1, sa1, sd2, sa2, in) \
do { \
   __m128i shuf012 = _mm_shufflelo_epi16(in, 0x40); \
   __m128i shuf345 = _mm_shufflelo_epi16(in, 0xA5); \
   __m128i repl3x_mmtmp = _mm_unpacklo_epi64(shuf012, shuf345); \
   repl3x_mmtmp = _mm_and_si128(repl3x_mmtmp, one_in_three_mask); \
   out = _mm_or_si128(_mm_or_si128(repl3x_mmtmp, _mm_s##sd1##li_si128(repl3x_mmtmp, sa1)), _mm_s##sd2##li_si128(repl3x_mmtmp, sa2)); \
} while (0)

#if HAVE_SSE2
ATTR_TARGET_SSE2
#endif
static inline void RENAME(vo_draw_alpha_rgb24)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
    int y;
#if HAVE_MMX
    __asm__ volatile(
        "pxor %%mm7, %%mm7\n\t"
        "pcmpeqb %%mm6, %%mm6\n\t" // F..F
        ::);
#endif
    for(y=0;y<h;y++){
        register unsigned char *dst = dstbase;
        register int x;
#if ARCH_X86 && (!ARCH_X86_64 || HAVE_MMX || HAVE_SSE2)
#if HAVE_MMX
    __asm__ volatile(
	PREFETCHW" %0\n\t"
	PREFETCH" %1\n\t"
	PREFETCH" %2\n\t"
	::"m"(*dst),"m"(*srca),"m"(*src):"memory");
    for(x=0;x<w;x+=2){
     if(srca[x] || srca[x+1])
	__asm__ volatile(
		PREFETCHW" 32%0\n\t"
		PREFETCH" 32%1\n\t"
		PREFETCH" 32%2\n\t"
		"movq	%0, %%mm0\n\t" // dstbase
		"movq	%%mm0, %%mm1\n\t"
		"movq	%%mm0, %%mm5\n\t"
		"punpcklbw %%mm7, %%mm0\n\t"
		"punpckhbw %%mm7, %%mm1\n\t"
		"movd	%1, %%mm2\n\t" // srca ABCD0000
		"paddb	%%mm6, %%mm2\n\t"
		"punpcklbw %%mm2, %%mm2\n\t" // srca AABBCCDD
		"punpcklbw %%mm2, %%mm2\n\t" // srca AAAABBBB
		"psrlq  $8, %%mm2\n\t" // srca AAABBBB0
		"movq	%%mm2, %%mm3\n\t"
		"punpcklbw %%mm7, %%mm2\n\t" // srca 0A0A0A0B
		"punpckhbw %%mm7, %%mm3\n\t" // srca 0B0B0B00
		"pmullw	%%mm2, %%mm0\n\t"
		"pmullw	%%mm3, %%mm1\n\t"
		"psrlw	$8, %%mm0\n\t"
		"psrlw	$8, %%mm1\n\t"
		"packuswb %%mm1, %%mm0\n\t"
		"movd %2, %%mm2	\n\t" // src ABCD0000
		"punpcklbw %%mm2, %%mm2\n\t" // src AABBCCDD
		"punpcklbw %%mm2, %%mm2\n\t" // src AAAABBBB
		"psrlq  $8, %%mm2\n\t" // src AAABBBB0
		"paddb	%%mm2, %%mm0\n\t"
		"pand	%4, %%mm5\n\t"
		"pand	%3, %%mm0\n\t"
		"por	%%mm0, %%mm5\n\t"
		"movq	%%mm5, %0\n\t"
		:: "m" (dst[0]), "m" (srca[x]), "m" (src[x]), "m"(mask24hl), "m"(mask24lh));
		dst += 6;
	}
#elif HAVE_SSE2
        __m128i one_in_three_mask = _mm_set_epi32(0xff0000ffu, 0x0000ff00u, 0x00ff0000u, 0xff0000ffu);
        __m128i zero = _mm_setzero_si128();
        for(x=0;x+15<w;x+=16){
            __m128i mmsrc, mmtmp, mmtmpa, mmdst, res;
            __m128i mmsrca = _mm_load_si128((const __m128i *)(srca + x));
            int alpha = _mm_movemask_epi8(_mm_cmpeq_epi8(mmsrca, zero));
            if (alpha == 0xffff) { dst += 48; continue; }

            mmsrc = _mm_load_si128((const __m128i *)(src + x));

            if ((alpha & 0x3f) != 0x3f) {
                mmdst = _mm_loadu_si128((const __m128i *)dst);
                REPL3X(mmtmpa, l, 1, l, 2, mmsrca);
                REPL3X(mmtmp, l, 1, l, 2, mmsrc);
                res = muladd_src(mmdst, mmtmp, mmtmpa);
                res = alphamask(mmdst, res, mmtmpa);
                _mm_storeu_si128((__m128i *)dst, res);
            }
            dst += 16;

            mmsrca = _mm_srli_si128(mmsrca, 5);
            mmsrc = _mm_srli_si128(mmsrc, 5);

            if ((alpha & 0x7e0) != 0x7e0) {
                mmdst = _mm_loadu_si128((const __m128i *)dst);
                REPL3X(mmtmpa, l, 1, r, 1, mmsrca);
                REPL3X(mmtmp, l, 1, r, 1, mmsrc);
                res = muladd_src(mmdst, mmtmp, mmtmpa);
                res = alphamask(mmdst, res, mmtmpa);
                _mm_storeu_si128((__m128i *)dst, res);
            }
            dst += 16;

            mmsrc = _mm_srli_si128(mmsrc, 5);
            mmsrca = _mm_srli_si128(mmsrca, 5);

            if ((alpha & 0xfc00) != 0xfc00) {
                mmdst = _mm_loadu_si128((const __m128i *)dst);
                REPL3X(mmtmpa, r, 1, r, 2, mmsrca);
                REPL3X(mmtmp, r, 1, r, 2, mmsrc);
                res = muladd_src(mmdst, mmtmp, mmtmpa);
                res = alphamask(mmdst, res, mmtmpa);
                _mm_storeu_si128((__m128i *)dst, res);
            }
            dst += 16;
        }
        for(;x<w;x++){
            if(srca[x]){
		dst[0]=((dst[0]*srca[x])>>8)+src[x];
		dst[1]=((dst[1]*srca[x])>>8)+src[x];
		dst[2]=((dst[2]*srca[x])>>8)+src[x];
            }
            dst+=3; // 24bpp
        }
#else /* HAVE_SSE2 */
    for(x=0;x<w;x++){
        if(srca[x]){
	    __asm__ volatile(
		"movzbl (%0), %%ecx\n\t"
		"movzbl 1(%0), %%eax\n\t"

		"imull %1, %%ecx\n\t"
		"imull %1, %%eax\n\t"

		"addl %2, %%ecx\n\t"
		"addl %2, %%eax\n\t"

		"movb %%ch, (%0)\n\t"
		"movb %%ah, 1(%0)\n\t"

                "movzbl 2(%0), %%eax\n\t"
		"imull %1, %%eax\n\t"
		"addl %2, %%eax\n\t"
		"movb %%ah, 2(%0)\n\t"
		:
		:"D" (dst),
		 "r" ((unsigned)srca[x]),
		 "r" (((unsigned)src[x])<<8)
		:"%eax", "%ecx"
		);
            }
	    dst += 3;
        }
#endif /* !HAVE_MMX */
#else /*non x86 arch or x86_64 with MMX and SSE2 disabled */
        for(x=0;x<w;x++){
            if(srca[x]){
#ifdef FAST_OSD
		dst[0]=dst[1]=dst[2]=src[x];
#else
		dst[0]=((dst[0]*srca[x])>>8)+src[x];
		dst[1]=((dst[1]*srca[x])>>8)+src[x];
		dst[2]=((dst[2]*srca[x])>>8)+src[x];
#endif
            }
            dst+=3; // 24bpp
        }
#endif /* arch_x86 */
        src+=srcstride;
        srca+=srcstride;
        dstbase+=dststride;
    }
#if HAVE_MMX
	__asm__ volatile(EMMS:::"memory");
#endif
    return;
}

#if HAVE_SSE2
ATTR_TARGET_SSE2
#endif
static inline void RENAME(vo_draw_alpha_rgb32)(int w,int h, unsigned char* src, unsigned char *srca, int srcstride, unsigned char* dstbase,int dststride){
    int y;
#if HAVE_BIGENDIAN
    dstbase++;
#endif
#if HAVE_MMX
#if HAVE_AMD3DNOW
    __asm__ volatile(
        "pxor %%mm7, %%mm7\n\t"
        "pcmpeqb %%mm6, %%mm6\n\t" // F..F
        ::);
#else /* HAVE_AMD3DNOW */
    __asm__ volatile(
        "pxor %%mm7, %%mm7\n\t"
        "pcmpeqb %%mm5, %%mm5\n\t" // F..F
        "movq %%mm5, %%mm4\n\t"
        "psllw $8, %%mm5\n\t" //FF00FF00FF00
        "psrlw $8, %%mm4\n\t" //00FF00FF00FF
        ::);
#endif /* HAVE_AMD3DNOW */
#endif /* HAVE_MMX */
    for(y=0;y<h;y++){
        register int x;
#if ARCH_X86 && (!ARCH_X86_64 || HAVE_MMX || HAVE_SSE2)
#if HAVE_MMX
#if HAVE_AMD3DNOW
    __asm__ volatile(
	PREFETCHW" %0\n\t"
	PREFETCH" %1\n\t"
	PREFETCH" %2\n\t"
	::"m"(*dstbase),"m"(*srca),"m"(*src):"memory");
    for(x=0;x<w;x+=2){
     if(srca[x] || srca[x+1])
	__asm__ volatile(
		PREFETCHW" 32%0\n\t"
		PREFETCH" 32%1\n\t"
		PREFETCH" 32%2\n\t"
		"movq	%0, %%mm0\n\t" // dstbase
		"movq	%%mm0, %%mm1\n\t"
		"punpcklbw %%mm7, %%mm0\n\t"
		"punpckhbw %%mm7, %%mm1\n\t"
		"movd	%1, %%mm2\n\t" // srca ABCD0000
		"paddb	%%mm6, %%mm2\n\t"
		"punpcklbw %%mm2, %%mm2\n\t" // srca AABBCCDD
		"punpcklbw %%mm2, %%mm2\n\t" // srca AAAABBBB
		"movq	%%mm2, %%mm3\n\t"
		"punpcklbw %%mm7, %%mm2\n\t" // srca 0A0A0A0A
		"punpckhbw %%mm7, %%mm3\n\t" // srca 0B0B0B0B
		"pmullw	%%mm2, %%mm0\n\t"
		"pmullw	%%mm3, %%mm1\n\t"
		"psrlw	$8, %%mm0\n\t"
		"psrlw	$8, %%mm1\n\t"
		"packuswb %%mm1, %%mm0\n\t"
		"movd %2, %%mm2	\n\t" // src ABCD0000
		"punpcklbw %%mm2, %%mm2\n\t" // src AABBCCDD
		"punpcklbw %%mm2, %%mm2\n\t" // src AAAABBBB
		"paddb	%%mm2, %%mm0\n\t"
		"movq	%%mm0, %0\n\t"
		:: "m" (dstbase[4*x]), "m" (srca[x]), "m" (src[x]));
	}
#else //this is faster for intels crap
    __asm__ volatile(
	PREFETCHW" %0\n\t"
	PREFETCH" %1\n\t"
	PREFETCH" %2\n\t"
	::"m"(*dstbase),"m"(*srca),"m"(*src):"memory");
    for(x=0;x<w;x+=4){
	__asm__ volatile(
		"movl %1, %%eax\n\t"
		"orl %%eax, %%eax\n\t"
		" jz 1f\n\t"
		PREFETCHW" 32%0\n\t"
		PREFETCH" 32%1\n\t"
		PREFETCH" 32%2\n\t"
		"movq	%0, %%mm0\n\t" // dstbase
		"movq	%%mm0, %%mm1\n\t"
		"pand %%mm4, %%mm0\n\t" 	//0R0B0R0B
		"psrlw $8, %%mm1\n\t"		//0?0G0?0G
		"movd	%%eax, %%mm2\n\t" 	//srca 0000DCBA
		"paddb	%3, %%mm2\n\t"
		"punpcklbw %%mm2, %%mm2\n\t"	//srca DDCCBBAA
		"movq %%mm2, %%mm3\n\t"
		"punpcklbw %%mm7, %%mm2\n\t"	//srca 0B0B0A0A
		"pmullw	%%mm2, %%mm0\n\t"
		"pmullw	%%mm2, %%mm1\n\t"
		"psrlw	$8, %%mm0\n\t"
		"pand %%mm5, %%mm1\n\t"
		"por %%mm1, %%mm0\n\t"
		"movd %2, %%mm2	\n\t"		//src 0000DCBA
		"punpcklbw %%mm2, %%mm2\n\t" 	//src DDCCBBAA
		"movq %%mm2, %%mm6\n\t"
		"punpcklbw %%mm2, %%mm2\n\t"	//src BBBBAAAA
		"paddb	%%mm2, %%mm0\n\t"
		"movq	%%mm0, %0\n\t"

		"movq	8%0, %%mm0\n\t" // dstbase
		"movq	%%mm0, %%mm1\n\t"
		"pand %%mm4, %%mm0\n\t" 	//0R0B0R0B
		"psrlw $8, %%mm1\n\t"		//0?0G0?0G
		"punpckhbw %%mm7, %%mm3\n\t"	//srca 0D0D0C0C
		"pmullw	%%mm3, %%mm0\n\t"
		"pmullw	%%mm3, %%mm1\n\t"
		"psrlw	$8, %%mm0\n\t"
		"pand %%mm5, %%mm1\n\t"
		"por %%mm1, %%mm0\n\t"
		"punpckhbw %%mm6, %%mm6\n\t"	//src DDDDCCCC
		"paddb	%%mm6, %%mm0\n\t"
		"movq	%%mm0, 8%0\n\t"
		"1:\n\t"
		:: "m" (dstbase[4*x]), "m" (srca[x]), "m" (src[x]), "m" (bFF)
		: "%eax");
	}
#endif
#elif HAVE_SSE2
        __m128i zero = _mm_setzero_si128();
        __m128i mmsrca = _mm_setzero_si128();
        for(x=0;x<w;x+=4){
            __m128i mmsrc, mmdst, mmsrcexp, mmsrcaexp, res;
            if ((x & 15) == 0) {
                int alpha;
                mmsrca = _mm_load_si128((const __m128i *)(srca + x));
                alpha = _mm_movemask_epi8(_mm_cmpeq_epi8(mmsrca, zero));
                if (alpha == 0xffff) { x += 12; continue; }
                mmsrc = _mm_load_si128((const __m128i *)(src + x));
            }

            mmdst = _mm_loadu_si128((const __m128i *)(dstbase + 4*x));

            mmsrcaexp = _mm_unpacklo_epi8(mmsrca, mmsrca);
            mmsrcaexp = _mm_unpacklo_epi8(mmsrcaexp, mmsrcaexp);
            mmsrcexp = _mm_unpacklo_epi8(mmsrc, mmsrc);
            mmsrcexp = _mm_unpacklo_epi8(mmsrcexp, mmsrcexp);

            res = muladd_src(mmdst, mmsrcexp, mmsrcaexp);

            res = alphamask(mmdst, res, mmsrcaexp);
            _mm_storeu_si128((__m128i *)(dstbase + 4*x), res);

            mmsrca = _mm_srli_si128(mmsrca, 4);
            mmsrc = _mm_srli_si128(mmsrc, 4);
        }
        for(;x<w;x++){
            if(srca[x]){
		dstbase[4*x+0]=((dstbase[4*x+0]*srca[x])>>8)+src[x];
		dstbase[4*x+1]=((dstbase[4*x+1]*srca[x])>>8)+src[x];
		dstbase[4*x+2]=((dstbase[4*x+2]*srca[x])>>8)+src[x];
	    }
        }
#else /* HAVE_SSE2 */
    for(x=0;x<w;x++){
        if(srca[x]){
	    __asm__ volatile(
		"movzbl (%0), %%ecx\n\t"
		"movzbl 1(%0), %%eax\n\t"
		"movzbl 2(%0), %%edx\n\t"

		"imull %1, %%ecx\n\t"
		"imull %1, %%eax\n\t"
		"imull %1, %%edx\n\t"

 		"addl %2, %%ecx\n\t"
		"addl %2, %%eax\n\t"
		"addl %2, %%edx\n\t"

		"movb %%ch, (%0)\n\t"
		"movb %%ah, 1(%0)\n\t"
		"movb %%dh, 2(%0)\n\t"

		:
		:"r" (&dstbase[4*x]),
		 "r" ((unsigned)srca[x]),
		 "r" (((unsigned)src[x])<<8)
		:"%eax", "%ecx", "%edx"
		);
            }
        }
#endif /* HAVE_MMX */
#else /*non x86 arch or x86_64 with MMX disabled */
        for(x=0;x<w;x++){
            if(srca[x]){
#ifdef FAST_OSD
		dstbase[4*x+0]=dstbase[4*x+1]=dstbase[4*x+2]=src[x];
#else
		dstbase[4*x+0]=((dstbase[4*x+0]*srca[x])>>8)+src[x];
		dstbase[4*x+1]=((dstbase[4*x+1]*srca[x])>>8)+src[x];
		dstbase[4*x+2]=((dstbase[4*x+2]*srca[x])>>8)+src[x];
#endif
            }
        }
#endif /* arch_x86 */
        src+=srcstride;
        srca+=srcstride;
        dstbase+=dststride;
    }
#if HAVE_MMX
	__asm__ volatile(EMMS:::"memory");
#endif
    return;
}
