use super::*;
pub fn image_node(
attrs: &Attrs,
opacity: Option<f32>,
assets: &AssetServer,
promoted: bool,
) -> ImageNode {
use crate::elements::image::{SRC, TINT};
let base = match attrs.get(&SRC) {
Some(path) => ImageNode::new(assets.load(path.clone())),
None => ImageNode::solid_color(
attrs
.get(&TINT)
.map(|t| parse_color(t))
.unwrap_or(Color::WHITE),
),
};
finish_image_node(base, attrs, opacity, promoted)
}
pub fn svg_image_node(attrs: &Attrs, opacity: Option<f32>, promoted: bool) -> ImageNode {
let mut image = finish_image_node(ImageNode::default(), attrs, opacity, promoted);
image.image_mode = NodeImageMode::Stretch;
image.rect = None;
image
}
pub(crate) fn image_tint(
base: Color,
tint: Option<&str>,
opacity: Option<f32>,
promoted: bool,
) -> Color {
let color = tint.map(parse_color).unwrap_or(base);
if promoted {
color
} else {
apply_opacity(color, opacity)
}
}
fn finish_image_node(
mut image: ImageNode,
attrs: &Attrs,
opacity: Option<f32>,
promoted: bool,
) -> ImageNode {
use crate::elements::image::{FLIP_X, FLIP_Y, IMAGE_MODE, SOURCE_RECT, TINT, VISUAL_BOX};
image.color = image_tint(
image.color,
attrs.get(&TINT).map(String::as_str),
opacity,
promoted,
);
image.flip_x = attrs.get(&FLIP_X).copied().unwrap_or(false);
image.flip_y = attrs.get(&FLIP_Y).copied().unwrap_or(false);
if let Some(mode) = attrs.get(&IMAGE_MODE) {
image.image_mode = match mode {
ImageMode::Keyword(s) if s == "stretch" => NodeImageMode::Stretch,
ImageMode::Keyword(_) => NodeImageMode::Auto,
ImageMode::Spec(ImageModeSpec::Sliced(s)) => NodeImageMode::Sliced(slicer(s)),
ImageMode::Spec(ImageModeSpec::Tiled(t)) => NodeImageMode::Tiled {
tile_x: t.tile_x,
tile_y: t.tile_y,
stretch_value: t.stretch_value.unwrap_or(1.0),
},
};
}
if let Some(r) = attrs.get(&SOURCE_RECT) {
image.rect = Some(bevy::math::Rect::new(
r.x,
r.y,
r.x + r.width,
r.y + r.height,
));
}
if let Some(vb) = attrs.get(&VISUAL_BOX) {
image.visual_box = match vb.as_str() {
"content" => VisualBox::ContentBox,
"border" => VisualBox::BorderBox,
_ => VisualBox::PaddingBox,
};
}
image
}
#[derive(Resource, Default)]
pub struct AtlasLayoutCache(HashMap<AtlasKey, Handle<TextureAtlasLayout>>);
#[derive(PartialEq, Eq, Hash)]
struct AtlasKey {
tile_width: u32,
tile_height: u32,
columns: u32,
rows: u32,
padding: Option<[u32; 2]>,
offset: Option<[u32; 2]>,
}
impl AtlasKey {
fn of(a: &AtlasSpec) -> Self {
AtlasKey {
tile_width: a.tile_width,
tile_height: a.tile_height,
columns: a.columns,
rows: a.rows,
padding: a.padding,
offset: a.offset,
}
}
}
pub fn apply_atlas(
image: &mut ImageNode,
atlas: Option<&AtlasSpec>,
layouts: &mut Assets<TextureAtlasLayout>,
cache: &mut AtlasLayoutCache,
) {
let Some(a) = atlas else { return };
let handle = cache
.0
.entry(AtlasKey::of(a))
.or_insert_with(|| {
layouts.add(TextureAtlasLayout::from_grid(
UVec2::new(a.tile_width, a.tile_height),
a.columns,
a.rows,
a.padding.map(|[x, y]| UVec2::new(x, y)),
a.offset.map(|[x, y]| UVec2::new(x, y)),
))
})
.clone();
image.texture_atlas = Some(TextureAtlas {
layout: handle,
index: a.index,
});
}
fn slicer(spec: &SliceSpec) -> TextureSlicer {
let border = match spec.border {
SliceBorder::Zero => BorderRect::ZERO,
SliceBorder::Uniform(n) => BorderRect::all(n),
SliceBorder::Sides {
top,
right,
bottom,
left,
} => BorderRect {
min_inset: Vec2::new(left, top),
max_inset: Vec2::new(right, bottom),
},
};
TextureSlicer {
border,
center_scale_mode: slice_scale(&spec.center_scale_mode),
sides_scale_mode: slice_scale(&spec.sides_scale_mode),
max_corner_scale: spec.max_corner_scale.unwrap_or(1.0),
}
}
fn slice_scale(mode: &Option<SliceScale>) -> SliceScaleMode {
match mode {
Some(SliceScale::Tile { tile }) => SliceScaleMode::Tile {
stretch_value: *tile,
},
_ => SliceScaleMode::Stretch,
}
}