]> git.sesse.net Git - vlc/blob - modules/video_output/msw/direct2d.c
Qt4: Tell the window manager to keep the fullscreen controller window on top.
[vlc] / modules / video_output / msw / direct2d.c
1 /*****************************************************************************
2  * direct2d.c : Direct2D video output plugin for vlc (Win7/Vista SP2 PF Update)
3  *****************************************************************************
4  * Copyright (C) 2010 VideoLAN and AUTHORS
5  * $Id$
6  *
7  * Author: David Kaplan <david@2of1.org>
8  *
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.
13  *
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.
18  *
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_playlist.h>
36 #include <vlc_vout_display.h>
37
38 #include <windows.h>
39 #include <commctrl.h>
40
41 #include <d2d1.h>
42
43 #include "common.h"
44
45 #include <initguid.h>
46 #undef GUID_EXT
47 #define GUID_EXT
48 DEFINE_GUID(IID_ID2D1Factory, 0x6152247, 0x6f50, 0x465a, 0x92, 0x45, 0x11, 0x8b, 0xfd, 0x3b, 0x60, 0x7);
49
50 /*****************************************************************************
51  * Module descriptor
52  *****************************************************************************/
53 static int  Open (vlc_object_t *);
54 static void Close(vlc_object_t *);
55
56 #define D2D_HELP N_("Video output for Windows 7/Windows Vista with Platform update")
57
58 vlc_module_begin ()
59     set_category(CAT_VIDEO)
60     set_subcategory(SUBCAT_VIDEO_VOUT)
61     set_help(D2D_HELP)
62     set_shortname("Direct2D")
63     set_description(N_("Direct2D video output"))
64     set_capability("vout display", 180)
65     add_shortcut("direct2d")
66     set_callbacks(Open, Close)
67 vlc_module_end ()
68
69
70 /*****************************************************************************
71  * Local prototypes
72  *****************************************************************************/
73 static picture_pool_t *Pool  (vout_display_t *, unsigned);
74 static void           Prepare(vout_display_t *, picture_t *, subpicture_t *);
75 static void           Display(vout_display_t *, picture_t *, subpicture_t *);
76 static int            Control(vout_display_t *, int, va_list);
77 static void           Manage (vout_display_t *);
78
79 static int      D2D_CreateRenderTarget(vout_display_t *vd);
80 static void     D2D_ResizeRenderTarget(vout_display_t *vd);
81 static void     D2D_DestroyRenderTarget(vout_display_t *vd);
82
83 /**
84  * Initialises Direct2D vout module
85  */
86 static int Open(vlc_object_t *object)
87 {
88     vout_display_t *vd = (vout_display_t *)object;
89     vout_display_sys_t *sys;
90
91     vd->sys = sys = calloc(1, sizeof(*sys));
92     if (!sys)
93         return VLC_ENOMEM;
94
95     sys->d2_render_target = NULL;
96
97     sys->d2_dll = LoadLibrary(TEXT("D2D1.DLL"));
98     if (!sys->d2_dll) {
99         if (object->b_force)
100             msg_Err(vd, "Cannot load D2D1.DLL, aborting");
101         goto error;
102     }
103
104     D2D1_FACTORY_OPTIONS fo = {
105         D2D1_DEBUG_LEVEL_NONE
106     };
107
108     HRESULT (WINAPI *D2D1CreateFactory)(D2D1_FACTORY_TYPE, REFIID,
109                                         const D2D1_FACTORY_OPTIONS *,
110                                         void **);
111
112     D2D1CreateFactory = (void *)GetProcAddress(sys->d2_dll,
113                                                TEXT("D2D1CreateFactory"));
114     if (!D2D1CreateFactory) {
115         msg_Err(vd,
116                 "Cannot locate reference to a D2D1CreateFactory ABI in D2D1.DLL");
117         goto error;
118     }
119
120 #ifndef NDEBUG
121     msg_Dbg(vd, "D2D1.DLL loaded");
122 #endif
123
124     HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
125                                    (REFIID)&IID_ID2D1Factory,
126                                    &fo,
127                                    (void **)&sys->d2_factory);
128     if (hr != S_OK) {
129         msg_Err(vd, "Cannot create Direct2D factory (hr = 0x%x)!",
130                 (unsigned)hr);
131         goto error;
132     }
133
134     if (CommonInit(vd))
135         goto error;
136
137     if (D2D_CreateRenderTarget(vd) != VLC_SUCCESS)
138         goto error;
139
140     vout_display_info_t info = vd->info;
141     info.is_slow              = false;
142     info.has_double_click     = true;
143     info.has_hide_mouse       = false;
144     info.has_pictures_invalid = false;
145     vd->info = info;
146
147     vd->fmt.i_chroma = VLC_CODEC_RGB32; /* masks change this to BGR32 for ID2D1Bitmap */
148     vd->fmt.i_rmask  = 0x0000ff00;
149     vd->fmt.i_gmask  = 0x00ff0000;
150     vd->fmt.i_bmask  = 0xff000000;
151
152     vd->pool    = Pool;
153     vd->prepare = Prepare;
154     vd->display = Display;
155     vd->manage  = Manage;
156     vd->control = Control;
157
158     EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct2D output)");
159
160 #ifndef NDEBUG
161     msg_Dbg(vd, "Ready");
162 #endif
163
164     return VLC_SUCCESS;
165
166 error:
167     Close(VLC_OBJECT(vd));
168     return VLC_EGENERIC;
169 }
170
171 /**
172  * Close Direct2D vout
173  */
174 static void Close(vlc_object_t *object)
175 {
176     vout_display_t *vd = (vout_display_t *)object;
177
178     D2D_DestroyRenderTarget(vd);
179
180     if (vd->sys->pool)
181         picture_pool_Delete(vd->sys->pool);
182
183     CommonClean(vd);
184
185     free(vd->sys);
186 }
187
188 /**
189  * Handles pool allocations for bitmaps
190  */
191 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
192 {
193     vout_display_sys_t *sys = vd->sys;
194
195     if (!sys->pool) {
196         sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
197 #ifndef NDEBUG
198         msg_Dbg(vd, "New picture pool created");
199 #endif
200     }
201
202     return sys->pool;
203 }
204
205 /**
206  * Performs set up of ID2D1Bitmap memory ready for blitting
207  */
208 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
209 {
210     vout_display_sys_t *sys = vd->sys;
211
212     if (sys->d2_render_target && sys->d2_bitmap) {
213
214         HRESULT hr = ID2D1Bitmap_CopyFromMemory(sys->d2_bitmap,
215                                                 NULL /*&r_src*/,
216                                                 picture->p[0].p_pixels,
217                                                 picture->p[0].i_pitch);
218         if (hr != S_OK)
219             msg_Err(vd, "Failed to copy bitmap memory (hr = 0x%x)!",
220                     (unsigned)hr);
221
222 #ifndef NDEBUG
223         /*msg_Dbg(vd, "Bitmap dbg: target = %p, pitch = %d, bitmap = %p",
224                 sys->d2_render_target, pitch, sys->d2_bitmap);*/
225 #endif
226     }
227     VLC_UNUSED(subpicture);
228 }
229
230 /**
231  * Blits a scaled picture_t to the render target
232  */
233 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
234 {
235     vout_display_sys_t *sys = vd->sys;
236
237     D2D1_RECT_F r_dest = {
238         sys->rect_dest.left,
239         sys->rect_dest.top,
240         sys->rect_dest.right,
241         sys->rect_dest.bottom
242     };
243
244     if (sys->d2_render_target && sys->d2_bitmap) {
245         ID2D1HwndRenderTarget_BeginDraw(sys->d2_render_target);
246
247         ID2D1HwndRenderTarget_DrawBitmap(sys->d2_render_target,
248                                          sys->d2_bitmap,
249                                          &r_dest,
250                                          1.0f,
251                                          D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
252                                          NULL);
253
254         HRESULT hr = ID2D1HwndRenderTarget_EndDraw(sys->d2_render_target,
255                                                    NULL,
256                                                    NULL);
257         if (hr ==  D2DERR_RECREATE_TARGET) {
258             D2D_DestroyRenderTarget(vd);
259             D2D_CreateRenderTarget(vd);
260         }
261     }
262
263     picture_Release(picture);
264     VLC_UNUSED(subpicture);
265
266     CommonDisplay(vd);
267 }
268
269  /**
270   * Control event handler
271   */
272 static int Control(vout_display_t *vd, int query, va_list args)
273 {
274     return CommonControl(vd, query, args);
275 }
276
277 /**
278  * Handles surface management
279  * ID2D1RenderTargets cannot be resized and must be recreated
280  */
281 static void Manage(vout_display_t *vd)
282 {
283     vout_display_sys_t *sys = vd->sys;
284
285     CommonManage(vd);
286
287     if (sys->changes & DX_POSITION_CHANGE) {
288         D2D_ResizeRenderTarget(vd);
289         sys->changes &= ~DX_POSITION_CHANGE;
290     }
291 }
292
293 /**
294  * Creates a ID2D1HwndRenderTarget and associated ID2D1Bitmap
295  */
296 static int D2D_CreateRenderTarget(vout_display_t *vd)
297 {
298     vout_display_sys_t *sys = vd->sys;
299
300     sys->d2_render_target = NULL;
301
302     D2D1_PIXEL_FORMAT pf = {
303         DXGI_FORMAT_B8G8R8A8_UNORM,
304         D2D1_ALPHA_MODE_IGNORE
305     };
306
307     D2D1_RENDER_TARGET_PROPERTIES rtp = {
308         D2D1_RENDER_TARGET_TYPE_DEFAULT,
309         pf,
310         0,
311         0,
312         D2D1_RENDER_TARGET_USAGE_NONE,
313         D2D1_FEATURE_LEVEL_DEFAULT
314     };
315
316     D2D1_SIZE_U size = {
317         sys->rect_dest.right - sys->rect_dest.left,
318         sys->rect_dest.bottom - sys->rect_dest.top
319     };
320
321     D2D1_HWND_RENDER_TARGET_PROPERTIES hrtp = {
322         sys->hvideownd,
323         size,
324         D2D1_PRESENT_OPTIONS_IMMEDIATELY /* this might need fiddling */
325     };
326
327     HRESULT hr  = ID2D1Factory_CreateHwndRenderTarget(sys->d2_factory,
328                                                       &rtp,
329                                                       &hrtp,
330                                                       &sys->d2_render_target);
331     if (hr != S_OK) {
332         msg_Err(vd, "Cannot create render target (hvideownd = 0x%x, width = %d, height = %d, pf.format = %d, hr = 0x%x)!",
333                 (unsigned)hrtp.hwnd, hrtp.pixelSize.width,
334                 hrtp.pixelSize.height, pf.format, (unsigned)hr);
335
336         sys->d2_render_target = NULL;
337
338         return VLC_EGENERIC;
339     }
340
341     FLOAT dpi_x, dpi_y;
342
343     ID2D1Factory_GetDesktopDpi(sys->d2_factory,
344                                &dpi_x,
345                                &dpi_y);
346
347     D2D1_BITMAP_PROPERTIES bp = {
348         pf,
349         dpi_x,
350         dpi_y
351     };
352
353     D2D1_SIZE_U bitmap_size = {
354         vd->fmt.i_width,
355         vd->fmt.i_height
356     };
357
358     hr = ID2D1HwndRenderTarget_CreateBitmap(sys->d2_render_target,
359                                             bitmap_size,
360                                             NULL,
361                                             0,
362                                             &bp,
363                                             &sys->d2_bitmap);
364     if (hr != S_OK) {
365         msg_Err(vd, "Failed to create bitmap (hr = 0x%x)!", (unsigned)hr);
366
367         sys->d2_bitmap = NULL;
368         D2D_DestroyRenderTarget(vd);
369
370         return VLC_EGENERIC;
371     }
372
373 #ifndef NDEBUG
374     msg_Dbg(vd, "Render trgt dbg: dpi = %f, render_target = %p, bitmap = %p",
375             dpi_x, sys->d2_render_target, sys->d2_bitmap);
376 #endif
377
378     return VLC_SUCCESS;
379 }
380
381 /**
382  * Resizes a ID2D1HWndRenderTarget
383  */
384 static void D2D_ResizeRenderTarget(vout_display_t *vd)
385 {
386     vout_display_sys_t *sys = vd->sys;
387
388     D2D1_SIZE_U size = {
389         sys->rect_dest.right - sys->rect_dest.left,
390         sys->rect_dest.bottom - sys->rect_dest.top
391     };
392
393     HRESULT hr  = ID2D1HwndRenderTarget_Resize(sys->d2_render_target, &size);
394     if (hr != S_OK)
395         msg_Err(vd, "Cannot resize render target (width = %d, height = %d, hr = 0x%x)!",
396                 size.width, size.height, (unsigned)hr);
397 }
398
399 /**
400  * Cleans up ID2D1HwndRenderTarget and ID2D1Bitmap
401  */
402 static void D2D_DestroyRenderTarget(vout_display_t *vd)
403 {
404     vout_display_sys_t *sys = vd->sys;
405
406     if (sys->d2_render_target) {
407         ID2D1HwndRenderTarget_Release(sys->d2_render_target);
408         sys->d2_render_target = NULL;
409     }
410
411     if (sys->d2_bitmap) {
412         ID2D1Bitmap_Release(sys->d2_bitmap);
413         sys->d2_bitmap = NULL;
414     }
415
416 #ifndef NDEBUG
417     msg_Dbg(vd, "Destroyed");
418 #endif
419 }