#include "pch.hpp"
//===========================================================================//
// File:	 Texture MipMap.cpp												 //
// Project:  GameOS					                                         //
// Contents: Handles generating mipmap levels								 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 12/18/98  AG     Created													 //
// 04/11/00  NK		Update for classes										 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Texture Manager.hpp"
#include "DirectX.hpp"


volatile __int64 TimeMipmapTextures;

#define WEIGHT(Pixel)								\
	if( Pixel&MaskAlpha )							\
	{												\
		if( ( Pixel&MaskAlpha ) == MaskAlpha )		\
		{											\
			div += 2;								\
			Blue += ((Pixel&MaskBlue )<<1);			\
			Green += ((Pixel&MaskGreen )<<1);		\
			Red += ((Pixel&MaskRed )<<1);			\
		}											\
		else										\
		{											\
			div++;									\
			Blue += (Pixel&MaskBlue);				\
			Green += (Pixel&MaskBlue);				\
			Red += (Pixel&MaskBlue);				\
		}											\
	}												

//
// If MipMapping - create all the other MipMap levels
//
void CTexInfo::GenerateMipMaps()
{
//
// Debugging information
//
	SPEW(( GROUP_TEXTURE, "Generating %d Mipmaps \"%s\"", MipMapLevels()-1, m_pName ));
#ifdef LAB_ONLY
	__int64 Temp=GetCycles();
#endif
//
// Setup Current and Next pointers in chain of mipmaps
//
	gosASSERT( m_pSysMemSurf );
	IDirectDrawSurface7* LargeTexture = m_pSysMemSurf;
	IDirectDrawSurface7* SmallTexture;

	DDSURFACEDESC2 LargeSurface;
	LargeSurface.dwSize = sizeof(LargeSurface);

	DDSURFACEDESC2 SmallSurface;
	SmallSurface.dwSize = sizeof(SmallSurface);

	DDSCAPS2 ddsCaps2;
	memset( &ddsCaps2, 0, sizeof(ddsCaps2) );
	ddsCaps2.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
//
// Run through all mipmap levels
//
	for( int Level=1; Level<MipMapLevels(); Level++ )
	{
//
// Get pointer to next smaller surface. Size=Size of smaller surface
//
		wGetAttachedSurface( LargeTexture, &ddsCaps2, &SmallTexture );
		wRelease( SmallTexture );
//
// Lock the two surfaces
//
		wLock( LargeTexture, NULL, &LargeSurface, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_READONLY, NULL );
		wLock( SmallTexture, NULL, &SmallSurface, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
//
// Get pixel masks and sizes (always same, (except size) for both surfaces)
//
		DWORD MaskRed		= SmallSurface.ddpfPixelFormat.dwRBitMask;
		DWORD MaskGreen		= SmallSurface.ddpfPixelFormat.dwGBitMask;
		DWORD MaskBlue		= SmallSurface.ddpfPixelFormat.dwBBitMask;
		DWORD MaskAlpha		= SmallSurface.ddpfPixelFormat.dwRGBAlphaBitMask;
		DWORD bpp			= SmallSurface.ddpfPixelFormat.dwRGBBitCount;
		DWORD Width			= SmallSurface.dwWidth;
		DWORD Height		= SmallSurface.dwHeight;
		BYTE* Source		= (BYTE*)LargeSurface.lpSurface;
		BYTE* Dest			= (BYTE*)SmallSurface.lpSurface;
//
// Loop through all pixels in texture
//
		switch( MipFilter() )
		{
//
// Type 0 - Box filter, average four pixels of larger texture to generate a single pixel of the smaller
//
			case 0:
			{
				if( bpp==16 )									// 16 bit texture formats may have any bit combination of ARGB.									
				{
					for( int y=0; y<Height; y++ )
					{
						for( int x=0; x<Width; x++ )
						{
							DWORD Pixel0=*(WORD*)(Source);
							DWORD Pixel1=*(WORD*)(Source+2);
							DWORD Pixel2=*(WORD*)(Source+LargeSurface.lPitch);
							DWORD Pixel3=*(WORD*)(Source+LargeSurface.lPitch+2);
							Source+=4;

							DWORD Blue;
							DWORD Green;
							DWORD Red;
							DWORD Alpha;

#if 0

							DWORD Diff = ( Pixel0 ^ Pixel1 ) | ( Pixel2 ^ Pixel3 ) | ( Pixel0 ^ Pixel2 );
							
							if( Diff == 0 )
							{
								// all pixels are the same so just use one at random
								*(WORD*)Dest=(WORD)Pixel0;
							}
							if( ( ( Pixel0 | Pixel1 | Pixel2 | Pixel3 ) & MaskAlpha ) == 0 )
							{
								// all alpha's are 0 so just use 0 for the color etc
								*(WORD*)Dest= 0;
							} else if( ( Diff & MaskAlpha ) == 0 )
							{
								// all pixels have the same alpha so weight the colors evenly
								Alpha = Pixel0 & MaskAlpha;
								Blue = MaskBlue  & (((Pixel0&MaskBlue ) + (Pixel1&MaskBlue ) + (Pixel2&MaskBlue ) + (Pixel3&MaskBlue ) )>>2);
								Green= MaskGreen & (((Pixel0&MaskGreen) + (Pixel1&MaskGreen) + (Pixel2&MaskGreen) + (Pixel3&MaskGreen) )>>2);
								Red  = MaskRed   & (((Pixel0&MaskRed  ) + (Pixel1&MaskRed  ) + (Pixel2&MaskRed  ) + (Pixel3&MaskRed  ) )>>2);
								*(WORD*)Dest=(WORD)(Red+Green+Blue+Alpha);
							}
							else
							{
								// mixed alpha so have each contribute accordingly
								Alpha = MaskAlpha & (((Pixel0&MaskAlpha) + (Pixel1&MaskAlpha) + (Pixel2&MaskAlpha) + (Pixel3&MaskAlpha) )>>2);

								int div=0;
								Blue = Red = Green = 0;
								WEIGHT(Pixel0);
								WEIGHT(Pixel1);
								WEIGHT(Pixel2);
								WEIGHT(Pixel3);


								Blue /= div;
								Red /= div;
								Green /= div;
								Blue = ( Blue > MaskBlue ) ? MaskBlue : ( Blue & MaskBlue );
								Red = ( Red > MaskBlue ) ? MaskRed : ( Red & MaskRed );
								Green = ( Green > MaskGreen ) ? MaskGreen : ( Green & MaskGreen );
								*(WORD*)Dest=(WORD)(Red+Green+Blue+Alpha);

							}
#else
								Alpha = Pixel0 & MaskAlpha;
								Blue = MaskBlue  & (((Pixel0&MaskBlue ) + (Pixel1&MaskBlue ) + (Pixel2&MaskBlue ) + (Pixel3&MaskBlue ) )>>2);
								Green= MaskGreen & (((Pixel0&MaskGreen) + (Pixel1&MaskGreen) + (Pixel2&MaskGreen) + (Pixel3&MaskGreen) )>>2);
								Red  = MaskRed   & (((Pixel0&MaskRed  ) + (Pixel1&MaskRed  ) + (Pixel2&MaskRed  ) + (Pixel3&MaskRed  ) )>>2);
								*(WORD*)Dest=(WORD)(Red+Green+Blue+Alpha);
#endif


							Dest+=2;
						}
						Dest=Dest-2*Width+SmallSurface.lPitch;
						Source=Source-Width*4+LargeSurface.lPitch*2;
					}
				}
				else												// 32 bit surface (only ARGB format supported)
				{
					gosASSERT( (MaskAlpha==0xff000000)||(MaskAlpha==0) );				// Alpha overflowing DWORD is handled

					for( int y=0; y<Height; y++ )
					{
						for( int x=0; x<Width; x++ )
						{
							DWORD Pixel0=*(DWORD*)(Source);
							DWORD Pixel1=*(DWORD*)(Source+4);
							DWORD Pixel2=*(DWORD*)(Source+LargeSurface.lPitch);
							DWORD Pixel3=*(DWORD*)(Source+LargeSurface.lPitch+4);
							Source+=8;

							DWORD Blue;
							DWORD Green;
							DWORD Red;
							DWORD Alpha;

#if 0

							DWORD Diff = ( Pixel0 ^ Pixel1 ) | ( Pixel2 ^ Pixel3 ) | ( Pixel0 ^ Pixel2 );
							
							if( Diff == 0 )
							{
								// all pixels are the same so just use one at random
								*(DWORD*)Dest=Pixel0;
							}
							if( ( ( Pixel0 | Pixel1 | Pixel2 | Pixel3 ) & MaskAlpha ) == 0 )
							{
								// all alpha's are 0 so just use 0 for the color etc
								*(DWORD*)Dest=0;
							} else if( ( Diff & MaskAlpha ) == 0 )
							{
								// all pixels have the same alpha so weight the colors evenly
								Alpha = Pixel0 & MaskAlpha;
								Blue = MaskBlue  & (((Pixel0&MaskBlue ) + (Pixel1&MaskBlue ) + (Pixel2&MaskBlue ) + (Pixel3&MaskBlue ) )>>2);
								Green= MaskGreen & (((Pixel0&MaskGreen) + (Pixel1&MaskGreen) + (Pixel2&MaskGreen) + (Pixel3&MaskGreen) )>>2);
								Red  = MaskRed   & (((Pixel0&MaskRed  ) + (Pixel1&MaskRed  ) + (Pixel2&MaskRed  ) + (Pixel3&MaskRed  ) )>>2);
								*(DWORD*)Dest=Red+Green+Blue+Alpha;
							}
							else
							{
								// mixed alpha so have each contribute accordingly
								Alpha= MaskAlpha & (((Pixel0>>2) + (Pixel1>>2) + (Pixel2>>2) + (Pixel3>>2) ));

								int div=0;
								Blue = Red = Green = 0;
								WEIGHT(Pixel0);
								WEIGHT(Pixel1);
								WEIGHT(Pixel2);
								WEIGHT(Pixel3);


								Blue /= div;
								Red /= div;
								Green /= div;
								Blue = ( Blue > MaskBlue ) ? MaskBlue : ( Blue & MaskBlue );
								Red = ( Red > MaskBlue ) ? MaskRed : ( Red & MaskRed );
								Green = ( Green > MaskGreen ) ? MaskGreen : ( Green & MaskGreen );
								*(DWORD*)Dest=Red+Green+Blue+Alpha;
							}
#else
								Alpha = Pixel0 & MaskAlpha;
								Blue = MaskBlue  & (((Pixel0&MaskBlue ) + (Pixel1&MaskBlue ) + (Pixel2&MaskBlue ) + (Pixel3&MaskBlue ) )>>2);
								Green= MaskGreen & (((Pixel0&MaskGreen) + (Pixel1&MaskGreen) + (Pixel2&MaskGreen) + (Pixel3&MaskGreen) )>>2);
								Red  = MaskRed   & (((Pixel0&MaskRed  ) + (Pixel1&MaskRed  ) + (Pixel2&MaskRed  ) + (Pixel3&MaskRed  ) )>>2);
								*(DWORD*)Dest=Red+Green+Blue+Alpha;
#endif

							Dest+=4;
						}
						Dest=Dest-4*Width+SmallSurface.lPitch;
						Source=Source-Width*8+LargeSurface.lPitch*2;
					}
				}
				break;
			}
//
// Type 1 - Point sample, take one pixel of the larger texture to generate a one pixel of the smaller
//
			case 1:
			{
				if( bpp==16 )									// 16 bit texture formats may have any bit combination of ARGB.									
				{
					for( int y=0; y<Height; y++ )
					{
						for( int x=0; x<Width; x++ )
						{
							*(WORD*)Dest=*(WORD*)(Source);
							Source+=4;
							Dest+=2;
						}
						Dest=Dest-2*Width+SmallSurface.lPitch;
						Source=Source-Width*4+LargeSurface.lPitch*2;
					}
				}
				else												// 32 bit surface (only ARGB format supported)
				{
					for( int y=0; y<Height; y++ )
					{
						for( int x=0; x<Width; x++ )
						{
							*(DWORD*)Dest=*(DWORD*)(Source);
							Source+=8;
							Dest+=4;
						}
						Dest=Dest-4*Width+SmallSurface.lPitch;
						Source=Source-Width*8+LargeSurface.lPitch*2;
					}
				}
				break;
			}
//
// Other filter types
//
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
				STOP(( "Texture mipmap filter type %d not implemented",MipFilter() ));
		}
//
// Move to smaller mipmap
//
		wUnlock( SmallTexture, NULL );
		wUnlock( LargeTexture, NULL );
		LargeTexture=SmallTexture;
	}
//
// Debugging information
//

#ifdef LAB_ONLY
	TimeMipmapTextures+=GetCycles()-Temp;
#endif
}


