--- a/gfx/webrender/examples/alpha_perf.rs
+++ b/gfx/webrender/examples/alpha_perf.rs
@@ -18,17 +18,17 @@ struct App {
rect_count: usize,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
- _resources: &mut ResourceUpdates,
+ _txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(1920, 1080);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -29,17 +29,17 @@ struct App {
opacity: f32,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
- _resources: &mut ResourceUpdates,
+ _txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
// Create a 200x200 stacking context with an animated transform property.
let bounds = (0, 0).to(200, 200);
let filters = vec![
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -177,17 +177,17 @@ struct App {
impl Example for App {
// Make this the only example to test all shaders for compile errors.
const PRECACHE_SHADERS: bool = true;
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- resources: &mut ResourceUpdates,
+ txn: &mut Transaction,
_: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
@@ -196,17 +196,17 @@ impl Example for App {
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
let image_mask_key = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
image_mask_key,
ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
ImageData::new(vec![0, 80, 180, 255]),
None,
);
let mask = ImageMask {
image: image_mask_key,
rect: (75, 75).by(100, 100),
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -11,17 +11,17 @@ extern crate webrender;
mod boilerplate;
use boilerplate::{Example, HandyDandyRectBuilder};
use rayon::{ThreadPool, ThreadPoolBuilder};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
-use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, ResourceUpdates};
+use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction};
// This example shows how to implement a very basic BlobImageRenderer that can only render
// a checkerboard pattern.
// The deserialized command list internally used by this example is just a color.
type ImageRenderingCommands = api::ColorU;
// Serialize/deserialize the blob.
@@ -219,31 +219,31 @@ impl api::BlobImageRenderer for Checkerb
struct App {}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- resources: &mut ResourceUpdates,
+ txn: &mut Transaction,
_framebuffer_size: api::DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let blob_img1 = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img1,
api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
Some(128),
);
let blob_img2 = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img2,
api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
let info = api::LayoutPrimitiveInfo::new(bounds);
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -66,17 +66,17 @@ pub trait Example {
const PRECACHE_SHADERS: bool = false;
const WIDTH: u32 = 1920;
const HEIGHT: u32 = 1080;
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- resources: &mut ResourceUpdates,
+ txn: &mut Transaction,
framebuffer_size: DeviceUintSize,
pipeline_id: PipelineId,
document_id: DocumentId,
);
fn on_event(&mut self, glutin::WindowEvent, &RenderApi, DocumentId) -> bool {
false
}
fn get_image_handlers(
@@ -164,35 +164,33 @@ pub fn main_wrapper<E: Example>(
if let Some(external_image_handler) = external {
renderer.set_external_image_handler(external_image_handler);
}
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
example.render(
&api,
&mut builder,
- &mut resources,
+ &mut txn,
framebuffer_size,
pipeline_id,
document_id,
);
- let mut txn = Transaction::new();
txn.set_display_list(
epoch,
None,
layout_size,
builder.finalize(),
true,
);
- txn.update_resources(resources);
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
api.send_transaction(document_id, txn);
println!("Entering event loop");
events_loop.run_forever(|global_event| {
let mut txn = Transaction::new();
let mut custom_event = true;
@@ -246,34 +244,32 @@ pub fn main_wrapper<E: Example>(
},
},
glutin::Event::WindowEvent { event, .. } => custom_event = example.on_event(event, &api, document_id),
_ => return glutin::ControlFlow::Continue,
};
if custom_event {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
- let mut resources = ResourceUpdates::new();
example.render(
&api,
&mut builder,
- &mut resources,
+ &mut txn,
framebuffer_size,
pipeline_id,
document_id,
);
txn.set_display_list(
epoch,
None,
layout_size,
builder.finalize(),
true,
);
- txn.update_resources(resources);
txn.generate_frame();
}
api.send_transaction(document_id, txn);
renderer.update();
renderer.render(framebuffer_size).unwrap();
let _ = renderer.flush_pipeline_info();
example.draw_custom(&*gl);
--- a/gfx/webrender/examples/document.rs
+++ b/gfx/webrender/examples/document.rs
@@ -82,17 +82,17 @@ impl App {
}
}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
base_builder: &mut DisplayListBuilder,
- _: &mut ResourceUpdates,
+ _txn: &mut Transaction,
framebuffer_size: DeviceUintSize,
_: PipelineId,
_: DocumentId,
) {
if self.documents.is_empty() {
let device_pixel_ratio = framebuffer_size.width as f32 /
base_builder.content_size().width;
// this is the first run, hack around the boilerplate,
--- a/gfx/webrender/examples/frame_output.rs
+++ b/gfx/webrender/examples/frame_output.rs
@@ -62,18 +62,18 @@ impl App {
fn init_output_document(
&mut self,
api: &RenderApi,
framebuffer_size: DeviceUintSize,
device_pixel_ratio: f32,
) {
// Generate the external image key that will be used to render the output document to the root document.
self.external_image_key = Some(api.generate_image_key());
- let mut resources = ResourceUpdates::new();
- resources.add_image(
+ let mut txn = Transaction::new();
+ txn.add_image(
self.external_image_key.unwrap(),
ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
}),
None,
@@ -107,20 +107,18 @@ impl App {
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
builder.pop_stacking_context();
- let mut txn = Transaction::new();
txn.set_root_pipeline(pipeline_id);
txn.enable_frame_output(document.pipeline_id, true);
- txn.update_resources(resources);
txn.set_display_list(
Epoch(0),
Some(document.color),
document.content_rect.size,
builder.finalize(),
true,
);
txn.generate_frame();
@@ -129,17 +127,17 @@ impl App {
}
}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- _resources: &mut ResourceUpdates,
+ _txn: &mut Transaction,
framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
if self.output_document.is_none() {
let device_pixel_ratio = framebuffer_size.width as f32 /
builder.content_size().width;
self.init_output_document(api, DeviceUintSize::new(200, 200), device_pixel_ratio);
--- a/gfx/webrender/examples/iframe.rs
+++ b/gfx/webrender/examples/iframe.rs
@@ -18,17 +18,17 @@ use webrender::api::*;
struct App {}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- _resources: &mut ResourceUpdates,
+ _txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
pipeline_id: PipelineId,
document_id: DocumentId,
) {
// All the sub_* things are for the nested pipeline
let sub_size = DeviceUintSize::new(100, 100);
let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
--- a/gfx/webrender/examples/image_resize.rs
+++ b/gfx/webrender/examples/image_resize.rs
@@ -18,23 +18,23 @@ struct App {
image_key: ImageKey,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
- resources: &mut ResourceUpdates,
+ txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32);
- resources.add_image(
+ txn.add_image(
self.image_key,
image_descriptor,
image_data,
None,
);
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
@@ -94,25 +94,24 @@ impl Example for App {
for y in 0 .. 64 {
for x in 0 .. 64 {
let r = 255 * ((y & 32) == 0) as u8;
let g = 255 * ((x & 32) == 0) as u8;
image_data.extend_from_slice(&[0, g, r, 0xff]);
}
}
- let mut updates = ResourceUpdates::new();
- updates.update_image(
+ let mut txn = Transaction::new();
+ txn.update_image(
self.image_key,
ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false),
ImageData::new(image_data),
None,
);
let mut txn = Transaction::new();
- txn.update_resources(updates);
txn.generate_frame();
api.send_transaction(document_id, txn);
}
_ => {}
}
false
}
--- a/gfx/webrender/examples/multiwindow.rs
+++ b/gfx/webrender/examples/multiwindow.rs
@@ -97,27 +97,25 @@ impl Window {
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
let api = sender.create_api();
let document_id = api.add_document(framebuffer_size, 0);
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let font_key = api.generate_font_key();
let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
- resources.add_raw_font(font_key, font_bytes, 0);
+ txn.add_raw_font(font_key, font_bytes, 0);
let font_instance_key = api.generate_font_instance_key();
- resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
+ txn.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
- let mut txn = Transaction::new();
- txn.update_resources(resources);
api.send_transaction(document_id, txn);
Window {
events_loop,
window,
renderer,
name,
epoch,
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -18,17 +18,17 @@ struct App {
cursor_position: WorldPoint,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
- _resources: &mut ResourceUpdates,
+ _txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let info = LayoutPrimitiveInfo::new(
LayoutRect::new(LayoutPoint::zero(), builder.content_size())
);
builder.push_stacking_context(
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ b/gfx/webrender/examples/texture_cache_stress.rs
@@ -78,17 +78,17 @@ struct App {
swap_index: usize,
}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- resources: &mut ResourceUpdates,
+ txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
@@ -105,25 +105,25 @@ impl Example for App {
let y0 = 50.0;
let image_size = LayoutSize::new(4.0, 4.0);
if self.swap_keys.is_empty() {
let key0 = api.generate_image_key();
let key1 = api.generate_image_key();
self.image_generator.generate_image(128);
- resources.add_image(
+ txn.add_image(
key0,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.image_generator.generate_image(128);
- resources.add_image(
+ txn.add_image(
key1,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.swap_keys.push(key0);
self.swap_keys.push(key1);
@@ -195,100 +195,100 @@ impl Example for App {
glutin::WindowEvent::KeyboardInput {
input: glutin::KeyboardInput {
state: glutin::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => {
- let mut updates = ResourceUpdates::new();
+ let mut txn = Transaction::new();
match key {
glutin::VirtualKeyCode::S => {
self.stress_keys.clear();
for _ in 0 .. 16 {
for _ in 0 .. 16 {
let size = 4;
let image_key = api.generate_image_key();
self.image_generator.generate_image(size);
- updates.add_image(
+ txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.stress_keys.push(image_key);
}
}
}
glutin::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
- updates.delete_image(image_key);
+ txn.delete_image(image_key);
},
glutin::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
let size = 128;
self.image_generator.generate_image(size);
- updates.update_image(
+ txn.update_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
},
glutin::VirtualKeyCode::E => {
if let Some(image_key) = self.image_key.take() {
- updates.delete_image(image_key);
+ txn.delete_image(image_key);
}
let size = 32;
let image_key = api.generate_image_key();
let image_data = ExternalImageData {
id: ExternalImageId(0),
channel_index: size as u8,
image_type: ExternalImageType::Buffer,
};
- updates.add_image(
+ txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::External(image_data),
None,
);
self.image_key = Some(image_key);
}
glutin::VirtualKeyCode::R => {
if let Some(image_key) = self.image_key.take() {
- updates.delete_image(image_key);
+ txn.delete_image(image_key);
}
let image_key = api.generate_image_key();
let size = 32;
self.image_generator.generate_image(size);
- updates.add_image(
+ txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.image_key = Some(image_key);
}
_ => {}
}
- api.update_resources(updates);
+ api.update_resources(txn.resource_updates);
return true;
}
_ => {}
}
false
}
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -73,17 +73,17 @@ impl webrender::ExternalImageHandler for
struct App {
}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
- resources: &mut ResourceUpdates,
+ txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
@@ -95,53 +95,53 @@ impl Example for App {
Vec::new(),
GlyphRasterSpace::Screen,
);
let yuv_chanel1 = api.generate_image_key();
let yuv_chanel2 = api.generate_image_key();
let yuv_chanel2_1 = api.generate_image_key();
let yuv_chanel3 = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
yuv_chanel1,
ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
- resources.add_image(
+ txn.add_image(
yuv_chanel2,
ImageDescriptor::new(100, 100, ImageFormat::RG8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(1),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
- resources.add_image(
+ txn.add_image(
yuv_chanel2_1,
ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(2),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
- resources.add_image(
+ txn.add_image(
yuv_chanel3,
ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(3),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -43,17 +43,17 @@ ImageBrushData fetch_image_data(int addr
}
#ifdef WR_FEATURE_ALPHA_PASS
vec2 transform_point_snapped(
vec2 local_pos,
RectWithSize local_rect,
mat4 transform
) {
- vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect);
+ vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect, vec2(0.5));
vec4 world_pos = transform * vec4(local_pos, 0.0, 1.0);
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
return device_pos + snap_offset;
}
#endif
void brush_vs(
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -62,17 +62,18 @@ ClipVertexInfo write_clip_tile_vertex(Re
local_clip_rect
);
vec2 snap_offsets = compute_snap_offset_impl(
device_pos,
scroll_node.transform,
local_clip_rect,
RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
- snap_positions
+ snap_positions,
+ vec2(0.5)
);
actual_pos -= snap_offsets;
}
vec4 node_pos;
// Select the local position, based on whether we are rasterizing this
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_border_segment.glsl
@@ -0,0 +1,127 @@
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#include shared,prim_shared,ellipse,shared_border
+
+flat varying vec4 vColor0;
+flat varying vec4 vColor1;
+flat varying vec4 vColorLine;
+flat varying int vFeatures;
+flat varying vec2 vClipCenter;
+flat varying vec4 vClipRadii;
+flat varying vec2 vClipSign;
+
+varying vec2 vPos;
+
+#define CLIP_RADII 1
+#define MIX_COLOR 2
+
+#ifdef WR_VERTEX_SHADER
+
+in vec2 aTaskOrigin;
+in vec4 aRect;
+in vec4 aColor0;
+in vec4 aColor1;
+in int aFlags;
+in vec2 aWidths;
+in vec2 aRadii;
+
+#define SEGMENT_TOP_LEFT 0
+#define SEGMENT_TOP_RIGHT 1
+#define SEGMENT_BOTTOM_RIGHT 2
+#define SEGMENT_BOTTOM_LEFT 3
+#define SEGMENT_LEFT 4
+#define SEGMENT_TOP 5
+#define SEGMENT_RIGHT 6
+#define SEGMENT_BOTTOM 7
+
+vec2 get_outer_corner_scale(int segment) {
+ vec2 p;
+
+ switch (segment) {
+ case SEGMENT_TOP_LEFT:
+ p = vec2(0.0, 0.0);
+ break;
+ case SEGMENT_TOP_RIGHT:
+ p = vec2(1.0, 0.0);
+ break;
+ case SEGMENT_BOTTOM_RIGHT:
+ p = vec2(1.0, 1.0);
+ break;
+ case SEGMENT_BOTTOM_LEFT:
+ p = vec2(0.0, 1.0);
+ break;
+ default:
+ // Should never get hit
+ p = vec2(0.0);
+ break;
+ }
+
+ return p;
+}
+
+void main(void) {
+ vec2 pos = aRect.xy + aRect.zw * aPosition.xy;
+
+ int segment = aFlags & 0xff;
+ int style = (aFlags >> 8) & 0xff;
+
+ vec2 outer_scale = get_outer_corner_scale(segment);
+ vec2 outer = aRect.xy + outer_scale * aRect.zw;
+ vec2 clip_sign = 1.0 - 2.0 * outer_scale;
+
+ vColor0 = aColor0;
+ vColor1 = aColor1;
+ vPos = pos;
+
+ vFeatures = 0;
+ vClipSign = clip_sign;
+ vClipCenter = outer + clip_sign * aRadii;
+ vClipRadii = vec4(aRadii, aRadii - aWidths);
+ vColorLine = vec4(0.0);
+
+ switch (segment) {
+ case SEGMENT_TOP_LEFT:
+ case SEGMENT_TOP_RIGHT:
+ case SEGMENT_BOTTOM_RIGHT:
+ case SEGMENT_BOTTOM_LEFT:
+ vFeatures |= (CLIP_RADII | MIX_COLOR);
+ vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
+ break;
+ default:
+ break;
+ }
+
+ gl_Position = uTransform * vec4(aTaskOrigin + pos, 0.0, 1.0);
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+void main(void) {
+ float aa_range = compute_aa_range(vPos);
+ float d = -1.0;
+
+ // Apply color mix
+ float mix_factor = 0.0;
+ if ((vFeatures & MIX_COLOR) != 0) {
+ float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
+ mix_factor = distance_aa(aa_range, -d_line);
+ }
+
+ // Apply clip radii
+ if ((vFeatures & CLIP_RADII) != 0) {
+ vec2 p = vPos - vClipCenter;
+ if (vClipSign.x * p.x < 0.0 && vClipSign.y * p.y < 0.0) {
+ float d_radii_a = distance_to_ellipse(p, vClipRadii.xy, aa_range);
+ float d_radii_b = distance_to_ellipse(p, vClipRadii.zw, aa_range);
+ float d_radii = max(d_radii_a, -d_radii_b);
+ d = max(d, d_radii);
+ }
+ }
+
+ float alpha = distance_aa(aa_range, d);
+ vec4 color = mix(vColor0, vColor1, mix_factor);
+ oFragColor = color * alpha;
+}
+#endif
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -5,16 +5,17 @@
#include rect,clip_scroll,render_task,resource_cache,snap,transform
#define EXTEND_MODE_CLAMP 0
#define EXTEND_MODE_REPEAT 1
#define SUBPX_DIR_NONE 0
#define SUBPX_DIR_HORIZONTAL 1
#define SUBPX_DIR_VERTICAL 2
+#define SUBPX_DIR_MIXED 3
#define RASTER_LOCAL 0
#define RASTER_SCREEN 1
uniform sampler2DArray sCacheA8;
uniform sampler2DArray sCacheRGBA8;
// An A8 target for standalone tasks that is available to all passes.
@@ -63,46 +64,27 @@ RectWithSize fetch_clip_chain_rect(int i
return RectWithSize(rect.xy, rect.zw);
}
struct Glyph {
vec2 offset;
};
Glyph fetch_glyph(int specific_prim_address,
- int glyph_index,
- int subpx_dir) {
+ int glyph_index) {
// Two glyphs are packed in each texel in the GPU cache.
int glyph_address = specific_prim_address +
VECS_PER_TEXT_RUN +
glyph_index / 2;
vec4 data = fetch_from_resource_cache_1(glyph_address);
// Select XY or ZW based on glyph index.
// We use "!= 0" instead of "== 1" here in order to work around a driver
// bug with equality comparisons on integers.
vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 != 0));
- // In subpixel mode, the subpixel offset has already been
- // accounted for while rasterizing the glyph.
- switch (subpx_dir) {
- case SUBPX_DIR_NONE:
- break;
- case SUBPX_DIR_HORIZONTAL:
- // Glyphs positioned [-0.125, 0.125] get a
- // subpx position of zero. So include that
- // offset in the glyph position to ensure
- // we round to the correct whole position.
- glyph.x = floor(glyph.x + 0.125);
- break;
- case SUBPX_DIR_VERTICAL:
- glyph.y = floor(glyph.y + 0.125);
- break;
- default: break;
- }
-
return Glyph(glyph);
}
struct PrimitiveInstance {
int prim_address;
int specific_prim_address;
int render_task_index;
int clip_task_index;
@@ -226,17 +208,18 @@ VertexInfo write_vertex(RectWithSize ins
// Clamp to the two local clip rects.
vec2 clamped_local_pos = clamp_rect(local_pos, local_clip_rect);
/// Compute the snapping offset.
vec2 snap_offset = compute_snap_offset(
clamped_local_pos,
scroll_node.transform,
- snap_rect
+ snap_rect,
+ vec2(0.5)
);
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -15,39 +15,45 @@ varying vec4 vUvClip;
#ifdef WR_VERTEX_SHADER
VertexInfo write_text_vertex(vec2 clamped_local_pos,
RectWithSize local_clip_rect,
float z,
ClipScrollNode scroll_node,
PictureTask task,
- RectWithSize snap_rect) {
+ RectWithSize snap_rect,
+ vec2 snap_bias) {
+#if defined(WR_FEATURE_GLYPH_TRANSFORM) || !defined(WR_FEATURE_TRANSFORM)
+ // Ensure the transform does not contain a subpixel translation to ensure
+ // that glyph snapping is stable for equivalent glyph subpixel positions.
+ scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5);
+#endif
+
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
// Apply offsets for the render task to get correct screen location.
vec2 final_pos = device_pos -
task.content_origin +
task.common_data.task_rect.p0;
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// For transformed subpixels, we just need to align the glyph origin to a device pixel.
- // Only check the scroll node transform's translation since the scales and axes match.
- vec2 world_snap_p0 = snap_rect.p0 + scroll_node.transform[3].xy * uDevicePixelRatio;
- final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0;
+ final_pos += floor(snap_rect.p0 + snap_bias) - snap_rect.p0;
#elif !defined(WR_FEATURE_TRANSFORM)
// Compute the snapping offset only if the scroll node transform is axis-aligned.
final_pos += compute_snap_offset(
clamped_local_pos,
scroll_node.transform,
- snap_rect
+ snap_rect,
+ snap_bias
);
#endif
gl_Position = uTransform * vec4(final_pos, z, 1.0);
VertexInfo vi = VertexInfo(
clamped_local_pos,
device_pos,
@@ -66,19 +72,17 @@ void main(void) {
int resource_address = prim.user_data1;
int subpx_dir = prim.user_data2 >> 16;
int color_mode = prim.user_data2 & 0xffff;
if (color_mode == COLOR_MODE_FROM_PASS) {
color_mode = uMode;
}
- Glyph glyph = fetch_glyph(prim.specific_prim_address,
- glyph_index,
- subpx_dir);
+ Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
GlyphResource res = fetch_glyph_resource(resource_address);
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// Transform from local space to glyph space.
mat2 transform = mat2(prim.scroll_node.transform) * uDevicePixelRatio;
// Compute the glyph rect in glyph space.
RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset),
@@ -107,22 +111,48 @@ void main(void) {
// Select the corner of the glyph rect that we are processing.
vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy;
// Clamp to the local clip rect.
local_pos = clamp_rect(local_pos, prim.local_clip_rect);
#endif
+ vec2 snap_bias;
+ // In subpixel mode, the subpixel offset has already been
+ // accounted for while rasterizing the glyph. However, we
+ // must still round with a subpixel bias rather than rounding
+ // to the nearest whole pixel, depending on subpixel direciton.
+ switch (subpx_dir) {
+ case SUBPX_DIR_NONE:
+ default:
+ snap_bias = vec2(0.5);
+ break;
+ case SUBPX_DIR_HORIZONTAL:
+ // Glyphs positioned [-0.125, 0.125] get a
+ // subpx position of zero. So include that
+ // offset in the glyph position to ensure
+ // we round to the correct whole position.
+ snap_bias = vec2(0.125, 0.5);
+ break;
+ case SUBPX_DIR_VERTICAL:
+ snap_bias = vec2(0.5, 0.125);
+ break;
+ case SUBPX_DIR_MIXED:
+ snap_bias = vec2(0.125);
+ break;
+ }
+
VertexInfo vi = write_text_vertex(local_pos,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
- glyph_rect);
+ glyph_rect,
+ snap_bias);
#ifdef WR_FEATURE_GLYPH_TRANSFORM
vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size;
vUvClip = vec4(f, 1.0 - f);
#else
vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size;
#endif
--- a/gfx/webrender/res/snap.glsl
+++ b/gfx/webrender/res/snap.glsl
@@ -22,42 +22,45 @@ vec4 compute_snap_positions(mat4 transfo
return world_snap;
}
vec2 compute_snap_offset_impl(
vec2 reference_pos,
mat4 transform,
RectWithSize snap_rect,
RectWithSize reference_rect,
- vec4 snap_positions) {
+ vec4 snap_positions,
+ vec2 snap_bias) {
/// World offsets applied to the corners of the snap rectangle.
- vec4 snap_offsets = floor(snap_positions + 0.5) - snap_positions;
+ vec4 snap_offsets = floor(snap_positions + snap_bias.xyxy) - snap_positions;
/// Compute the position of this vertex inside the snap rectangle.
vec2 normalized_snap_pos = (reference_pos - reference_rect.p0) / reference_rect.size;
/// Compute the actual world offset for this vertex needed to make it snap.
return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
}
// Compute a snapping offset in world space (adjusted to pixel ratio),
// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
mat4 transform,
- RectWithSize snap_rect) {
+ RectWithSize snap_rect,
+ vec2 snap_bias) {
vec4 snap_positions = compute_snap_positions(
transform,
snap_rect
);
vec2 snap_offsets = compute_snap_offset_impl(
local_pos,
transform,
snap_rect,
snap_rect,
- snap_positions
+ snap_positions,
+ snap_bias
);
return snap_offsets;
}
#endif //WR_VERTEX_SHADER
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,15 +1,15 @@
/* 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 http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering, LayoutRect};
-use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
+use api::{DeviceIntPoint, YuvColorSpace, YuvFormat};
use api::{LayoutToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress};
use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex, ClipMaskBorderCornerDotDash};
@@ -17,17 +17,17 @@ use gpu_types::{ClipMaskInstance, ClipSc
use gpu_types::{PrimitiveInstance, RasterizationSpace, SimplePrimitiveInstance, ZBufferId};
use gpu_types::ZBufferIdGenerator;
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, CachedGradient, DeferredResolve};
use prim_store::{EdgeAaSegmentMask, ImageSource, PictureIndex, PrimitiveIndex, PrimitiveKind};
use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore, VisibleGradientTile};
-use prim_store::CachedGradientIndex;
+use prim_store::{BorderSource, CachedGradientIndex};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::{BLOCKS_PER_UV_RECT, ShaderColorMode};
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use scene::FilterOpHelpers;
use std::{usize, f32, i32};
use tiling::{RenderTargetContext};
use util::{MatrixHelpers, TransformedRectKind};
@@ -1148,16 +1148,17 @@ impl AlphaBatchBuilder {
PrimitiveKind::TextRun => {
let text_cpu =
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
let font = text_cpu.get_font(
ctx.device_pixel_scale,
Some(scroll_node.transform),
);
+ let subpx_dir = font.get_subpx_dir();
let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
let batch_list = &mut self.batch_list;
ctx.resource_cache.fetch_glyphs(
font,
&text_cpu.glyph_keys,
glyph_fetch_buffer,
@@ -1165,21 +1166,17 @@ impl AlphaBatchBuilder {
|texture_id, mut glyph_format, glyphs| {
debug_assert_ne!(texture_id, SourceTexture::Invalid);
// Ignore color and only sample alpha when shadowing.
if text_cpu.shadow {
glyph_format = glyph_format.ignore_color();
}
- let subpx_dir = match glyph_format {
- GlyphFormat::Bitmap |
- GlyphFormat::ColorBitmap => SubpixelDirection::None,
- _ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
- };
+ let subpx_dir = subpx_dir.limit_by(glyph_format);
let textures = BatchTextures {
colors: [
texture_id,
SourceTexture::Invalid,
SourceTexture::Invalid,
],
};
@@ -1519,23 +1516,35 @@ impl BrushPrimitive {
cache_item.uv_rect_handle.as_int(gpu_cache),
(ShaderColorMode::ColorBitmap as i32) << 16|
RasterizationSpace::Local as i32,
0,
],
))
}
}
- BrushKind::Border { request, .. } => {
- let cache_item = resolve_image(
- request,
- resource_cache,
- gpu_cache,
- deferred_resolves,
- );
+ BrushKind::Border { ref source, .. } => {
+ let cache_item = match *source {
+ BorderSource::Image(request) => {
+ resolve_image(
+ request,
+ resource_cache,
+ gpu_cache,
+ deferred_resolves,
+ )
+ }
+ BorderSource::Border { ref handle, .. } => {
+ let rt_handle = handle
+ .as_ref()
+ .expect("bug: render task handle not allocated");
+ let rt_cache_entry = resource_cache
+ .get_cached_render_task(rt_handle);
+ resource_cache.get_texture_cache_item(&rt_cache_entry.handle)
+ }
+ };
if cache_item.texture_id == SourceTexture::Invalid {
None
} else {
let textures = BatchTextures::color(cache_item.texture_id);
Some((
BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,22 +1,24 @@
/* 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 http://mozilla.org/MPL/2.0/. */
-use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipMode, ColorF, LayoutPoint};
-use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, NormalBorder};
+use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayoutPoint};
+use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
+use api::{DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
+use app_units::Au;
use clip::ClipSource;
use ellipse::Ellipse;
use display_list_flattener::DisplayListFlattener;
-use gpu_types::BrushFlags;
+use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
use gpu_cache::GpuDataRequest;
-use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor};
-use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
-use util::{lerp, pack_as_float};
+use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentDescriptor};
+use prim_store::{BorderSource, EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
+use util::{lerp, pack_as_float, RectHelpers};
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BorderCornerInstance {
None,
Single, // Single instance needed - corner styles are same or similar.
Double, // Different corner styles. Draw two instances, one per style.
}
@@ -31,16 +33,106 @@ pub enum BorderCornerSide {
#[repr(C)]
enum BorderCorner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
+trait AuSizeConverter {
+ fn to_au(&self) -> LayoutSizeAu;
+}
+
+impl AuSizeConverter for LayoutSize {
+ fn to_au(&self) -> LayoutSizeAu {
+ LayoutSizeAu::new(
+ Au::from_f32_px(self.width),
+ Au::from_f32_px(self.height),
+ )
+ }
+}
+
+// TODO(gw): Perhaps there is a better way to store
+// the border cache key than duplicating
+// all the border structs with hashable
+// variants...
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderRadiusAu {
+ pub top_left: LayoutSizeAu,
+ pub top_right: LayoutSizeAu,
+ pub bottom_left: LayoutSizeAu,
+ pub bottom_right: LayoutSizeAu,
+}
+
+impl From<BorderRadius> for BorderRadiusAu {
+ fn from(radius: BorderRadius) -> BorderRadiusAu {
+ BorderRadiusAu {
+ top_left: radius.top_left.to_au(),
+ top_right: radius.top_right.to_au(),
+ bottom_right: radius.bottom_right.to_au(),
+ bottom_left: radius.bottom_left.to_au(),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderWidthsAu {
+ pub left: Au,
+ pub top: Au,
+ pub right: Au,
+ pub bottom: Au,
+}
+
+impl From<BorderWidths> for BorderWidthsAu {
+ fn from(widths: BorderWidths) -> Self {
+ BorderWidthsAu {
+ left: Au::from_f32_px(widths.left),
+ top: Au::from_f32_px(widths.top),
+ right: Au::from_f32_px(widths.right),
+ bottom: Au::from_f32_px(widths.bottom),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderSideAu {
+ pub color: ColorU,
+ pub style: BorderStyle,
+}
+
+impl From<BorderSide> for BorderSideAu {
+ fn from(side: BorderSide) -> Self {
+ BorderSideAu {
+ color: side.color.into(),
+ style: side.style,
+ }
+ }
+}
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderCacheKey {
+ pub left: BorderSideAu,
+ pub right: BorderSideAu,
+ pub top: BorderSideAu,
+ pub bottom: BorderSideAu,
+ pub radius: BorderRadiusAu,
+ pub widths: BorderWidthsAu,
+ pub scale: Au,
+}
+
#[derive(Clone, Debug, PartialEq)]
pub enum BorderCornerKind {
None,
Solid,
Clip(BorderCornerInstance),
Mask(
BorderCornerClipData,
LayoutSize,
@@ -369,70 +461,64 @@ impl<'a> DisplayListFlattener<'a> {
ensure_no_corner_overlap(&mut border.radius, &info.rect);
let radius = &border.radius;
let left = &border.left;
let right = &border.right;
let top = &border.top;
let bottom = &border.bottom;
- let constant_color = left.color;
- let is_simple_border = [left, top, right, bottom].iter().all(|edge| {
- edge.style == BorderStyle::Solid &&
- edge.color == constant_color
+ let brush_border_supported = [left, top, right, bottom].iter().all(|edge| {
+ match edge.style {
+ BorderStyle::Solid |
+ BorderStyle::Hidden |
+ BorderStyle::None |
+ BorderStyle::Inset |
+ BorderStyle::Outset => {
+ true
+ }
+
+ BorderStyle::Double |
+ BorderStyle::Dotted |
+ BorderStyle::Dashed |
+ BorderStyle::Groove |
+ BorderStyle::Ridge => {
+ false
+ }
+ }
});
- if is_simple_border {
- let extra_clips = vec![
- ClipSource::new_rounded_rect(
- info.rect,
- border.radius,
- ClipMode::Clip,
- ),
- ClipSource::new_rounded_rect(
- LayoutRect::new(
- LayoutPoint::new(
- info.rect.origin.x + widths.left,
- info.rect.origin.y + widths.top,
- ),
- LayoutSize::new(
- info.rect.size.width - widths.left - widths.right,
- info.rect.size.height - widths.top - widths.bottom,
- ),
- ),
- BorderRadius {
- top_left: LayoutSize::new(
- (border.radius.top_left.width - widths.left).max(0.0),
- (border.radius.top_left.height - widths.top).max(0.0),
- ),
- top_right: LayoutSize::new(
- (border.radius.top_right.width - widths.right).max(0.0),
- (border.radius.top_right.height - widths.top).max(0.0),
- ),
- bottom_left: LayoutSize::new(
- (border.radius.bottom_left.width - widths.left).max(0.0),
- (border.radius.bottom_left.height - widths.bottom).max(0.0),
- ),
- bottom_right: LayoutSize::new(
- (border.radius.bottom_right.width - widths.right).max(0.0),
- (border.radius.bottom_right.height - widths.bottom).max(0.0),
- ),
+ if brush_border_supported {
+ let prim = BrushPrimitive::new(
+ BrushKind::Border {
+ source: BorderSource::Border {
+ border,
+ widths: *widths,
+ cache_key: BorderCacheKey {
+ left: border.left.into(),
+ top: border.top.into(),
+ right: border.right.into(),
+ bottom: border.bottom.into(),
+ widths: (*widths).into(),
+ radius: border.radius.into(),
+ scale: Au::from_f32_px(0.0),
+ },
+ task_info: None,
+ handle: None,
},
- ClipMode::ClipOut,
- ),
- ];
+ },
+ None,
+ );
- self.add_solid_rectangle(
+ self.add_primitive(
clip_and_scroll,
info,
- border.top.color,
- None,
- extra_clips,
+ Vec::new(),
+ PrimitiveContainer::Brush(prim),
);
-
return;
}
let corners = [
get_corner(
left,
widths.left,
top,
@@ -500,18 +586,17 @@ impl<'a> DisplayListFlattener<'a> {
);
let p2 = LayoutPoint::new(
info.rect.origin.x + info.rect.size.width - right_len,
info.rect.origin.y + info.rect.size.height - bottom_len,
);
let p3 = info.rect.bottom_right();
let segment = |x0, y0, x1, y1| BrushSegment::new(
- LayoutPoint::new(x0, y0),
- LayoutSize::new(x1-x0, y1-y0),
+ LayoutRect::from_floats(x0, y0, x1, y1),
true,
EdgeAaSegmentMask::all(), // Note: this doesn't seem right, needs revision
[0.0; 4],
BrushFlags::empty(),
);
// Add a solid rectangle for each visible edge/corner combination.
if top_edge == BorderEdgeKind::Solid {
@@ -924,8 +1009,439 @@ struct DotInfo {
diameter: f32,
}
impl DotInfo {
fn new(arc_pos: f32, diameter: f32) -> DotInfo {
DotInfo { arc_pos, diameter }
}
}
+
+#[derive(Debug)]
+pub struct BorderRenderTaskInfo {
+ pub instances: Vec<BorderInstance>,
+ pub segments: Vec<BrushSegment>,
+ pub size: DeviceIntSize,
+}
+
+impl BorderRenderTaskInfo {
+ pub fn new(
+ rect: &LayoutRect,
+ border: &NormalBorder,
+ widths: &BorderWidths,
+ scale: LayoutToDeviceScale,
+ ) -> Self {
+ let mut instances = Vec::new();
+ let mut segments = Vec::new();
+
+ let dp_width_top = (widths.top * scale.0).ceil();
+ let dp_width_bottom = (widths.bottom * scale.0).ceil();
+ let dp_width_left = (widths.left * scale.0).ceil();
+ let dp_width_right = (widths.right * scale.0).ceil();
+
+ let dp_corner_tl = (border.radius.top_left * scale).ceil();
+ let dp_corner_tr = (border.radius.top_right * scale).ceil();
+ let dp_corner_bl = (border.radius.bottom_left * scale).ceil();
+ let dp_corner_br = (border.radius.bottom_right * scale).ceil();
+
+ let dp_size_tl = DeviceSize::new(
+ dp_corner_tl.width.max(dp_width_left),
+ dp_corner_tl.height.max(dp_width_top),
+ );
+ let dp_size_tr = DeviceSize::new(
+ dp_corner_tr.width.max(dp_width_right),
+ dp_corner_tr.height.max(dp_width_top),
+ );
+ let dp_size_br = DeviceSize::new(
+ dp_corner_br.width.max(dp_width_right),
+ dp_corner_br.height.max(dp_width_bottom),
+ );
+ let dp_size_bl = DeviceSize::new(
+ dp_corner_bl.width.max(dp_width_left),
+ dp_corner_bl.height.max(dp_width_bottom),
+ );
+
+ let local_size_tl = LayoutSize::new(
+ border.radius.top_left.width.max(widths.left),
+ border.radius.top_left.height.max(widths.top),
+ );
+ let local_size_tr = LayoutSize::new(
+ border.radius.top_right.width.max(widths.right),
+ border.radius.top_right.height.max(widths.top),
+ );
+ let local_size_br = LayoutSize::new(
+ border.radius.bottom_right.width.max(widths.right),
+ border.radius.bottom_right.height.max(widths.bottom),
+ );
+ let local_size_bl = LayoutSize::new(
+ border.radius.bottom_left.width.max(widths.left),
+ border.radius.bottom_left.height.max(widths.bottom),
+ );
+
+ // TODO(gw): The inner and outer widths don't matter for simple
+ // border types. Once we push dashing and dotted styles
+ // through border brushes, we need to calculate an
+ // appropriate length here.
+ let width_inner = 16.0;
+ let height_inner = 16.0;
+
+ let size = DeviceSize::new(
+ dp_size_tl.width.max(dp_size_bl.width) + width_inner + dp_size_tr.width.max(dp_size_br.width),
+ dp_size_tl.height.max(dp_size_tr.height) + height_inner + dp_size_bl.height.max(dp_size_br.height),
+ );
+
+ // These modulate colors are not part of the specification. They
+ // are derived from the Gecko source code and experimentation, and
+ // used to modulate the colors in order to generate colors for
+ // the inset/outset and groove/ridge border styles.
+ let left_color = border.left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
+ let top_color = border.top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
+ let right_color = border.right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
+ let bottom_color = border.bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
+
+ add_edge_segment(
+ LayoutRect::from_floats(
+ rect.origin.x,
+ rect.origin.y + local_size_tl.height,
+ rect.origin.x + widths.left,
+ rect.origin.y + rect.size.height - local_size_bl.height,
+ ),
+ DeviceRect::from_floats(
+ 0.0,
+ dp_size_tl.height,
+ dp_width_left,
+ size.height - dp_size_bl.height,
+ ),
+ border.left.style,
+ left_color,
+ BorderSegment::Left,
+ EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
+ &mut instances,
+ BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
+ &mut segments,
+ );
+
+ add_edge_segment(
+ LayoutRect::from_floats(
+ rect.origin.x + local_size_tl.width,
+ rect.origin.y,
+ rect.origin.x + rect.size.width - local_size_tr.width,
+ rect.origin.y + widths.top,
+ ),
+ DeviceRect::from_floats(
+ dp_size_tl.width,
+ 0.0,
+ size.width - dp_size_tr.width,
+ dp_width_top,
+ ),
+ border.top.style,
+ top_color,
+ BorderSegment::Top,
+ EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
+ &mut instances,
+ BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
+ &mut segments,
+ );
+
+ add_edge_segment(
+ LayoutRect::from_floats(
+ rect.origin.x + rect.size.width - widths.right,
+ rect.origin.y + local_size_tr.height,
+ rect.origin.x + rect.size.width,
+ rect.origin.y + rect.size.height - local_size_br.height,
+ ),
+ DeviceRect::from_floats(
+ size.width - dp_width_right,
+ dp_size_tr.height,
+ size.width,
+ size.height - dp_size_br.height,
+ ),
+ border.right.style,
+ right_color,
+ BorderSegment::Right,
+ EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
+ &mut instances,
+ BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
+ &mut segments,
+ );
+
+ add_edge_segment(
+ LayoutRect::from_floats(
+ rect.origin.x + local_size_bl.width,
+ rect.origin.y + rect.size.height - widths.bottom,
+ rect.origin.x + rect.size.width - local_size_br.width,
+ rect.origin.y + rect.size.height,
+ ),
+ DeviceRect::from_floats(
+ dp_size_bl.width,
+ size.height - dp_width_bottom,
+ size.width - dp_size_br.width,
+ size.height,
+ ),
+ border.bottom.style,
+ bottom_color,
+ BorderSegment::Bottom,
+ EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
+ &mut instances,
+ BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
+ &mut segments,
+ );
+
+ add_corner_segment(
+ LayoutRect::from_floats(
+ rect.origin.x,
+ rect.origin.y,
+ rect.origin.x + local_size_tl.width,
+ rect.origin.y + local_size_tl.height,
+ ),
+ DeviceRect::from_floats(
+ 0.0,
+ 0.0,
+ dp_size_tl.width,
+ dp_size_tl.height,
+ ),
+ border.left.style,
+ left_color,
+ border.top.style,
+ top_color,
+ DeviceSize::new(dp_width_left, dp_width_top),
+ dp_corner_tl,
+ BorderSegment::TopLeft,
+ EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
+ &mut instances,
+ &mut segments,
+ );
+
+ add_corner_segment(
+ LayoutRect::from_floats(
+ rect.origin.x + rect.size.width - local_size_tr.width,
+ rect.origin.y,
+ rect.origin.x + rect.size.width,
+ rect.origin.y + local_size_tr.height,
+ ),
+ DeviceRect::from_floats(
+ size.width - dp_size_tr.width,
+ 0.0,
+ size.width,
+ dp_size_tr.height,
+ ),
+ border.top.style,
+ top_color,
+ border.right.style,
+ right_color,
+ DeviceSize::new(dp_width_right, dp_width_top),
+ dp_corner_tr,
+ BorderSegment::TopRight,
+ EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
+ &mut instances,
+ &mut segments,
+ );
+
+ add_corner_segment(
+ LayoutRect::from_floats(
+ rect.origin.x + rect.size.width - local_size_br.width,
+ rect.origin.y + rect.size.height - local_size_br.height,
+ rect.origin.x + rect.size.width,
+ rect.origin.y + rect.size.height,
+ ),
+ DeviceRect::from_floats(
+ size.width - dp_size_br.width,
+ size.height - dp_size_br.height,
+ size.width,
+ size.height,
+ ),
+ border.right.style,
+ right_color,
+ border.bottom.style,
+ bottom_color,
+ DeviceSize::new(dp_width_right, dp_width_bottom),
+ dp_corner_br,
+ BorderSegment::BottomRight,
+ EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
+ &mut instances,
+ &mut segments,
+ );
+
+ add_corner_segment(
+ LayoutRect::from_floats(
+ rect.origin.x,
+ rect.origin.y + rect.size.height - local_size_bl.height,
+ rect.origin.x + local_size_bl.width,
+ rect.origin.y + rect.size.height,
+ ),
+ DeviceRect::from_floats(
+ 0.0,
+ size.height - dp_size_bl.height,
+ dp_size_bl.width,
+ size.height,
+ ),
+ border.bottom.style,
+ bottom_color,
+ border.left.style,
+ left_color,
+ DeviceSize::new(dp_width_left, dp_width_bottom),
+ dp_corner_bl,
+ BorderSegment::BottomLeft,
+ EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
+ &mut instances,
+ &mut segments,
+ );
+
+ BorderRenderTaskInfo {
+ segments,
+ instances,
+ size: size.to_i32(),
+ }
+ }
+}
+
+fn add_brush_segment(
+ image_rect: LayoutRect,
+ task_rect: DeviceRect,
+ brush_flags: BrushFlags,
+ edge_flags: EdgeAaSegmentMask,
+ brush_segments: &mut Vec<BrushSegment>,
+) {
+ brush_segments.push(
+ BrushSegment::new(
+ image_rect,
+ true,
+ edge_flags,
+ [
+ task_rect.origin.x,
+ task_rect.origin.y,
+ task_rect.origin.x + task_rect.size.width,
+ task_rect.origin.y + task_rect.size.height,
+ ],
+ brush_flags,
+ )
+ );
+}
+
+fn add_segment(
+ task_rect: DeviceRect,
+ style0: BorderStyle,
+ style1: BorderStyle,
+ color0: ColorF,
+ color1: ColorF,
+ segment: BorderSegment,
+ instances: &mut Vec<BorderInstance>,
+ widths: DeviceSize,
+ radius: DeviceSize,
+) {
+ let flags = (segment as i32) |
+ ((style0 as i32) << 8) |
+ ((style1 as i32) << 16);
+
+ let base_instance = BorderInstance {
+ task_origin: DevicePoint::zero(),
+ local_rect: task_rect,
+ flags,
+ color0: color0.premultiplied(),
+ color1: color1.premultiplied(),
+ widths,
+ radius,
+ };
+
+ instances.push(base_instance);
+}
+
+fn add_corner_segment(
+ image_rect: LayoutRect,
+ task_rect: DeviceRect,
+ mut style0: BorderStyle,
+ color0: ColorF,
+ mut style1: BorderStyle,
+ color1: ColorF,
+ widths: DeviceSize,
+ radius: DeviceSize,
+ segment: BorderSegment,
+ edge_flags: EdgeAaSegmentMask,
+ instances: &mut Vec<BorderInstance>,
+ brush_segments: &mut Vec<BrushSegment>,
+) {
+ // TODO(gw): This will need to be a bit more involved when
+ // we support other border types here. For example,
+ // groove / ridge borders will always need to
+ // use two instances.
+
+ if color0.a <= 0.0 && color1.a <= 0.0 {
+ return;
+ }
+
+ if widths.width <= 0.0 && widths.height <= 0.0 {
+ return;
+ }
+
+ let style0_hidden = style0 == BorderStyle::Hidden || style0 == BorderStyle::None;
+ let style1_hidden = style1 == BorderStyle::Hidden || style1 == BorderStyle::None;
+
+ if style0_hidden && style1_hidden {
+ return;
+ }
+
+ if style0_hidden {
+ style0 = style1;
+ }
+ if style1_hidden {
+ style1 = style0;
+ }
+
+ add_segment(
+ task_rect,
+ style0,
+ style1,
+ color0,
+ color1,
+ segment,
+ instances,
+ widths,
+ radius,
+ );
+
+ add_brush_segment(
+ image_rect,
+ task_rect,
+ BrushFlags::SEGMENT_RELATIVE,
+ edge_flags,
+ brush_segments,
+ );
+}
+
+fn add_edge_segment(
+ image_rect: LayoutRect,
+ task_rect: DeviceRect,
+ style: BorderStyle,
+ color: ColorF,
+ segment: BorderSegment,
+ edge_flags: EdgeAaSegmentMask,
+ instances: &mut Vec<BorderInstance>,
+ brush_flags: BrushFlags,
+ brush_segments: &mut Vec<BrushSegment>,
+) {
+ if color.a <= 0.0 {
+ return;
+ }
+
+ if style == BorderStyle::Hidden || style == BorderStyle::None {
+ return;
+ }
+
+ add_segment(
+ task_rect,
+ style,
+ style,
+ color,
+ color,
+ segment,
+ instances,
+ DeviceSize::zero(),
+ DeviceSize::zero(),
+ );
+
+ add_brush_segment(
+ image_rect,
+ task_rect,
+ brush_flags,
+ edge_flags,
+ brush_segments,
+ );
+}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -22,17 +22,17 @@ use frame_builder::{FrameBuilder, FrameB
use glyph_rasterizer::FontInstance;
use gpu_types::BrushFlags;
use hit_test::{HitTestingItem, HitTestingRun};
use image::simplify_repeated_primitive;
use internal_types::{FastHashMap, FastHashSet};
use picture::PictureCompositeMode;
use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageSource};
-use prim_store::{BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
+use prim_store::{BorderSource, BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::{BuiltScene, SceneRequest};
use std::{f32, mem, usize};
use tiling::{CompositeOps, ScrollbarPrimitive};
use util::{MaxRect, RectHelpers, recycle_vec};
@@ -1631,18 +1631,17 @@ impl<'a> DisplayListFlattener<'a> {
if repeat_horizontal == RepeatMode::Repeat {
brush_flags |= BrushFlags::SEGMENT_REPEAT_X;
}
if repeat_vertical == RepeatMode::Repeat {
brush_flags |= BrushFlags::SEGMENT_REPEAT_Y;
}
let segment = BrushSegment::new(
- rect.origin,
- rect.size,
+ rect,
true,
EdgeAaSegmentMask::empty(),
[
uv_rect.uv0.x,
uv_rect.uv0.y,
uv_rect.uv1.x,
uv_rect.uv1.y,
],
@@ -1736,23 +1735,25 @@ impl<'a> DisplayListFlattener<'a> {
);
let descriptor = BrushSegmentDescriptor {
segments,
clip_mask_kind: BrushClipMaskKind::Unknown,
};
let prim = PrimitiveContainer::Brush(match border.source {
NinePatchBorderSource::Image(image_key) => {
+ let source = BorderSource::Image(ImageRequest {
+ key: image_key,
+ rendering: ImageRendering::Auto,
+ tile: None,
+ });
+
BrushPrimitive::new(
BrushKind::Border {
- request: ImageRequest {
- key: image_key,
- rendering: ImageRendering::Auto,
- tile: None,
- },
+ source
},
Some(descriptor),
)
}
});
self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
}
@@ -1961,30 +1962,28 @@ impl<'a> DisplayListFlattener<'a> {
};
let prim_font = FontInstance::new(
font_instance.font_key,
font_instance.size,
*text_color,
font_instance.bg_color,
render_mode,
- font_instance.subpx_dir,
flags,
font_instance.platform_options,
font_instance.variations.clone(),
);
- TextRunPrimitiveCpu {
- font: prim_font,
+ TextRunPrimitiveCpu::new(
+ prim_font,
+ run_offset,
glyph_range,
- glyph_gpu_blocks: Vec::new(),
- glyph_keys: Vec::new(),
- offset: run_offset,
- shadow: false,
+ Vec::new(),
+ false,
glyph_raster_space,
- }
+ )
};
self.add_primitive(
clip_and_scroll,
prim_info,
Vec::new(),
PrimitiveContainer::TextRun(prim),
);
--- a/gfx/webrender/src/glyph_cache.rs
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -1,16 +1,15 @@
/* 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 http://mozilla.org/MPL/2.0/. */
#[cfg(feature = "pathfinder")]
use api::DeviceIntPoint;
-use api::GlyphKey;
-use glyph_rasterizer::{FontInstance, GlyphFormat};
+use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey};
use internal_types::FastHashMap;
use render_task::RenderTaskCache;
#[cfg(feature = "pathfinder")]
use render_task::RenderTaskCacheKey;
use resource_cache::ResourceClassCache;
use std::sync::Arc;
use texture_cache::{EvictionNotice, TextureCache};
#[cfg(not(feature = "pathfinder"))]
--- a/gfx/webrender/src/glyph_rasterizer/mod.rs
+++ b/gfx/webrender/src/glyph_rasterizer/mod.rs
@@ -1,17 +1,19 @@
/* 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 http://mozilla.org/MPL/2.0/. */
-use api::{ColorF, ColorU};
+use api::{ColorF, ColorU, DevicePoint};
use api::{FontInstanceFlags, FontInstancePlatformOptions};
use api::{FontKey, FontRenderMode, FontTemplate, FontVariation};
-use api::{GlyphDimensions, GlyphKey, LayoutToWorldTransform, SubpixelDirection};
+use api::{GlyphIndex, GlyphDimensions};
+use api::{LayoutPoint, LayoutToWorldTransform, WorldPoint};
use app_units::Au;
+use euclid::approxeq::ApproxEq;
use internal_types::ResourceCacheError;
use platform::font::FontContext;
use rayon::ThreadPool;
use std::cmp;
use std::hash::{Hash, Hasher};
use std::mem;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::mpsc::{channel, Receiver, Sender};
@@ -122,17 +124,37 @@ impl FontTransform {
FontTransform::new(self.skew_x, self.scale_x, self.scale_y, self.skew_y)
}
pub fn flip_x(&self) -> Self {
FontTransform::new(-self.scale_x, self.skew_x, -self.skew_y, self.scale_y)
}
pub fn flip_y(&self) -> Self {
- FontTransform::new(self.scale_x, -self.skew_y, self.skew_y, -self.scale_y)
+ FontTransform::new(self.scale_x, -self.skew_x, self.skew_y, -self.scale_y)
+ }
+
+ pub fn transform(&self, point: &LayoutPoint) -> WorldPoint {
+ WorldPoint::new(
+ self.scale_x * point.x + self.skew_x * point.y,
+ self.skew_y * point.x + self.scale_y * point.y,
+ )
+ }
+
+ pub fn get_subpx_dir(&self) -> SubpixelDirection {
+ if self.skew_y.approx_eq(&0.0) {
+ // The X axis is not projected onto the Y axis
+ SubpixelDirection::Horizontal
+ } else if self.scale_x.approx_eq(&0.0) {
+ // The X axis has been swapped with the Y axis
+ SubpixelDirection::Vertical
+ } else {
+ // Use subpixel precision on all axes
+ SubpixelDirection::Mixed
+ }
}
}
impl<'a> From<&'a LayoutToWorldTransform> for FontTransform {
fn from(xform: &'a LayoutToWorldTransform) -> Self {
FontTransform::new(xform.m11, xform.m21, xform.m12, xform.m22)
}
}
@@ -146,67 +168,90 @@ pub struct FontInstance {
// It is stored as an Au since we need sub-pixel sizes, but
// can't store as a f32 due to use of this type as a hash key.
// TODO(gw): Perhaps consider having LogicalAu and DeviceAu
// or something similar to that.
pub size: Au,
pub color: ColorU,
pub bg_color: ColorU,
pub render_mode: FontRenderMode,
- pub subpx_dir: SubpixelDirection,
pub flags: FontInstanceFlags,
pub platform_options: Option<FontInstancePlatformOptions>,
pub variations: Vec<FontVariation>,
pub transform: FontTransform,
}
impl FontInstance {
pub fn new(
font_key: FontKey,
size: Au,
color: ColorF,
bg_color: ColorU,
render_mode: FontRenderMode,
- subpx_dir: SubpixelDirection,
flags: FontInstanceFlags,
platform_options: Option<FontInstancePlatformOptions>,
variations: Vec<FontVariation>,
) -> Self {
// If a background color is enabled, it only makes sense
// for it to be completely opaque.
debug_assert!(bg_color.a == 0 || bg_color.a == 255);
FontInstance {
font_key,
size,
color: color.into(),
bg_color,
render_mode,
- subpx_dir,
flags,
platform_options,
variations,
transform: FontTransform::identity(),
}
}
pub fn get_alpha_glyph_format(&self) -> GlyphFormat {
if self.transform.is_identity() { GlyphFormat::Alpha } else { GlyphFormat::TransformedAlpha }
}
pub fn get_subpixel_glyph_format(&self) -> GlyphFormat {
if self.transform.is_identity() { GlyphFormat::Subpixel } else { GlyphFormat::TransformedSubpixel }
}
+ pub fn disable_subpixel_aa(&mut self) {
+ self.render_mode = self.render_mode.limit_by(FontRenderMode::Alpha);
+ }
+
+ pub fn disable_subpixel_position(&mut self) {
+ self.flags.remove(FontInstanceFlags::SUBPIXEL_POSITION);
+ }
+
+ pub fn use_subpixel_position(&self) -> bool {
+ self.flags.contains(FontInstanceFlags::SUBPIXEL_POSITION) &&
+ self.render_mode != FontRenderMode::Mono
+ }
+
+ pub fn get_subpx_dir(&self) -> SubpixelDirection {
+ if self.use_subpixel_position() {
+ let mut subpx_dir = self.transform.get_subpx_dir();
+ if self.flags.contains(FontInstanceFlags::TRANSPOSE) {
+ subpx_dir = subpx_dir.swap_xy();
+ }
+ subpx_dir
+ } else {
+ SubpixelDirection::None
+ }
+ }
+
#[allow(dead_code)]
pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
- match self.subpx_dir {
- SubpixelDirection::None => (0.0, 0.0),
- SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
- SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
+ if self.use_subpixel_position() {
+ let (dx, dy) = glyph.subpixel_offset;
+ (dx.into(), dy.into())
+ } else {
+ (0.0, 0.0)
}
}
#[allow(dead_code)]
pub fn get_glyph_format(&self) -> GlyphFormat {
match self.render_mode {
FontRenderMode::Mono | FontRenderMode::Alpha => self.get_alpha_glyph_format(),
FontRenderMode::Subpixel => self.get_subpixel_glyph_format(),
@@ -222,16 +267,121 @@ impl FontInstance {
}
(bold_offset * x_scale).max(1.0).round() as usize
} else {
0
}
}
}
+#[repr(u32)]
+#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
+pub enum SubpixelDirection {
+ None = 0,
+ Horizontal,
+ Vertical,
+ Mixed,
+}
+
+impl SubpixelDirection {
+ // Limit the subpixel direction to what is supported by the glyph format.
+ pub fn limit_by(self, glyph_format: GlyphFormat) -> Self {
+ match glyph_format {
+ GlyphFormat::Bitmap |
+ GlyphFormat::ColorBitmap => SubpixelDirection::None,
+ _ => self,
+ }
+ }
+
+ pub fn swap_xy(self) -> Self {
+ match self {
+ SubpixelDirection::None | SubpixelDirection::Mixed => self,
+ SubpixelDirection::Horizontal => SubpixelDirection::Vertical,
+ SubpixelDirection::Vertical => SubpixelDirection::Horizontal,
+ }
+ }
+}
+
+#[repr(u8)]
+#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub enum SubpixelOffset {
+ Zero = 0,
+ Quarter = 1,
+ Half = 2,
+ ThreeQuarters = 3,
+}
+
+impl SubpixelOffset {
+ // Skia quantizes subpixel offsets into 1/4 increments.
+ // Given the absolute position, return the quantized increment
+ fn quantize(pos: f32) -> Self {
+ // Following the conventions of Gecko and Skia, we want
+ // to quantize the subpixel position, such that abs(pos) gives:
+ // [0.0, 0.125) -> Zero
+ // [0.125, 0.375) -> Quarter
+ // [0.375, 0.625) -> Half
+ // [0.625, 0.875) -> ThreeQuarters,
+ // [0.875, 1.0) -> Zero
+ // The unit tests below check for this.
+ let apos = ((pos - pos.floor()) * 8.0) as i32;
+
+ match apos {
+ 0 | 7 => SubpixelOffset::Zero,
+ 1...2 => SubpixelOffset::Quarter,
+ 3...4 => SubpixelOffset::Half,
+ 5...6 => SubpixelOffset::ThreeQuarters,
+ _ => unreachable!("bug: unexpected quantized result"),
+ }
+ }
+}
+
+impl Into<f64> for SubpixelOffset {
+ fn into(self) -> f64 {
+ match self {
+ SubpixelOffset::Zero => 0.0,
+ SubpixelOffset::Quarter => 0.25,
+ SubpixelOffset::Half => 0.5,
+ SubpixelOffset::ThreeQuarters => 0.75,
+ }
+ }
+}
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct GlyphKey {
+ pub index: u32,
+ pub subpixel_offset: (SubpixelOffset, SubpixelOffset),
+}
+
+impl GlyphKey {
+ pub fn new(
+ index: u32,
+ point: DevicePoint,
+ subpx_dir: SubpixelDirection,
+ ) -> GlyphKey {
+ let (dx, dy) = match subpx_dir {
+ SubpixelDirection::None => (0.0, 0.0),
+ SubpixelDirection::Horizontal => (point.x, 0.0),
+ SubpixelDirection::Vertical => (0.0, point.y),
+ SubpixelDirection::Mixed => (point.x, point.y),
+ };
+
+ GlyphKey {
+ index,
+ subpixel_offset: (
+ SubpixelOffset::quantize(dx),
+ SubpixelOffset::quantize(dy),
+ ),
+ }
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[allow(dead_code)]
pub enum GlyphFormat {
Alpha,
TransformedAlpha,
Subpixel,
@@ -390,21 +540,27 @@ impl GlyphRasterizer {
pub fn prepare_font(&self, font: &mut FontInstance) {
FontContext::prepare_font(font);
}
pub fn get_glyph_dimensions(
&mut self,
font: &FontInstance,
- glyph_key: &GlyphKey,
+ glyph_index: GlyphIndex,
) -> Option<GlyphDimensions> {
+ let glyph_key = GlyphKey::new(
+ glyph_index,
+ DevicePoint::zero(),
+ SubpixelDirection::None,
+ );
+
self.font_contexts
.lock_shared_context()
- .get_glyph_dimensions(font, glyph_key)
+ .get_glyph_dimensions(font, &glyph_key)
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
self.font_contexts
.lock_shared_context()
.get_glyph_index(font_key, ch)
}
@@ -449,33 +605,16 @@ impl AddFont for FontContext {
}
FontTemplate::Native(ref native_font_handle) => {
self.add_native_font(font_key, (*native_font_handle).clone());
}
}
}
}
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct GlyphRequest {
- pub key: GlyphKey,
- pub font: FontInstance,
-}
-
-impl GlyphRequest {
- pub fn new(font: &FontInstance, key: &GlyphKey) -> Self {
- GlyphRequest {
- key: key.clone(),
- font: font.clone(),
- }
- }
-}
-
#[allow(dead_code)]
pub(in glyph_rasterizer) struct GlyphRasterJob {
key: GlyphKey,
result: GlyphRasterResult,
}
#[allow(dead_code)]
pub enum GlyphRasterResult {
@@ -506,23 +645,23 @@ mod test_glyph_rasterizer {
use std::io::Read;
use texture_cache::TextureCache;
use glyph_cache::GlyphCache;
use gpu_cache::GpuCache;
use tiling::SpecialRenderPasses;
use api::DeviceIntSize;
use render_task::{RenderTaskCache, RenderTaskTree};
use profiler::TextureCacheProfileCounters;
- use api::{FontKey, FontTemplate, FontRenderMode, GlyphKey,
- IdNamespace, LayoutPoint, ColorF, ColorU, SubpixelDirection};
+ use api::{FontKey, FontTemplate, FontRenderMode,
+ IdNamespace, ColorF, ColorU, DevicePoint};
use render_backend::FrameId;
use app_units::Au;
use thread_profiler::register_thread_with_profiler;
use std::sync::Arc;
- use glyph_rasterizer::{GlyphRasterizer, FontInstance};
+ use glyph_rasterizer::{FontInstance, GlyphKey, GlyphRasterizer};
let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.start_handler(move |idx| {
register_thread_with_profiler(format!("WRWorker#{}", idx));
})
.build();
let workers = Arc::new(worker.unwrap());
@@ -545,29 +684,28 @@ mod test_glyph_rasterizer {
glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));
let font = FontInstance::new(
font_key,
Au::from_px(32),
ColorF::new(0.0, 0.0, 0.0, 1.0),
ColorU::new(0, 0, 0, 0),
FontRenderMode::Subpixel,
- SubpixelDirection::Horizontal,
Default::default(),
None,
Vec::new(),
);
+ let subpx_dir = font.get_subpx_dir();
let mut glyph_keys = Vec::with_capacity(200);
for i in 0 .. 200 {
glyph_keys.push(GlyphKey::new(
i,
- LayoutPoint::zero(),
- font.render_mode,
- font.subpx_dir,
+ DevicePoint::zero(),
+ subpx_dir,
));
}
for i in 0 .. 4 {
glyph_rasterizer.request_glyphs(
&mut glyph_cache,
font.clone(),
&glyph_keys[(50 * i) .. (50 * (i + 1))],
@@ -585,9 +723,53 @@ mod test_glyph_rasterizer {
&mut glyph_cache,
&mut TextureCache::new(4096),
&mut gpu_cache,
&mut render_task_cache,
&mut render_task_tree,
&mut TextureCacheProfileCounters::new(),
);
}
-}
\ No newline at end of file
+
+ #[test]
+ fn test_subpx_quantize() {
+ use glyph_rasterizer::SubpixelOffset;
+
+ assert_eq!(SubpixelOffset::quantize(0.0), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(-0.0), SubpixelOffset::Zero);
+
+ assert_eq!(SubpixelOffset::quantize(0.1), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.01), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.05), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.12), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.124), SubpixelOffset::Zero);
+
+ assert_eq!(SubpixelOffset::quantize(0.125), SubpixelOffset::Quarter);
+ assert_eq!(SubpixelOffset::quantize(0.2), SubpixelOffset::Quarter);
+ assert_eq!(SubpixelOffset::quantize(0.25), SubpixelOffset::Quarter);
+ assert_eq!(SubpixelOffset::quantize(0.33), SubpixelOffset::Quarter);
+ assert_eq!(SubpixelOffset::quantize(0.374), SubpixelOffset::Quarter);
+
+ assert_eq!(SubpixelOffset::quantize(0.375), SubpixelOffset::Half);
+ assert_eq!(SubpixelOffset::quantize(0.4), SubpixelOffset::Half);
+ assert_eq!(SubpixelOffset::quantize(0.5), SubpixelOffset::Half);
+ assert_eq!(SubpixelOffset::quantize(0.58), SubpixelOffset::Half);
+ assert_eq!(SubpixelOffset::quantize(0.624), SubpixelOffset::Half);
+
+ assert_eq!(SubpixelOffset::quantize(0.625), SubpixelOffset::ThreeQuarters);
+ assert_eq!(SubpixelOffset::quantize(0.67), SubpixelOffset::ThreeQuarters);
+ assert_eq!(SubpixelOffset::quantize(0.7), SubpixelOffset::ThreeQuarters);
+ assert_eq!(SubpixelOffset::quantize(0.78), SubpixelOffset::ThreeQuarters);
+ assert_eq!(SubpixelOffset::quantize(0.874), SubpixelOffset::ThreeQuarters);
+
+ assert_eq!(SubpixelOffset::quantize(0.875), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.89), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.91), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.967), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(0.999), SubpixelOffset::Zero);
+
+ assert_eq!(SubpixelOffset::quantize(-1.0), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(1.0), SubpixelOffset::Zero);
+ assert_eq!(SubpixelOffset::quantize(1.5), SubpixelOffset::Half);
+ assert_eq!(SubpixelOffset::quantize(-1.625), SubpixelOffset::Half);
+ assert_eq!(SubpixelOffset::quantize(-4.33), SubpixelOffset::ThreeQuarters);
+ }
+}
--- a/gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs
@@ -1,23 +1,24 @@
/* 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 http://mozilla.org/MPL/2.0/. */
//! Module only available when pathfinder is deactivated when webrender is
//! compiled regularly (i.e. any configuration without feature = "pathfinder")
-use api::{GlyphKey, ImageData, ImageDescriptor, ImageFormat};
+use api::{ImageData, ImageDescriptor, ImageFormat};
use device::TextureFilter;
use euclid::size2;
use gpu_types::UvRectKind;
use rayon::prelude::*;
use std::sync::{Arc, MutexGuard};
use platform::font::FontContext;
-use glyph_rasterizer::{FontInstance, FontContexts, GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult};
+use glyph_rasterizer::{FontInstance, FontContexts, GlyphKey};
+use glyph_rasterizer::{GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult};
use glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry};
use texture_cache::{TextureCache, TextureCacheHandle};
use gpu_cache::GpuCache;
use render_task::{RenderTaskTree, RenderTaskCache};
use tiling::SpecialRenderPasses;
use profiler::TextureCacheProfileCounters;
use std::collections::hash_map::Entry;
@@ -197,9 +198,9 @@ impl GlyphRasterizer {
glyph_key_cache.insert(key, glyph_info);
}
}
// Now that we are done with the critical path (rendering the glyphs),
// we can schedule removing the fonts if needed.
self.remove_dead_fonts();
}
-}
\ No newline at end of file
+}
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
@@ -1,31 +1,31 @@
/* 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 http://mozilla.org/MPL/2.0/. */
//! Module only available when pathfinder is activated
-use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, GlyphKey, NativeFontHandle};
+use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, NativeFontHandle};
use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D};
use pathfinder_font_renderer;
use pathfinder_partitioner::mesh::Mesh as PathfinderMesh;
use pathfinder_path_utils::cubic_to_quadratic::CubicToQuadraticTransformer;
use render_task::{RenderTask, RenderTaskTree, RenderTaskCache, RenderTaskCacheKey, RenderTaskCacheEntryHandle,
RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use std::ops::Deref;
use std::sync::{Arc, Mutex, MutexGuard};
use tiling::{RenderTargetKind, SpecialRenderPasses};
use glyph_rasterizer::AddFont;
use internal_types::ResourceCacheError;
use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
use std::collections::hash_map::Entry;
use std::f32;
-use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, FontContexts};
+use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, GlyphKey, FontContexts};
use texture_cache::TextureCache;
use gpu_cache::GpuCache;
use profiler::TextureCacheProfileCounters;
/// Should match macOS 10.13 High Sierra.
///
/// We multiply by sqrt(2) to compensate for the fact that dilation amounts are relative to the
/// pixel square on macOS and relative to the vertex normal in Pathfinder.
@@ -185,18 +185,19 @@ impl GlyphRasterizer {
None => {
let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
font_key: font.font_key.clone(),
size: font.size,
};
+ // TODO: pathfinder will need to support 2D subpixel offset
let pathfinder_subpixel_offset =
- pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8);
+ pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
let pathfinder_glyph_key =
pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
pathfinder_subpixel_offset);
let glyph_dimensions =
match pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
&pathfinder_glyph_key,
false) {
Ok(glyph_dimensions) => glyph_dimensions,
@@ -267,19 +268,20 @@ fn request_render_task_from_pathfinder(g
render_tasks: &mut RenderTaskTree,
render_passes: &mut SpecialRenderPasses)
-> Result<RenderTaskId, ()> {
let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
font_key: font.font_key.clone(),
size: font.size,
};
+ // TODO: pathfinder will need to support 2D subpixel offset
let pathfinder_subpixel_offset =
- pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8);
- let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.into();
+ pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
+ let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into();
let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
pathfinder_subpixel_offset);
// TODO(pcwalton): Fall back to CPU rendering if Pathfinder fails to collect the outline.
let mut mesh = PathfinderMesh::new();
let outline = try!(font_context.glyph_outline(&pathfinder_font_instance,
&pathfinder_glyph_key));
let tolerance = CUBIC_TO_QUADRATIC_APPROX_TOLERANCE;
@@ -306,9 +308,9 @@ fn request_render_task_from_pathfinder(g
FontRenderMode::Mono | FontRenderMode::Alpha => &mut render_passes.alpha_glyph_pass,
FontRenderMode::Subpixel => &mut render_passes.color_glyph_pass,
};
render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color);
Ok(root_task_id)
}
-pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
\ No newline at end of file
+pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -1,13 +1,14 @@
/* 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 http://mozilla.org/MPL/2.0/. */
-use api::{DevicePoint, LayoutToWorldTransform, WorldToLayoutTransform};
+use api::{DevicePoint, DeviceSize, DeviceRect, LayoutToWorldTransform};
+use api::{PremultipliedColorF, WorldToLayoutTransform};
use gpu_cache::{GpuCacheAddress, GpuDataRequest};
use prim_store::{VECS_PER_SEGMENT, EdgeAaSegmentMask};
use render_task::RenderTaskAddress;
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
// Contains type that must exactly match the same structures declared in GLSL.
const INT_BITS: usize = 31; //TODO: convert to unsigned
@@ -74,16 +75,45 @@ pub enum BlurDirection {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BlurInstance {
pub task_address: RenderTaskAddress,
pub src_task_address: RenderTaskAddress,
pub blur_direction: BlurDirection,
}
+#[derive(Debug, Copy, Clone)]
+#[repr(C)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub enum BorderSegment {
+ TopLeft,
+ TopRight,
+ BottomRight,
+ BottomLeft,
+ Left,
+ Top,
+ Right,
+ Bottom,
+}
+
+#[derive(Debug, Clone)]
+#[repr(C)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderInstance {
+ pub task_origin: DevicePoint,
+ pub local_rect: DeviceRect,
+ pub color0: PremultipliedColorF,
+ pub color1: PremultipliedColorF,
+ pub flags: i32,
+ pub widths: DeviceSize,
+ pub radius: DeviceSize,
+}
+
/// A clipping primitive drawn into the clipping mask.
/// Could be an image or a rectangle, which defines the
/// way `address` is treated.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub struct ClipMaskInstance {
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -1,15 +1,14 @@
/* 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 http://mozilla.org/MPL/2.0/. */
use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
use api::{FontInstanceFlags, FontVariation, NativeFontHandle};
-use api::{GlyphKey, SubpixelDirection};
use app_units::Au;
use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::TCFType;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::{CFNumber, CFNumberRef};
use core_foundation::string::{CFString, CFStringRef};
use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGBitmapByteOrder32Little};
#[cfg(not(feature = "pathfinder"))]
@@ -22,17 +21,17 @@ use core_graphics::data_provider::CGData
use core_graphics::font::{CGFont, CGGlyph};
use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize};
#[cfg(not(feature = "pathfinder"))]
use core_graphics::geometry::CGRect;
use core_text;
use core_text::font::{CTFont, CTFontRef};
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, FontTransform};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
#[cfg(feature = "pathfinder")]
use glyph_rasterizer::NativeFontHandleWrapper;
#[cfg(not(feature = "pathfinder"))]
use glyph_rasterizer::{GlyphFormat, GlyphRasterResult, RasterizedGlyph};
use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
@@ -458,17 +457,17 @@ impl FontContext {
}
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono => {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono mode.
- font.subpx_dir = SubpixelDirection::None;
+ font.disable_subpixel_position();
}
FontRenderMode::Alpha => {
font.color = if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) {
// Only the G channel is used to index grayscale tables,
// so use R and B to preserve light/dark determination.
let ColorU { g, a, .. } = font.color.luminance_color().quantized_ceil();
let rb = if should_use_white_on_black(font.color) { 255 } else { 0 };
ColorU::new(rb, g, rb, a)
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,29 +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 http://mozilla.org/MPL/2.0/. */
-use api::{ColorU, GlyphDimensions, GlyphKey, FontKey, FontRenderMode};
+use api::{ColorU, GlyphDimensions, FontKey, FontRenderMode};
use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
-use api::{FontInstanceFlags, NativeFontHandle, SubpixelDirection};
+use api::{FontInstanceFlags, NativeFontHandle};
use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform};
use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES};
use freetype::succeeded;
-use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterResult, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterResult, RasterizedGlyph};
#[cfg(feature = "pathfinder")]
use glyph_rasterizer::NativeFontHandleWrapper;
use internal_types::{FastHashMap, ResourceCacheError};
#[cfg(feature = "pathfinder")]
use pathfinder_font_renderer::freetype as pf_freetype;
use std::{cmp, mem, ptr, slice};
use std::cmp::max;
use std::ffi::CString;
@@ -248,19 +248,20 @@ impl FontContext {
(font.transform.skew_x != 0.0 || font.transform.skew_y != 0.0)) {
hinting = FontHinting::None;
}
match (hinting, font.render_mode) {
(FontHinting::None, _) => load_flags |= FT_LOAD_NO_HINTING,
(FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO,
(FontHinting::Light, _) => load_flags = FT_LOAD_TARGET_LIGHT,
(FontHinting::LCD, FontRenderMode::Subpixel) => {
- load_flags = match font.subpx_dir {
- SubpixelDirection::Vertical => FT_LOAD_TARGET_LCD_V,
- _ => FT_LOAD_TARGET_LCD,
+ load_flags = if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
+ FT_LOAD_TARGET_LCD_V
+ } else {
+ FT_LOAD_TARGET_LCD
};
if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) {
load_flags |= FT_LOAD_FORCE_AUTOHINT;
}
}
_ => {
if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) {
load_flags |= FT_LOAD_FORCE_AUTOHINT;
@@ -370,41 +371,39 @@ impl FontContext {
FT_Outline_Get_CBox(&(*slot).outline, &mut cbox);
// For spaces and other non-printable characters, early out.
if (*slot).outline.n_contours == 0 {
return cbox;
}
}
- // Convert the subpixel offset to floats.
- let (dx, dy) = font.get_subpx_offset(glyph);
-
// Apply extra pixel of padding for subpixel AA, due to the filter.
- let padding = match font.render_mode {
- FontRenderMode::Subpixel => (self.lcd_extra_pixels * 64) as FT_Pos,
- FontRenderMode::Alpha |
- FontRenderMode::Mono => 0 as FT_Pos,
- };
+ if font.render_mode == FontRenderMode::Subpixel {
+ let padding = (self.lcd_extra_pixels * 64) as FT_Pos;
+ if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
+ cbox.yMin -= padding;
+ cbox.yMax += padding;
+ } else {
+ cbox.xMin -= padding;
+ cbox.xMax += padding;
+ }
+ }
// Offset the bounding box by subpixel positioning.
// Convert to 26.6 fixed point format for FT.
- match font.subpx_dir {
- SubpixelDirection::None => {}
- SubpixelDirection::Horizontal => {
- let dx = (dx * 64.0 + 0.5) as FT_Long;
- cbox.xMin += dx - padding;
- cbox.xMax += dx + padding;
- }
- SubpixelDirection::Vertical => {
- let dy = (dy * 64.0 + 0.5) as FT_Long;
- cbox.yMin += dy - padding;
- cbox.yMax += dy + padding;
- }
- }
+ let (dx, dy) = font.get_subpx_offset(glyph);
+ let (dx, dy) = (
+ (dx * 64.0 + 0.5) as FT_Pos,
+ -(dy * 64.0 + 0.5) as FT_Pos,
+ );
+ cbox.xMin += dx;
+ cbox.xMax += dx;
+ cbox.yMin += dy;
+ cbox.yMax += dy;
// Outset the box to device pixel boundaries
cbox.xMin &= !63;
cbox.yMin &= !63;
cbox.xMax = (cbox.xMax + 63) & !63;
cbox.yMax = (cbox.yMax + 63) & !63;
cbox
@@ -517,35 +516,37 @@ impl FontContext {
}
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono => {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
// Subpixel positioning is disabled in mono mode.
- font.subpx_dir = SubpixelDirection::None;
+ font.disable_subpixel_position();
}
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
// We don't do any preblending with FreeType currently, so the color is not used.
font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
}
}
}
fn rasterize_glyph_outline(
&mut self,
slot: FT_GlyphSlot,
font: &FontInstance,
key: &GlyphKey,
) -> bool {
// Get the subpixel offsets in FT 26.6 format.
let (dx, dy) = font.get_subpx_offset(key);
- let dx = (dx * 64.0 + 0.5) as FT_Long;
- let dy = (dy * 64.0 + 0.5) as FT_Long;
+ let (dx, dy) = (
+ (dx * 64.0 + 0.5) as FT_Pos,
+ -(dy * 64.0 + 0.5) as FT_Pos,
+ );
// Move the outline curves to be at the origin, taking
// into account the subpixel positioning.
unsafe {
let outline = &(*slot).outline;
let mut cbox: FT_BBox = mem::uninitialized();
FT_Outline_Get_CBox(outline, &mut cbox);
FT_Outline_Translate(
@@ -560,21 +561,24 @@ impl FontContext {
let filter = match lcd_filter {
FontLCDFilter::None => FT_LcdFilter::FT_LCD_FILTER_NONE,
FontLCDFilter::Default => FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
FontLCDFilter::Light => FT_LcdFilter::FT_LCD_FILTER_LIGHT,
FontLCDFilter::Legacy => FT_LcdFilter::FT_LCD_FILTER_LEGACY,
};
unsafe { FT_Library_SetLcdFilter(self.lib, filter) };
}
- let render_mode = match (font.render_mode, font.subpx_dir) {
- (FontRenderMode::Mono, _) => FT_Render_Mode::FT_RENDER_MODE_MONO,
- (FontRenderMode::Alpha, _) => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
- (FontRenderMode::Subpixel, SubpixelDirection::Vertical) => FT_Render_Mode::FT_RENDER_MODE_LCD_V,
- (FontRenderMode::Subpixel, _) => FT_Render_Mode::FT_RENDER_MODE_LCD,
+ let render_mode = match font.render_mode {
+ FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
+ FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
+ FontRenderMode::Subpixel => if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
+ FT_Render_Mode::FT_RENDER_MODE_LCD_V
+ } else {
+ FT_Render_Mode::FT_RENDER_MODE_LCD
+ },
};
let result = unsafe { FT_Render_Glyph(slot, render_mode) };
if !succeeded(result) {
error!("Unable to rasterize");
debug!(
"{:?} with {:?}, {:?}",
key,
render_mode,
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,17 +1,17 @@
/* 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 http://mozilla.org/MPL/2.0/. */
use api::{FontInstanceFlags, FontKey, FontRenderMode};
-use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
+use api::{ColorU, GlyphDimensions};
use dwrote;
use gamma_lut::ColorLut;
-use glyph_rasterizer::{FontInstance, FontTransform};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
cfg_if! {
if #[cfg(feature = "pathfinder")] {
use pathfinder_font_renderer::{PathfinderComPtr, IDWriteFontFace};
use glyph_rasterizer::NativeFontHandleWrapper;
} else if #[cfg(not(feature = "pathfinder"))] {
@@ -367,17 +367,17 @@ impl FontContext {
}
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono => {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono mode.
- font.subpx_dir = SubpixelDirection::None;
+ font.disable_subpixel_position();
}
FontRenderMode::Alpha => {
font.color = font.color.luminance_color().quantize();
}
FontRenderMode::Subpixel => {
font.color = font.color.quantize();
}
}
@@ -464,9 +464,9 @@ impl<'a> From<NativeFontHandleWrapper<'a
let system_fc = ::dwrote::FontCollection::system();
let font = match system_fc.get_font_from_descriptor(&font_handle.0) {
Some(font) => font,
None => panic!("missing descriptor {:?}", font_handle.0),
};
let face = font.create_font_face();
unsafe { PathfinderComPtr::new(face.as_ptr()) }
}
-}
\ No newline at end of file
+}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,26 +1,28 @@
/* 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 http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
-use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
-use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
+use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode};
+use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets};
-use border::{BorderCornerInstance, BorderEdgeKind};
+use api::{BorderWidths, LayoutToWorldScale, NormalBorder};
+use app_units::Au;
+use border::{BorderCacheKey, BorderCornerInstance, BorderRenderTaskInfo, BorderEdgeKind};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
-use glyph_rasterizer::{FontInstance, FontTransform};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{BrushFlags, ClipChainRectIndex};
use image::{for_each_tile, for_each_repetition};
use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
#[cfg(debug_assertions)]
use render_backend::FrameId;
use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
@@ -251,16 +253,28 @@ pub struct VisibleImageTile {
}
#[derive(Debug)]
pub struct VisibleGradientTile {
pub handle: GpuCacheHandle,
}
#[derive(Debug)]
+pub enum BorderSource {
+ Image(ImageRequest),
+ Border {
+ handle: Option<RenderTaskCacheEntryHandle>,
+ cache_key: BorderCacheKey,
+ task_info: Option<BorderRenderTaskInfo>,
+ border: NormalBorder,
+ widths: BorderWidths,
+ },
+}
+
+#[derive(Debug)]
pub enum BrushKind {
Solid {
color: ColorF,
opacity_binding: OpacityBinding,
},
Clear,
Picture {
pic_index: PictureIndex,
@@ -302,17 +316,17 @@ pub enum BrushKind {
reverse_stops: bool,
start_point: LayoutPoint,
end_point: LayoutPoint,
stretch_size: LayoutSize,
tile_spacing: LayoutSize,
visible_tiles: Vec<VisibleGradientTile>,
},
Border {
- request: ImageRequest,
+ source: BorderSource,
},
}
impl BrushKind {
fn supports_segments(&self) -> bool {
match *self {
BrushKind::Solid { .. } |
BrushKind::Image { .. } |
@@ -348,53 +362,52 @@ bitflags! {
pub struct EdgeAaSegmentMask: u8 {
const LEFT = 0x1;
const TOP = 0x2;
const RIGHT = 0x4;
const BOTTOM = 0x8;
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub enum BrushSegmentTaskId {
RenderTaskId(RenderTaskId),
Opaque,
Empty,
}
impl BrushSegmentTaskId {
pub fn needs_blending(&self) -> bool {
match *self {
BrushSegmentTaskId::RenderTaskId(..) => true,
BrushSegmentTaskId::Opaque | BrushSegmentTaskId::Empty => false,
}
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct BrushSegment {
pub local_rect: LayoutRect,
pub clip_task_id: BrushSegmentTaskId,
pub may_need_clip_mask: bool,
pub edge_flags: EdgeAaSegmentMask,
pub extra_data: [f32; 4],
pub brush_flags: BrushFlags,
}
impl BrushSegment {
pub fn new(
- origin: LayoutPoint,
- size: LayoutSize,
+ rect: LayoutRect,
may_need_clip_mask: bool,
edge_flags: EdgeAaSegmentMask,
extra_data: [f32; 4],
brush_flags: BrushFlags,
) -> BrushSegment {
BrushSegment {
- local_rect: LayoutRect::new(origin, size),
+ local_rect: rect,
clip_task_id: BrushSegmentTaskId::Opaque,
may_need_clip_mask,
edge_flags,
extra_data,
brush_flags,
}
}
}
@@ -756,68 +769,93 @@ impl<'a> GradientGpuBlockBuilder<'a> {
#[derive(Debug, Clone)]
pub struct TextRunPrimitiveCpu {
pub font: FontInstance,
pub offset: LayoutVector2D,
pub glyph_range: ItemRange<GlyphInstance>,
pub glyph_keys: Vec<GlyphKey>,
pub glyph_gpu_blocks: Vec<GpuBlockData>,
+ pub glyph_transform: (DevicePixelScale, FontTransform),
pub shadow: bool,
pub glyph_raster_space: GlyphRasterSpace,
}
impl TextRunPrimitiveCpu {
+ pub fn new(
+ font: FontInstance,
+ offset: LayoutVector2D,
+ glyph_range: ItemRange<GlyphInstance>,
+ glyph_keys: Vec<GlyphKey>,
+ shadow: bool,
+ glyph_raster_space: GlyphRasterSpace,
+ ) -> Self {
+ TextRunPrimitiveCpu {
+ font,
+ offset,
+ glyph_range,
+ glyph_keys,
+ glyph_gpu_blocks: Vec::new(),
+ glyph_transform: (DevicePixelScale::new(1.0), FontTransform::identity()),
+ shadow,
+ glyph_raster_space,
+ }
+ }
+
pub fn get_font(
&self,
device_pixel_scale: DevicePixelScale,
transform: Option<LayoutToWorldTransform>,
) -> FontInstance {
let mut font = self.font.clone();
font.size = font.size.scale_by(device_pixel_scale.0);
if let Some(transform) = transform {
if transform.has_perspective_component() ||
!transform.has_2d_inverse() ||
self.glyph_raster_space != GlyphRasterSpace::Screen {
- font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
+ font.disable_subpixel_aa();
+ font.disable_subpixel_position();
} else {
font.transform = FontTransform::from(&transform).quantize();
}
}
font
}
fn prepare_for_render(
&mut self,
device_pixel_scale: DevicePixelScale,
transform: Option<LayoutToWorldTransform>,
allow_subpixel_aa: bool,
display_list: &BuiltDisplayList,
frame_building_state: &mut FrameBuildingState,
) {
if !allow_subpixel_aa && self.font.bg_color.a == 0 {
- self.font.render_mode = self.font.render_mode.limit_by(FontRenderMode::Alpha);
+ self.font.disable_subpixel_aa();
}
let font = self.get_font(device_pixel_scale, transform);
// Cache the glyph positions, if not in the cache already.
// TODO(gw): In the future, remove `glyph_instances`
// completely, and just reference the glyphs
// directly from the display list.
- if self.glyph_keys.is_empty() {
- let subpx_dir = font.subpx_dir.limit_by(font.render_mode);
+ if self.glyph_keys.is_empty() || self.glyph_transform != (device_pixel_scale, font.transform) {
+ let subpx_dir = font.get_subpx_dir();
let src_glyphs = display_list.get(self.glyph_range);
// TODO(gw): If we support chunks() on AuxIter
// in the future, this code below could
// be much simpler...
let mut gpu_block = [0.0; 4];
for (i, src) in src_glyphs.enumerate() {
- let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir);
+ let layout_offset = src.point + self.offset;
+ let world_offset = font.transform.transform(&layout_offset);
+ let device_offset = device_pixel_scale.transform_point(&world_offset);
+ let key = GlyphKey::new(src.index, device_offset, subpx_dir);
self.glyph_keys.push(key);
// Two glyphs are packed per GPU block.
if (i & 1) == 0 {
gpu_block[0] = src.point.x;
gpu_block[1] = src.point.y;
} else {
@@ -827,16 +865,18 @@ impl TextRunPrimitiveCpu {
}
}
// Ensure the last block is added in the case
// of an odd number of glyphs.
if (self.glyph_keys.len() & 1) != 0 {
self.glyph_gpu_blocks.push(gpu_block.into());
}
+
+ self.glyph_transform = (device_pixel_scale, font.transform);
}
frame_building_state.resource_cache
.request_glyphs(font,
&self.glyph_keys,
frame_building_state.gpu_cache,
frame_building_state.render_tasks,
frame_building_state.special_render_passes);
@@ -1085,35 +1125,32 @@ impl PrimitiveContainer {
}
// Create a clone of this PrimitiveContainer, applying whatever
// changes are necessary to the primitive to support rendering
// it as part of the supplied shadow.
pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer {
match *self {
PrimitiveContainer::TextRun(ref info) => {
- let mut render_mode = info.font.render_mode;
-
+ let mut font = FontInstance {
+ color: shadow.color.into(),
+ ..info.font.clone()
+ };
if shadow.blur_radius > 0.0 {
- render_mode = render_mode.limit_by(FontRenderMode::Alpha);
+ font.disable_subpixel_aa();
}
- PrimitiveContainer::TextRun(TextRunPrimitiveCpu {
- font: FontInstance {
- color: shadow.color.into(),
- render_mode,
- ..info.font.clone()
- },
- offset: info.offset + shadow.offset,
- glyph_range: info.glyph_range,
- glyph_keys: info.glyph_keys.clone(),
- glyph_gpu_blocks: Vec::new(),
- shadow: true,
- glyph_raster_space: info.glyph_raster_space,
- })
+ PrimitiveContainer::TextRun(TextRunPrimitiveCpu::new(
+ font,
+ info.offset + shadow.offset,
+ info.glyph_range,
+ info.glyph_keys.clone(),
+ true,
+ info.glyph_raster_space,
+ ))
}
PrimitiveContainer::Brush(ref brush) => {
match brush.kind {
BrushKind::Solid { .. } => {
PrimitiveContainer::Brush(BrushPrimitive::new(
BrushKind::new_solid(shadow.color),
None,
))
@@ -1396,16 +1433,94 @@ impl PrimitiveStore {
pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
&self.cpu_metadata[index.0]
}
pub fn prim_count(&self) -> usize {
self.cpu_metadata.len()
}
+ fn build_prim_segments_if_needed(
+ &mut self,
+ prim_index: PrimitiveIndex,
+ pic_state: &mut PictureState,
+ frame_state: &mut FrameBuildingState,
+ frame_context: &FrameBuildingContext,
+ ) {
+ let metadata = &self.cpu_metadata[prim_index.0];
+
+ if metadata.prim_kind != PrimitiveKind::Brush {
+ return;
+ }
+
+ let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
+
+ if let BrushKind::Border { ref mut source, .. } = brush.kind {
+ if let BorderSource::Border {
+ ref border,
+ ref mut cache_key,
+ ref widths,
+ ref mut handle,
+ ref mut task_info,
+ ..
+ } = *source {
+ // TODO(gw): When drawing in screen raster mode, we should also incorporate a
+ // scale factor from the world transform to get an appropriately
+ // sized border task.
+ let world_scale = LayoutToWorldScale::new(1.0);
+ let scale = world_scale * frame_context.device_pixel_scale;
+ let scale_au = Au::from_f32_px(scale.0);
+ let needs_update = scale_au != cache_key.scale;
+
+ if needs_update {
+ cache_key.scale = scale_au;
+
+ *task_info = Some(BorderRenderTaskInfo::new(
+ &metadata.local_rect,
+ border,
+ widths,
+ scale,
+ ));
+ }
+
+ let task_info = task_info.as_ref().unwrap();
+
+ *handle = Some(frame_state.resource_cache.request_render_task(
+ RenderTaskCacheKey {
+ size: DeviceIntSize::zero(),
+ kind: RenderTaskCacheKeyKind::Border(cache_key.clone()),
+ },
+ frame_state.gpu_cache,
+ frame_state.render_tasks,
+ None,
+ false, // todo
+ |render_tasks| {
+ let task = RenderTask::new_border(
+ task_info.size,
+ task_info.instances.clone(),
+ );
+
+ let task_id = render_tasks.add(task);
+
+ pic_state.tasks.push(task_id);
+
+ task_id
+ }
+ ));
+
+ if needs_update {
+ brush.segment_desc = Some(BrushSegmentDescriptor {
+ segments: task_info.segments.clone(),
+ clip_mask_kind: BrushClipMaskKind::Unknown,
+ });
+ }
+ }
+ }
+ }
+
fn prepare_prim_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
prim_run_context: &PrimitiveRunContext,
pic_state_for_children: PictureState,
pic_context: &PictureContext,
pic_state: &mut PictureState,
frame_context: &FrameBuildingContext,
@@ -1658,31 +1773,39 @@ impl PrimitiveStore {
key: yuv_key[channel],
rendering: image_rendering,
tile: None,
},
frame_state.gpu_cache,
);
}
}
- BrushKind::Border { request, .. } => {
- let image_properties = frame_state
- .resource_cache
- .get_image_properties(request.key);
+ BrushKind::Border { ref mut source, .. } => {
+ match *source {
+ BorderSource::Image(request) => {
+ let image_properties = frame_state
+ .resource_cache
+ .get_image_properties(request.key);
- if let Some(image_properties) = image_properties {
- // Update opacity for this primitive to ensure the correct
- // batching parameters are used.
- metadata.opacity.is_opaque =
- image_properties.descriptor.is_opaque;
+ if let Some(image_properties) = image_properties {
+ // Update opacity for this primitive to ensure the correct
+ // batching parameters are used.
+ metadata.opacity.is_opaque =
+ image_properties.descriptor.is_opaque;
- frame_state.resource_cache.request_image(
- request,
- frame_state.gpu_cache,
- );
+ frame_state.resource_cache.request_image(
+ request,
+ frame_state.gpu_cache,
+ );
+ }
+ }
+ BorderSource::Border { .. } => {
+ // Handled earlier since we need to update the segment
+ // descriptor *before* update_clip_task() is called.
+ }
}
}
BrushKind::RadialGradient {
gradient_index,
stops_range,
center,
start_radius,
end_radius,
@@ -1999,18 +2122,17 @@ impl PrimitiveStore {
// patterns of this and the segment
// builder significantly better, by
// retaining it across primitives.
let mut segments = Vec::new();
segment_builder.build(|segment| {
segments.push(
BrushSegment::new(
- segment.rect.origin,
- segment.rect.size,
+ segment.rect,
segment.has_mask,
segment.edge_flags,
[0.0; 4],
BrushFlags::empty(),
),
);
});
@@ -2385,16 +2507,23 @@ impl PrimitiveStore {
clipped,
unclipped,
});
metadata.clip_chain_rect_index = prim_run_context.clip_chain_rect_index;
(local_rect, unclipped)
};
+ self.build_prim_segments_if_needed(
+ prim_index,
+ pic_state,
+ frame_state,
+ frame_context,
+ );
+
if may_need_clip_mask && !self.update_clip_task(
prim_index,
prim_run_context,
&unclipped_device_rect,
pic_state,
frame_context,
frame_state,
) {
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -808,21 +808,21 @@ impl RenderBackend {
}
ApiMsg::FlushSceneBuilder(tx) => {
self.scene_tx.send(SceneBuilderRequest::Flush(tx)).unwrap();
}
ApiMsg::UpdateResources(updates) => {
self.resource_cache
.update_resources(updates, &mut profile_counters.resources);
}
- ApiMsg::GetGlyphDimensions(instance_key, glyph_keys, tx) => {
- let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
+ ApiMsg::GetGlyphDimensions(instance_key, glyph_indices, tx) => {
+ let mut glyph_dimensions = Vec::with_capacity(glyph_indices.len());
if let Some(font) = self.resource_cache.get_font_instance(instance_key) {
- for glyph_key in &glyph_keys {
- let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key);
+ for glyph_index in &glyph_indices {
+ let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index);
glyph_dimensions.push(glyph_dim);
}
}
tx.send(glyph_dimensions).unwrap();
}
ApiMsg::GetGlyphIndices(font_key, text, tx) => {
let mut glyph_indices = Vec::new();
for ch in text.chars() {
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,25 +1,26 @@
/* 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 http://mozilla.org/MPL/2.0/. */
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets, ImageDescriptor, ImageFormat};
#[cfg(feature = "pathfinder")]
use api::FontRenderMode;
+use border::BorderCacheKey;
use box_shadow::{BoxShadowCacheKey};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::CoordinateSystemId;
use device::TextureFilter;
#[cfg(feature = "pathfinder")]
use euclid::{TypedPoint2D, TypedVector2D};
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use glyph_rasterizer::GpuGlyphCacheKey;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
-use gpu_types::{ImageSource, RasterizationSpace, UvRectKind};
+use gpu_types::{BorderInstance, ImageSource, RasterizationSpace, UvRectKind};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
use picture::PictureCacheKey;
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use render_backend::FrameId;
@@ -247,16 +248,23 @@ pub enum BlitSource {
RenderTask {
task_id: RenderTaskId,
},
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderTask {
+ pub instances: Vec<BorderInstance>,
+}
+
+#[derive(Debug)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BlitTask {
pub source: BlitSource,
pub padding: DeviceIntSideOffsets,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -273,16 +281,17 @@ pub enum RenderTaskKind {
ClipRegion(ClipRegionTask),
VerticalBlur(BlurTask),
HorizontalBlur(BlurTask),
#[allow(dead_code)]
Glyph(GlyphTask),
Readback(DeviceIntRect),
Scaling(RenderTargetKind),
Blit(BlitTask),
+ Border(BorderTask),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ClearMode {
// Applicable to color and alpha targets.
Zero,
@@ -552,16 +561,31 @@ impl RenderTask {
uv_rect_handle: GpuCacheHandle::new(),
uv_rect_kind,
}),
clear_mode,
saved_index: None,
}
}
+ pub fn new_border(
+ size: DeviceIntSize,
+ instances: Vec<BorderInstance>,
+ ) -> Self {
+ RenderTask {
+ children: Vec::new(),
+ location: RenderTaskLocation::Dynamic(None, Some(size)),
+ kind: RenderTaskKind::Border(BorderTask {
+ instances,
+ }),
+ clear_mode: ClearMode::Transparent,
+ saved_index: None,
+ }
+ }
+
pub fn new_scaling(
target_kind: RenderTargetKind,
src_task_id: RenderTaskId,
target_size: DeviceIntSize,
) -> Self {
RenderTask {
children: vec![src_task_id],
location: RenderTaskLocation::Dynamic(None, Some(target_size)),
@@ -611,16 +635,17 @@ impl RenderTask {
RenderTaskKind::VerticalBlur(ref task) |
RenderTaskKind::HorizontalBlur(ref task) => {
task.uv_rect_kind
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Glyph(_) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::Blit(..) => {
UvRectKind::Rect
}
}
}
// Write (up to) 8 floats of data specific to the type
// of render task that is provided to the GPU shaders
@@ -665,16 +690,17 @@ impl RenderTask {
0.0,
]
}
RenderTaskKind::Glyph(_) => {
[1.0, 0.0, 0.0]
}
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::Blit(..) => {
[0.0; 3]
}
};
let (target_rect, target_index) = self.get_target_rect();
RenderTaskData {
@@ -699,16 +725,17 @@ impl RenderTask {
RenderTaskKind::VerticalBlur(ref info) |
RenderTaskKind::HorizontalBlur(ref info) => {
gpu_cache.get_address(&info.uv_rect_handle)
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Glyph(..) => {
panic!("texture handle not supported for this task kind");
}
}
}
pub fn get_dynamic_size(&self) -> DeviceIntSize {
@@ -771,16 +798,17 @@ impl RenderTask {
RenderTaskKind::Glyph(..) => {
RenderTargetKind::Color
}
RenderTaskKind::Scaling(target_kind) => {
target_kind
}
+ RenderTaskKind::Border(..) |
RenderTaskKind::Picture(..) => {
RenderTargetKind::Color
}
RenderTaskKind::Blit(..) => {
RenderTargetKind::Color
}
}
@@ -796,16 +824,17 @@ impl RenderTask {
match self.kind {
RenderTaskKind::Picture(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Blit(..) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::Glyph(..) => false,
// TODO(gw): For now, we've disabled the shared clip mask
// optimization. It's of dubious value in the
// future once we start to cache clip tasks anyway.
// I have left shared texture support here though,
// just in case we want it in the future.
RenderTaskKind::CacheMask(..) => false,
@@ -832,16 +861,17 @@ impl RenderTask {
}
RenderTaskKind::Picture(ref mut info) => {
(&mut info.uv_rect_handle, info.uv_rect_kind)
}
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::ClipRegion(..) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Glyph(..) => {
return;
}
};
if let Some(mut request) = gpu_cache.request(cache_handle) {
let p0 = target_rect.origin.to_f32();
@@ -882,16 +912,19 @@ impl RenderTask {
RenderTaskKind::Readback(ref rect) => {
pt.new_level("Readback".to_owned());
pt.add_item(format!("rect: {:?}", rect));
}
RenderTaskKind::Scaling(ref kind) => {
pt.new_level("Scaling".to_owned());
pt.add_item(format!("kind: {:?}", kind));
}
+ RenderTaskKind::Border(..) => {
+ pt.new_level("Border".to_owned());
+ }
RenderTaskKind::Blit(ref task) => {
pt.new_level("Blit".to_owned());
pt.add_item(format!("source: {:?}", task.source));
}
RenderTaskKind::Glyph(..) => {
pt.new_level("Glyph".to_owned());
}
}
@@ -926,16 +959,17 @@ impl RenderTask {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderTaskCacheKeyKind {
BoxShadow(BoxShadowCacheKey),
Image(ImageCacheKey),
#[allow(dead_code)]
Glyph(GpuGlyphCacheKey),
Picture(PictureCacheKey),
+ Border(BorderCacheKey),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTaskCacheKey {
pub size: DeviceIntSize,
pub kind: RenderTaskCacheKeyKind,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -123,16 +123,20 @@ const GPU_TAG_BRUSH_IMAGE: GpuProfileTag
const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag {
label: "B_Solid",
color: debug_colors::RED,
};
const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
label: "C_Clip",
color: debug_colors::PURPLE,
};
+const GPU_TAG_CACHE_BORDER: GpuProfileTag = GpuProfileTag {
+ label: "C_Border",
+ color: debug_colors::CORNSILK,
+};
const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag {
label: "target init",
color: debug_colors::SLATEGREY,
};
const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag {
label: "data init",
color: debug_colors::LIGHTGREY,
};
@@ -385,16 +389,63 @@ pub(crate) mod desc {
VertexAttribute {
name: "aBlurDirection",
count: 1,
kind: VertexAttributeKind::I32,
},
],
};
+ pub const BORDER: VertexDescriptor = VertexDescriptor {
+ vertex_attributes: &[
+ VertexAttribute {
+ name: "aPosition",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ ],
+ instance_attributes: &[
+ VertexAttribute {
+ name: "aTaskOrigin",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aRect",
+ count: 4,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aColor0",
+ count: 4,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aColor1",
+ count: 4,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aFlags",
+ count: 1,
+ kind: VertexAttributeKind::I32,
+ },
+ VertexAttribute {
+ name: "aWidths",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aRadii",
+ count: 2,
+ kind: VertexAttributeKind::F32,
+ },
+ ],
+ };
+
pub const CLIP: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[
VertexAttribute {
name: "aPosition",
count: 2,
kind: VertexAttributeKind::F32,
},
],
@@ -568,16 +619,17 @@ pub(crate) mod desc {
#[derive(Debug, Copy, Clone)]
pub(crate) enum VertexArrayKind {
Primitive,
Blur,
Clip,
DashAndDot,
VectorStencil,
VectorCover,
+ Border,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphicsApi {
OpenGL,
}
#[derive(Clone, Debug)]
@@ -1288,16 +1340,17 @@ impl LazyInitializedDebugRenderer {
}
}
pub struct RendererVAOs {
prim_vao: VAO,
blur_vao: VAO,
clip_vao: VAO,
dash_and_dot_vao: VAO,
+ border_vao: VAO,
}
/// The renderer is responsible for submitting to the GPU the work prepared by the
/// RenderBackend.
pub struct Renderer {
result_rx: Receiver<ResultMsg>,
debug_server: DebugServer,
pub device: Device,
@@ -1574,16 +1627,18 @@ impl Renderer {
device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static);
let gpu_glyph_renderer = try!(GpuGlyphRenderer::new(&mut device,
&prim_vao,
options.precache_shaders));
let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
+ let border_vao =
+ device.create_vao_with_new_instances(&desc::BORDER, &prim_vao);
let dash_and_dot_vao =
device.create_vao_with_new_instances(&desc::BORDER_CORNER_DASH_AND_DOT, &prim_vao);
let texture_cache_upload_pbo = device.create_pbo();
let texture_resolver = SourceTextureResolver::new(&mut device);
let node_data_texture = VertexDataTexture::new(&mut device);
let local_clip_rects_texture = VertexDataTexture::new(&mut device);
@@ -1729,16 +1784,17 @@ impl Renderer {
last_time: 0,
gpu_profile,
gpu_glyph_renderer,
vaos: RendererVAOs {
prim_vao,
blur_vao,
clip_vao,
dash_and_dot_vao,
+ border_vao,
},
node_data_texture,
local_clip_rects_texture,
render_task_texture,
pipeline_info: PipelineInfo::default(),
dither_matrix_texture,
external_image_handler: None,
output_image_handler: None,
@@ -3243,19 +3299,46 @@ impl Renderer {
self.device
.bind_draw_target(Some((texture, layer)), Some(target_size));
}
self.device.disable_depth();
self.device.disable_depth_write();
self.device.set_blend(false);
+ for rect in &target.clears {
+ self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, Some(*rect));
+ }
+
// Handle any blits to this texture from child tasks.
self.handle_blits(&target.blits, render_tasks);
+ // Draw any borders for this target.
+ if !target.border_segments.is_empty() {
+ let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_BORDER);
+
+ self.device.set_blend(true);
+ self.device.set_blend_mode_premultiplied_alpha();
+
+ self.shaders.cs_border_segment.bind(
+ &mut self.device,
+ &projection,
+ &mut self.renderer_errors,
+ );
+
+ self.draw_instanced_batch(
+ &target.border_segments,
+ VertexArrayKind::Border,
+ &BatchTextures::no_texture(),
+ stats,
+ );
+
+ self.device.set_blend(false);
+ }
+
// Draw any blurs for this target.
if !target.horizontal_blurs.is_empty() {
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
match target.target_kind {
RenderTargetKind::Alpha => &mut self.shaders.cs_blur_a8,
RenderTargetKind::Color => &mut self.shaders.cs_blur_rgba8,
}.bind(&mut self.device, &projection, &mut self.renderer_errors);
@@ -3848,16 +3931,17 @@ impl Renderer {
self.local_clip_rects_texture.deinit(&mut self.device);
self.render_task_texture.deinit(&mut self.device);
self.device.delete_pbo(self.texture_cache_upload_pbo);
self.texture_resolver.deinit(&mut self.device);
self.device.delete_vao(self.vaos.prim_vao);
self.device.delete_vao(self.vaos.clip_vao);
self.device.delete_vao(self.vaos.blur_vao);
self.device.delete_vao(self.vaos.dash_and_dot_vao);
+ self.device.delete_vao(self.vaos.border_vao);
#[cfg(feature = "debug_renderer")]
{
self.debug.deinit(&mut self.device);
}
for (_, target) in self.output_targets {
self.device.delete_fbo(target.fbo_id);
@@ -4443,24 +4527,26 @@ fn get_vao<'a>(vertex_array_kind: Vertex
-> &'a VAO {
match vertex_array_kind {
VertexArrayKind::Primitive => &vaos.prim_vao,
VertexArrayKind::Clip => &vaos.clip_vao,
VertexArrayKind::Blur => &vaos.blur_vao,
VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao,
VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao,
VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao,
+ VertexArrayKind::Border => &vaos.border_vao,
}
}
#[cfg(not(feature = "pathfinder"))]
fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
vaos: &'a RendererVAOs,
_: &'a GpuGlyphRenderer)
-> &'a VAO {
match vertex_array_kind {
VertexArrayKind::Primitive => &vaos.prim_vao,
VertexArrayKind::Clip => &vaos.clip_vao,
VertexArrayKind::Blur => &vaos.blur_vao,
VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao,
VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(),
+ VertexArrayKind::Border => &vaos.border_vao,
}
}
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1,34 +1,34 @@
/* 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 http://mozilla.org/MPL/2.0/. */
-use api::{AddFont, BlobImageResources, ResourceUpdate, ResourceUpdates};
+use api::{AddFont, BlobImageResources, ResourceUpdate};
use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
-use api::{Epoch, FontInstanceKey, FontKey, FontTemplate};
+use api::{Epoch, FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
use api::{ExternalImageData, ExternalImageType};
use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
-use api::{GlyphDimensions, GlyphKey, IdNamespace};
+use api::{GlyphDimensions, IdNamespace};
use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
use api::{TileOffset, TileSize};
use app_units::Au;
#[cfg(feature = "capture")]
use capture::ExternalCaptureImage;
#[cfg(feature = "replay")]
use capture::PlainExternalImage;
#[cfg(any(feature = "replay", feature = "png"))]
use capture::CaptureConfig;
use device::TextureFilter;
use euclid::size2;
use glyph_cache::GlyphCache;
#[cfg(not(feature = "pathfinder"))]
use glyph_cache::GlyphCacheEntry;
-use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
+use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::UvRectKind;
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId};
use render_task::{RenderTaskCacheEntry, RenderTaskCacheEntryHandle, RenderTaskTree};
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
@@ -169,17 +169,17 @@ pub fn intersect_for_tile(
}
impl<K, V, U> ResourceClassCache<K, V, U>
where
K: Clone + Hash + Eq + Debug,
U: Default,
{
- pub fn new() -> ResourceClassCache<K, V, U> {
+ pub fn new() -> Self {
ResourceClassCache {
resources: FastHashMap::default(),
user_data: Default::default(),
}
}
pub fn get(&self, key: &K) -> &V {
self.resources.get(key)
@@ -277,17 +277,17 @@ impl BlobImageResources for Resources {
}
fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)> {
self.image_templates
.get(key)
.map(|resource| (&resource.data, &resource.descriptor))
}
}
-pub type GlyphDimensionsCache = FastHashMap<GlyphRequest, Option<GlyphDimensions>>;
+pub type GlyphDimensionsCache = FastHashMap<(FontInstance, GlyphIndex), Option<GlyphDimensions>>;
pub struct ResourceCache {
cached_glyphs: GlyphCache,
cached_images: ImageCache,
cached_render_tasks: RenderTaskCache,
resources: Resources,
state: State,
@@ -366,24 +366,24 @@ impl ResourceCache {
user_data,
is_opaque,
|render_task_tree| Ok(f(render_task_tree))
).expect("Failed to request a render task from the resource cache!")
}
pub fn update_resources(
&mut self,
- updates: ResourceUpdates,
+ updates: Vec<ResourceUpdate>,
profile_counters: &mut ResourceProfileCounters,
) {
// TODO, there is potential for optimization here, by processing updates in
// bulk rather than one by one (for example by sorting allocations by size or
// in a way that reduces fragmentation in the atlas).
- for update in updates.updates {
+ for update in updates {
match update {
ResourceUpdate::AddImage(img) => {
if let ImageData::Raw(ref bytes) = img.data {
profile_counters.image_templates.inc(bytes.len());
}
self.add_image_template(img.key, img.descriptor, img.data, img.tiling);
}
ResourceUpdate::UpdateImage(img) => {
@@ -444,28 +444,26 @@ impl ResourceCache {
font_key: FontKey,
glyph_size: Au,
options: Option<FontInstanceOptions>,
platform_options: Option<FontInstancePlatformOptions>,
variations: Vec<FontVariation>,
) {
let FontInstanceOptions {
render_mode,
- subpx_dir,
flags,
bg_color,
..
} = options.unwrap_or_default();
let instance = FontInstance::new(
font_key,
glyph_size,
ColorF::new(0.0, 0.0, 0.0, 1.0),
bg_color,
render_mode,
- subpx_dir,
flags,
platform_options,
variations,
);
self.resources.font_instances
.write()
.unwrap()
.insert(instance_key, instance);
@@ -505,26 +503,24 @@ impl ResourceCache {
if let ImageData::Blob(ref blob) = data {
self.blob_image_renderer.as_mut().unwrap().add(
image_key,
Arc::clone(&blob),
tiling,
);
}
+ let dirty_rect = Some(descriptor.full_rect());
let resource = ImageResource {
descriptor,
data,
epoch: Epoch(0),
tiling,
- dirty_rect: Some(DeviceUintRect::new(
- DeviceUintPoint::zero(),
- descriptor.size,
- )),
+ dirty_rect,
};
self.resources.image_templates.insert(image_key, resource);
}
pub fn update_image_template(
&mut self,
image_key: ImageKey,
@@ -631,19 +627,24 @@ impl ResourceCache {
)),
true,
),
};
let needs_upload = self.texture_cache
.request(&entry.as_ref().unwrap().texture_cache_handle, gpu_cache);
- if !needs_upload && !needs_update {
- return;
- }
+ let dirty_rect = if needs_upload {
+ // the texture cache entry has been evicted, treat it as all dirty
+ Some(template.descriptor.full_rect())
+ } else if needs_update {
+ template.dirty_rect
+ } else {
+ return
+ };
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
// - The blob hasn't already been requested this frame.
if self.pending_image_requests.insert(request) && template.data.is_blob() {
if let Some(ref mut renderer) = self.blob_image_renderer {
let (offset, size) = match template.tiling {
Some(tile_size) => {
@@ -653,17 +654,17 @@ impl ResourceCache {
tile_size,
tile_offset,
);
let offset = DevicePoint::new(
tile_offset.x as f32 * tile_size as f32,
tile_offset.y as f32 * tile_size as f32,
);
- if let Some(dirty) = template.dirty_rect {
+ if let Some(dirty) = dirty_rect {
if intersect_for_tile(dirty, actual_size, tile_size, tile_offset).is_none() {
// don't bother requesting unchanged tiles
return
}
}
(offset, actual_size)
}
@@ -673,17 +674,17 @@ impl ResourceCache {
renderer.request(
&self.resources,
request.into(),
&BlobImageDescriptor {
size,
offset,
format: template.descriptor.format,
},
- template.dirty_rect,
+ dirty_rect,
);
}
}
}
pub fn request_glyphs(
&mut self,
mut font: FontInstance,
@@ -807,25 +808,23 @@ impl ResourceCache {
f(current_texture_id, current_glyph_format, fetch_buffer);
fetch_buffer.clear();
}
}
pub fn get_glyph_dimensions(
&mut self,
font: &FontInstance,
- key: &GlyphKey,
+ glyph_index: GlyphIndex,
) -> Option<GlyphDimensions> {
- let key = GlyphRequest::new(font, key);
-
- match self.cached_glyph_dimensions.entry(key.clone()) {
+ match self.cached_glyph_dimensions.entry((font.clone(), glyph_index)) {
Occupied(entry) => *entry.get(),
Vacant(entry) => *entry.insert(
self.glyph_rasterizer
- .get_glyph_dimensions(&key.font, &key.key),
+ .get_glyph_dimensions(font, glyph_index),
),
}
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
self.glyph_rasterizer.get_glyph_index(font_key, ch)
}
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -1,44 +1,44 @@
/* 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 http://mozilla.org/MPL/2.0/. */
-use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdates};
+use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate};
use api::channel::MsgSender;
use display_list_flattener::build_scene;
use frame_builder::{FrameBuilderConfig, FrameBuilder};
use clip_scroll_tree::ClipScrollTree;
use internal_types::FastHashSet;
use resource_cache::FontInstanceMap;
use render_backend::DocumentView;
use renderer::{PipelineInfo, SceneBuilderHooks};
use scene::Scene;
use std::sync::mpsc::{channel, Receiver, Sender};
// Message from render backend to scene builder.
pub enum SceneBuilderRequest {
Transaction {
document_id: DocumentId,
scene: Option<SceneRequest>,
- resource_updates: ResourceUpdates,
+ resource_updates: Vec<ResourceUpdate>,
frame_ops: Vec<FrameMsg>,
render: bool,
},
WakeUp,
Flush(MsgSender<()>),
Stop
}
// Message from scene builder to render backend.
pub enum SceneBuilderResult {
Transaction {
document_id: DocumentId,
built_scene: Option<BuiltScene>,
- resource_updates: ResourceUpdates,
+ resource_updates: Vec<ResourceUpdate>,
frame_ops: Vec<FrameMsg>,
render: bool,
result_tx: Option<Sender<SceneSwapResult>>,
},
FlushComplete(MsgSender<()>),
Stopped,
}
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -398,16 +398,17 @@ fn create_prim_shader(
let vertex_descriptor = match vertex_format {
VertexArrayKind::Primitive => desc::PRIM_INSTANCES,
VertexArrayKind::Blur => desc::BLUR,
VertexArrayKind::Clip => desc::CLIP,
VertexArrayKind::DashAndDot => desc::BORDER_CORNER_DASH_AND_DOT,
VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL,
VertexArrayKind::VectorCover => desc::VECTOR_COVER,
+ VertexArrayKind::Border => desc::BORDER,
};
let program = device.create_program(name, &prefix, &vertex_descriptor);
if let Ok(ref program) = program {
device.bind_shader_samplers(
program,
&[
@@ -459,16 +460,17 @@ fn create_clip_shader(name: &'static str
pub struct Shaders {
// These are "cache shaders". These shaders are used to
// draw intermediate results to cache targets. The results
// of these shaders are then used by the primitive shaders.
pub cs_blur_a8: LazilyCompiledShader,
pub cs_blur_rgba8: LazilyCompiledShader,
+ pub cs_border_segment: LazilyCompiledShader,
// Brush shaders
brush_solid: BrushShader,
brush_image: Vec<Option<BrushShader>>,
brush_blend: BrushShader,
brush_mix_blend: BrushShader,
brush_yuv_image: Vec<Option<BrushShader>>,
brush_radial_gradient: BrushShader,
@@ -706,31 +708,40 @@ impl Shaders {
let ps_border_edge = PrimitiveShader::new(
"ps_border_edge",
device,
&[],
options.precache_shaders,
)?;
+ let cs_border_segment = LazilyCompiledShader::new(
+ ShaderKind::Cache(VertexArrayKind::Border),
+ "cs_border_segment",
+ &[],
+ device,
+ options.precache_shaders,
+ )?;
+
let ps_split_composite = LazilyCompiledShader::new(
ShaderKind::Primitive,
"ps_split_composite",
&[],
device,
options.precache_shaders,
)?;
if let Some(vao) = dummy_vao {
device.delete_custom_vao(vao);
}
Ok(Shaders {
cs_blur_a8,
cs_blur_rgba8,
+ cs_border_segment,
brush_solid,
brush_image,
brush_blend,
brush_mix_blend,
brush_yuv_image,
brush_radial_gradient,
brush_linear_gradient,
cs_clip_rectangle,
@@ -839,11 +850,12 @@ impl Shaders {
}
for shader in self.brush_yuv_image {
if let Some(shader) = shader {
shader.deinit(device);
}
}
self.ps_border_corner.deinit(device);
self.ps_border_edge.deinit(device);
+ self.cs_border_segment.deinit(device);
self.ps_split_composite.deinit(device);
}
}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -7,28 +7,28 @@ use api::{DeviceUintRect, DeviceUintSize
use api::{MixBlendMode, PipelineId};
use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree, ClipScrollNodeIndex};
use device::{FrameId, Texture};
#[cfg(feature = "pathfinder")]
use euclid::{TypedPoint2D, TypedVector2D};
use gpu_cache::{GpuCache};
-use gpu_types::{BlurDirection, BlurInstance};
+use gpu_types::{BorderInstance, BlurDirection, BlurInstance};
use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushKind, DeferredResolve};
use profiler::FrameProfileCounters;
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskTree};
use resource_cache::ResourceCache;
-use std::{cmp, usize, f32, i32};
+use std::{cmp, usize, f32, i32, mem};
use texture_allocator::GuillotineAllocator;
#[cfg(feature = "pathfinder")]
use webrender_api::{DevicePixel, FontRenderMode};
const MIN_TARGET_SIZE: u32 = 2048;
#[derive(Debug)]
pub struct ScrollbarPrimitive {
@@ -428,16 +428,17 @@ impl RenderTarget for ColorRenderTarget
}
_ => {
// No other primitives make use of primitive caching yet!
unreachable!()
}
}
}
RenderTaskKind::ClipRegion(..) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::CacheMask(..) => {
panic!("Should not be added to color target!");
}
RenderTaskKind::Glyph(..) => {
// FIXME(pcwalton): Support color glyphs.
panic!("Glyphs should not be added to color target!");
}
RenderTaskKind::Readback(device_rect) => {
@@ -560,16 +561,17 @@ impl RenderTarget for AlphaRenderTarget
panic!("bug: invalid clear mode for alpha task");
}
}
match task.kind {
RenderTaskKind::Readback(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::Blit(..) |
+ RenderTaskKind::Border(..) |
RenderTaskKind::Glyph(..) => {
panic!("BUG: should not be added to alpha target!");
}
RenderTaskKind::VerticalBlur(ref info) => {
info.add_instances(
&mut self.vertical_blurs,
BlurDirection::Vertical,
render_tasks.get_task_address(task_id),
@@ -622,25 +624,29 @@ impl RenderTarget for AlphaRenderTarget
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TextureCacheRenderTarget {
pub target_kind: RenderTargetKind,
pub horizontal_blurs: Vec<BlurInstance>,
pub blits: Vec<BlitJob>,
pub glyphs: Vec<GlyphJob>,
+ pub border_segments: Vec<BorderInstance>,
+ pub clears: Vec<DeviceIntRect>,
}
impl TextureCacheRenderTarget {
fn new(target_kind: RenderTargetKind) -> Self {
TextureCacheRenderTarget {
target_kind,
horizontal_blurs: vec![],
blits: vec![],
glyphs: vec![],
+ border_segments: vec![],
+ clears: vec![],
}
}
fn add_task(
&mut self,
task_id: RenderTaskId,
render_tasks: &mut RenderTaskTree,
) {
@@ -673,16 +679,30 @@ impl TextureCacheRenderTarget {
// task to this target.
self.blits.push(BlitJob {
source: BlitJobSource::RenderTask(task_id),
target_rect: target_rect.0.inner_rect(task_info.padding),
});
}
}
}
+ RenderTaskKind::Border(ref mut task_info) => {
+ self.clears.push(target_rect.0);
+
+ // TODO(gw): It may be better to store the task origin in
+ // the render task data instead of per instance.
+ let task_origin = target_rect.0.origin.to_f32();
+ for instance in &mut task_info.instances {
+ instance.task_origin = task_origin;
+ }
+
+ let instances = mem::replace(&mut task_info.instances, Vec::new());
+
+ self.border_segments.extend(instances);
+ }
RenderTaskKind::Glyph(ref mut task_info) => {
self.add_glyph_task(task_info, target_rect.0)
}
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Readback(..) |
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -13,18 +13,18 @@ deserialize = []
[dependencies]
app_units = "0.6"
bincode = "1.0"
bitflags = "1.0"
byteorder = "1.2.1"
ipc-channel = {version = "0.10.0", optional = true}
euclid = { version = "0.17", features = ["serde"] }
-serde = { version = "=1.0.37", features = ["rc"] }
-serde_derive = { version = "=1.0.37", features = ["deserialize_in_place"] }
+serde = { version = "=1.0.58", features = ["rc"] }
+serde_derive = { version = "=1.0.58", features = ["deserialize_in_place"] }
serde_bytes = "0.10"
time = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
core-graphics = "0.13"
[target.'cfg(target_os = "windows")'.dependencies]
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -8,160 +8,66 @@ use app_units::Au;
use channel::{self, MsgSender, Payload, PayloadSender, PayloadSenderHelperMethods};
use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
use std::path::PathBuf;
use std::u32;
use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceUintRect};
use {DeviceUintSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions};
-use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData};
+use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData};
use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
use {NativeFontHandle, WorldPoint};
pub type TileSize = u16;
/// Documents are rendered in the ascending order of their associated layer values.
pub type DocumentLayer = i8;
-/// The resource updates for a given transaction (they must be applied in the same frame).
-#[derive(Clone, Deserialize, Serialize)]
-pub struct ResourceUpdates {
- pub updates: Vec<ResourceUpdate>,
-}
-
#[derive(Clone, Deserialize, Serialize)]
pub enum ResourceUpdate {
AddImage(AddImage),
UpdateImage(UpdateImage),
DeleteImage(ImageKey),
AddFont(AddFont),
DeleteFont(FontKey),
AddFontInstance(AddFontInstance),
DeleteFontInstance(FontInstanceKey),
}
-impl ResourceUpdates {
- pub fn new() -> Self {
- ResourceUpdates {
- updates: Vec::new(),
- }
- }
-
- pub fn add_image(
- &mut self,
- key: ImageKey,
- descriptor: ImageDescriptor,
- data: ImageData,
- tiling: Option<TileSize>,
- ) {
- self.updates.push(ResourceUpdate::AddImage(AddImage {
- key,
- descriptor,
- data,
- tiling,
- }));
- }
-
- pub fn update_image(
- &mut self,
- key: ImageKey,
- descriptor: ImageDescriptor,
- data: ImageData,
- dirty_rect: Option<DeviceUintRect>,
- ) {
- self.updates.push(ResourceUpdate::UpdateImage(UpdateImage {
- key,
- descriptor,
- data,
- dirty_rect,
- }));
- }
-
- pub fn delete_image(&mut self, key: ImageKey) {
- self.updates.push(ResourceUpdate::DeleteImage(key));
- }
-
- pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec<u8>, index: u32) {
- self.updates
- .push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index)));
- }
-
- pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) {
- self.updates
- .push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
- }
-
- pub fn delete_font(&mut self, key: FontKey) {
- self.updates.push(ResourceUpdate::DeleteFont(key));
- }
-
- pub fn add_font_instance(
- &mut self,
- key: FontInstanceKey,
- font_key: FontKey,
- glyph_size: Au,
- options: Option<FontInstanceOptions>,
- platform_options: Option<FontInstancePlatformOptions>,
- variations: Vec<FontVariation>,
- ) {
- self.updates
- .push(ResourceUpdate::AddFontInstance(AddFontInstance {
- key,
- font_key,
- glyph_size,
- options,
- platform_options,
- variations,
- }));
- }
-
- pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
- self.updates.push(ResourceUpdate::DeleteFontInstance(key));
- }
-
- pub fn merge(&mut self, mut other: ResourceUpdates) {
- self.updates.append(&mut other.updates);
- }
-
- pub fn clear(&mut self) {
- self.updates.clear()
- }
-}
-
/// A Transaction is a group of commands to apply atomically to a document.
///
/// This mechanism ensures that:
/// - no other message can be interleaved between two commands that need to be applied together.
/// - no redundant work is performed if two commands in the same transaction cause the scene or
/// the frame to be rebuilt.
pub struct Transaction {
// Operations affecting the scene (applied before scene building).
scene_ops: Vec<SceneMsg>,
// Operations affecting the generation of frames (applied after scene building).
frame_ops: Vec<FrameMsg>,
// Additional display list data.
payloads: Vec<Payload>,
// Resource updates are applied after scene building.
- resource_updates: ResourceUpdates,
+ pub resource_updates: Vec<ResourceUpdate>,
// If true the transaction is piped through the scene building thread, if false
// it will be applied directly on the render backend.
use_scene_builder_thread: bool,
generate_frame: bool,
}
impl Transaction {
pub fn new() -> Self {
Transaction {
scene_ops: Vec::new(),
frame_ops: Vec::new(),
- resource_updates: ResourceUpdates::new(),
+ resource_updates: Vec::new(),
payloads: Vec::new(),
use_scene_builder_thread: false, // TODO: make this true by default.
generate_frame: false,
}
}
// TODO: better name?
pub fn skip_scene_builder(&mut self) {
@@ -173,17 +79,17 @@ impl Transaction {
pub fn use_scene_builder_thread(&mut self) {
self.use_scene_builder_thread = true;
}
pub fn is_empty(&self) -> bool {
!self.generate_frame &&
self.scene_ops.is_empty() &&
self.frame_ops.is_empty() &&
- self.resource_updates.updates.is_empty()
+ self.resource_updates.is_empty()
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
// We track epochs before and after scene building.
// This one will be applied to the pending scene right away:
self.scene_ops.push(SceneMsg::UpdateEpoch(pipeline_id, epoch));
// And this one will be applied to the currently built scene at the end
// of the transaction (potentially long after the scene_ops one).
@@ -252,18 +158,18 @@ impl Transaction {
content_size,
list_descriptor,
preserve_frame_state,
}
);
self.payloads.push(Payload { epoch, pipeline_id, display_list_data });
}
- pub fn update_resources(&mut self, resources: ResourceUpdates) {
- self.resource_updates.merge(resources);
+ pub fn update_resources(&mut self, resources: Vec<ResourceUpdate>) {
+ self.merge(resources);
}
pub fn set_window_parameters(
&mut self,
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
device_pixel_ratio: f32,
) {
@@ -348,52 +254,132 @@ impl Transaction {
frame_ops: self.frame_ops,
resource_updates: self.resource_updates,
use_scene_builder_thread: self.use_scene_builder_thread,
generate_frame: self.generate_frame,
},
self.payloads,
)
}
+
+ pub fn add_image(
+ &mut self,
+ key: ImageKey,
+ descriptor: ImageDescriptor,
+ data: ImageData,
+ tiling: Option<TileSize>,
+ ) {
+ self.resource_updates.push(ResourceUpdate::AddImage(AddImage {
+ key,
+ descriptor,
+ data,
+ tiling,
+ }));
+ }
+
+ pub fn update_image(
+ &mut self,
+ key: ImageKey,
+ descriptor: ImageDescriptor,
+ data: ImageData,
+ dirty_rect: Option<DeviceUintRect>,
+ ) {
+ self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage {
+ key,
+ descriptor,
+ data,
+ dirty_rect,
+ }));
+ }
+
+ pub fn delete_image(&mut self, key: ImageKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteImage(key));
+ }
+
+ pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec<u8>, index: u32) {
+ self.resource_updates
+ .push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index)));
+ }
+
+ pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) {
+ self.resource_updates
+ .push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
+ }
+
+ pub fn delete_font(&mut self, key: FontKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteFont(key));
+ }
+
+ pub fn add_font_instance(
+ &mut self,
+ key: FontInstanceKey,
+ font_key: FontKey,
+ glyph_size: Au,
+ options: Option<FontInstanceOptions>,
+ platform_options: Option<FontInstancePlatformOptions>,
+ variations: Vec<FontVariation>,
+ ) {
+ self.resource_updates
+ .push(ResourceUpdate::AddFontInstance(AddFontInstance {
+ key,
+ font_key,
+ glyph_size,
+ options,
+ platform_options,
+ variations,
+ }));
+ }
+
+ pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
+ self.resource_updates.push(ResourceUpdate::DeleteFontInstance(key));
+ }
+
+ pub fn merge(&mut self, mut other: Vec<ResourceUpdate>) {
+ self.resource_updates.append(&mut other);
+ }
+
+ pub fn clear(&mut self) {
+ self.resource_updates.clear()
+ }
}
/// Represents a transaction in the format sent through the channel.
#[derive(Clone, Deserialize, Serialize)]
pub struct TransactionMsg {
pub scene_ops: Vec<SceneMsg>,
pub frame_ops: Vec<FrameMsg>,
- pub resource_updates: ResourceUpdates,
+ pub resource_updates: Vec<ResourceUpdate>,
pub generate_frame: bool,
pub use_scene_builder_thread: bool,
}
impl TransactionMsg {
pub fn is_empty(&self) -> bool {
!self.generate_frame &&
self.scene_ops.is_empty() &&
self.frame_ops.is_empty() &&
- self.resource_updates.updates.is_empty()
+ self.resource_updates.is_empty()
}
// TODO: We only need this for a few RenderApi methods which we should remove.
fn frame_message(msg: FrameMsg) -> Self {
TransactionMsg {
scene_ops: Vec::new(),
frame_ops: vec![msg],
- resource_updates: ResourceUpdates::new(),
+ resource_updates: Vec::new(),
generate_frame: false,
use_scene_builder_thread: false,
}
}
fn scene_message(msg: SceneMsg) -> Self {
TransactionMsg {
scene_ops: vec![msg],
frame_ops: Vec::new(),
- resource_updates: ResourceUpdates::new(),
+ resource_updates: Vec::new(),
generate_frame: false,
use_scene_builder_thread: false,
}
}
}
#[derive(Clone, Deserialize, Serialize)]
pub struct AddImage {
@@ -593,21 +579,21 @@ pub enum DebugCommand {
ClearCaches(ClearCache),
/// Invalidate GPU cache, forcing the update from the CPU mirror.
InvalidateGpuCache,
}
#[derive(Clone, Deserialize, Serialize)]
pub enum ApiMsg {
/// Add/remove/update images and fonts.
- UpdateResources(ResourceUpdates),
+ UpdateResources(Vec<ResourceUpdate>),
/// Gets the glyph dimensions
GetGlyphDimensions(
FontInstanceKey,
- Vec<GlyphKey>,
+ Vec<GlyphIndex>,
MsgSender<Vec<Option<GlyphDimensions>>>,
),
/// Gets the glyph indices from a string
GetGlyphIndices(FontKey, String, MsgSender<Vec<Option<u32>>>),
/// Adds a new document namespace.
CloneApi(MsgSender<IdNamespace>),
/// Adds a new document with given initial size.
AddDocument(DocumentId, DeviceUintSize, DocumentLayer),
@@ -806,20 +792,20 @@ impl RenderApi {
/// Gets the dimensions for the supplied glyph keys
///
/// Note: Internally, the internal texture cache doesn't store
/// 'empty' textures (height or width = 0)
/// This means that glyph dimensions e.g. for spaces (' ') will mostly be None.
pub fn get_glyph_dimensions(
&self,
font: FontInstanceKey,
- glyph_keys: Vec<GlyphKey>,
+ glyph_indices: Vec<GlyphIndex>,
) -> Vec<Option<GlyphDimensions>> {
let (tx, rx) = channel::msg_channel().unwrap();
- let msg = ApiMsg::GetGlyphDimensions(font, glyph_keys, tx);
+ let msg = ApiMsg::GetGlyphDimensions(font, glyph_indices, tx);
self.api_sender.send(msg).unwrap();
rx.recv().unwrap()
}
/// Gets the glyph indices for the supplied string. These
/// can be used to construct GlyphKeys.
pub fn get_glyph_indices(&self, font_key: FontKey, text: &str) -> Vec<Option<u32>> {
let (tx, rx) = channel::msg_channel().unwrap();
@@ -830,18 +816,18 @@ impl RenderApi {
/// Creates an `ImageKey`.
pub fn generate_image_key(&self) -> ImageKey {
let new_id = self.next_unique_id();
ImageKey::new(self.namespace_id, new_id)
}
/// Add/remove/update resources such as images and fonts.
- pub fn update_resources(&self, resources: ResourceUpdates) {
- if resources.updates.is_empty() {
+ pub fn update_resources(&self, resources: Vec<ResourceUpdate>) {
+ if resources.is_empty() {
return;
}
self.api_sender
.send(ApiMsg::UpdateResources(resources))
.unwrap();
}
pub fn send_external_event(&self, evt: ExternalEvent) {
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -367,17 +367,17 @@ pub struct BorderWidths {
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct BorderSide {
pub color: ColorF,
pub style: BorderStyle,
}
#[repr(u32)]
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Hash, Eq)]
pub enum BorderStyle {
None = 0,
Solid = 1,
Double = 2,
Dotted = 3,
Dashed = 4,
Hidden = 5,
Groove = 6,
--- a/gfx/webrender_api/src/font.rs
+++ b/gfx/webrender_api/src/font.rs
@@ -89,86 +89,26 @@ pub enum FontTemplate {
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
pub enum FontRenderMode {
Mono = 0,
Alpha,
Subpixel,
}
-#[repr(u32)]
-#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub enum SubpixelDirection {
- None = 0,
- Horizontal,
- Vertical,
-}
-
impl FontRenderMode {
- // Skia quantizes subpixel offsets into 1/4 increments.
- // Given the absolute position, return the quantized increment
- fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
- // Following the conventions of Gecko and Skia, we want
- // to quantize the subpixel position, such that abs(pos) gives:
- // [0.0, 0.125) -> Zero
- // [0.125, 0.375) -> Quarter
- // [0.375, 0.625) -> Half
- // [0.625, 0.875) -> ThreeQuarters,
- // [0.875, 1.0) -> Zero
- // The unit tests below check for this.
- let apos = ((pos - pos.floor()) * 8.0) as i32;
-
- match apos {
- 0 | 7 => SubpixelOffset::Zero,
- 1...2 => SubpixelOffset::Quarter,
- 3...4 => SubpixelOffset::Half,
- 5...6 => SubpixelOffset::ThreeQuarters,
- _ => unreachable!("bug: unexpected quantized result"),
- }
- }
-
// Combine two font render modes such that the lesser amount of AA limits the AA of the result.
pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode {
match (self, other) {
(FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other,
_ => self,
}
}
}
-impl SubpixelDirection {
- // Limit the subpixel direction to what is supported by the render mode.
- pub fn limit_by(self, render_mode: FontRenderMode) -> SubpixelDirection {
- match render_mode {
- FontRenderMode::Mono => SubpixelDirection::None,
- FontRenderMode::Alpha | FontRenderMode::Subpixel => self,
- }
- }
-}
-
-#[repr(u8)]
-#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
-pub enum SubpixelOffset {
- Zero = 0,
- Quarter = 1,
- Half = 2,
- ThreeQuarters = 3,
-}
-
-impl Into<f64> for SubpixelOffset {
- fn into(self) -> f64 {
- match self {
- SubpixelOffset::Zero => 0.0,
- SubpixelOffset::Quarter => 0.25,
- SubpixelOffset::Half => 0.5,
- SubpixelOffset::ThreeQuarters => 0.75,
- }
- }
-}
-
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialOrd, Deserialize, Serialize)]
pub struct FontVariation {
pub tag: u32,
pub value: f32,
}
impl Ord for FontVariation {
@@ -217,65 +157,66 @@ bitflags! {
// Common flags
const SYNTHETIC_ITALICS = 1 << 0;
const SYNTHETIC_BOLD = 1 << 1;
const EMBEDDED_BITMAPS = 1 << 2;
const SUBPIXEL_BGR = 1 << 3;
const TRANSPOSE = 1 << 4;
const FLIP_X = 1 << 5;
const FLIP_Y = 1 << 6;
+ const SUBPIXEL_POSITION = 1 << 7;
// Windows flags
const FORCE_GDI = 1 << 16;
// Mac flags
const FONT_SMOOTHING = 1 << 16;
// FreeType flags
const FORCE_AUTOHINT = 1 << 16;
const NO_AUTOHINT = 1 << 17;
const VERTICAL_LAYOUT = 1 << 18;
+ const LCD_VERTICAL = 1 << 19;
}
}
impl Default for FontInstanceFlags {
#[cfg(target_os = "windows")]
fn default() -> FontInstanceFlags {
- FontInstanceFlags::empty()
+ FontInstanceFlags::SUBPIXEL_POSITION
}
#[cfg(target_os = "macos")]
fn default() -> FontInstanceFlags {
+ FontInstanceFlags::SUBPIXEL_POSITION |
FontInstanceFlags::FONT_SMOOTHING
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
fn default() -> FontInstanceFlags {
- FontInstanceFlags::empty()
+ FontInstanceFlags::SUBPIXEL_POSITION
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct FontInstanceOptions {
pub render_mode: FontRenderMode,
- pub subpx_dir: SubpixelDirection,
pub flags: FontInstanceFlags,
/// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel,
/// the text will be rendered with bg_color.r/g/b as an opaque estimated
/// background color.
pub bg_color: ColorU,
}
impl Default for FontInstanceOptions {
fn default() -> FontInstanceOptions {
FontInstanceOptions {
render_mode: FontRenderMode::Subpixel,
- subpx_dir: SubpixelDirection::Horizontal,
flags: Default::default(),
bg_color: ColorU::new(0, 0, 0, 0),
}
}
}
#[cfg(target_os = "windows")]
#[repr(C)]
@@ -353,92 +294,17 @@ impl Default for FontInstancePlatformOpt
pub struct FontInstanceKey(pub IdNamespace, pub u32);
impl FontInstanceKey {
pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
FontInstanceKey(namespace, key)
}
}
-#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
-pub struct GlyphKey {
- pub index: u32,
- pub subpixel_offset: SubpixelOffset,
-}
-
-impl GlyphKey {
- pub fn new(
- index: u32,
- point: LayoutPoint,
- render_mode: FontRenderMode,
- subpx_dir: SubpixelDirection,
- ) -> GlyphKey {
- let pos = match subpx_dir {
- SubpixelDirection::None => 0.0,
- SubpixelDirection::Horizontal => point.x,
- SubpixelDirection::Vertical => point.y,
- };
-
- GlyphKey {
- index,
- subpixel_offset: render_mode.subpixel_quantize_offset(pos),
- }
- }
-}
-
pub type GlyphIndex = u32;
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct GlyphInstance {
pub index: GlyphIndex,
pub point: LayoutPoint,
}
-#[cfg(test)]
-mod test {
- use super::{FontRenderMode, SubpixelOffset};
-
- #[test]
- fn test_subpx_quantize() {
- let rm = FontRenderMode::Subpixel;
-
- assert_eq!(rm.subpixel_quantize_offset(0.0), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(-0.0), SubpixelOffset::Zero);
-
- assert_eq!(rm.subpixel_quantize_offset(0.1), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.01), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.05), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.12), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.124), SubpixelOffset::Zero);
-
- assert_eq!(rm.subpixel_quantize_offset(0.125), SubpixelOffset::Quarter);
- assert_eq!(rm.subpixel_quantize_offset(0.2), SubpixelOffset::Quarter);
- assert_eq!(rm.subpixel_quantize_offset(0.25), SubpixelOffset::Quarter);
- assert_eq!(rm.subpixel_quantize_offset(0.33), SubpixelOffset::Quarter);
- assert_eq!(rm.subpixel_quantize_offset(0.374), SubpixelOffset::Quarter);
-
- assert_eq!(rm.subpixel_quantize_offset(0.375), SubpixelOffset::Half);
- assert_eq!(rm.subpixel_quantize_offset(0.4), SubpixelOffset::Half);
- assert_eq!(rm.subpixel_quantize_offset(0.5), SubpixelOffset::Half);
- assert_eq!(rm.subpixel_quantize_offset(0.58), SubpixelOffset::Half);
- assert_eq!(rm.subpixel_quantize_offset(0.624), SubpixelOffset::Half);
-
- assert_eq!(rm.subpixel_quantize_offset(0.625), SubpixelOffset::ThreeQuarters);
- assert_eq!(rm.subpixel_quantize_offset(0.67), SubpixelOffset::ThreeQuarters);
- assert_eq!(rm.subpixel_quantize_offset(0.7), SubpixelOffset::ThreeQuarters);
- assert_eq!(rm.subpixel_quantize_offset(0.78), SubpixelOffset::ThreeQuarters);
- assert_eq!(rm.subpixel_quantize_offset(0.874), SubpixelOffset::ThreeQuarters);
-
- assert_eq!(rm.subpixel_quantize_offset(0.875), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.89), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.91), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.967), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(0.999), SubpixelOffset::Zero);
-
- assert_eq!(rm.subpixel_quantize_offset(-1.0), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(1.0), SubpixelOffset::Zero);
- assert_eq!(rm.subpixel_quantize_offset(1.5), SubpixelOffset::Half);
- assert_eq!(rm.subpixel_quantize_offset(-1.625), SubpixelOffset::Half);
- assert_eq!(rm.subpixel_quantize_offset(-4.33), SubpixelOffset::ThreeQuarters);
-
- }
-}
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -1,17 +1,18 @@
/* 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 http://mozilla.org/MPL/2.0/. */
extern crate serde_bytes;
use font::{FontInstanceKey, FontKey, FontTemplate};
use std::sync::Arc;
-use {DevicePoint, DeviceUintRect, DeviceUintSize, IdNamespace, TileOffset, TileSize};
+use {DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
+use {IdNamespace, TileOffset, TileSize};
use euclid::size2;
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ImageKey(pub IdNamespace, pub u32);
impl ImageKey {
pub const DUMMY: Self = ImageKey(IdNamespace(0), 0);
@@ -101,16 +102,23 @@ impl ImageDescriptor {
pub fn compute_stride(&self) -> u32 {
self.stride.unwrap_or(self.size.width * self.format.bytes_per_pixel())
}
pub fn compute_total_size(&self) -> u32 {
self.compute_stride() * self.size.height
}
+
+ pub fn full_rect(&self) -> DeviceUintRect {
+ DeviceUintRect::new(
+ DeviceUintPoint::zero(),
+ self.size,
+ )
+ }
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ImageData {
Raw(#[serde(with = "serde_image_data_raw")] Arc<Vec<u8>>),
Blob(#[serde(with = "serde_image_data_raw")] Arc<BlobImageData>),
External(ExternalImageData),
}
--- a/gfx/webrender_api/src/units.rs
+++ b/gfx/webrender_api/src/units.rs
@@ -87,16 +87,18 @@ pub type WorldVector3D = TypedVector3D<f
pub struct Tiles;
pub type TileOffset = TypedPoint2D<u16, Tiles>;
/// Scaling ratio from world pixels to device pixels.
pub type DevicePixelScale = TypedScale<f32, WorldPixel, DevicePixel>;
/// Scaling ratio from layout to world. Used for cases where we know the layout
/// is in world space, or specifically want to treat it this way.
pub type LayoutToWorldScale = TypedScale<f32, LayoutPixel, WorldPixel>;
+/// A complete scaling ratio from layout space to device pixel space.
+pub type LayoutToDeviceScale = TypedScale<f32, LayoutPixel, DevicePixel>;
pub type LayoutTransform = TypedTransform3D<f32, LayoutPixel, LayoutPixel>;
pub type LayoutToScrollTransform = TypedTransform3D<f32, LayoutPixel, ScrollLayerPixel>;
pub type ScrollToLayoutTransform = TypedTransform3D<f32, ScrollLayerPixel, LayoutPixel>;
pub type LayoutToWorldTransform = TypedTransform3D<f32, LayoutPixel, WorldPixel>;
pub type WorldToLayoutTransform = TypedTransform3D<f32, WorldPixel, LayoutPixel>;
pub type ScrollToWorldTransform = TypedTransform3D<f32, ScrollLayerPixel, WorldPixel>;
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-bb354abbf84602d3d8357c63c4f0b1139ec4deb1
+63c71ca9bbe4dec0ebc9c9bc8ab65b06a6b40641
--- a/gfx/wrench/src/json_frame_writer.rs
+++ b/gfx/wrench/src/json_frame_writer.rs
@@ -106,18 +106,18 @@ impl JsonFrameWriter {
let mut file = fs::File::create(&frame_file_name).unwrap();
let s = serde_json::to_string_pretty(&dl).unwrap();
file.write_all(&s.into_bytes()).unwrap();
file.write_all(b"\n").unwrap();
}
- fn update_resources(&mut self, updates: &ResourceUpdates) {
- for update in &updates.updates {
+ fn update_resources(&mut self, updates: &[ResourceUpdate]) {
+ for update in updates {
match *update {
ResourceUpdate::AddImage(ref img) => {
let stride = img.descriptor.stride.unwrap_or(
img.descriptor.size.width * img.descriptor.format.bytes_per_pixel(),
);
let bytes = match img.data {
ImageData::Raw(ref v) => (**v).clone(),
ImageData::External(_) | ImageData::Blob(_) => {
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -25,76 +25,80 @@ fn size<T: Copy, U>(x: T, y: T) -> Typed
TypedSize2D::new(x, y)
}
fn rect<T: Copy, U>(x: T, y: T, width: T, height: T) -> TypedRect<T, U> {
TypedRect::new(point(x, y), size(width, height))
}
impl<'a> RawtestHarness<'a> {
- pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: &'a Receiver<NotifierEvent>) -> Self {
+ pub fn new(wrench: &'a mut Wrench,
+ window: &'a mut WindowWrapper,
+ rx: &'a Receiver<NotifierEvent>) -> Self {
RawtestHarness {
wrench,
rx,
window,
}
}
pub fn run(mut self) {
self.test_hit_testing();
self.test_retained_blob_images_test();
self.test_blob_update_test();
self.test_blob_update_epoch_test();
self.test_tile_decomposition();
self.test_very_large_blob();
+ self.test_offscreen_blob();
self.test_save_restore();
self.test_capture();
self.test_zero_height_window();
}
fn render_and_get_pixels(&mut self, window_rect: DeviceUintRect) -> Vec<u8> {
self.rx.recv().unwrap();
self.wrench.render();
self.wrench.renderer.read_pixels_rgba8(window_rect)
}
fn submit_dl(
&mut self,
epoch: &mut Epoch,
layout_size: LayoutSize,
builder: DisplayListBuilder,
- resources: Option<ResourceUpdates>
+ resources: &[ResourceUpdate]
) {
let mut txn = Transaction::new();
let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0));
txn.use_scene_builder_thread();
- if let Some(resources) = resources {
- txn.update_resources(resources);
+ if !resources.is_empty() {
+ txn.resource_updates = resources.to_vec();
}
txn.set_display_list(
*epoch,
root_background_color,
layout_size,
builder.finalize(),
false,
);
epoch.0 += 1;
txn.generate_frame();
self.wrench.api.send_transaction(self.wrench.document_id, txn);
}
+
fn test_tile_decomposition(&mut self) {
println!("\ttile decomposition...");
// This exposes a crash in tile decomposition
let layout_size = LayoutSize::new(800., 800.);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let blob_img = self.wrench.api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img,
ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(128),
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
@@ -107,26 +111,26 @@ impl<'a> RawtestHarness<'a> {
size(151.0, 56.0),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
let mut epoch = Epoch(0);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
self.rx.recv().unwrap();
self.wrench.render();
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
- resources = ResourceUpdates::new();
- resources.delete_image(blob_img);
- self.wrench.api.update_resources(resources);
+ txn = Transaction::new();
+ txn.delete_image(blob_img);
+ self.wrench.api.update_resources(txn.resource_updates);
}
fn test_very_large_blob(&mut self) {
println!("\tvery large blob...");
assert_eq!(self.wrench.device_pixel_ratio, 1.);
let window_size = self.window.get_inner_size();
@@ -135,20 +139,20 @@ impl<'a> RawtestHarness<'a> {
let window_rect = DeviceUintRect::new(
DeviceUintPoint::new(0, window_size.height - test_size.height),
test_size,
);
// This exposes a crash in tile decomposition
let layout_size = LayoutSize::new(800., 800.);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let blob_img = self.wrench.api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img,
ImageDescriptor::new(1510, 111256, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(31),
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
@@ -167,69 +171,195 @@ impl<'a> RawtestHarness<'a> {
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
builder.pop_clip_id();
let mut epoch = Epoch(0);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let called = Arc::new(AtomicIsize::new(0));
let called_inner = Arc::clone(&called);
self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
called_inner.fetch_add(1, Ordering::SeqCst);
});
let pixels = self.render_and_get_pixels(window_rect);
// make sure we didn't request too many blobs
assert_eq!(called.load(Ordering::SeqCst), 16);
// make sure things are in the right spot
assert!(
- pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4] == 255 &&
- pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 1] == 255 &&
- pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 2] == 255 &&
- pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 3] == 255
+ pixels[(148 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4] == 255 &&
+ pixels[(148 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4 + 1] == 255 &&
+ pixels[(148 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4 + 2] == 255 &&
+ pixels[(148 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4 + 3] == 255
);
assert!(
- pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4] == 50 &&
- pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 1] == 50 &&
- pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 2] == 150 &&
- pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 3] == 255
+ pixels[(132 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4] == 50 &&
+ pixels[(132 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4 + 1] == 50 &&
+ pixels[(132 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4 + 2] == 150 &&
+ pixels[(132 +
+ (window_rect.size.height as usize - 148) *
+ window_rect.size.width as usize) * 4 + 3] == 255
);
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
- resources = ResourceUpdates::new();
- resources.delete_image(blob_img);
- self.wrench.api.update_resources(resources);
+ txn = Transaction::new();
+ txn.delete_image(blob_img);
+ self.wrench.api.update_resources(txn.resource_updates);
+ }
+
+ fn test_offscreen_blob(&mut self) {
+ println!("\toffscreen blob update.");
+
+ assert_eq!(self.wrench.device_pixel_ratio, 1.);
+
+ let window_size = self.window.get_inner_size();
+
+ let test_size = DeviceUintSize::new(800, 800);
+
+ let window_rect = DeviceUintRect::new(
+ DeviceUintPoint::new(0, window_size.height - test_size.height),
+ test_size,
+ );
+
+ // This exposes a crash in tile decomposition
+ let mut txn = Transaction::new();
+ let layout_size = LayoutSize::new(800., 800.);
+
+ let blob_img = self.wrench.api.generate_image_key();
+ txn.add_image(
+ blob_img,
+ ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false),
+ ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
+ None,
+ );
+
+ let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
+
+ let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.));
+
+ let image_size = size(1510., 1510.);
+
+ // setup some malicious image size parameters
+ builder.push_image(
+ &info,
+ image_size,
+ image_size,
+ ImageRendering::Auto,
+ AlphaType::PremultipliedAlpha,
+ blob_img,
+ );
+
+ let mut epoch = Epoch(0);
+
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
+
+ let original_pixels = self.render_and_get_pixels(window_rect);
+
+ let mut epoch = Epoch(1);
+
+ let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
+
+ let info = LayoutPrimitiveInfo::new(rect(-10000., 0.0, 1510., 1510.));
+
+ let image_size = size(1510., 1510.);
+
+ // setup some malicious image size parameters
+ builder.push_image(
+ &info,
+ image_size,
+ image_size,
+ ImageRendering::Auto,
+ AlphaType::PremultipliedAlpha,
+ blob_img,
+ );
+
+ self.submit_dl(&mut epoch, layout_size, builder, &[]);
+
+ let _offscreen_pixels = self.render_and_get_pixels(window_rect);
+
+ let mut txn = Transaction::new();
+
+ txn.update_image(
+ blob_img,
+ ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false),
+ ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
+ Some(rect(10, 10, 100, 100)),
+ );
+
+ let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
+
+ let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.));
+
+ let image_size = size(1510., 1510.);
+
+ // setup some malicious image size parameters
+ builder.push_image(
+ &info,
+ image_size,
+ image_size,
+ ImageRendering::Auto,
+ AlphaType::PremultipliedAlpha,
+ blob_img,
+ );
+
+ let mut epoch = Epoch(2);
+
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
+
+ let pixels = self.render_and_get_pixels(window_rect);
+
+ assert!(pixels == original_pixels);
+
+ // Leaving a tiled blob image in the resource cache
+ // confuses the `test_capture`. TODO: remove this
+ txn = Transaction::new();
+ txn.delete_image(blob_img);
+ self.wrench.api.update_resources(txn.resource_updates);
}
fn test_retained_blob_images_test(&mut self) {
println!("\tretained blob images test...");
let blob_img;
let window_size = self.window.get_inner_size();
let test_size = DeviceUintSize::new(400, 400);
let window_rect = DeviceUintRect::new(
DeviceUintPoint::new(0, window_size.height - test_size.height),
test_size,
);
let layout_size = LayoutSize::new(400., 400.);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
{
let api = &self.wrench.api;
blob_img = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
}
// draw the blob the first time
@@ -242,17 +372,17 @@ impl<'a> RawtestHarness<'a> {
size(0.0, 0.0),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
let mut epoch = Epoch(0);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let called = Arc::new(AtomicIsize::new(0));
let called_inner = Arc::clone(&called);
self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
called_inner.fetch_add(1, Ordering::SeqCst);
});
@@ -269,17 +399,19 @@ impl<'a> RawtestHarness<'a> {
&info,
size(200.0, 200.0),
size(0.0, 0.0),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
- self.submit_dl(&mut epoch, layout_size, builder, None);
+ txn.resource_updates.clear();
+
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_second = self.render_and_get_pixels(window_rect);
// make sure we only requested once
assert!(called.load(Ordering::SeqCst) == 1);
// use png;
// png::save_flipped("out1.png", &pixels_first, window_rect.size);
@@ -295,29 +427,29 @@ impl<'a> RawtestHarness<'a> {
let test_size = DeviceUintSize::new(400, 400);
let window_rect = DeviceUintRect::new(
point(0, window_size.height - test_size.height),
test_size,
);
let layout_size = LayoutSize::new(400., 400.);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let (blob_img, blob_img2) = {
let api = &self.wrench.api;
blob_img = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
blob_img2 = api.generate_image_key();
- resources.add_image(
+ txn.add_image(
blob_img2,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(80, 50, 150, 255))),
None,
);
(blob_img, blob_img2)
};
@@ -359,53 +491,53 @@ impl<'a> RawtestHarness<'a> {
blob_img2,
);
};
push_images(&mut builder);
let mut epoch = Epoch(0);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_first = self.render_and_get_pixels(window_rect);
// update and redraw both images
- let mut resources = ResourceUpdates::new();
- resources.update_image(
+ let mut txn = Transaction::new();
+ txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
- resources.update_image(
+ txn.update_image(
blob_img2,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(59, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
push_images(&mut builder);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_second = self.render_and_get_pixels(window_rect);
// only update the first image
- let mut resources = ResourceUpdates::new();
- resources.update_image(
+ let mut txn = Transaction::new();
+ txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))),
Some(rect(200, 200, 100, 100)),
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
push_images(&mut builder);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_third = 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);
}
@@ -415,21 +547,21 @@ impl<'a> RawtestHarness<'a> {
let test_size = DeviceUintSize::new(400, 400);
let window_rect = DeviceUintRect::new(
point(0, window_size.height - test_size.height),
test_size,
);
let layout_size = LayoutSize::new(400., 400.);
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let blob_img = {
let img = self.wrench.api.generate_image_key();
- resources.add_image(
+ txn.add_image(
img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
img
};
@@ -443,22 +575,22 @@ impl<'a> RawtestHarness<'a> {
size(0.0, 0.0),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
let mut epoch = Epoch(0);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_first = self.render_and_get_pixels(window_rect);
// draw the blob image a second time after updating it with the same color
- let mut resources = ResourceUpdates::new();
- resources.update_image(
+ let mut txn = Transaction::new();
+ txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
// make a new display list that refers to the first image
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
@@ -467,22 +599,22 @@ impl<'a> RawtestHarness<'a> {
&info,
size(200.0, 200.0),
size(0.0, 0.0),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_second = self.render_and_get_pixels(window_rect);
// draw the blob image a third time after updating it with a different color
- let mut resources = ResourceUpdates::new();
- resources.update_image(
+ let mut txn = Transaction::new();
+ txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))),
Some(rect(200, 200, 100, 100)),
);
// make a new display list that refers to the first image
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
@@ -491,17 +623,17 @@ impl<'a> RawtestHarness<'a> {
&info,
size(200.0, 200.0),
size(0.0, 0.0),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
- self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_third = self.render_and_get_pixels(window_rect);
assert!(pixels_first == pixels_second);
assert!(pixels_first != pixels_third);
}
// Ensures that content doing a save-restore produces the same results as not
fn test_save_restore(&mut self) {
@@ -532,23 +664,23 @@ impl<'a> RawtestHarness<'a> {
builder.save();
let clip = builder.define_clip(
rect(80., 80., 90., 90.),
None::<ComplexClipRegion>,
None
);
builder.push_clip_id(clip);
builder.push_rect(&PrimitiveInfo::new(rect(110., 110., 50., 50.)),
- ColorF::new(0.0, 1.0, 0.0, 1.0));
+ ColorF::new(0.0, 1.0, 0.0, 1.0));
builder.push_shadow(&PrimitiveInfo::new(rect(100., 100., 100., 100.)),
- Shadow {
- offset: LayoutVector2D::new(1.0, 1.0),
- blur_radius: 1.0,
- color: ColorF::new(0.0, 0.0, 0.0, 1.0),
- });
+ Shadow {
+ offset: LayoutVector2D::new(1.0, 1.0),
+ blur_radius: 1.0,
+ color: ColorF::new(0.0, 0.0, 0.0, 1.0),
+ });
builder.push_line(&PrimitiveInfo::new(rect(110., 110., 50., 2.)),
0.0, LineOrientation::Horizontal,
&ColorF::new(0.0, 0.0, 0.0, 1.0), LineStyle::Solid);
builder.restore();
}
{
builder.save();
@@ -562,17 +694,19 @@ impl<'a> RawtestHarness<'a> {
ColorF::new(0.0, 0.0, 1.0, 1.0));
builder.pop_clip_id();
builder.clear_save();
}
builder.pop_clip_id();
- self.submit_dl(&mut Epoch(0), layout_size, builder, None);
+ let txn = Transaction::new();
+
+ self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates);
self.render_and_get_pixels(window_rect)
};
let first = do_test(false);
let second = do_test(true);
assert_eq!(first, second);
@@ -585,19 +719,19 @@ impl<'a> RawtestHarness<'a> {
let dim = self.window.get_inner_size();
let window_rect = DeviceUintRect::new(
point(0, dim.height - layout_size.height as u32),
size(layout_size.width as u32, layout_size.height as u32),
);
// 1. render some scene
- let mut resources = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let image = self.wrench.api.generate_image_key();
- resources.add_image(
+ txn.add_image(
image,
ImageDescriptor::new(1, 1, ImageFormat::BGRA8, true, false),
ImageData::new(vec![0xFF, 0, 0, 0xFF]),
None,
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
@@ -663,17 +797,18 @@ impl<'a> RawtestHarness<'a> {
fn test_zero_height_window(&mut self) {
println!("\tzero height test...");
let layout_size = LayoutSize::new(120.0, 0.0);
let window_size = DeviceUintSize::new(layout_size.width as u32, layout_size.height as u32);
let doc_id = self.wrench.api.add_document(window_size, 1);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
- let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0)));
+ let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(),
+ LayoutSize::new(100.0, 100.0)));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
let mut txn = Transaction::new();
txn.set_root_pipeline(self.wrench.root_pipeline_id);
txn.set_display_list(
Epoch(1),
Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
layout_size,
@@ -704,17 +839,17 @@ impl<'a> RawtestHarness<'a> {
// Add a simple 100x100 rectangle at 100,0.
let mut info = LayoutPrimitiveInfo::new(LayoutRect::new(
LayoutPoint::new(100., 0.),
LayoutSize::new(100., 100.)
));
info.tag = Some((0, 2));
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
- let make_rounded_complex_clip = | rect: &LayoutRect, radius: f32 | -> ComplexClipRegion {
+ let make_rounded_complex_clip = |rect: &LayoutRect, radius: f32| -> ComplexClipRegion {
ComplexClipRegion::new(
*rect,
BorderRadius::uniform_size(LayoutSize::new(radius, radius)),
ClipMode::Clip
)
};
@@ -738,32 +873,33 @@ impl<'a> RawtestHarness<'a> {
));
let mut info = LayoutPrimitiveInfo::new(rect);
info.tag = Some((0, 5));
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
builder.pop_clip_id();
let mut epoch = Epoch(0);
- self.submit_dl(&mut epoch, layout_size, builder, None);
+ let txn = Transaction::new();
+ self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
// 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 {
+ let hit_test = |point: WorldPoint| -> HitTestResult {
self.wrench.api.hit_test(
self.wrench.document_id,
None,
point,
HitTestFlags::FIND_ALL,
)
};
- let assert_hit_test = | point: WorldPoint, tags: Vec<ItemTag> | {
+ 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);
}
};
@@ -774,17 +910,17 @@ impl<'a> RawtestHarness<'a> {
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 | {
+ 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.);
assert_hit_test(
WorldPoint::new(point.x + (size.width / 2.), point.y + (size.height / 2.)),
vec![tag, (0, 1)]
);
--- a/gfx/wrench/src/ron_frame_writer.rs
+++ b/gfx/wrench/src/ron_frame_writer.rs
@@ -82,18 +82,18 @@ impl RonFrameWriter {
let mut file = fs::File::create(&frame_file_name).unwrap();
let s = ron::ser::to_string_pretty(&dl, Default::default()).unwrap();
file.write_all(&s.into_bytes()).unwrap();
file.write_all(b"\n").unwrap();
}
- fn update_resources(&mut self, updates: &ResourceUpdates) {
- for update in &updates.updates {
+ fn update_resources(&mut self, updates: &[ResourceUpdate]) {
+ for update in updates {
match *update {
ResourceUpdate::AddImage(ref img) => {
let bytes = match img.data {
ImageData::Raw(ref v) => (**v).clone(),
ImageData::External(_) | ImageData::Blob(_) => {
return;
}
};
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -275,44 +275,31 @@ impl Wrench {
self.api.send_transaction(self.document_id, txn);
self.set_title("");
}
pub fn layout_simple_ascii(
&mut self,
font_key: FontKey,
instance_key: FontInstanceKey,
- render_mode: Option<FontRenderMode>,
text: &str,
size: Au,
origin: LayoutPoint,
flags: FontInstanceFlags,
) -> (Vec<u32>, Vec<LayoutPoint>, LayoutRect) {
// Map the string codepoints to glyph indices in this font.
// Just drop any glyph that isn't present in this font.
let indices: Vec<u32> = self.api
.get_glyph_indices(font_key, text)
.iter()
.filter_map(|idx| *idx)
.collect();
- let render_mode = render_mode.unwrap_or(<FontInstanceOptions as Default>::default().render_mode);
- let subpx_dir = SubpixelDirection::Horizontal.limit_by(render_mode);
-
// Retrieve the metrics for each glyph.
- let mut keys = Vec::new();
- for glyph_index in &indices {
- keys.push(GlyphKey::new(
- *glyph_index,
- LayoutPoint::zero(),
- render_mode,
- subpx_dir,
- ));
- }
- let metrics = self.api.get_glyph_dimensions(instance_key, keys);
+ let metrics = self.api.get_glyph_dimensions(instance_key, indices.clone());
let mut bounding_rect = LayoutRect::zero();
let mut positions = Vec::new();
let mut cursor = origin;
let direction = if flags.contains(FontInstanceFlags::TRANSPOSE) {
LayoutVector2D::new(
0.0,
@@ -380,19 +367,19 @@ impl Wrench {
self.window_size.width as f32,
self.window_size.height as f32,
)
}
#[cfg(target_os = "windows")]
pub fn font_key_from_native_handle(&mut self, descriptor: &NativeFontHandle) -> FontKey {
let key = self.api.generate_font_key();
- let mut resources = ResourceUpdates::new();
- resources.add_native_font(key, descriptor.clone());
- self.api.update_resources(resources);
+ let mut txn = Transaction::new();
+ txn.add_native_font(key, descriptor.clone());
+ self.api.update_resources(txn.resource_updates);
key
}
#[cfg(target_os = "windows")]
pub fn font_key_from_name(&mut self, font_name: &str) -> FontKey {
let system_fc = dwrote::FontCollection::system();
let family = system_fc.get_font_family_by_name(font_name).unwrap();
let font = family.get_first_matching_font(
@@ -451,49 +438,49 @@ impl Wrench {
#[cfg(target_os = "android")]
pub fn font_key_from_name(&mut self, font_name: &str) -> FontKey {
unimplemented!()
}
pub fn font_key_from_bytes(&mut self, bytes: Vec<u8>, index: u32) -> FontKey {
let key = self.api.generate_font_key();
- let mut update = ResourceUpdates::new();
- update.add_raw_font(key, bytes, index);
- self.api.update_resources(update);
+ let mut txn = Transaction::new();
+ txn.add_raw_font(key, bytes, index);
+ self.api.update_resources(txn.resource_updates);
key
}
pub fn add_font_instance(&mut self,
font_key: FontKey,
size: Au,
flags: FontInstanceFlags,
render_mode: Option<FontRenderMode>,
bg_color: Option<ColorU>,
) -> FontInstanceKey {
let key = self.api.generate_font_instance_key();
- let mut update = ResourceUpdates::new();
+ let mut txn = Transaction::new();
let mut options: FontInstanceOptions = Default::default();
options.flags |= flags;
if let Some(render_mode) = render_mode {
options.render_mode = render_mode;
}
if let Some(bg_color) = bg_color {
options.bg_color = bg_color;
}
- update.add_font_instance(key, font_key, size, Some(options), None, Vec::new());
- self.api.update_resources(update);
+ txn.add_font_instance(key, font_key, size, Some(options), None, Vec::new());
+ self.api.update_resources(txn.resource_updates);
key
}
#[allow(dead_code)]
pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
- let mut update = ResourceUpdates::new();
- update.delete_font_instance(key);
- self.api.update_resources(update);
+ let mut txn = Transaction::new();
+ txn.delete_font_instance(key);
+ self.api.update_resources(txn.resource_updates);
}
pub fn update(&mut self, dim: DeviceUintSize) {
if dim != self.window_size {
self.window_size = dim;
}
}
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -225,27 +225,27 @@ impl YamlFrameReader {
font_render_mode: None,
image_map: HashMap::new(),
clip_id_map: HashMap::new(),
allow_mipmaps: false,
}
}
pub fn deinit(mut self, wrench: &mut Wrench) {
- let mut updates = ResourceUpdates::new();
+ let mut txn = Transaction::new();
for (_, font_instance) in self.font_instances.drain() {
- updates.delete_font_instance(font_instance);
+ txn.delete_font_instance(font_instance);
}
for (_, font) in self.fonts.drain() {
- updates.delete_font(font);
+ txn.delete_font(font);
}
- wrench.api.update_resources(updates);
+ wrench.api.update_resources(txn.resource_updates);
}
pub fn yaml_path(&self) -> &PathBuf {
&self.yaml_path
}
pub fn new_from_args(args: &clap::ArgMatches) -> YamlFrameReader {
let yaml_file = args.value_of("INPUT").map(|s| PathBuf::from(s)).unwrap();
@@ -506,19 +506,19 @@ impl YamlFrameReader {
_ => {
panic!("Failed to load image {:?}", file.to_str());
}
}
}
};
let tiling = tiling.map(|tile_size| tile_size as u16);
let image_key = wrench.api.generate_image_key();
- let mut resources = ResourceUpdates::new();
- resources.add_image(image_key, descriptor, image_data, tiling);
- wrench.api.update_resources(resources);
+ let mut txn = Transaction::new();
+ txn.add_image(image_key, descriptor, image_data, tiling);
+ wrench.api.update_resources(txn.resource_updates);
let val = (
image_key,
LayoutSize::new(descriptor.size.width as f32, descriptor.size.height as f32),
);
self.image_map.insert(key, val);
val
}
@@ -1205,17 +1205,16 @@ impl YamlFrameReader {
} 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,
- self.font_render_mode,
text,
size,
origin,
flags,
);
let glyphs = glyph_indices
.iter()
--- a/gfx/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wrench/src/yaml_frame_writer.rs
@@ -498,18 +498,18 @@ impl YamlFrameWriter {
frame_file_name.push(format!("frame-{}.yaml", current_shown_frame));
let mut file = fs::File::create(&frame_file_name).unwrap();
file.write_all(&sb).unwrap();
}
scene.finish_display_list(self.pipeline_id.unwrap(), dl);
}
- fn update_resources(&mut self, updates: &ResourceUpdates) {
- for update in &updates.updates {
+ fn update_resources(&mut self, updates: &[ResourceUpdate]) {
+ for update in updates {
match *update {
ResourceUpdate::AddImage(ref img) => {
if let Some(ref data) = self.images.get(&img.key) {
if data.path.is_some() {
return;
}
}
@@ -540,17 +540,18 @@ impl YamlFrameWriter {
assert_eq!(data.width, img.descriptor.size.width);
assert_eq!(data.height, img.descriptor.size.height);
assert_eq!(data.format, img.descriptor.format);
if let ImageData::Raw(ref bytes) = img.data {
data.path = None;
data.bytes = Some((**bytes).clone());
} else {
- // Other existing image types only make sense within the gecko integration.
+ // Other existing image types only make sense
+ // within the gecko integration.
println!(
"Wrench only supports updating buffer images ({}).",
"ignoring update command"
);
}
}
}
ResourceUpdate::DeleteImage(img) => {