1 /*****************************************************************************
2 * vout_directx.c: Windows DirectX video output display method
3 *****************************************************************************
4 * Copyright (C) 1998, 1999, 2000 VideoLAN
5 * $Id: vout_directx.c,v 1.6 2001/07/08 17:45:52 gbazin Exp $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 #define MODULE_NAME directx
25 #include "modules_inner.h"
29 * When the option: "Display full screen when dragging window" is enabled in
30 * Windows display properties, the overlay surface coordinates won't be updated
31 * (but it won't crash anymore ;-) I know where the problem is in the code, but * I just don't know yet of a nice way to fix this.
35 * Port this plugin to Video Output IV
38 /*****************************************************************************
41 * This plugin will use YUV overlay if supported, using overlay will result in
42 * the best video quality (hardware interpolation when rescaling the picture)
43 * and the fastest display as it requires less processing.
45 * If YUV overlay is not supported the plugin will use an RGB offscreen video
46 * surface that will be blitted onto the primary surface (display) to
47 * effectively display the picture. this fallback method enables us to display
48 * video in window mode.
49 * Another fallback method (which isn't implemented yet) would be to use the
50 * primary surface as the video buffer. This would allow for better
51 * performance but this is restricted to fullscreen video. In short,
52 * implementing this is not considered high priority.
54 *****************************************************************************/
57 #include <errno.h> /* ENOMEM */
58 #include <stdlib.h> /* free() */
59 #include <string.h> /* strerror() */
73 #include "video_output.h"
76 #include "interface.h"
80 #include "modules_export.h"
82 /*****************************************************************************
83 * vout_sys_t: video output DirectX method descriptor
84 *****************************************************************************
85 * This structure is part of the video output thread descriptor.
86 * It describes the DirectX specific properties of an output thread.
87 *****************************************************************************/
88 typedef struct vout_sys_s
91 LPDIRECTDRAW2 p_ddobject; /* DirectDraw object */
92 LPDIRECTDRAWSURFACE3 p_display; /* Display device */
93 LPDIRECTDRAWSURFACE3 p_surface; /* surface where we display the video */
94 LPDIRECTDRAWCLIPPER p_clipper; /* clipper used for blitting */
95 HINSTANCE hddraw_dll; /* handle of the opened ddraw dll */
96 HBRUSH hbrush; /* window backgound brush (color) */
97 HWND hwnd; /* Handle of the main window */
99 int i_image_width; /* size of the decoded image */
101 int i_window_width; /* size of the displayed image */
104 int i_colorkey; /* colorkey used to display the overlay */
106 boolean_t b_display_enabled;
110 boolean_t b_cursor_autohidden;
113 char *p_directx_buf[2]; /* Buffer information */
117 /*****************************************************************************
119 *****************************************************************************/
120 static int vout_Probe ( probedata_t *p_data );
121 static int vout_Create ( struct vout_thread_s * );
122 static int vout_Init ( struct vout_thread_s * );
123 static void vout_End ( struct vout_thread_s * );
124 static void vout_Destroy ( struct vout_thread_s * );
125 static int vout_Manage ( struct vout_thread_s * );
126 static void vout_Display ( struct vout_thread_s * );
127 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
128 u16 *blue, u16 *transp );
130 static int DirectXCreateWindow ( vout_thread_t *p_vout );
131 static int DirectXInitDDraw ( vout_thread_t *p_vout );
132 static int DirectXCreateDisplay ( vout_thread_t *p_vout );
133 static int DirectXCreateSurface ( vout_thread_t *p_vout );
134 static int DirectXCreateClipper ( vout_thread_t *p_vout );
135 static int DirectXUpdateOverlay ( vout_thread_t *p_vout );
136 static void DirectXCloseDDraw ( vout_thread_t *p_vout );
137 static void DirectXCloseWindow ( vout_thread_t *p_vout );
138 static void DirectXCloseDisplay ( vout_thread_t *p_vout );
139 static void DirectXCloseSurface ( vout_thread_t *p_vout );
140 static void DirectXKeepAspectRatio( vout_thread_t *p_vout, RECT *coordinates );
142 /*****************************************************************************
143 * Functions exported as capabilities. They are declared as static so that
144 * we don't pollute the namespace too much.
145 *****************************************************************************/
146 void _M( vout_getfunctions )( function_list_t * p_function_list )
148 p_function_list->pf_probe = vout_Probe;
149 p_function_list->functions.vout.pf_create = vout_Create;
150 p_function_list->functions.vout.pf_init = vout_Init;
151 p_function_list->functions.vout.pf_end = vout_End;
152 p_function_list->functions.vout.pf_destroy = vout_Destroy;
153 p_function_list->functions.vout.pf_manage = vout_Manage;
154 p_function_list->functions.vout.pf_display = vout_Display;
155 p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
158 /*****************************************************************************
159 * vout_Probe: probe the video driver and return a score
160 *****************************************************************************
161 * This function tries to initialize Windows DirectX and returns a score to
162 * the plugin manager so that it can select the best plugin.
163 *****************************************************************************/
164 static int vout_Probe( probedata_t *p_data )
167 if( TestMethod( VOUT_METHOD_VAR, "directx" ) )
172 /* Check that at least DirectX5 is installed on the computer */
178 /*****************************************************************************
179 * vout_Create: allocate DirectX video thread output method
180 *****************************************************************************
181 * This function allocates and initialize the DirectX vout method.
182 *****************************************************************************/
183 static int vout_Create( vout_thread_t *p_vout )
185 /* Allocate structure */
186 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
187 if( p_vout->p_sys == NULL )
189 intf_ErrMsg( "vout error: can't create p_sys (%s)", strerror(ENOMEM) );
193 /* Initialisations */
194 p_vout->p_sys->p_ddobject = NULL;
195 p_vout->p_sys->p_display = NULL;
196 p_vout->p_sys->p_surface = NULL;
197 p_vout->p_sys->p_clipper = NULL;
198 p_vout->p_sys->hbrush = NULL;
199 p_vout->p_sys->hwnd = NULL;
200 p_vout->p_sys->b_display_enabled = 0;
201 p_vout->b_need_render = 0; /* by default try an YUV overlay display */
203 p_vout->p_sys->b_cursor = 1; /* TODO should be done with a main_GetInt.. */
205 p_vout->p_sys->b_cursor_autohidden = 0;
206 p_vout->p_sys->i_lastmoved = mdate();
208 p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
209 VOUT_FULLSCREEN_DEFAULT );
210 p_vout->p_sys->b_overlay = main_GetIntVariable( VOUT_OVERLAY_VAR,
211 VOUT_OVERLAY_DEFAULT );
212 p_vout->p_sys->i_window_width = main_GetIntVariable( VOUT_WIDTH_VAR,
213 VOUT_WIDTH_DEFAULT );
214 p_vout->p_sys->i_window_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
215 VOUT_HEIGHT_DEFAULT );
216 /* We don't know yet the dimensions of the video so the best guess is to
217 * pick the same as the window */
218 p_vout->p_sys->i_image_width = p_vout->p_sys->i_window_width;
219 p_vout->p_sys->i_image_height = p_vout->p_sys->i_window_height;
221 /* Create a window for the video */
222 /* Creating a window under Windows also initializes the thread's event
224 if( DirectXCreateWindow( p_vout ) )
226 intf_ErrMsg( "vout error: can't create window" );
227 free( p_vout->p_sys );
231 /* Initialise DirectDraw */
232 if( DirectXInitDDraw( p_vout ) )
234 intf_ErrMsg( "vout error: can't initialise DirectDraw" );
235 DirectXCloseWindow( p_vout );
236 free( p_vout->p_sys );
240 /* Create the directx display */
241 if( DirectXCreateDisplay( p_vout ) )
243 intf_ErrMsg( "vout error: can't initialise DirectDraw" );
244 DirectXCloseDDraw( p_vout );
245 DirectXCloseWindow( p_vout );
246 free( p_vout->p_sys );
253 /*****************************************************************************
254 * vout_Init: initialize DirectX video thread output method
255 *****************************************************************************
257 *****************************************************************************/
258 static int vout_Init( vout_thread_t *p_vout )
263 /*****************************************************************************
264 * vout_End: terminate Sys video thread output method
265 *****************************************************************************
266 * Terminate an output method created by vout_Create.
267 * It is called at the end of the thread.
268 *****************************************************************************/
269 static void vout_End( vout_thread_t *p_vout )
274 /*****************************************************************************
275 * vout_Destroy: destroy Sys video thread output method
276 *****************************************************************************
277 * Terminate an output method created by vout_Create
278 *****************************************************************************/
279 static void vout_Destroy( vout_thread_t *p_vout )
281 intf_WarnMsg( 3, "vout: vout_Destroy" );
282 DirectXCloseDisplay( p_vout );
283 DirectXCloseDDraw( p_vout );
284 DirectXCloseWindow( p_vout );
286 if( p_vout->p_sys != NULL )
288 free( p_vout->p_sys );
289 p_vout->p_sys = NULL;
294 /*****************************************************************************
295 * vout_Manage: handle Sys events
296 *****************************************************************************
297 * This function should be called regularly by video output thread. It returns
298 * a non null value if an error occured.
299 *****************************************************************************/
300 static int vout_Manage( vout_thread_t *p_vout )
303 WINDOWPLACEMENT window_placement;
304 boolean_t b_dispatch_msg = TRUE;
306 while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
308 switch( msg.message )
312 intf_WarnMsg( 4, "vout: vout_Manage WM_CLOSE" );
317 intf_WarnMsg( 4, "vout: vout_Manage WM_QUIT" );
318 p_main->p_intf->b_die = 1;
322 intf_WarnMsg( 3, "vout: vout_Manage WM_MOVE" );
323 if( !p_vout->b_need_render )
325 p_vout->i_changes |= VOUT_SIZE_CHANGE;
327 /* don't create a never ending loop */
328 b_dispatch_msg = FALSE;
332 intf_WarnMsg( 3, "vout: vout_Manage WM_APP" );
333 if( !p_vout->b_need_render )
335 p_vout->i_changes |= VOUT_SIZE_CHANGE;
337 /* don't create a never ending loop */
338 b_dispatch_msg = FALSE;
343 intf_WarnMsg( 4, "vout: vout_Manage WM_PAINT" );
347 intf_WarnMsg( 4, "vout: vout_Manage WM_ERASEBKGND" );
352 intf_WarnMsg( 4, "vout: vout_Manage WM_MOUSEMOVE" );
353 if( p_vout->p_sys->b_cursor )
355 if( p_vout->p_sys->b_cursor_autohidden )
357 p_vout->p_sys->b_cursor_autohidden = 0;
358 p_vout->p_sys->i_lastmoved = mdate();
363 p_vout->p_sys->i_lastmoved = mdate();
369 /* the key events are first processed here. The next
370 * message processed by this main message loop will be the
371 * char translation of the key event */
372 intf_WarnMsg( 3, "vout: vout_Manage WM_KEYDOWN" );
377 p_main->p_intf->b_die = 1;
380 TranslateMessage(&msg);
381 b_dispatch_msg = FALSE;
385 intf_WarnMsg( 3, "vout: vout_Manage WM_CHAR" );
390 p_main->p_intf->b_die = 1;
393 case 'f': /* switch to fullscreen */
395 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
398 case 'y': /* switch to hard YUV */
400 p_vout->i_changes |= VOUT_YUV_CHANGE;
403 case 'c': /* toggle grayscale */
405 p_vout->b_grayscale = ! p_vout->b_grayscale;
406 p_vout->i_changes |= VOUT_GRAYSCALE_CHANGE;
409 case 'i': /* toggle info */
411 p_vout->b_info = ! p_vout->b_info;
412 p_vout->i_changes |= VOUT_INFO_CHANGE;
415 case 's': /* toggle scaling */
417 p_vout->b_scale = ! p_vout->b_scale;
418 p_vout->i_changes |= VOUT_SCALE_CHANGE;
421 case ' ': /* toggle interface */
422 p_vout->b_interface = ! p_vout->b_interface;
423 p_vout->i_changes |= VOUT_INTF_CHANGE;
426 case '0': network_ChannelJoin( 0 ); break;
427 case '1': network_ChannelJoin( 1 ); break;
428 case '2': network_ChannelJoin( 2 ); break;
429 case '3': network_ChannelJoin( 3 ); break;
430 case '4': network_ChannelJoin( 4 ); break;
431 case '5': network_ChannelJoin( 5 ); break;
432 case '6': network_ChannelJoin( 6 ); break;
433 case '7': network_ChannelJoin( 7 ); break;
434 case '8': network_ChannelJoin( 8 ); break;
435 case '9': network_ChannelJoin( 9 ); break;
438 if( intf_ProcessKey( p_main->p_intf,
439 (char )msg.wParam ) )
441 intf_DbgMsg( "unhandled key '%c' (%i)",
442 (char)msg.wParam, msg.wParam );
449 intf_WarnMsg( 4, "vout: vout_Manage WM Default %i",
456 /* don't create a never ending loop */
459 TranslateMessage(&msg);
460 DispatchMessage(&msg);
462 b_dispatch_msg = TRUE;
470 if( p_vout->i_changes & VOUT_SCALE_CHANGE )
472 intf_WarnMsg( 3, "vout: vout_Manage Scale Change" );
473 if( DirectXUpdateOverlay( p_vout ) )
474 /* failed so try again next time */
475 PostMessage( p_vout->p_sys->hwnd, WM_CHAR, (WPARAM)'S', 0);
476 p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
482 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
484 intf_WarnMsg( 3, "vout: vout_Manage Size Change" );
485 if( DirectXUpdateOverlay( p_vout ) )
486 /* failed so try again next time */
487 PostMessage( p_vout->p_sys->hwnd, WM_APP, 0, 0);
488 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
494 if( p_vout->i_changes & VOUT_YUV_CHANGE )
496 p_vout->b_need_render = ! p_vout->b_need_render;
498 /* Need to reopen display */
499 DirectXCloseSurface( p_vout );
500 if( DirectXCreateSurface( p_vout ) )
502 intf_ErrMsg( "error: can't reopen display after YUV change" );
506 /* Repaint the window background (needed by the overlay surface) */
507 if( !p_vout->b_need_render )
509 InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE );
510 p_vout->p_sys->b_display_enabled = 1;
511 if( DirectXUpdateOverlay( p_vout ) )
512 /* failed so try again next time */
513 PostMessage( p_vout->p_sys->hwnd, WM_CHAR, (WPARAM)'S', 0);
515 p_vout->i_changes &= ~VOUT_YUV_CHANGE;
521 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
523 p_vout->b_fullscreen = ! p_vout->b_fullscreen;
525 /* We need to switch between Maximized and Normal sized window */
526 window_placement.length = sizeof(WINDOWPLACEMENT);
527 GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
528 if( p_vout->b_fullscreen )
530 /* Maximized window */
531 window_placement.showCmd = SW_SHOWMAXIMIZED;
532 /* Change window style, no borders and no title bar */
533 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 );
539 window_placement.showCmd = SW_SHOWNORMAL;
540 /* Change window style, borders and title bar */
541 SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
542 WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
545 SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
547 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
553 if( ! p_vout->p_sys->b_cursor_autohidden &&
554 ( mdate() - p_vout->p_sys->i_lastmoved > 5000000 ) )
556 /* Hide the mouse automatically */
557 p_vout->p_sys->b_cursor_autohidden = 1;
561 if( p_vout->i_changes & VOUT_CURSOR_CHANGE )
563 p_vout->p_sys->b_cursor = ! p_vout->p_sys->b_cursor;
565 ShowCursor( p_vout->p_sys->b_cursor &&
566 ! p_vout->p_sys->b_cursor_autohidden );
568 p_vout->i_changes &= ~VOUT_CURSOR_CHANGE;
574 /*****************************************************************************
575 * vout_SetPalette: sets an 8 bpp palette
576 *****************************************************************************
577 * This function sets the palette given as an argument. It does not return
578 * anything, but could later send information on which colors it was unable
580 *****************************************************************************/
581 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
582 u16 *blue, u16 *transp)
588 /*****************************************************************************
589 * vout_Display: displays previously rendered output
590 *****************************************************************************
591 * This function send the currently rendered image to the display, wait until
592 * it is displayed and switch the two rendering buffer, preparing next frame.
593 *****************************************************************************/
594 static void vout_Display( vout_thread_t *p_vout )
602 if( (p_vout->p_sys->p_display == NULL) )
604 intf_WarnMsg( 3, "vout error: vout_Display no display!!" );
608 /* if the size of the decoded pictures has changed then we close the
609 * video surface (which doesn't have the right size anymore). */
610 i_image_width = ( p_vout->p_rendered_pic ) ?
611 p_vout->p_rendered_pic->i_width : p_vout->p_sys->i_image_width;
612 i_image_height = ( p_vout->p_rendered_pic ) ?
613 p_vout->p_rendered_pic->i_height : p_vout->p_sys->i_image_height;
615 if( p_vout->p_sys->i_image_width != i_image_width
616 || p_vout->p_sys->i_image_height != i_image_height )
618 intf_WarnMsg( 3, "vout: video surface size changed" );
619 p_vout->p_sys->i_image_width = i_image_width;
620 p_vout->p_sys->i_image_height = i_image_height;
621 DirectXCloseSurface( p_vout );
624 if( p_vout->b_need_render )
630 if( p_vout->p_sys->p_surface == NULL )
632 intf_WarnMsg( 3, "vout: no video surface, open one..." );
633 if( DirectXCreateSurface( p_vout ) )
635 intf_WarnMsg( 3, "vout: cannot open a new video surface !!" );
638 /* Display the surface */
639 p_vout->p_sys->b_display_enabled = 1;
642 /* Now get the coordinates of the window. We don't actually want the
643 * window coordinates but these of the usable surface inside the window
644 * By specification GetClientRect will always set rect_window.left and
645 * rect_window.top to 0 because the Client area is always relative to
646 * the container window */
647 GetClientRect(p_vout->p_sys->hwnd, &rect_window);
651 ClientToScreen(p_vout->p_sys->hwnd, &point_window);
652 rect_window.left = point_window.x;
653 rect_window.top = point_window.y;
655 point_window.x = rect_window.right;
656 point_window.y = rect_window.bottom;
657 ClientToScreen(p_vout->p_sys->hwnd, &point_window);
658 rect_window.right = point_window.x;
659 rect_window.bottom = point_window.y;
661 /* We want to keep the aspect ratio of the video */
662 if( p_vout->b_scale )
664 DirectXKeepAspectRatio( p_vout, &rect_window );
667 /* Blit video surface to display */
668 dxresult = IDirectDrawSurface3_Blt(p_vout->p_sys->p_display,
670 p_vout->p_sys->p_surface,
673 if( dxresult != DD_OK )
675 intf_WarnMsg( 3, "vout: could not Blit the surface" );
683 * p_vout->p_rendered_pic->p_y/u/v contains the YUV buffers to
686 /* TODO: support for streams other than 4:2:0 */
688 if( p_vout->p_sys->p_surface == NULL )
690 intf_WarnMsg( 3, "vout: no video surface, open one..." );
691 if( DirectXCreateSurface( p_vout ) )
693 intf_WarnMsg( 3, "vout: cannot open a new video surface !!" );
698 /* Lock the overlay surface */
699 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
700 ddsd.dwSize = sizeof(DDSURFACEDESC);
701 dxresult = IDirectDrawSurface3_Lock(p_vout->p_sys->p_surface, NULL,
702 &ddsd, DDLOCK_NOSYSLOCK, NULL);
703 if ( dxresult == DDERR_SURFACELOST )
705 /* Your surface can be lost (thanks to windows) so be sure
706 * to check this and restore it if needed */
707 dxresult = IDirectDrawSurface3_Restore( p_vout->p_sys->p_surface );
708 dxresult = IDirectDrawSurface3_Lock( p_vout->p_sys->p_surface,
709 NULL, &ddsd, DDLOCK_NOSYSLOCK
710 | DDLOCK_WAIT, NULL);
712 if( dxresult != DD_OK )
714 intf_WarnMsg( 3, "vout: could not lock the surface" );
718 /* Now we can do the actual image copy.
719 * The copy has to be done line by line because of the special case
720 * when the Pitch does not equal the width of the picture */
721 for( i=0; i < ddsd.dwHeight/2; i++)
723 #ifdef NONAMELESSUNION
724 /* copy Y, we copy two lines at once */
725 memcpy(ddsd.lpSurface + i*2*ddsd.u1.lPitch,
726 p_vout->p_rendered_pic->p_y + i*2*i_image_width,
728 memcpy(ddsd.lpSurface + (i*2+1)*ddsd.u1.lPitch,
729 p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
732 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
733 + i * ddsd.u1.lPitch/2,
734 p_vout->p_rendered_pic->p_v + i*i_image_width/2,
737 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
738 + (ddsd.dwHeight * ddsd.u1.lPitch/4)
739 + i * ddsd.u1.lPitch/2,
740 p_vout->p_rendered_pic->p_u + i*i_image_width/2,
743 /* copy Y, we copy two lines at once */
744 memcpy(ddsd.lpSurface + i*2*ddsd.lPitch,
745 p_vout->p_rendered_pic->p_y + i*2*i_image_width,
747 memcpy(ddsd.lpSurface + (i*2+1)*ddsd.lPitch,
748 p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
751 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch)
753 p_vout->p_rendered_pic->p_v + i*i_image_width/2,
756 memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.lPitch)
757 + (ddsd.dwHeight * ddsd.lPitch/4)
759 p_vout->p_rendered_pic->p_u + i*i_image_width/2,
761 #endif /* NONAMELESSUNION */
765 /* Unlock the Surface */
766 dxresult = IDirectDrawSurface3_Unlock(p_vout->p_sys->p_surface,
769 /* If display not enabled yet then enable */
770 if( !p_vout->p_sys->b_display_enabled )
772 p_vout->p_sys->b_display_enabled = 1;
773 DirectXUpdateOverlay( p_vout );
778 /* The first time this function is called it enables the display */
779 p_vout->p_sys->b_display_enabled = 1;
784 /* following functions are local */
787 /*****************************************************************************
788 * DirectXEventProc: This is the window event processing function.
789 *****************************************************************************
790 * On Windows, when you create a window you have to attach an event processing
791 * function to it. The aim of this function is to manage "Queued Messages" and
792 * "Nonqueued Messages".
793 * Queued Messages are those picked up and retransmitted by vout_Manage
794 * (using the GetMessage function).
795 * Nonqueued Messages are those that Windows will send directly to this
796 * function (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
797 *****************************************************************************/
798 long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
799 WPARAM wParam, LPARAM lParam )
806 intf_WarnMsg( 3, "vout: WinProc WM_APP" );
810 intf_WarnMsg( 4, "vout: WinProc WM_ACTIVED" );
814 intf_WarnMsg( 4, "vout: WinProc WM_CREATE" );
817 /* the user wants to close the window */
819 intf_WarnMsg( 4, "vout: WinProc WM_CLOSE" );
823 /* the window has been closed so shut down everything now */
825 intf_WarnMsg( 4, "vout: WinProc WM_DESTROY" );
826 PostQuitMessage( 0 );
832 case SC_SCREENSAVE: /* catch the screensaver */
833 case SC_MONITORPOWER: /* catch the monitor turn-off */
834 intf_WarnMsg( 3, "vout: WinProc WM_SYSCOMMAND" );
835 return 0; /* this stops them from happening */
841 intf_WarnMsg( 4, "vout: WinProc WM_MOVE" );
845 intf_WarnMsg( 4, "vout: WinProc WM_SIZE" );
849 intf_WarnMsg( 4, "vout: WinProc WM_MOVING" );
852 case WM_ENTERSIZEMOVE:
853 intf_WarnMsg( 4, "vout: WinProc WM_ENTERSIZEMOVE" );
857 intf_WarnMsg( 4, "vout: WinProc WM_SIZING" );
861 case WM_WINDOWPOSCHANGED:
862 intf_WarnMsg( 3, "vout: WinProc WM_WINDOWPOSCHANGED" );
863 PostMessage( hwnd, WM_APP, 0, 0);
867 case WM_WINDOWPOSCHANGING:
868 intf_WarnMsg( 3, "vout: WinProc WM_WINDOWPOSCHANGING" );
872 intf_WarnMsg( 4, "vout: WinProc WM_PAINT" );
876 intf_WarnMsg( 4, "vout: WinProc WM_ERASEBKGND" );
880 intf_WarnMsg( 4, "vout: WinProc WM Default %i", message );
885 return DefWindowProc(hwnd, message, wParam, lParam);
888 /*****************************************************************************
889 * DirectXCreateWindow: create a windows window where the video will play.
890 *****************************************************************************
891 * Before creating a direct draw surface, we need to create a window in which
892 * the video will be displayed. This window will also allow us to capture the
894 *****************************************************************************/
895 static int DirectXCreateWindow( vout_thread_t *p_vout )
898 WNDCLASSEX wc; /* window class components */
903 intf_WarnMsg( 3, "vout: WinDX WinDXCreateWindow" );
905 /* get this module's instance */
906 hInstance = GetModuleHandle(NULL);
908 /* Create a BRUSH that will be used by Windows to paint the window
910 * This window background is important for us as it will be used by the
911 * graphics card to display the overlay.
912 * This is why we carefully choose the color for this background, the goal
913 * being to choose a color which isn't complete black but nearly. We
914 * obviously don't want to use black as a colorkey for the overlay because
915 * black is one of the most used color and thus would give us undesirable
917 /* the first step is to find the colorkey we want to use. The difficulty
918 * comes from the potential dithering (depends on the display depth)
919 * because we need to know the real RGB value of the chosen colorkey */
920 hdc = GetDC( GetDesktopWindow() );
921 for( colorkey = 5; colorkey < 0xFF /*all shades of red*/; colorkey++ )
923 if( colorkey == GetNearestColor( hdc, colorkey ) )
926 intf_WarnMsg(3,"vout: DirectXCreateWindow background color:%i", colorkey);
927 ReleaseDC( p_vout->p_sys->hwnd, hdc );
929 /* create the actual brush */
930 p_vout->p_sys->hbrush = CreateSolidBrush(colorkey);
931 p_vout->p_sys->i_colorkey = (int)colorkey;
933 /* fill in the window class structure */
934 wc.cbSize = sizeof(WNDCLASSEX);
935 wc.style = 0; /* no special styles */
936 wc.lpfnWndProc = (WNDPROC)DirectXEventProc; /* event handler */
937 wc.cbClsExtra = 0; /* no extra class data */
938 wc.cbWndExtra = 0; /* no extra window data */
939 wc.hInstance = hInstance; /* instance */
940 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); /* load the vlc icon */
941 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* load a default cursor */
942 wc.hbrBackground = p_vout->p_sys->hbrush; /* background color */
943 wc.lpszMenuName = NULL; /* no menu */
944 wc.lpszClassName = "VLC DirectX"; /* use a special class */
945 wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); /* load the vlc icon */
947 /* register the window class */
948 if (!RegisterClassEx(&wc))
950 intf_WarnMsg( 3, "vout: DirectXCreateWindow register window FAILED" );
954 /* when you create a window you give the dimensions you wish it to have.
955 * Unfortunatly these dimensions will include the borders and title bar.
956 * We use the following function to find out the size of the window
957 * corresponding to the useable surface we want */
958 rect_window.top = 10;
959 rect_window.left = 10;
960 rect_window.right = rect_window.left + p_vout->p_sys->i_window_width;
961 rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
962 AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
964 /* create the window */
965 p_vout->p_sys->hwnd = CreateWindow("VLC DirectX",/* name of window class */
966 "VLC DirectX", /* window title bar text */
968 | WS_SIZEBOX | WS_VISIBLE, /* window style */
969 10, /* default X coordinate */
970 10, /* default Y coordinate */
971 rect_window.right - rect_window.left, /* window width */
972 rect_window.bottom - rect_window.top, /* window height */
973 NULL, /* no parent window */
974 NULL, /* no menu in this window */
975 hInstance, /* handle of this program instance */
976 NULL); /* no additional arguments */
978 if (p_vout->p_sys->hwnd == NULL) {
979 intf_WarnMsg( 3, "vout: DirectXCreateWindow create window FAILED" );
983 /* now display the window */
984 ShowWindow(p_vout->p_sys->hwnd, SW_SHOW);
989 /*****************************************************************************
990 * DirectXInitDDraw: Takes care of all the DirectDraw initialisations
991 *****************************************************************************
992 * This function initialise and allocate resources for DirectDraw.
993 *****************************************************************************/
994 static int DirectXInitDDraw( vout_thread_t *p_vout )
997 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
998 LPDIRECTDRAW p_ddobject;
1000 intf_WarnMsg( 3, "vout: DirectXInitDDraw" );
1002 /* load direct draw DLL */
1003 p_vout->p_sys->hddraw_dll = LoadLibrary("DDRAW.DLL");
1004 if( p_vout->p_sys->hddraw_dll == NULL )
1006 intf_WarnMsg( 3, "vout: DirectXInitDDraw failed loading ddraw.dll" );
1010 OurDirectDrawCreate =
1011 (void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate");
1012 if ( OurDirectDrawCreate == NULL )
1014 intf_ErrMsg( "vout error: DirectXInitDDraw failed GetProcAddress" );
1015 FreeLibrary( p_vout->p_sys->hddraw_dll );
1016 p_vout->p_sys->hddraw_dll = NULL;
1020 /* Initialize DirectDraw now */
1021 dxresult = OurDirectDrawCreate( NULL, &p_ddobject, NULL );
1022 if( dxresult != DD_OK )
1024 intf_ErrMsg( "vout error: DirectXInitDDraw can't initialize DDraw" );
1025 p_vout->p_sys->p_ddobject = NULL;
1026 FreeLibrary( p_vout->p_sys->hddraw_dll );
1027 p_vout->p_sys->hddraw_dll = NULL;
1031 /* Set DirectDraw Cooperative level, ie what control we want over Windows
1033 dxresult = IDirectDraw_SetCooperativeLevel( p_ddobject,
1034 p_vout->p_sys->hwnd, DDSCL_NORMAL );
1035 if( dxresult != DD_OK )
1037 intf_ErrMsg( "vout error: can't set direct draw cooperative level." );
1038 IDirectDraw_Release( p_ddobject );
1039 p_vout->p_sys->p_ddobject = NULL;
1040 FreeLibrary( p_vout->p_sys->hddraw_dll );
1041 p_vout->p_sys->hddraw_dll = NULL;
1045 /* Get the IDirectDraw2 interface */
1046 dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2,
1047 (LPVOID *)&p_vout->p_sys->p_ddobject );
1048 if( dxresult != DD_OK )
1050 intf_ErrMsg( "vout error: can't get IDirectDraw2 interface." );
1051 IDirectDraw_Release( p_ddobject );
1052 p_vout->p_sys->p_ddobject = NULL;
1053 FreeLibrary( p_vout->p_sys->hddraw_dll );
1054 p_vout->p_sys->hddraw_dll = NULL;
1059 /* Release the unused interface */
1060 IDirectDraw_Release( p_ddobject );
1066 /*****************************************************************************
1067 * DirectXCreateDisplay: create the DirectDraw display.
1068 *****************************************************************************
1069 * Create and initialize display according to preferences specified in the vout
1071 *****************************************************************************/
1072 static int DirectXCreateDisplay( vout_thread_t *p_vout )
1076 LPDIRECTDRAWSURFACE p_display;
1077 DDPIXELFORMAT ddpfPixelFormat;
1079 intf_WarnMsg( 3, "vout: DirectXCreateDisplay" );
1081 /* Now create the primary surface. This surface is what you actually see
1083 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1084 ddsd.dwSize = sizeof(DDSURFACEDESC);
1085 ddsd.dwFlags = DDSD_CAPS;
1086 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
1088 dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
1091 if( dxresult != DD_OK )
1093 intf_ErrMsg( "vout error: can't create direct draw primary surface." );
1094 p_vout->p_sys->p_display = NULL;
1098 dxresult = IDirectDrawSurface_QueryInterface( p_display,
1099 &IID_IDirectDrawSurface3,
1100 (LPVOID *)&p_vout->p_sys->p_display );
1101 if ( dxresult != DD_OK )
1103 intf_ErrMsg( "vout error: can't get IDirectDrawSurface3 interface." );
1104 IDirectDrawSurface_Release( p_display );
1105 p_vout->p_sys->p_display = NULL;
1110 /* Release the old interface */
1111 IDirectDrawSurface_Release( p_display );
1115 /* We need to fill in some information for the video output thread.
1116 * We do this here because it must be done before the video_output
1117 * thread enters its main loop - and DirectXCreateSurface can be called
1119 ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1120 IDirectDrawSurface3_GetPixelFormat( p_vout->p_sys->p_display,
1122 #ifdef NONAMELESSUNION
1123 p_vout->i_screen_depth = ddpfPixelFormat.u1.dwRGBBitCount;
1124 p_vout->i_bytes_per_pixel = ddpfPixelFormat.u1.dwRGBBitCount/8;
1126 p_vout->i_red_mask = ddpfPixelFormat.u2.dwRBitMask;
1127 p_vout->i_green_mask = ddpfPixelFormat.u3.dwGBitMask;
1128 p_vout->i_blue_mask = ddpfPixelFormat.u4.dwBBitMask;
1130 p_vout->i_screen_depth = ddpfPixelFormat.dwRGBBitCount;
1131 p_vout->i_bytes_per_pixel = ddpfPixelFormat.dwRGBBitCount/8;
1133 p_vout->i_red_mask = ddpfPixelFormat.dwRBitMask;
1134 p_vout->i_green_mask = ddpfPixelFormat.dwGBitMask;
1135 p_vout->i_blue_mask = ddpfPixelFormat.dwBBitMask;
1136 #endif /* NONAMELESSUNION */
1138 /* Create a video surface. This function will try to create an
1139 * YUV overlay first and if it can't it will create a simple RGB surface */
1140 if( DirectXCreateSurface( p_vout ) )
1142 intf_ErrMsg( "vout error: can't create a video surface." );
1143 IDirectDrawSurface3_Release( p_vout->p_sys->p_display );
1144 p_vout->p_sys->p_display = NULL;
1151 /*****************************************************************************
1152 * DirectXCreateSurface: create an YUV overlay or RGB surface for the video.
1153 *****************************************************************************
1154 * The best method of display is with an YUV overlay because the YUV->RGB
1155 * conversion is done in hardware, so we'll try to create this surface first.
1156 * If we fail, we'll try to create a plain RGB surface.
1157 * ( Maybe we could also try an RGB overlay surface, which could have hardware
1158 * scaling and which would also be faster in window mode because you don't
1159 * need to do any blitting to the main display...)
1160 *****************************************************************************/
1161 static int DirectXCreateSurface( vout_thread_t *p_vout )
1165 LPDIRECTDRAWSURFACE p_surface;
1168 intf_WarnMsg( 3, "vout: DirectXCreateSurface" );
1170 /* Disable display */
1171 p_vout->p_sys->b_display_enabled = 0;
1174 /* Probe the capabilities of the hardware */
1175 /* This is just an indication of whether or not we'll support overlay,
1176 * but with this test we don't know if we support YUV overlay */
1177 memset( &ddcaps, 0, sizeof( DDCAPS ));
1178 ddcaps.dwSize = sizeof(DDCAPS);
1179 dxresult = IDirectDraw2_GetCaps( p_vout->p_sys->p_ddobject,
1181 if(dxresult != DD_OK )
1183 intf_WarnMsg( 3,"vout error: can't get caps." );
1187 BOOL bHasOverlay, bHasColorKey, bCanStretch;
1189 /* Determine if the hardware supports overlay surfaces */
1190 bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) ==
1191 DDCAPS_OVERLAY) ? TRUE : FALSE;
1192 /* Determine if the hardware supports colorkeying */
1193 bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) ==
1194 DDCAPS_COLORKEY) ? TRUE : FALSE;
1195 /* Determine if the hardware supports scaling of the overlay surface */
1196 bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ==
1197 DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE;
1198 intf_WarnMsg( 3, "vout: Dx Caps: overlay=%i colorkey=%i stretch=%i",
1199 bHasOverlay, bHasColorKey, bCanStretch );
1202 if( !bHasOverlay ) p_vout->b_need_render = 1;
1208 /* Create the video surface */
1209 if( !p_vout->b_need_render )
1211 /* Now try to create the YUV overlay surface.
1212 * This overlay will be displayed on top of the primary surface.
1213 * A color key is used to determine whether or not the overlay will be
1214 * displayed, ie the overlay will be displayed in place of the primary
1215 * surface wherever the primary surface will have this color.
1216 * The video window has been created with a background of this color so
1217 * the overlay will be only displayed on top of this window */
1219 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1220 ddsd.dwSize = sizeof(DDSURFACEDESC);
1221 ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
1222 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
1223 ddsd.ddpfPixelFormat.dwFourCC = mmioFOURCC('Y','V','1','2');
1224 #ifdef NONAMELESSUNION
1225 ddsd.ddpfPixelFormat.u1.dwYUVBitCount = 16;
1227 ddsd.ddpfPixelFormat.dwYUVBitCount = 16;
1229 ddsd.dwFlags = DDSD_CAPS |
1233 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
1234 ddsd.dwHeight = p_vout->p_sys->i_image_height;
1235 ddsd.dwWidth = p_vout->p_sys->i_image_width;
1236 ddsd.dwBackBufferCount = 1; /* One back buffer */
1238 dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
1239 &ddsd, &p_surface, NULL );
1240 if( dxresult == DD_OK )
1242 intf_WarnMsg( 3,"vout: DirectX YUV overlay created successfully" );
1246 intf_ErrMsg( "vout error: can't create YUV overlay surface." );
1247 p_vout->b_need_render = 1;
1251 if( p_vout->b_need_render )
1253 /* Now try to create a plain RGB surface. */
1254 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1255 ddsd.dwSize = sizeof(DDSURFACEDESC);
1256 ddsd.dwFlags = DDSD_HEIGHT |
1259 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1260 ddsd.dwHeight = p_vout->p_sys->i_image_height;
1261 ddsd.dwWidth = p_vout->p_sys->i_image_width;
1263 dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject,
1264 &ddsd, &p_surface, NULL );
1265 if( dxresult == DD_OK )
1267 intf_WarnMsg( 3,"vout: DirectX RGB surface created successfully" );
1271 intf_ErrMsg( "vout error: can't create RGB surface." );
1272 p_vout->p_sys->p_surface = NULL;
1277 /* Now that the surface is created, try to get a newer DirectX interface */
1278 dxresult = IDirectDrawSurface_QueryInterface( p_surface,
1279 &IID_IDirectDrawSurface3,
1280 (LPVOID *)&p_vout->p_sys->p_surface );
1281 if ( dxresult != DD_OK )
1283 intf_ErrMsg( "vout error: can't get IDirectDrawSurface3 interface." );
1284 IDirectDrawSurface_Release( p_surface );
1285 p_vout->p_sys->p_surface = NULL;
1290 /* Release the old interface */
1291 IDirectDrawSurface_Release( p_surface );
1294 if( !p_vout->b_need_render )
1296 /* Hide the overlay for now */
1297 IDirectDrawSurface3_UpdateOverlay(p_vout->p_sys->p_surface,
1299 p_vout->p_sys->p_display,
1306 DirectXCreateClipper( p_vout );
1310 /* From now on, do some initialisation for video_output */
1312 /* if we want a valid pointer to the surface memory, we must lock
1315 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1316 ddsd.dwSize = sizeof(DDSURFACEDESC);
1317 dxresult = IDirectDrawSurface3_Lock( p_vout->p_sys->p_surface, NULL, &ddsd,
1318 DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
1319 if ( dxresult == DDERR_SURFACELOST )
1321 /* Your surface can be lost so be sure
1322 * to check this and restore it if needed */
1323 dxresult = IDirectDrawSurface3_Restore( p_vout->p_sys->p_surface );
1324 dxresult = IDirectDrawSurface3_Lock( p_vout->p_sys->p_surface,
1325 NULL, &ddsd, DDLOCK_NOSYSLOCK
1326 | DDLOCK_WAIT, NULL);
1328 if( dxresult != DD_OK )
1330 intf_ErrMsg( "vout: DirectXCreateDisplay could not lock the surface" );
1334 /* Set the pointer to the surface memory */
1335 p_vout->p_sys->p_directx_buf[ 0 ] = ddsd.lpSurface;
1336 /* back buffer, none for now */
1337 p_vout->p_sys->p_directx_buf[ 1 ] = ddsd.lpSurface;
1339 /* Set thread information */
1340 p_vout->i_width = ddsd.dwWidth;
1341 p_vout->i_height = ddsd.dwHeight;
1342 #ifdef NONAMELESSUNION
1343 p_vout->i_bytes_per_line = ddsd.u1.lPitch;
1345 p_vout->i_bytes_per_line = ddsd.lPitch;
1346 #endif /* NONAMELESSUNION */
1349 if( p_vout->b_need_render )
1351 /* For an RGB surface we need to fill in some more info */
1352 #ifdef NONAMELESSUNION
1353 p_vout->i_screen_depth = ddsd.ddpfPixelFormat.u1.dwRGBBitCount;
1354 p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.u1.dwRGBBitCount/8;
1356 p_vout->i_red_mask = ddsd.ddpfPixelFormat.u2.dwRBitMask;
1357 p_vout->i_green_mask = ddsd.ddpfPixelFormat.u3.dwGBitMask;
1358 p_vout->i_blue_mask = ddsd.ddpfPixelFormat.u4.dwBBitMask;
1360 p_vout->i_screen_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
1361 p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.dwRGBBitCount/8;
1363 p_vout->i_red_mask = ddsd.ddpfPixelFormat.dwRBitMask;
1364 p_vout->i_green_mask = ddsd.ddpfPixelFormat.dwGBitMask;
1365 p_vout->i_blue_mask = ddsd.ddpfPixelFormat.dwBBitMask;
1367 #endif /* NONAMELESSUNION */
1370 /* Unlock the Surface */
1371 dxresult = IDirectDrawSurface3_Unlock(p_vout->p_sys->p_surface,
1374 /* Set and initialize buffers */
1375 p_vout->pf_setbuffers( p_vout, p_vout->p_sys->p_directx_buf[ 0 ],
1376 p_vout->p_sys->p_directx_buf[ 1 ] );
1383 /*****************************************************************************
1384 * DirectXCreateClipper: Create a clipper that will be used when blitting the
1385 * RGB surface to the main display.
1386 *****************************************************************************
1387 * This clipper prevents us to modify by mistake anything on the screen
1388 * which doesn't belong to our window. For example when a part of our video
1389 * window is hidden by another window.
1390 *****************************************************************************/
1391 static int DirectXCreateClipper( vout_thread_t *p_vout )
1395 intf_WarnMsg( 3, "vout: DirectXCreateClipper" );
1397 /* Create the clipper */
1398 dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0,
1399 &p_vout->p_sys->p_clipper, NULL );
1400 if( dxresult != DD_OK )
1402 intf_WarnMsg( 3, "vout: DirectXCreateClipper can't create clipper." );
1403 IDirectDrawSurface_Release( p_vout->p_sys->p_clipper );
1404 p_vout->p_sys->p_clipper = NULL;
1408 /* associate the clipper to the window */
1409 dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
1410 p_vout->p_sys->hwnd);
1411 if( dxresult != DD_OK )
1414 "vout: DirectXCreateClipper can't attach clipper to window." );
1415 IDirectDrawSurface_Release( p_vout->p_sys->p_clipper );
1416 p_vout->p_sys->p_clipper = NULL;
1420 /* associate the clipper with the surface */
1421 dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
1422 p_vout->p_sys->p_clipper);
1423 if( dxresult != DD_OK )
1426 "vout: DirectXCreateClipper can't attach clipper to surface." );
1427 IDirectDrawSurface_Release( p_vout->p_sys->p_clipper );
1428 p_vout->p_sys->p_clipper = NULL;
1436 /*****************************************************************************
1437 * DirectXUpdateOverlay: Move or resize overlay surface on video display.
1438 *****************************************************************************
1439 * This function is used to move or resize an overlay surface on the screen.
1440 * Ususally the overlay is moved by the user and thus, by a move or resize
1441 * event (in vout_Manage).
1442 *****************************************************************************/
1443 static int DirectXUpdateOverlay( vout_thread_t *p_vout )
1446 RECT rect_window, rect_window_backup, rect_image;
1451 DDPIXELFORMAT pixel_format;
1454 if( p_vout->p_sys->p_surface == NULL || p_vout->b_need_render )
1456 intf_WarnMsg( 3, "vout: DirectXUpdateOverlay no overlay !!" );
1460 if( !p_vout->p_rendered_pic )
1462 intf_WarnMsg( 3, "vout: DirectXUpdateOverlay p_rendered_pic=NULL !" );
1466 if( !p_vout->p_sys->b_display_enabled )
1471 /* Now get the coordinates of the window. We don't actually want the
1472 * window coordinates but these of the usable surface inside the window.
1473 * By specification GetClientRect will always set rect_window.left and
1474 * rect_window.top to 0 because the Client area is always relative to the
1475 * container window */
1476 GetClientRect(p_vout->p_sys->hwnd, &rect_window);
1480 ClientToScreen(p_vout->p_sys->hwnd, &point_window);
1481 rect_window.left = point_window.x;
1482 rect_window.top = point_window.y;
1484 point_window.x = rect_window.right;
1485 point_window.y = rect_window.bottom;
1486 ClientToScreen(p_vout->p_sys->hwnd, &point_window);
1487 rect_window.right = point_window.x;
1488 rect_window.bottom = point_window.y;
1491 /* We want to keep the aspect ratio of the video */
1492 if( p_vout->b_scale )
1494 DirectXKeepAspectRatio( p_vout, &rect_window );
1497 /* It seems we can't feed the UpdateOverlay directdraw function with
1498 * negative values so we have to clip the computed rectangles */
1499 memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
1500 ddsd.dwSize = sizeof(DDSURFACEDESC);
1501 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
1502 IDirectDraw2_GetDisplayMode( p_vout->p_sys->p_ddobject, &ddsd );
1504 rect_window_backup = rect_window;
1506 /* Clip the destination window */
1507 rect_window.left = (rect_window.left < 0) ? 0 : rect_window.left;
1508 rect_window.right = (rect_window.right < 0) ? 0 : rect_window.right;
1509 rect_window.top = (rect_window.top < 0) ? 0 : rect_window.top;
1510 rect_window.bottom = (rect_window.bottom < 0) ? 0 : rect_window.bottom;
1512 rect_window.left = (rect_window.left > ddsd.dwWidth) ? ddsd.dwWidth
1514 rect_window.right = (rect_window.right > ddsd.dwWidth) ? ddsd.dwWidth
1515 : rect_window.right;
1516 rect_window.top = (rect_window.top > ddsd.dwHeight) ? ddsd.dwHeight
1518 rect_window.bottom = (rect_window.bottom > ddsd.dwHeight) ? ddsd.dwHeight
1519 : rect_window.bottom;
1521 intf_WarnMsg( 3, "vout: DirectXUpdateOverlay window coords: %i,%i,%i,%i",
1522 rect_window.left, rect_window.top,
1523 rect_window.right, rect_window.bottom);
1525 /* the 2 following lines are to fix a bug when click on Windows desktop */
1526 if( (rect_window.right-rect_window.left)==0 ||
1527 (rect_window.bottom-rect_window.top)==0 ) return 0;
1529 /* Clip the source image */
1530 rect_image.left = ( rect_window.left == rect_window_backup.left ) ? 0
1531 : labs(rect_window_backup.left - rect_window.left) *
1532 p_vout->p_rendered_pic->i_width /
1533 (rect_window_backup.right - rect_window_backup.left);
1534 rect_image.right = ( rect_window.right == rect_window_backup.right ) ?
1535 p_vout->p_rendered_pic->i_width
1536 : p_vout->p_rendered_pic->i_width -
1537 labs(rect_window_backup.right - rect_window.right) *
1538 p_vout->p_rendered_pic->i_width /
1539 (rect_window_backup.right - rect_window_backup.left);
1540 rect_image.top = ( rect_window.top == rect_window_backup.top ) ? 0
1541 : labs(rect_window_backup.top - rect_window.top) *
1542 p_vout->p_rendered_pic->i_height /
1543 (rect_window_backup.bottom - rect_window_backup.top);
1544 rect_image.bottom = ( rect_window.bottom == rect_window_backup.bottom ) ?
1545 p_vout->p_rendered_pic->i_height
1546 : p_vout->p_rendered_pic->i_height -
1547 labs(rect_window_backup.bottom - rect_window.bottom) *
1548 p_vout->p_rendered_pic->i_height /
1549 (rect_window_backup.bottom - rect_window_backup.top);
1551 intf_WarnMsg( 3, "vout: DirectXUpdateOverlay image coords: %i,%i,%i,%i",
1552 rect_image.left, rect_image.top,
1553 rect_image.right, rect_image.bottom);
1555 /* compute the colorkey pixel value from the RGB value we've got */
1556 memset( &pixel_format, 0, sizeof( DDPIXELFORMAT ));
1557 pixel_format.dwSize = sizeof( DDPIXELFORMAT );
1558 dxresult = IDirectDrawSurface3_GetPixelFormat( p_vout->p_sys->p_display,
1560 if( dxresult != DD_OK )
1561 intf_WarnMsg( 3, "vout: DirectXUpdateOverlay GetPixelFormat failed" );
1562 dw_colorkey = (DWORD)p_vout->p_sys->i_colorkey;
1563 #ifdef NONAMELESSUNION
1564 dw_colorkey = (DWORD)((( dw_colorkey * pixel_format.u2.dwRBitMask) / 255)
1565 & pixel_format.u2.dwRBitMask);
1567 dw_colorkey = (DWORD)((( dw_colorkey * pixel_format.dwRBitMask) / 255)
1568 & pixel_format.dwRBitMask);
1571 /* Position and show the overlay */
1572 memset(&ddofx, 0, sizeof(DDOVERLAYFX));
1573 ddofx.dwSize = sizeof(DDOVERLAYFX);
1574 ddofx.dckDestColorkey.dwColorSpaceLowValue = dw_colorkey;
1575 ddofx.dckDestColorkey.dwColorSpaceHighValue = dw_colorkey;
1577 dwFlags = DDOVER_KEYDESTOVERRIDE | DDOVER_SHOW;
1579 dxresult = IDirectDrawSurface3_UpdateOverlay(p_vout->p_sys->p_surface,
1581 p_vout->p_sys->p_display,
1585 if(dxresult != DD_OK)
1588 "vout: DirectXUpdateOverlay can't move or resize overlay" );
1594 /*****************************************************************************
1595 * DirectXCloseWindow: close the window created by DirectXCreateWindow
1596 *****************************************************************************
1597 * This function returns all resources allocated by DirectXCreateWindow.
1598 *****************************************************************************/
1599 static void DirectXCloseWindow( vout_thread_t *p_vout )
1601 HINSTANCE hInstance;
1603 intf_WarnMsg( 3, "vout: DirectXCloseWindow" );
1604 if( p_vout->p_sys->hwnd != NULL )
1606 DestroyWindow( p_vout->p_sys->hwnd);
1607 p_vout->p_sys->hwnd = NULL;
1610 hInstance = GetModuleHandle(NULL);
1611 UnregisterClass( "VLC DirectX", /* class name */
1612 hInstance ); /* handle to application instance */
1614 /* free window background brush */
1615 if( p_vout->p_sys->hwnd != NULL )
1617 DeleteObject( p_vout->p_sys->hbrush );
1618 p_vout->p_sys->hbrush = NULL;
1622 /*****************************************************************************
1623 * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw
1624 *****************************************************************************
1625 * This function returns all resources allocated by DirectXInitDDraw.
1626 *****************************************************************************/
1627 static void DirectXCloseDDraw( vout_thread_t *p_vout )
1629 intf_WarnMsg(3, "vout: DirectXCloseDDraw" );
1630 if( p_vout->p_sys->p_ddobject != NULL )
1632 IDirectDraw2_Release(p_vout->p_sys->p_ddobject);
1633 p_vout->p_sys->p_ddobject = NULL;
1636 if( p_vout->p_sys->hddraw_dll != NULL )
1638 FreeLibrary( p_vout->p_sys->hddraw_dll );
1639 p_vout->p_sys->hddraw_dll = NULL;
1643 /*****************************************************************************
1644 * DirectXCloseDisplay: close and reset the DirectX display device
1645 *****************************************************************************
1646 * This function returns all resources allocated by DirectXCreateDisplay.
1647 *****************************************************************************/
1648 static void DirectXCloseDisplay( vout_thread_t *p_vout )
1650 intf_WarnMsg( 3, "vout: DirectXCloseDisplay" );
1651 if( p_vout->p_sys->p_display != NULL )
1653 DirectXCloseSurface( p_vout );
1655 intf_WarnMsg( 3, "vout: DirectXCloseDisplay display" );
1656 IDirectDraw2_Release( p_vout->p_sys->p_display );
1657 p_vout->p_sys->p_display = NULL;
1661 /*****************************************************************************
1662 * DirectXCloseSurface: close the YUV overlay or RGB surface.
1663 *****************************************************************************
1664 * This function returns all resources allocated by the surface.
1665 * We also call this function when the decoded picture change its dimensions
1666 * (in that case we close the overlay surface and reopen another with the
1667 * right dimensions).
1668 *****************************************************************************/
1669 static void DirectXCloseSurface( vout_thread_t *p_vout )
1671 intf_WarnMsg( 3, "vout: DirectXCloseSurface" );
1672 if( p_vout->p_sys->p_surface != NULL )
1674 intf_WarnMsg( 3, "vout: DirectXCloseSurface surface" );
1675 IDirectDraw2_Release( p_vout->p_sys->p_surface );
1676 p_vout->p_sys->p_surface = NULL;
1679 if( p_vout->p_sys->p_clipper != NULL )
1681 intf_WarnMsg( 3, "vout: DirectXCloseSurface clipper" );
1682 IDirectDraw2_Release( p_vout->p_sys->p_clipper );
1683 p_vout->p_sys->p_clipper = NULL;
1686 /* Disable any display */
1687 p_vout->p_sys->b_display_enabled = 0;
1690 /*****************************************************************************
1691 * DirectXKeepAspectRatio:
1692 *****************************************************************************
1693 * This function adjusts the coordinates of the video rectangle to keep the
1694 * aspect/ratio of the video.
1695 *****************************************************************************/
1696 static void DirectXKeepAspectRatio( vout_thread_t *p_vout, RECT *rect_window )
1699 if( !p_vout->p_rendered_pic ) return;
1701 switch( p_vout->p_rendered_pic->i_aspect_ratio )
1703 case AR_16_9_PICTURE:
1704 if( ((rect_window->right-rect_window->left)*9)
1705 > ((rect_window->bottom-rect_window->top)*16) )
1708 temp = (rect_window->bottom-rect_window->top)*16/9;
1709 temp = (rect_window->right-rect_window->left) - temp;
1710 rect_window->left += (temp/2);
1711 rect_window->right -= (temp/2);
1716 temp = (rect_window->right-rect_window->left)*9/16;
1717 temp = (rect_window->bottom-rect_window->top) - temp;
1718 rect_window->top += (temp/2);
1719 rect_window->bottom -= (temp/2);
1723 case AR_221_1_PICTURE:
1724 if( ((rect_window->right-rect_window->left)*100)
1725 > ((rect_window->bottom-rect_window->top)*221) )
1728 temp = (rect_window->bottom-rect_window->top)*221/100;
1729 temp = (rect_window->right-rect_window->left) - temp;
1730 rect_window->left += (temp/2);
1731 rect_window->right -= (temp/2);
1736 temp = (rect_window->right-rect_window->left)*100/221;
1737 temp = (rect_window->bottom-rect_window->top) - temp;
1738 rect_window->top += (temp/2);
1739 rect_window->bottom -= (temp/2);
1743 case AR_3_4_PICTURE:
1744 if( ((rect_window->right-rect_window->left)*3)
1745 > ((rect_window->bottom-rect_window->top)*4) )
1748 temp = (rect_window->bottom-rect_window->top)*4/3;
1749 temp = (rect_window->right-rect_window->left) - temp;
1750 rect_window->left += (temp/2);
1751 rect_window->right -= (temp/2);
1756 temp = (rect_window->right-rect_window->left)*3/4;
1757 temp = (rect_window->bottom-rect_window->top) - temp;
1758 rect_window->top += (temp/2);
1759 rect_window->bottom -= (temp/2);
1763 case AR_SQUARE_PICTURE:
1765 if( (rect_window->right-rect_window->left)
1766 > (rect_window->bottom-rect_window->top) )
1769 temp = (rect_window->bottom-rect_window->top);
1770 temp = (rect_window->right-rect_window->left) - temp;
1771 rect_window->left += (temp/2);
1772 rect_window->right -= (temp/2);
1777 temp = (rect_window->right-rect_window->left);
1778 temp = (rect_window->bottom-rect_window->top) - temp;
1779 rect_window->top += (temp/2);
1780 rect_window->bottom -= (temp/2);