use crate::draw::texture::{ColorFormat, Texture};
use crate::ecs::{Entity, World};
use crate::types::{Point, Rect};
use crate::widget::offscreen::{WidgetTextureAccess, WidgetTextureRef};
use crate::widget::view::{View, ViewCtx};
pub struct MirrorOf {
pub source: Entity,
pub fade: u8,
}
impl MirrorOf {
pub fn new(source: Entity) -> Self {
Self { source, fade: 0 }
}
pub fn with_fade(mut self, fade: u8) -> Self {
self.fade = fade;
self
}
}
fn mirror_render(
renderer: &mut dyn Renderer,
world: &World,
entity: Entity,
rect: &Rect,
ctx: &mut ViewCtx,
) {
let Some(mir) = world.get::<MirrorOf>(entity) else {
return;
};
let Some(snap) = world.texture_of(mir.source) else {
return;
};
let src = snap.borrow();
flip_into(&src, ctx.transform, rect, ctx.clip, mir.fade, renderer);
}
fn flip_into(
src: &Texture,
transform: crate::types::Transform,
rect: &Rect,
clip: &Rect,
fade: u8,
renderer: &mut dyn Renderer,
) {
use crate::draw::command::DrawCommand;
let Ok(_) = ensure_rgba(src) else {
return;
};
let w = src.width;
let h = src.height;
let mut tmp = Texture::owned(w, h, ColorFormat::RGBA8888);
let src_buf = src.buf.as_slice();
let src_stride = src.stride;
if let crate::draw::texture::TexBuf::Owned(ref mut dst_buf) = tmp.buf {
let dst_stride = w as usize * 4;
for y in 0..h as usize {
let src_row = (h as usize - 1 - y) * src_stride;
let dst_row = y * dst_stride;
let row_fade = (fade as i32 * y as i32 / h as i32) as u8;
let alpha_scale = 255i32 - row_fade as i32;
for x in 0..w as usize {
let si = src_row + x * 4;
let di = dst_row + x * 4;
dst_buf[di] = src_buf[si];
dst_buf[di + 1] = src_buf[si + 1];
dst_buf[di + 2] = src_buf[si + 2];
let a = src_buf[si + 3] as i32;
dst_buf[di + 3] = ((a * alpha_scale) / 255) as u8;
}
}
}
let cmd = DrawCommand::Blit {
pos: Point::new(rect.x, rect.y),
size: Point::new(rect.w, rect.h),
transform,
quad: None,
texture: &tmp,
};
renderer.draw(&cmd, clip);
}
fn ensure_rgba(tex: &Texture) -> Result<(), ()> {
match tex.format {
ColorFormat::RGBA8888 => Ok(()),
_ => Err(()),
}
}
fn mirror_attach(world: &mut World, entity: Entity) {
let Some(mir) = world.get::<MirrorOf>(entity) else {
return;
};
let source = mir.source;
if world.get::<WidgetTextureRef>(entity).is_none() {
world.insert(entity, WidgetTextureRef(source));
}
use crate::widget::offscreen::{OffscreenAlphaMode, OffscreenAutoAdded};
if world
.get::<crate::widget::OffscreenRender>(source)
.is_none()
{
world.insert(source, crate::widget::OffscreenRender::default());
world.insert(source, OffscreenAutoAdded);
}
if world.get::<OffscreenAlphaMode>(source).is_none() {
world.insert(source, OffscreenAlphaMode::clear_transparent());
}
}
pub fn view() -> View {
View::new("MirrorOf", 60, mirror_render).with_attach(mirror_attach)
}
use crate::draw::renderer::Renderer;