/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use euclid::SideOffsets2D; use gleam::gl; use image::GenericImageView; usecrate::parse_function::parse_function; usecrate::premultiply::premultiply; use std::collections::HashMap; use std::convert::TryInto; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; use std::usize; use webrender::api::*; use webrender::render_api::*; use webrender::api::units::*; use webrender::api::FillRule; usecrate::wrench::{FontDescriptor, Wrench, WrenchThing, DisplayList}; usecrate::yaml_helper::{StringEnum, YamlHelper, make_perspective}; use yaml_rust::{Yaml, YamlLoader}; usecrate::PLATFORM_DEFAULT_FACE_NAME;
macro_rules! try_intersect {
($first: expr, $second: expr) => { iflet Some(rect) = ($first).intersection($second) {
rect
} else {
warn!("skipping item with non-intersecting bounds and clip_rect"); return;
}
}
}
for y in0 .. height { for x in0 .. width { if y < border || y >= (height - border) ||
x < border || x >= (width - border) {
pixels.push(0);
pixels.push(0);
pixels.push(0xff);
pixels.push(0xff);
} else { let xon = ((x - border) % (2 * tile_x_size)) < tile_x_size; let yon = ((y - border) % (2 * tile_y_size)) < tile_y_size; match kind {
CheckerboardKind::BlackGrey => { let value = if xon ^ yon { 0xff } else { 0x7f };
pixels.push(value);
pixels.push(value);
pixels.push(value);
pixels.push(0xff);
}
CheckerboardKind::BlackTransparent => { let value = if xon ^ yon { 0xff } else { 0x00 };
pixels.push(value);
pixels.push(value);
pixels.push(value);
pixels.push(value);
}
}
}
}
}
let flags = match kind {
CheckerboardKind::BlackGrey => ImageDescriptorFlags::IS_OPAQUE,
CheckerboardKind::BlackTransparent => ImageDescriptorFlags::empty(),
};
(
ImageDescriptor::new(width as i32, height as i32, ImageFormat::BGRA8, flags),
ImageData::new(pixels),
)
}
fn generate_xy_gradient_image(w: u32, h: u32) -> (ImageDescriptor, ImageData) { letmut pixels = Vec::with_capacity((w * h * 4) as usize); for y in0 .. h { for x in0 .. w { let grid = if x % 100 < 3 || y % 100 < 3 { 0.9 } else { 1.0 };
pixels.push((y as f32 / h as f32 * 255.0 * grid) as u8);
pixels.push(0);
pixels.push((x as f32 / w as f32 * 255.0 * grid) as u8);
pixels.push(255);
}
}
(
ImageDescriptor::new(w as i32, h as i32, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(pixels),
)
}
/// A HashMap of offsets which specify what scroll offsets particular /// scroll layers should be initialized with.
scroll_offsets: HashMap<ExternalScrollId, Vec<SampledScrollOffset>>,
next_external_scroll_id: u64,
/// A HashMap that allows specifying a numeric id for clip and clip chains in YAML /// and having each of those ids correspond to a unique ClipId.
user_clip_id_map: HashMap<u64, ClipId>,
user_clipchain_id_map: HashMap<u64, ClipChainId>,
user_spatial_id_map: HashMap<u64, SpatialId>,
// If replaying the same frame during interactive use, the frame gets rebuilt, // but the external image handler has already been consumed by the renderer. iflet Some(external_image_handler) = self.external_image_handler.take() {
wrench.renderer.set_external_image_handler(external_image_handler);
}
}
fn build_pipeline(
&mutself,
wrench: &mut Wrench,
builder: &mut DisplayListBuilder,
pipeline_id: PipelineId,
send_transaction: bool,
yaml: &Yaml
) { let offscreen = yaml["offscreen"].as_bool().unwrap_or(false); // By default, present if send_transaction is set to true. Can be overridden // by a field in the pipeline's root. let present = !offscreen && yaml["present"].as_bool().unwrap_or(send_transaction);
// Don't allow referencing clips between pipelines for now. self.user_clip_id_map.clear(); self.user_clipchain_id_map.clear(); self.user_spatial_id_map.clear(); self.spatial_id_stack.clear(); self.spatial_id_stack.push(SpatialId::root_scroll_node(pipeline_id));
fn to_spatial_id(&self, item: &Yaml, pipeline_id: PipelineId) -> Option<SpatialId> { match *item {
Yaml::Integer(value) => Some(self.user_spatial_id_map[&(value as u64)]),
Yaml::String(ref id_string) if id_string == "root-reference-frame" =>
Some(SpatialId::root_reference_frame(pipeline_id)),
Yaml::String(ref id_string) if id_string == "root-scroll-node" =>
Some(SpatialId::root_scroll_node(pipeline_id)),
Yaml::BadValue => None,
_ => {
println!("Unable to parse SpatialId {:?}", item);
None
}
}
}
fn add_clip_id_mapping(&mutself, numeric_id: u64, real_id: ClipId) {
assert_ne!(numeric_id, 0, "id=0 is reserved for the root clip"); self.user_clip_id_map.insert(numeric_id, real_id);
}
fn add_clip_chain_id_mapping(&mutself, numeric_id: u64, real_id: ClipChainId) {
assert_ne!(numeric_id, 0, "id=0 is reserved for the root clip-chain"); self.user_clipchain_id_map.insert(numeric_id, real_id);
}
fn add_spatial_id_mapping(&mutself, numeric_id: u64, real_id: SpatialId) {
assert_ne!(numeric_id, 0, "id=0 is reserved for the root reference frame");
assert_ne!(numeric_id, 1, "id=1 is reserved for the root scroll node"); self.user_spatial_id_map.insert(numeric_id, real_id);
}
fn to_hit_testing_tag(&self, item: &Yaml) -> Option<ItemTag> { match *item {
Yaml::Array(ref array) if array.len() == 2 => { match (array[0].as_i64(), array[1].as_i64()) {
(Some(first), Some(second)) => Some((first as u64, second as u16)),
_ => None,
}
}
_ => None,
}
let external = item["external"].as_bool().unwrap_or(false); if external { // This indicates we want to simulate an external texture, // ensure it gets created as such let external_target = match item["external-target"].as_str() {
Some("2d") => ImageBufferKind::Texture2D,
Some("rect") => ImageBufferKind::TextureRect,
Some(t) => panic!("Unsupported external texture target: {}", t),
None => ImageBufferKind::Texture2D,
};
wrench.api.send_transaction(wrench.document_id, txn); let val = (
image_key,
LayoutSize::new(descriptor.size.width as f32, descriptor.size.height as f32),
); self.image_map.insert(key, val);
val
}
fn get_or_create_font(&mutself, desc: FontDescriptor, wrench: &pan style='color:red'>mut Wrench) -> FontKey { let list_resources = self.list_resources;
*self.fonts
.entry(desc.clone())
.or_insert_with(|| match desc {
FontDescriptor::Path { ref path,
font_index,
} => { if list_resources { println!("{}", path.to_string_lossy()); } letmut file = File::open(path).expect("Couldn't open font file"); letmut bytes = vec![];
file.read_to_end(&mut bytes)
.expect("failed to read font file");
wrench.font_key_from_bytes(bytes, font_index)
}
FontDescriptor::Family { ref name } => wrench.font_key_from_name(name),
FontDescriptor::Properties { ref family,
weight,
style,
stretch,
} => wrench.font_key_from_properties(family, weight, style, stretch),
})
}
let bounds = self.resolve_rect(&item[bounds_key]); let color = self.resolve_colorf(&item["color"]).unwrap_or(ColorF::BLACK);
dl.push_rect(info, bounds, color);
}
fn handle_hit_test(
&mutself,
dl: &mut DisplayListBuilder,
item: &Yaml,
info: &mut CommonItemProperties,
) {
info.clip_rect = try_intersect!(
item["bounds"].as_rect().expect("hit-test type must have bounds"),
&info.clip_rect
);
fn handle_line(
&mutself,
dl: &mut DisplayListBuilder,
item: &Yaml,
info: &mut CommonItemProperties,
) { let color = item["color"].as_colorf().unwrap_or(ColorF::BLACK); let orientation = item["orientation"]
.as_str()
.and_then(LineOrientation::from_str)
.expect("line must have orientation"); let style = item["style"]
.as_str()
.and_then(LineStyle::from_str)
.expect("line must have style");
let wavy_line_thickness = iflet LineStyle::Wavy = style {
item["thickness"].as_f32().expect("wavy lines must have a thickness")
} else { 0.0
};
let area = if item["baseline"].is_badvalue() { let bounds_key = if item["type"].is_badvalue() { "rect"
} else { "bounds"
};
item[bounds_key]
.as_rect()
.expect("line type must have bounds")
} else { // Legacy line representation let baseline = item["baseline"].as_f32().expect("line must have baseline"); let start = item["start"].as_f32().expect("line must have start"); let end = item["end"].as_f32().expect("line must have end"); let width = item["width"].as_f32().expect("line must have width");
fn handle_gradient(
&mutself,
dl: &mut DisplayListBuilder,
item: &Yaml,
info: &mut CommonItemProperties,
) { let bounds_key = if item["type"].is_badvalue() { "gradient"
} else { "bounds"
}; let bounds = item[bounds_key]
.as_rect()
.expect("gradient must have bounds");
let gradient = item.as_gradient(dl); let tile_size = item["tile-size"].as_size().unwrap_or_else(|| bounds.size()); let tile_spacing = item["tile-spacing"].as_size().unwrap_or_else(LayoutSize::zero);
fn handle_yuv_image(
&mutself,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
item: &Yaml,
info: &mut CommonItemProperties,
) { // TODO(gw): Support other YUV color depth and spaces. let color_depth = ColorDepth::Color8; let color_space = YuvColorSpace::Rec709; let color_range = ColorRange::Limited;
let yuv_data = match item["format"].as_str().expect("no format supplied") { "planar" => { let y_path = rsrc_path(&item["src-y"], &self.aux_dir); let (y_key, _) = self.add_or_get_image(&y_path, None, item, wrench);
let u_path = rsrc_path(&item["src-u"], &self.aux_dir); let (u_key, _) = self.add_or_get_image(&u_path, None, item, wrench);
let v_path = rsrc_path(&item["src-v"], &self.aux_dir); let (v_key, _) = self.add_or_get_image(&v_path, None, item, wrench);
YuvData::PlanarYCbCr(y_key, u_key, v_key)
} "nv12" => { let y_path = rsrc_path(&item["src-y"], &self.aux_dir); let (y_key, _) = self.add_or_get_image(&y_path, None, item, wrench);
let uv_path = rsrc_path(&item["src-uv"], &self.aux_dir); let (uv_key, _) = self.add_or_get_image(&uv_path, None, item, wrench);
YuvData::NV12(y_key, uv_key)
} "p010" => { let y_path = rsrc_path(&item["src-y"], &self.aux_dir); let (y_key, _) = self.add_or_get_image(&y_path, None, item, wrench);
let uv_path = rsrc_path(&item["src-uv"], &self.aux_dir); let (uv_key, _) = self.add_or_get_image(&uv_path, None, item, wrench);
YuvData::P010(y_key, uv_key)
} "nv16" => { let y_path = rsrc_path(&item["src-y"], &self.aux_dir); let (y_key, _) = self.add_or_get_image(&y_path, None, item, wrench);
let uv_path = rsrc_path(&item["src-uv"], &self.aux_dir); let (uv_key, _) = self.add_or_get_image(&uv_path, None, item, wrench);
YuvData::NV16(y_key, uv_key)
} "interleaved" => { let yuv_path = rsrc_path(&item["src"], &self.aux_dir); let (yuv_key, _) = self.add_or_get_image(&yuv_path, None, item, wrench);
letmut flags = FontInstanceFlags::empty(); if item["synthetic-bold"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::SYNTHETIC_BOLD;
} if item["embedded-bitmaps"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::EMBEDDED_BITMAPS;
} if item["transpose"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::TRANSPOSE;
} if item["flip-x"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::FLIP_X;
} if item["flip-y"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::FLIP_Y;
}
assert!(
item["blur-radius"].is_badvalue(), "text no longer has a blur radius, use PushShadow and PopAllShadows"
);
let desc = FontDescriptor::from_yaml(item, &self.aux_dir); let font_key = self.get_or_create_font(desc, wrench); let font_instance_key = self.get_or_create_font_instance(font_key,
size,
flags,
synthetic_italics,
wrench);
assert!(
!(item["glyphs"].is_badvalue() && item["text"].is_badvalue()), "text item had neither text nor glyphs!"
);
let (glyphs, rect) = if item["text"].is_badvalue() { // if glyphs are specified, then the glyph positions can have the // origin baked in. let origin = item["origin"]
.as_point()
.unwrap_or(LayoutPoint::new(0.0, 0.0)); let glyph_indices = item["glyphs"].as_vec_u32().unwrap(); let glyph_offsets = item["offsets"].as_vec_f32().unwrap();
assert_eq!(glyph_offsets.len(), glyph_indices.len() * 2);
let glyphs = glyph_indices
.iter()
.enumerate()
.map(|k| {
GlyphInstance {
index: *k.1, // In the future we want to change the API to be relative, eliminating this
point: LayoutPoint::new(
origin.x + glyph_offsets[k.0 * 2],
origin.y + glyph_offsets[k.0 * 2 + 1],
),
}
})
.collect::<Vec<_>>(); // TODO(gw): We could optionally use the WR API to query glyph dimensions // here and calculate the bounding region here if we want to. let rect = item["bounds"]
.as_rect()
.expect("Text items with glyphs require bounds [for now]");
(glyphs, rect)
} else { let text = item["text"].as_str().unwrap(); let origin = item["origin"]
.as_point()
.expect("origin required for text without glyphs"); let (glyph_indices, glyph_positions, bounds) = wrench.layout_simple_ascii(
font_key,
font_instance_key,
text,
size,
origin,
flags,
);
fn handle_iframe(
&mutself,
dl: &mut DisplayListBuilder,
item: &Yaml,
info: &mut CommonItemProperties,
) { let bounds = item["bounds"].as_rect().expect("iframe must have bounds"); let pipeline_id = item["id"].as_pipeline_id().unwrap(); let ignore = item["ignore_missing_pipeline"].as_bool().unwrap_or(true);
dl.push_iframe(
bounds,
info.clip_rect,
&SpaceAndClipInfo {
spatial_id: info.spatial_id,
clip_chain_id: info.clip_chain_id
},
pipeline_id,
ignore
);
}
fn get_item_type_from_yaml(item: &Yaml) -> &str { let shorthands = [ "rect", "image", "text", "glyphs", "box-shadow", // Note: box_shadow shorthand check has to come before border. "border", "gradient", "radial-gradient", "conic-gradient"
];
for shorthand in shorthands.iter() { if !item[*shorthand].is_badvalue() { return shorthand;
}
}
item["type"].as_str().unwrap_or("unknown")
}
fn add_display_list_items_from_yaml(
&mutself,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
yaml_items: &[Yaml],
) { // A very large number (but safely far away from finite limits of f32) let big_number = 1.0e30; // A rect that should in practical terms serve as a no-op for clipping let full_clip = LayoutRect::from_origin_and_size(
LayoutPoint::new(-big_number / 2.0, -big_number / 2.0),
LayoutSize::new(big_number, big_number));
for item in yaml_items { let item_type = Self::get_item_type_from_yaml(item);
let spatial_id = self.to_spatial_id(&item["spatial-id"], dl.pipeline_id);
let clip_rect = item["clip-rect"].as_rect().unwrap_or(full_clip); let clip_chain_id = self.to_clip_chain_id(&item["clip-chain"], dl).unwrap_or(ClipChainId::INVALID);
if spatial_id.is_some() { self.spatial_id_stack.pop().unwrap();
}
}
}
fn handle_scroll_frame(
&mutself,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
yaml: &Yaml,
) { let clip_rect = yaml["bounds"]
.as_rect()
.expect("scroll frame must have a bounds"); let content_size = yaml["content-size"].as_size().unwrap_or_else(|| clip_rect.size()); let content_rect = LayoutRect::from_origin_and_size(clip_rect.min, content_size); let external_scroll_offset = yaml["external-scroll-offset"].as_vector().unwrap_or_else(LayoutVector2D::zero); let scroll_generation = yaml["scroll-generation"].as_i64().map_or(APZScrollGeneration::default(), |v| v as u64); let has_scroll_linked_effect =
yaml["has-scroll-linked-effect"].as_bool().map_or(HasScrollLinkedEffect::default(),
|v| if v { HasScrollLinkedEffect::Yes } else { HasScrollLinkedEffect::No }
);
let numeric_id = yaml["id"].as_i64().map(|id| id as u64);
let external_id = ExternalScrollId(self.next_external_scroll_id, dl.pipeline_id); self.next_external_scroll_id += 1;
if !yaml["scroll-offsets"].is_badvalue() { letmut offsets = Vec::new(); for entry in yaml["scroll-offsets"].as_vec().unwrap() { let offset = entry["offset"].as_vector().unwrap_or(LayoutVector2D::zero()); let generation = entry["generation"].as_i64().map_or(APZScrollGeneration::default(), |v| v as u64);
offsets.push(SampledScrollOffset { offset, generation });
} self.scroll_offsets.insert(external_id, offsets);
}
let clip_to_frame = yaml["clip-to-frame"].as_bool().unwrap_or(false);
let clip_id = if clip_to_frame {
Some(dl.define_clip_rect( self.top_space(),
clip_rect,
))
} else {
None
};
fn handle_sticky_frame(
&mutself,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
yaml: &Yaml,
) { let bounds = yaml["bounds"].as_rect().expect("sticky frame must have a bounds"); let numeric_id = yaml["id"].as_i64().map(|id| id as u64);
fn handle_clip_chain(&mutself, builder: &mut DisplayListBuilder, yaml: &Yaml) { let numeric_id = yaml["id"].as_i64().expect("clip chains must have an id"); let clip_ids: Vec<ClipId> = yaml["clips"]
.as_vec_u64()
.unwrap_or_default()
.iter()
.map(|id| self.user_clip_id_map[id])
.collect();
let parent = self.to_clip_chain_id(&yaml["parent"], builder); let real_id = builder.define_clip_chain(parent, clip_ids); self.add_clip_chain_id_mapping(numeric_id as u64, real_id);
}
fn handle_clip(&mutself, dl: &mut DisplayListBuilder, wrench: &mut Wrench, yaml: &Yaml) { let numeric_id = yaml["id"].as_i64(); let spatial_id = self.top_space(); let complex_clips = yaml["complex"].as_complex_clip_regions(); letmut clip_id = None;
let transform = yaml["transform"]
.as_transform(&transform_origin);
let perspective = match yaml["perspective"].as_f32() {
Some(value) if value != 0.0 => {
Some(make_perspective(perspective_origin, value as f32))
}
Some(..) => None,
_ => yaml["perspective"].as_matrix4d(),
};
let reference_frame_id = dl.push_reference_frame(
bounds.min,
*self.spatial_id_stack.last().unwrap(),
transform_style,
transform.or(perspective).unwrap_or_default().into(),
reference_frame_kind,
);
let numeric_id = yaml["id"].as_i64(); iflet Some(numeric_id) = numeric_id { self.add_spatial_id_mapping(numeric_id as u64, reference_frame_id);
}
let scale_from = yaml["scale-from"].as_size(); let vertical_flip = yaml["vertical-flip"].as_bool().unwrap_or(false); let rotation = yaml["rotation"].as_rotation().unwrap_or(Rotation::Degree0);
let reference_frame_id = dl.push_computed_frame(
bounds.min,
*self.spatial_id_stack.last().unwrap(),
scale_from,
vertical_flip,
rotation,
);
let numeric_id = yaml["id"].as_i64(); iflet Some(numeric_id) = numeric_id { self.add_spatial_id_mapping(numeric_id as u64, reference_frame_id);
}
let clip_chain_id = self.to_clip_chain_id(&yaml["clip-chain"], dl); let mix_blend_mode = yaml["mix-blend-mode"]
.as_mix_blend_mode()
.unwrap_or(MixBlendMode::Normal); let raster_space = yaml["raster-space"]
.as_raster_space()
.unwrap_or(RasterSpace::Screen); let is_blend_container = yaml["blend-container"].as_bool().unwrap_or(false); let wraps_backdrop_filter = yaml["wraps-backdrop-filter"].as_bool().unwrap_or(false);
let filters = yaml["filters"].as_vec_filter_op().unwrap_or_default(); let filter_datas = yaml["filter-datas"].as_vec_filter_data().unwrap_or_default();
let snapshot = if !yaml["snapshot"].is_badvalue() { let yaml = &yaml["snapshot"]; let name = yaml["name"].as_str().unwrap_or("snapshot"); let area = yaml["area"].as_rect().unwrap_or(bounds); let detached = yaml["detached"].as_bool().unwrap_or(false);
let filters = item["filters"].as_vec_filter_op().unwrap_or_default(); let filter_datas = item["filter-datas"].as_vec_filter_data().unwrap_or_default();
// If YAML isn't read yet, or watching source file, reload from disk. ifself.yaml_string.is_empty() || self.watch_source { self.yaml_string = std::fs::read_to_string(&self.yaml_path)
.unwrap_or_else(|_| panic!("YAML '{:?}' doesn't exist", self.yaml_path));
should_build_yaml = true;
}
// Evaluate conditions that require parsing the YAML. ifself.built_frame != self.requested_frame { // Requested frame has changed
should_build_yaml = true;
}
// Build the DL from YAML if required if should_build_yaml { self.build(wrench);
}
// Determine whether to send a new DL, or just refresh. if should_build_yaml || wrench.should_rebuild_display_lists() {
wrench.begin_frame();
wrench.send_lists(
&mutself.frame_count, self.display_lists.clone(),
&self.scroll_offsets,
);
} else {
wrench.refresh();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.