-
Notifications
You must be signed in to change notification settings - Fork 463
/
Copy pathrender.rs
2842 lines (2604 loc) · 95.6 KB
/
render.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//! 2D accelerated rendering
//!
//! Official C documentation: https://wiki.libsdl.org/CategoryRender
//! # Introduction
//!
//! This module contains functions for 2D accelerated rendering.
//!
//! This API supports the following features:
//!
//! * single pixel points
//! * single pixel lines
//! * filled rectangles
//! * texture images
//! * All of these may be drawn in opaque, blended, or additive modes.
//!
//! The texture images can have an additional color tint or alpha modulation
//! applied to them, and may also be stretched with linear interpolation,
//! rotated or flipped/mirrored.
//!
//! For advanced functionality like particle effects or actual 3D you should use
//! SDL's OpenGL/Direct3D support or one of the many available 3D engines.
//!
//! This API is not designed to be used from multiple threads, see
//! [this bug](http://bugzilla.libsdl.org/show_bug.cgi?id=1995) for details.
//!
//! ---
//!
//! None of the draw methods in `Canvas` are expected to fail.
//! If they do, a panic is raised and the program is aborted.
use crate::common::{validate_int, IntegerOrSdlError};
use crate::get_error;
use crate::pixels;
use crate::pixels::PixelFormatEnum;
use crate::rect::FPoint;
use crate::rect::FRect;
use crate::rect::Point;
use crate::rect::Rect;
use crate::surface;
use crate::surface::{Surface, SurfaceContext, SurfaceRef};
use crate::video::{Window, WindowContext};
use libc::c_void;
use libc::{c_double, c_int};
use std::convert::TryFrom;
use std::error::Error;
use std::ffi::CStr;
use std::fmt;
#[cfg(not(feature = "unsafe_textures"))]
use std::marker::PhantomData;
use std::mem;
use std::mem::{transmute, MaybeUninit};
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
use crate::sys;
use crate::sys::SDL_BlendMode;
use crate::sys::SDL_TextureAccess;
/// Contains the description of an error returned by SDL
#[derive(Debug, Clone)]
pub struct SdlError(String);
/// Possible errors returned by targeting a `Canvas` to render to a `Texture`
#[derive(Debug, Clone)]
pub enum TargetRenderError {
SdlError(SdlError),
NotSupported,
}
impl fmt::Display for SdlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SDL error: {}", self.0)
}
}
impl Error for SdlError {}
impl fmt::Display for TargetRenderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TargetRenderError::*;
match *self {
SdlError(ref e) => e.fmt(f),
NotSupported => write!(f, "The renderer does not support the use of render targets"),
}
}
}
impl Error for TargetRenderError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::SdlError(err) => Some(err),
Self::NotSupported => None,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum TextureAccess {
Static = SDL_TextureAccess::SDL_TEXTUREACCESS_STATIC as i32,
Streaming = SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32,
Target = SDL_TextureAccess::SDL_TEXTUREACCESS_TARGET as i32,
}
impl TryFrom<u32> for TextureAccess {
type Error = ();
fn try_from(n: u32) -> Result<Self, Self::Error> {
use self::TextureAccess::*;
use crate::sys::SDL_TextureAccess::*;
Ok(match unsafe { transmute(n) } {
SDL_TEXTUREACCESS_STATIC => Static,
SDL_TEXTUREACCESS_STREAMING => Streaming,
SDL_TEXTUREACCESS_TARGET => Target,
})
}
}
/// A structure that contains information on the capabilities of a render driver
/// or the current render context.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct RendererInfo {
pub name: &'static str,
pub flags: u32,
pub texture_formats: Vec<PixelFormatEnum>,
pub max_texture_width: u32,
pub max_texture_height: u32,
}
/// Blend mode for `Canvas`, `Texture` or `Surface`.
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum BlendMode {
/// no blending (replace destination with source).
None = SDL_BlendMode::SDL_BLENDMODE_NONE as i32,
/// Alpha blending
///
/// dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
///
/// dstA = srcA + (dstA * (1-srcA))
Blend = SDL_BlendMode::SDL_BLENDMODE_BLEND as i32,
/// Additive blending
///
/// dstRGB = (srcRGB * srcA) + dstRGB
///
/// dstA = dstA (keep original alpha)
Add = SDL_BlendMode::SDL_BLENDMODE_ADD as i32,
/// Color modulate
///
/// dstRGB = srcRGB * dstRGB
Mod = SDL_BlendMode::SDL_BLENDMODE_MOD as i32,
/// Color multiply
Mul = SDL_BlendMode::SDL_BLENDMODE_MUL as i32,
/// Invalid blending mode (indicates error)
Invalid = SDL_BlendMode::SDL_BLENDMODE_INVALID as i32,
}
impl TryFrom<u32> for BlendMode {
type Error = ();
fn try_from(n: u32) -> Result<Self, Self::Error> {
use self::BlendMode::*;
use crate::sys::SDL_BlendMode::*;
Ok(match unsafe { transmute(n) } {
SDL_BLENDMODE_NONE => None,
SDL_BLENDMODE_BLEND => Blend,
SDL_BLENDMODE_ADD => Add,
SDL_BLENDMODE_MOD => Mod,
SDL_BLENDMODE_MUL => Mul,
SDL_BLENDMODE_INVALID => Invalid,
})
}
}
impl RendererInfo {
pub unsafe fn from_ll(info: &sys::SDL_RendererInfo) -> RendererInfo {
let texture_formats: Vec<PixelFormatEnum> = info.texture_formats
[0..(info.num_texture_formats as usize)]
.iter()
.map(|&format| PixelFormatEnum::try_from(format).unwrap_or(PixelFormatEnum::Unknown))
.collect();
// The driver name is always a static string, compiled into SDL2.
let name = CStr::from_ptr(info.name as *const _).to_str().unwrap();
RendererInfo {
name,
flags: info.flags,
texture_formats,
max_texture_width: info.max_texture_width as u32,
max_texture_height: info.max_texture_height as u32,
}
}
}
/// Manages what keeps a `SDL_Renderer` alive
///
/// When the `RendererContext` is dropped, it destroys the `SDL_Renderer`
pub struct RendererContext<T> {
raw: *mut sys::SDL_Renderer,
_target: Rc<T>,
}
impl<T> Drop for RendererContext<T> {
#[doc(alias = "SDL_DestroyRenderer")]
fn drop(&mut self) {
unsafe {
sys::SDL_DestroyRenderer(self.raw);
};
}
}
impl<T> RendererContext<T> {
/// Gets information about the rendering context.
#[doc(alias = "SDL_GetRendererInfo")]
pub fn info(&self) -> RendererInfo {
let mut renderer_info_raw = mem::MaybeUninit::uninit();
let result =
unsafe { sys::SDL_GetRendererInfo(self.raw, renderer_info_raw.as_mut_ptr()) != 0 };
if result {
// Should only fail on an invalid renderer
panic!();
} else {
unsafe {
let renderer_info_raw = renderer_info_raw.assume_init();
RendererInfo::from_ll(&renderer_info_raw)
}
}
}
/// Gets the raw pointer to the SDL_Renderer
// this can prevent introducing UB until
// https://github.com/rust-lang/rust-clippy/issues/5953 is fixed
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn raw(&self) -> *mut sys::SDL_Renderer {
self.raw
}
pub unsafe fn from_ll(raw: *mut sys::SDL_Renderer, target: Rc<T>) -> Self {
RendererContext {
raw,
_target: target,
}
}
unsafe fn set_raw_target(&self, raw_texture: *mut sys::SDL_Texture) -> Result<(), SdlError> {
if sys::SDL_SetRenderTarget(self.raw, raw_texture) == 0 {
Ok(())
} else {
Err(SdlError(get_error()))
}
}
unsafe fn get_raw_target(&self) -> *mut sys::SDL_Texture {
sys::SDL_GetRenderTarget(self.raw)
}
}
impl<T: RenderTarget> Deref for Canvas<T> {
type Target = RendererContext<T::Context>;
fn deref(&self) -> &RendererContext<T::Context> {
self.context.as_ref()
}
}
/// Represents structs which can be the target of a `SDL_Renderer` (or Canvas).
///
/// This is intended for internal use only. It should not be used outside of this crate,
/// but is still visible for documentation reasons.
pub trait RenderTarget {
type Context;
}
impl<'s> RenderTarget for Surface<'s> {
type Context = SurfaceContext<'s>;
}
/// Manages and owns a target (`Surface` or `Window`) and allows drawing in it.
///
/// If the `Window` manipulates the shell of the Window, `Canvas<Window>` allows you to
/// manipulate both the shell and the inside of the window;
/// you can manipulate pixel by pixel (*not recommended*), lines, colored rectangles, or paste
/// `Texture`s to this `Canvas`.
///
/// Drawing to the `Canvas` does not take effect immediately, it draws to a buffer until you
/// call `present()`, where all the operations you did until the last `present()`
/// are updated to your target
///
/// Its context may be shared with the `TextureCreator`.
///
/// The context will not be dropped until all references of it are out of scope.
///
/// # Examples
///
/// ```rust,no_run
/// # use sdl2::render::Canvas;
/// # use sdl2::video::Window;
/// # use sdl2::pixels::Color;
/// # use sdl2::rect::Rect;
/// # let sdl_context = sdl2::init().unwrap();
/// # let video_subsystem = sdl_context.video().unwrap();
/// let window = video_subsystem.window("Example", 800, 600).build().unwrap();
///
/// // Let's create a Canvas which we will use to draw in our Window
/// let mut canvas : Canvas<Window> = window.into_canvas()
/// .present_vsync() //< this means the screen cannot
/// // render faster than your display rate (usually 60Hz or 144Hz)
/// .build().unwrap();
///
/// canvas.set_draw_color(Color::RGB(0, 0, 0));
/// // fills the canvas with the color we set in `set_draw_color`.
/// canvas.clear();
///
/// // change the color of our drawing with a gold-color ...
/// canvas.set_draw_color(Color::RGB(255, 210, 0));
/// // A draw a rectangle which almost fills our window with it !
/// canvas.fill_rect(Rect::new(10, 10, 780, 580));
///
/// // However the canvas has not been updated to the window yet,
/// // everything has been processed to an internal buffer,
/// // but if we want our buffer to be displayed on the window,
/// // we need to call `present`. We need to call this every time
/// // we want to render a new frame on the window.
/// canvas.present();
/// // present does not "clear" the buffer, that means that
/// // you have to clear it yourself before rendering again,
/// // otherwise leftovers of what you've renderer before might
/// // show up on the window !
/// //
/// // A good rule of thumb is to `clear()`, draw every texture
/// // needed, and then `present()`; repeat this every new frame.
///
/// ```
pub struct Canvas<T: RenderTarget> {
target: T,
context: Rc<RendererContext<T::Context>>,
default_pixel_format: PixelFormatEnum,
}
/// Alias for a `Canvas` that was created out of a `Surface`
pub type SurfaceCanvas<'s> = Canvas<Surface<'s>>;
/// Methods for the `SurfaceCanvas`.
impl<'s> Canvas<Surface<'s>> {
/// Creates a 2D software rendering context for a surface.
///
/// This method should only fail if SDL2 is not built with rendering
/// support, or there's an out-of-memory error.
#[doc(alias = "SDL_CreateSoftwareRenderer")]
pub fn from_surface(surface: surface::Surface<'s>) -> Result<Self, String> {
let raw_renderer = unsafe { sys::SDL_CreateSoftwareRenderer(surface.raw()) };
if !raw_renderer.is_null() {
let context =
Rc::new(unsafe { RendererContext::from_ll(raw_renderer, surface.context()) });
let default_pixel_format = surface.pixel_format_enum();
Ok(Canvas {
target: surface,
context,
default_pixel_format,
})
} else {
Err(get_error())
}
}
/// Gets a reference to the associated surface of the Canvas
#[inline]
pub fn surface(&self) -> &SurfaceRef {
&self.target
}
/// Gets a mutable reference to the associated surface of the Canvas
#[inline]
pub fn surface_mut(&mut self) -> &mut SurfaceRef {
&mut self.target
}
/// Gets the associated surface of the Canvas and destroys the Canvas
#[inline]
pub fn into_surface(self) -> Surface<'s> {
self.target
}
/// Returns a `TextureCreator` that can create Textures to be drawn on this `Canvas`
///
/// This `TextureCreator` will share a reference to the renderer and target context.
///
/// The target (i.e., `Window`) will not be destroyed and the SDL_Renderer will not be
/// destroyed if the `TextureCreator` is still in scope.
pub fn texture_creator(&self) -> TextureCreator<SurfaceContext<'s>> {
TextureCreator {
context: self.context.clone(),
default_pixel_format: self.default_pixel_format,
}
}
}
pub type WindowCanvas = Canvas<Window>;
impl RenderTarget for Window {
type Context = WindowContext;
}
/// Methods for the `WindowCanvas`.
impl Canvas<Window> {
/// Gets a reference to the associated window of the Canvas
#[inline]
pub fn window(&self) -> &Window {
&self.target
}
/// Gets a mutable reference to the associated window of the Canvas
#[inline]
pub fn window_mut(&mut self) -> &mut Window {
&mut self.target
}
/// Gets the associated window of the Canvas and destroys the Canvas
#[inline]
pub fn into_window(self) -> Window {
self.target
}
#[inline]
pub fn default_pixel_format(&self) -> PixelFormatEnum {
self.window().window_pixel_format()
}
/// Returns a `TextureCreator` that can create Textures to be drawn on this `Canvas`
///
/// This `TextureCreator` will share a reference to the renderer and target context.
///
/// The target (i.e., `Window`) will not be destroyed and the SDL_Renderer will not be
/// destroyed if the `TextureCreator` is still in scope.
pub fn texture_creator(&self) -> TextureCreator<WindowContext> {
TextureCreator {
context: self.context.clone(),
default_pixel_format: self.default_pixel_format(),
}
}
}
impl<T: RenderTarget> Canvas<T> {
/// Determine whether a window supports the use of render targets.
#[doc(alias = "SDL_RenderTargetSupported")]
pub fn render_target_supported(&self) -> bool {
unsafe { sys::SDL_RenderTargetSupported(self.context.raw) == sys::SDL_bool::SDL_TRUE }
}
/// Temporarily sets the target of `Canvas` to a `Texture`. This effectively allows rendering
/// to a `Texture` in any way you want: you can make a `Texture` a combination of other
/// `Texture`s, be a complex geometry form with the `gfx` module, ... You can draw pixel by
/// pixel in it if you want, so you can do basically anything with that `Texture`.
///
/// If you want to set the content of multiple `Texture` at once the most efficient way
/// possible, *don't* make a loop and call this function every time and use
/// `with_multiple_texture_canvas` instead. Using `with_texture_canvas` is actually
/// inefficient because the target is reset to the source (the `Window` or the `Surface`)
/// at the end of this function, but using it in a loop would make this reset useless.
/// Plus, the check that render_target is actually supported on that `Canvas` is also
/// done every time, leading to useless checks.
///
/// # Notes
///
/// Note that the `Canvas` in the closure is exactly the same as the one you call this
/// function with, meaning that you can call every function of your original `Canvas`.
///
/// That means you can also call `with_texture_canvas` and `with_multiple_texture_canvas` from
/// the inside of the closure. Even though this is useless and inefficient, this is totally
/// safe to do and allowed.
///
/// Since the render target is now a Texture, some calls of Canvas might return another result
/// than if the target was to be the original source. For instance `output_size` will return
/// this size of the current `Texture` in the closure, but the size of the `Window` or
/// `Surface` outside of the closure.
///
/// You do not need to call `present` after drawing in the Canvas in the closure, the changes
/// are applied directly to the `Texture` instead of a hidden buffer.
///
/// # Errors
///
/// * returns `TargetRenderError::NotSupported`
/// if the renderer does not support the use of render targets
/// * returns `TargetRenderError::SdlError` if SDL2 returned with an error code.
///
/// The texture *must* be created with the texture access:
/// `sdl2::render::TextureAccess::Target`.
/// Using a texture which was not created with the texture access `Target` is undefined
/// behavior.
///
/// # Examples
///
/// The example below changes a newly created `Texture` to be a 150-by-150 black texture with a
/// 50-by-50 red square in the middle.
///
/// ```rust,no_run
/// # use sdl2::render::{Canvas, Texture};
/// # use sdl2::video::Window;
/// # use sdl2::pixels::Color;
/// # use sdl2::rect::Rect;
/// # let mut canvas : Canvas<Window> = unimplemented!();
/// let texture_creator = canvas.texture_creator();
/// let mut texture = texture_creator
/// .create_texture_target(texture_creator.default_pixel_format(), 150, 150)
/// .unwrap();
/// let result = canvas.with_texture_canvas(&mut texture, |texture_canvas| {
/// texture_canvas.set_draw_color(Color::RGBA(0, 0, 0, 255));
/// texture_canvas.clear();
/// texture_canvas.set_draw_color(Color::RGBA(255, 0, 0, 255));
/// texture_canvas.fill_rect(Rect::new(50, 50, 50, 50)).unwrap();
/// });
/// ```
///
pub fn with_texture_canvas<F>(
&mut self,
texture: &mut Texture,
f: F,
) -> Result<(), TargetRenderError>
where
for<'r> F: FnOnce(&'r mut Canvas<T>),
{
if self.render_target_supported() {
let target = unsafe { self.get_raw_target() };
unsafe { self.set_raw_target(texture.raw) }.map_err(TargetRenderError::SdlError)?;
f(self);
unsafe { self.set_raw_target(target) }.map_err(TargetRenderError::SdlError)?;
Ok(())
} else {
Err(TargetRenderError::NotSupported)
}
}
/// Same as `with_texture_canvas`, but allows to change multiple `Texture`s at once with the
/// least amount of overhead. It means that between every iteration the Target is not reset to
/// the source, and that the fact that the Canvas supports render target isn't checked every
/// iteration either; the check is actually only done once, at the beginning, avoiding useless
/// checks.
///
/// The closure is run once for every `Texture` sent as parameter.
///
/// The main changes from `with_texture_canvas` is that is takes an `Iterator` of `(&mut
/// Texture, U)`, where U is a type defined by the user. The closure takes a `&mut Canvas`, and
/// `&U` as arguments instead of a simple `&mut Canvas`. This user-defined type allows you to
/// keep track of what to do with the Canvas you have received in the closure.
///
/// You will usually want to keep track of the number, a property, or anything that will allow
/// you to uniquely track this `Texture`, but it can also be an empty struct or `()` as well!
///
/// # Examples
///
/// Let's create two textures, one which will be yellow, and the other will be white
///
/// ```rust,no_run
/// # use sdl2::pixels::Color;
/// # use sdl2::rect::Rect;
/// # use sdl2::video::Window;
/// # use sdl2::render::{Canvas, Texture};
/// # let mut canvas : Canvas<Window> = unimplemented!();
/// let texture_creator = canvas.texture_creator();
/// enum TextureColor {
/// Yellow,
/// White,
/// };
///
/// let mut square_texture1 : Texture =
/// texture_creator.create_texture_target(None, 100, 100).unwrap();
/// let mut square_texture2 : Texture =
/// texture_creator.create_texture_target(None, 100, 100).unwrap();
/// let textures : Vec<(&mut Texture, TextureColor)> = vec![
/// (&mut square_texture1, TextureColor::Yellow),
/// (&mut square_texture2, TextureColor::White)
/// ];
/// let result : Result<(), _> =
/// canvas.with_multiple_texture_canvas(textures.iter(), |texture_canvas, user_context| {
/// match *user_context {
/// TextureColor::White => {
/// texture_canvas.set_draw_color(Color::RGB(255, 255, 255));
/// },
/// TextureColor::Yellow => {
/// texture_canvas.set_draw_color(Color::RGB(255, 255, 0));
/// }
/// };
/// texture_canvas.clear();
/// });
/// // square_texture1 is now Yellow and square_texture2 is now White!
/// ```
///
///
#[cfg(not(feature = "unsafe_textures"))]
pub fn with_multiple_texture_canvas<'t: 'a, 'a: 's, 's, I, F, U: 's>(
&mut self,
textures: I,
mut f: F,
) -> Result<(), TargetRenderError>
where
for<'r> F: FnMut(&'r mut Canvas<T>, &U),
I: Iterator<Item = &'s (&'a mut Texture<'t>, U)>,
{
if self.render_target_supported() {
let target = unsafe { self.get_raw_target() };
for (texture, user_context) in textures {
unsafe { self.set_raw_target(texture.raw) }.map_err(TargetRenderError::SdlError)?;
f(self, user_context);
}
// reset the target to its source
unsafe { self.set_raw_target(target) }.map_err(TargetRenderError::SdlError)?;
Ok(())
} else {
Err(TargetRenderError::NotSupported)
}
}
#[cfg(feature = "unsafe_textures")]
pub fn with_multiple_texture_canvas<'a: 's, 's, I, F, U: 's>(
&mut self,
textures: I,
mut f: F,
) -> Result<(), TargetRenderError>
where
for<'r> F: FnMut(&'r mut Canvas<T>, &U),
I: Iterator<Item = &'s (&'a mut Texture, U)>,
{
if self.render_target_supported() {
for &(ref texture, ref user_context) in textures {
unsafe { self.set_raw_target(texture.raw) }
.map_err(|e| TargetRenderError::SdlError(e))?;
f(self, &user_context);
}
// reset the target to its source
unsafe { self.set_raw_target(ptr::null_mut()) }
.map_err(|e| TargetRenderError::SdlError(e))?;
Ok(())
} else {
Err(TargetRenderError::NotSupported)
}
}
}
/// Creates Textures that cannot outlive the creator
///
/// The `TextureCreator` does not hold a lifetime to its Canvas by design choice.
///
/// If a `Canvas` is dropped before its `TextureCreator`, it is still safe to use.
///
/// It is, however, useless.
///
/// Any `Texture` created here can only be drawn onto the original `Canvas`. A `Texture` used in a
/// `Canvas` must come from a `TextureCreator` coming from that same `Canvas`. Using a `Texture` to
/// render to a `Canvas` not being the parent of the `Texture`'s `TextureCreator` is undefined
/// behavior.
pub struct TextureCreator<T> {
context: Rc<RendererContext<T>>,
default_pixel_format: PixelFormatEnum,
}
/// The type that allows you to build Window-based renderers.
///
/// By default, the renderer builder will prioritize for a hardware-accelerated
/// renderer, which is probably what you want.
pub struct CanvasBuilder {
window: Window,
index: Option<u32>,
renderer_flags: u32,
}
impl CanvasBuilder {
/// Initializes a new `CanvasBuilder`.
pub fn new(window: Window) -> CanvasBuilder {
CanvasBuilder {
window,
// -1 means to initialize the first rendering driver supporting the
// renderer flags
index: None,
// no flags gives priority to available SDL_RENDERER_ACCELERATED
// renderers
renderer_flags: 0,
}
}
/// Builds the renderer.
#[doc(alias = "SDL_CreateRenderer")]
pub fn build(self) -> Result<WindowCanvas, IntegerOrSdlError> {
use crate::common::IntegerOrSdlError::*;
let index = match self.index {
None => -1,
Some(index) => validate_int(index, "index")?,
};
let raw = unsafe { sys::SDL_CreateRenderer(self.window.raw(), index, self.renderer_flags) };
if raw.is_null() {
Err(SdlError(get_error()))
} else {
let context = Rc::new(unsafe { RendererContext::from_ll(raw, self.window.context()) });
let default_pixel_format = self.window.window_pixel_format();
Ok(Canvas {
context,
target: self.window,
default_pixel_format,
})
}
}
/// Sets the index of the rendering driver to initialize.
/// If you desire the first rendering driver to support the flags provided,
/// or if you're translating code from C which passes -1 for the index,
/// **do not** invoke the `index` method.
pub fn index(mut self, index: u32) -> CanvasBuilder {
self.index = Some(index);
self
}
/// Set the renderer to a software fallback.
/// This flag is accumulative, and may be specified with other flags.
pub fn software(mut self) -> CanvasBuilder {
self.renderer_flags |= sys::SDL_RendererFlags::SDL_RENDERER_SOFTWARE as u32;
self
}
/// Set the renderer to use hardware acceleration.
/// This flag is accumulative, and may be specified with other flags.
pub fn accelerated(mut self) -> CanvasBuilder {
self.renderer_flags |= sys::SDL_RendererFlags::SDL_RENDERER_ACCELERATED as u32;
self
}
/// Synchronize renderer `present` method calls with the refresh rate.
/// This flag is accumulative, and may be specified with other flags.
pub fn present_vsync(mut self) -> CanvasBuilder {
self.renderer_flags |= sys::SDL_RendererFlags::SDL_RENDERER_PRESENTVSYNC as u32;
self
}
/// Set the renderer to support rendering to a texture.
/// This flag is accumulative, and may be specified with other flags.
pub fn target_texture(mut self) -> CanvasBuilder {
self.renderer_flags |= sys::SDL_RendererFlags::SDL_RENDERER_TARGETTEXTURE as u32;
self
}
}
#[derive(Debug, Clone)]
pub enum TextureValueError {
WidthOverflows(u32),
HeightOverflows(u32),
WidthMustBeMultipleOfTwoForFormat(u32, PixelFormatEnum),
SdlError(String),
}
impl fmt::Display for TextureValueError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TextureValueError::*;
match *self {
WidthOverflows(value) => write!(f, "Integer width overflows ({})", value),
HeightOverflows(value) => write!(f, "Integer height overflows ({})", value),
WidthMustBeMultipleOfTwoForFormat(value, format) => {
write!(
f,
"Texture width must be multiple of two for pixel format '{:?}' ({})",
format, value
)
}
SdlError(ref e) => write!(f, "SDL error: {}", e),
}
}
}
impl Error for TextureValueError {}
#[doc(alias = "SDL_CreateTexture")]
fn ll_create_texture(
context: *mut sys::SDL_Renderer,
pixel_format: PixelFormatEnum,
access: TextureAccess,
width: u32,
height: u32,
) -> Result<*mut sys::SDL_Texture, TextureValueError> {
use self::TextureValueError::*;
let w = match validate_int(width, "width") {
Ok(w) => w,
Err(_) => return Err(WidthOverflows(width)),
};
let h = match validate_int(height, "height") {
Ok(h) => h,
Err(_) => return Err(HeightOverflows(height)),
};
// If the pixel format is YUV 4:2:0 and planar, the width and height must
// be multiples-of-two. See issue #334 for details.
match pixel_format {
PixelFormatEnum::YV12
| PixelFormatEnum::IYUV
| PixelFormatEnum::NV12
| PixelFormatEnum::NV21 => {
if w % 2 != 0 || h % 2 != 0 {
return Err(WidthMustBeMultipleOfTwoForFormat(width, pixel_format));
}
}
_ => (),
};
Ok(unsafe { sys::SDL_CreateTexture(context, pixel_format as u32, access as c_int, w, h) })
}
/// Texture-creating methods for the renderer
impl<T> TextureCreator<T> {
// this can prevent introducing UB until
// https://github.com/rust-lang/rust-clippy/issues/5953 is fixed
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn raw(&self) -> *mut sys::SDL_Renderer {
self.context.raw()
}
pub fn default_pixel_format(&self) -> PixelFormatEnum {
self.default_pixel_format
}
/// Creates a texture for a rendering context.
///
/// If format is `None`, the format will be the one the parent Window or Surface uses.
///
/// If format is `Some(pixel_format)`, the default will be overridden, and the texture will be
/// created with the specified format if possible. If the PixelFormat is not supported, this
/// will return an error.
///
/// You should prefer the default format if possible to have performance gains and to avoid
/// unsupported Pixel Formats that can cause errors. However, be careful with the default
/// `PixelFormat` if you want to create transparent textures.
pub fn create_texture<F>(
&self,
format: F,
access: TextureAccess,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormatEnum>>,
{
use self::TextureValueError::*;
let format: PixelFormatEnum = format.into().unwrap_or(self.default_pixel_format);
let result = ll_create_texture(self.context.raw(), format, access, width, height)?;
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
#[inline]
/// Shorthand for `create_texture(format, TextureAccess::Static, width, height)`
pub fn create_texture_static<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormatEnum>>,
{
self.create_texture(format, TextureAccess::Static, width, height)
}
#[inline]
/// Shorthand for `create_texture(format, TextureAccess::Streaming, width, height)`
pub fn create_texture_streaming<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormatEnum>>,
{
self.create_texture(format, TextureAccess::Streaming, width, height)
}
#[inline]
/// Shorthand for `create_texture(format, TextureAccess::Target, width, height)`
pub fn create_texture_target<F>(
&self,
format: F,
width: u32,
height: u32,
) -> Result<Texture, TextureValueError>
where
F: Into<Option<PixelFormatEnum>>,
{
self.create_texture(format, TextureAccess::Target, width, height)
}
/// Creates a texture from an existing surface.
///
/// # Remarks
///
/// The access hint for the created texture is [`TextureAccess::Static`].
///
/// ```no_run
/// use sdl2::pixels::PixelFormatEnum;
/// use sdl2::surface::Surface;
/// use sdl2::render::{Canvas, Texture};
/// use sdl2::video::Window;
///
/// // We init systems.
/// let sdl_context = sdl2::init().expect("failed to init SDL");
/// let video_subsystem = sdl_context.video().expect("failed to get video context");
///
/// // We create a window.
/// let window = video_subsystem.window("sdl2 demo", 800, 600)
/// .build()
/// .expect("failed to build window");
///
/// // We get the canvas from which we can get the `TextureCreator`.
/// let mut canvas: Canvas<Window> = window.into_canvas()
/// .build()
/// .expect("failed to build window's canvas");
/// let texture_creator = canvas.texture_creator();
///
/// let surface = Surface::new(512, 512, PixelFormatEnum::RGB24).unwrap();
/// let texture = texture_creator.create_texture_from_surface(surface).unwrap();
/// ```
#[doc(alias = "SDL_CreateTextureFromSurface")]
pub fn create_texture_from_surface<S: AsRef<SurfaceRef>>(
&self,
surface: S,
) -> Result<Texture, TextureValueError> {
use self::TextureValueError::*;
let result =
unsafe { sys::SDL_CreateTextureFromSurface(self.context.raw, surface.as_ref().raw()) };
if result.is_null() {
Err(SdlError(get_error()))
} else {
unsafe { Ok(self.raw_create_texture(result)) }
}
}
/// Create a texture from its raw `SDL_Texture`.
#[cfg(not(feature = "unsafe_textures"))]
#[inline]
pub const unsafe fn raw_create_texture(&self, raw: *mut sys::SDL_Texture) -> Texture {
Texture {
raw,
_marker: PhantomData,
}
}
/// Create a texture from its raw `SDL_Texture`. Should be used with care.
#[cfg(feature = "unsafe_textures")]
pub const unsafe fn raw_create_texture(&self, raw: *mut sys::SDL_Texture) -> Texture {
Texture { raw }
}
}
/// Drawing methods
impl<T: RenderTarget> Canvas<T> {
// this can prevent introducing UB until
// https://github.com/rust-lang/rust-clippy/issues/5953 is fixed
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn raw(&self) -> *mut sys::SDL_Renderer {
self.context.raw()
}
/// Sets the color used for drawing operations (Rect, Line and Clear).
#[doc(alias = "SDL_SetRenderDrawColor")]
pub fn set_draw_color<C: Into<pixels::Color>>(&mut self, color: C) {
let (r, g, b, a) = color.into().rgba();
let ret = unsafe { sys::SDL_SetRenderDrawColor(self.raw, r, g, b, a) };
// Should only fail on an invalid renderer
if ret != 0 {
panic!("{}", get_error())
}
}
/// Gets the color used for drawing operations (Rect, Line and Clear).
#[doc(alias = "SDL_GetRenderDrawColor")]
pub fn draw_color(&self) -> pixels::Color {
let (mut r, mut g, mut b, mut a) = (0, 0, 0, 0);
let ret = unsafe {
sys::SDL_GetRenderDrawColor(self.context.raw, &mut r, &mut g, &mut b, &mut a)
};
// Should only fail on an invalid renderer
if ret != 0 {
panic!("{}", get_error())
} else {
pixels::Color::RGBA(r, g, b, a)
}
}
/// Sets the blend mode used for drawing operations (Fill and Line).
#[doc(alias = "SDL_SetRenderDrawBlendMode")]
pub fn set_blend_mode(&mut self, blend: BlendMode) {
let ret =
unsafe { sys::SDL_SetRenderDrawBlendMode(self.context.raw, transmute(blend as u32)) };
// Should only fail on an invalid renderer
if ret != 0 {