├── .gitignore ├── img └── capture.png ├── Cargo.toml ├── readme.md ├── Cargo.lock └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /img/capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabdube/D2D1_rs_simple_example/HEAD/img/capture.png -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "d2d1_example" 3 | version = "1.0.0" 4 | authors = ["dberube4 "] 5 | build = "build.rs" 6 | 7 | [dependencies] 8 | winapi = "0.2" 9 | user32-sys = "0.1.2" 10 | kernel32-sys = "0.2.1" -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Direct2D from Rust 2 | 3 | A simple yet very unsafe example to demonstrate how to use Direct2D from rust. Window creation and all that stuff is also included. 4 | 5 | ### Pretty picture 6 | ![](img/capture.png) 7 | 8 | ### Requirements 9 | 10 | - Lastest version of rust nightly (MSVC ABI) 11 | - Only tested on 64bits 12 | - winapi 13 | - user32-sys 14 | - kernel32-sys 15 | 16 | ### Note 17 | This example is heavily inspired from this tutorial: 18 | https://msdn.microsoft.com/en-us/library/windows/desktop/dd370994%28v=vs.85%29.aspx 19 | 20 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "d2d1_example" 3 | version = "1.0.0" 4 | dependencies = [ 5 | "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 7 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "kernel32-sys" 12 | version = "0.2.1" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | dependencies = [ 15 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "user32-sys" 21 | version = "0.1.2" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 26 | ] 27 | 28 | [[package]] 29 | name = "winapi" 30 | version = "0.2.5" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "winapi-build" 35 | version = "0.1.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | 38 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate winapi; 2 | extern crate user32; 3 | extern crate kernel32; 4 | 5 | use winapi::*; 6 | use user32::*; 7 | use kernel32::*; 8 | 9 | use std::ptr::{null_mut, null}; 10 | use std::mem::{size_of, uninitialized, transmute}; 11 | 12 | /////// 13 | // EXTERNS 14 | /////// 15 | 16 | #[cfg(any(target_arch = "x86_64"))] 17 | extern "system" { 18 | pub fn D2D1CreateFactory( 19 | factoryType: D2D1_FACTORY_TYPE, 20 | riid: REFIID, 21 | pFactoryOptions: *const D2D1_FACTORY_OPTIONS, 22 | ppIFactory: *mut *mut ID2D1Factory 23 | ) -> HRESULT; 24 | } 25 | 26 | /////// 27 | // STRUCTURES 28 | /////// 29 | 30 | pub struct MyAppResources{ 31 | render_target: *mut ID2D1HwndRenderTarget, 32 | brush1: *mut ID2D1SolidColorBrush, 33 | brush2: *mut ID2D1SolidColorBrush 34 | } 35 | 36 | pub struct MyApp{ 37 | resources: MyAppResources, 38 | factory: *mut ID2D1Factory, 39 | hwnd: HWND, 40 | } 41 | 42 | 43 | /////// 44 | // D2D1 SETUP 45 | /////// 46 | 47 | /* 48 | Create a D2D1CreateFactory 49 | */ 50 | unsafe fn setup_d2d_factory(app: &mut MyApp){ 51 | let null_options: *const D2D1_FACTORY_OPTIONS = null(); 52 | let mut factory: *mut ID2D1Factory = null_mut(); 53 | 54 | let result = D2D1CreateFactory( 55 | D2D1_FACTORY_TYPE_SINGLE_THREADED, 56 | &UuidOfID2D1Factory, 57 | null_options, 58 | &mut factory 59 | ); 60 | 61 | if result != S_OK{ 62 | panic!("Could not create D2D1 factory."); 63 | } 64 | 65 | app.factory = factory; 66 | } 67 | 68 | /* 69 | Create the ressource used when drawing in the window. 70 | 71 | */ 72 | unsafe fn setup_d2d_resources(app: &mut MyApp){ 73 | 74 | //Check if the resources are already allocated. 75 | if !app.resources.render_target.is_null(){ 76 | return; 77 | }else if app.factory.is_null(){ 78 | panic!("Cannot initialize resources without a factory!"); 79 | } 80 | 81 | let hwnd = app.hwnd; 82 | let mut rc: RECT = uninitialized(); 83 | 84 | let mut resources = MyAppResources{ 85 | render_target: null_mut(), 86 | brush1: null_mut(), 87 | brush2: null_mut(), 88 | }; 89 | 90 | /* 91 | Structures for CreateHwndRenderTarget 92 | */ 93 | GetClientRect(hwnd, &mut rc); 94 | let size = D2D_SIZE_U{width: (rc.right-rc.left) as u32, 95 | height: (rc.bottom-rc.top) as u32}; 96 | 97 | let pixel_format = D2D1_PIXEL_FORMAT{ 98 | format: DXGI_FORMAT_B8G8R8A8_UNORM.0, 99 | alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED 100 | }; 101 | 102 | let render_props = D2D1_RENDER_TARGET_PROPERTIES{ 103 | _type: D2D1_RENDER_TARGET_TYPE_DEFAULT, 104 | pixelFormat: pixel_format, 105 | dpiX: 0.0, dpiY: 0.0, 106 | usage: D2D1_RENDER_TARGET_USAGE_NONE, 107 | minLevel: D2D1_FEATURE_LEVEL_DEFAULT 108 | }; 109 | 110 | let hwnd_render_props = D2D1_HWND_RENDER_TARGET_PROPERTIES{ 111 | hwnd: hwnd, 112 | pixelSize: size, 113 | presentOptions: D2D1_PRESENT_OPTIONS_NONE 114 | }; 115 | 116 | /* 117 | Structures for ID2D1SolidColorBrush 118 | */ 119 | let null_properties: *const D2D1_BRUSH_PROPERTIES = null(); 120 | let gray = D2D1_COLOR_F{r: 0.345, g: 0.423, b: 0.463, a: 1.0}; 121 | let red = D2D1_COLOR_F{r: 0.941, g: 0.353, b: 0.392, a: 1.0}; 122 | 123 | /* 124 | Allocate the resources 125 | */ 126 | 127 | let factory: &mut ID2D1Factory = &mut *app.factory; 128 | let mut rt: &mut ID2D1HwndRenderTarget; 129 | 130 | if factory.CreateHwndRenderTarget(&render_props, &hwnd_render_props, &mut resources.render_target) != S_OK{ 131 | panic!("Could not create render target."); 132 | } 133 | 134 | rt = transmute(resources.render_target); 135 | 136 | if rt.CreateSolidColorBrush(&gray, null_properties, &mut resources.brush1) != S_OK{ 137 | panic!("Could not create brush!"); 138 | } 139 | 140 | if rt.CreateSolidColorBrush(&red, null_properties, &mut resources.brush2) != S_OK{ 141 | panic!("Could not create brush!"); 142 | } 143 | 144 | app.resources = resources; 145 | } 146 | 147 | 148 | /* 149 | Release the resources used by Direct2D 150 | */ 151 | unsafe fn clean_d2d_resources(app: &mut MyApp){ 152 | if !app.resources.render_target.is_null(){ 153 | (*app.resources.brush1).Release(); 154 | (*app.resources.brush2).Release(); 155 | (*app.resources.render_target).Release(); 156 | 157 | app.resources.brush1 = null_mut(); 158 | app.resources.brush2 = null_mut(); 159 | app.resources.render_target = null_mut(); 160 | } 161 | } 162 | 163 | /* 164 | Release the resources used by Direct2D 165 | */ 166 | unsafe fn clean_d2d(app: &mut MyApp){ 167 | clean_d2d_resources(app); 168 | 169 | if !app.factory.is_null(){ 170 | (*app.factory).Release(); 171 | app.factory = null_mut(); 172 | } 173 | 174 | } 175 | 176 | /////// 177 | // WINDOW PROCEDURE 178 | /////// 179 | 180 | /* 181 | Painting event 182 | */ 183 | unsafe fn render_window(myapp: &mut MyApp) -> HRESULT{ 184 | let identity = D2D1_MATRIX_3X2_F{ 185 | matrix:[[1.0, 0.0, 0.0], 186 | [1.0, 1.0, 0.0]] 187 | }; 188 | 189 | let white = D2D1_COLOR_F{r:1.0, g:1.0, b:1.0, a:1.0}; 190 | 191 | let render = &mut *myapp.resources.render_target; 192 | let mut render_size = D2D1_SIZE_F{width: 0.0, height: 0.0}; 193 | 194 | render.BeginDraw(); 195 | render.Clear(&white); 196 | 197 | render.SetTransform(&identity); 198 | render.GetSize(&mut render_size); 199 | 200 | 201 | // Draw a grid background. 202 | let mut count: FLOAT = 0.0; 203 | while count < render_size.width{ 204 | render.DrawLine( 205 | D2D_POINT_2F{x: count, y: 0.0}, 206 | D2D_POINT_2F{x: count, y: render_size.height}, 207 | transmute(myapp.resources.brush1), 208 | 0.5, 209 | null_mut() 210 | ); 211 | 212 | count += 10.0; 213 | } 214 | 215 | count = 0.0; 216 | while count < render_size.height{ 217 | render.DrawLine( 218 | D2D_POINT_2F{x: 0.0, y: count}, 219 | D2D_POINT_2F{x: render_size.width, y: count}, 220 | transmute(myapp.resources.brush1), 221 | 0.5, 222 | null_mut() 223 | ); 224 | 225 | count += 10.0; 226 | } 227 | 228 | // Draw two rectangles. 229 | let rx = render_size.width/2.0; 230 | let ry = render_size.height/2.0; 231 | let rect1 = D2D1_RECT_F{left: rx-50.0, right: rx+50.0, top: ry-50.0, bottom: ry+50.0}; 232 | let rect2 = D2D1_RECT_F{left: rx-100.0, right: rx+100.0, top: ry-100.0, bottom: ry+100.0}; 233 | 234 | render.FillRectangle(&rect1, transmute(myapp.resources.brush1)); 235 | render.DrawRectangle(&rect2, transmute(myapp.resources.brush2), 3.0, null_mut()); 236 | 237 | 238 | render.EndDraw(null_mut(), null_mut()) 239 | } 240 | 241 | unsafe extern "system" fn wndproc(hwnd: HWND, msg: UINT, w: WPARAM, l: LPARAM) -> LRESULT{ 242 | let mut result: (LPARAM, bool) = (1, true); 243 | let myapp_ptr = GetWindowLongPtrW(hwnd, 0); 244 | let myapp: &mut MyApp = transmute(myapp_ptr); 245 | 246 | match msg{ 247 | WM_PAINT =>{ 248 | //Recreate the resources if the render target needs to be rebuilt 249 | setup_d2d_resources(myapp); 250 | 251 | // Render the window & check if the resources needs to be recreated. 252 | if render_window(myapp) == D2DERR_RECREATE_TARGET{ 253 | clean_d2d_resources(myapp); 254 | } 255 | }, 256 | WM_SIZE => { 257 | if myapp_ptr != 0{ 258 | let width = GET_X_LPARAM(l) as u32; 259 | let height = GET_Y_LPARAM(l) as u32; 260 | let render_size = D2D_SIZE_U{width: width, height: height}; 261 | 262 | let render = &mut *myapp.resources.render_target; 263 | render.Resize(&render_size); 264 | }else{ 265 | result = (0, false); 266 | } 267 | }, 268 | WM_DESTROY =>{ 269 | PostQuitMessage(0); 270 | }, 271 | WM_CREATE => { 272 | SetWindowLongPtrW(hwnd, 0, 0); 273 | }, 274 | _ => {result = (0, false);} 275 | } 276 | 277 | match result.1{ 278 | true => result.0, 279 | false => DefWindowProcW(hwnd, msg, w, l) 280 | } 281 | } 282 | 283 | /////// 284 | // WINDOW SETUP 285 | /////// 286 | 287 | /* 288 | Create the window class. 289 | */ 290 | unsafe fn setup_class(class_name: &Vec){ 291 | let null_icon: HICON = null_mut(); 292 | let null_background: HBRUSH = null_mut(); 293 | let null_name: *const WCHAR = null(); 294 | let module = GetModuleHandleW(null_name); 295 | 296 | let class = 297 | WNDCLASSEXW{ 298 | cbSize: size_of::() as UINT, 299 | style: CS_HREDRAW | CS_VREDRAW, 300 | lpfnWndProc: Some(wndproc), 301 | cbClsExtra: 0, 302 | cbWndExtra: 32, 303 | hInstance: module, 304 | hIcon: null_icon, 305 | hCursor: LoadCursorW(module, IDC_ARROW), 306 | hbrBackground: null_background, 307 | lpszMenuName: null_name, 308 | lpszClassName: class_name.as_ptr(), 309 | hIconSm: null_icon 310 | }; 311 | 312 | //Register the class 313 | match RegisterClassExW(&class){ 314 | 0 => panic!("Could not register class!"), 315 | _ => {} 316 | }; 317 | } 318 | 319 | /* 320 | Create the window 321 | */ 322 | unsafe fn setup_window(app: &mut MyApp, class_name: &Vec, window_name: &Vec){ 323 | let null_hwnd: HWND = null_mut(); 324 | let null_menu: HMENU = null_mut(); 325 | let null_name: *const WCHAR = null(); 326 | let null: LPVOID = null_mut(); 327 | let module = GetModuleHandleW(null_name); 328 | 329 | let hwnd = 330 | CreateWindowExW( 331 | WS_EX_COMPOSITED, 332 | class_name.as_ptr(), 333 | window_name.as_ptr(), 334 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 335 | CW_USEDEFAULT, CW_USEDEFAULT, 336 | 600, 400, 337 | null_hwnd, 338 | null_menu, 339 | module, 340 | null 341 | ); 342 | 343 | if hwnd.is_null(){ 344 | panic!("Could not create window"); 345 | } 346 | 347 | app.hwnd = hwnd; 348 | } 349 | 350 | /* 351 | Save the app address inside the window data. 352 | */ 353 | unsafe fn pack_app(app: &mut MyApp){ 354 | SetWindowLongPtrW(app.hwnd, 0, transmute(app)); 355 | } 356 | 357 | 358 | 359 | /////// 360 | // MAIN 361 | /////// 362 | 363 | fn main() { 364 | unsafe{ 365 | let mut app = MyApp{ 366 | factory: null_mut(), 367 | hwnd: null_mut(), 368 | resources: MyAppResources{ 369 | render_target: null_mut(), 370 | brush1: null_mut(), 371 | brush2: null_mut() 372 | } 373 | }; 374 | 375 | // 'MyApp' as UTF16 376 | let class_name: Vec = vec![77, 121, 65, 112, 112, 0]; 377 | let window_name: Vec = vec![77, 121, 65, 112, 112, 0]; 378 | 379 | // Window setup 380 | setup_class(&class_name); 381 | setup_window(&mut app, &class_name, &window_name); 382 | pack_app(&mut app); 383 | 384 | // D2D1 Setup 385 | setup_d2d_factory(&mut app); 386 | setup_d2d_resources(&mut app); 387 | 388 | // Application Loop 389 | let mut msg = uninitialized(); 390 | let null_handle: HWND = null_mut(); 391 | while GetMessageW(&mut msg, null_handle, 0, 0) != 0{ 392 | TranslateMessage(&msg); 393 | DispatchMessageW(&msg); 394 | } 395 | 396 | //App cleaning 397 | clean_d2d(&mut app); 398 | } 399 | 400 | } 401 | --------------------------------------------------------------------------------