/* 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::{point2, size2, rect, Box2D}; use std::sync::Arc; use std::sync::atomic::{AtomicIsize, Ordering}; use std::sync::mpsc::Receiver; use webrender::api::*; use webrender::render_api::*; use webrender::api::units::*; usecrate::{WindowWrapper, NotifierEvent}; usecrate::blob; usecrate::reftest::{ReftestImage, ReftestImageComparison}; usecrate::wrench::Wrench;
fn test_resize_image(&mutself) {
println!("\tresize image..."); // This test changes the size of an image to make it go switch back and forth // between tiled and non-tiled. // The resource cache should be able to handle this without crashing.
letmut txn = Transaction::new(); let img = self.wrench.api.generate_image_key();
// Start with a non-tiled image.
txn.add_image(
img,
ImageDescriptor::new(64, 64, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(vec![255; 64 * 64 * 4]),
None,
);
letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 0.0, 64.0, 64.0).to_box2d());
letmut txn = Transaction::new(); // Resize the image to something bigger than the max texture size (8196) to force tiling.
txn.update_image(
img,
ImageDescriptor::new(8200, 32, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(vec![255; 8200 * 32 * 4]),
&DirtyRect::All,
);
letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0).to_box2d());
// Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this
txn = Transaction::new();
txn.delete_blob_image(blob_img); self.wrench.api.send_transaction(self.wrench.document_id, txn);
}
fn test_very_large_blob(&mutself) {
println!("\tvery large blob...");
let window_size = self.window.get_inner_size();
let test_size = FramebufferIntSize::new(800, 800);
let window_rect = FramebufferIntRect::from_origin_and_size(
FramebufferIntPoint::new(0, window_size.height - test_size.height),
test_size,
);
// This exposes a crash in tile decomposition letmut txn = Transaction::new();
// make sure things are in the right spot let w = window_rect.width() as usize; let h = window_rect.height() as usize; let p1 = (40 + (h - 100) * w) * 4;
assert_eq!(pixels[p1 ], 50);
assert_eq!(pixels[p1 + 1], 50);
assert_eq!(pixels[p1 + 2], 150);
assert_eq!(pixels[p1 + 3], 255);
// Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this
txn = Transaction::new();
txn.delete_blob_image(blob_img); self.wrench.api.send_transaction(self.wrench.document_id, txn);
// make sure things are in the right spot let w = window_rect.width() as usize; let h = window_rect.height() as usize; let p1 = (65 + (h - 15) * w) * 4;
assert_eq!(pixels[p1 ], 255);
assert_eq!(pixels[p1 + 1], 255);
assert_eq!(pixels[p1 + 2], 255);
assert_eq!(pixels[p1 + 3], 255);
// Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this
txn = Transaction::new();
txn.delete_blob_image(blob_img); self.wrench.api.send_transaction(self.wrench.document_id, txn);
fn test_blob_set_visible_area(&mutself) { // In this test we first render a blob with a certain visible area, // then change the visible area without updating the blob image.
println!("\tblob visible area update...");
let window_size = self.window.get_inner_size(); let test_size = FramebufferIntSize::new(800, 800); let window_rect = FramebufferIntRect::from_origin_and_size(
FramebufferIntPoint::new(0, window_size.height - test_size.height),
test_size,
); letmut txn = Transaction::new();
// Render the first display list. We don't care about the result but we // want to make sure the next display list updates an already rendered // state. self.submit_dl(&mut epoch, builder, txn); let _ = self.render_and_get_pixels(window_rect);
// Now render a similar scene with an updated blob visible area. // In this test we care about the fact that the visible area was updated // without using update_blob_image.
// Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this
txn = Transaction::new();
txn.delete_blob_image(blob_img); self.wrench.api.send_transaction(self.wrench.document_id, txn);
}
fn test_retained_blob_images_test(&mutself) {
println!("\tretained blob images test..."); let blob_img; let window_size = self.window.get_inner_size();
let test_size = FramebufferIntSize::new(400, 400); let window_rect = FramebufferIntRect::from_origin_and_size(
FramebufferIntPoint::new(0, window_size.height - test_size.height),
test_size,
);
letmut txn = Transaction::new();
{ let api = &self.wrench.api;
// draw the blob the first time letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
let pixels_first = self.render_and_get_pixels(window_rect);
assert_eq!(1, called.load(Ordering::SeqCst));
// draw the blob image a second time at a different location
// make a new display list that refers to the first image letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(1.0, 60.0, 200.0, 200.0).to_box2d());
builder.push_image(
&info,
info.clip_rect,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img.as_image(),
ColorF::WHITE,
);
// setup some counters to count how many times each image is requested let img1_requested = Arc::new(AtomicIsize::new(0)); let img1_requested_inner = Arc::clone(&img1_requested); let img2_requested = Arc::new(AtomicIsize::new(0)); let img2_requested_inner = Arc::clone(&img2_requested);
// track the number of times that the second image has been requested self.wrench.callbacks.lock().unwrap().request = Box::new(move |requests| { for item in requests { if item.request.key == blob_img {
img1_requested_inner.fetch_add(1, Ordering::SeqCst);
} if item.request.key == blob_img2 {
img2_requested_inner.fetch_add(1, Ordering::SeqCst);
}
}
});
// create two blob images and draw them letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d()); let info2 = self.make_common_properties(rect(200.0, 60.0, 200.0, 200.0).to_box2d()); let push_images = |builder: &mut DisplayListBuilder| {
builder.push_image(
&info,
info.clip_rect,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img.as_image(),
ColorF::WHITE,
);
builder.push_image(
&info2,
info2.clip_rect,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img2.as_image(),
ColorF::WHITE,
);
};
push_images(&mut builder);
letmut epoch = Epoch(0);
self.submit_dl(&mut epoch, builder, txn); let _pixels_first = self.render_and_get_pixels(window_rect);
// the first image should be requested 3 times
assert_eq!(img1_requested.load(Ordering::SeqCst), 3); // the second image should've been requested twice
assert_eq!(img2_requested.load(Ordering::SeqCst), 2);
// draw the blobs the first time letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
self.submit_dl(&mut epoch, builder, txn); let pixels_first = self.render_and_get_pixels(window_rect);
// draw the blob image a second time after updating it with the same color letmut txn = Transaction::new();
txn.update_blob_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
DeviceIntRect::from_size(size2(500, 500)),
&Box2D { min: point2(100, 100), max: point2(200, 200) }.into(),
);
// make a new display list that refers to the first image letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
builder.push_image(
&info,
info.clip_rect,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img.as_image(),
ColorF::WHITE,
);
self.submit_dl(&mut epoch, builder, txn); let pixels_second = self.render_and_get_pixels(window_rect);
// draw the blob image a third time after updating it with a different color letmut txn = Transaction::new();
txn.update_blob_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
blob::serialize_blob(ColorU::new(50, 150, 150, 255)),
DeviceIntRect::from_size(size2(500, 500)),
&Box2D { min: point2(200, 200), max: point2(300, 300) }.into(),
);
// make a new display list that refers to the first image letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
builder.push_image(
&info,
info.clip_rect,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img.as_image(),
ColorF::WHITE,
);
self.submit_dl(&mut epoch, builder, txn); let pixels_third = self.render_and_get_pixels(window_rect);
// Ensures that content doing a save-restore produces the same results as not fn test_save_restore(&mutself) {
println!("\tsave/restore..."); let window_size = self.window.get_inner_size();
let test_size = FramebufferIntSize::new(400, 400); let window_rect = FramebufferIntRect::from_origin_and_size(
point2(0, window_size.height - test_size.height),
test_size,
);
// regression test for #2769 // "async scene building: cache collisions from reused picture ids" fn test_blur_cache(&mutself) {
println!("\tblur cache..."); let window_size = self.window.get_inner_size();
let test_size = FramebufferIntSize::new(400, 400); let window_rect = FramebufferIntRect::from_origin_and_size(
point2(0, window_size.height - test_size.height),
test_size,
);
let txn = Transaction::new(); self.submit_dl(&mut Epoch(0), builder, txn);
self.render_and_get_pixels(window_rect)
};
let first = do_test(false); let second = do_test(true);
assert_ne!(first, second);
}
fn test_capture(&mutself) {
println!("\tcapture..."); let path = "../captures/test"; let layout_size = LayoutSize::new(400., 400.); let dim = self.window.get_inner_size(); let window_rect = FramebufferIntRect::from_origin_and_size(
point2(0, dim.height - layout_size.height as i32),
size2(layout_size.width as i32, layout_size.height as i32),
);
letmut documents = self.wrench.api.load_capture(path.into(), None); let captured = documents.swap_remove(0);
// 5. render the built frame and compare let pixels1 = self.render_and_get_pixels(window_rect); self.compare_pixels(pixels0.clone(), pixels1, window_rect.size());
// 6. rebuild the scene and compare again letmut txn = Transaction::new();
txn.set_root_pipeline(captured.root_pipeline_id.unwrap());
txn.generate_frame(0, true, false, RenderReasons::TESTING); self.wrench.api.send_transaction(captured.document_id, txn); let pixels2 = self.render_and_get_pixels(window_rect); self.compare_pixels(pixels0, pixels2, window_rect.size());
}
let layout_size = LayoutSize::new(120.0, 0.0); let window_size = DeviceIntSize::new(layout_size.width as i32, layout_size.height as i32); let doc_id = self.wrench.api.add_document(window_size);
letmut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
builder.begin(); let info = self.make_common_properties(
LayoutRect::from_size(LayoutSize::new(100.0, 100.0))
);
builder.push_rect(
&info,
info.clip_rect,
ColorF::new(0.0, 1.0, 0.0, 1.0),
);
// Ensure we get a notification from rendering the above, even though // there are zero visible pixels
assert!(self.rx.recv().unwrap() == NotifierEvent::WakeUp { composite_needed: true });
}
// Add a rectangle that is clipped by a rounded rect clip item. let rect = LayoutRect::from_origin_and_size(LayoutPoint::new(100., 100.), LayoutSize::new(100., 100.)); let temp_clip_id = builder.define_clip_rounded_rect(
space_and_clip.spatial_id,
make_rounded_complex_clip(&rect, 20.),
); let clip_chain_id = builder.define_clip_chain(None, vec![temp_clip_id]);
builder.push_hit_test(
rect,
clip_chain_id,
space_and_clip.spatial_id,
PrimitiveFlags::default(),
(0, 4),
);
// Add a rectangle that is clipped by a ClipChain containing a rounded rect. let rect = LayoutRect::from_origin_and_size(LayoutPoint::new(200., 100.), LayoutSize::new(100., 100.)); let clip_id = builder.define_clip_rounded_rect(
space_and_clip.spatial_id,
make_rounded_complex_clip(&rect, 20.),
); let clip_chain_id = builder.define_clip_chain(None, vec![clip_id]);
builder.push_hit_test(
rect,
clip_chain_id,
space_and_clip.spatial_id,
PrimitiveFlags::default(),
(0, 5),
);
// We render to ensure that the hit tester is up to date with the current scene. self.rx.recv().unwrap(); self.wrench.render();
let hit_test = |point: WorldPoint| -> HitTestResult { self.wrench.api.hit_test( self.wrench.document_id,
point,
)
};
let assert_hit_test = |point: WorldPoint, tags: Vec<ItemTag>| { let result = hit_test(point);
assert_eq!(result.items.len(), tags.len());
for (hit_test_item, item_b) in result.items.iter().zip(tags.iter()) {
assert_eq!(hit_test_item.tag, *item_b);
}
};
// We should not have any hits outside the boundaries of the scene.
assert_hit_test(WorldPoint::new(-10., -10.), Vec::new());
assert_hit_test(WorldPoint::new(-10., 10.), Vec::new());
assert_hit_test(WorldPoint::new(450., 450.), Vec::new());
assert_hit_test(WorldPoint::new(100., 450.), Vec::new());
// The top left corner of the scene should only contain the background.
assert_hit_test(WorldPoint::new(50., 50.), vec![(0, 1)]);
// The middle of the normal rectangle should be hit.
assert_hit_test(WorldPoint::new(150., 50.), vec![(0, 2), (0, 1)]);
let test_rounded_rectangle = |point: WorldPoint, size: WorldSize, tag: ItemTag| { // The cut out corners of the rounded rectangle should not be hit. let top_left = point + WorldVector2D::new(5., 5.); let bottom_right = point + size.to_vector() - WorldVector2D::new(5., 5.);
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.