obs_wrapper/graphics/
mod.rs

1use crate::{Error, Result};
2use core::convert::TryFrom;
3use core::ptr::null_mut;
4use obs_sys::{
5    gs_address_mode, gs_address_mode_GS_ADDRESS_BORDER, gs_address_mode_GS_ADDRESS_CLAMP,
6    gs_address_mode_GS_ADDRESS_MIRROR, gs_address_mode_GS_ADDRESS_MIRRORONCE,
7    gs_address_mode_GS_ADDRESS_WRAP, gs_color_format, gs_color_format_GS_A8,
8    gs_color_format_GS_BGRA, gs_color_format_GS_BGRX, gs_color_format_GS_DXT1,
9    gs_color_format_GS_DXT3, gs_color_format_GS_DXT5, gs_color_format_GS_R10G10B10A2,
10    gs_color_format_GS_R16, gs_color_format_GS_R16F, gs_color_format_GS_R32F,
11    gs_color_format_GS_R8, gs_color_format_GS_R8G8, gs_color_format_GS_RG16F,
12    gs_color_format_GS_RG32F, gs_color_format_GS_RGBA, gs_color_format_GS_RGBA16,
13    gs_color_format_GS_RGBA16F, gs_color_format_GS_RGBA32F, gs_color_format_GS_UNKNOWN,
14    gs_effect_create, gs_effect_destroy, gs_effect_get_param_by_name, gs_effect_get_param_info,
15    gs_effect_param_info, gs_effect_set_next_sampler, gs_effect_set_vec2, gs_effect_t, gs_eparam_t,
16    gs_sample_filter, gs_sample_filter_GS_FILTER_ANISOTROPIC, gs_sample_filter_GS_FILTER_LINEAR,
17    gs_sample_filter_GS_FILTER_MIN_LINEAR_MAG_MIP_POINT,
18    gs_sample_filter_GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
19    gs_sample_filter_GS_FILTER_MIN_MAG_LINEAR_MIP_POINT,
20    gs_sample_filter_GS_FILTER_MIN_MAG_POINT_MIP_LINEAR,
21    gs_sample_filter_GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
22    gs_sample_filter_GS_FILTER_MIN_POINT_MAG_MIP_LINEAR, gs_sample_filter_GS_FILTER_POINT,
23    gs_sampler_info, gs_samplerstate_create, gs_samplerstate_destroy, gs_samplerstate_t,
24    gs_shader_param_type, gs_shader_param_type_GS_SHADER_PARAM_BOOL,
25    gs_shader_param_type_GS_SHADER_PARAM_FLOAT, gs_shader_param_type_GS_SHADER_PARAM_INT,
26    gs_shader_param_type_GS_SHADER_PARAM_INT2, gs_shader_param_type_GS_SHADER_PARAM_INT3,
27    gs_shader_param_type_GS_SHADER_PARAM_INT4, gs_shader_param_type_GS_SHADER_PARAM_MATRIX4X4,
28    gs_shader_param_type_GS_SHADER_PARAM_STRING, gs_shader_param_type_GS_SHADER_PARAM_TEXTURE,
29    gs_shader_param_type_GS_SHADER_PARAM_UNKNOWN, gs_shader_param_type_GS_SHADER_PARAM_VEC2,
30    gs_shader_param_type_GS_SHADER_PARAM_VEC3, gs_shader_param_type_GS_SHADER_PARAM_VEC4,
31    gs_texture_create, gs_texture_destroy, gs_texture_get_height, gs_texture_get_width,
32    gs_texture_map, gs_texture_set_image, gs_texture_t, gs_texture_unmap, obs_allow_direct_render,
33    obs_allow_direct_render_OBS_ALLOW_DIRECT_RENDERING,
34    obs_allow_direct_render_OBS_NO_DIRECT_RENDERING, obs_enter_graphics, obs_leave_graphics,
35    obs_source_draw, vec2, vec3, vec4, GS_DYNAMIC,
36};
37use paste::item;
38use std::{
39    ffi::{CStr, CString},
40    ptr,
41};
42use std::{os::raw::c_int, slice};
43
44use super::string::ObsString;
45
46/// Guard to guarantee that we exit graphics context properly.
47/// This does not prevent one from calling APIs that are not supposed to be
48/// called outside of the context.
49struct GraphicsGuard;
50
51impl GraphicsGuard {
52    fn enter() -> Self {
53        unsafe {
54            obs_enter_graphics();
55        }
56        Self
57    }
58
59    pub fn with_enter<T, F: FnOnce() -> T>(f: F) -> T {
60        let _g = Self::enter();
61        f()
62    }
63}
64
65impl Drop for GraphicsGuard {
66    fn drop(&mut self) {
67        unsafe {
68            obs_leave_graphics();
69        }
70    }
71}
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub enum ShaderParamType {
75    Unknown,
76    Bool,
77    Float,
78    Int,
79    String,
80    Vec2,
81    Vec3,
82    Vec4,
83    Int2,
84    Int3,
85    Int4,
86    Mat4,
87    Texture,
88}
89
90impl ShaderParamType {
91    pub fn as_raw(&self) -> gs_shader_param_type {
92        match self {
93            ShaderParamType::Unknown => gs_shader_param_type_GS_SHADER_PARAM_UNKNOWN,
94            ShaderParamType::Bool => gs_shader_param_type_GS_SHADER_PARAM_BOOL,
95            ShaderParamType::Float => gs_shader_param_type_GS_SHADER_PARAM_FLOAT,
96            ShaderParamType::Int => gs_shader_param_type_GS_SHADER_PARAM_INT,
97            ShaderParamType::String => gs_shader_param_type_GS_SHADER_PARAM_STRING,
98            ShaderParamType::Vec2 => gs_shader_param_type_GS_SHADER_PARAM_VEC2,
99            ShaderParamType::Vec3 => gs_shader_param_type_GS_SHADER_PARAM_VEC3,
100            ShaderParamType::Vec4 => gs_shader_param_type_GS_SHADER_PARAM_VEC4,
101            ShaderParamType::Int2 => gs_shader_param_type_GS_SHADER_PARAM_INT2,
102            ShaderParamType::Int3 => gs_shader_param_type_GS_SHADER_PARAM_INT3,
103            ShaderParamType::Int4 => gs_shader_param_type_GS_SHADER_PARAM_INT4,
104            ShaderParamType::Mat4 => gs_shader_param_type_GS_SHADER_PARAM_MATRIX4X4,
105            ShaderParamType::Texture => gs_shader_param_type_GS_SHADER_PARAM_TEXTURE,
106        }
107    }
108
109    #[allow(non_upper_case_globals)]
110    pub fn from_raw(param_type: gs_shader_param_type) -> Self {
111        match param_type {
112            gs_shader_param_type_GS_SHADER_PARAM_UNKNOWN => ShaderParamType::Unknown,
113            gs_shader_param_type_GS_SHADER_PARAM_BOOL => ShaderParamType::Bool,
114            gs_shader_param_type_GS_SHADER_PARAM_FLOAT => ShaderParamType::Float,
115            gs_shader_param_type_GS_SHADER_PARAM_INT => ShaderParamType::Int,
116            gs_shader_param_type_GS_SHADER_PARAM_STRING => ShaderParamType::String,
117            gs_shader_param_type_GS_SHADER_PARAM_VEC2 => ShaderParamType::Vec2,
118            gs_shader_param_type_GS_SHADER_PARAM_VEC3 => ShaderParamType::Vec3,
119            gs_shader_param_type_GS_SHADER_PARAM_VEC4 => ShaderParamType::Vec4,
120            gs_shader_param_type_GS_SHADER_PARAM_INT2 => ShaderParamType::Int2,
121            gs_shader_param_type_GS_SHADER_PARAM_INT3 => ShaderParamType::Int3,
122            gs_shader_param_type_GS_SHADER_PARAM_INT4 => ShaderParamType::Int4,
123            gs_shader_param_type_GS_SHADER_PARAM_MATRIX4X4 => ShaderParamType::Mat4,
124            gs_shader_param_type_GS_SHADER_PARAM_TEXTURE => ShaderParamType::Texture,
125            _ => panic!("Invalid param_type!"),
126        }
127    }
128}
129
130pub struct GraphicsEffect {
131    raw: *mut gs_effect_t,
132}
133
134impl GraphicsEffect {
135    pub fn from_effect_string(value: ObsString, name: ObsString) -> Option<Self> {
136        let raw = GraphicsGuard::with_enter(|| unsafe {
137            gs_effect_create(value.as_ptr(), name.as_ptr(), std::ptr::null_mut())
138        });
139        if raw.is_null() {
140            None
141        } else {
142            Some(Self { raw })
143        }
144    }
145
146    pub fn get_effect_param_by_name<T: TryFrom<GraphicsEffectParam>>(
147        &mut self,
148        name: ObsString,
149    ) -> Option<T> {
150        unsafe {
151            let pointer = gs_effect_get_param_by_name(self.raw, name.as_ptr());
152            if !pointer.is_null() {
153                T::try_from(GraphicsEffectParam::from_raw(pointer)).ok()
154            } else {
155                None
156            }
157        }
158    }
159
160    /// # Safety
161    /// Returns a mutable pointer to an effect which if modified could cause UB.
162    pub unsafe fn as_ptr(&self) -> *mut gs_effect_t {
163        self.raw
164    }
165}
166
167impl Drop for GraphicsEffect {
168    fn drop(&mut self) {
169        GraphicsGuard::with_enter(|| unsafe {
170            gs_effect_destroy(self.raw);
171        });
172    }
173}
174
175pub enum GraphicsEffectParamConversionError {
176    InvalidType,
177}
178
179pub struct GraphicsEffectParam {
180    raw: *mut gs_eparam_t,
181    name: String,
182    shader_type: ShaderParamType,
183}
184
185impl GraphicsEffectParam {
186    /// # Safety
187    /// Creates a GraphicsEffectParam from a mutable reference. This data could
188    /// be modified somewhere else so this is UB.
189    pub unsafe fn from_raw(raw: *mut gs_eparam_t) -> Self {
190        let mut info = gs_effect_param_info::default();
191        gs_effect_get_param_info(raw, &mut info);
192
193        let shader_type = ShaderParamType::from_raw(info.type_);
194        let name = CString::from(CStr::from_ptr(info.name))
195            .into_string()
196            .unwrap_or_else(|_| String::from("{unknown-param-name}"));
197
198        Self {
199            raw,
200            shader_type,
201            name,
202        }
203    }
204
205    pub fn get_name(&self) -> &str {
206        &self.name
207    }
208}
209
210macro_rules! impl_graphics_effects {
211    ($($t:ident)*) => {
212        $(
213            item! {
214                pub struct [<GraphicsEffect $t Param>] {
215                    effect: GraphicsEffectParam,
216                }
217
218                impl TryFrom<GraphicsEffectParam> for [<GraphicsEffect $t Param>] {
219                    type Error = Error;
220
221                    fn try_from(effect: GraphicsEffectParam) -> Result<Self> {
222                        match effect.shader_type {
223                            ShaderParamType::[<$t>] => Ok([<GraphicsEffect $t Param>] { effect }),
224                            _ => Err(Error),
225                        }
226                    }
227                }
228            }
229        )*
230    };
231}
232
233impl_graphics_effects! {
234    Vec2
235    Texture
236}
237
238impl GraphicsEffectVec2Param {
239    pub fn set_vec2(&mut self, _context: &GraphicsEffectContext, value: &Vec2) {
240        unsafe {
241            gs_effect_set_vec2(self.effect.raw, &value.raw);
242        }
243    }
244}
245
246impl GraphicsEffectTextureParam {
247    pub fn set_next_sampler(
248        &mut self,
249        _context: &GraphicsEffectContext,
250        value: &mut GraphicsSamplerState,
251    ) {
252        unsafe {
253            gs_effect_set_next_sampler(self.effect.raw, value.raw);
254        }
255    }
256}
257
258pub enum GraphicsAddressMode {
259    Clamp,
260    Wrap,
261    Mirror,
262    Border,
263    MirrorOnce,
264}
265
266impl GraphicsAddressMode {
267    pub fn as_raw(&self) -> gs_address_mode {
268        match self {
269            GraphicsAddressMode::Clamp => gs_address_mode_GS_ADDRESS_CLAMP,
270            GraphicsAddressMode::Wrap => gs_address_mode_GS_ADDRESS_WRAP,
271            GraphicsAddressMode::Mirror => gs_address_mode_GS_ADDRESS_MIRROR,
272            GraphicsAddressMode::Border => gs_address_mode_GS_ADDRESS_BORDER,
273            GraphicsAddressMode::MirrorOnce => gs_address_mode_GS_ADDRESS_MIRRORONCE,
274        }
275    }
276}
277
278pub enum GraphicsSampleFilter {
279    Point,
280    Linear,
281    Anisotropic,
282    MinMagPointMipLinear,
283    MinPointMagLinearMipPoint,
284    MinPointMagMipLinear,
285    MinLinearMapMipPoint,
286    MinLinearMagPointMipLinear,
287    MinMagLinearMipPoint,
288}
289
290impl GraphicsSampleFilter {
291    fn as_raw(&self) -> gs_sample_filter {
292        match self {
293            GraphicsSampleFilter::Point => gs_sample_filter_GS_FILTER_POINT,
294            GraphicsSampleFilter::Linear => gs_sample_filter_GS_FILTER_LINEAR,
295            GraphicsSampleFilter::Anisotropic => gs_sample_filter_GS_FILTER_ANISOTROPIC,
296            GraphicsSampleFilter::MinMagPointMipLinear => {
297                gs_sample_filter_GS_FILTER_MIN_MAG_POINT_MIP_LINEAR
298            }
299            GraphicsSampleFilter::MinPointMagLinearMipPoint => {
300                gs_sample_filter_GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT
301            }
302            GraphicsSampleFilter::MinPointMagMipLinear => {
303                gs_sample_filter_GS_FILTER_MIN_POINT_MAG_MIP_LINEAR
304            }
305            GraphicsSampleFilter::MinLinearMapMipPoint => {
306                gs_sample_filter_GS_FILTER_MIN_LINEAR_MAG_MIP_POINT
307            }
308            GraphicsSampleFilter::MinLinearMagPointMipLinear => {
309                gs_sample_filter_GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR
310            }
311            GraphicsSampleFilter::MinMagLinearMipPoint => {
312                gs_sample_filter_GS_FILTER_MIN_MAG_LINEAR_MIP_POINT
313            }
314        }
315    }
316}
317
318pub struct GraphicsSamplerInfo {
319    info: gs_sampler_info,
320}
321
322impl GraphicsSamplerInfo {
323    pub fn new() -> Self {
324        Self {
325            info: gs_sampler_info {
326                address_u: GraphicsAddressMode::Clamp.as_raw(),
327                address_v: GraphicsAddressMode::Clamp.as_raw(),
328                address_w: GraphicsAddressMode::Clamp.as_raw(),
329                max_anisotropy: 0,
330                border_color: 0,
331                filter: GraphicsSampleFilter::Point.as_raw(),
332            },
333        }
334    }
335
336    pub fn with_address_u(mut self, mode: GraphicsAddressMode) -> Self {
337        self.info.address_u = mode.as_raw();
338        self
339    }
340
341    pub fn with_address_v(mut self, mode: GraphicsAddressMode) -> Self {
342        self.info.address_v = mode.as_raw();
343        self
344    }
345
346    pub fn with_address_w(mut self, mode: GraphicsAddressMode) -> Self {
347        self.info.address_w = mode.as_raw();
348        self
349    }
350
351    pub fn with_filter(mut self, mode: GraphicsSampleFilter) -> Self {
352        self.info.filter = mode.as_raw();
353        self
354    }
355}
356
357impl Default for GraphicsSamplerInfo {
358    fn default() -> Self {
359        Self::new()
360    }
361}
362
363pub struct GraphicsSamplerState {
364    raw: *mut gs_samplerstate_t,
365}
366
367impl From<GraphicsSamplerInfo> for GraphicsSamplerState {
368    fn from(info: GraphicsSamplerInfo) -> GraphicsSamplerState {
369        let raw = GraphicsGuard::with_enter(|| unsafe { gs_samplerstate_create(&info.info) });
370        GraphicsSamplerState { raw }
371    }
372}
373
374impl Drop for GraphicsSamplerState {
375    fn drop(&mut self) {
376        GraphicsGuard::with_enter(|| unsafe {
377            gs_samplerstate_destroy(self.raw);
378        });
379    }
380}
381
382pub struct GraphicsEffectContext {}
383
384impl GraphicsEffectContext {
385    /// # Safety
386    /// GraphicsEffectContext has methods that should only be used in certain situations.
387    /// Constructing it at the wrong time could cause UB.
388    pub(crate) unsafe fn new() -> Self {
389        Self {}
390    }
391}
392
393#[derive(Debug, Eq, PartialEq, Clone, Copy)]
394pub enum GraphicsColorFormat {
395    UNKNOWN,
396    A8,
397    R8,
398    RGBA,
399    BGRX,
400    BGRA,
401    R10G10B10A2,
402    RGBA16,
403    R16,
404    RGBA16F,
405    RGBA32F,
406    RG16F,
407    RG32F,
408    R16F,
409    R32F,
410    DXT1,
411    DXT3,
412    DXT5,
413    R8G8,
414}
415
416impl GraphicsColorFormat {
417    pub fn as_raw(&self) -> gs_color_format {
418        match self {
419            GraphicsColorFormat::UNKNOWN => gs_color_format_GS_UNKNOWN,
420            GraphicsColorFormat::A8 => gs_color_format_GS_A8,
421            GraphicsColorFormat::R8 => gs_color_format_GS_R8,
422            GraphicsColorFormat::RGBA => gs_color_format_GS_RGBA,
423            GraphicsColorFormat::BGRX => gs_color_format_GS_BGRX,
424            GraphicsColorFormat::BGRA => gs_color_format_GS_BGRA,
425            GraphicsColorFormat::R10G10B10A2 => gs_color_format_GS_R10G10B10A2,
426            GraphicsColorFormat::RGBA16 => gs_color_format_GS_RGBA16,
427            GraphicsColorFormat::R16 => gs_color_format_GS_R16,
428            GraphicsColorFormat::RGBA16F => gs_color_format_GS_RGBA16F,
429            GraphicsColorFormat::RGBA32F => gs_color_format_GS_RGBA32F,
430            GraphicsColorFormat::RG16F => gs_color_format_GS_RG16F,
431            GraphicsColorFormat::RG32F => gs_color_format_GS_RG32F,
432            GraphicsColorFormat::R16F => gs_color_format_GS_R16F,
433            GraphicsColorFormat::R32F => gs_color_format_GS_R32F,
434            GraphicsColorFormat::DXT1 => gs_color_format_GS_DXT1,
435            GraphicsColorFormat::DXT3 => gs_color_format_GS_DXT3,
436            GraphicsColorFormat::DXT5 => gs_color_format_GS_DXT5,
437            GraphicsColorFormat::R8G8 => gs_color_format_GS_R8G8,
438        }
439    }
440}
441
442pub enum GraphicsAllowDirectRendering {
443    NoDirectRendering,
444    AllowDirectRendering,
445}
446
447impl GraphicsAllowDirectRendering {
448    pub fn as_raw(&self) -> obs_allow_direct_render {
449        match self {
450            GraphicsAllowDirectRendering::NoDirectRendering => {
451                obs_allow_direct_render_OBS_NO_DIRECT_RENDERING
452            }
453            GraphicsAllowDirectRendering::AllowDirectRendering => {
454                obs_allow_direct_render_OBS_ALLOW_DIRECT_RENDERING
455            }
456        }
457    }
458}
459
460macro_rules! vector_impls {
461    ($($rust_name: ident, $name:ident => $($component:ident)*,)*) => (
462        $(
463        #[derive(Clone)]
464        pub struct $rust_name {
465            raw: $name,
466        }
467
468        impl $rust_name {
469            pub fn new($( $component: f32, )*) -> Self {
470                let mut v = Self {
471                    raw: $name::default(),
472                };
473                v.set($($component,)*);
474                v
475            }
476
477            #[inline]
478            pub fn zero(&mut self) {
479                $(
480                    self.raw.__bindgen_anon_1.__bindgen_anon_1.$component = 0.;
481                )*
482            }
483
484            #[inline]
485            pub fn copy(&mut self, input: &$rust_name) {
486                self.set($(input.$component(),)*);
487            }
488
489            #[inline]
490            pub fn add(&mut self, input: &$rust_name) {
491                self.set($(self.$component() + input.$component(),)*);
492            }
493
494            #[inline]
495            pub fn sub(&mut self, input: &$rust_name) {
496                self.set($(self.$component() - input.$component(),)*);
497            }
498
499            #[inline]
500            pub fn mul(&mut self, input: &$rust_name) {
501                self.set($(self.$component() * input.$component(),)*);
502            }
503
504            #[inline]
505            pub fn div(&mut self, input: &$rust_name) {
506                self.set($(self.$component() / input.$component(),)*);
507            }
508
509            #[inline]
510            pub fn addf(&mut self, input: f32) {
511                self.set($(self.$component() + input,)*);
512            }
513
514            #[inline]
515            pub fn subf(&mut self, input: f32) {
516                self.set($(self.$component() - input,)*);
517            }
518
519            #[inline]
520            pub fn mulf(&mut self, input: f32) {
521                self.set($(self.$component() * input,)*);
522            }
523
524            #[inline]
525            pub fn divf(&mut self, input: f32) {
526                self.set($(self.$component() / input,)*);
527            }
528
529            #[inline]
530            pub fn neg(&mut self) {
531                self.set($(-self.$component(),)*);
532            }
533
534            #[inline]
535            pub fn dot(&mut self, input: &$rust_name) -> f32 {
536                $(
537                    self.$component() * input.$component() +
538                )* 0.
539            }
540
541            #[inline]
542            pub fn len(&mut self) -> f32 {
543                ($( self.$component() * self.$component() + )* 0.).sqrt()
544            }
545
546            #[inline]
547            pub fn set(&mut self, $( $component: f32, )*) {
548                $(
549                    self.raw.__bindgen_anon_1.__bindgen_anon_1.$component = $component;
550                )*
551            }
552
553            #[inline]
554            pub fn min(&mut self, input: &$rust_name) {
555                self.set($(self.$component().min(input.$component()),)*);
556            }
557
558            #[inline]
559            pub fn max(&mut self, input: &$rust_name) {
560                self.set($(self.$component().max(input.$component()),)*);
561            }
562
563            #[inline]
564            pub fn minf(&mut self, input: f32) {
565                self.set($(self.$component().min(input),)*);
566            }
567
568            #[inline]
569            pub fn maxf(&mut self, input: f32) {
570                self.set($(self.$component().max(input),)*);
571            }
572
573            #[inline]
574            pub fn abs(&mut self) {
575                self.set($(self.$component().abs(),)*);
576            }
577
578            #[inline]
579            pub fn ceil(&mut self) {
580                self.set($(self.$component().ceil(),)*);
581            }
582
583            #[inline]
584            pub fn floor(&mut self) {
585                self.set($(self.$component().floor(),)*);
586            }
587
588            #[inline]
589            pub fn close(&mut self, input: &$rust_name, epsilon: f32) -> bool {
590                $(
591                    (self.$component() - input.$component()).abs() > epsilon &&
592                )* true
593            }
594
595            $(
596                item! {
597                    #[inline]
598                    pub fn [<$component>](&self) -> f32 {
599                        unsafe {
600                            self.raw.__bindgen_anon_1.__bindgen_anon_1.$component
601                        }
602                    }
603                }
604            )*
605
606            pub fn as_ptr(&mut self) -> *mut $name {
607                &mut self.raw
608            }
609        }
610
611        impl Default for $rust_name {
612            fn default() -> Self {
613                $(
614                    let $component = 0.;
615                )*
616                Self::new($( $component, )*)
617            }
618        }
619        )*
620    );
621}
622
623vector_impls! {
624    Vec2, vec2 => x y,
625    Vec3, vec3 => x y z,
626    Vec4, vec4 => x y z w,
627}
628
629/// Wrapper around [`gs_texture_t`](https://obsproject.com/docs/reference-libobs-graphics-graphics.html#c.gs_texture_t)
630pub struct GraphicsTexture {
631    raw: *mut gs_texture_t,
632}
633
634impl GraphicsTexture {
635    pub fn new(width: u32, height: u32, format: GraphicsColorFormat) -> Self {
636        let raw = GraphicsGuard::with_enter(|| unsafe {
637            gs_texture_create(width, height, format.as_raw(), 1, null_mut(), GS_DYNAMIC)
638        });
639        Self { raw }
640    }
641
642    #[inline]
643    pub fn height(&self) -> u32 {
644        GraphicsGuard::with_enter(|| unsafe { gs_texture_get_height(self.raw) })
645    }
646
647    #[inline]
648    pub fn width(&self) -> u32 {
649        GraphicsGuard::with_enter(|| unsafe { gs_texture_get_width(self.raw) })
650    }
651
652    pub fn set_image(&mut self, data: &[u8], linesize: u32, invert: bool) {
653        GraphicsGuard::with_enter(|| unsafe {
654            gs_texture_set_image(self.raw, data.as_ptr(), linesize, invert);
655        });
656    }
657
658    pub fn draw(&self, x: c_int, y: c_int, cx: u32, cy: u32, flip: bool) {
659        unsafe {
660            obs_source_draw(self.raw, x, y, cx, cy, flip);
661        }
662    }
663
664    #[inline]
665    pub fn map(&mut self) -> Result<MappedTexture> {
666        MappedTexture::new(self)
667    }
668
669    pub fn as_ptr(&self) -> *mut gs_texture_t {
670        self.raw
671    }
672}
673
674impl Drop for GraphicsTexture {
675    fn drop(&mut self) {
676        GraphicsGuard::with_enter(|| unsafe {
677            gs_texture_destroy(self.raw);
678        });
679    }
680}
681
682/// Represents a mapped texture blob from [`GraphicsTexture`].
683pub struct MappedTexture<'tex> {
684    tex: &'tex mut GraphicsTexture,
685    ptr: *mut u8,
686    len: usize,
687}
688
689impl<'tex> MappedTexture<'tex> {
690    fn new(tex: &'tex mut GraphicsTexture) -> Result<Self> {
691        let mut ptr: *mut u8 = ptr::null_mut();
692        let mut linesize = 0u32;
693        let map_result = GraphicsGuard::with_enter(|| unsafe {
694            gs_texture_map(tex.as_ptr(), &mut ptr, &mut linesize)
695        });
696        if !map_result {
697            return Err(Error);
698        }
699        let len = (linesize * tex.height()) as usize;
700        Ok(Self { tex, ptr, len })
701    }
702
703    #[inline]
704    pub fn as_ptr(&self) -> *const u8 {
705        self.ptr
706    }
707
708    #[inline]
709    pub fn as_mut_ptr(&mut self) -> *mut u8 {
710        self.ptr
711    }
712
713    #[inline]
714    pub fn width(&self) -> u32 {
715        self.tex.width()
716    }
717
718    #[inline]
719    pub fn height(&self) -> u32 {
720        self.tex.height()
721    }
722}
723
724impl std::ops::Deref for MappedTexture<'_> {
725    type Target = [u8];
726
727    fn deref(&self) -> &[u8] {
728        unsafe { slice::from_raw_parts(self.as_ptr(), self.len) }
729    }
730}
731
732impl std::ops::DerefMut for MappedTexture<'_> {
733    fn deref_mut(&mut self) -> &mut [u8] {
734        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) }
735    }
736}
737
738impl std::fmt::Debug for MappedTexture<'_> {
739    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
740        std::fmt::Debug::fmt(&**self, f)
741    }
742}
743
744impl Drop for MappedTexture<'_> {
745    fn drop(&mut self) {
746        GraphicsGuard::with_enter(|| unsafe {
747            gs_texture_unmap(self.tex.as_ptr());
748        });
749    }
750}