#include <windows.h>		// FIXME...0AAARGH... cuando haya tiempo, definir estructuras propias de bmp y no usar las de w.
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include "MSurface.h"
#include "MFastFpu.h"
#include "MSystemFunctions.h"


#define max(a,b)            (((a) > (b)) ? (a) : (b))
#define min(a,b)            (((a) < (b)) ? (a) : (b))

#define POW2(X) ( ((X)&(-X))==X )

#ifndef DEBUG
#define DITHER
#endif


// Nombre      : MSurface::MSurface()
// Parametros  : No
// Retorno     : Nofad
// Descripcion : Constructor de la clase
MSurface::MSurface()
{
	m_bValid=false;
	m_pvSurface=NULL;
	m_bLocalSurfaceAllocation=false;
	m_bLocalPaletteAllocation=false;
	m_pcName[0]='\0';
}

// Nombre      : MSurface::~MSurface()
// Parametros  : No
// Retorno     : No
// Descripcion : Destructor de la clase
MSurface::~MSurface()
{
	Shutdown();
}

// Nombre      : MSurface::Init()
// Parametros  : MSurfaceDescriptor& sdSurfaceDescriptor,
//				void*		pvSurface,
//				MPalette*	m_pPalette
// Retorno     : 
// Descripcion : Inicializa las superficies... en caso de que los punteros pasados como parametro
// sean NULL, la superficie alocara la memoria necesaria para tenerla
int	MSurface::Init(const MSurfaceDescriptor& sdSurfaceDescriptor,
		 void*		pvSurface,
		MPalette*	pPalette ,
		char*		pcName)
{
	Shutdown();

	strcpy(m_pcName, "INVALID");

	// Copiamos descriptor de superficie
	m_sdSurfaceDescriptor=sdSurfaceDescriptor;

	if (!pvSurface)
	{
		// Hay que alocar memoria para la superficie
		int iSurfaceSize=m_sdSurfaceDescriptor.m_uiWidth*
						 m_sdSurfaceDescriptor.m_uiHeight*
						 (m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3);
		m_pvSurface=(void*) new char[iSurfaceSize];
		if (!m_pvSurface)
		{
			return (-1);
		}

		// El stride es igual al ancho de linea porque trabajamos de forma lineal
		m_sdSurfaceDescriptor.m_uiByteStride=m_sdSurfaceDescriptor.m_uiWidth*
											 (m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3);

		m_bLocalSurfaceAllocation=true;
	}
	else
	{
		m_pvSurface=pvSurface;
	}

	// Vemos si necesitamos paleta
	if (m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP==8)
	{
		if (!pPalette && m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP==8)
		{
			// Creamos nueva paleta
			m_pPalette=new MPalette;
			if (!m_pPalette)
			{
				return (-1);
			}

			// Asignamos por defecto paleta de 24 bits almacenada en 32
			m_pPalette->m_pfPixelFormat=CommonPixelDescriptors[E__RGB32__888];

			m_bLocalPaletteAllocation=true;
		}
		else
		{
			// Asignamos paleta pasada por parametro
			m_pPalette=pPalette;			
		}
	}
	else
	{
		m_pPalette=NULL;
	}

	m_bValid=true;

	strcpy(m_pcName, "");

	return (0);
}

// Nombre      : MSurface::Shutdown()
// Parametros  : No
// Retorno     : No
// Descripcion : Chapa la superficie
int	MSurface::Shutdown()
{
	// Si hay alocaciones locales las liberamos
	if (m_bLocalSurfaceAllocation)
	{
		if (m_pvSurface)
		{
			delete[] m_pvSurface; m_pvSurface=NULL;
		}
	}

	if (m_bLocalPaletteAllocation)
	{
		if (m_pPalette)
		{
			delete[] m_pPalette; m_pPalette=NULL;
		}
	}

	strcpy(m_pcName,"INVALID");
	return (0);
}

// Nombre      : MSurface::GetSurfacePointer()
// Parametros  : No
// Retorno     : void*
// Descripcion : Devuelve el puntero a la superficie
void* MSurface::GetSurfacePointer() const
{
	return (m_pvSurface);
}

// Nombre      : MSurface::GetSurfacePointer()
// Parametros  : const int& x, const int& y
// Retorno     : void*
// Descripcion : Devuelve puntero al pixel de coordenadas especificadas como parametro
void* MSurface::GetSurfacePointer(const int& x, const int& y) const
{
	return ((void*)((char*)m_pvSurface+
			x*(m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3)+
			y*(m_sdSurfaceDescriptor.m_uiByteStride)));
}

// Nombre      : MSurface::GetPalette()
// Parametros  : No
// Retorno     : MPalette*
// Descripcion : Devuelve la paleta de la superficie
MPalette* MSurface::GetPalette()
{
	return (m_pPalette);
}

// Nombre      : MSurface::ConvertFrom()
// Parametros  : MSurface& sSurface
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Convierte la superficie pasada como parametro a la actual
__int64 mmx32_rgb888_mask = 0x00ffffff00ffffff;

__int64 mmx32_rgb565_b    = 0x000000f8000000f8;
__int64 mmx32_rgb565_g    = 0x0000fc000000fc00;
__int64 mmx32_rgb565_r    = 0x00f8000000f80000;

__int64 mmx32_rgb555_rb   = 0x00f800f800f800f8;
__int64 mmx32_rgb555_g    = 0x0000f8000000f800;
__int64 mmx32_rgb555_mul  = 0x2000000820000008;
__int64 mmx32_bgr555_mul  = 0x0008200000082000;


int	MSurface::ConvertFrom(MSurface& sSurface)
{
	unsigned		i,j;
	unsigned*		pScanLine;
	unsigned*		pTempPalette;
	unsigned char*	pConversionTable;
	unsigned char*  pSource;
	unsigned char*  pDestiny;

  //return (0);

	// Comprobamos que sean del mismo tamaņo
	if (m_sdSurfaceDescriptor.m_uiWidth!=sSurface.m_sdSurfaceDescriptor.m_uiWidth ||
		m_sdSurfaceDescriptor.m_uiHeight!=sSurface.m_sdSurfaceDescriptor.m_uiHeight)
	{
		// Hasta que hagamos conversiones de tamaņo... XD
		return (-1);
	}

	if (m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP==8 ||
		sSurface.m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP==8)
	{
		if (sSurface.m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP!=8 && 
			m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP==8)
		{
			// De momento no soportamos destino paleta y origen no paletizado... habra que currarselo... XD
			return (-1);
		}
		else
		{
			pTempPalette=new unsigned[256];
			if (!pTempPalette)
			{
				return (-1);
			}

			if (m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP==8)
			{
				// Destino tambien paletizado. Convertimos paleta y ya esta				
				// Convertimos paleta origen a ARGB32
				sSurface.m_pPalette->m_pfPixelFormat.ConvertARGBToARGB32(
					pTempPalette, sSurface.m_pPalette->m_uiPalette , 256);

				// Convertimos ARGB32 a paleta destino
				m_pPalette->m_pfPixelFormat.ConvertARGBFromARGB32(
					m_pPalette->m_uiPalette, pTempPalette, 256);

				// Copiamos la imagen tal cual
				for (i=0 ; i<m_sdSurfaceDescriptor.m_uiHeight ; i++)
				{
					memcpy(GetSurfacePointer(0, i), sSurface.GetSurfacePointer(0, i), m_sdSurfaceDescriptor.m_uiWidth);
				}
			}
			else
			{
				// Destino no paletizado. Convertimos paleta a formato pixel destino y copiamos indexando en tabla temporal
				pConversionTable=new unsigned char[256*(m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3)];

				// Convertimos paleta origen a ARGB32
				sSurface.m_pPalette->m_pfPixelFormat.ConvertARGBToARGB32(
					pTempPalette, sSurface.m_pPalette->m_uiPalette , 256);

				// Creamos tabla de conversion
				m_sdSurfaceDescriptor.m_pfPixelFormat.ConvertARGBFromARGB32(
					pConversionTable, pTempPalette, 256);						

				// Copiamos la imagen utilizando la tabla de conversion
				int iDestinyBytesPerPixel=m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3;
				for (i=0 ; i<m_sdSurfaceDescriptor.m_uiHeight ; i++)
				{
					pSource =(unsigned char*)sSurface.GetSurfacePointer(0, i);
					pDestiny=(unsigned char*)GetSurfacePointer(0, i);
					
					for (j=0 ; j<m_sdSurfaceDescriptor.m_uiWidth ; j++)
					{
						memcpy(pDestiny, &(pConversionTable[(*pSource)*iDestinyBytesPerPixel]), iDestinyBytesPerPixel);

						// Incrementos
						pSource++;
						pDestiny+=iDestinyBytesPerPixel;
					}
					
				}


				delete[] pConversionTable;
			}
						
			delete[] pTempPalette;
		}
	}
	else
	{		
    if (m_sdSurfaceDescriptor.m_pfPixelFormat==sSurface.m_sdSurfaceDescriptor.m_pfPixelFormat)
    {
      for (i=0 ; i<m_sdSurfaceDescriptor.m_uiHeight ; i++)
		  {
        memcpy(
          GetSurfacePointer(0, i),
          sSurface.GetSurfacePointer(0, i),
          m_sdSurfaceDescriptor.m_uiWidth*(m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3));

      }
    }
    else
    if (m_sdSurfaceDescriptor.m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__565] && sSurface.m_sdSurfaceDescriptor.m_pfPixelFormat==CommonPixelDescriptors[E__RGB32__888])
    {
      for(unsigned uY=0;uY<m_sdSurfaceDescriptor.m_uiHeight;uY++)
      {
        unsigned        *pOrig  = (unsigned      *)sSurface.GetSurfacePointer(0,uY);
        unsigned short  *pDest  = (unsigned short*)         GetSurfacePointer(0,uY);
        unsigned         uAncho = m_sdSurfaceDescriptor.m_uiWidth;
        /*
        for(unsigned uX=0;uX<m_sdSurfaceDescriptor.m_uiWidth;uX+=2)
        {
          unsigned uDest, uTmp0, uTmp1;
          uTmp0 = pOrig[uX];
          uTmp1 = pOrig[uX+1];
          uDest  = (uTmp0&(0x1f<< 3))>>3;
          uDest |= (uTmp1&(0x1f<< 3))<<13;
          uDest |= (uTmp0&(0x3f<<10))>>5;
          uDest |= (uTmp1&(0x3f<<10))<<11;
          uDest |= (uTmp0&(0x1f<<19))>>8;
          uDest |= (uTmp1&(0x1f<<19))<<8;
          *(unsigned*)(&pDest[uX]) = uDest;
        }
        */
        __asm
        {
          mov ecx, uAncho
          mov edi, pDest
          mov esi, pOrig
          // set up masks
          movq mm5, mmx32_rgb565_b
          movq mm6, mmx32_rgb565_g
          movq mm7, mmx32_rgb565_r

          mov edx, ecx
          shr ecx, 2
          jnz L565_1
          jmp L565_2         // not necessary at the moment, but doesn't hurt (much)

        L565_1:
          movq mm0, [esi]         //; argb
          movq mm1, mm0           //; argb
          pand mm0, mm6           //; 00g0
          movq mm3, mm1           //; argb
          pand mm1, mm5           //; 000b
          pand mm3, mm7           //; 0r00
          pslld mm1, 2            //; 0 0 000000bb bbb00000
          por mm0, mm1            //; 0 0 ggggggbb bbb00000
          psrld mm0, 5            //; 0 0 00000ggg gggbbbbb

          movq mm4, [esi+8]       //; argb
          movq mm2, mm4           //; argb
          pand mm4, mm6           //; 00g0
          movq mm1, mm2           //; argb
          pand mm2, mm5           //; 000b
          pand mm1, mm7           //; 0r00
          pslld mm2, 2            //; 0 0 000000bb bbb00000
          por mm4, mm2            //; 0 0 ggggggbb bbb00000
          psrld mm4, 5            //; 0 0 00000ggg gggbbbbb

          packuswb mm3, mm1       //; R 0 r 0
          packssdw mm0, mm4       //; as above.. ish
          por mm0, mm3            //; done.
          movq [edi], mm0

          add esi, 16
          add edi, 8
          dec ecx
          jnz L565_1

        L565_2:
          mov ecx, edx
          and ecx, 3
          jz L565_4
        L565_3:
          mov al, [esi]
          mov bh, [esi+1]
          mov ah, [esi+2]
          shr al, 3
          and eax, 0F81Fh            //; BYTE?
          shr ebx, 5
          and ebx, 07E0h             //; BYTE?
          add eax, ebx
          mov [edi], al
          mov [edi+1], ah
          add esi,  4
          add edi,  2
          dec ecx
          jnz L565_3

          L565_4:
          emms
        }
      }
    }
    else
    if (m_sdSurfaceDescriptor.m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__555] && sSurface.m_sdSurfaceDescriptor.m_pfPixelFormat==CommonPixelDescriptors[E__RGB32__888])
    {
      for(unsigned uY=0;uY<m_sdSurfaceDescriptor.m_uiHeight;uY++)
      {
        unsigned        *pOrig  = (unsigned      *)sSurface.GetSurfacePointer(0,uY);
        unsigned short  *pDest  = (unsigned short*)         GetSurfacePointer(0,uY);
        unsigned         uAncho = m_sdSurfaceDescriptor.m_uiWidth;
        /*
        for(unsigned uX=0;uX<m_sdSurfaceDescriptor.m_uiWidth;uX+=2)
        {
          unsigned uDest, uTmp0, uTmp1;
          uTmp0 = pOrig[uX];
          uTmp1 = pOrig[uX+1];
          uDest  = (uTmp0&(0x1f<< 3))>>3;
          uDest |= (uTmp1&(0x1f<< 3))<<13;
          uDest |= (uTmp0&(0x1f<<11))>>6;
          uDest |= (uTmp1&(0x1f<<11))<<10;
          uDest |= (uTmp0&(0x1f<<19))>>8;
          uDest |= (uTmp1&(0x1f<<19))<<8;
          *(unsigned*)(&pDest[uX]) = uDest;
        }
        */
        __asm
        {
          mov ecx, uAncho
          mov edi, pDest
          mov esi, pOrig
          movq mm5, mmx32_rgb565_b
          movq mm6, mmx32_rgb565_g
          movq mm7, mmx32_rgb565_r

          mov edx, ecx
          shr ecx, 2
          jnz L555_1
          jmp L555_2         // not necessary at the moment, but doesn't hurt (much)

        L555_1:
          movq mm0, [esi]         //; argb
          movq mm1, mm0           //; argb
          pand mm0, mm6           //; 00g0
          movq mm3, mm1           //; argb
          pand mm1, mm5           //; 000b
          pand mm3, mm7           //; 0r00
          pslld mm1, 2            //; 0 0 000000bb bbb00000
          por mm0, mm1            //; 0 0 ggggggbb bbb00000
          psrld mm0, 5            //; 0 0 00000ggg gggbbbbb

          movq mm4, [esi+8]       //; argb
          movq mm2, mm4           //; argb
          pand mm4, mm6           //; 00g0
          movq mm1, mm2           //; argb
          pand mm2, mm5           //; 000b
          pand mm1, mm7           //; 0r00
          pslld mm2, 2            //; 0 0 000000bb bbb00000
          por mm4, mm2            //; 0 0 ggggggbb bbb00000
          psrld mm4, 5            //; 0 0 00000ggg gggbbbbb

          packuswb mm3, mm1       //; R 0 r 0
          packssdw mm0, mm4       //; as above.. ish
          por mm0, mm3            //; done.
          movq [edi], mm0

          add esi, 16
          add edi, 8
          dec ecx
          jnz L555_1

        L555_2:
          mov ecx, edx
          and ecx, 3
          jz L555_4
        L555_3:
          mov al, [esi]
          mov bh, [esi+1]
          mov ah, [esi+2]
          shr al, 3
          and eax, 0F81Fh            //; BYTE?
          shr ebx, 5
          and ebx, 07E0h             //; BYTE?
          add eax, ebx
          mov [edi], al
          mov [edi+1], ah
          add esi, 4
          add edi, 2
          dec ecx
          jnz L555_3

        L555_4:
          emms
        }
      }
    }
    else
    {
		  // Alocamos memoria para ARGB32 de una linea
		  pScanLine=new unsigned[m_sdSurfaceDescriptor.m_uiWidth];
		  if (!pScanLine) 
		  {
			  return (-1);
		  }

		  // Tamaņo igual y sin paletas. Hacemos conversiones RGB
		  for (i=0 ; i<m_sdSurfaceDescriptor.m_uiHeight ; i++)
		  {
			  // Convertimos a ARGB32 el origen 
        sSurface.m_sdSurfaceDescriptor.m_pfPixelFormat.ConvertARGBToARGB32(
				  pScanLine, sSurface.GetSurfacePointer(0, i) , sSurface.m_sdSurfaceDescriptor.m_uiWidth);

			  // Convertimos desde scanline a local
			  m_sdSurfaceDescriptor.m_pfPixelFormat.ConvertARGBFromARGB32(
				  GetSurfacePointer(0, i)	, pScanLine, m_sdSurfaceDescriptor.m_uiWidth);						        
		  }

		  // Borramos memoria alocada
		  delete[] pScanLine;
    }
	}

	return (0);
}

// Nombre      : MSurface::GetSurfaceDescriptor()
// Parametros  : No
// Retorno     : MSurfaceDescriptor
// Descripcion : Devuelve el descriptor de la superficie
MSurfaceDescriptor	MSurface::GetSurfaceDescriptor()
{
	return (m_sdSurfaceDescriptor);
}

// Nombre      : MSurface::GetPSurfaceDescriptor()
// Parametros  : No
// Retorno     : MSurfaceDescriptor*
// Descripcion : Devuelve puntero al miembro descriptor de superficie. No Modificar
MSurfaceDescriptor*	MSurface::GetPSurfaceDescriptor()
{
	return (&m_sdSurfaceDescriptor);
}

// Nombre      : ROW()
// Parametros  : unsigned* pBuffer, float xStart, float xEnd
// Retorno     : unsigned con ARGB32
// Descripcion : Devuelve la media de color de la ristra de pixeles pasada como parametro
unsigned ROW(unsigned* pBuffer, float xStart, float xEnd)
{
  float r,g,b,a;
  int   i, iStart, iEnd;
  float fSampleArea=0.0f;
  float fWeight;

  assert(xStart<xEnd);

  r=0.0f; g=0.0f; b=0.0f; a=0.0f;
  iStart=MFPU_ftol(min(MFPU_fceil(xStart), xEnd  ));
  iEnd  =MFPU_ftol(max(MFPU_ftol(xEnd)   , xStart));
  assert(iEnd>=iStart);
  

  // Pre
  fWeight=min(xEnd, float(MFPU_fceil(xStart)))-xStart;
  if (fWeight!=0.0f)
  {
    a+=fWeight*MFPU_ltof(((pBuffer[iStart-1]) & 0xFF000000)>>24);
		r+=fWeight*MFPU_ltof(((pBuffer[iStart-1]) & 0x00FF0000)>>16);
		g+=fWeight*MFPU_ltof(((pBuffer[iStart-1]) & 0x0000FF00)>> 8);
		b+=fWeight*MFPU_ltof(((pBuffer[iStart-1]) & 0x000000FF)    );					
    fSampleArea+=fWeight;
  }

  for (i=iStart ; i<iEnd ; i++)
  {
    a+=MFPU_ltof(((pBuffer[i]) & 0xFF000000)>>24);
		r+=MFPU_ltof(((pBuffer[i]) & 0x00FF0000)>>16);
		g+=MFPU_ltof(((pBuffer[i]) & 0x0000FF00)>> 8);
		b+=MFPU_ltof(((pBuffer[i]) & 0x000000FF)    );					
  }
  fSampleArea+=MFPU_ltof(iEnd-iStart);


  // Trail
  fWeight=xEnd-max(MFPU_ltof(int(iEnd)), xStart);
  if (fWeight!=0.0f)
  {
    a+=fWeight*MFPU_ltof(((pBuffer[iEnd]) & 0xFF000000)>>24);
		r+=fWeight*MFPU_ltof(((pBuffer[iEnd]) & 0x00FF0000)>>16);
		g+=fWeight*MFPU_ltof(((pBuffer[iEnd]) & 0x0000FF00)>> 8);
		b+=fWeight*MFPU_ltof(((pBuffer[iEnd]) & 0x000000FF)    );					
    fSampleArea+=fWeight;
  }

  a/=fSampleArea; r/=fSampleArea; g/=fSampleArea; b/=fSampleArea;
			  
	return ((MFPU_ftol(a)<<24) | (MFPU_ftol(r)<<16) |(MFPU_ftol(g)<<8) | MFPU_ftol(b));
}

// Nombre      : COLUMN()
// Parametros  : unsigned* pBuffer, unsigned uJump, float yStart, float yEnd 
// Retorno     : unsigned 
// Descripcion : Devuelve la media de la columna pasada como parametro. uJump es la distancia
//               en pixels de una linea a otra. Posible ERROR, uJump deberia estar en bytes 
unsigned COLUMN(unsigned* pBuffer, unsigned uJump, float yStart, float yEnd)
{
  float    r,g,b,a;
  int      i, iStart, iEnd;
  float    fSampleArea=0.0f;
  float    fWeight;
  unsigned uPixel;

  assert(yStart<yEnd);

  r=0.0f; g=0.0f; b=0.0f; a=0.0f;
  iStart=MFPU_ftol(min(MFPU_fceil(yStart), yEnd  ));
  iEnd  =MFPU_ftol(max(MFPU_ftol(yEnd)  , yStart));
  assert(iEnd>=iStart);



  // Pre
  fWeight=min(yEnd, float(MFPU_fceil(yStart)))-yStart;
  if (fWeight!=0.0f)
  {
    uPixel=pBuffer[uJump*MFPU_ftol(yStart)];  
    a+=fWeight*MFPU_ltof(((uPixel) & 0xFF000000)>>24);
		r+=fWeight*MFPU_ltof(((uPixel) & 0x00FF0000)>>16);
		g+=fWeight*MFPU_ltof(((uPixel) & 0x0000FF00)>> 8);
		b+=fWeight*MFPU_ltof(((uPixel) & 0x000000FF)    );					
    fSampleArea+=fWeight;
  }

  for (i=iStart ; i<iEnd ; i++)
  {
    unsigned uPixel=pBuffer[uJump*i];
    a+=MFPU_ltof(((uPixel) & 0xFF000000)>>24);
		r+=MFPU_ltof(((uPixel) & 0x00FF0000)>>16);
		g+=MFPU_ltof(((uPixel) & 0x0000FF00)>> 8);
		b+=MFPU_ltof(((uPixel) & 0x000000FF)    );					
  }
  fSampleArea+=MFPU_ltof(iEnd-iStart);


  // Trail
  fWeight=yEnd-max(MFPU_ltof(int(iEnd)), yStart);
  if (fWeight!=0.0f)
  {
    uPixel=pBuffer[uJump*(iEnd)];    
    a+=fWeight*MFPU_ltof(((uPixel) & 0xFF000000)>>24);
		r+=fWeight*MFPU_ltof(((uPixel) & 0x00FF0000)>>16);
		g+=fWeight*MFPU_ltof(((uPixel) & 0x0000FF00)>> 8);
		b+=fWeight*MFPU_ltof(((uPixel) & 0x000000FF)    );					
    fSampleArea+=fWeight;
  }

  a/=fSampleArea; r/=fSampleArea; g/=fSampleArea; b/=fSampleArea;
			  
	return ((MFPU_ftol(a)<<24) | (MFPU_ftol(r)<<16) |(MFPU_ftol(g)<<8) | MFPU_ftol(b));
}


// Nombre      : MSurface::ScaleAndConvertFrom()
// Parametros  : MSurface& sSurface
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Convierte y escala una superficie. Es lenta pero buena... XD... no para usar en tiempo real
int	MSurface::ScaleAndConvertFrom(MSurface& sSurface)
{
	MSurface			      sARGB32Source;
	MSurfaceDescriptor	sdARGB32Source;
	MSurface			      sARGB32XYScale;
	MSurfaceDescriptor	sdARGB32XYScale;
  MSurface			      sARGB32XScale;
	MSurfaceDescriptor	sdARGB32XScale;
	unsigned i,j;

  if (sSurface.GetPSurfaceDescriptor()->m_uiWidth==GetPSurfaceDescriptor()->m_uiWidth &&
      sSurface.GetPSurfaceDescriptor()->m_uiHeight==GetPSurfaceDescriptor()->m_uiHeight)
  {
    return (ConvertFrom(sSurface));
  }
  

	// Inicializamos superficie temporal donde guardaremos la superficie ARGB32 que usaremos para escalar
	sdARGB32Source=sSurface.GetSurfaceDescriptor();
	sdARGB32Source.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];
	if (sARGB32Source.Init(sdARGB32Source, NULL, NULL)!=0)
	{
		return (-1);
	}
	// Convertimos la superficie original a ARGB32	
	sARGB32Source.ConvertFrom(sSurface);

  // Detectamos caso especial optimizado

  if ( POW2(int(sARGB32Source.GetPSurfaceDescriptor()->m_uiHeight)) &&
       POW2(int(sARGB32Source.GetPSurfaceDescriptor()->m_uiWidth))  &&
       (this->GetPSurfaceDescriptor()->m_uiWidth==
       sARGB32Source.GetPSurfaceDescriptor()->m_uiWidth/2)    &&
       (this->GetPSurfaceDescriptor()->m_uiHeight==
       sARGB32Source.GetPSurfaceDescriptor()->m_uiHeight/2)   &&
       ((this->GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__555]) ||
       (this->GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__565])) )
  {
    Generate32To16MipMap(sARGB32Source);
  }
  else
  {

      


    // Escalamos en x
	  sdARGB32XScale=GetSurfaceDescriptor();
    sdARGB32XScale.m_uiHeight=sSurface.GetSurfaceDescriptor().m_uiHeight;
	  sdARGB32XScale.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];
	  if (sARGB32XScale.Init(sdARGB32XScale, NULL, NULL)!=0)
	  {
		  return (-1);
	  }

    float dx,dy;
    int   x,y, xStep, yStep;
    dx=float(sARGB32Source.GetPSurfaceDescriptor()->m_uiWidth)/float(sARGB32XScale.GetPSurfaceDescriptor()->m_uiWidth);
    xStep=int(dx);
    dy=float(sARGB32Source.GetPSurfaceDescriptor()->m_uiHeight)/float(GetPSurfaceDescriptor()->m_uiHeight);
    yStep=int(dy);
    dx-=int(xStep);
    dy-=int(yStep);
    
    
    for (i=0 ; i<sARGB32XScale.GetPSurfaceDescriptor()->m_uiHeight ; i++)
	  {
      unsigned* puScanline=(unsigned *)sARGB32XScale.GetSurfacePointer(0,i);
      unsigned* puSource=(unsigned *)sARGB32Source.GetSurfacePointer(0,i);  
		 
      float fDDA=0.0f;

      x=0;
      for (j=0 ; j<sARGB32XScale.GetPSurfaceDescriptor()->m_uiWidth ; j++)
		  {        
        //puScanline[j]=puSource[x];  // ROW(x,x+dx+xStep)
        puScanline[j]=ROW(puSource, float(x+fDDA), dx+float(x+xStep+fDDA));


        fDDA+=dx;
        if (fDDA>1.0f)
        {
          fDDA-=1.0f;          
          x++;
        }
        x+=xStep;                               
      }
    }
		

    sdARGB32XYScale=GetSurfaceDescriptor();
    sdARGB32XYScale.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];
    if (sARGB32XYScale.Init(sdARGB32XYScale, NULL, NULL)!=0)
	  {
		  return (-1);
	  }

    
    y=0;      
    float fDDA=0.0f;

    unsigned uJump=(sARGB32XYScale.GetPSurfaceDescriptor()->m_uiByteStride/(sARGB32XYScale.GetPSurfaceDescriptor()->m_pfPixelFormat.m_uBPP>>3));
    for (i=0 ; i<sARGB32XYScale.GetPSurfaceDescriptor()->m_uiHeight ; i++)
	  {
      unsigned* puScanline=(unsigned *)sARGB32XYScale.GetSurfacePointer(0,i);
      unsigned* puSource=(unsigned *)  sARGB32XScale.GetSurfacePointer(0,0);  
		 
      
      for (j=0 ; j<sARGB32XYScale.GetPSurfaceDescriptor()->m_uiWidth ; j++)
		  {        
        //puScanline[j]=puSource[j];        
        puScanline[j]=COLUMN(puSource+j, uJump, float(fDDA+y), dy+float(y+yStep+fDDA));
      }

      fDDA+=dy;

      if (fDDA>1.0f)
      {
        fDDA-=1.0f;          
        y++;
      }
      y+=yStep;                               
    }

    ConvertFrom(sARGB32XYScale);
  }
  
	return (0);
}


void AssignAlpha(MSurface* pARGB32, MSurface* pRGB)
{
  unsigned uWidth =pARGB32->GetPSurfaceDescriptor()->m_uiWidth;
  unsigned uHeight=pARGB32->GetPSurfaceDescriptor()->m_uiHeight;


  unsigned i,j;
  for (j=0 ; j<uHeight ; j++)
  {
    unsigned*      pDestiny=(unsigned*)pARGB32->GetSurfacePointer(0, j);
    unsigned char* pSource =(unsigned char*)pRGB->GetSurfacePointer(0, j);

    for (i=0 ; i<uWidth ; i++)
    {
      *pDestiny&=0x00FFFFFF;
      *pDestiny|=int(*pSource)<<24;

      pDestiny++;
      pSource+=3;
    }    
  }

}

// Nombre      : MSurface::Load()
// Parametros  : char* pcFileName
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Carga un fichero grafico de tipo TGA, BMP o JPG con el nombre pasado como parametro			
int MSurface::Load(char* pcFileName, bool bRGBAlpha)
{
  char pcAuxFilename[256];
  char pcXtension[256];
  
  MExtractExtension(pcXtension, pcFileName);
  _strupr(pcXtension);


  if (bRGBAlpha)
  {
    char pcPathBasename[256];
    MExtractPathBasename(pcPathBasename, pcFileName);
    strcat(pcPathBasename, "@RGB.JPG");
    
    MSurface sRGB;
    MSurface sAlpha;
    if (sRGB.Load(pcPathBasename)==0)
    {
      MExtractPathBasename(pcPathBasename, pcFileName);
      strcat(pcPathBasename, "@ALPHA.JPG");

      if (sAlpha.Load(pcPathBasename)==0)
      {
        if (sRGB.GetPSurfaceDescriptor()->m_uiWidth==sAlpha.GetPSurfaceDescriptor()->m_uiWidth &&
            sRGB.GetPSurfaceDescriptor()->m_uiHeight==sAlpha.GetPSurfaceDescriptor()->m_uiHeight &&
            sAlpha.GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB24__888])
        {
          MSurfaceDescriptor sd;
          sd.m_uiWidth      =sRGB.GetPSurfaceDescriptor()->m_uiWidth;
          sd.m_uiHeight     =sRGB.GetPSurfaceDescriptor()->m_uiHeight;
          sd.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];
        
          if (Init(sd, 0, 0)!=0)
          {
            return (-1);
          }
          ScaleAndConvertFrom(sRGB);

          AssignAlpha(this, &sAlpha);

          return 0;
        }        
      }
    }
  }


  if (strcmp(pcXtension, "TGA")==0)
  {
    return (LoadARGB32_TGA(pcFileName, true));
  }

  if (strcmp(pcXtension, "BMP")==0)
  {
    return (LoadBMP(pcFileName));
  }

  if (strcmp(pcXtension, "JPG")==0)
  {
    return (LoadJPG(pcFileName));
  }

  strcpy(pcAuxFilename, pcFileName);
  strcat(pcAuxFilename, ".TGA");
  if (LoadARGB32_TGA(pcAuxFilename, true)==0)
  {
    return (0);
  }

  strcpy(pcAuxFilename, pcFileName);
  strcat(pcAuxFilename, ".BMP");
  if (LoadBMP(pcAuxFilename)==0)
  {
    return (0);
  }

  strcpy(pcAuxFilename, pcFileName);
  strcat(pcAuxFilename, ".JPG");
  if (LoadJPG(pcAuxFilename)==0)
  {
    return (0);
  }
  
  // Nada, no hay manera
  return (-1);
}

// Nombre      : MSurface::Save()
// Parametros  : char* pcFileName
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Graba en un fichero grafico el contenido de superficie. El formato viene
//               determinado por la extension o, si no tiene, grabara un TGA
int MSurface::Save(char* pcFileName)
{
  char pcAuxFilename[256];
  char pcXtension[256];
  
  MExtractExtension(pcXtension, pcFileName);
  _strupr(pcXtension);

  if (strcmp(pcXtension, "TGA")==0)
  {
    return (SaveARGB32_TGA(pcFileName));
  }

  if (strcmp(pcXtension, "BMP")==0)
  {
    return (SaveBMP(pcFileName));
  }

  if (strcmp(pcXtension, "JPG")==0)
  {
    return (SaveJPG(pcFileName, 100));
  }

  strcpy(pcAuxFilename, pcFileName);
  strcat(pcAuxFilename, ".TGA");

  return (SaveARGB32_TGA(pcAuxFilename));
}

// Nombre      : MSurface::LoadARGB32_TGA()
// Parametros  : FILE* f
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Carga un TGA desde un fichero abierto
int	MSurface::LoadARGB32_TGA(MVFSFILE* f, bool bUseAlpha)
{
	int i,j,k;
	unsigned char	pcHeader[18];
	unsigned short	uWidth;
	unsigned short	uHeight;

	if (MVFS::fread(pcHeader, sizeof(unsigned char)*18, 1, f)!=1)
	{
		return (-1);
	}

	// Si es necesario avanzamos 
	if ( pcHeader[0] != 0 )
	{
		MVFS::fseek(f,pcHeader[0],SEEK_CUR);
	}

	// Comprobacion
	if ( pcHeader[1] != 0 )
	{
		MVFS::fclose(f);
		return (-1);
	}

  if (pcHeader[2]!=2 && pcHeader[2]!=10)
  {
    return (-1);
  }
	
	uWidth	=*((unsigned short*) &(pcHeader[12]));
	uHeight	=*((unsigned short*) &(pcHeader[14]));


  if (pcHeader[16]==24)
  {
    MSurfaceDescriptor sdDescriptor;
	  sdDescriptor.m_uiWidth		=uWidth;
	  sdDescriptor.m_uiHeight		=uHeight;

    sdDescriptor.m_pfPixelFormat=CommonPixelDescriptors[E__BGR24__888];

    if (Init(sdDescriptor, NULL, NULL)!=0)
	  {
		  return (-1);
	  }

    char* pcBuffer=new char[uWidth*uHeight*3];
	  if (!pcBuffer)
	  {
		  return (-1);
	  }

    if (pcHeader[2]==2)
    {
      if (MVFS::fread(pcBuffer, uWidth*uHeight*3, 1, f)!=1)
	    {
        delete[] pcBuffer;
		    return (-1);
	    }
    }

    bool bUp=false;
	  if (pcHeader[17]&0x20)
	  {
		  bUp=true;
	  }
	  for (i=0; i<uHeight ; i++)
	  {
		  char* pcScanLine;

		  pcScanLine=(char*) GetSurfacePointer(0, bUp?i:uHeight-i-1);
      memcpy(pcScanLine, pcBuffer+uWidth*3*i, uWidth*3);
	  }	  

    delete[] pcBuffer;
  }
  else
  {
	  MSurfaceDescriptor sdDescriptor;
	  sdDescriptor.m_uiWidth		=uWidth;
	  sdDescriptor.m_uiHeight		=uHeight;
	  if (bUseAlpha)
	  {
		  sdDescriptor.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];
	  }
	  else
	  {
		  sdDescriptor.m_pfPixelFormat=CommonPixelDescriptors[E__RGB32__888];
	  }

	  if (Init(sdDescriptor, NULL, NULL)!=0)
	  {
		  return (-1);
	  }

	  unsigned* pBuffer=new unsigned[uWidth*uHeight];
	  if (!pBuffer)
	  {
		  return (-1);
	  }

    if (pcHeader[2]==2)
    {
      if (MVFS::fread(pBuffer, uWidth*uHeight*4, 1, f)!=1)
	    {
        delete[] pBuffer;
		    return (-1);
	    }
    }
    else
    {
      unsigned  uCount=uWidth*uHeight;
      unsigned* puPixels=pBuffer;
      while (uCount)
      {
        char      cPacket;
        unsigned  uPacketCount;

        if (MVFS::fread(&cPacket, 1, 1, f)!=1)
	      {
          delete[] pBuffer;
          return (-1);
        }

        uPacketCount=(cPacket&0x7F)+1;
        if (cPacket&0x80)
        {
          // Comprimido
          unsigned uColor;
          if (MVFS::fread(&uColor, 4, 1, f)!=1)
          {
            delete[] pBuffer;
            return (-1);
          }

          for (unsigned i=0 ; i<uPacketCount ; i++)
          {
            *puPixels++=uColor;
          }        
        }
        else
        {
          // No Comprimido
          if (MVFS::fread(puPixels, 4*uPacketCount, 1, f)!=1)
          {
            delete[] pBuffer;
            return (-1);
          }

          puPixels+=uPacketCount;        
        }

        uCount-=uPacketCount;
      }
    }

	  bool bUp=false;
	  if (pcHeader[17]&0x20)
	  {
		  bUp=true;
	  }
	  for (i=0, k=0 ; i<uHeight ; i++)
	  {
		  unsigned* pScanLine;

		  pScanLine=(unsigned*) GetSurfacePointer(0, bUp?i:uHeight-i-1);

		  for (j=0 ; j<uWidth ; j++)
		  {
			  pScanLine[j]=pBuffer[k];
			  if (!bUseAlpha)
			  {
				  pScanLine[j]|=0xFF000000;
			  }
			  k++;
		  }		
	  }

	  delete[] pBuffer;
  }

	return (0);
}

// Nombre      : MSurface::LoadARGB32_TGA()
// Parametros  : char* pcFileName
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Carga un TGA dado el nombre del fichero
int	MSurface::LoadARGB32_TGA(char* pcFileName, bool bUseAlpha)
{
	MVFSFILE* f;

	f=MVFS::fopen(pcFileName, "rb");
	if (!f)
	{
		return (-1);
	}

	int iReturn=LoadARGB32_TGA(f, bUseAlpha);

	MVFS::fclose(f);

	return (iReturn);
}

// Nombre      : MSurface::SaveBMP()
// Parametros  : char* pcFileName
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Graba en un BMP la superficie
int MSurface::SaveBMP(char* pcFileName)
{
  MSurface sAux;
	BITMAPFILEHEADER bfhBitmapFileHeader;
	BITMAPINFOHEADER bihBitmapInfoHeader;
	MVFSFILE* f;
	
	MSurfaceDescriptor sd;
	sd=m_sdSurfaceDescriptor;
	sd.m_pfPixelFormat=CommonPixelDescriptors[E__BGR24__888];
	sAux.Init(sd, NULL, NULL);
	sAux.ConvertFrom(*this);


	f=MVFS::fopen(pcFileName,"wb");
	if (!f)
	{
		return (-1);
	}

  bfhBitmapFileHeader.bfType='MB';
  bfhBitmapFileHeader.bfSize=sizeof(BITMAPINFOHEADER)+sizeof(BITMAPFILEHEADER)+3*m_sdSurfaceDescriptor.m_uiWidth*m_sdSurfaceDescriptor.m_uiHeight;
  bfhBitmapFileHeader.bfReserved1=0;
  bfhBitmapFileHeader.bfReserved2=0;
  bfhBitmapFileHeader.bfOffBits=sizeof(BITMAPINFOHEADER)+sizeof(BITMAPFILEHEADER);
  
  bihBitmapInfoHeader.biSize=sizeof(BITMAPINFOHEADER);
  bihBitmapInfoHeader.biWidth =m_sdSurfaceDescriptor.m_uiWidth;
  bihBitmapInfoHeader.biHeight=m_sdSurfaceDescriptor.m_uiHeight;
  bihBitmapInfoHeader.biBitCount=24;
  bihBitmapInfoHeader.biPlanes=1;
  bihBitmapInfoHeader.biCompression=BI_RGB;
  bihBitmapInfoHeader.biSizeImage=0;
  bihBitmapInfoHeader.biClrUsed=0;
  bihBitmapInfoHeader.biClrImportant=0;
  
  // Leemos la cabecera de fichero
	if (MVFS::fwrite(&bfhBitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, f)!=1)
	{
		return (-1);
	}

	// Leemos la informacion del bitmap
	if (MVFS::fwrite(&bihBitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, f)!=1)
	{
		return (-1);
	}
  
  for (int i=0 ; i<(int)m_sdSurfaceDescriptor.m_uiHeight ; i++)
  {
    void* pv=GetSurfacePointer(0, m_sdSurfaceDescriptor.m_uiHeight-i-1);
    if (MVFS::fwrite(pv, 3*m_sdSurfaceDescriptor.m_uiWidth, 1, f)!=1)
    {
      return (-1);
    }
  }

  MVFS::fclose(f);

  return (0);
}

// Nombre      : MSurface::LoadBMP()
// Parametros  : FILE* f
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Carga un BMP paletizado
int	MSurface::LoadBMP(MVFSFILE* f)
{
	BITMAPFILEHEADER bfhBitmapFileHeader;
	BITMAPINFOHEADER bihBitmapInfoHeader;
	unsigned i,j,k;

	// Leemos la cabecera de fichero
	if (MVFS::fread(&bfhBitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, f)!=1)
	{
		return (-1);
	}

	// Leemos la informacion del bitmap
	if (MVFS::fread(&bihBitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, f)!=1)
	{
		return (-1);
	}

	if (bihBitmapInfoHeader.biBitCount!=8 && bihBitmapInfoHeader.biBitCount!=24)
	{
		// Solo aceptamos paletizados de 256 colores
		return (-1);
	}

	if (bihBitmapInfoHeader.biCompression!=BI_RGB)
	{
		// No aceptamos compresion
		return (-1);
	}

  if (bihBitmapInfoHeader.biBitCount==8)
  {
	  // Inicializamos superficie
	  MSurfaceDescriptor sdDescriptor;
	  sdDescriptor.m_uiWidth		=bihBitmapInfoHeader.biWidth;
	  sdDescriptor.m_uiHeight		=bihBitmapInfoHeader.biHeight;
	  sdDescriptor.m_pfPixelFormat=CommonPixelDescriptors[E__PALET__256];

	  if (Init(sdDescriptor, NULL, NULL)!=0)
	  {
		  return (-1);
	  }

	  // Leemos paleta
	  MPalette* pPalette;					
	  pPalette=GetPalette();
	  if (MVFS::fread(pPalette->m_uiPalette, sizeof(unsigned int)*256, 1, f)!=1)
	  {
		  return (-1);
	  }
	  pPalette->m_pfPixelFormat=CommonPixelDescriptors[E__RGB32__888];
	  

	  // Buffer Intermedio
	  unsigned char* pcBuffer=new unsigned char[sdDescriptor.m_uiWidth*sdDescriptor.m_uiHeight];
	  if (!pcBuffer)
	  {
		  return (-1);
	  }


	  // Nos colocamos
	  MVFS::fseek(f, bfhBitmapFileHeader.bfOffBits, SEEK_SET);

	  // Leemos del buffer
	  if (MVFS::fread(pcBuffer, 1, sdDescriptor.m_uiWidth*sdDescriptor.m_uiHeight, f)!=sdDescriptor.m_uiWidth*sdDescriptor.m_uiHeight)
	  {
		  return (-1);
	  }

    // Copiamos con inversion de y
	  for (i=0, k=0 ; i<sdDescriptor.m_uiHeight ; i++)
	  {
		  unsigned char* pScanLine;
		  pScanLine=(unsigned char*) GetSurfacePointer(0, sdDescriptor.m_uiHeight-i-1);

		  for (j=0 ; j<sdDescriptor.m_uiWidth ; j++)
		  {
			  pScanLine[j]=pcBuffer[k];			
			  k++;
		  }
	  }

    delete[] pcBuffer;
  }
  else
  {
    MSurfaceDescriptor sdDescriptor;
	  sdDescriptor.m_uiWidth		=bihBitmapInfoHeader.biWidth;
	  sdDescriptor.m_uiHeight		=bihBitmapInfoHeader.biHeight;
	  sdDescriptor.m_pfPixelFormat=CommonPixelDescriptors[E__BGR24__888];

	  if (Init(sdDescriptor, NULL, NULL)!=0)
	  {
		  return (-1);
	  }

    // Nos colocamos
	  MVFS::fseek(f, bfhBitmapFileHeader.bfOffBits, SEEK_SET);

    // Buffer Intermedio
	  unsigned char* pcBuffer=new unsigned char[sdDescriptor.m_uiWidth*sdDescriptor.m_uiHeight*3];
	  if (!pcBuffer)
	  {
		  return (-1);
	  }

    // Leemos del buffer
	  if (MVFS::fread(pcBuffer, 1, sdDescriptor.m_uiWidth*sdDescriptor.m_uiHeight*3, f)!=sdDescriptor.m_uiWidth*sdDescriptor.m_uiHeight*3)
	  {
		  return (-1);
	  }

    // Copiamos con inversion de y
	  for (i=0 ; i<sdDescriptor.m_uiHeight ; i++)
	  {
		  unsigned char* pScanLine;
		  pScanLine=(unsigned char*) GetSurfacePointer(0, sdDescriptor.m_uiHeight-i-1);

      memcpy(pScanLine, pcBuffer+i*sdDescriptor.m_uiWidth*3, sdDescriptor.m_uiWidth*3);		  
	  }

    delete[] pcBuffer;
  }

	return (0);
}

// Nombre      : MSurface::LoadBMP()
// Parametros  : char* pcFileName
// Retorno     : 0: OK ; -1: ERROR
// Descripcion : Carga un BMP paletizado
int	MSurface::LoadBMP(char* pcFileName)
{
	MVFSFILE* f;

	f=MVFS::fopen(pcFileName, "rb");
	if (!f)
	{
		return (-1);
	}

	int iReturn=LoadBMP(f);
	
	MVFS::fclose(f);

	return (iReturn);
}

int	MSurface::SetName(char* pcName)
{
  assert(strlen(pcName)<=MAX_SURFACE_NAME_LENGTH); // Ver si habria que ponerlo pasiempre (y no solo en debug)
	strcpy(m_pcName, pcName);
	return (0);
}

char* MSurface::GetName()
{
	return (m_pcName);
}

#pragma pack(1)
struct TGAHeader
{
	unsigned char	m_ucIDLength;
	unsigned char	m_ucColorMapType;
	unsigned char	m_ucImageType;
	unsigned char	m_ucColorMapSpecification[5];
	unsigned short	m_usXOrigin;
	unsigned short	m_usYOrigin;
	unsigned short	m_usWidth;
	unsigned short	m_usHeight;
	unsigned char	m_ucPixelDepth;
	unsigned char	m_ucImageDescriptor;
};
#pragma pack()

int	MSurface::SaveARGB32_TGA(char* pcFileName)
{
	MSurface sAux;
	TGAHeader header;
	MVFSFILE* f;
	
	MSurfaceDescriptor sd;
	sd=m_sdSurfaceDescriptor;
	sd.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];
	sAux.Init(sd, NULL, NULL);
	sAux.ConvertFrom(*this);

	f=MVFS::fopen(pcFileName,"wb");
	if (!f)
	{
		return (-1);
	}

	
	memset(&header, 0, sizeof(header));
	header.m_ucIDLength					=0;
	header.m_ucColorMapType				=0;
	header.m_ucImageType				=2;	
	header.m_usXOrigin					=0;
	header.m_usYOrigin					=0;
	header.m_usWidth					=(unsigned short)sAux.GetSurfaceDescriptor().m_uiWidth;
	header.m_usHeight					=(unsigned short)sAux.GetSurfaceDescriptor().m_uiHeight;
	header.m_ucPixelDepth				=32;
	header.m_ucImageDescriptor			=0x28;
	
	// Escribimos cabecera
	MVFS::fwrite(&header, sizeof(header), 1, f);

	// Escribimos a saco la imagen
	MVFS::fwrite(sAux.GetSurfacePointer(), sAux.GetSurfaceDescriptor().m_uiWidth*sAux.GetSurfaceDescriptor().m_uiHeight*4, 1, f);

	MVFS::fclose(f);

	return (0);
}

int	MSurface::WriteRaw(char* pcFileName)
{
	MVFSFILE* f;
	int iReturn;

	f=MVFS::fopen(pcFileName, "wb");
	if (!f)
	{
		return (-1);
	}
	iReturn=WriteRaw(f);
	MVFS::fclose(f);

	return (0);
}

int	MSurface::ReadRaw(char* pcFileName)
{
	MVFSFILE* f;
	int iReturn;

	f=MVFS::fopen(pcFileName, "rb");
	if (!f)
	{
		return (-1);
	}
	iReturn=ReadRaw(f);
	MVFS::fclose(f);

	return (0);
}

int	MSurface::WriteRaw(MVFSFILE* f)
{
	// Cabecera
	if (MVFS::fwrite(m_pcName, MAX_SURFACE_NAME_LENGTH, 1, f)!=1)
	{
		return (-1);
	}

	if (MVFS::fwrite(&m_sdSurfaceDescriptor, sizeof(m_sdSurfaceDescriptor), 1, f)!=1)
	{
		return (-1);
	}

	if (MVFS::fwrite(&m_bLocalSurfaceAllocation, sizeof(bool), 1, f)!=1)
	{
		return (-1);
	}

	if (MVFS::fwrite(&m_bLocalPaletteAllocation, sizeof(bool), 1, f)!=1)
	{
		return (-1);
	}

	// Paleta
	if (m_pPalette)
	{
		if (MVFS::fwrite(m_pPalette, sizeof(MPalette), 1 ,f)!=1)
		{
			return (-1);
		}
	}

	// Superficie
	if (m_pvSurface)
	{
		int iBytes=m_sdSurfaceDescriptor.m_uiByteStride*
			m_sdSurfaceDescriptor.m_uiHeight;			
		if (MVFS::fwrite(m_pvSurface, iBytes, 1 ,f)!=1)
		{
			return (-1);
		}
	}

	return (0);
}

int	MSurface::ReadRaw(MVFSFILE* f)
{
	return (0);
}

int	MSurface::ReadInitializedRaw(char* pcFileName)
{
	MVFSFILE* f;
	int iReturn;

	f=MVFS::fopen(pcFileName, "rb");
	if (!f)
	{
		return (-1);
	}
	iReturn=ReadInitializedRaw(f);
	MVFS::fclose(f);

	return (iReturn);

}

int MSurface::ReadInitializedRaw(MVFSFILE* f)
{
	char	pcName[MAX_SURFACE_NAME_LENGTH];
	MSurfaceDescriptor sdSurfaceDescriptor;

	// Cabecera
	if (MVFS::fread(pcName, MAX_SURFACE_NAME_LENGTH, 1, f)!=1)
	{
		return (-1);
	}

	if (strcmp(pcName, m_pcName)!=0)
	{
		return (-1);
	}

	if (MVFS::fread(&sdSurfaceDescriptor, sizeof(sdSurfaceDescriptor), 1, f)!=1)
	{
		return (-1);
	}

	if (memcmp(&sdSurfaceDescriptor, &m_sdSurfaceDescriptor, sizeof(sdSurfaceDescriptor))!=0)
	{
		return (-1);
	}

	if (MVFS::fread(&m_bLocalSurfaceAllocation, sizeof(bool), 1, f)!=1)
	{
		return (-1);
	}

	if (MVFS::fread(&m_bLocalPaletteAllocation, sizeof(bool), 1, f)!=1)
	{
		return (-1);
	}

	// Paleta
	if (m_pPalette)
	{
		if (MVFS::fread(m_pPalette, sizeof(MPalette), 1 ,f)!=1)
		{
			return (-1);
		}
	}

	// Superficie
	if (m_pvSurface)
	{
		int iBytes=m_sdSurfaceDescriptor.m_uiByteStride*
			m_sdSurfaceDescriptor.m_uiHeight;			
		if (MVFS::fread(m_pvSurface, iBytes, 1 ,f)!=1)
		{
			return (-1);
		}
	}

	return (0);
}

int MSurface::Generate32To16MipMap(MSurface& sARGB32Source)
{
   int i,j;

   assert ( POW2(int(sARGB32Source.GetPSurfaceDescriptor()->m_uiHeight)) &&
            POW2(int(sARGB32Source.GetPSurfaceDescriptor()->m_uiWidth))  &&
            (this->GetPSurfaceDescriptor()->m_uiWidth==
            sARGB32Source.GetPSurfaceDescriptor()->m_uiWidth/2)    &&
            (this->GetPSurfaceDescriptor()->m_uiHeight==
            sARGB32Source.GetPSurfaceDescriptor()->m_uiHeight/2)   &&
            ((this->GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__555]) ||
            (this->GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__565])) );

   unsigned int*    puiSourceEven;
   unsigned int*    puiSourceOdd;
   unsigned short*  puDestiny;

   // 
   puiSourceEven=(unsigned*)sARGB32Source.GetSurfacePointer(0,0);
   puiSourceOdd =(unsigned*)sARGB32Source.GetSurfacePointer(0,1);
   puDestiny=(unsigned short*) GetSurfacePointer(0,0);
   
   int iWidth=this->GetPSurfaceDescriptor()->m_uiWidth;
   int iHeight=this->GetPSurfaceDescriptor()->m_uiHeight;

   int iSourceEndScanJump =     
     2*(sARGB32Source.GetPSurfaceDescriptor()->m_uiByteStride/(sARGB32Source.GetPSurfaceDescriptor()->m_pfPixelFormat.m_uBPP>>3))-
     sARGB32Source.m_sdSurfaceDescriptor.m_uiWidth;


     //sARGB32Source.GetPSurfaceDescriptor()->m_uiByteStride/(sARGB32Source.GetPSurfaceDescriptor()->m_pfPixelFormat.m_uBPP>>3)-iWidth*2;
   int iDestinyEndScanJump=     
     GetPSurfaceDescriptor()->m_uiByteStride/(GetPSurfaceDescriptor()->m_pfPixelFormat.m_uBPP>>3)-iWidth;
   
   unsigned uiPixel;
   if (this->GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__565])
   {
     for (j=0 ; j<iHeight ; j++)
     {
       for (i=0 ; i<iWidth ; i++)
       {
         
                  
          uiPixel=((( *(puiSourceEven+0) ) >>2) & 0x3F3F3F3F)+
                  ((( *(puiSourceEven+1) ) >>2) & 0x3F3F3F3F)+
                  ((( *(puiSourceOdd +0) ) >>2) & 0x3F3F3F3F)+
                  ((( *(puiSourceOdd +1) ) >>2) & 0x3F3F3F3F);                
          
          *(puDestiny)=
            ((uiPixel & 0x00F80000) >>  8) |
            ((uiPixel & 0x0000FC00) >>  5) |
            ((uiPixel & 0x000000F8) >>  3) ;

          puiSourceEven+=2;
          puiSourceOdd+=2;
          puDestiny++;
       }

       puiSourceEven+=iSourceEndScanJump;
       puiSourceOdd+=iSourceEndScanJump;
       puDestiny+=iDestinyEndScanJump;
     }
   }
   else if (this->GetPSurfaceDescriptor()->m_pfPixelFormat==CommonPixelDescriptors[E__RGB16__555])
   {
     unsigned int uiPixel;
     for (j=0 ; j<iHeight ; j++)
     {
       for (i=0 ; i<iWidth ; i++)
       {
         
          uiPixel=(((*(puiSourceEven+0))>>2)&0x3F3F3F3F)+
                  (((*(puiSourceEven+1))>>2)&0x3F3F3F3F)+
                  (((*(puiSourceOdd +0))>>2)&0x3F3F3F3F)+
                  (((*(puiSourceOdd +1))>>2)&0x3F3F3F3F);                

          *(puDestiny++)=
            ((uiPixel & 0x00F80000) >>  9) |
            ((uiPixel & 0x0000F800) >>  6) |
            ((uiPixel & 0x000000F8) >>  3) ;

          puiSourceEven+=2;
          puiSourceOdd+=2;
       }

       puiSourceEven+=iSourceEndScanJump;
       puiSourceOdd+=iSourceEndScanJump;
       puDestiny+=iDestinyEndScanJump;
     }
   }

  return (0);
}

int MSurface::SurfaceBlt(MSurface& sSource, int xo, int yo, int xw, int yh, int xd, int yd)
{
  // Es un blt chankero... no soportamos cambios de formato de pixel.
  if (!(GetPSurfaceDescriptor()->m_pfPixelFormat==sSource.GetPSurfaceDescriptor()->m_pfPixelFormat))
  {

    return (-1);
  }
  
  // Hacemos clipeo con origen
  if (xo<0)                                                    { xd-=xo; xo=0; }
  if (yo<0)                                                    { yd-=yo; yo=0; }
  if (xo+xw>(int)sSource.GetPSurfaceDescriptor()->m_uiWidth)   { xw+=(int)sSource.GetPSurfaceDescriptor()->m_uiWidth-(xo+xw); }
  if (yo+yh>(int)sSource.GetPSurfaceDescriptor()->m_uiHeight)  { yh+=(int)sSource.GetPSurfaceDescriptor()->m_uiHeight-(yo+yh); }

  // Hacemos clipeo con destino
  if (xd<0)                                                    { xo-=xd; xd=0;  }
  if (yd<0)                                                    { yo-=yd; yd=0;  }
  if (xd+xw>(int)GetPSurfaceDescriptor()->m_uiWidth)           { xw+=(int)GetPSurfaceDescriptor()->m_uiWidth-(xd+xw); }
  if (yd+yh>(int)GetPSurfaceDescriptor()->m_uiWidth)           { yh+=(int)GetPSurfaceDescriptor()->m_uiHeight-(yd+yh); }
  
  // blt
  char* pcSource;
  char* pcDestiny;

  pcSource=(char*)sSource.GetSurfacePointer(xo, yo);
  pcDestiny=(char*)GetSurfacePointer(xd,yd);

  int i;
  unsigned uByteRun=xw*(GetPSurfaceDescriptor()->m_pfPixelFormat.m_uBPP>>3);

  if (uByteRun>0)
  {
    for (i=0 ; i<yh ; i++)
    {
      memcpy(pcDestiny, pcSource, uByteRun);
      pcDestiny+=GetPSurfaceDescriptor()->m_uiByteStride;
      pcSource+=sSource.GetPSurfaceDescriptor()->m_uiByteStride;
    }
  }

  return (0);
}

int MSurface::ScaleFrom(MSurface& sSurface, int iWidth, int iHeight)
{
  Shutdown();

  MSurfaceDescriptor sd;

  sd                =sSurface.m_sdSurfaceDescriptor;
  sd.m_uiWidth      =iWidth;
  sd.m_uiHeight     =iHeight;
  
  Init(sd, NULL, NULL);
  ScaleAndConvertFrom(sSurface);

  return (0);
}

int MSurface::Modulate(float f)
{
  MSurface tempSurface;

  MSurfaceDescriptor sd;

  sd=m_sdSurfaceDescriptor;
  sd.m_pfPixelFormat=CommonPixelDescriptors[E_ARGB32_8888];

  tempSurface.Init(sd, NULL, NULL);
  tempSurface.ConvertFrom(*this);

  unsigned i,j;
  unsigned* puPixel;
  float a,r,g,b;
  for (j=0 ; j<tempSurface.GetPSurfaceDescriptor()->m_uiHeight ; j++)
  {
    puPixel=(unsigned*)tempSurface.GetSurfacePointer(0, j);

    for (i=0 ; i<tempSurface.GetPSurfaceDescriptor()->m_uiWidth ; i++)
    {
      unsigned uPixel;
      
      uPixel=puPixel[i];

      a=float((uPixel&0xFF000000)>>24);
      r=float((uPixel&0x00FF0000)>>16);
      g=float((uPixel&0x0000FF00)>> 8);
      b=float((uPixel&0x000000FF)>> 0);

      a*=f; r*=f; g*=f; b*=f;

      uPixel=
        int(a)<<24 |
        int(r)<<16 |
        int(g)<< 8 |
        int(b)<< 0;

      puPixel[i]=uPixel;
    }
  }  

  ConvertFrom(tempSurface);

  return (0);
}

MSurface& MSurface::operator=(const MSurface& sSurface)
{
  Shutdown();
  Init(sSurface.m_sdSurfaceDescriptor, NULL, NULL);

  if (m_pvSurface)
  {
    // Se copia linea a linea por si el source tenia bytestride diston
    for (unsigned i=0 ; i<m_sdSurfaceDescriptor.m_uiHeight ; i++)
    {
      void* pDestiny;
      void* pSource;
      pDestiny=GetSurfacePointer(0, i);    
      pSource =sSurface.GetSurfacePointer(0, i);    
      memcpy(pDestiny, pSource, m_sdSurfaceDescriptor.m_uiWidth*(m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3));
    }   
  }

  if (m_pPalette)
  {
    memcpy(m_pPalette, sSurface.m_pPalette, 256*4);
  }

  strcpy(m_pcName, sSurface.m_pcName);

  return (*this);
}

MSurface::MSurface(const MSurface& sSurface)
{
  m_bValid=false;
	m_pvSurface=NULL;
	m_bLocalSurfaceAllocation=false;
	m_bLocalPaletteAllocation=false;
	m_pcName[0]='\0';

  *this=sSurface;
}
          
int MSurface::SwapXY()
{
  MSurface           sTemp;
  MSurfaceDescriptor sdDescriptor;


  unsigned uSwap;
  sdDescriptor            =m_sdSurfaceDescriptor;
  uSwap                   =sdDescriptor.m_uiHeight;
  sdDescriptor.m_uiHeight =sdDescriptor.m_uiWidth;
  sdDescriptor.m_uiWidth  =uSwap;

  // Inicializamos
  sTemp.Init(sdDescriptor, NULL, NULL);

  // Copiamos... evidentemente est es la basura...pero bueno... rapido de teclear
  for (unsigned y=0 ; y<sTemp.GetPSurfaceDescriptor()->m_uiHeight ; y++)
  {
    for (unsigned x=0 ; x<sTemp.GetPSurfaceDescriptor()->m_uiWidth ; x++)
    {
      void* pDestiny=sTemp.GetSurfacePointer(x, y);
      void* pSource =GetSurfacePointer(y, x);
      memcpy(pDestiny, pSource, sTemp.m_sdSurfaceDescriptor.m_pfPixelFormat.m_uBPP>>3);
    }
    
  }

  // Copiamos superficie
  *this=sTemp;

  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
int MSurface::GenerateMipMapChain(MSurface* pSurfaces, unsigned uMips, bool bRenormalizeRGB)
{
  unsigned i,j,k;

  // Comprobacion de integridad
  unsigned uWidth,uHeight;
  
  uWidth =GetPSurfaceDescriptor()->m_uiWidth;
  uHeight=GetPSurfaceDescriptor()->m_uiHeight;
  
  MPixelFormatDescriptor pfSource, pfDestiny;
  pfDestiny =pSurfaces[0].GetPSurfaceDescriptor()->m_pfPixelFormat;
  pfSource  =GetPSurfaceDescriptor()->m_pfPixelFormat;

  bool bFastPath=true;

  // Comprobacion de que datos estan bien
  for (i=0 ; i<uMips ; i++)
  {
    
    if (!(pfDestiny==pSurfaces[i].GetPSurfaceDescriptor()->m_pfPixelFormat))
    {
      assert(0);
      return(-1);
    }
  }

  if (bFastPath && (pfSource==CommonPixelDescriptors[E_ARGB32_8888] ||
       pfSource==CommonPixelDescriptors[E__RGB32__888] ||
       pfSource==CommonPixelDescriptors[E__RGB24__888] ||
       pfSource==CommonPixelDescriptors[E__BGR24__888])
      &&      
      pfDestiny.m_uBPP==16)
  {
    for (i=0 ; i<uMips ; i++)
    {
      if (i==0)
      {        
        if ( ((pSurfaces[0].GetPSurfaceDescriptor()->m_uiWidth) ==uWidth) ||
             ((pSurfaces[0].GetPSurfaceDescriptor()->m_uiHeight)==uHeight) )
        {            
          if (pfSource.m_uBPP==32)
          {
            Convert32To16(pSurfaces[0]);
          }
          else
          {
            Convert24To16(pSurfaces[0]);
          }               
        }
        else
        {
          pSurfaces[0].ScaleAndConvertFrom(*this);
        }
      }
      else
      {
        unsigned short* pSourceUp;
        unsigned short* pSourceDown;
        unsigned short* pDestiny;

        // Caso especial de w!=h y (w==1 || h==1)
        if ( (pSurfaces[i].GetPSurfaceDescriptor()->m_uiWidth !=(pSurfaces[i-1].GetPSurfaceDescriptor()->m_uiWidth )>>1 )||
             (pSurfaces[i].GetPSurfaceDescriptor()->m_uiHeight!=(pSurfaces[i-1].GetPSurfaceDescriptor()->m_uiHeight)>>1) )
        {
          pSurfaces[i].ScaleAndConvertFrom(pSurfaces[i-1]);          
          continue;
        }          

        #ifdef DITHER
        unsigned uError[4]={0,0,0,0};
        unsigned uColor[4];


        uWidth  =pSurfaces[i].GetPSurfaceDescriptor()->m_uiWidth;
        uHeight =pSurfaces[i].GetPSurfaceDescriptor()->m_uiHeight;

        unsigned short* pErrorA=new unsigned short[(uWidth +2)*4];
        unsigned short* pErrorB=new unsigned short[(uWidth+2)*4];;
        unsigned short* pUp           =pErrorA;
        unsigned short* pDown         =pErrorB;
        unsigned short* puErrorUp;
        unsigned short* puErrorDown;

        memset(pUp  , 0, (uWidth+2)*4*sizeof(short));            
        memset(pDown, 0, (uWidth+2)*4*sizeof(short));            
  
        unsigned puError[4]={0,0,0,0};

        for (j=0 ; j<pSurfaces[i].GetPSurfaceDescriptor()->m_uiHeight ; j++)
        {        
          pSourceUp  =(unsigned short*)pSurfaces[i-1].GetSurfacePointer(0,j*2  );
          pSourceDown=(unsigned short*)pSurfaces[i-1].GetSurfacePointer(0,j*2+1);
          pDestiny   =(unsigned short*)pSurfaces[i].GetSurfacePointer(0,j);

          
          memset(pDown+8, 0, (uWidth)*4*sizeof(short));            
          
          puErrorUp  =pUp+4;
          puErrorDown=pDown+4;

  
          
          for (k=0 ; k<pSurfaces[i].GetPSurfaceDescriptor()->m_uiWidth ; k++)
          { 

            
            //#define MIPMACRO(i, mask, shift) uColor[i]=\
            //            ((pSourceUp  [0]&mask)>>shift)+\
            //            ((pSourceUp  [1]&mask)>>shift)+\
            //            ((pSourceDown[0]&mask)>>shift)+\
            //            ((pSourceDown[1]&mask)>>shift)+(puErrorUp[i]);\
            //            puErrorUp  [i+4]+=/*(puErrorUp[i]&1)+*/(uColor[i]&3);\
            //            /*puErrorDown[i  ]+=(puErrorUp[i]&1)+(uColor[i]&3);*/\
            //            uColor[i]=min(mask>>shift, uColor[i]>>2)<<(shift);            

            #define MIPMACRO(i, mask, shift) uColor[i]=\
                        ((pSourceUp  [0]&mask)>>shift)+\
                        ((pSourceUp  [1]&mask)>>shift)+\
                        ((pSourceDown[0]&mask)>>shift)+\
                        ((pSourceDown[1]&mask)>>shift)+(puError[i]);\
                        puError[i]=(uColor[i]&3);\
                        uColor[i]=min(mask>>shift, uColor[i]>>2)<<(shift);            


                        
            

            MIPMACRO(0, pfDestiny.m_uAMask, pfDestiny.m_uAShiftLeft);
            MIPMACRO(1, pfDestiny.m_uRMask, pfDestiny.m_uRShiftLeft);
            MIPMACRO(2, pfDestiny.m_uGMask, pfDestiny.m_uGShiftLeft);
            MIPMACRO(3, pfDestiny.m_uBMask, pfDestiny.m_uBShiftLeft);
            
            *pDestiny=uColor[0] | uColor[1] | uColor[2] | uColor[3];
            
            pSourceUp  +=2;
            pSourceDown+=2;
            pDestiny   ++;


            puErrorUp  +=4;
            puErrorDown+=4;
          }

          pUp[4]=puErrorUp[0];
          pUp[5]=puErrorUp[1];
          pUp[6]=puErrorUp[2];
          pUp[7]=puErrorUp[3];
          
          unsigned short* pAux;
          pAux=pUp;
          pUp=pDown;
          pDown=pAux;                          
        }

        delete[] pErrorA;
        delete[] pErrorB;
        
        #else
        for (j=0 ; j<pSurfaces[i].GetPSurfaceDescriptor()->m_uiHeight ; j++)
        {        
          pSourceUp  =(unsigned short*)pSurfaces[i-1].GetSurfacePointer(0,j*2  );
          pSourceDown=(unsigned short*)pSurfaces[i-1].GetSurfacePointer(0,j*2+1);
          pDestiny   =(unsigned short*)pSurfaces[i].GetSurfacePointer(0,j);

          for (k=0 ; k<pSurfaces[i].GetPSurfaceDescriptor()->m_uiWidth ; k++)
          {            
            *pDestiny=
              ((((((*pSourceUp)   & pfDestiny.m_uRMask)+((*(pSourceUp  +1))  & pfDestiny.m_uRMask)+
                  ((*pSourceDown) & pfDestiny.m_uRMask)+((*(pSourceDown+1))  & pfDestiny.m_uRMask))>>2))& pfDestiny.m_uRMask)|

              ((((((*pSourceUp)   & pfDestiny.m_uGMask)+((*(pSourceUp  +1))  & pfDestiny.m_uGMask)+
                  ((*pSourceDown) & pfDestiny.m_uGMask)+((*(pSourceDown+1))  & pfDestiny.m_uGMask))>>2))& pfDestiny.m_uGMask)|
                 
              ((((((*pSourceUp)   & pfDestiny.m_uBMask)+((*(pSourceUp  +1))  & pfDestiny.m_uBMask)+
                  ((*pSourceDown) & pfDestiny.m_uBMask)+((*(pSourceDown+1))  & pfDestiny.m_uBMask))>>2))& pfDestiny.m_uBMask)|

              ((((((*pSourceUp)   & pfDestiny.m_uAMask)+((*(pSourceUp  +1))  & pfDestiny.m_uAMask)+
                  ((*pSourceDown) & pfDestiny.m_uAMask)+((*(pSourceDown+1))  & pfDestiny.m_uAMask))>>2))& pfDestiny.m_uAMask)
                 ;                                      
              
            pSourceUp  +=2;
            pSourceDown+=2;
            pDestiny   ++;
          }
        }
        #endif
      }
    }

  }
  else if (bFastPath && (pfSource==CommonPixelDescriptors[E_ARGB32_8888] ||
       pfSource==CommonPixelDescriptors[E__RGB32__888] ||
       pfSource==CommonPixelDescriptors[E__RGB24__888] ||
       pfSource==CommonPixelDescriptors[E__BGR24__888]) &&
       pfDestiny.m_uBPP==32)
  {
    for (i=0 ; i<uMips ; i++)
    {
      if (i==0)
      {        
        if ( ((pSurfaces[0].GetPSurfaceDescriptor()->m_uiWidth) ==uWidth) ||
             ((pSurfaces[0].GetPSurfaceDescriptor()->m_uiHeight)==uHeight) )
        {            
          if (pfSource.m_uBPP==32)
          {
            Convert32To32(pSurfaces[0]);
          }
          else
          {
            Convert24To32(pSurfaces[0]);
          }               
        }
        else
        {
          pSurfaces[0].ScaleAndConvertFrom(*this);
        }
      }
      else
      {
        unsigned* pSourceUp;
        unsigned* pSourceDown;
        unsigned* pDestiny;

        
        // Caso especial de w!=h y (w==1 || h==1)
        if ( (pSurfaces[i].GetPSurfaceDescriptor()->m_uiWidth !=(pSurfaces[i-1].GetPSurfaceDescriptor()->m_uiWidth )>>1 )||
             (pSurfaces[i].GetPSurfaceDescriptor()->m_uiHeight!=(pSurfaces[i-1].GetPSurfaceDescriptor()->m_uiHeight)>>1) )
        {
          pSurfaces[i].ScaleAndConvertFrom(pSurfaces[i-1]);          
          continue;
        }          
      
        for (j=0 ; j<pSurfaces[i].GetPSurfaceDescriptor()->m_uiHeight ; j++)
        {        
          pSourceUp  =(unsigned int*)pSurfaces[i-1].GetSurfacePointer(0,j*2  );
          pSourceDown=(unsigned int*)pSurfaces[i-1].GetSurfacePointer(0,j*2+1);
          pDestiny   =(unsigned int*)pSurfaces[i]  .GetSurfacePointer(0,j);

          unsigned uColor[4];
          for (k=0 ; k<pSurfaces[i].GetPSurfaceDescriptor()->m_uiWidth ; k++)
          {
            uColor[0] =(((pSourceUp[0])&pfDestiny.m_uAMask)>>pfDestiny.m_uAShiftLeft)<<pfDestiny.m_uAShiftRight;
            uColor[0]+=(((pSourceUp[1])&pfDestiny.m_uAMask)>>pfDestiny.m_uAShiftLeft)<<pfDestiny.m_uAShiftRight;
            uColor[0]+=(((pSourceDown[0])&pfDestiny.m_uAMask)>>pfDestiny.m_uAShiftLeft)<<pfDestiny.m_uAShiftRight;
            uColor[0]+=(((pSourceDown[1])&pfDestiny.m_uAMask)>>pfDestiny.m_uAShiftLeft)<<pfDestiny.m_uAShiftRight;
            uColor[0]>>=2;
          

            uColor[1] =(((pSourceUp[0])&pfDestiny.m_uRMask)>>pfDestiny.m_uRShiftLeft)<<pfDestiny.m_uRShiftRight;
            uColor[1]+=(((pSourceUp[1])&pfDestiny.m_uRMask)>>pfDestiny.m_uRShiftLeft)<<pfDestiny.m_uRShiftRight;
            uColor[1]+=(((pSourceDown[0])&pfDestiny.m_uRMask)>>pfDestiny.m_uRShiftLeft)<<pfDestiny.m_uRShiftRight;
            uColor[1]+=(((pSourceDown[1])&pfDestiny.m_uRMask)>>pfDestiny.m_uRShiftLeft)<<pfDestiny.m_uRShiftRight;
            uColor[1]>>=2;

            uColor[2] =(((pSourceUp[0])&pfDestiny.m_uGMask)>>pfDestiny.m_uGShiftLeft)<<pfDestiny.m_uGShiftRight;
            uColor[2]+=(((pSourceUp[1])&pfDestiny.m_uGMask)>>pfDestiny.m_uGShiftLeft)<<pfDestiny.m_uGShiftRight;
            uColor[2]+=(((pSourceDown[0])&pfDestiny.m_uGMask)>>pfDestiny.m_uGShiftLeft)<<pfDestiny.m_uGShiftRight;
            uColor[2]+=(((pSourceDown[1])&pfDestiny.m_uGMask)>>pfDestiny.m_uGShiftLeft)<<pfDestiny.m_uGShiftRight;
            uColor[2]>>=2;

            uColor[3] =(((pSourceUp[0])&pfDestiny.m_uBMask)>>pfDestiny.m_uBShiftLeft)<<pfDestiny.m_uBShiftRight;
            uColor[3]+=(((pSourceUp[1])&pfDestiny.m_uBMask)>>pfDestiny.m_uBShiftLeft)<<pfDestiny.m_uBShiftRight;
            uColor[3]+=(((pSourceDown[0])&pfDestiny.m_uBMask)>>pfDestiny.m_uBShiftLeft)<<pfDestiny.m_uBShiftRight;
            uColor[3]+=(((pSourceDown[1])&pfDestiny.m_uBMask)>>pfDestiny.m_uBShiftLeft)<<pfDestiny.m_uBShiftRight;
            uColor[3]>>=2;

            if (bRenormalizeRGB)
            {
              float fLength = 255.0f/sqrtf(float(uColor[1]*uColor[1] + uColor[2]*uColor[2] + uColor[3]*uColor[3]));
  
              uColor[1] = int(float(uColor[1])*fLength);
              uColor[2] = int(float(uColor[2])*fLength);
              uColor[3] = int(float(uColor[3])*fLength);

            }
          
            *pDestiny=
              ((uColor[0]>>pfDestiny.m_uAShiftRight)<<pfDestiny.m_uAShiftLeft) |
              ((uColor[1]>>pfDestiny.m_uRShiftRight)<<pfDestiny.m_uRShiftLeft) |
              ((uColor[2]>>pfDestiny.m_uGShiftRight)<<pfDestiny.m_uGShiftLeft) |
              ((uColor[3]>>pfDestiny.m_uBShiftRight)<<pfDestiny.m_uBShiftLeft);
            
            pSourceUp  +=2;
            pSourceDown+=2;
            pDestiny   ++;
          }
        }                
      }
    }
  }
  else
  {
    for (i=0 ; i<uMips ; i++)
    {
      pSurfaces[i].ScaleAndConvertFrom(*this);
    }    
  }
   
  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
int MSurface::Convert32To16(MSurface& sSurface)
{
  MPixelFormatDescriptor pfSource, pfDestiny;
  pfDestiny =sSurface.GetPSurfaceDescriptor()->m_pfPixelFormat;
  pfSource  =GetPSurfaceDescriptor()->m_pfPixelFormat;

  unsigned uWidth =GetPSurfaceDescriptor()->m_uiWidth;
  unsigned uHeight=GetPSurfaceDescriptor()->m_uiHeight;
  unsigned j,k;

  #ifndef DITHER
  unsigned int  * pSource;
  unsigned short* pDestiny;

  
  for (j=0 ; j<sSurface.GetPSurfaceDescriptor()->m_uiHeight ; j++)
  {        
    pSource =(unsigned int  *)GetSurfacePointer(0,j);
    pDestiny=(unsigned short*)sSurface.GetSurfacePointer(0,j);

    for (k=0 ; k<uWidth ; k++)
    {
      *pDestiny=
        (((*pSource & 0xFF000000) >> (24+pfDestiny.m_uAShiftRight))<< pfDestiny.m_uAShiftLeft)|
				(((*pSource & 0x00FF0000) >> (16+pfDestiny.m_uRShiftRight))<< pfDestiny.m_uRShiftLeft)|
			  (((*pSource & 0x0000FF00) >> ( 8+pfDestiny.m_uGShiftRight))<< pfDestiny.m_uGShiftLeft)|
			  (((*pSource & 0x000000FF) >> (	 pfDestiny.m_uBShiftRight))<< pfDestiny.m_uBShiftLeft);

      pSource++;
      pDestiny++;
    }
  }
  #else

  unsigned int  * pSource;
  unsigned short* pDestiny;

  unsigned        uSource;
  unsigned short  puSource[4];
  unsigned short  puError[4];
  unsigned short* pErrorA=new unsigned short[(uWidth+2)*4];
  unsigned short* pErrorB=new unsigned short[(uWidth+2)*4];;
  unsigned short* pUp           =pErrorA;
  unsigned short* pDown         =pErrorB;
  unsigned short* puErrorUp;
  unsigned short* puErrorDown;

  unsigned uMask[4]=
    {
      ((1<<(pfDestiny.m_uAShiftRight))-1),
      ((1<<(pfDestiny.m_uRShiftRight))-1),
      ((1<<(pfDestiny.m_uGShiftRight))-1),
      ((1<<(pfDestiny.m_uBShiftRight))-1)
    };


  memset(pUp, 0, (uWidth+2)*4*sizeof(short));            
  for (j=0 ; j<sSurface.GetPSurfaceDescriptor()->m_uiHeight ; j++)
  {        
    pSource =(unsigned int  *)GetSurfacePointer(0,j);
    pDestiny=(unsigned short*)sSurface.GetSurfacePointer(0,j);

    memset(pDown, 0, (uWidth+2)*4*sizeof(short));
    
    
    puErrorUp  =pUp+4;
    puErrorDown=pDown+4;

    for (k=0 ; k<uWidth ; k++)
    {

      uSource=*pSource;

      puSource[0]=min(255, ((uSource&0xFF000000)>>24)+(puErrorUp[0]>>4));
      puSource[1]=min(255, ((uSource&0x00FF0000)>>16)+(puErrorUp[1]>>4));
      puSource[2]=min(255, ((uSource&0x0000FF00)>> 8)+(puErrorUp[2]>>4));
      puSource[3]=min(255, ((uSource&0x000000FF)>> 0)+(puErrorUp[3]>>4));

      puError[0]=puSource[0]&uMask[0];
      puError[1]=puSource[1]&uMask[1];
      puError[2]=puSource[2]&uMask[2];
      puError[3]=puSource[3]&uMask[3];

      puErrorUp   [4]+=puError[0]*7;
      puErrorUp   [5]+=puError[1]*7;
      puErrorUp   [6]+=puError[2]*7;
      puErrorUp   [7]+=puError[3]*7;

      puErrorDown[-4]+=puError[0]*1;
      puErrorDown[-3]+=puError[1]*1;
      puErrorDown[-2]+=puError[2]*1;
      puErrorDown[-1]+=puError[3]*1;


      puErrorDown[ 0]+=puError[0]*5;
      puErrorDown[ 1]+=puError[1]*5;
      puErrorDown[ 2]+=puError[2]*5;
      puErrorDown[ 3]+=puError[3]*5;

      puErrorDown[ 4]+=puError[0]*3;
      puErrorDown[ 5]+=puError[1]*3;
      puErrorDown[ 6]+=puError[2]*3;
      puErrorDown[ 7]+=puError[3]*3;

      *pDestiny=
        (((puSource[0]) >> (pfDestiny.m_uAShiftRight))<< pfDestiny.m_uAShiftLeft)|
				(((puSource[1]) >> (pfDestiny.m_uRShiftRight))<< pfDestiny.m_uRShiftLeft)|
			  (((puSource[2]) >> (pfDestiny.m_uGShiftRight))<< pfDestiny.m_uGShiftLeft)|
			  (((puSource[3]) >> (pfDestiny.m_uBShiftRight))<< pfDestiny.m_uBShiftLeft);

      pSource++;
      pDestiny++;

      puErrorUp  +=4;
      puErrorDown+=4;
    }

    unsigned short* pAux;
    pAux=pUp;
    pUp=pDown;
    pDown=pAux;                          
  }

  delete[] pErrorA;
  delete[] pErrorB;            
  #endif

  return (0);
}

// Nombre      : 
// Parametros  : 
// Retorno     : 
// Descripcion : 
int MSurface::Convert24To16(MSurface& sSurface)
{
  MPixelFormatDescriptor pfSource, pfDestiny;
  
  unsigned char * pSource;
  unsigned short* pDestiny;                                    
  pfDestiny =sSurface.GetPSurfaceDescriptor()->m_pfPixelFormat;
  pfSource  =GetPSurfaceDescriptor()->m_pfPixelFormat;

  unsigned uSource;  
  unsigned uWidth =GetPSurfaceDescriptor()->m_uiWidth;
  unsigned uHeight=GetPSurfaceDescriptor()->m_uiHeight;
  unsigned j,k;

  #ifdef  DITHER
  

  // Para dithering
  unsigned short  puSource[3];
  unsigned short* pErrorA=new unsigned short[(uWidth+2)*3];
  unsigned short* pErrorB=new unsigned short[(uWidth+2)*3];;
  unsigned short* pUp           =pErrorA;
  unsigned short* pDown         =pErrorB;
  unsigned short* puErrorUp;
  unsigned short* puErrorDown;
  

  memset(pUp, 0, (uWidth+2)*3*sizeof(short));
  for (j=0 ; j<sSurface.GetPSurfaceDescriptor()->m_uiHeight ; j++)
  {        
    pSource =(unsigned char *)GetSurfacePointer(0,j);
    pDestiny=(unsigned short*)sSurface.GetSurfacePointer(0,j);

    memset(pDown, 0, (uWidth+2)*3*sizeof(short));
    
    unsigned uMask[3]=
    {
      ((1<<(pfDestiny.m_uRShiftRight))-1),
      ((1<<(pfDestiny.m_uGShiftRight))-1),
      ((1<<(pfDestiny.m_uBShiftRight))-1)
    };

    puErrorUp  =pUp+3;
    puErrorDown=pDown+3;

    for (k=0 ; k<uWidth ; k++)
    {
      
      uSource=(pSource[0]<<pfSource.m_uRShiftLeft) | (pSource[1]<<pfSource.m_uGShiftLeft) | (pSource[2]<<pfSource.m_uBShiftLeft);

      puSource[0]=min(255, ((uSource&0x00FF0000)>>16)+(puErrorUp[0]>>4));
      puSource[1]=min(255, ((uSource&0x0000FF00)>> 8)+(puErrorUp[1]>>4));
      puSource[2]=min(255, ((uSource&0x000000FF)>> 0)+(puErrorUp[2]>>4));

      puErrorUp[3]+=((puSource[0]&uMask[0])*7);
      puErrorUp[4]+=((puSource[1]&uMask[1])*7);
      puErrorUp[5]+=((puSource[2]&uMask[2])*7);

      puErrorDown[-3]+=((puSource[0]&uMask[0])*1);
      puErrorDown[-2]+=((puSource[1]&uMask[1])*1);
      puErrorDown[-1]+=((puSource[2]&uMask[2])*1);

      puErrorDown[0]+=((puSource[0]&uMask[0])*5);
      puErrorDown[1]+=((puSource[1]&uMask[1])*5);
      puErrorDown[2]+=((puSource[2]&uMask[2])*5);

      puErrorDown[3]+=((puSource[0]&uMask[0])*3);
      puErrorDown[4]+=((puSource[1]&uMask[1])*3);
      puErrorDown[5]+=((puSource[2]&uMask[2])*3);


      *pDestiny=
        (((puSource[0] ) >> (pfDestiny.m_uRShiftRight))<< pfDestiny.m_uRShiftLeft)|
			  (((puSource[1] ) >> (pfDestiny.m_uGShiftRight))<< pfDestiny.m_uGShiftLeft)|
			  (((puSource[2] ) >> (pfDestiny.m_uBShiftRight))<< pfDestiny.m_uBShiftLeft);


      pSource +=3;                
      pDestiny++;

      puErrorUp+=3;
      puErrorDown+=3;
    }
    unsigned short* pAux;
  pAux=pUp;
  pUp=pDown;
  pDown=pAux;
  }       
  delete[] pErrorA;
  delete[] pErrorB;

  #else     
  
  for (j=0 ; j<sSurface.GetPSurfaceDescriptor()->m_uiHeight ; j++)
  {        
    pSource =(unsigned char *)GetSurfacePointer(0,j);
    pDestiny=(unsigned short*)sSurface.GetSurfacePointer(0,j);

    for (k=0 ; k<uWidth ; k++)
    {
    

      uSource=(pSource[0]<<pfSource.m_uRShiftLeft) | (pSource[1]<<pfSource.m_uGShiftLeft) | (pSource[2]<<pfSource.m_uBShiftLeft);
      *pDestiny=
        (((uSource & 0x00FF0000) >> (16+pfDestiny.m_uRShiftRight))<< pfDestiny.m_uRShiftLeft)|
			  (((uSource & 0x0000FF00) >> ( 8+pfDestiny.m_uGShiftRight))<< pfDestiny.m_uGShiftLeft)|
			  (((uSource & 0x000000FF) >> (	 pfDestiny.m_uBShiftRight))<< pfDestiny.m_uBShiftLeft);


      pSource +=3;

    
      pDestiny++;
    }
  }
  #endif                                                                       
  return (0);
}

int MSurface::Convert32To32(MSurface& sSurface)
{
  if (sSurface.GetPSurfaceDescriptor()->m_pfPixelFormat==
      GetPSurfaceDescriptor()->m_pfPixelFormat)
  {
    unsigned i;
    for (i=0 ; i<sSurface.GetPSurfaceDescriptor()->m_uiHeight ; i++)
    {
      memcpy(
        sSurface.GetSurfacePointer(0, i),
        GetSurfacePointer(0, i),
        sSurface.GetPSurfaceDescriptor()->m_uiWidth*(sSurface.GetPSurfaceDescriptor()->m_pfPixelFormat.m_uBPP>>3));
    }
  }
  else
  {
    sSurface.ConvertFrom(*this);
  }
  
  return (0);
}

int MSurface::Convert24To32(MSurface& sSurface)
{
  sSurface.ConvertFrom(*this);
  return (0);
}



										