Files
FC1/BinkSDK/examddp.c
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

740 lines
19 KiB
C

//############################################################################
//## ##
//## EXAMDDP.C ##
//## ##
//## Example of using Bink to draw onto a DirectDraw primary surface ##
//## ##
//## Author: Jeff Roberts ##
//## ##
//############################################################################
//## ##
//## Copyright (C) RAD Game Tools, Inc. ##
//## ##
//## For technical support, contact RAD Game Tools at 425 - 893 - 4300. ##
//## ##
//############################################################################
#include <windows.h>
#include <ddraw.h>
#include "bink.h"
//
// Example globals
//
static HBINK Bink = 0;
static S32 Window_x, Window_y;
static S32 Client_offset_x, Client_offset_y;
//############################################################################
//## ##
//## Open_directdraw - opens DirectDraw, the surface, and gets the surface ##
//## description. ##
//## ##
//############################################################################
static S32 Open_directdraw( HWND window,
LPDIRECTDRAW* out_directdraw,
LPDIRECTDRAWSURFACE* out_directdraw_surface,
DDSURFACEDESC* out_surface_description,
S32* out_surface_type )
{
//
// Try to open the DirectDraw object, if that fails just return 0.
//
if ( DirectDrawCreate( 0, out_directdraw, NULL ) != DD_OK )
{
*out_directdraw = 0;
*out_directdraw_surface = 0;
return( 0 );
}
//
// Try to set the DirectDraw cooperative level. If we fail, cleanup and exit.
//
if ( IDirectDraw_SetCooperativeLevel( *out_directdraw,
window,
DDSCL_NORMAL ) != DD_OK )
{
IDirectDraw_Release( *out_directdraw );
*out_directdraw = 0;
*out_directdraw_surface = 0;
return( 0 );
}
//
// Set up the surface description to request the primary surface.
//
memset( out_surface_description, 0, sizeof(DDSURFACEDESC) );
out_surface_description->dwSize = sizeof( DDSURFACEDESC );
out_surface_description->dwFlags = DDSD_CAPS;
out_surface_description->ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
//
// Now try to create the surface. If we fail, clean up and exit.
if ( IDirectDraw_CreateSurface( *out_directdraw,
out_surface_description,
out_directdraw_surface, NULL ) != DD_OK )
{
IDirectDraw_Release( *out_directdraw );
*out_directdraw = 0;
*out_directdraw_surface = 0;
return( 0 );
}
//
// Get the Bink surface type of the newly create surface.
//
*out_surface_type = BinkDDSurfaceType( *out_directdraw_surface );
//
// Is it a surface type that we can directly copy into? If not, fail with an error.
//
if ( ( *out_surface_type == -1 ) || ( *out_surface_type == BINKSURFACE8P ) )
{
MessageBox( window,
"Unsupported primary surface format.",
"Error",
MB_OK | MB_ICONSTOP );
IDirectDrawSurface_Release( *out_directdraw_surface );
IDirectDraw_Release( *out_directdraw );
*out_directdraw = 0;
*out_directdraw_surface = 0;
return( 0 );
}
return( 1 );
}
//############################################################################
//## ##
//## Close_directdraw - frees the surface and shuts down DirectDraw. ##
//## ##
//############################################################################
static void Close_directdraw( LPDIRECTDRAW* out_directdraw,
LPDIRECTDRAWSURFACE* out_directdraw_surface )
{
//
// Free the primary surface.
//
if ( out_directdraw_surface )
{
IDirectDrawSurface_Release( *out_directdraw_surface );
*out_directdraw_surface = 0;
}
//
// Free the directdraw object.
//
if ( out_directdraw )
{
IDirectDraw_Release( *out_directdraw );
*out_directdraw = 0;
}
}
//############################################################################
//## ##
//## Clear_to_black - just fills a window with black pixels. ##
//## ##
//############################################################################
static void Clear_to_black( HWND window )
{
PAINTSTRUCT ps;
HDC dc;
//
// Get the repaint DC and then fill the window with black.
//
dc = BeginPaint( window, &ps );
PatBlt( dc, 0, 0, 4096, 4096, BLACKNESS );
EndPaint( window, &ps );
}
//############################################################################
//## ##
//## WindowProc - the main window message procedure. ##
//## ##
//############################################################################
LONG FAR PASCAL WindowProc( HWND window,
UINT message,
WPARAM wparam,
LPARAM lparam )
{
switch( message )
{
//
// Just close the window if the user hits a key.
//
case WM_CHAR:
DestroyWindow( window );
break;
//
// Pause/resume the video when the focus changes.
//
case WM_KILLFOCUS:
BinkPause( Bink, 1 );
break;
case WM_SETFOCUS:
BinkPause( Bink, 0 );
break;
//
// Handle the window paint messages.
//
case WM_PAINT:
Clear_to_black( window );
return( 0 );
case WM_ERASEBKGND:
return( 1 );
//
// Handle the window being moved.
//
case WM_WINDOWPOSCHANGING:
//
// Is the window even being moved?
//
if ( ! ( ( ( WINDOWPOS* )lparam )->flags & SWP_NOMOVE ) )
{
//
// Yup, it's being moved - force it to a 4 pixel boundary.
//
( ( WINDOWPOS* )lparam )->x =
( ( ( ( WINDOWPOS* )lparam )-> x + Client_offset_x) & ~3 ) -
Client_offset_x;
//
// Now record the final window position.
//
Window_x = ( ( WINDOWPOS* ) lparam )->x + Client_offset_x;
Window_y = ( ( WINDOWPOS* ) lparam )->y + Client_offset_y;
}
break;
//
// Post the quit message.
//
case WM_DESTROY:
PostQuitMessage( 0 );
return( 0 );
}
//
// Call the OS default window procedure.
//
return( DefWindowProc( window, message, wparam, lparam ) );
}
//############################################################################
//## ##
//## Calc_window_values - calculates the X, Y, the X and Y adjustments for ##
//## the non-client areas (border, title bar) and the extra width and ##
//## height to add to a windows's size so that the video fits. ##
//## ##
//############################################################################
static void Calc_window_values( HWND window,
S32* out_window_x,
S32* out_window_y,
S32* out_client_x,
S32* out_client_y,
S32* out_extra_width,
S32* out_extra_height )
{
RECT r, c;
POINT p;
//
// Get the position of the upper-left client coordinate (in screen space).
//
p.x = 0;
p.y = 0;
ClientToScreen( window, &p );
*out_window_x = p.x;
*out_window_y = p.y;
//
// Get the current window rect (in screen space).
//
GetWindowRect( window, &r );
*out_client_x = p.x - r.left;
*out_client_y = p.y - r.top;
//
// Get the client rectangle of the window.
//
GetClientRect( window, &c );
*out_extra_width = ( r.right - r.left ) - ( c.right - c.left );
*out_extra_height = ( r.bottom - r.top ) - ( c.bottom - c.top );
}
//############################################################################
//## ##
//## Build_window_handle - creates a window class and window handle. ##
//## ##
//############################################################################
static HWND Build_window_handle( HINSTANCE instance,
HINSTANCE previous_instance,
HCURSOR* out_cursor )
{
//
// Load a cursor.
//
*out_cursor = LoadCursor( 0, IDC_ARROW );
//
// Create the window class if this is the first instance.
//
if ( !previous_instance )
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = instance;
wc.hIcon = LoadIcon( instance, MAKEINTRESOURCE( 101 ) );
wc.hCursor = *out_cursor;
wc.hbrBackground = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = "BinkExam";
//
// Try to register the class.
//
if ( !RegisterClass( &wc ) )
{
return( 0 );
}
}
//
// Return the new window with a tiny initial default size (we assume this
// initial size later on when we are calculating the non-client areas).
//
return( CreateWindow( "BinkExam",
"Bink Example Player",
WS_CAPTION|WS_POPUP|WS_CLIPCHILDREN|
WS_SYSMENU|WS_MINIMIZEBOX,
64, 64, 64, 64, 0, 0, instance,0 ) );
}
//############################################################################
//## ##
//## Next_bink_frame - advances to the next Bink frame. ##
//## ##
//############################################################################
static void Show_next_frame( HBINK bink,
HWND window,
LPDIRECTDRAWSURFACE surface,
DDSURFACEDESC* surface_description,
S32 surface_type,
S32 window_x,
S32 window_y,
S32 software_cursor )
{
S32 count = 0;
//
// Decompress the Bink frame.
//
BinkDoFrame( bink );
//
// If we have a software cursor, hide it, if we are going to overwrite it.
//
if ( software_cursor )
count = BinkCheckCursor( window,
0,0,
bink->Width, bink->Height );
//
// Try to lock the surface.
//
while ( IDirectDrawSurface_Lock( surface,
0,
surface_description,
DDLOCK_WAIT,
0 ) == DDERR_SURFACELOST )
{
//
// Surface was lost, try to restore it. If we can't, skip over the copy.
//
if ( IDirectDrawSurface_Restore( surface ) != DD_OK )
goto unable_to_lock;
}
//
// Copy the decompressed frame onto the screen.
//
BinkCopyToBuffer( bink,
surface_description->lpSurface,
surface_description->lPitch,
bink->Height,
window_x, window_y,
surface_type );
//
// Now unlock the surface.
//
IDirectDrawSurface_Unlock( surface,
surface_description->lpSurface );
unable_to_lock:
//
// Restore the software cursor, if we hid it earlier.
//
if ( software_cursor )
BinkRestoreCursor( count );
//
// Are we at the end of the movie?
//
if ( bink->FrameNum == bink->Frames )
{
//
// Yup, close the window.
//
DestroyWindow( window );
}
else
{
//
// Nope, advance to the next frame.
//
BinkNextFrame( bink );
}
}
//############################################################################
//## ##
//## Good_sleep_us - sleeps for a specified number of MICROseconds. ##
//## The task switcher in Windows has a latency of 15 ms. That means ##
//## you can ask for a Sleep of one millisecond and actually get a ##
//## sleep of 15 ms! In normal applications, this is no big deal, ##
//## however, with a video player at 30 fps, 15 ms is almost half our ##
//## frame time! The Good_sleep_us function times each sleep and keeps ##
//## the average sleep time to what you requested. It also give more ##
//## accuracy than Sleep - Good_sleep_us() uses microseconds instead of ##
//## milliseconds. ##
//## ##
//############################################################################
static void Good_sleep_us( S32 microseconds )
{
static S32 total_sleep=0;
static S32 slept_in_advance=0;
static U64 frequency=1000;
static S32 got_frequency=0;
//
// If this is the first time called, get the high-performance timer count.
//
if ( !got_frequency )
{
got_frequency = 1;
QueryPerformanceFrequency( ( LARGE_INTEGER* )&frequency );
}
total_sleep += microseconds;
//
// Have we exceeded our reserve of slept microseconds?
//
if (( total_sleep - slept_in_advance ) > 1000)
{
U64 start, end;
total_sleep -= slept_in_advance;
//
// Do the timed sleep.
//
QueryPerformanceCounter( ( LARGE_INTEGER* )&start );
Sleep( total_sleep / 1000 );
QueryPerformanceCounter( ( LARGE_INTEGER* )&end );
//
// Calculate delta time in microseconds.
//
end = ( (end - start) * (U64)1000000 ) / frequency;
//
// Keep track of how much extra we slept.
//
slept_in_advance = ( U32 )end - total_sleep;
total_sleep %= 1000;
}
}
//############################################################################
//## ##
//## WinMain - the primary function entry point ##
//## ##
//############################################################################
int PASCAL WinMain( HINSTANCE instance,
HINSTANCE previous_instance,
LPSTR cmd_line,
int cmd_show )
{
//
// Win32 locals.
//
HWND window = 0;
HCURSOR cursor = 0;
LPDIRECTDRAW directdraw = 0;
LPDIRECTDRAWSURFACE directdraw_surface = 0;
MSG msg;
DDSURFACEDESC surface_description;
//
// Example locals.
//
S32 surface_type;
S32 software_cursor;
S32 extra_width, extra_height;
//
// Try to create our window.
//
window = Build_window_handle( instance,
previous_instance,
&cursor );
if ( !window )
{
MessageBox( 0,
"Error creating window.",
"Windows",
MB_OK | MB_ICONSTOP );
return( 1 );
}
//
// Calculate the initial window positions and the client offsets.
//
Calc_window_values( window,
&Window_x, &Window_y,
&Client_offset_x, &Client_offset_y,
&extra_width, &extra_height );
//
// Try to open DirectDraw.
//
if ( !Open_directdraw( window,
&directdraw,
&directdraw_surface,
&surface_description,
&surface_type ) )
{
DestroyWindow( window );
return( 2 );
}
//
// Determine if we have a software cursor.
//
software_cursor = BinkIsSoftwareCursor( directdraw_surface, cursor );
//
// Tell Bink to use DirectSound (must be before BinkOpen)!
//
BinkSoundUseDirectSound( 0 );
//
// Try to open the Bink file.
//
Bink = BinkOpen( cmd_line, 0 );
if ( !Bink )
{
MessageBox( window,
BinkGetError( ),
"Bink Error",
MB_OK | MB_ICONSTOP );
DestroyWindow( window );
return( 3 );
}
//
// Size the window such that its client area exactly fits our Bink movie.
//
SetWindowPos( window, 0,
0, 0,
Bink->Width+extra_width,
Bink->Height+extra_height,
SWP_NOMOVE );
//
// Now display the window and start the message loop.
//
ShowWindow( window, cmd_show );
for ( ; ; )
{
//
// Are there any messages to handle?
//
if ( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
//
// Yup, handle them.
//
if ( msg.message == WM_QUIT )
break;
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
//
// Is it time for a new Bink frame?
//
if ( !BinkWait( Bink ) )
{
//
// Yup, draw the next frame.
//
Show_next_frame( Bink,
window,
directdraw_surface,
&surface_description,
surface_type,
Window_x,
Window_y,
software_cursor );
}
else
{
//
// Nope, give the rest of the system a chance to run (500 MICROseconds).
//
Good_sleep_us( 500 );
}
}
}
//
// Close the Bink file.
//
if ( Bink )
{
BinkClose( Bink );
Bink = 0;
}
//
// Clean up DirectDraw.
//
Close_directdraw( &directdraw,
&directdraw_surface );
//
// And exit.
//
return( 0 );
}
// some stuff for the RAD build utility
// @cdep pre $DefaultsWinEXE
// @cdep pre $requiresbinary($BuildDir/binkw32.lib)
// @cdep pre $requiresbinary(ddraw.lib)
// @cdep post $BuildWinEXE( , )