1use crate::*;
2
3#[derive(Clone, Debug)]
5pub struct Image {
6    pub id: Option<String>,
8    pub name: Option<String>,
10    pub format: Option<String>,
17    pub height: u32,
20    pub width: u32,
23    pub depth: u32,
26    pub asset: Option<Box<Asset>>,
28    pub source: ImageSource,
30    pub extra: Vec<Extra>,
32}
33
34impl Image {
35    pub fn new(id: impl Into<String>, name: Option<String>, source: ImageSource) -> Self {
37        Self {
38            id: Some(id.into()),
39            name,
40            format: None,
41            height: 0,
42            width: 0,
43            depth: 1,
44            asset: None,
45            source,
46            extra: vec![],
47        }
48    }
49}
50
51impl XNode for Image {
52    const NAME: &'static str = "image";
53    fn parse(element: &Element) -> Result<Self> {
54        debug_assert_eq!(element.name(), Self::NAME);
55        let mut it = element.children().peekable();
56        Ok(Image {
57            id: element.attr("id").map(Into::into),
58            name: element.attr("name").map(Into::into),
59            format: element.attr("format").map(Into::into),
60            height: parse_attr(element.attr("height"))?.unwrap_or(0),
61            width: parse_attr(element.attr("width"))?.unwrap_or(0),
62            depth: parse_attr(element.attr("depth"))?.unwrap_or(1),
63            asset: Asset::parse_opt_box(&mut it)?,
64            source: parse_one_many(&mut it, ImageSource::parse)?,
65            extra: Extra::parse_many(it)?,
66        })
67    }
68}
69
70impl XNodeWrite for Image {
71    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
72        let mut e = Self::elem();
73        e.opt_attr("id", &self.id);
74        e.opt_attr("name", &self.name);
75        e.opt_attr("format", &self.format);
76        e.def_print_attr("height", self.height, 0);
77        e.def_print_attr("width", self.width, 0);
78        e.def_print_attr("depth", self.depth, 1);
79        let e = e.start(w)?;
80        self.asset.write_to(w)?;
81        self.source.write_to(w)?;
82        self.extra.write_to(w)?;
83        e.end(w)
84    }
85}
86
87#[derive(Clone, Debug)]
89pub enum ImageParam {
90    NewParam(NewParam),
92    Image(Image),
94}
95
96impl From<Image> for ImageParam {
97    fn from(v: Image) -> Self {
98        Self::Image(v)
99    }
100}
101
102impl ImageParam {
103    pub(crate) fn parse_list(it: &mut ElementIter<'_>) -> Result<Vec<Self>> {
104        parse_list_many(it, |e| {
105            Ok(Some(match e.name() {
106                Image::NAME => Self::Image(Image::parse(e)?),
107                NewParam::NAME => Self::NewParam(NewParam::parse(e)?),
108                _ => return Ok(None),
109            }))
110        })
111    }
112}
113
114impl XNodeWrite for ImageParam {
115    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
116        match self {
117            Self::NewParam(e) => e.write_to(w),
118            Self::Image(e) => e.write_to(w),
119        }
120    }
121}
122
123fn parse_hex_array(s: &str) -> Box<[u8]> {
124    let mut out = vec![];
125    let mut hi = 0;
126    let mut odd = false;
127    for c in s.bytes() {
128        let c = match c {
129            b'0'..=b'9' => c - b'0',
130            b'a'..=b'f' => c - b'a' + 10,
131            b'A'..=b'F' => c - b'A' + 10,
132            _ => continue,
133        };
134        if odd {
135            out.push(hi << 4 | c)
136        } else {
137            hi = c
138        }
139        odd = !odd
140    }
141    out.into()
142}
143
144#[derive(Clone, Debug)]
146pub enum ImageSource {
147    Data(Box<[u8]>),
149    InitFrom(Url),
151}
152
153impl ImageSource {
154    pub fn parse(element: &Element) -> Result<Option<Self>> {
156        Ok(Some(match element.name() {
157            "data" => {
158                let s = get_text(element).ok_or("expected text element")?;
159                ImageSource::Data(parse_hex_array(s))
160            }
161            "init_from" => ImageSource::InitFrom(parse_elem(element)?),
162            _ => return Ok(None),
163        }))
164    }
165}
166
167impl XNodeWrite for ImageSource {
168    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
169        match self {
170            Self::Data(data) => {
171                let e = ElemBuilder::new("data").start(w)?;
172                let mut indent = false;
173                for chunk in data.chunks(40) {
174                    if indent {
175                        w.write_indent()?
176                    }
177                    indent = true;
178                    for &c in chunk {
179                        write!(w.get_mut(), "{:x}", c)?
180                    }
181                }
182                e.end(w)
183            }
184            Self::InitFrom(url) => ElemBuilder::print("init_from", url, w),
185        }
186    }
187}
188
189#[derive(Clone, Debug)]
191pub struct Sampler2D {
192    pub source: NameRef<Surface>,
196    pub wrap_s: WrapMode,
198    pub wrap_t: WrapMode,
200    pub min_filter: SamplerFilter,
207    pub mag_filter: SamplerFilter,
212    pub mip_filter: SamplerFilter,
214    pub border_color: Option<Box<[f32; 4]>>,
218    pub mipmap_max_level: u8,
220    pub mipmap_bias: f32,
223    pub extra: Vec<Extra>,
225}
226
227impl Sampler2D {
228    pub fn new(source: String) -> Self {
230        Self {
231            source: Ref::new(source),
232            wrap_s: Default::default(),
233            wrap_t: Default::default(),
234            min_filter: Default::default(),
235            mag_filter: Default::default(),
236            mip_filter: Default::default(),
237            border_color: None,
238            mipmap_max_level: 0,
239            mipmap_bias: 0.,
240            extra: vec![],
241        }
242    }
243}
244
245impl XNode for Sampler2D {
246    const NAME: &'static str = "sampler2D";
247    fn parse(element: &Element) -> Result<Self> {
248        debug_assert_eq!(element.name(), Self::NAME);
249        let mut it = element.children().peekable();
250        Ok(Sampler2D {
251            source: parse_one("source", &mut it, parse_elem)?,
252            wrap_s: parse_opt("wrap_s", &mut it, parse_elem)?.unwrap_or_default(),
253            wrap_t: parse_opt("wrap_t", &mut it, parse_elem)?.unwrap_or_default(),
254            min_filter: parse_opt("minfilter", &mut it, parse_elem)?.unwrap_or_default(),
255            mag_filter: parse_opt("magfilter", &mut it, parse_elem)?.unwrap_or_default(),
256            mip_filter: parse_opt("mipfilter", &mut it, parse_elem)?.unwrap_or_default(),
257            border_color: parse_opt("border_color", &mut it, parse_array_n)?,
258            mipmap_max_level: parse_opt("mipmap_maxlevel", &mut it, parse_elem)?.unwrap_or(0),
259            mipmap_bias: parse_opt("mipmap_bias", &mut it, parse_elem)?.unwrap_or(0.),
260            extra: Extra::parse_many(it)?,
261        })
262    }
263}
264
265impl XNodeWrite for Sampler2D {
266    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
267        let e = Self::elem().start(w)?;
268        ElemBuilder::print("source", &self.source, w)?;
269        ElemBuilder::def_print("wrap_s", self.wrap_s, Default::default(), w)?;
270        ElemBuilder::def_print("wrap_t", self.wrap_t, Default::default(), w)?;
271        ElemBuilder::def_print("minfilter", self.min_filter, Default::default(), w)?;
272        ElemBuilder::def_print("magfilter", self.mag_filter, Default::default(), w)?;
273        ElemBuilder::def_print("mipfilter", self.mip_filter, Default::default(), w)?;
274        opt(&self.border_color, |e| {
275            ElemBuilder::print_arr("border_color", &**e, w)
276        })?;
277        ElemBuilder::def_print("mipmap_maxlevel", self.mipmap_max_level, 0, w)?;
278        ElemBuilder::def_print("mipmap_bias", self.mipmap_bias, 0., w)?;
279        self.extra.write_to(w)?;
280        e.end(w)
281    }
282}
283
284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
287pub enum WrapMode {
288    Wrap,
291    Mirror,
295    Clamp,
305    Border,
311    None,
315}
316
317impl Default for WrapMode {
318    fn default() -> Self {
319        Self::Wrap
320    }
321}
322
323impl FromStr for WrapMode {
324    type Err = ();
325
326    fn from_str(s: &str) -> Result<Self, Self::Err> {
327        match s {
328            "WRAP" => Ok(Self::Wrap),
329            "MIRROR" => Ok(Self::Mirror),
330            "CLAMP" => Ok(Self::Clamp),
331            "BORDER" => Ok(Self::Border),
332            "NONE" => Ok(Self::None),
333            _ => Err(()),
334        }
335    }
336}
337
338impl WrapMode {
339    pub fn to_str(self) -> &'static str {
341        match self {
342            Self::Wrap => "WRAP",
343            Self::Mirror => "MIRROR",
344            Self::Clamp => "CLAMP",
345            Self::Border => "BORDER",
346            Self::None => "NONE",
347        }
348    }
349}
350
351impl Display for WrapMode {
352    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353        Display::fmt(self.to_str(), f)
354    }
355}
356
357#[derive(Clone, Copy, Debug, PartialEq, Eq)]
359#[allow(missing_docs)]
360pub enum SamplerFilter {
361    None,
362    Nearest,
363    Linear,
364    NearestMipmapNearest,
365    LinearMipmapNearest,
366    NearestMipmapLinear,
367    LinearMipmapLinear,
368}
369
370impl Default for SamplerFilter {
371    fn default() -> Self {
372        Self::None
373    }
374}
375
376impl FromStr for SamplerFilter {
377    type Err = ();
378
379    fn from_str(s: &str) -> Result<Self, Self::Err> {
380        match s {
381            "NONE" => Ok(Self::None),
382            "NEAREST" => Ok(Self::Nearest),
383            "LINEAR" => Ok(Self::Linear),
384            "NEAREST_MIPMAP_NEAREST" => Ok(Self::NearestMipmapNearest),
385            "LINEAR_MIPMAP_NEAREST" => Ok(Self::LinearMipmapNearest),
386            "NEAREST_MIPMAP_LINEAR" => Ok(Self::NearestMipmapLinear),
387            "LINEAR_MIPMAP_LINEAR" => Ok(Self::LinearMipmapLinear),
388            _ => Err(()),
389        }
390    }
391}
392
393impl SamplerFilter {
394    pub fn to_str(self) -> &'static str {
396        match self {
397            Self::None => "NONE",
398            Self::Nearest => "NEAREST",
399            Self::Linear => "LINEAR",
400            Self::NearestMipmapNearest => "NEAREST_MIPMAP_NEAREST",
401            Self::LinearMipmapNearest => "LINEAR_MIPMAP_NEAREST",
402            Self::NearestMipmapLinear => "NEAREST_MIPMAP_LINEAR",
403            Self::LinearMipmapLinear => "LINEAR_MIPMAP_LINEAR",
404        }
405    }
406}
407
408impl Display for SamplerFilter {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        Display::fmt(self.to_str(), f)
411    }
412}
413
414#[derive(Clone, Debug)]
417pub struct Surface {
418    pub ty: SurfaceType,
420    pub init: SurfaceInit,
422    pub format: Option<String>,
427    pub format_hint: Option<Box<FormatHint>>,
432    pub size: Option<Box<[u32; 3]>>,
440    pub viewport_ratio: Option<Box<[f32; 2]>>,
449    pub mip_levels: u32,
454    pub mipmap_generate: bool,
464    pub extra: Vec<Extra>,
466}
467
468impl Surface {
469    pub fn new(ty: SurfaceType, init: SurfaceInit) -> Self {
471        Self {
472            ty,
473            init,
474            format: None,
475            format_hint: None,
476            size: None,
477            viewport_ratio: None,
478            mip_levels: 0,
479            mipmap_generate: false,
480            extra: vec![],
481        }
482    }
483}
484
485impl XNode for Surface {
486    const NAME: &'static str = "surface";
487    fn parse(element: &Element) -> Result<Self> {
488        debug_assert_eq!(element.name(), Self::NAME);
489        let mut it = element.children().peekable();
490        let res = Surface {
491            ty: parse_attr(element.attr("type"))?.ok_or("expected 'type' attr")?,
492            init: parse_one_many(&mut it, SurfaceInit::parse)?,
493            format: parse_opt("format", &mut it, parse_text)?,
494            format_hint: FormatHint::parse_opt_box(&mut it)?,
495            size: parse_opt("size", &mut it, parse_array_n)?,
496            viewport_ratio: parse_opt("viewport_ratio", &mut it, parse_array_n)?,
497            mip_levels: parse_opt("mip_levels", &mut it, parse_elem)?.unwrap_or(0),
498            mipmap_generate: parse_opt("mipmap_generate", &mut it, parse_elem)?.unwrap_or(false),
499            extra: Extra::parse_many(it)?,
500        };
501        if res.size.is_some() && res.viewport_ratio.is_some() {
502            return Err("size and viewport_ratio cannot be used together".into());
503        }
504        Ok(res)
505    }
506}
507
508impl XNodeWrite for Surface {
509    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
510        let mut e = Self::elem();
511        e.print_attr("type", self.ty);
512        let e = e.start(w)?;
513        self.init.write_to(w)?;
514        opt(&self.format, |e| ElemBuilder::print_str("format", e, w))?;
515        self.format_hint.write_to(w)?;
516        opt(&self.size, |e| ElemBuilder::print_arr("size", &**e, w))?;
517        opt(&self.viewport_ratio, |e| {
518            ElemBuilder::print_arr("viewport_ratio", &**e, w)
519        })?;
520        ElemBuilder::def_print("mip_levels", self.mip_levels, 0, w)?;
521        ElemBuilder::def_print("mipmap_generate", self.mipmap_generate, false, w)?;
522        self.extra.write_to(w)?;
523        e.end(w)
524    }
525}
526
527#[derive(Clone, Copy, Debug, PartialEq, Eq)]
529pub enum SurfaceType {
530    Untyped,
539    _1D,
541    _2D,
543    _3D,
545    Rect,
547    Cube,
549    Depth,
551}
552
553impl Default for SurfaceType {
554    fn default() -> Self {
555        Self::Untyped
556    }
557}
558
559impl FromStr for SurfaceType {
560    type Err = ();
561
562    fn from_str(s: &str) -> Result<Self, Self::Err> {
563        match s {
564            "UNTYPED" => Ok(Self::Untyped),
565            "1D" => Ok(Self::_1D),
566            "2D" => Ok(Self::_2D),
567            "3D" => Ok(Self::_3D),
568            "RECT" => Ok(Self::Rect),
569            "CUBE" => Ok(Self::Cube),
570            "DEPTH" => Ok(Self::Depth),
571            _ => Err(()),
572        }
573    }
574}
575
576impl SurfaceType {
577    pub fn to_str(self) -> &'static str {
579        match self {
580            Self::Untyped => "UNTYPED",
581            Self::_1D => "1D",
582            Self::_2D => "2D",
583            Self::_3D => "3D",
584            Self::Rect => "RECT",
585            Self::Cube => "CUBE",
586            Self::Depth => "DEPTH",
587        }
588    }
589}
590
591impl Display for SurfaceType {
592    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
593        Display::fmt(self.to_str(), f)
594    }
595}
596
597#[derive(Clone, Debug)]
600pub struct FormatHint {
601    pub channels: SurfaceChannels,
603    pub range: SurfaceRange,
605    pub precision: SurfacePrecision,
607    pub options: Vec<SurfaceOption>,
610    pub extra: Vec<Extra>,
612}
613
614impl FormatHint {
615    pub fn new(
617        channels: SurfaceChannels,
618        range: SurfaceRange,
619        precision: SurfacePrecision,
620    ) -> Self {
621        Self {
622            channels,
623            range,
624            precision,
625            options: vec![],
626            extra: vec![],
627        }
628    }
629}
630
631impl XNode for FormatHint {
632    const NAME: &'static str = "format_hint";
633    fn parse(element: &Element) -> Result<Self> {
634        debug_assert_eq!(element.name(), Self::NAME);
635        let mut it = element.children().peekable();
636        Ok(FormatHint {
637            channels: parse_one("channels", &mut it, parse_elem)?,
638            range: parse_one("range", &mut it, parse_elem)?,
639            precision: parse_one("precision", &mut it, parse_elem)?,
640            options: parse_list("option", &mut it, parse_elem)?,
641            extra: Extra::parse_many(it)?,
642        })
643    }
644}
645
646impl XNodeWrite for FormatHint {
647    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
648        let e = Self::elem().start(w)?;
649        ElemBuilder::print("channels", &self.channels, w)?;
650        ElemBuilder::print("range", &self.range, w)?;
651        ElemBuilder::print("precision", &self.precision, w)?;
652        many(&self.options, |e| ElemBuilder::print("option", e, w))?;
653        self.extra.write_to(w)?;
654        e.end(w)
655    }
656}
657
658#[derive(Clone, Debug)]
661pub enum SurfaceInit {
662    Null,
669    Target,
672    From {
683        mip: u32,
685        slice: u32,
690        face: SurfaceFace,
694        image: NameRef<Image>,
697    },
698}
699
700impl SurfaceInit {
701    pub fn init_from(image: impl Into<String>) -> Self {
703        Self::From {
704            mip: 0,
705            slice: 0,
706            face: Default::default(),
707            image: Ref::new(image.into()),
708        }
709    }
710    pub fn parse(element: &Element) -> Result<Option<Self>> {
712        Ok(Some(match element.name() {
713            "init_as_null" => Self::Null,
714            "init_as_target" => Self::Target,
715            "init_cube" | "init_volume" | "init_planar" => unimplemented!(),
716            "init_from" => Self::From {
717                mip: parse_attr(element.attr("mip"))?.unwrap_or(0),
718                slice: parse_attr(element.attr("slice"))?.unwrap_or(0),
719                face: parse_attr(element.attr("face"))?.unwrap_or_default(),
720                image: parse_elem(element)?,
721            },
722            _ => return Ok(None),
723        }))
724    }
725}
726
727impl XNodeWrite for SurfaceInit {
728    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
729        match self {
730            Self::Null => ElemBuilder::new("init_as_null").end(w),
731            Self::Target => ElemBuilder::new("init_as_target").end(w),
732            &Self::From {
733                mip,
734                slice,
735                face,
736                ref image,
737            } => {
738                let mut e = ElemBuilder::new("init_from");
739                e.def_print_attr("mip", mip, 0);
740                e.def_print_attr("slice", slice, 0);
741                e.def_print_attr("face", face, Default::default());
742                let e = e.start(w)?;
743                print_elem(image, w)?;
744                e.end(w)
745            }
746        }
747    }
748}
749
750#[derive(Clone, Copy, Debug, PartialEq, Eq)]
752pub enum SurfaceFace {
753    PosX,
755    NegX,
757    PosY,
759    NegY,
761    PosZ,
763    NegZ,
765}
766
767impl Default for SurfaceFace {
768    fn default() -> Self {
769        Self::PosX
770    }
771}
772
773impl FromStr for SurfaceFace {
774    type Err = ();
775
776    fn from_str(s: &str) -> Result<Self, Self::Err> {
777        match s {
778            "POSITIVE_X" => Ok(Self::PosX),
779            "NEGATIVE_X" => Ok(Self::NegX),
780            "POSITIVE_Y" => Ok(Self::PosY),
781            "NEGATIVE_Y" => Ok(Self::NegY),
782            "POSITIVE_Z" => Ok(Self::PosZ),
783            "NEGATIVE_Z" => Ok(Self::NegZ),
784            _ => Err(()),
785        }
786    }
787}
788
789impl SurfaceFace {
790    pub fn to_str(self) -> &'static str {
792        match self {
793            Self::PosX => "POSITIVE_X",
794            Self::NegX => "NEGATIVE_X",
795            Self::PosY => "POSITIVE_Y",
796            Self::NegY => "NEGATIVE_Y",
797            Self::PosZ => "POSITIVE_Z",
798            Self::NegZ => "NEGATIVE_Z",
799        }
800    }
801}
802
803impl Display for SurfaceFace {
804    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
805        Display::fmt(self.to_str(), f)
806    }
807}
808
809#[derive(Clone, Copy, Debug, PartialEq, Eq)]
813pub enum SurfaceChannels {
814    RGB,
816    RGBA,
819    L,
821    LA,
823    D,
825    XYZ,
827    XYZW,
829}
830
831impl FromStr for SurfaceChannels {
832    type Err = ();
833
834    fn from_str(s: &str) -> Result<Self, Self::Err> {
835        match s {
836            "RGB" => Ok(Self::RGB),
837            "RGBA" => Ok(Self::RGBA),
838            "L" => Ok(Self::L),
839            "LA" => Ok(Self::LA),
840            "D" => Ok(Self::D),
841            "XYZ" => Ok(Self::XYZ),
842            "XYZW" => Ok(Self::XYZW),
843            _ => Err(()),
844        }
845    }
846}
847
848impl SurfaceChannels {
849    pub fn to_str(self) -> &'static str {
851        match self {
852            Self::RGB => "RGB",
853            Self::RGBA => "RGBA",
854            Self::L => "L",
855            Self::LA => "LA",
856            Self::D => "D",
857            Self::XYZ => "XYZ",
858            Self::XYZW => "XYZW",
859        }
860    }
861}
862
863impl Display for SurfaceChannels {
864    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
865        Display::fmt(self.to_str(), f)
866    }
867}
868
869#[derive(Clone, Copy, Debug, PartialEq, Eq)]
874pub enum SurfaceRange {
875    SNorm,
878    UNorm,
881    SInt,
884    UInt,
887    Float,
889}
890
891impl FromStr for SurfaceRange {
892    type Err = ();
893
894    fn from_str(s: &str) -> Result<Self, Self::Err> {
895        match s {
896            "SNORM" => Ok(Self::SNorm),
897            "UNORM" => Ok(Self::UNorm),
898            "SINT" => Ok(Self::SInt),
899            "UINT" => Ok(Self::UInt),
900            "FLOAT" => Ok(Self::Float),
901            _ => Err(()),
902        }
903    }
904}
905
906impl SurfaceRange {
907    pub fn to_str(self) -> &'static str {
909        match self {
910            Self::SNorm => "SNORM",
911            Self::UNorm => "UNORM",
912            Self::SInt => "SINT",
913            Self::UInt => "UINT",
914            Self::Float => "FLOAT",
915        }
916    }
917}
918
919impl Display for SurfaceRange {
920    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
921        Display::fmt(self.to_str(), f)
922    }
923}
924
925#[derive(Clone, Copy, Debug, PartialEq, Eq)]
931pub enum SurfacePrecision {
932    Low,
935    Mid,
938    High,
941}
942
943impl FromStr for SurfacePrecision {
944    type Err = ();
945
946    fn from_str(s: &str) -> Result<Self, Self::Err> {
947        match s {
948            "LOW" => Ok(Self::Low),
949            "MID" => Ok(Self::Mid),
950            "HIGH" => Ok(Self::High),
951            _ => Err(()),
952        }
953    }
954}
955
956impl SurfacePrecision {
957    pub fn to_str(self) -> &'static str {
959        match self {
960            Self::Low => "LOW",
961            Self::Mid => "MID",
962            Self::High => "HIGH",
963        }
964    }
965}
966
967impl Display for SurfacePrecision {
968    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
969        Display::fmt(self.to_str(), f)
970    }
971}
972
973#[derive(Clone, Copy, Debug, PartialEq, Eq)]
976pub enum SurfaceOption {
977    SrgbGamma,
980    Normalized3,
983    Normalized4,
986    Compressible,
990}
991
992impl FromStr for SurfaceOption {
993    type Err = ();
994
995    fn from_str(s: &str) -> Result<Self, Self::Err> {
996        match s {
997            "SRGB_GAMMA" => Ok(Self::SrgbGamma),
998            "NORMALIZED3" => Ok(Self::Normalized3),
999            "NORMALIZED4" => Ok(Self::Normalized4),
1000            "COMPRESSABLE" => Ok(Self::Compressible),
1001            _ => Err(()),
1002        }
1003    }
1004}
1005
1006impl SurfaceOption {
1007    pub fn to_str(self) -> &'static str {
1009        match self {
1010            Self::SrgbGamma => "SRGB_GAMMA",
1011            Self::Normalized3 => "NORMALIZED3",
1012            Self::Normalized4 => "NORMALIZED4",
1013            Self::Compressible => "COMPRESSABLE",
1014        }
1015    }
1016}
1017
1018impl Display for SurfaceOption {
1019    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1020        Display::fmt(self.to_str(), f)
1021    }
1022}