//-----------------------------------------------------------------------------
//
//  $Logfile:: /win32/rw_dib.c                                                $
// $Revision:: 1                                                              $
//   $Author:: Burger                                                         $
//     $Date:: 12/27/00 7:57p                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /win32/rw_dib.c                                                     $
// * 
// * 1     12/27/00 7:57p Burger
// 
// 8     8/15/98 4:10p Markd
// working on 16-bit fullscreen direct draw stuff
// 
// 7     8/14/98 3:45p Markd
// Went from 5-5-5 to 5-6-5 color format
// 
// 6     8/13/98 2:43p Markd
// second 16-bit checkin
// 
// 5     8/11/98 6:39p Markd
// first stage 16-bit software
// 
// 4     8/02/98 9:01p Markd
// Merged code 3.17
// 
// 3     12/30/97 6:06p Jimdose
// Added header text
//
// DESCRIPTION:
// This handles DIB section management under Windows.
//

#include "r_local.h"
#include "rw_win.h"

static qboolean s_systemcolors_saved;

static HGDIOBJ previously_selected_GDI_obj;

static int s_syspalindices[] =
{
	COLOR_ACTIVEBORDER,
	COLOR_ACTIVECAPTION,
	COLOR_APPWORKSPACE,
	COLOR_BACKGROUND,
	COLOR_BTNFACE,
	COLOR_BTNSHADOW,
	COLOR_BTNTEXT,
	COLOR_CAPTIONTEXT,
	COLOR_GRAYTEXT,
	COLOR_HIGHLIGHT,
	COLOR_HIGHLIGHTTEXT,
	COLOR_INACTIVEBORDER,

	COLOR_INACTIVECAPTION,
	COLOR_MENU,
	COLOR_MENUTEXT,
	COLOR_SCROLLBAR,
	COLOR_WINDOW,
	COLOR_WINDOWFRAME,
	COLOR_WINDOWTEXT
};

#define NUM_SYS_COLORS (int)(sizeof(s_syspalindices) / sizeof(int))

static COLORREF s_oldsyscolors[NUM_SYS_COLORS];

typedef struct dibinfo
{
	BITMAPINFOHEADER	header;
	RGBQUAD				acolors[256];
} dibinfo_t;

typedef struct
{
	WORD palVersion;
	WORD palNumEntries;
	PALETTEENTRY palEntries[256];
} identitypalette_t;

static identitypalette_t s_ipal;

static void DIB_SaveSystemColors( void );
static void DIB_RestoreSystemColors( void );

/*
** DIB_Init
**
** Builds our DIB section
*/
qboolean DIB_Init( unsigned char **ppbuffer, int *ppitch )
{
	dibinfo_t   dibheader;
	BITMAPINFO *pbmiDIB = ( BITMAPINFO * ) &dibheader;
	int i;

	memset( &dibheader, 0, sizeof( dibheader ) );

	/*
	** grab a DC
	*/
	if ( !sww_state.hDC )
	{
		if ( ( sww_state.hDC = GetDC( sww_state.hWnd ) ) == NULL )
			return false;
	}

	/*
	** figure out if we're running in an 8-bit display mode
	*/
 	if ( GetDeviceCaps( sww_state.hDC, RASTERCAPS ) & RC_PALETTE )
	{
		if ( sww_state.bitdepth != 8 )
			return false;

		sww_state.palettized = true;

		// save system colors
		if ( !s_systemcolors_saved )
		{
			DIB_SaveSystemColors();
			s_systemcolors_saved = true;
		}
	}
	else
	{
		sww_state.palettized = false;
	}

	/*
	** fill in the BITMAPINFO struct
	*/
	pbmiDIB->bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
	pbmiDIB->bmiHeader.biWidth    = vid.width;
	pbmiDIB->bmiHeader.biHeight   = vid.height;
	pbmiDIB->bmiHeader.biPlanes   = 1;
	pbmiDIB->bmiHeader.biBitCount = sww_state.bitdepth;
	if ( sww_state.bitdepth == 8 )
	{
		unsigned *ppal = (unsigned *) d_8to24table;

		pbmiDIB->bmiHeader.biCompression   = BI_RGB;
		pbmiDIB->bmiHeader.biSizeImage     = 0;
		pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
		pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
		pbmiDIB->bmiHeader.biClrUsed       = 256;
		pbmiDIB->bmiHeader.biClrImportant  = 256;

		/*
		** fill in the palette
		*/
		for ( i = 0; i < 256; i++ )
		{
			dibheader.acolors[i].rgbRed   = ( ppal[i] >> 0 ) & 0xff;
			dibheader.acolors[i].rgbGreen = ( ppal[i] >> 8 ) & 0xff;
			dibheader.acolors[i].rgbBlue  = ( ppal[i] >> 16) & 0xff;
		}
	}
	else
	{
		unsigned *ptr;
		pbmiDIB->bmiHeader.biCompression   = BI_BITFIELDS;
		pbmiDIB->bmiHeader.biSizeImage     = 0;
		pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
		pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
		pbmiDIB->bmiHeader.biClrUsed       = 0;
		pbmiDIB->bmiHeader.biClrImportant  = 0;

		//
		// fill in the three color masks
		//
		ptr = (unsigned *) dibheader.acolors;
		*ptr++ = RED16BITMASKSHIFTED;
		*ptr++ = GREEN16BITMASKSHIFTED;
		*ptr++ = BLUE16BITMASKSHIFTED;
	}

	/*
	** create the DIB section
	*/
	sww_state.hDIBSection = CreateDIBSection( sww_state.hDC,
						  pbmiDIB,
											 DIB_RGB_COLORS,
											 &sww_state.pDIBBase,
											 NULL,
											 0 );

	if ( sww_state.hDIBSection == NULL )
	{
		ri.Con_Printf( PRINT_ALL, "DIB_Init() - CreateDIBSection failed\n" );
		goto fail;
	}

	if ( pbmiDIB->bmiHeader.biHeight > 0 )
	{
		// bottom up
		*ppbuffer	= (unsigned char *)sww_state.pDIBBase + (( vid.height - 1 ) * vid.width * ( sww_state.bitdepth>>3 ));
		*ppitch		= -vid.width * ( sww_state.bitdepth>>3 );
	}
	else
	{
		// top down
		*ppbuffer	= (unsigned char *)sww_state.pDIBBase;
		*ppitch		= vid.width * ( sww_state.bitdepth>>3 );
	}

	/*
	** clear the DIB memory buffer
	*/
	memset( sww_state.pDIBBase, 0xff, vid.width * vid.height * ( sww_state.bitdepth>>3 ) );

	if ( ( sww_state.hdcDIBSection = CreateCompatibleDC( sww_state.hDC ) ) == NULL )
	{
		ri.Con_Printf( PRINT_ALL, "DIB_Init() - CreateCompatibleDC failed\n" );
		goto fail;
	}
	if ( ( previously_selected_GDI_obj = SelectObject( sww_state.hdcDIBSection, sww_state.hDIBSection ) ) == NULL )
	{
		ri.Con_Printf( PRINT_ALL, "DIB_Init() - SelectObject failed\n" );
		goto fail;
	}

	return true;

fail:
	DIB_Shutdown();
	return false;
}

/*
** DIB_SetPalette
**
** Sets the color table in our DIB section, and also sets the system palette
** into an identity mode if we're running in an 8-bit palettized display mode.
**
** The palette is expected to be 1024 bytes, in the format:
**
** R = offset 0
** G = offset 1
** B = offset 2
** A = offset 3
*/
void DIB_SetPalette( const unsigned char *_pal )
{
	const unsigned char *pal = _pal;
	LOGPALETTE		*pLogPal = ( LOGPALETTE * ) &s_ipal;
	RGBQUAD			colors[256];
	int				i;
	int				ret;
	HDC				hDC = sww_state.hDC;

	if ( sww_state.bitdepth != 8 )
		return;

	/*
	** set the DIB color table
	*/
	if ( sww_state.hdcDIBSection )
	{
		for ( i = 0; i < 256; i++, pal += 4 )
		{
			colors[i].rgbRed   = pal[0];
			colors[i].rgbGreen = pal[1];
			colors[i].rgbBlue  = pal[2];
			colors[i].rgbReserved = 0;
		}

		colors[0].rgbRed = 0;
		colors[0].rgbGreen = 0;
		colors[0].rgbBlue = 0;

		colors[255].rgbRed = 0xff;
		colors[255].rgbGreen = 0xff;
		colors[255].rgbBlue = 0xff;

		if ( SetDIBColorTable( sww_state.hdcDIBSection, 0, 256, colors ) == 0 )
		{
			ri.Con_Printf( PRINT_ALL, "DIB_SetPalette() - SetDIBColorTable failed\n" );
		}
	}

	/*
	** for 8-bit color desktop modes we set up the palette for maximum
	** speed by going into an identity palette mode.
	*/
	if ( sww_state.palettized )
	{
		int i;
		HPALETTE hpalOld;

		if ( SetSystemPaletteUse( hDC, SYSPAL_NOSTATIC ) == SYSPAL_ERROR )
		{
			ri.Sys_Error( ERR_FATAL, "DIB_SetPalette() - SetSystemPaletteUse() failed\n" );
		}

		/*
		** destroy our old palette
		*/
		if ( sww_state.hPal )
		{
			DeleteObject( sww_state.hPal );
			sww_state.hPal = 0;
		}

		/*
		** take up all physical palette entries to flush out anything that's currently
		** in the palette
		*/
		pLogPal->palVersion		= 0x300;
		pLogPal->palNumEntries	= 256;

		for ( i = 0, pal = _pal; i < 256; i++, pal += 4 )
		{
			pLogPal->palPalEntry[i].peRed	= pal[0];
			pLogPal->palPalEntry[i].peGreen	= pal[1];
			pLogPal->palPalEntry[i].peBlue	= pal[2];
			pLogPal->palPalEntry[i].peFlags	= PC_RESERVED | PC_NOCOLLAPSE;
		}
		pLogPal->palPalEntry[0].peRed		= 0;
		pLogPal->palPalEntry[0].peGreen		= 0;
		pLogPal->palPalEntry[0].peBlue		= 0;
		pLogPal->palPalEntry[0].peFlags		= 0;
		pLogPal->palPalEntry[255].peRed		= 0xff;
		pLogPal->palPalEntry[255].peGreen	= 0xff;
		pLogPal->palPalEntry[255].peBlue	= 0xff;
		pLogPal->palPalEntry[255].peFlags	= 0;

		if ( ( sww_state.hPal = CreatePalette( pLogPal ) ) == NULL )
		{
			ri.Sys_Error( ERR_FATAL, "DIB_SetPalette() - CreatePalette failed(%x)\n", (unsigned int) GetLastError() );
		}

		if ( ( hpalOld = SelectPalette( hDC, sww_state.hPal, FALSE ) ) == NULL )
		{
			ri.Sys_Error( ERR_FATAL, "DIB_SetPalette() - SelectPalette failed(%x)\n", (unsigned int) GetLastError() );
		}

		if ( sww_state.hpalOld == NULL )
			sww_state.hpalOld = hpalOld;

		if ( ( ret = RealizePalette( hDC ) ) != pLogPal->palNumEntries ) 
		{
			ri.Sys_Error( ERR_FATAL, "DIB_SetPalette() - RealizePalette set %d entries\n", ret );
		}
	}
}

/*
** DIB_Shutdown
*/
void DIB_Shutdown( void )
{
	if ( sww_state.palettized && s_systemcolors_saved )
		DIB_RestoreSystemColors();

	if ( sww_state.hPal )
	{
		DeleteObject( sww_state.hPal );
		sww_state.hPal = 0;
	}

	if ( sww_state.hpalOld )
	{
		SelectPalette( sww_state.hDC, sww_state.hpalOld, FALSE );
		RealizePalette( sww_state.hDC );
		sww_state.hpalOld = NULL;
	}

	if ( sww_state.hdcDIBSection )
	{
		SelectObject( sww_state.hdcDIBSection, previously_selected_GDI_obj );
		DeleteDC( sww_state.hdcDIBSection );
		sww_state.hdcDIBSection = NULL;
	}

	if ( sww_state.hDIBSection )
	{
		DeleteObject( sww_state.hDIBSection );
		sww_state.hDIBSection = NULL;
		sww_state.pDIBBase = NULL;
	}

	if ( sww_state.hDC )
	{
		ReleaseDC( sww_state.hWnd, sww_state.hDC );
		sww_state.hDC = 0;
	}
}


/*
** DIB_Save/RestoreSystemColors
*/
static void DIB_RestoreSystemColors( void )
{
	SetSystemPaletteUse( sww_state.hDC, SYSPAL_STATIC );
	SetSysColors( NUM_SYS_COLORS, s_syspalindices, s_oldsyscolors );
}

static void DIB_SaveSystemColors( void )
{
	int i;

	for ( i = 0; i < NUM_SYS_COLORS; i++ )
		s_oldsyscolors[i] = GetSysColor( s_syspalindices[i] );
}
