diff options
| author | 2024-05-03 13:25:58 +0200 | |
|---|---|---|
| committer | 2024-05-03 13:25:58 +0200 | |
| commit | fa9e1d96ea1924b51749b775ea0e67e69bc8a305 (patch) | |
| tree | 20cb13d6e3074569a15a16c8b0d261b1875a2045 | |
| parent | 38cf87cb45484c7e52ddf775fb3abd7edbecc652 (diff) | |
| download | iced-fa9e1d96ea1924b51749b775ea0e67e69bc8a305.tar.gz iced-fa9e1d96ea1924b51749b775ea0e67e69bc8a305.tar.bz2 iced-fa9e1d96ea1924b51749b775ea0e67e69bc8a305.zip  | |
Introduce dynamic `opacity` support for `Image` and `Svg`
| -rw-r--r-- | core/src/image.rs | 1 | ||||
| -rw-r--r-- | core/src/renderer/null.rs | 2 | ||||
| -rw-r--r-- | core/src/svg.rs | 1 | ||||
| -rw-r--r-- | examples/ferris/src/main.rs | 45 | ||||
| -rw-r--r-- | graphics/src/image.rs | 10 | ||||
| -rw-r--r-- | renderer/src/fallback.rs | 12 | ||||
| -rw-r--r-- | tiny_skia/src/engine.rs | 4 | ||||
| -rw-r--r-- | tiny_skia/src/layer.rs | 4 | ||||
| -rw-r--r-- | tiny_skia/src/lib.rs | 12 | ||||
| -rw-r--r-- | tiny_skia/src/raster.rs | 2 | ||||
| -rw-r--r-- | tiny_skia/src/vector.rs | 6 | ||||
| -rw-r--r-- | wgpu/src/image/mod.rs | 22 | ||||
| -rw-r--r-- | wgpu/src/layer.rs | 4 | ||||
| -rw-r--r-- | wgpu/src/lib.rs | 12 | ||||
| -rw-r--r-- | wgpu/src/shader/image.wgsl | 11 | ||||
| -rw-r--r-- | widget/src/image.rs | 14 | ||||
| -rw-r--r-- | widget/src/image/viewer.rs | 1 | ||||
| -rw-r--r-- | widget/src/svg.rs | 12 | 
18 files changed, 142 insertions, 33 deletions
diff --git a/core/src/image.rs b/core/src/image.rs index 91a7fd36..82ecdd0f 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -174,5 +174,6 @@ pub trait Renderer: crate::Renderer {          filter_method: FilterMethod,          bounds: Rectangle,          rotation: Radians, +        opacity: f32,      );  } diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 91519b40..e8709dbc 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -173,6 +173,7 @@ impl image::Renderer for () {          _filter_method: image::FilterMethod,          _bounds: Rectangle,          _rotation: Radians, +        _opacity: f32,      ) {      }  } @@ -188,6 +189,7 @@ impl svg::Renderer for () {          _color: Option<Color>,          _bounds: Rectangle,          _rotation: Radians, +        _opacity: f32,      ) {      }  } diff --git a/core/src/svg.rs b/core/src/svg.rs index 01f102e3..946b8156 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -101,5 +101,6 @@ pub trait Renderer: crate::Renderer {          color: Option<Color>,          bounds: Rectangle,          rotation: Radians, +        opacity: f32,      );  } diff --git a/examples/ferris/src/main.rs b/examples/ferris/src/main.rs index 13d746dd..1d0e9181 100644 --- a/examples/ferris/src/main.rs +++ b/examples/ferris/src/main.rs @@ -17,6 +17,7 @@ pub fn main() -> iced::Result {  struct Image {      width: f32, +    opacity: f32,      rotation: Rotation,      content_fit: ContentFit,      spin: bool, @@ -26,6 +27,7 @@ struct Image {  #[derive(Debug, Clone, Copy)]  enum Message {      WidthChanged(f32), +    OpacityChanged(f32),      RotationStrategyChanged(RotationStrategy),      RotationChanged(Degrees),      ContentFitChanged(ContentFit), @@ -39,6 +41,9 @@ impl Image {              Message::WidthChanged(width) => {                  self.width = width;              } +            Message::OpacityChanged(opacity) => { +                self.opacity = opacity; +            }              Message::RotationStrategyChanged(strategy) => {                  self.rotation = match strategy {                      RotationStrategy::Floating => { @@ -97,6 +102,7 @@ impl Image {                  .width(self.width)                  .content_fit(self.content_fit)                  .rotation(self.rotation) +                .opacity(self.opacity)              )              .explain(Color::WHITE),              "I am Ferris!" @@ -117,14 +123,15 @@ impl Image {                  Message::ContentFitChanged              )              .width(Length::Fill), -            column![ +            with_value(                  slider(100.0..=500.0, self.width, Message::WidthChanged), -                text(format!("Width: {}px", self.width)) -                    .size(12) -                    .line_height(1.0) -            ] -            .spacing(2) -            .align_items(Alignment::Center) +                format!("Width: {}px", self.width) +            ), +            with_value( +                slider(0.0..=1.0, self.opacity, Message::OpacityChanged) +                    .step(0.01), +                format!("Opacity: {:.2}", self.opacity) +            )          ]          .spacing(10)          .align_items(Alignment::End); @@ -139,7 +146,7 @@ impl Image {                  Message::RotationStrategyChanged,              )              .width(Length::Fill), -            column![ +            with_value(                  row![                      slider(                          Degrees::RANGE, @@ -153,15 +160,8 @@ impl Image {                  ]                  .spacing(10)                  .align_items(Alignment::Center), -                text(format!( -                    "Rotation: {:.0}°", -                    f32::from(self.rotation.degrees()) -                )) -                .size(12) -                .line_height(1.0) -            ] -            .spacing(2) -            .align_items(Alignment::Center) +                format!("Rotation: {:.0}°", f32::from(self.rotation.degrees())) +            )          ]          .spacing(10)          .align_items(Alignment::End); @@ -176,6 +176,7 @@ impl Default for Image {      fn default() -> Self {          Self {              width: 300.0, +            opacity: 1.0,              rotation: Rotation::default(),              content_fit: ContentFit::default(),              spin: false, @@ -198,3 +199,13 @@ impl std::fmt::Display for RotationStrategy {          })      }  } + +fn with_value<'a>( +    control: impl Into<Element<'a, Message>>, +    value: String, +) -> Element<'a, Message> { +    column![control.into(), text(value).size(12).line_height(1.0)] +        .spacing(2) +        .align_items(Alignment::Center) +        .into() +} diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 9d09bf4c..318592be 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -18,8 +18,11 @@ pub enum Image {          /// The bounds of the image.          bounds: Rectangle, -        /// The rotation of the image in radians +        /// The rotation of the image.          rotation: Radians, + +        /// The opacity of the image. +        opacity: f32,      },      /// A vector image.      Vector { @@ -32,8 +35,11 @@ pub enum Image {          /// The bounds of the image.          bounds: Rectangle, -        /// The rotation of the image in radians +        /// The rotation of the image.          rotation: Radians, + +        /// The opacity of the image. +        opacity: f32,      },  } diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index a077031b..6a169692 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -155,11 +155,18 @@ where          filter_method: image::FilterMethod,          bounds: Rectangle,          rotation: Radians, +        opacity: f32,      ) {          delegate!(              self,              renderer, -            renderer.draw_image(handle, filter_method, bounds, rotation) +            renderer.draw_image( +                handle, +                filter_method, +                bounds, +                rotation, +                opacity +            )          );      }  } @@ -179,11 +186,12 @@ where          color: Option<Color>,          bounds: Rectangle,          rotation: Radians, +        opacity: f32,      ) {          delegate!(              self,              renderer, -            renderer.draw_svg(handle, color, bounds, rotation) +            renderer.draw_svg(handle, color, bounds, rotation, opacity)          );      }  } diff --git a/tiny_skia/src/engine.rs b/tiny_skia/src/engine.rs index 544ff614..028b304f 100644 --- a/tiny_skia/src/engine.rs +++ b/tiny_skia/src/engine.rs @@ -551,6 +551,7 @@ impl Engine {                  filter_method,                  bounds,                  rotation, +                opacity,              } => {                  let physical_bounds = *bounds * _transformation; @@ -574,6 +575,7 @@ impl Engine {                      handle,                      *filter_method,                      *bounds, +                    *opacity,                      _pixels,                      transform,                      clip_mask, @@ -585,6 +587,7 @@ impl Engine {                  color,                  bounds,                  rotation, +                opacity,              } => {                  let physical_bounds = *bounds * _transformation; @@ -608,6 +611,7 @@ impl Engine {                      handle,                      *color,                      physical_bounds, +                    *opacity,                      _pixels,                      transform,                      clip_mask, diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index c907c93c..48fca1d8 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -122,12 +122,14 @@ impl Layer {          bounds: Rectangle,          transformation: Transformation,          rotation: Radians, +        opacity: f32,      ) {          let image = Image::Raster {              handle,              filter_method,              bounds: bounds * transformation,              rotation, +            opacity,          };          self.images.push(image); @@ -140,12 +142,14 @@ impl Layer {          bounds: Rectangle,          transformation: Transformation,          rotation: Radians, +        opacity: f32,      ) {          let svg = Image::Vector {              handle,              color,              bounds: bounds * transformation,              rotation, +            opacity,          };          self.images.push(svg); diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index e0cbfa0d..1aabff00 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -378,6 +378,7 @@ impl core::image::Renderer for Renderer {          filter_method: core::image::FilterMethod,          bounds: Rectangle,          rotation: core::Radians, +        opacity: f32,      ) {          let (layer, transformation) = self.layers.current_mut();          layer.draw_image( @@ -386,6 +387,7 @@ impl core::image::Renderer for Renderer {              bounds,              transformation,              rotation, +            opacity,          );      }  } @@ -405,9 +407,17 @@ impl core::svg::Renderer for Renderer {          color: Option<Color>,          bounds: Rectangle,          rotation: core::Radians, +        opacity: f32,      ) {          let (layer, transformation) = self.layers.current_mut(); -        layer.draw_svg(handle, color, bounds, transformation, rotation); +        layer.draw_svg( +            handle, +            color, +            bounds, +            transformation, +            rotation, +            opacity, +        );      }  } diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs index 907fce7c..c40f55b2 100644 --- a/tiny_skia/src/raster.rs +++ b/tiny_skia/src/raster.rs @@ -31,6 +31,7 @@ impl Pipeline {          handle: &raster::Handle,          filter_method: raster::FilterMethod,          bounds: Rectangle, +        opacity: f32,          pixels: &mut tiny_skia::PixmapMut<'_>,          transform: tiny_skia::Transform,          clip_mask: Option<&tiny_skia::Mask>, @@ -56,6 +57,7 @@ impl Pipeline {                  image,                  &tiny_skia::PixmapPaint {                      quality, +                    opacity,                      ..Default::default()                  },                  transform, diff --git a/tiny_skia/src/vector.rs b/tiny_skia/src/vector.rs index 8e3463f2..bbe08cb8 100644 --- a/tiny_skia/src/vector.rs +++ b/tiny_skia/src/vector.rs @@ -34,6 +34,7 @@ impl Pipeline {          handle: &Handle,          color: Option<Color>,          bounds: Rectangle, +        opacity: f32,          pixels: &mut tiny_skia::PixmapMut<'_>,          transform: Transform,          clip_mask: Option<&tiny_skia::Mask>, @@ -47,7 +48,10 @@ impl Pipeline {                  bounds.x as i32,                  bounds.y as i32,                  image, -                &tiny_skia::PixmapPaint::default(), +                &tiny_skia::PixmapPaint { +                    opacity, +                    ..tiny_skia::PixmapPaint::default() +                },                  transform,                  clip_mask,              ); diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 285eb2f6..063822aa 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -137,16 +137,18 @@ impl Pipeline {                              0 => Float32x2,                              // Center                              1 => Float32x2, -                            // Image size +                            // Scale                              2 => Float32x2,                              // Rotation                              3 => Float32, +                            // Opacity +                            4 => Float32,                              // Atlas position -                            4 => Float32x2, -                            // Atlas scale                              5 => Float32x2, +                            // Atlas scale +                            6 => Float32x2,                              // Layer -                            6 => Sint32, +                            7 => Sint32,                          ),                      }],                  }, @@ -229,6 +231,7 @@ impl Pipeline {                      filter_method,                      bounds,                      rotation, +                    opacity,                  } => {                      if let Some(atlas_entry) =                          cache.upload_raster(device, encoder, handle) @@ -237,6 +240,7 @@ impl Pipeline {                              [bounds.x, bounds.y],                              [bounds.width, bounds.height],                              f32::from(*rotation), +                            *opacity,                              atlas_entry,                              match filter_method {                                  crate::core::image::FilterMethod::Nearest => { @@ -258,6 +262,7 @@ impl Pipeline {                      color,                      bounds,                      rotation, +                    opacity,                  } => {                      let size = [bounds.width, bounds.height]; @@ -268,6 +273,7 @@ impl Pipeline {                              [bounds.x, bounds.y],                              size,                              f32::from(*rotation), +                            *opacity,                              atlas_entry,                              nearest_instances,                          ); @@ -498,6 +504,7 @@ struct Instance {      _center: [f32; 2],      _size: [f32; 2],      _rotation: f32, +    _opacity: f32,      _position_in_atlas: [f32; 2],      _size_in_atlas: [f32; 2],      _layer: u32, @@ -517,6 +524,7 @@ fn add_instances(      image_position: [f32; 2],      image_size: [f32; 2],      rotation: f32, +    opacity: f32,      entry: &atlas::Entry,      instances: &mut Vec<Instance>,  ) { @@ -532,6 +540,7 @@ fn add_instances(                  center,                  image_size,                  rotation, +                opacity,                  allocation,                  instances,              ); @@ -561,7 +570,8 @@ fn add_instances(                  ];                  add_instance( -                    position, center, size, rotation, allocation, instances, +                    position, center, size, rotation, opacity, allocation, +                    instances,                  );              }          } @@ -574,6 +584,7 @@ fn add_instance(      center: [f32; 2],      size: [f32; 2],      rotation: f32, +    opacity: f32,      allocation: &atlas::Allocation,      instances: &mut Vec<Instance>,  ) { @@ -586,6 +597,7 @@ fn add_instance(          _center: center,          _size: size,          _rotation: rotation, +        _opacity: opacity,          _position_in_atlas: [              (x as f32 + 0.5) / atlas::SIZE as f32,              (y as f32 + 0.5) / atlas::SIZE as f32, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index e0242c59..9551311d 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -119,12 +119,14 @@ impl Layer {          bounds: Rectangle,          transformation: Transformation,          rotation: Radians, +        opacity: f32,      ) {          let image = Image::Raster {              handle,              filter_method,              bounds: bounds * transformation,              rotation, +            opacity,          };          self.images.push(image); @@ -137,12 +139,14 @@ impl Layer {          bounds: Rectangle,          transformation: Transformation,          rotation: Radians, +        opacity: f32,      ) {          let svg = Image::Vector {              handle,              color,              bounds: bounds * transformation,              rotation, +            opacity,          };          self.images.push(svg); diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index eb600dde..4c168029 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -518,6 +518,7 @@ impl core::image::Renderer for Renderer {          filter_method: core::image::FilterMethod,          bounds: Rectangle,          rotation: core::Radians, +        opacity: f32,      ) {          let (layer, transformation) = self.layers.current_mut();          layer.draw_image( @@ -526,6 +527,7 @@ impl core::image::Renderer for Renderer {              bounds,              transformation,              rotation, +            opacity,          );      }  } @@ -542,9 +544,17 @@ impl core::svg::Renderer for Renderer {          color_filter: Option<Color>,          bounds: Rectangle,          rotation: core::Radians, +        opacity: f32,      ) {          let (layer, transformation) = self.layers.current_mut(); -        layer.draw_svg(handle, color_filter, bounds, transformation, rotation); +        layer.draw_svg( +            handle, +            color_filter, +            bounds, +            transformation, +            rotation, +            opacity, +        );      }  } diff --git a/wgpu/src/shader/image.wgsl b/wgpu/src/shader/image.wgsl index 71bf939c..accefc17 100644 --- a/wgpu/src/shader/image.wgsl +++ b/wgpu/src/shader/image.wgsl @@ -12,15 +12,17 @@ struct VertexInput {      @location(1) center: vec2<f32>,      @location(2) scale: vec2<f32>,      @location(3) rotation: f32, -    @location(4) atlas_pos: vec2<f32>, -    @location(5) atlas_scale: vec2<f32>, -    @location(6) layer: i32, +    @location(4) opacity: f32, +    @location(5) atlas_pos: vec2<f32>, +    @location(6) atlas_scale: vec2<f32>, +    @location(7) layer: i32,  }  struct VertexOutput {      @builtin(position) position: vec4<f32>,      @location(0) uv: vec2<f32>,      @location(1) layer: f32, // this should be an i32, but naga currently reads that as requiring interpolation. +    @location(2) opacity: f32,  }  @vertex @@ -33,6 +35,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {      // Map the vertex position to the atlas texture.      out.uv = vec2<f32>(v_pos * input.atlas_scale + input.atlas_pos);      out.layer = f32(input.layer); +    out.opacity = input.opacity;      // Calculate the vertex position and move the center to the origin      v_pos = input.pos + v_pos * input.scale - input.center; @@ -56,5 +59,5 @@ fn vs_main(input: VertexInput) -> VertexOutput {  @fragment  fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {      // Sample the texture at the given UV coordinate and layer. -    return textureSample(u_texture, u_sampler, input.uv, i32(input.layer)); +    return textureSample(u_texture, u_sampler, input.uv, i32(input.layer)) * vec4<f32>(1.0, 1.0, 1.0, input.opacity);  } diff --git a/widget/src/image.rs b/widget/src/image.rs index 45209a91..80e17263 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -38,6 +38,7 @@ pub struct Image<Handle> {      content_fit: ContentFit,      filter_method: FilterMethod,      rotation: Rotation, +    opacity: f32,  }  impl<Handle> Image<Handle> { @@ -50,6 +51,7 @@ impl<Handle> Image<Handle> {              content_fit: ContentFit::default(),              filter_method: FilterMethod::default(),              rotation: Rotation::default(), +            opacity: 1.0,          }      } @@ -84,6 +86,15 @@ impl<Handle> Image<Handle> {          self.rotation = rotation.into();          self      } + +    /// Sets the opacity of the [`Image`]. +    /// +    /// It should be in the [0.0, 1.0] range—`0.0` meaning completely transparent, +    /// and `1.0` meaning completely opaque. +    pub fn opacity(mut self, opacity: impl Into<f32>) -> Self { +        self.opacity = opacity.into(); +        self +    }  }  /// Computes the layout of an [`Image`]. @@ -136,6 +147,7 @@ pub fn draw<Renderer, Handle>(      content_fit: ContentFit,      filter_method: FilterMethod,      rotation: Rotation, +    opacity: f32,  ) where      Renderer: image::Renderer<Handle = Handle>,      Handle: Clone, @@ -173,6 +185,7 @@ pub fn draw<Renderer, Handle>(              filter_method,              drawing_bounds,              rotation.radians(), +            opacity,          );      }; @@ -231,6 +244,7 @@ where              self.content_fit,              self.filter_method,              self.rotation, +            self.opacity,          );      }  } diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 5eb76452..8fe6f021 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -342,6 +342,7 @@ where                          ..Rectangle::with_size(image_size)                      },                      Radians(0.0), +                    1.0,                  );              });          }); diff --git a/widget/src/svg.rs b/widget/src/svg.rs index c1fccba1..4551bcad 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -30,6 +30,7 @@ where      content_fit: ContentFit,      class: Theme::Class<'a>,      rotation: Rotation, +    opacity: f32,  }  impl<'a, Theme> Svg<'a, Theme> @@ -45,6 +46,7 @@ where              content_fit: ContentFit::Contain,              class: Theme::default(),              rotation: Rotation::default(), +            opacity: 1.0,          }      } @@ -103,6 +105,15 @@ where          self.rotation = rotation.into();          self      } + +    /// Sets the opacity of the [`Svg`]. +    /// +    /// It should be in the [0.0, 1.0] range—`0.0` meaning completely transparent, +    /// and `1.0` meaning completely opaque. +    pub fn opacity(mut self, opacity: impl Into<f32>) -> Self { +        self.opacity = opacity.into(); +        self +    }  }  impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> @@ -204,6 +215,7 @@ where                  style.color,                  drawing_bounds,                  self.rotation.radians(), +                self.opacity,              );          };  | 
