use bincode;
use euclid::SideOffsets2D;
#[cfg(feature = "deserialize")]
use serde::de::Deserializer;
#[cfg(feature = "serialize")]
use serde::ser::{Serializer, SerializeSeq};
use serde::{Deserialize, Serialize};
use std::io::{Read, stdout, Write};
use std::marker::PhantomData;
use std::ops::Range;
use std::{io, mem, ptr, slice};
use time::precise_time_ns;
use display_item as di;
use api::{PipelineId, PropertyBinding};
use gradient_builder::GradientBuilder;
use color::ColorF;
use font::{FontInstanceKey, GlyphInstance, GlyphOptions};
use image::{ColorDepth, ImageKey};
use units::*;
pub const MAX_TEXT_RUN_LENGTH: usize = 2040;
const FIRST_SPATIAL_NODE_INDEX: usize = 2;
const FIRST_CLIP_NODE_INDEX: usize = 1;
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ItemRange<T> {
start: usize,
length: usize,
_boo: PhantomData<T>,
}
impl<T> Default for ItemRange<T> {
fn default() -> Self {
ItemRange {
start: 0,
length: 0,
_boo: PhantomData,
}
}
}
impl<T> ItemRange<T> {
pub fn is_empty(&self) -> bool {
self.length <= mem::size_of::<u64>()
}
}
#[derive(Clone, Default)]
pub struct BuiltDisplayList {
data: Vec<u8>,
descriptor: BuiltDisplayListDescriptor,
}
#[repr(C)]
#[derive(Copy, Clone, Default, Deserialize, Serialize)]
pub struct BuiltDisplayListDescriptor {
builder_start_time: u64,
builder_finish_time: u64,
send_start_time: u64,
total_clip_nodes: usize,
total_spatial_nodes: usize,
}
pub struct BuiltDisplayListIter<'a> {
list: &'a BuiltDisplayList,
data: &'a [u8],
cur_item: di::DisplayItem,
cur_stops: ItemRange<di::GradientStop>,
cur_glyphs: ItemRange<GlyphInstance>,
cur_filters: ItemRange<di::FilterOp>,
cur_clip_chain_items: ItemRange<di::ClipId>,
cur_complex_clip: (ItemRange<di::ComplexClipRegion>, usize),
peeking: Peek,
}
pub struct DisplayItemRef<'a: 'b, 'b> {
iter: &'b BuiltDisplayListIter<'a>,
}
#[derive(PartialEq)]
enum Peek {
StartPeeking,
IsPeeking,
NotPeeking,
}
#[derive(Clone)]
pub struct AuxIter<'a, T> {
data: &'a [u8],
size: usize,
_boo: PhantomData<T>,
}
impl BuiltDisplayListDescriptor {}
impl BuiltDisplayList {
pub fn from_data(data: Vec<u8>, descriptor: BuiltDisplayListDescriptor) -> Self {
BuiltDisplayList { data, descriptor }
}
pub fn into_data(mut self) -> (Vec<u8>, BuiltDisplayListDescriptor) {
self.descriptor.send_start_time = precise_time_ns();
(self.data, self.descriptor)
}
pub fn data(&self) -> &[u8] {
&self.data[..]
}
pub fn item_slice(&self) -> &[u8] {
&self.data[..]
}
pub fn descriptor(&self) -> &BuiltDisplayListDescriptor {
&self.descriptor
}
pub fn times(&self) -> (u64, u64, u64) {
(
self.descriptor.builder_start_time,
self.descriptor.builder_finish_time,
self.descriptor.send_start_time,
)
}
pub fn total_clip_nodes(&self) -> usize {
self.descriptor.total_clip_nodes
}
pub fn total_spatial_nodes(&self) -> usize {
self.descriptor.total_spatial_nodes
}
pub fn iter(&self) -> BuiltDisplayListIter {
BuiltDisplayListIter::new(self)
}
pub fn get<'de, T: Deserialize<'de>>(&self, range: ItemRange<T>) -> AuxIter<T> {
AuxIter::new(&self.data[range.start .. range.start + range.length])
}
}
fn skip_slice<T: for<'de> Deserialize<'de>>(
list: &BuiltDisplayList,
mut data: &mut &[u8],
) -> (ItemRange<T>, usize) {
let base = list.data.as_ptr() as usize;
let byte_size: usize = bincode::deserialize_from(&mut data)
.expect("MEH: malicious input?");
let start = data.as_ptr() as usize;
let item_count: usize = bincode::deserialize_from(&mut data)
.expect("MEH: malicious input?");
let item_count_size = data.as_ptr() as usize - start;
let range = ItemRange {
start: start - base,
length: byte_size + item_count_size,
_boo: PhantomData,
};
*data = &data[byte_size ..];
(range, item_count)
}
impl<'a> BuiltDisplayListIter<'a> {
pub fn new(list: &'a BuiltDisplayList) -> Self {
Self::new_with_list_and_data(list, list.item_slice())
}
pub fn new_with_list_and_data(list: &'a BuiltDisplayList, data: &'a [u8]) -> Self {
BuiltDisplayListIter {
list,
data,
cur_item: di::DisplayItem {
item: di::SpecificDisplayItem::PopStackingContext,
layout: di::LayoutPrimitiveInfo::new(LayoutRect::zero()),
space_and_clip: di::SpaceAndClipInfo::root_scroll(PipelineId::dummy())
},
cur_stops: ItemRange::default(),
cur_glyphs: ItemRange::default(),
cur_filters: ItemRange::default(),
cur_clip_chain_items: ItemRange::default(),
cur_complex_clip: (ItemRange::default(), 0),
peeking: Peek::NotPeeking,
}
}
pub fn display_list(&self) -> &'a BuiltDisplayList {
self.list
}
pub fn next<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
use SpecificDisplayItem::*;
match self.peeking {
Peek::IsPeeking => {
self.peeking = Peek::NotPeeking;
return Some(self.as_ref());
}
Peek::StartPeeking => {
self.peeking = Peek::IsPeeking;
}
Peek::NotPeeking => { }
}
self.cur_stops = ItemRange::default();
self.cur_complex_clip = (ItemRange::default(), 0);
self.cur_clip_chain_items = ItemRange::default();
loop {
self.next_raw()?;
if let SetGradientStops = self.cur_item.item {
continue;
}
break;
}
Some(self.as_ref())
}
pub fn next_raw<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
use SpecificDisplayItem::*;
if self.data.is_empty() {
return None;
}
{
let reader = bincode::IoReader::new(UnsafeReader::new(&mut self.data));
bincode::deserialize_in_place(reader, &mut self.cur_item)
.expect("MEH: malicious process?");
}
match self.cur_item.item {
SetGradientStops => {
self.cur_stops = skip_slice::<di::GradientStop>(self.list, &mut self.data).0;
}
ClipChain(_) => {
self.cur_clip_chain_items = skip_slice::<di::ClipId>(self.list, &mut self.data).0;
}
Clip(_) | ScrollFrame(_) => {
self.cur_complex_clip = self.skip_slice::<di::ComplexClipRegion>()
}
Text(_) => self.cur_glyphs = self.skip_slice::<GlyphInstance>().0,
PushStackingContext(_) => self.cur_filters = self.skip_slice::<di::FilterOp>().0,
_ => { }
}
Some(self.as_ref())
}
fn skip_slice<T: for<'de> Deserialize<'de>>(&mut self) -> (ItemRange<T>, usize) {
skip_slice::<T>(self.list, &mut self.data)
}
pub fn as_ref<'b>(&'b self) -> DisplayItemRef<'a, 'b> {
DisplayItemRef { iter: self }
}
pub fn starting_stacking_context(
&mut self,
) -> Option<(di::StackingContext, LayoutRect, ItemRange<di::FilterOp>)> {
self.next().and_then(|item| match *item.item() {
di::SpecificDisplayItem::PushStackingContext(ref specific_item) => Some((
specific_item.stacking_context,
item.rect(),
item.filters(),
)),
_ => None,
})
}
pub fn skip_current_stacking_context(&mut self) {
let mut depth = 0;
while let Some(item) = self.next() {
match *item.item() {
di::SpecificDisplayItem::PushStackingContext(..) => depth += 1,
di::SpecificDisplayItem::PopStackingContext if depth == 0 => return,
di::SpecificDisplayItem::PopStackingContext => depth -= 1,
_ => {}
}
debug_assert!(depth >= 0);
}
}
pub fn current_stacking_context_empty(&mut self) -> bool {
match self.peek() {
Some(item) => *item.item() == di::SpecificDisplayItem::PopStackingContext,
None => true,
}
}
pub fn peek<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
if self.peeking == Peek::NotPeeking {
self.peeking = Peek::StartPeeking;
self.next()
} else {
Some(self.as_ref())
}
}
}
impl<'a, 'b> DisplayItemRef<'a, 'b> {
pub fn display_item(&self) -> &di::DisplayItem {
&self.iter.cur_item
}
pub fn rect(&self) -> LayoutRect {
self.iter.cur_item.layout.rect
}
pub fn get_layout_primitive_info(&self, offset: &LayoutVector2D) -> di::LayoutPrimitiveInfo {
let layout = self.iter.cur_item.layout;
di::LayoutPrimitiveInfo {
rect: layout.rect.translate(offset),
clip_rect: layout.clip_rect.translate(offset),
is_backface_visible: layout.is_backface_visible,
tag: layout.tag,
}
}
pub fn clip_rect(&self) -> &LayoutRect {
&self.iter.cur_item.layout.clip_rect
}
pub fn space_and_clip_info(&self) -> &di::SpaceAndClipInfo {
&self.iter.cur_item.space_and_clip
}
pub fn item(&self) -> &di::SpecificDisplayItem {
&self.iter.cur_item.item
}
pub fn complex_clip(&self) -> (ItemRange<di::ComplexClipRegion>, usize) {
self.iter.cur_complex_clip
}
pub fn gradient_stops(&self) -> ItemRange<di::GradientStop> {
self.iter.cur_stops
}
pub fn glyphs(&self) -> ItemRange<GlyphInstance> {
self.iter.cur_glyphs
}
pub fn filters(&self) -> ItemRange<di::FilterOp> {
self.iter.cur_filters
}
pub fn clip_chain_items(&self) -> ItemRange<di::ClipId> {
self.iter.cur_clip_chain_items
}
pub fn display_list(&self) -> &BuiltDisplayList {
self.iter.display_list()
}
pub fn is_backface_visible(&self) -> bool {
self.iter.cur_item.layout.is_backface_visible
}
pub fn sub_iter(&self) -> BuiltDisplayListIter<'a> {
BuiltDisplayListIter::new_with_list_and_data(self.iter.list, self.iter.data)
}
}
impl<'de, 'a, T: Deserialize<'de>> AuxIter<'a, T> {
pub fn new(mut data: &'a [u8]) -> Self {
let size: usize = if data.is_empty() {
0
} else {
bincode::deserialize_from(&mut UnsafeReader::new(&mut data)).expect("MEH: malicious input?")
};
AuxIter {
data,
size,
_boo: PhantomData,
}
}
}
impl<'a, T: for<'de> Deserialize<'de>> Iterator for AuxIter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.size == 0 {
None
} else {
self.size -= 1;
Some(
bincode::deserialize_from(&mut UnsafeReader::new(&mut self.data))
.expect("MEH: malicious input?"),
)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.size, Some(self.size))
}
}
impl<'a, T: for<'de> Deserialize<'de>> ::std::iter::ExactSizeIterator for AuxIter<'a, T> {}
#[cfg(feature = "serialize")]
impl Serialize for BuiltDisplayList {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use display_item::CompletelySpecificDisplayItem::*;
use display_item::GenericDisplayItem;
let mut seq = serializer.serialize_seq(None)?;
let mut traversal = self.iter();
while let Some(item) = traversal.next_raw() {
let display_item = item.display_item();
let serial_di = GenericDisplayItem {
item: match display_item.item {
di::SpecificDisplayItem::Clip(v) => Clip(
v,
item.iter.list.get(item.iter.cur_complex_clip.0).collect()
),
di::SpecificDisplayItem::ClipChain(v) => ClipChain(
v,
item.iter.list.get(item.iter.cur_clip_chain_items).collect(),
),
di::SpecificDisplayItem::ScrollFrame(v) => ScrollFrame(
v,
item.iter.list.get(item.iter.cur_complex_clip.0).collect()
),
di::SpecificDisplayItem::StickyFrame(v) => StickyFrame(v),
di::SpecificDisplayItem::Rectangle(v) => Rectangle(v),
di::SpecificDisplayItem::ClearRectangle => ClearRectangle,
di::SpecificDisplayItem::Line(v) => Line(v),
di::SpecificDisplayItem::Text(v) => Text(
v,
item.iter.list.get(item.iter.cur_glyphs).collect()
),
di::SpecificDisplayItem::Image(v) => Image(v),
di::SpecificDisplayItem::YuvImage(v) => YuvImage(v),
di::SpecificDisplayItem::Border(v) => Border(v),
di::SpecificDisplayItem::BoxShadow(v) => BoxShadow(v),
di::SpecificDisplayItem::Gradient(v) => Gradient(v),
di::SpecificDisplayItem::RadialGradient(v) => RadialGradient(v),
di::SpecificDisplayItem::Iframe(v) => Iframe(v),
di::SpecificDisplayItem::PushReferenceFrame(v) => PushReferenceFrame(v),
di::SpecificDisplayItem::PopReferenceFrame => PopReferenceFrame,
di::SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(
v,
item.iter.list.get(item.iter.cur_filters).collect()
),
di::SpecificDisplayItem::PopStackingContext => PopStackingContext,
di::SpecificDisplayItem::SetGradientStops => SetGradientStops(
item.iter.list.get(item.iter.cur_stops).collect()
),
di::SpecificDisplayItem::PushShadow(v) => PushShadow(v),
di::SpecificDisplayItem::PopAllShadows => PopAllShadows,
di::SpecificDisplayItem::PushCacheMarker(m) => PushCacheMarker(m),
di::SpecificDisplayItem::PopCacheMarker => PopCacheMarker,
},
layout: display_item.layout,
space_and_clip: display_item.space_and_clip,
};
seq.serialize_element(&serial_di)?
}
seq.end()
}
}
#[cfg(feature = "deserialize")]
impl<'de> Deserialize<'de> for BuiltDisplayList {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use display_item::CompletelySpecificDisplayItem::*;
use display_item::{CompletelySpecificDisplayItem, GenericDisplayItem};
let list = Vec::<GenericDisplayItem<CompletelySpecificDisplayItem>>
::deserialize(deserializer)?;
let mut data = Vec::new();
let mut temp = Vec::new();
let mut total_clip_nodes = FIRST_CLIP_NODE_INDEX;
let mut total_spatial_nodes = FIRST_SPATIAL_NODE_INDEX;
for complete in list {
let item = di::DisplayItem {
item: match complete.item {
Clip(specific_item, complex_clips) => {
total_clip_nodes += 1;
DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
di::SpecificDisplayItem::Clip(specific_item)
},
ClipChain(specific_item, clip_chain_ids) => {
DisplayListBuilder::push_iter_impl(&mut temp, clip_chain_ids);
di::SpecificDisplayItem::ClipChain(specific_item)
}
ScrollFrame(specific_item, complex_clips) => {
total_spatial_nodes += 1;
total_clip_nodes += 1;
DisplayListBuilder::push_iter_impl(&mut temp, complex_clips);
di::SpecificDisplayItem::ScrollFrame(specific_item)
}
StickyFrame(specific_item) => {
total_spatial_nodes += 1;
di::SpecificDisplayItem::StickyFrame(specific_item)
}
Rectangle(specific_item) => di::SpecificDisplayItem::Rectangle(specific_item),
ClearRectangle => di::SpecificDisplayItem::ClearRectangle,
Line(specific_item) => di::SpecificDisplayItem::Line(specific_item),
Text(specific_item, glyphs) => {
DisplayListBuilder::push_iter_impl(&mut temp, glyphs);
di::SpecificDisplayItem::Text(specific_item)
},
Image(specific_item) => di::SpecificDisplayItem::Image(specific_item),
YuvImage(specific_item) => di::SpecificDisplayItem::YuvImage(specific_item),
Border(specific_item) => di::SpecificDisplayItem::Border(specific_item),
BoxShadow(specific_item) => di::SpecificDisplayItem::BoxShadow(specific_item),
Gradient(specific_item) => di::SpecificDisplayItem::Gradient(specific_item),
RadialGradient(specific_item) =>
di::SpecificDisplayItem::RadialGradient(specific_item),
Iframe(specific_item) => {
total_clip_nodes += 1;
di::SpecificDisplayItem::Iframe(specific_item)
}
PushReferenceFrame(v) => {
total_spatial_nodes += 1;
di::SpecificDisplayItem::PushReferenceFrame(v)
}
PopReferenceFrame => di::SpecificDisplayItem::PopReferenceFrame,
PushStackingContext(specific_item, filters) => {
DisplayListBuilder::push_iter_impl(&mut temp, filters);
di::SpecificDisplayItem::PushStackingContext(specific_item)
},
PopStackingContext => di::SpecificDisplayItem::PopStackingContext,
SetGradientStops(stops) => {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
di::SpecificDisplayItem::SetGradientStops
},
PushShadow(specific_item) => di::SpecificDisplayItem::PushShadow(specific_item),
PopAllShadows => di::SpecificDisplayItem::PopAllShadows,
PushCacheMarker(marker) => di::SpecificDisplayItem::PushCacheMarker(marker),
PopCacheMarker => di::SpecificDisplayItem::PopCacheMarker,
},
layout: complete.layout,
space_and_clip: complete.space_and_clip,
};
serialize_fast(&mut data, &item);
data.extend(temp.drain(..));
}
Ok(BuiltDisplayList {
data,
descriptor: BuiltDisplayListDescriptor {
builder_start_time: 0,
builder_finish_time: 1,
send_start_time: 0,
total_clip_nodes,
total_spatial_nodes,
},
})
}
}
struct UnsafeVecWriter(*mut u8);
impl Write for UnsafeVecWriter {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len());
self.0 = self.0.add(buf.len());
}
Ok(buf.len())
}
#[inline(always)]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len());
self.0 = self.0.add(buf.len());
}
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
struct SizeCounter(usize);
impl<'a> Write for SizeCounter {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0 += buf.len();
Ok(buf.len())
}
#[inline(always)]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0 += buf.len();
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: T) {
let mut size = SizeCounter(0);
bincode::serialize_into(&mut size, &e).unwrap();
vec.reserve(size.0);
let old_len = vec.len();
let ptr = unsafe { vec.as_mut_ptr().add(old_len) };
let mut w = UnsafeVecWriter(ptr);
bincode::serialize_into(&mut w, &e).unwrap();
unsafe { vec.set_len(old_len + size.0); }
debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
}
fn serialize_iter_fast<I>(vec: &mut Vec<u8>, iter: I) -> usize
where I: ExactSizeIterator + Clone,
I::Item: Serialize,
{
let mut size = SizeCounter(0);
let mut count1 = 0;
for e in iter.clone() {
bincode::serialize_into(&mut size, &e).unwrap();
count1 += 1;
}
vec.reserve(size.0);
let old_len = vec.len();
let ptr = unsafe { vec.as_mut_ptr().add(old_len) };
let mut w = UnsafeVecWriter(ptr);
let mut count2 = 0;
for e in iter {
bincode::serialize_into(&mut w, &e).unwrap();
count2 += 1;
}
unsafe { vec.set_len(old_len + size.0); }
debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
debug_assert_eq!(count1, count2);
count1
}
struct UnsafeReader<'a: 'b, 'b> {
start: *const u8,
end: *const u8,
slice: &'b mut &'a [u8],
}
impl<'a, 'b> UnsafeReader<'a, 'b> {
#[inline(always)]
fn new(buf: &'b mut &'a [u8]) -> UnsafeReader<'a, 'b> {
unsafe {
let end = buf.as_ptr().add(buf.len());
let start = buf.as_ptr();
UnsafeReader { start, end, slice: buf }
}
}
#[inline(always)]
fn read_internal(&mut self, buf: &mut [u8]) {
unsafe {
assert!(self.start.add(buf.len()) <= self.end, "UnsafeReader: read past end of target");
ptr::copy_nonoverlapping(self.start, buf.as_mut_ptr(), buf.len());
self.start = self.start.add(buf.len());
}
}
}
impl<'a, 'b> Drop for UnsafeReader<'a, 'b> {
#[inline(always)]
fn drop(&mut self) {
unsafe {
*self.slice = slice::from_raw_parts(self.start, (self.end as usize) - (self.start as usize));
}
}
}
impl<'a, 'b> Read for UnsafeReader<'a, 'b> {
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read_internal(buf);
Ok(buf.len())
}
#[inline(always)]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.read_internal(buf);
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct SaveState {
dl_len: usize,
next_clip_index: usize,
next_spatial_index: usize,
next_clip_chain_id: u64,
}
#[derive(Clone)]
pub struct DisplayListBuilder {
pub data: Vec<u8>,
pub pipeline_id: PipelineId,
next_clip_index: usize,
next_spatial_index: usize,
next_clip_chain_id: u64,
builder_start_time: u64,
content_size: LayoutSize,
save_state: Option<SaveState>,
}
impl DisplayListBuilder {
pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> Self {
Self::with_capacity(pipeline_id, content_size, 0)
}
pub fn with_capacity(
pipeline_id: PipelineId,
content_size: LayoutSize,
capacity: usize,
) -> Self {
let start_time = precise_time_ns();
DisplayListBuilder {
data: Vec::with_capacity(capacity),
pipeline_id,
next_clip_index: FIRST_CLIP_NODE_INDEX,
next_spatial_index: FIRST_SPATIAL_NODE_INDEX,
next_clip_chain_id: 0,
builder_start_time: start_time,
content_size,
save_state: None,
}
}
pub fn content_size(&self) -> LayoutSize {
self.content_size
}
pub fn save(&mut self) {
assert!(self.save_state.is_none(), "DisplayListBuilder doesn't support nested saves");
self.save_state = Some(SaveState {
dl_len: self.data.len(),
next_clip_index: self.next_clip_index,
next_spatial_index: self.next_spatial_index,
next_clip_chain_id: self.next_clip_chain_id,
});
}
pub fn restore(&mut self) {
let state = self.save_state.take().expect("No save to restore DisplayListBuilder from");
self.data.truncate(state.dl_len);
self.next_clip_index = state.next_clip_index;
self.next_spatial_index = state.next_spatial_index;
self.next_clip_chain_id = state.next_clip_chain_id;
}
pub fn clear_save(&mut self) {
self.save_state.take().expect("No save to clear in DisplayListBuilder");
}
pub fn print_display_list(&mut self) {
self.emit_display_list(0, Range { start: None, end: None }, stdout());
}
pub fn emit_display_list<W>(
&mut self,
indent: usize,
range: Range<Option<usize>>,
mut sink: W,
) -> usize
where
W: Write
{
let mut temp = BuiltDisplayList::default();
mem::swap(&mut temp.data, &mut self.data);
let mut index: usize = 0;
{
let mut iter = BuiltDisplayListIter::new(&temp);
while let Some(item) = iter.next_raw() {
if index >= range.start.unwrap_or(0) && range.end.map_or(true, |e| index < e) {
writeln!(sink, "{}{:?}", " ".repeat(indent), item.display_item()).unwrap();
}
index += 1;
}
}
self.data = temp.data;
index
}
#[inline]
pub fn push_item(
&mut self,
item: &di::SpecificDisplayItem,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
) {
serialize_fast(
&mut self.data,
di::SerializedDisplayItem {
item,
layout,
space_and_clip,
},
)
}
#[inline]
fn push_new_empty_item(&mut self, item: &di::SpecificDisplayItem) {
let pipeline_id = self.pipeline_id;
self.push_item(
item,
&di::LayoutPrimitiveInfo::new(LayoutRect::zero()),
&di::SpaceAndClipInfo::root_scroll(pipeline_id),
)
}
fn push_iter_impl<I>(data: &mut Vec<u8>, iter_source: I)
where
I: IntoIterator,
I::IntoIter: ExactSizeIterator + Clone,
I::Item: Serialize,
{
let iter = iter_source.into_iter();
let len = iter.len();
let byte_size_offset = data.len();
serialize_fast(data, &0usize);
serialize_fast(data, &len);
let payload_offset = data.len();
let count = serialize_iter_fast(data, iter);
let final_offset = data.len();
let byte_size = final_offset - payload_offset;
bincode::serialize_into(
&mut &mut data[byte_size_offset..],
&byte_size,
).unwrap();
debug_assert_eq!(len, count);
}
pub fn push_iter<I>(&mut self, iter: I)
where
I: IntoIterator,
I::IntoIter: ExactSizeIterator + Clone,
I::Item: Serialize,
{
Self::push_iter_impl(&mut self.data, iter);
}
pub fn push_rect(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
color: ColorF,
) {
let item = di::SpecificDisplayItem::Rectangle(di::RectangleDisplayItem { color });
self.push_item(&item, layout, space_and_clip);
}
pub fn push_clear_rect(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
) {
self.push_item(&di::SpecificDisplayItem::ClearRectangle, layout, space_and_clip);
}
pub fn push_line(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
wavy_line_thickness: f32,
orientation: di::LineOrientation,
color: &ColorF,
style: di::LineStyle,
) {
let item = di::SpecificDisplayItem::Line(di::LineDisplayItem {
wavy_line_thickness,
orientation,
color: *color,
style,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_image(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
stretch_size: LayoutSize,
tile_spacing: LayoutSize,
image_rendering: di::ImageRendering,
alpha_type: di::AlphaType,
key: ImageKey,
color: ColorF,
) {
let item = di::SpecificDisplayItem::Image(di::ImageDisplayItem {
image_key: key,
stretch_size,
tile_spacing,
image_rendering,
alpha_type,
color,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_yuv_image(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
yuv_data: di::YuvData,
color_depth: ColorDepth,
color_space: di::YuvColorSpace,
image_rendering: di::ImageRendering,
) {
let item = di::SpecificDisplayItem::YuvImage(di::YuvImageDisplayItem {
yuv_data,
color_depth,
color_space,
image_rendering,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_text(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
glyphs: &[GlyphInstance],
font_key: FontInstanceKey,
color: ColorF,
glyph_options: Option<GlyphOptions>,
) {
let item = di::SpecificDisplayItem::Text(di::TextDisplayItem {
color,
font_key,
glyph_options,
});
for split_glyphs in glyphs.chunks(MAX_TEXT_RUN_LENGTH) {
self.push_item(&item, layout, space_and_clip);
self.push_iter(split_glyphs);
}
}
pub fn create_gradient(
&mut self,
start_point: LayoutPoint,
end_point: LayoutPoint,
stops: Vec<di::GradientStop>,
extend_mode: di::ExtendMode,
) -> di::Gradient {
let mut builder = GradientBuilder::with_stops(stops);
let gradient = builder.gradient(start_point, end_point, extend_mode);
self.push_stops(builder.stops());
gradient
}
pub fn create_radial_gradient(
&mut self,
center: LayoutPoint,
radius: LayoutSize,
stops: Vec<di::GradientStop>,
extend_mode: di::ExtendMode,
) -> di::RadialGradient {
let mut builder = GradientBuilder::with_stops(stops);
let gradient = builder.radial_gradient(center, radius, extend_mode);
self.push_stops(builder.stops());
gradient
}
pub fn push_border(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
widths: LayoutSideOffsets,
details: di::BorderDetails,
) {
let item = di::SpecificDisplayItem::Border(di::BorderDisplayItem { details, widths });
self.push_item(&item, layout, space_and_clip);
}
pub fn push_box_shadow(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
box_bounds: LayoutRect,
offset: LayoutVector2D,
color: ColorF,
blur_radius: f32,
spread_radius: f32,
border_radius: di::BorderRadius,
clip_mode: di::BoxShadowClipMode,
) {
let item = di::SpecificDisplayItem::BoxShadow(di::BoxShadowDisplayItem {
box_bounds,
offset,
color,
blur_radius,
spread_radius,
border_radius,
clip_mode,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_gradient(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
gradient: di::Gradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
) {
let item = di::SpecificDisplayItem::Gradient(di::GradientDisplayItem {
gradient,
tile_size,
tile_spacing,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_radial_gradient(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
gradient: di::RadialGradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
) {
let item = di::SpecificDisplayItem::RadialGradient(di::RadialGradientDisplayItem {
gradient,
tile_size,
tile_spacing,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_reference_frame(
&mut self,
rect: &LayoutRect,
parent: di::SpatialId,
transform_style: di::TransformStyle,
transform: PropertyBinding<LayoutTransform>,
kind: di::ReferenceFrameKind,
) -> di::SpatialId {
let id = self.generate_spatial_index();
let item = di::SpecificDisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem {
reference_frame: di::ReferenceFrame {
transform_style,
transform,
kind,
id,
},
});
let layout = di::LayoutPrimitiveInfo::new(*rect);
self.push_item(&item, &layout, &di::SpaceAndClipInfo {
spatial_id: parent,
clip_id: di::ClipId::invalid(),
});
id
}
pub fn push_cache_marker(&mut self) {
self.push_new_empty_item(&di::SpecificDisplayItem::PushCacheMarker(di::CacheMarkerDisplayItem {
}));
}
pub fn pop_cache_marker(&mut self) {
self.push_new_empty_item(&di::SpecificDisplayItem::PopCacheMarker);
}
pub fn pop_reference_frame(&mut self) {
self.push_new_empty_item(&di::SpecificDisplayItem::PopReferenceFrame);
}
pub fn push_stacking_context(
&mut self,
layout: &di::LayoutPrimitiveInfo,
spatial_id: di::SpatialId,
clip_id: Option<di::ClipId>,
transform_style: di::TransformStyle,
mix_blend_mode: di::MixBlendMode,
filters: &[di::FilterOp],
raster_space: di::RasterSpace,
cache_tiles: bool,
) {
let item = di::SpecificDisplayItem::PushStackingContext(di::PushStackingContextDisplayItem {
stacking_context: di::StackingContext {
transform_style,
mix_blend_mode,
clip_id,
raster_space,
cache_tiles,
},
});
self.push_item(&item, layout, &di::SpaceAndClipInfo {
spatial_id,
clip_id: di::ClipId::invalid(),
});
self.push_iter(filters);
}
pub fn push_simple_stacking_context(
&mut self,
layout: &di::LayoutPrimitiveInfo,
spatial_id: di::SpatialId,
) {
self.push_simple_stacking_context_with_filters(layout, spatial_id, &[]);
}
pub fn push_simple_stacking_context_with_filters(
&mut self,
layout: &di::LayoutPrimitiveInfo,
spatial_id: di::SpatialId,
filters: &[di::FilterOp],
) {
self.push_stacking_context(
layout,
spatial_id,
None,
di::TransformStyle::Flat,
di::MixBlendMode::Normal,
filters,
di::RasterSpace::Screen,
false,
);
}
pub fn pop_stacking_context(&mut self) {
self.push_new_empty_item(&di::SpecificDisplayItem::PopStackingContext);
}
pub fn push_stops(&mut self, stops: &[di::GradientStop]) {
if stops.is_empty() {
return;
}
self.push_new_empty_item(&di::SpecificDisplayItem::SetGradientStops);
self.push_iter(stops);
}
fn generate_clip_index(&mut self) -> di::ClipId {
self.next_clip_index += 1;
di::ClipId::Clip(self.next_clip_index - 1, self.pipeline_id)
}
fn generate_spatial_index(&mut self) -> di::SpatialId {
self.next_spatial_index += 1;
di::SpatialId::new(self.next_spatial_index - 1, self.pipeline_id)
}
fn generate_clip_chain_id(&mut self) -> di::ClipChainId {
self.next_clip_chain_id += 1;
di::ClipChainId(self.next_clip_chain_id - 1, self.pipeline_id)
}
pub fn define_scroll_frame<I>(
&mut self,
parent_space_and_clip: &di::SpaceAndClipInfo,
external_id: Option<di::ExternalScrollId>,
content_rect: LayoutRect,
clip_rect: LayoutRect,
complex_clips: I,
image_mask: Option<di::ImageMask>,
scroll_sensitivity: di::ScrollSensitivity,
) -> di::SpaceAndClipInfo
where
I: IntoIterator<Item = di::ComplexClipRegion>,
I::IntoIter: ExactSizeIterator + Clone,
{
let clip_id = self.generate_clip_index();
let scroll_frame_id = self.generate_spatial_index();
let item = di::SpecificDisplayItem::ScrollFrame(di::ScrollFrameDisplayItem {
clip_id,
scroll_frame_id,
external_id,
image_mask,
scroll_sensitivity,
});
self.push_item(
&item,
&di::LayoutPrimitiveInfo::with_clip_rect(content_rect, clip_rect),
parent_space_and_clip,
);
self.push_iter(complex_clips);
di::SpaceAndClipInfo {
spatial_id: scroll_frame_id,
clip_id,
}
}
pub fn define_clip_chain<I>(
&mut self,
parent: Option<di::ClipChainId>,
clips: I,
) -> di::ClipChainId
where
I: IntoIterator<Item = di::ClipId>,
I::IntoIter: ExactSizeIterator + Clone,
{
let id = self.generate_clip_chain_id();
self.push_new_empty_item(&di::SpecificDisplayItem::ClipChain(di::ClipChainItem { id, parent }));
self.push_iter(clips);
id
}
pub fn define_clip<I>(
&mut self,
parent_space_and_clip: &di::SpaceAndClipInfo,
clip_rect: LayoutRect,
complex_clips: I,
image_mask: Option<di::ImageMask>,
) -> di::ClipId
where
I: IntoIterator<Item = di::ComplexClipRegion>,
I::IntoIter: ExactSizeIterator + Clone,
{
let id = self.generate_clip_index();
let item = di::SpecificDisplayItem::Clip(di::ClipDisplayItem {
id,
image_mask,
});
self.push_item(
&item,
&di::LayoutPrimitiveInfo::new(clip_rect),
parent_space_and_clip,
);
self.push_iter(complex_clips);
id
}
pub fn define_sticky_frame(
&mut self,
parent_spatial_id: di::SpatialId,
frame_rect: LayoutRect,
margins: SideOffsets2D<Option<f32>>,
vertical_offset_bounds: di::StickyOffsetBounds,
horizontal_offset_bounds: di::StickyOffsetBounds,
previously_applied_offset: LayoutVector2D,
) -> di::SpatialId {
let id = self.generate_spatial_index();
let item = di::SpecificDisplayItem::StickyFrame(di::StickyFrameDisplayItem {
id,
margins,
vertical_offset_bounds,
horizontal_offset_bounds,
previously_applied_offset,
});
self.push_item(
&item,
&di::LayoutPrimitiveInfo::new(frame_rect),
&di::SpaceAndClipInfo {
spatial_id: parent_spatial_id,
clip_id: di::ClipId::invalid(),
},
);
id
}
pub fn push_iframe(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
pipeline_id: PipelineId,
ignore_missing_pipeline: bool
) {
let item = di::SpecificDisplayItem::Iframe(di::IframeDisplayItem {
pipeline_id,
ignore_missing_pipeline,
});
self.push_item(&item, layout, space_and_clip);
}
pub fn push_shadow(
&mut self,
layout: &di::LayoutPrimitiveInfo,
space_and_clip: &di::SpaceAndClipInfo,
shadow: di::Shadow,
) {
self.push_item(&di::SpecificDisplayItem::PushShadow(shadow), layout, space_and_clip);
}
pub fn pop_all_shadows(&mut self) {
self.push_new_empty_item(&di::SpecificDisplayItem::PopAllShadows);
}
pub fn finalize(self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
assert!(self.save_state.is_none(), "Finalized DisplayListBuilder with a pending save");
let end_time = precise_time_ns();
(
self.pipeline_id,
self.content_size,
BuiltDisplayList {
descriptor: BuiltDisplayListDescriptor {
builder_start_time: self.builder_start_time,
builder_finish_time: end_time,
send_start_time: 0,
total_clip_nodes: self.next_clip_index,
total_spatial_nodes: self.next_spatial_index,
},
data: self.data,
},
)
}
}