svg_img: Fix memory leak in SvgImg (#818)
This commit is contained in:
parent
547c9c1827
commit
dc776224f9
2 changed files with 51 additions and 23 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
px, App, AppContext, Entity, FocusHandle, Focusable, ParentElement as _, Render, Styled, Window,
|
px, App, AppContext, ElementId, Entity, FocusHandle, Focusable, ParentElement as _, Render,
|
||||||
|
Styled, Window,
|
||||||
};
|
};
|
||||||
use gpui_component::{dock::PanelControl, v_flex, SvgImg};
|
use gpui_component::{dock::PanelControl, v_flex, SvgImg};
|
||||||
|
|
||||||
|
|
@ -9,7 +10,6 @@ const GOOGLE_LOGO: &str = include_str!("./fixtures/google.svg");
|
||||||
|
|
||||||
pub struct ImageStory {
|
pub struct ImageStory {
|
||||||
focus_handle: gpui::FocusHandle,
|
focus_handle: gpui::FocusHandle,
|
||||||
google_logo: SvgImg,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Story for ImageStory {
|
impl super::Story for ImageStory {
|
||||||
|
|
@ -34,7 +34,6 @@ impl ImageStory {
|
||||||
pub fn new(_: &mut Window, cx: &mut App) -> Self {
|
pub fn new(_: &mut Window, cx: &mut App) -> Self {
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
google_logo: SvgImg::new().source(GOOGLE_LOGO.as_bytes(), px(300.), px(300.)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,11 +56,15 @@ impl Render for ImageStory {
|
||||||
) -> impl gpui::IntoElement {
|
) -> impl gpui::IntoElement {
|
||||||
v_flex().gap_4().size_full().child(
|
v_flex().gap_4().size_full().child(
|
||||||
section("SVG Image")
|
section("SVG Image")
|
||||||
.child(self.google_logo.clone().size(px(100.)).flex_grow())
|
.child(svg_img("logo1").size(px(100.)).flex_grow())
|
||||||
.child(self.google_logo.clone().size(px(100.)).flex_grow())
|
.child(svg_img("logo2").size(px(100.)).flex_grow())
|
||||||
.child(self.google_logo.clone().size_80().flex_grow())
|
.child(svg_img("logo3").size_80().flex_grow())
|
||||||
.child(self.google_logo.clone().size_12().flex_grow())
|
.child(svg_img("logo4").size_12().flex_grow())
|
||||||
.child(self.google_logo.clone().size(px(100.))),
|
.child(svg_img("logo5").size(px(100.))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn svg_img(id: impl Into<ElementId>) -> SvgImg {
|
||||||
|
SvgImg::new(id).source(GOOGLE_LOGO.as_bytes(), px(300.), px(300.))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
px, size, App, Asset, Bounds, Element, ElementId, GlobalElementId, Hitbox, ImageCacheError,
|
hash, px, size, App, Asset, Bounds, Element, ElementId, GlobalElementId, Hitbox,
|
||||||
InteractiveElement, Interactivity, IntoElement, IsZero, Pixels, RenderImage, SharedString,
|
ImageCacheError, InteractiveElement, Interactivity, IntoElement, IsZero, Pixels, RenderImage,
|
||||||
Size, StyleRefinement, Styled, Window,
|
SharedString, Size, StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
use image::Frame;
|
use image::Frame;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
@ -15,6 +15,9 @@ use smallvec::SmallVec;
|
||||||
use image::ImageBuffer;
|
use image::ImageBuffer;
|
||||||
|
|
||||||
const SCALE: f32 = 2.;
|
const SCALE: f32 = 2.;
|
||||||
|
|
||||||
|
struct SvgImgState(Option<(u64, Arc<RenderImage>)>);
|
||||||
|
|
||||||
static OPTIONS: LazyLock<usvg::Options> = LazyLock::new(|| {
|
static OPTIONS: LazyLock<usvg::Options> = LazyLock::new(|| {
|
||||||
let mut options = usvg::Options::default();
|
let mut options = usvg::Options::default();
|
||||||
options.fontdb_mut().load_system_fonts();
|
options.fontdb_mut().load_system_fonts();
|
||||||
|
|
@ -56,6 +59,7 @@ impl From<&'static str> for SvgSource {
|
||||||
impl Clone for SvgImg {
|
impl Clone for SvgImg {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id: self.id.clone(),
|
||||||
interactivity: Interactivity::default(),
|
interactivity: Interactivity::default(),
|
||||||
source: self.source.clone(),
|
source: self.source.clone(),
|
||||||
size: self.size,
|
size: self.size,
|
||||||
|
|
@ -146,6 +150,7 @@ impl Asset for Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SvgImg {
|
pub struct SvgImg {
|
||||||
|
id: ElementId,
|
||||||
interactivity: Interactivity,
|
interactivity: Interactivity,
|
||||||
source: Option<ImageSource>,
|
source: Option<ImageSource>,
|
||||||
size: Size<Pixels>,
|
size: Size<Pixels>,
|
||||||
|
|
@ -155,8 +160,9 @@ impl SvgImg {
|
||||||
/// Create a new svg image element.
|
/// Create a new svg image element.
|
||||||
///
|
///
|
||||||
/// The `src_width` and `src_height` are the original width and height of the svg image.
|
/// The `src_width` and `src_height` are the original width and height of the svg image.
|
||||||
pub fn new() -> Self {
|
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id: id.into(),
|
||||||
interactivity: Interactivity::default(),
|
interactivity: Interactivity::default(),
|
||||||
source: None,
|
source: None,
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
|
|
@ -200,7 +206,7 @@ impl Element for SvgImg {
|
||||||
type PrepaintState = (Option<Hitbox>, Option<Arc<RenderImage>>);
|
type PrepaintState = (Option<Hitbox>, Option<Arc<RenderImage>>);
|
||||||
|
|
||||||
fn id(&self) -> Option<ElementId> {
|
fn id(&self) -> Option<ElementId> {
|
||||||
self.interactivity.element_id.clone()
|
Some(self.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_layout(
|
fn request_layout(
|
||||||
|
|
@ -214,18 +220,37 @@ impl Element for SvgImg {
|
||||||
.request_layout(global_id, window, cx, |style, window, cx| {
|
.request_layout(global_id, window, cx, |style, window, cx| {
|
||||||
window.request_layout(style, None, cx)
|
window.request_layout(style, None, cx)
|
||||||
});
|
});
|
||||||
|
let global_id = global_id.unwrap();
|
||||||
|
|
||||||
let source = self.source.clone();
|
window.with_element_state::<SvgImgState, _>(global_id, |state, window| {
|
||||||
let data = if let Some(source) = source {
|
match (state, &self.source) {
|
||||||
match window.use_asset::<Image>(&source, cx) {
|
(_, None) => ((layout_id, None), SvgImgState(None)),
|
||||||
Some(Ok(data)) => Some(data),
|
(Some(SvgImgState(Some((prev_hash, image)))), Some(source))
|
||||||
_ => None,
|
if hash(source) == prev_hash =>
|
||||||
|
{
|
||||||
|
(
|
||||||
|
(layout_id, Some(image.clone())),
|
||||||
|
SvgImgState(Some((prev_hash, image))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(state, Some(source)) => {
|
||||||
|
if let Some(SvgImgState(Some((_, prev_image)))) = state {
|
||||||
|
// Drop the previous image from the cache
|
||||||
|
_ = window.drop_image(prev_image);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
(layout_id, data)
|
let image = window
|
||||||
|
.use_asset::<Image>(&source, cx)
|
||||||
|
.transpose()
|
||||||
|
.ok()
|
||||||
|
.flatten();
|
||||||
|
(
|
||||||
|
(layout_id, image.clone()),
|
||||||
|
SvgImgState(image.map(|image| (hash(source), image))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepaint(
|
fn prepaint(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue