///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004 Intel Corporation 
// All rights reserved. 
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met: 
//
// * Redistributions of source code must retain the above copyright notice, 
// this list of conditions and the following disclaimer. 
// * Redistributions in binary form must reproduce the above copyright notice, 
// this list of conditions and the following disclaimer in the documentation 
// and/or other materials provided with the distribution. 
// * Neither name of Intel Corporation nor the names of its contributors 
// may be used to endorse or promote products derived from this software 
// without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <ipp.h>

#include "pyramid.h"
#include "Image.h"
#include "localbuffer.h"

PyrGauss::PyrGauss()
{
}
PyrGauss::~PyrGauss()
{
}

int PyrGauss::PyrDown(Image8u* pSrc, Image8u* pDst)
{
	IppiSize dstSize = GetDestSizeDown(pSrc);

	if (dstSize.width > pDst->GetSize().width) return -1;
	if (dstSize.height > pDst->GetSize().height) return -1;

	int bufSize;
	IppStatus st;
	st = ippiPyrDownGetBufSize_Gauss5x5(
		pSrc->GetSize().width,
		ipp8u, pSrc->GetChannelCount(), &bufSize);
	if (st != 0) return -1;
	buf.SetMinAlloc(bufSize);
	Ipp8u* pBuffer = buf.Alloc_8u(bufSize);

	IppiSize srcSize = pSrc->GetSize();
	// Round source size to even
	srcSize.width &= ~1;
	srcSize.height &= ~1;

	switch (pDst->GetChannelCount())
	{
		case 1:
			st = ippiPyrDown_Gauss5x5_8u_C1R(
				pSrc->GetData(), pSrc->GetStride(),
				pDst->GetData(), pDst->GetStride(),
				srcSize, pBuffer);
			break;
		case 3:
			st = ippiPyrDown_Gauss5x5_8u_C3R(
				pSrc->GetData(), pSrc->GetStride(),
				pDst->GetData(), pDst->GetStride(),
				srcSize, pBuffer);
			break;
		default:
			buf.ReleaseAll();
			return -1;
	}

	buf.ReleaseAll();
	if (st != 0) return -1;
	else return 0;
}

int PyrGauss::PyrUp(Image8u* pSrc, Image8u* pDst)
{
	IppiSize dstSize = GetDestSizeUp(pSrc);

	if (dstSize.width > pDst->GetSize().width) return -1;
	if (dstSize.height > pDst->GetSize().height) return -1;

	int bufSize;
	IppStatus st;
	st = ippiPyrUpGetBufSize_Gauss5x5(
		pDst->GetSize().width,
		ipp8u, pDst->GetChannelCount(), &bufSize);
	if (st != 0) return -1;
	buf.SetMinAlloc(bufSize);
	Ipp8u* pBuffer = buf.Alloc_8u(bufSize);

	IppiSize srcSize = pSrc->GetSize();
	// Round source size to even
	srcSize.width &= ~1;
	srcSize.height &= ~1;

	switch (pDst->GetChannelCount())
	{
		case 1:
			st = ippiPyrUp_Gauss5x5_8u_C1R(
				pSrc->GetData(), pSrc->GetStride(),
				pDst->GetData(), pDst->GetStride(),
				srcSize, pBuffer);
			break;
		case 3:
			st = ippiPyrUp_Gauss5x5_8u_C3R(
				pSrc->GetData(), pSrc->GetStride(),
				pDst->GetData(), pDst->GetStride(),
				srcSize, pBuffer);
			break;
		default:
			buf.ReleaseAll();
			return -1;
	}

	buf.ReleaseAll();
	if (st != 0) return -1;
	else return 0;
}

IppiSize PyrGauss::GetDestSizeUp(Image8u* pSrc)
{
	IppiSize dstSize = { pSrc->GetSize().width*2,
		pSrc->GetSize().height*2 };
	return dstSize;
}

IppiSize PyrGauss::GetDestSizeDown(Image8u* pSrc)
{
	IppiSize dstSize = { pSrc->GetSize().width/2,
		pSrc->GetSize().height/2 };
	return dstSize;
}

PyrGauss Pyramid8u::pyr_;

Pyramid8u::Pyramid8u()
{
	int i;
	for (i=0; i<MaxLevels; i++)
		pImages[i] = 0;
}

Pyramid8u::~Pyramid8u()
{
	FreeAll_();
}

void Pyramid8u::FreeAll_()
{
	int i;
	for (i=1; i<MaxLevels; i++)
		if (pImages[i] != 0)
		{
			delete pImages[i];
			pImages[i] = 0;
		}
}

void Pyramid8u::SetTopLevel(Image8u* pImage)
{
	pImages[0] = pImage;
	FreeAll_();
}

Image8u* Pyramid8u::GetLevel(int level)
{
	int i;
	if (level == 0) return pImages[0];
	if (level > MaxLevels) level = MaxLevels-1;
	for (i=1; i<=level; i++)
	{
		if (!pImages[i])
		{
			pImages[i] = new Image8u(
				pyr_.GetDestSizeDown(pImages[i-1]),
				pImages[i-1]->GetChannelCount());
			if ((pImages[i]->GetSize().width == 0) ||
				(pImages[i]->GetSize().height == 0))
				return 0;
			pyr_.PyrDown(pImages[i-1], pImages[i]);
		}
	}
	
	return pImages[level];
}
