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