aboutsummaryrefslogtreecommitdiff
path: root/src/render_helpers/texture.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-06-01 12:27:30 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2024-06-10 18:08:00 +0300
commit3c63be6261f3420008366b41b3df6d57b3ec3867 (patch)
treec937af80e68e25d6a0d18aeae9338a4a161d83ab /src/render_helpers/texture.rs
parente3406ac2556c7f68cd00f11b5856222dcce7f680 (diff)
downloadniri-3c63be6261f3420008366b41b3df6d57b3ec3867.tar.gz
niri-3c63be6261f3420008366b41b3df6d57b3ec3867.tar.bz2
niri-3c63be6261f3420008366b41b3df6d57b3ec3867.zip
Implement our own TextureBuffer/RenderElement
Supports fractional texture scale + has some getters.
Diffstat (limited to 'src/render_helpers/texture.rs')
-rw-r--r--src/render_helpers/texture.rs207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/render_helpers/texture.rs b/src/render_helpers/texture.rs
new file mode 100644
index 00000000..c1eec367
--- /dev/null
+++ b/src/render_helpers/texture.rs
@@ -0,0 +1,207 @@
+use smithay::backend::allocator::Fourcc;
+use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};
+use smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};
+use smithay::backend::renderer::{Frame as _, ImportMem, Renderer, Texture};
+use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
+
+/// Smithay's texture buffer, but with fractional scale.
+#[derive(Debug, Clone)]
+pub struct TextureBuffer<T> {
+ id: Id,
+ commit_counter: CommitCounter,
+ renderer_id: usize,
+ texture: T,
+ scale: Scale<f64>,
+ transform: Transform,
+ opaque_regions: Vec<Rectangle<i32, Buffer>>,
+}
+
+/// Render element for a [`TextureBuffer`].
+#[derive(Debug)]
+pub struct TextureRenderElement<T> {
+ buffer: TextureBuffer<T>,
+ location: Point<f64, Logical>,
+ alpha: f32,
+ src: Option<Rectangle<f64, Logical>>,
+ size: Option<Size<f64, Logical>>,
+ kind: Kind,
+}
+
+impl<T> TextureBuffer<T> {
+ pub fn from_texture<R: Renderer<TextureId = T>>(
+ renderer: &R,
+ texture: T,
+ scale: impl Into<Scale<f64>>,
+ transform: Transform,
+ opaque_regions: Vec<Rectangle<i32, Buffer>>,
+ ) -> Self {
+ TextureBuffer {
+ id: Id::new(),
+ commit_counter: CommitCounter::default(),
+ renderer_id: renderer.id(),
+ texture,
+ scale: scale.into(),
+ transform,
+ opaque_regions,
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn from_memory<R: Renderer<TextureId = T> + ImportMem>(
+ renderer: &mut R,
+ data: &[u8],
+ format: Fourcc,
+ size: impl Into<Size<i32, Buffer>>,
+ flipped: bool,
+ scale: impl Into<Scale<f64>>,
+ transform: Transform,
+ opaque_regions: Vec<Rectangle<i32, Buffer>>,
+ ) -> Result<Self, <R as Renderer>::Error> {
+ let texture = renderer.import_memory(data, format, size.into(), flipped)?;
+ Ok(TextureBuffer::from_texture(
+ renderer,
+ texture,
+ scale,
+ transform,
+ opaque_regions,
+ ))
+ }
+
+ pub fn texture(&self) -> &T {
+ &self.texture
+ }
+
+ pub fn texture_scale(&self) -> Scale<f64> {
+ self.scale
+ }
+}
+
+impl<T: Texture> TextureBuffer<T> {
+ pub fn logical_size(&self) -> Size<f64, Logical> {
+ self.texture
+ .size()
+ .to_f64()
+ .to_logical(self.scale, self.transform)
+ }
+}
+
+impl<T> TextureRenderElement<T> {
+ pub fn from_texture_buffer(
+ buffer: TextureBuffer<T>,
+ location: impl Into<Point<f64, Logical>>,
+ alpha: f32,
+ src: Option<Rectangle<f64, Logical>>,
+ size: Option<Size<f64, Logical>>,
+ kind: Kind,
+ ) -> Self {
+ TextureRenderElement {
+ buffer,
+ location: location.into(),
+ alpha,
+ src,
+ size,
+ kind,
+ }
+ }
+}
+
+impl<T: Texture> TextureRenderElement<T> {
+ pub fn logical_size(&self) -> Size<f64, Logical> {
+ self.size
+ .or_else(|| self.src.map(|src| src.size))
+ .unwrap_or_else(|| self.buffer.logical_size())
+ }
+}
+
+impl<T: Texture> Element for TextureRenderElement<T> {
+ fn id(&self) -> &Id {
+ &self.buffer.id
+ }
+
+ fn current_commit(&self) -> CommitCounter {
+ self.buffer.commit_counter
+ }
+
+ fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
+ let logical_geo = Rectangle::from_loc_and_size(self.location, self.logical_size());
+ logical_geo.to_physical_precise_round(scale)
+ }
+
+ fn transform(&self) -> Transform {
+ self.buffer.transform
+ }
+
+ fn src(&self) -> Rectangle<f64, Buffer> {
+ self.src
+ .map(|src| {
+ src.to_buffer(
+ self.buffer.scale,
+ self.buffer.transform,
+ &self.buffer.logical_size(),
+ )
+ })
+ .unwrap_or_else(|| {
+ Rectangle::from_loc_and_size((0, 0), self.buffer.texture.size()).to_f64()
+ })
+ }
+
+ fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
+ let texture_size = self.buffer.texture.size().to_f64();
+ let src = self.src();
+
+ self.buffer
+ .opaque_regions
+ .iter()
+ .filter_map(|region| {
+ let mut region = region.to_f64().intersection(src)?;
+
+ region.loc -= src.loc;
+ region.upscale(texture_size / src.size);
+
+ let logical =
+ region.to_logical(self.buffer.scale, self.buffer.transform, &src.size);
+ Some(logical.to_physical_precise_down(scale))
+ })
+ .collect()
+ }
+
+ fn alpha(&self) -> f32 {
+ self.alpha
+ }
+
+ fn kind(&self) -> Kind {
+ self.kind
+ }
+}
+
+impl<R, T> RenderElement<R> for TextureRenderElement<T>
+where
+ R: Renderer<TextureId = T>,
+ T: Texture,
+{
+ fn draw(
+ &self,
+ frame: &mut <R as Renderer>::Frame<'_>,
+ src: Rectangle<f64, Buffer>,
+ dest: Rectangle<i32, Physical>,
+ damage: &[Rectangle<i32, Physical>],
+ ) -> Result<(), <R as Renderer>::Error> {
+ if frame.id() != self.buffer.renderer_id {
+ warn!("trying to render texture from different renderer");
+ return Ok(());
+ }
+
+ frame.render_texture_from_to(
+ &self.buffer.texture,
+ src,
+ dest,
+ damage,
+ self.buffer.transform,
+ self.alpha,
+ )
+ }
+
+ fn underlying_storage(&self, _renderer: &mut R) -> Option<UnderlyingStorage<'_>> {
+ None
+ }
+}