mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
Animation: Aggregate Animated Image Info to Document (#36141)
Signed-off-by: rayguo17 <rayguo17@gmail.com>
This commit is contained in:
parent
53f7c7b1de
commit
ed3dd8fbe0
10 changed files with 145 additions and 9 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -6426,6 +6426,7 @@ dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"fonts",
|
"fonts",
|
||||||
"fonts_traits",
|
"fonts_traits",
|
||||||
|
"fxhash",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
|
@ -7,11 +7,13 @@ use std::sync::Arc;
|
||||||
use base::id::PipelineId;
|
use base::id::PipelineId;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fonts::FontContext;
|
use fonts::FontContext;
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use net_traits::image_cache::{
|
use net_traits::image_cache::{
|
||||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
||||||
};
|
};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use script_layout_interface::{IFrameSizes, PendingImage, PendingImageState};
|
use pixels::Image;
|
||||||
|
use script_layout_interface::{IFrameSizes, ImageAnimationState, PendingImage, PendingImageState};
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
|
@ -40,6 +42,8 @@ pub struct LayoutContext<'a> {
|
||||||
|
|
||||||
pub webrender_image_cache:
|
pub webrender_image_cache:
|
||||||
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
|
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
|
||||||
|
|
||||||
|
pub node_image_animation_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for LayoutContext<'_> {
|
impl Drop for LayoutContext<'_> {
|
||||||
|
@ -100,6 +104,26 @@ impl LayoutContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_animated_image(&self, node: OpaqueNode, image: Arc<Image>) {
|
||||||
|
let mut store = self.node_image_animation_map.write();
|
||||||
|
|
||||||
|
// 1. first check whether node previously being track for animated image.
|
||||||
|
if let Some(image_state) = store.get(&node) {
|
||||||
|
// a. if the node is not containing the same image as before.
|
||||||
|
if image_state.image_key() != image.id {
|
||||||
|
if image.should_animate() {
|
||||||
|
// i. Register/Replace tracking item in image_animation_manager.
|
||||||
|
store.insert(node, ImageAnimationState::new(image));
|
||||||
|
} else {
|
||||||
|
// ii. Cancel Action if the node's image is no longer animated.
|
||||||
|
store.remove(&node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if image.should_animate() {
|
||||||
|
store.insert(node, ImageAnimationState::new(image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_webrender_image_for_url(
|
pub fn get_webrender_image_for_url(
|
||||||
&self,
|
&self,
|
||||||
node: OpaqueNode,
|
node: OpaqueNode,
|
||||||
|
@ -116,6 +140,7 @@ impl LayoutContext<'_> {
|
||||||
|
|
||||||
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
|
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
|
||||||
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
|
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
|
||||||
|
self.handle_animated_image(node, image.clone());
|
||||||
let image_info = WebRenderImageInfo {
|
let image_info = WebRenderImageInfo {
|
||||||
width: image.width,
|
width: image.width,
|
||||||
height: image.height,
|
height: image.height,
|
||||||
|
|
|
@ -182,6 +182,10 @@ impl ReplacedContents {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let ReplacedContentKind::Image(Some(ref image)) = kind {
|
||||||
|
context.handle_animated_image(element.opaque(), image.clone());
|
||||||
|
}
|
||||||
|
|
||||||
let natural_size = if let Some(naturalc_size_in_dots) = natural_size_in_dots {
|
let natural_size = if let Some(naturalc_size_in_dots) = natural_size_in_dots {
|
||||||
// FIXME: should 'image-resolution' (when implemented) be used *instead* of
|
// FIXME: should 'image-resolution' (when implemented) be used *instead* of
|
||||||
// `script::dom::htmlimageelement::ImageRequest::current_pixel_density`?
|
// `script::dom::htmlimageelement::ImageRequest::current_pixel_density`?
|
||||||
|
|
|
@ -24,7 +24,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fonts::{FontContext, FontContextWebFontMethods};
|
use fonts::{FontContext, FontContextWebFontMethods};
|
||||||
use fonts_traits::StylesheetWebFontLoadFinishedCallback;
|
use fonts_traits::StylesheetWebFontLoadFinishedCallback;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list::{DisplayList, WebRenderImageInfo};
|
use layout::display_list::{DisplayList, WebRenderImageInfo};
|
||||||
|
@ -46,15 +46,15 @@ use profile_traits::time::{
|
||||||
use profile_traits::{path, time_profile};
|
use profile_traits::{path, time_profile};
|
||||||
use script::layout_dom::{ServoLayoutElement, ServoLayoutNode};
|
use script::layout_dom::{ServoLayoutElement, ServoLayoutNode};
|
||||||
use script_layout_interface::{
|
use script_layout_interface::{
|
||||||
Layout, LayoutConfig, LayoutFactory, NodesFromPointQueryType, OffsetParentResponse, ReflowGoal,
|
ImageAnimationState, Layout, LayoutConfig, LayoutFactory, NodesFromPointQueryType,
|
||||||
ReflowRequest, ReflowResult, TrustedNodeAddress,
|
OffsetParentResponse, ReflowGoal, ReflowRequest, ReflowResult, TrustedNodeAddress,
|
||||||
};
|
};
|
||||||
use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
|
use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
use servo_config::opts::{self, DebugOptions};
|
use servo_config::opts::{self, DebugOptions};
|
||||||
use servo_config::pref;
|
use servo_config::pref;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use style::animation::DocumentAnimationSet;
|
use style::animation::{AnimationSetKey, DocumentAnimationSet};
|
||||||
use style::context::{
|
use style::context::{
|
||||||
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
|
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
|
||||||
};
|
};
|
||||||
|
@ -536,7 +536,7 @@ impl LayoutThread {
|
||||||
&'a self,
|
&'a self,
|
||||||
guards: StylesheetGuards<'a>,
|
guards: StylesheetGuards<'a>,
|
||||||
snapshot_map: &'a SnapshotMap,
|
snapshot_map: &'a SnapshotMap,
|
||||||
reflow_request: &ReflowRequest,
|
reflow_request: &mut ReflowRequest,
|
||||||
use_rayon: bool,
|
use_rayon: bool,
|
||||||
) -> LayoutContext<'a> {
|
) -> LayoutContext<'a> {
|
||||||
let traversal_flags = match reflow_request.stylesheets_changed {
|
let traversal_flags = match reflow_request.stylesheets_changed {
|
||||||
|
@ -558,6 +558,9 @@ impl LayoutThread {
|
||||||
font_context: self.font_context.clone(),
|
font_context: self.font_context.clone(),
|
||||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||||
pending_images: Mutex::default(),
|
pending_images: Mutex::default(),
|
||||||
|
node_image_animation_map: Arc::new(RwLock::new(std::mem::take(
|
||||||
|
&mut reflow_request.node_to_image_animation_map,
|
||||||
|
))),
|
||||||
iframe_sizes: Mutex::default(),
|
iframe_sizes: Mutex::default(),
|
||||||
use_rayon,
|
use_rayon,
|
||||||
}
|
}
|
||||||
|
@ -696,8 +699,12 @@ impl LayoutThread {
|
||||||
let rayon_pool = rayon_pool.as_ref();
|
let rayon_pool = rayon_pool.as_ref();
|
||||||
|
|
||||||
// Create a layout context for use throughout the following passes.
|
// Create a layout context for use throughout the following passes.
|
||||||
let mut layout_context =
|
let mut layout_context = self.build_layout_context(
|
||||||
self.build_layout_context(guards.clone(), &map, &reflow_request, rayon_pool.is_some());
|
guards.clone(),
|
||||||
|
&map,
|
||||||
|
&mut reflow_request,
|
||||||
|
rayon_pool.is_some(),
|
||||||
|
);
|
||||||
|
|
||||||
let dirty_root = unsafe {
|
let dirty_root = unsafe {
|
||||||
ServoLayoutNode::new(&reflow_request.dirty_root.unwrap())
|
ServoLayoutNode::new(&reflow_request.dirty_root.unwrap())
|
||||||
|
@ -791,9 +798,12 @@ impl LayoutThread {
|
||||||
|
|
||||||
let pending_images = std::mem::take(&mut *layout_context.pending_images.lock());
|
let pending_images = std::mem::take(&mut *layout_context.pending_images.lock());
|
||||||
let iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock());
|
let iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock());
|
||||||
|
let node_to_image_animation_map =
|
||||||
|
std::mem::take(&mut *layout_context.node_image_animation_map.write());
|
||||||
Some(ReflowResult {
|
Some(ReflowResult {
|
||||||
pending_images,
|
pending_images,
|
||||||
iframe_sizes,
|
iframe_sizes,
|
||||||
|
node_to_image_animation_map,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,6 +831,11 @@ impl LayoutThread {
|
||||||
&fragment_tree,
|
&fragment_tree,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Self::cancel_image_animation_for_nodes_not_in_fragment_tree(
|
||||||
|
context.node_image_animation_map.clone(),
|
||||||
|
&fragment_tree,
|
||||||
|
);
|
||||||
|
|
||||||
if !reflow_goal.needs_display_list() {
|
if !reflow_goal.needs_display_list() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -920,6 +935,22 @@ impl LayoutThread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cancel_image_animation_for_nodes_not_in_fragment_tree(
|
||||||
|
image_animation_set: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
|
||||||
|
root: &FragmentTree,
|
||||||
|
) {
|
||||||
|
let mut image_animations = image_animation_set.write().to_owned();
|
||||||
|
let mut invalid_nodes: FxHashSet<AnimationSetKey> = image_animations
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.map(|node| AnimationSetKey::new(node, None))
|
||||||
|
.collect();
|
||||||
|
root.remove_nodes_in_fragment_tree_from_set(&mut invalid_nodes);
|
||||||
|
for node in &invalid_nodes {
|
||||||
|
image_animations.remove(&node.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn viewport_did_change(&mut self, window_size_data: WindowSizeData) -> bool {
|
fn viewport_did_change(&mut self, window_size_data: WindowSizeData) -> bool {
|
||||||
let new_pixel_ratio = window_size_data.device_pixel_ratio.get();
|
let new_pixel_ratio = window_size_data.device_pixel_ratio.get();
|
||||||
let new_viewport_size = Size2D::new(
|
let new_viewport_size = Size2D::new(
|
||||||
|
|
|
@ -199,6 +199,7 @@ use crate::dom::xpathevaluator::XPathEvaluator;
|
||||||
use crate::drag_data_store::{DragDataStore, Kind, Mode};
|
use crate::drag_data_store::{DragDataStore, Kind, Mode};
|
||||||
use crate::fetch::FetchCanceller;
|
use crate::fetch::FetchCanceller;
|
||||||
use crate::iframe_collection::IFrameCollection;
|
use crate::iframe_collection::IFrameCollection;
|
||||||
|
use crate::image_animation::ImageAnimationManager;
|
||||||
use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg};
|
use crate::messaging::{CommonScriptMsg, MainThreadScriptMsg};
|
||||||
use crate::network_listener::{NetworkListener, PreInvoke};
|
use crate::network_listener::{NetworkListener, PreInvoke};
|
||||||
use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
|
use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
|
||||||
|
@ -484,6 +485,8 @@ pub(crate) struct Document {
|
||||||
animation_timeline: DomRefCell<AnimationTimeline>,
|
animation_timeline: DomRefCell<AnimationTimeline>,
|
||||||
/// Animations for this Document
|
/// Animations for this Document
|
||||||
animations: DomRefCell<Animations>,
|
animations: DomRefCell<Animations>,
|
||||||
|
/// Image Animation Manager for this Document
|
||||||
|
image_animation_manager: DomRefCell<ImageAnimationManager>,
|
||||||
/// The nearest inclusive ancestors to all the nodes that require a restyle.
|
/// The nearest inclusive ancestors to all the nodes that require a restyle.
|
||||||
dirty_root: MutNullableDom<Element>,
|
dirty_root: MutNullableDom<Element>,
|
||||||
/// <https://html.spec.whatwg.org/multipage/#will-declaratively-refresh>
|
/// <https://html.spec.whatwg.org/multipage/#will-declaratively-refresh>
|
||||||
|
@ -3877,6 +3880,7 @@ impl Document {
|
||||||
DomRefCell::new(AnimationTimeline::new())
|
DomRefCell::new(AnimationTimeline::new())
|
||||||
},
|
},
|
||||||
animations: DomRefCell::new(Animations::new()),
|
animations: DomRefCell::new(Animations::new()),
|
||||||
|
image_animation_manager: DomRefCell::new(ImageAnimationManager::new()),
|
||||||
dirty_root: Default::default(),
|
dirty_root: Default::default(),
|
||||||
declarative_refresh: Default::default(),
|
declarative_refresh: Default::default(),
|
||||||
pending_animation_ticks: Default::default(),
|
pending_animation_ticks: Default::default(),
|
||||||
|
@ -4715,6 +4719,13 @@ impl Document {
|
||||||
self.animations().send_pending_events(self.window(), can_gc);
|
self.animations().send_pending_events(self.window(), can_gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn image_animation_manager(&self) -> Ref<ImageAnimationManager> {
|
||||||
|
self.image_animation_manager.borrow()
|
||||||
|
}
|
||||||
|
pub(crate) fn image_animation_manager_mut(&self) -> RefMut<ImageAnimationManager> {
|
||||||
|
self.image_animation_manager.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn will_declaratively_refresh(&self) -> bool {
|
pub(crate) fn will_declaratively_refresh(&self) -> bool {
|
||||||
self.declarative_refresh.borrow().is_some()
|
self.declarative_refresh.borrow().is_some()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1967,6 +1967,9 @@ impl Window {
|
||||||
pending_restyles,
|
pending_restyles,
|
||||||
animation_timeline_value: document.current_animation_timeline_value(),
|
animation_timeline_value: document.current_animation_timeline_value(),
|
||||||
animations: document.animations().sets.clone(),
|
animations: document.animations().sets.clone(),
|
||||||
|
node_to_image_animation_map: document
|
||||||
|
.image_animation_manager_mut()
|
||||||
|
.take_image_animate_set(),
|
||||||
theme: self.theme.get(),
|
theme: self.theme.get(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2017,7 +2020,9 @@ impl Window {
|
||||||
if !size_messages.is_empty() {
|
if !size_messages.is_empty() {
|
||||||
self.send_to_constellation(ScriptMsg::IFrameSizes(size_messages));
|
self.send_to_constellation(ScriptMsg::IFrameSizes(size_messages));
|
||||||
}
|
}
|
||||||
|
document
|
||||||
|
.image_animation_manager_mut()
|
||||||
|
.restore_image_animate_set(results.node_to_image_animation_map);
|
||||||
document.update_animations_post_reflow();
|
document.update_animations_post_reflow();
|
||||||
self.update_constellation_epoch();
|
self.update_constellation_epoch();
|
||||||
|
|
||||||
|
|
29
components/script/image_animation.rs
Normal file
29
components/script/image_animation.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
use script_layout_interface::ImageAnimationState;
|
||||||
|
use style::dom::OpaqueNode;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, JSTraceable, MallocSizeOf)]
|
||||||
|
pub struct ImageAnimationManager {
|
||||||
|
#[no_trace]
|
||||||
|
pub node_to_image_map: FxHashMap<OpaqueNode, ImageAnimationState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageAnimationManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ImageAnimationManager {
|
||||||
|
node_to_image_map: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_image_animate_set(&mut self) -> FxHashMap<OpaqueNode, ImageAnimationState> {
|
||||||
|
std::mem::take(&mut self.node_to_image_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_image_animate_set(&mut self, map: FxHashMap<OpaqueNode, ImageAnimationState>) {
|
||||||
|
let _ = std::mem::replace(&mut self.node_to_image_map, map);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ mod layout_image;
|
||||||
|
|
||||||
pub(crate) mod document_collection;
|
pub(crate) mod document_collection;
|
||||||
pub(crate) mod iframe_collection;
|
pub(crate) mod iframe_collection;
|
||||||
|
pub(crate) mod image_animation;
|
||||||
pub mod layout_dom;
|
pub mod layout_dom;
|
||||||
mod mem;
|
mod mem;
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
|
|
@ -21,6 +21,7 @@ euclid = { workspace = true }
|
||||||
fnv = { workspace = true }
|
fnv = { workspace = true }
|
||||||
fonts = { path = "../../fonts" }
|
fonts = { path = "../../fonts" }
|
||||||
fonts_traits = { workspace = true }
|
fonts_traits = { workspace = true }
|
||||||
|
fxhash = { workspace = true }
|
||||||
html5ever = { workspace = true }
|
html5ever = { workspace = true }
|
||||||
ipc-channel = { workspace = true }
|
ipc-channel = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
|
@ -24,10 +24,12 @@ use euclid::Size2D;
|
||||||
use euclid::default::{Point2D, Rect};
|
use euclid::default::{Point2D, Rect};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fonts::{FontContext, SystemFontServiceProxy};
|
use fonts::{FontContext, SystemFontServiceProxy};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use net_traits::image_cache::{ImageCache, PendingImageId};
|
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||||
|
use pixels::Image;
|
||||||
use profile_traits::mem::Report;
|
use profile_traits::mem::Report;
|
||||||
use profile_traits::time;
|
use profile_traits::time;
|
||||||
use script_traits::{InitialScriptState, LoadData, Painter, ScriptThreadMessage};
|
use script_traits::{InitialScriptState, LoadData, Painter, ScriptThreadMessage};
|
||||||
|
@ -400,6 +402,8 @@ pub struct ReflowResult {
|
||||||
/// to communicate them with the Constellation and also the `Window`
|
/// to communicate them with the Constellation and also the `Window`
|
||||||
/// element of their content pages.
|
/// element of their content pages.
|
||||||
pub iframe_sizes: IFrameSizes,
|
pub iframe_sizes: IFrameSizes,
|
||||||
|
/// The mapping of node to animated image, need to be returned to ImageAnimationManager
|
||||||
|
pub node_to_image_animation_map: FxHashMap<OpaqueNode, ImageAnimationState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information needed for a script-initiated reflow.
|
/// Information needed for a script-initiated reflow.
|
||||||
|
@ -427,6 +431,8 @@ pub struct ReflowRequest {
|
||||||
pub animation_timeline_value: f64,
|
pub animation_timeline_value: f64,
|
||||||
/// The set of animations for this document.
|
/// The set of animations for this document.
|
||||||
pub animations: DocumentAnimationSet,
|
pub animations: DocumentAnimationSet,
|
||||||
|
/// The set of image animations.
|
||||||
|
pub node_to_image_animation_map: FxHashMap<OpaqueNode, ImageAnimationState>,
|
||||||
/// The theme for the window
|
/// The theme for the window
|
||||||
pub theme: PrefersColorScheme,
|
pub theme: PrefersColorScheme,
|
||||||
}
|
}
|
||||||
|
@ -501,3 +507,25 @@ pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
|
pub struct ImageAnimationState {
|
||||||
|
#[ignore_malloc_size_of = "Arc is hard"]
|
||||||
|
image: Arc<Image>,
|
||||||
|
active_frame: usize,
|
||||||
|
last_update_time: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageAnimationState {
|
||||||
|
pub fn new(image: Arc<Image>) -> Self {
|
||||||
|
Self {
|
||||||
|
image,
|
||||||
|
active_frame: 0,
|
||||||
|
last_update_time: 0.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn image_key(&self) -> Option<ImageKey> {
|
||||||
|
self.image.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue