// Copyright (c) 2002 Tom Barry.  All rights reserved.
//      trbarry@trbarry.com
// Requires Avisynth source code to compile for Avisynth
// Avisynth Copyright 2000 Ben Rudiak-Gould.
//      http://www.math.berkeley.edu/~benrg/avisynth.html
/////////////////////////////////////////////////////////////////////////////
//
//	This file is subject to the terms of the GNU General Public License as
//	published by the Free Software Foundation.  A copy of this license is
//	included with this software distribution in the file COPYING.  If you
//	do not have a copy, you may obtain a copy by writing to the Free
//	Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
//	This software 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
//  
//  Also, this program is "Philanthropy-Ware".  That is, if you like it and 
//  feel the need to reward or inspire the author then please feel free (but
//  not obligated) to consider joining or donating to the Electronic Frontier
//  Foundation. This will help keep cyber space free of barbed wire and bullsh*t.  
//
/////////////////////////////////////////////////////////////////////////////
// Change Log
//
// Date          Version  Developer      Changes
//
// 02 Apr 2003   1.0.2.0  Tom Barry      Fix right margin OBO err, smooth top & bottom

// 02 Apr 2003   1.0.0.0  Tom Barry      New Release
//
/////////////////////////////////////////////////////////////////////////////
// CVS Log
//
//
/////////////////////////////////////////////////////////////////////////////
#include "windows.h"
#include "internal.h"


class FillMargins : public GenericVideoFilter {
    int  Max_Frames;
	bool SSEMMXenabled;

// Only for YUY2
    BYTE* dstp;
    int src_pitch;
    int dst_pitch;
    int row_size;
    int RowPixels;
    int height;
    int FldHeight;
	int fLeft;
	int fTop;
	int fRight;
	int fBottom;
// Only for YV12


BOOL	FillMargins::FillMarginsYV12(int dst_pit, int row_size, BYTE* dstp, int FldHeight,
			int fLeftW, int fTopW, int fRightW, int fBottomW);

public:
	int FillMargins::FillLine(BYTE* pDest, BYTE* pSrc, int row_size);

    FillMargins(PClip _child, int _fLeft, int _fTop, int _fRight, int _fBottom, 
		IScriptEnvironment* env) : GenericVideoFilter(_child)
    {
		fLeft = _fLeft;
		fTop = _fTop;
		fRight = _fRight;
		fBottom = _fBottom;
		if ((env->GetCPUFlags() & CPUF_INTEGER_SSE) == 0)
		{
			env->ThrowError("FillMargins: Only SSEMMX CPU's currently supported");
		}

	    if (!vi.IsYV12())
		{
			env->ThrowError("FillMargins: Supports YV12 color format only");
		}
    }
    PVideoFrame __stdcall GetFrame(int inFrame, IScriptEnvironment* env);
};


PVideoFrame __stdcall FillMargins::GetFrame(int inFrame, IScriptEnvironment* env) 
{
	PVideoFrame src = child->GetFrame(inFrame, env);
	env->MakeWritable(&src);
	BYTE* dstpY;
	BYTE* dstpV;   
	BYTE* dstpU;
	int dst_pitchY;
	int dst_pitchUV;
	int row_sizeY;   // Could also be PLANAR_Y_ALIGNED which would return a mod16 row_size   
	int row_sizeUV;  // Could also be PLANAR_U_ALIGNED which would return a mod8 row_size   
	int heightY;
	int heightUV;

	dstpY = src->GetWritePtr(PLANAR_Y);
	dstpV = src->GetWritePtr(PLANAR_V);   
	dstpU = src->GetWritePtr(PLANAR_U);

	dst_pitchY = src->GetPitch(PLANAR_Y);
	dst_pitchUV = src->GetPitch(PLANAR_U);

	row_sizeY = src->GetRowSize(PLANAR_Y);   // Could also be PLANAR_Y_ALIGNED which would return a mod16 row_size   
	row_sizeUV = src->GetRowSize(PLANAR_U);  // Could also be PLANAR_U_ALIGNED which would return a mod8 row_size   

	heightY = src->GetHeight(PLANAR_Y);
	heightUV = src->GetHeight(PLANAR_U);

	FillMarginsYV12(dst_pitchY,	 row_sizeY,  dstpY, heightY,  fLeft,   fTop,   fRight,   fBottom);					

	FillMarginsYV12(dst_pitchUV, row_sizeUV, dstpU, heightUV, fLeft/2, fTop/2, fRight/2, fBottom/2);					

	FillMarginsYV12(dst_pitchUV, row_sizeUV, dstpV, heightUV, fLeft/2, fTop/2, fRight/2, fBottom/2);					

	__asm emms;
	return src;
}


BOOL	FillMargins::FillMarginsYV12(int dst_pit, int row_size, BYTE* dstp, int FldHeight,
			int fLeftW, int fTopW, int fRightW, int fBottomW)	
{

BYTE* pDestT = dstp;										// top line
BYTE* pDestM = dstp + dst_pit * fTopW;						// top non-filled line
BYTE* pDestB = dstp + dst_pit * (FldHeight - fBottomW - 1);	// bottom non-filled line
BYTE* pDestE = dstp + dst_pit * (FldHeight - 1);			// last line to fill (if any)
int	  Roffs = (fRightW)
			? row_size - fRightW  
			: 0;											// offset to right margin
BYTE* pDestW;

	// first fill left and right margins of middle area
	for (pDestW=pDestM; pDestW <= pDestB; pDestW += dst_pit)	
	{
		__asm
		{	
		cld	
		mov		edi, pDestW
		mov		ecx, fLeftW
		mov		al,	 byte ptr[edi+ecx]
		rep stosb

		mov		edi, pDestW
		add		edi, Roffs
		mov		al,	 byte ptr[edi-1]
		mov		ecx, fRightW
		rep stosb
		}
	}

	// Fill top margin, if any, average left and right a bit now
	for (pDestW=pDestM - dst_pit;  pDestW >= pDestT;  pDestW -= dst_pit)
	{
		FillLine(pDestW, pDestW + dst_pit, row_size);
	}

	// fill bottom margin 
	for (pDestW=pDestB+dst_pit; pDestW <= pDestE; pDestW += dst_pit)
	{
		FillLine(pDestW, pDestW - dst_pit, row_size);
	}
	return 0;
}

__int64 AllOnes = 0x0101010101010101;

int FillMargins::FillLine(BYTE* pDest, BYTE* pSrc, int row_size)
{
	int ct = (row_size-2) >> 3;

	__asm
	{
		mov esi, pSrc				// src addr
		mov edi, pDest				// dest addr
		mov	ebx, row_size
		mov	ecx, ct

		mov		al, byte ptr[esi]	// move 1st byte
		mov		byte ptr[edi], al

		movq	mm0, qword ptr[esi+ebx-8]	
		movq	qword ptr[edi+ebx-8], mm0   // move last qword
		sub		ebx, ebx					// reset ebx to begin of line
		align 16
qloop:
		movq	mm0, qword ptr[esi+ebx]		// get left bytes
		movq    mm1, qword ptr[esi+ebx+1]	// get center bytes
		pavgb   mm0, qword ptr[esi+ebx+2]	// avg right bytes
		psubusb mm0, AllOnes				// adjust for rounding
		pavgb    mm1, mm0					// avg center bytes
		pavgb    mm0, mm1					// avg less center bytes
		movq	qword ptr[edi+ebx+1], mm0	// and save in center of dest
		add		ebx, 8
		loop	qloop
		
		emms
	}
	return 0;
}
	
AVSValue __cdecl Create_FillMargins(AVSValue args, void* user_data,
IScriptEnvironment* env) 
{
    return new FillMargins(args[0].AsClip(), args[1].AsInt(), args[2].AsInt(),
		args[3].AsInt(), args[4].AsInt(), env);
}


extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit2(IScriptEnvironment* env) {
    env->AddFunction("FillMargins", "ciiii", Create_FillMargins, 0);
    return "`FillMargins' Remove stray dots, dering";
}

