--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -3,26 +3,31 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef WR_VERTEX_SHADER
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
+ int brush_flags,
vec4 segment_data
);
#define VECS_PER_BRUSH_PRIM 2
#define VECS_PER_SEGMENT 2
#define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1
+#define BRUSH_FLAG_SEGMENT_RELATIVE 2
+#define BRUSH_FLAG_SEGMENT_REPEAT_X 4
+#define BRUSH_FLAG_SEGMENT_REPEAT_Y 8
struct BrushInstance {
int picture_address;
int prim_address;
int clip_chain_rect_index;
int scroll_node_id;
int clip_address;
int z;
@@ -142,19 +147,21 @@ void main(void) {
);
#endif
// Run the specific brush VS code to write interpolators.
brush_vs(
vi,
brush.prim_address + VECS_PER_BRUSH_PRIM,
brush_prim.local_rect,
+ local_segment_rect,
brush.user_data,
scroll_node.transform,
pic_task,
+ brush.flags,
segment_data[1]
);
}
#endif
#ifdef WR_FRAGMENT_SHADER
struct Fragment {
--- a/gfx/webrender/res/brush_blend.glsl
+++ b/gfx/webrender/res/brush_blend.glsl
@@ -1,14 +1,13 @@
/* 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/. */
-#define VECS_PER_SPECIFIC_BRUSH 2
-#define FORCE_NO_PERSPECTIVE
+#define VECS_PER_SPECIFIC_BRUSH 3
#include shared,prim_shared,brush
varying vec3 vUv;
flat varying float vAmount;
flat varying int vOp;
flat varying mat3 vColorMat;
@@ -16,19 +15,21 @@ flat varying vec3 vColorOffset;
flat varying vec4 vUvClipBounds;
#ifdef WR_VERTEX_SHADER
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
+ int brush_flags,
vec4 unused
) {
PictureTask src_task = fetch_picture_task(user_data.x);
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
vec2 uv = vi.snapped_device_pos +
src_task.common_data.task_rect.p0 -
src_task.content_origin;
vUv = vec3(uv / texture_size, src_task.common_data.texture_layer_index);
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -1,13 +1,13 @@
/* 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/. */
-#define VECS_PER_SPECIFIC_BRUSH 2
+#define VECS_PER_SPECIFIC_BRUSH 3
#include shared,prim_shared,brush
#ifdef WR_FEATURE_ALPHA_PASS
varying vec2 vLocalPos;
#endif
// Interpolated uv coordinates in xy, and layer in z.
@@ -24,23 +24,25 @@ flat varying vec2 vMaskSwizzle;
flat varying vec2 vTileRepeat;
#endif
#ifdef WR_VERTEX_SHADER
struct ImageBrushData {
vec4 color;
vec4 background_color;
+ vec2 stretch_size;
};
ImageBrushData fetch_image_data(int address) {
- vec4[2] raw_data = fetch_from_resource_cache_2(address);
+ vec4[3] raw_data = fetch_from_resource_cache_3(address);
ImageBrushData data = ImageBrushData(
raw_data[0],
- raw_data[1]
+ raw_data[1],
+ raw_data[2].xy
);
return data;
}
#ifdef WR_FEATURE_ALPHA_PASS
vec2 transform_point_snapped(
vec2 local_pos,
RectWithSize local_rect,
@@ -52,34 +54,62 @@ vec2 transform_point_snapped(
return device_pos + snap_offset;
}
#endif
void brush_vs(
VertexInfo vi,
int prim_address,
- RectWithSize local_rect,
+ RectWithSize prim_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
- vec4 repeat
+ int brush_flags,
+ vec4 texel_rect
) {
+ ImageBrushData image_data = fetch_image_data(prim_address);
+
// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
// non-normalized texture coordinates.
#ifdef WR_FEATURE_TEXTURE_RECT
vec2 texture_size = vec2(1, 1);
#else
vec2 texture_size = vec2(textureSize(sColor0, 0));
#endif
ImageResource res = fetch_image_resource(user_data.x);
vec2 uv0 = res.uv_rect.p0;
vec2 uv1 = res.uv_rect.p1;
+ RectWithSize local_rect = prim_rect;
+ vec2 stretch_size = image_data.stretch_size;
+
+ // If this segment should interpolate relative to the
+ // segment, modify the parameters for that.
+ if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
+ local_rect = segment_rect;
+ stretch_size = local_rect.size;
+
+ // Note: Here we can assume that texels in device
+ // space map to local space, due to how border-image
+ // works. That assumption may not hold if this
+ // is used for other purposes in the future.
+ if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
+ stretch_size.x = texel_rect.z - texel_rect.x;
+ }
+ if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
+ stretch_size.y = texel_rect.w - texel_rect.y;
+ }
+
+ uv0 = res.uv_rect.p0 + texel_rect.xy;
+ uv1 = res.uv_rect.p0 + texel_rect.zw;
+ }
+
vUv.z = res.layer;
// Handle case where the UV coords are inverted (e.g. from an
// external image).
vec2 min_uv = min(uv0, uv1);
vec2 max_uv = max(uv0, uv1);
vUvSampleBounds = vec4(
@@ -87,17 +117,16 @@ void brush_vs(
max_uv - vec2(0.5)
) / texture_size.xyxy;
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
#ifdef WR_FEATURE_ALPHA_PASS
int color_mode = user_data.y >> 16;
int raster_space = user_data.y & 0xffff;
- ImageBrushData image_data = fetch_image_data(prim_address);
if (color_mode == COLOR_MODE_FROM_PASS) {
color_mode = uMode;
}
// Derive the texture coordinates for this image, based on
// whether the source image is a local-space or screen-space
// image.
@@ -113,16 +142,17 @@ void brush_vs(
break;
}
default:
break;
}
#endif
// Offset and scale vUv here to avoid doing it in the fragment shader.
+ vec2 repeat = local_rect.size / stretch_size;
vUv.xy = mix(uv0, uv1, f) - min_uv;
vUv.xy /= texture_size;
vUv.xy *= repeat.xy;
#ifdef WR_FEATURE_TEXTURE_RECT
vUvBounds = vec4(0.0, 0.0, vec2(textureSize(sColor0)));
#else
vUvBounds = vec4(min_uv, max_uv) / texture_size.xyxy;
--- a/gfx/webrender/res/brush_linear_gradient.glsl
+++ b/gfx/webrender/res/brush_linear_gradient.glsl
@@ -22,53 +22,61 @@ varying vec2 vPos;
varying vec2 vLocalPos;
flat varying vec2 vTileRepeat;
#endif
#ifdef WR_VERTEX_SHADER
struct Gradient {
vec4 start_end_point;
- vec4 extend_mode;
+ int extend_mode;
+ vec2 stretch_size;
};
Gradient fetch_gradient(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
- return Gradient(data[0], data[1]);
+ return Gradient(
+ data[0],
+ int(data[1].x),
+ data[1].yz
+ );
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
- vec4 tile_repeat
+ int brush_flags,
+ vec4 unused
) {
Gradient gradient = fetch_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
vec2 start_point = gradient.start_end_point.xy;
vec2 end_point = gradient.start_end_point.zw;
vec2 dir = end_point - start_point;
vStartPoint = start_point;
vScaledDir = dir / dot(dir, dir);
- vRepeatedSize = local_rect.size / tile_repeat.xy;
+ vec2 tile_repeat = local_rect.size / gradient.stretch_size;
+ vRepeatedSize = gradient.stretch_size;
vGradientAddress = user_data.x;
// Whether to repeat the gradient along the line instead of clamping.
- vGradientRepeat = float(int(gradient.extend_mode.x) != EXTEND_MODE_CLAMP);
+ vGradientRepeat = float(gradient.extend_mode != EXTEND_MODE_CLAMP);
#ifdef WR_FEATURE_ALPHA_PASS
- vTileRepeat = tile_repeat.xy;
+ vTileRepeat = tile_repeat;
vLocalPos = vi.local_pos;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
Fragment brush_fs() {
--- a/gfx/webrender/res/brush_mix_blend.glsl
+++ b/gfx/webrender/res/brush_mix_blend.glsl
@@ -1,29 +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/. */
-#define VECS_PER_SPECIFIC_BRUSH 2
+#define VECS_PER_SPECIFIC_BRUSH 3
#include shared,prim_shared,brush
varying vec3 vSrcUv;
varying vec3 vBackdropUv;
flat varying int vOp;
#ifdef WR_VERTEX_SHADER
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
+ int brush_flags,
vec4 unused
) {
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
vOp = user_data.x;
PictureTask src_task = fetch_picture_task(user_data.z);
vec2 src_uv = vi.snapped_device_pos +
src_task.common_data.task_rect.p0 -
--- a/gfx/webrender/res/brush_radial_gradient.glsl
+++ b/gfx/webrender/res/brush_radial_gradient.glsl
@@ -20,53 +20,62 @@ flat varying vec2 vRepeatedSize;
varying vec2 vLocalPos;
flat varying vec2 vTileRepeat;
#endif
#ifdef WR_VERTEX_SHADER
struct RadialGradient {
vec4 center_start_end_radius;
- vec4 ratio_xy_extend_mode;
+ float ratio_xy;
+ int extend_mode;
+ vec2 stretch_size;
};
RadialGradient fetch_radial_gradient(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
- return RadialGradient(data[0], data[1]);
+ return RadialGradient(
+ data[0],
+ data[1].x,
+ int(data[1].y),
+ data[1].zw
+ );
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
- vec4 tile_repeat
+ int brush_flags,
+ vec4 unused
) {
RadialGradient gradient = fetch_radial_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
vCenter = gradient.center_start_end_radius.xy;
vStartRadius = gradient.center_start_end_radius.z;
vEndRadius = gradient.center_start_end_radius.w;
// Transform all coordinates by the y scale so the
// fragment shader can work with circles
- float ratio_xy = gradient.ratio_xy_extend_mode.x;
- vPos.y *= ratio_xy;
- vCenter.y *= ratio_xy;
- vRepeatedSize = local_rect.size / tile_repeat.xy;
- vRepeatedSize.y *= ratio_xy;
+ vec2 tile_repeat = local_rect.size / gradient.stretch_size;
+ vPos.y *= gradient.ratio_xy;
+ vCenter.y *= gradient.ratio_xy;
+ vRepeatedSize = gradient.stretch_size;
+ vRepeatedSize.y *= gradient.ratio_xy;
vGradientAddress = user_data.x;
// Whether to repeat the gradient instead of clamping.
- vGradientRepeat = float(int(gradient.ratio_xy_extend_mode.y) != EXTEND_MODE_CLAMP);
+ vGradientRepeat = float(gradient.extend_mode != EXTEND_MODE_CLAMP);
#ifdef WR_FEATURE_ALPHA_PASS
vTileRepeat = tile_repeat.xy;
vLocalPos = vi.local_pos;
#endif
}
#endif
--- a/gfx/webrender/res/brush_solid.glsl
+++ b/gfx/webrender/res/brush_solid.glsl
@@ -22,19 +22,21 @@ SolidBrush fetch_solid_primitive(int add
vec4 data = fetch_from_resource_cache_1(address);
return SolidBrush(data);
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
+ int brush_flags,
vec4 unused
) {
SolidBrush prim = fetch_solid_primitive(prim_address);
vColor = prim.color;
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
--- a/gfx/webrender/res/brush_yuv_image.glsl
+++ b/gfx/webrender/res/brush_yuv_image.glsl
@@ -69,19 +69,21 @@ void write_uv_rect(
uv_bounds /= texture_size.xyxy;
#endif
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
+ RectWithSize segment_rect,
ivec3 user_data,
mat4 transform,
PictureTask pic_task,
+ int brush_flags,
vec4 unused
) {
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -14,18 +14,19 @@ use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress};
use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex, ClipMaskBorderCornerDotDash};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, CompositePrimitiveInstance};
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::{CachedGradient, ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
-use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PictureIndex, PrimitiveRun};
+use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, CachedGradient, DeferredResolve};
+use prim_store::{EdgeAaSegmentMask, ImageSource, PictureIndex, PrimitiveIndex, PrimitiveKind};
+use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore};
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};
@@ -1265,27 +1266,31 @@ impl AlphaBatchBuilder {
let opaque_batch = self.batch_list.opaque_batch_list.get_suitable_batch(
opaque_batch_key,
task_relative_bounding_rect
);
for (i, segment) in segment_desc.segments.iter().enumerate() {
let is_inner = segment.edge_flags.is_empty();
let needs_blending = !prim_metadata.opacity.is_opaque ||
- segment.clip_task_id.is_some() ||
+ segment.clip_task_id.needs_blending() ||
(!is_inner && transform_kind == TransformedRectKind::Complex);
- let clip_task_address = segment
- .clip_task_id
- .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
+ let clip_task_address = match segment.clip_task_id {
+ BrushSegmentTaskId::RenderTaskId(id) =>
+ render_tasks.get_task_address(id),
+ BrushSegmentTaskId::Opaque => OPAQUE_TASK_ADDRESS,
+ BrushSegmentTaskId::Empty => continue,
+ };
let instance = PrimitiveInstance::from(BrushInstance {
segment_index: i as i32,
edge_flags: segment.edge_flags,
clip_task_address,
+ brush_flags: base_instance.brush_flags | segment.brush_flags,
..base_instance
});
if needs_blending {
alpha_batch.push(instance);
} else {
opaque_batch.push(instance);
}
@@ -1356,16 +1361,41 @@ 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,
+ );
+
+ 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)),
+ textures,
+ [
+ cache_item.uv_rect_handle.as_int(gpu_cache),
+ (ShaderColorMode::ColorBitmap as i32) << 16|
+ RasterizationSpace::Local as i32,
+ 0,
+ ],
+ ))
+ }
+ }
BrushKind::Picture { .. } => {
panic!("bug: get_batch_key is handled at higher level for pictures");
}
BrushKind::Solid { .. } => {
Some((
BrushBatchKind::Solid,
BatchTextures::no_texture(),
[0; 3],
@@ -1487,16 +1517,17 @@ impl AlphaBatchHelpers for PrimitiveStor
AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
AlphaType::Alpha => BlendMode::Alpha,
}
}
BrushKind::Solid { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::LinearGradient { .. } |
+ BrushKind::Border { .. } |
BrushKind::Picture { .. } => {
BlendMode::PremultipliedAlpha
}
}
}
PrimitiveKind::Image => {
let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
match image_cpu.alpha_type {
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.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/. */
use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipMode, ColorF, LayoutPoint};
-use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, NormalBorder, RepeatMode, TexelRect};
+use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, NormalBorder};
use clip::ClipSource;
use ellipse::Ellipse;
use display_list_flattener::DisplayListFlattener;
+use gpu_types::BrushFlags;
use gpu_cache::GpuDataRequest;
use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor};
use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
use util::{lerp, pack_as_float};
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum BorderCornerInstance {
@@ -502,17 +503,19 @@ impl<'a> DisplayListFlattener<'a> {
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),
true,
- EdgeAaSegmentMask::all() // Note: this doesn't seem right, needs revision
+ 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 {
let descriptor = BrushSegmentDescriptor {
segments: vec![
segment(p0.x, p0.y, p1.x, p1.y),
segment(p2.x, p0.y, p3.x, p1.y),
@@ -921,60 +924,8 @@ struct DotInfo {
diameter: f32,
}
impl DotInfo {
fn new(arc_pos: f32, diameter: f32) -> DotInfo {
DotInfo { arc_pos, diameter }
}
}
-
-#[derive(Debug, Clone)]
-pub struct ImageBorderSegment {
- pub geom_rect: LayoutRect,
- pub sub_rect: TexelRect,
- pub stretch_size: LayoutSize,
- pub tile_spacing: LayoutSize,
-}
-
-impl ImageBorderSegment {
- pub fn new(
- rect: LayoutRect,
- sub_rect: TexelRect,
- repeat_horizontal: RepeatMode,
- repeat_vertical: RepeatMode,
- ) -> ImageBorderSegment {
- let tile_spacing = LayoutSize::zero();
-
- debug_assert!(sub_rect.uv1.x >= sub_rect.uv0.x);
- debug_assert!(sub_rect.uv1.y >= sub_rect.uv0.y);
-
- let image_size = LayoutSize::new(
- sub_rect.uv1.x - sub_rect.uv0.x,
- sub_rect.uv1.y - sub_rect.uv0.y,
- );
-
- let stretch_size_x = match repeat_horizontal {
- RepeatMode::Stretch => rect.size.width,
- RepeatMode::Repeat => image_size.width,
- RepeatMode::Round | RepeatMode::Space => {
- error!("Round/Space not supported yet!");
- rect.size.width
- }
- };
-
- let stretch_size_y = match repeat_vertical {
- RepeatMode::Stretch => rect.size.height,
- RepeatMode::Repeat => image_size.height,
- RepeatMode::Round | RepeatMode::Space => {
- error!("Round/Space not supported yet!");
- rect.size.height
- }
- };
-
- ImageBorderSegment {
- geom_rect: rect,
- sub_rect,
- stretch_size: LayoutSize::new(stretch_size_x, stretch_size_y),
- tile_spacing,
- }
- }
-}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -9,30 +9,30 @@ use api::{DevicePixelScale, DeviceUintRe
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, LayoutPrimitiveInfo};
use api::{LayoutRect, LayoutVector2D, LayoutSize, LayoutTransform};
use api::{LineOrientation, LineStyle, LocalClip, PipelineId, PropertyBinding};
use api::{RepeatMode, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity, Shadow};
use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect, TileOffset};
use api::{TransformStyle, YuvColorSpace, YuvData};
use app_units::Au;
-use border::ImageBorderSegment;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
+use gpu_types::BrushFlags;
use hit_test::{HitTestingItem, HitTestingRun};
use image::{decompose_image, TiledImageInfo};
use internal_types::{FastHashMap, FastHashSet};
use picture::PictureCompositeMode;
-use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
-use prim_store::{CachedGradientIndex, ImageCacheKey, ImagePrimitiveCpu, ImageSource};
-use prim_store::{PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
+use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
+use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageCacheKey, ImagePrimitiveCpu, ImageSource};
+use prim_store::{BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest, TiledImageMap};
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};
@@ -1671,29 +1671,52 @@ impl<'a> DisplayListFlattener<'a> {
let br_outer = LayoutPoint::new(
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height,
);
let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
fn add_segment(
- segments: &mut Vec<ImageBorderSegment>,
+ segments: &mut Vec<BrushSegment>,
rect: LayoutRect,
uv_rect: TexelRect,
repeat_horizontal: RepeatMode,
- repeat_vertical: RepeatMode) {
+ repeat_vertical: RepeatMode
+ ) {
if uv_rect.uv1.x > uv_rect.uv0.x &&
uv_rect.uv1.y > uv_rect.uv0.y {
- segments.push(ImageBorderSegment::new(
- rect,
- uv_rect,
- repeat_horizontal,
- repeat_vertical,
- ));
+
+ // Use segment relative interpolation for all
+ // instances in this primitive.
+ let mut brush_flags = BrushFlags::SEGMENT_RELATIVE;
+
+ // Enable repeat modes on the segment.
+ 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,
+ true,
+ EdgeAaSegmentMask::empty(),
+ [
+ uv_rect.uv0.x,
+ uv_rect.uv0.y,
+ uv_rect.uv1.x,
+ uv_rect.uv1.y,
+ ],
+ brush_flags,
+ );
+
+ segments.push(segment);
}
}
// Build the list of image segments
let mut segments = vec![];
// Top left
add_segment(
@@ -1769,31 +1792,40 @@ impl<'a> DisplayListFlattener<'a> {
add_segment(
&mut segments,
LayoutRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
TexelRect::new(px2, py1, px3, py2),
RepeatMode::Stretch,
border.repeat_vertical,
);
- for segment in segments {
- let mut info = info.clone();
- info.rect = segment.geom_rect;
- self.add_image(
- clip_and_scroll,
- &info,
- segment.stretch_size,
- segment.tile_spacing,
- Some(segment.sub_rect),
- border.image_key,
- ImageRendering::Auto,
- AlphaType::PremultipliedAlpha,
- None,
- );
- }
+ let descriptor = BrushSegmentDescriptor {
+ segments,
+ clip_mask_kind: BrushClipMaskKind::Unknown,
+ };
+
+ let prim = BrushPrimitive::new(
+ BrushKind::Border {
+ request: ImageRequest {
+ key: border.image_key,
+ rendering: ImageRendering::Auto,
+ tile: None,
+ },
+ },
+ Some(descriptor),
+ );
+
+ let prim = PrimitiveContainer::Brush(prim);
+
+ self.add_primitive(
+ clip_and_scroll,
+ info,
+ Vec::new(),
+ prim,
+ );
}
BorderDetails::Normal(ref border) => {
self.add_normal_border(info, border, &border_item.widths, clip_and_scroll);
}
BorderDetails::Gradient(ref border) => for segment in create_segments(border.outset) {
let segment_rel = segment.origin - rect.origin;
let mut info = info.clone();
info.rect = segment;
--- a/gfx/webrender/src/ellipse.rs
+++ b/gfx/webrender/src/ellipse.rs
@@ -32,28 +32,39 @@ impl Ellipse {
pub fn find_angle_for_arc_length(&self, arc_length: f32) -> f32 {
// Clamp arc length to [0, pi].
let arc_length = arc_length.max(0.0).min(self.total_arc_length);
let epsilon = 0.01;
let mut low = 0.0;
let mut high = FRAC_PI_2;
let mut theta = 0.0;
+ let mut new_low = 0.0;
+ let mut new_high = FRAC_PI_2;
while low <= high {
theta = 0.5 * (low + high);
let length = get_simpson_length(theta, self.radius.width, self.radius.height);
if (length - arc_length).abs() < epsilon {
break;
} else if length < arc_length {
- low = theta;
+ new_low = theta;
} else {
- high = theta;
+ new_high = theta;
}
+
+ // If we have stopped moving down the arc, the answer that we have is as good as
+ // it is going to get. We break to avoid going into an infinite loop.
+ if new_low == low && new_high == high {
+ break;
+ }
+
+ high = new_high;
+ low = new_low;
}
theta
}
/// Get a point and tangent on this ellipse from a given angle.
/// This only works for the first quadrant of the ellipse.
pub fn get_point_and_tangent(&self, theta: f32) -> (LayoutPoint, LayoutPoint) {
@@ -149,8 +160,26 @@ fn get_simpson_length(theta: f32, rx: f3
4.0
};
sum += q * y;
}
(df / 3.0) * sum
}
+
+#[cfg(test)]
+pub mod test {
+ use super::*;
+
+ #[test]
+ fn find_angle_for_arc_length_for_long_eclipse() {
+ // Ensure that finding the angle on giant ellipses produces and answer and
+ // doesn't send us into an infinite loop.
+ let ellipse = Ellipse::new(LayoutSize::new(57500.0, 25.0));
+ let _ = ellipse.find_angle_for_arc_length(55674.53);
+ assert!(true);
+
+ let ellipse = Ellipse::new(LayoutSize::new(25.0, 57500.0));
+ let _ = ellipse.find_angle_for_arc_length(55674.53);
+ assert!(true);
+ }
+}
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -219,16 +219,20 @@ impl FontInstance {
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,
@@ -1071,9 +1075,9 @@ fn request_render_task_from_pathfinder(g
FontRenderMode::Subpixel => &mut render_passes.color_glyph_pass,
};
render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color);
Ok(root_task_id)
}
#[cfg(feature = "pathfinder")]
-pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
+pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
\ No newline at end of file
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -196,17 +196,25 @@ impl From<CompositePrimitiveInstance> fo
}
}
}
bitflags! {
/// Flags that define how the common brush shader
/// code should process this instance.
pub struct BrushFlags: u8 {
+ /// Apply perspective interpolation to UVs
const PERSPECTIVE_INTERPOLATION = 0x1;
+ /// Do interpolation relative to segment rect,
+ /// rather than primitive rect.
+ const SEGMENT_RELATIVE = 0x2;
+ /// Repeat UVs horizontally.
+ const SEGMENT_REPEAT_X = 0x4;
+ /// Repeat UVs vertically.
+ const SEGMENT_REPEAT_Y = 0x8;
}
}
// TODO(gw): While we are converting things over, we
// need to have the instance be the same
// size as an old PrimitiveInstance. In the
// future, we can compress this vertex
// format a lot - e.g. z, render task
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -474,31 +474,37 @@ impl PicturePrimitive {
// rect for the shadow. To tidy this up in future,
// we could consider abstracting the code in prim_store.rs
// that writes a brush primitive header.
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
// local_rect
// clip_rect
// [brush specific data]
- // [segment_rect, (repetitions.xy, 0.0, 0.0)]
+ // [segment_rect, segment data]
let shadow_rect = prim_metadata.local_rect.translate(&offset);
let shadow_clip_rect = prim_metadata.local_clip_rect.translate(&offset);
// local_rect, clip_rect
request.push(shadow_rect);
request.push(shadow_clip_rect);
// ImageBrush colors
request.push(color.premultiplied());
request.push(PremultipliedColorF::WHITE);
+ request.push([
+ prim_metadata.local_rect.size.width,
+ prim_metadata.local_rect.size.height,
+ 0.0,
+ 0.0,
+ ]);
- // segment rect / repetitions
+ // segment rect / extra data
request.push(shadow_rect);
- request.push([1.0, 1.0, 0.0, 0.0]);
+ request.push([0.0, 0.0, 0.0, 0.0]);
}
}
Some(PictureCompositeMode::MixBlend(..)) => {
let uv_rect_kind = calculate_uv_rect_kind(
&prim_metadata.local_rect,
&prim_run_context.scroll_node,
&prim_screen_rect.clipped,
frame_context.device_pixel_scale,
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -6,16 +6,20 @@ use api::{FontInstanceFlags, FontKey, Fo
use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
use dwrote;
use gamma_lut::{ColorLut, GammaLut};
use glyph_rasterizer::{FontInstance, FontTransform, GlyphFormat};
use glyph_rasterizer::{GlyphRasterResult, RasterizedGlyph};
use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
+#[cfg(feature = "pathfinder")]
+use pathfinder_font_renderer::{PathfinderComPtr, IDWriteFontFace};
+#[cfg(feature = "pathfinder")]
+use glyph_rasterizer::NativeFontHandleWrapper;
lazy_static! {
static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
family_name: "Arial".to_owned(),
weight: dwrote::FontWeight::Regular,
stretch: dwrote::FontStretch::Normal,
style: dwrote::FontStyle::Normal,
};
@@ -434,8 +438,21 @@ impl FontContext {
width,
height,
scale: if bitmaps { y_scale.recip() as f32 } else { 1.0 },
format: if bitmaps { GlyphFormat::Bitmap } else { font.get_glyph_format() },
bytes: bgra_pixels,
})
}
}
+
+#[cfg(feature = "pathfinder")]
+impl<'a> From<NativeFontHandleWrapper<'a>> for PathfinderComPtr<IDWriteFontFace> {
+ fn from(font_handle: NativeFontHandleWrapper<'a>) -> Self {
+ 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();
+ 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
@@ -13,17 +13,17 @@ use clip_scroll_tree::{ClipChainIndex, C
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 gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
-use gpu_types::{ClipChainRectIndex};
+use gpu_types::{BrushFlags, ClipChainRectIndex};
use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
#[cfg(debug_assertions)]
use render_backend::FrameId;
use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{ImageProperties, ImageRequest};
use scene::SceneProperties;
@@ -282,26 +282,30 @@ pub enum BrushKind {
gradient_index: CachedGradientIndex,
stops_range: ItemRange<GradientStop>,
stops_count: usize,
extend_mode: ExtendMode,
reverse_stops: bool,
start_point: LayoutPoint,
end_point: LayoutPoint,
stretch_size: LayoutSize,
- }
+ },
+ Border {
+ request: ImageRequest,
+ },
}
impl BrushKind {
fn supports_segments(&self) -> bool {
match *self {
BrushKind::Solid { .. } |
BrushKind::Image { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
+ BrushKind::Border { .. } |
BrushKind::LinearGradient { .. } => true,
// TODO(gw): Allow batch.rs to add segment instances
// for Picture primitives.
BrushKind::Picture { .. } => false,
BrushKind::Clear => false,
}
@@ -327,35 +331,57 @@ bitflags! {
const LEFT = 0x1;
const TOP = 0x2;
const RIGHT = 0x4;
const BOTTOM = 0x8;
}
}
#[derive(Debug)]
+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)]
pub struct BrushSegment {
pub local_rect: LayoutRect,
- pub clip_task_id: Option<RenderTaskId>,
+ 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,
may_need_clip_mask: bool,
edge_flags: EdgeAaSegmentMask,
+ extra_data: [f32; 4],
+ brush_flags: BrushFlags,
) -> BrushSegment {
BrushSegment {
local_rect: LayoutRect::new(origin, size),
- clip_task_id: None,
+ clip_task_id: BrushSegmentTaskId::Opaque,
may_need_clip_mask,
edge_flags,
+ extra_data,
+ brush_flags,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BrushClipMaskKind {
Unknown,
Individual,
@@ -392,64 +418,85 @@ impl BrushPrimitive {
},
segment_desc: None,
}
}
fn write_gpu_blocks(
&self,
request: &mut GpuDataRequest,
+ local_rect: LayoutRect,
) {
// has to match VECS_PER_SPECIFIC_BRUSH
match self.kind {
+ BrushKind::Border { .. } => {
+ // Border primitives currently used for
+ // image borders, and run through the
+ // normal brush_image shader.
+ request.push(PremultipliedColorF::WHITE);
+ request.push(PremultipliedColorF::WHITE);
+ request.push([
+ local_rect.size.width,
+ local_rect.size.height,
+ 0.0,
+ 0.0,
+ ]);
+ }
BrushKind::YuvImage { .. } => {}
BrushKind::Picture { .. } => {
request.push(PremultipliedColorF::WHITE);
request.push(PremultipliedColorF::WHITE);
+ request.push([
+ local_rect.size.width,
+ local_rect.size.height,
+ 0.0,
+ 0.0,
+ ]);
}
// Images are drawn as a white color, modulated by the total
// opacity coming from any collapsed property bindings.
- BrushKind::Image { ref opacity_binding, .. } => {
+ BrushKind::Image { stretch_size, ref opacity_binding, .. } => {
request.push(ColorF::new(1.0, 1.0, 1.0, opacity_binding.current).premultiplied());
request.push(PremultipliedColorF::WHITE);
+ request.push([stretch_size.width, stretch_size.height, 0.0, 0.0]);
}
// Solid rects also support opacity collapsing.
BrushKind::Solid { color, ref opacity_binding, .. } => {
request.push(color.scale_alpha(opacity_binding.current).premultiplied());
}
BrushKind::Clear => {
// Opaque black with operator dest out
request.push(PremultipliedColorF::BLACK);
}
- BrushKind::LinearGradient { start_point, end_point, extend_mode, .. } => {
+ BrushKind::LinearGradient { stretch_size, start_point, end_point, extend_mode, .. } => {
request.push([
start_point.x,
start_point.y,
end_point.x,
end_point.y,
]);
request.push([
pack_as_float(extend_mode as u32),
- 0.0,
- 0.0,
+ stretch_size.width,
+ stretch_size.height,
0.0,
]);
}
- BrushKind::RadialGradient { center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
+ BrushKind::RadialGradient { stretch_size, center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
request.push([
center.x,
center.y,
start_radius,
end_radius,
]);
request.push([
ratio_xy,
pack_as_float(extend_mode as u32),
- 0.,
- 0.,
+ stretch_size.width,
+ stretch_size.height,
]);
}
}
}
}
// Key that identifies a unique (partial) image that is being
// stored in the render task cache.
@@ -590,19 +637,26 @@ impl<'a> GradientGpuBlockBuilder<'a> {
let src_stops = self.display_list.get(self.stops_range);
// Preconditions (should be ensured by DisplayListBuilder):
// * we have at least two stops
// * first stop has offset 0.0
// * last stop has offset 1.0
let mut src_stops = src_stops.into_iter();
- let first = src_stops.next().unwrap();
- let mut cur_color = first.color.premultiplied();
- debug_assert_eq!(first.offset, 0.0);
+ let mut cur_color = match src_stops.next() {
+ Some(stop) => {
+ debug_assert_eq!(stop.offset, 0.0);
+ stop.color.premultiplied()
+ }
+ None => {
+ error!("Zero gradient stops found!");
+ PremultipliedColorF::BLACK
+ }
+ };
// A table of gradient entries, with two colors per entry, that specify the start and end color
// within the segment of the gradient space represented by that entry. To lookup a gradient result,
// first the entry index is calculated to determine which two colors to interpolate between, then
// the offset within that entry bucket is used to interpolate between the two colors in that entry.
// This layout preserves hard stops, as the end color for a given entry can differ from the start
// color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
// format for texture upload. This table requires the gradient color stops to be normalized to the
@@ -630,17 +684,20 @@ impl<'a> GradientGpuBlockBuilder<'a> {
if next_idx < cur_idx {
self.fill_colors(next_idx, cur_idx, &next_color, &cur_color, &mut entries);
cur_idx = next_idx;
}
cur_color = next_color;
}
- debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_BEGIN);
+ if cur_idx != GRADIENT_DATA_TABLE_BEGIN {
+ error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
+ self.fill_colors(GRADIENT_DATA_TABLE_BEGIN, cur_idx, &PremultipliedColorF::WHITE, &cur_color, &mut entries);
+ }
// Fill in the last entry (for reversed stops) with the last color stop
self.fill_colors(
GRADIENT_DATA_FIRST_STOP,
GRADIENT_DATA_FIRST_STOP + 1,
&cur_color,
&cur_color,
&mut entries,
@@ -665,17 +722,21 @@ impl<'a> GradientGpuBlockBuilder<'a> {
if next_idx > cur_idx {
self.fill_colors(cur_idx, next_idx, &cur_color, &next_color, &mut entries);
cur_idx = next_idx;
}
cur_color = next_color;
}
- debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_END);
+ if cur_idx != GRADIENT_DATA_TABLE_END {
+ error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
+ self.fill_colors(cur_idx, GRADIENT_DATA_TABLE_END, &PremultipliedColorF::WHITE, &cur_color, &mut entries);
+ }
+
// Fill in the last entry with the last color stop
self.fill_colors(
GRADIENT_DATA_LAST_STOP,
GRADIENT_DATA_LAST_STOP + 1,
&cur_color,
&cur_color,
&mut entries,
@@ -723,17 +784,17 @@ impl TextRunPrimitiveCpu {
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 {
+ if !allow_subpixel_aa && self.font.bg_color.a == 0 {
self.font.render_mode = self.font.render_mode.limit_by(FontRenderMode::Alpha);
}
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
@@ -1003,16 +1064,17 @@ impl PrimitiveContainer {
BrushKind::Solid { ref color, .. } => {
color.a > 0.0
}
BrushKind::Clear |
BrushKind::Picture { .. } |
BrushKind::Image { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
+ BrushKind::Border { .. } |
BrushKind::LinearGradient { .. } => {
true
}
}
}
PrimitiveContainer::Image(..) |
PrimitiveContainer::Border(..) => {
true
@@ -1053,16 +1115,17 @@ impl PrimitiveContainer {
BrushKind::new_solid(shadow.color),
None,
))
}
BrushKind::Clear |
BrushKind::Picture { .. } |
BrushKind::Image { .. } |
BrushKind::YuvImage { .. } |
+ BrushKind::Border { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::LinearGradient { .. } => {
panic!("bug: other brush kinds not expected here yet");
}
}
}
PrimitiveContainer::Image(..) |
PrimitiveContainer::Border(..) => {
@@ -1169,16 +1232,17 @@ impl PrimitiveStore {
let opacity = match brush.kind {
BrushKind::Clear => PrimitiveOpacity::translucent(),
BrushKind::Solid { ref color, .. } => PrimitiveOpacity::from_alpha(color.a),
BrushKind::Image { .. } => PrimitiveOpacity::translucent(),
BrushKind::YuvImage { .. } => PrimitiveOpacity::opaque(),
BrushKind::RadialGradient { .. } => PrimitiveOpacity::translucent(),
BrushKind::LinearGradient { .. } => PrimitiveOpacity::translucent(),
BrushKind::Picture { .. } => PrimitiveOpacity::translucent(),
+ BrushKind::Border { .. } => PrimitiveOpacity::translucent(),
};
let metadata = PrimitiveMetadata {
opacity,
prim_kind: PrimitiveKind::Brush,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_brushes.len()),
..base_metadata
};
@@ -1265,16 +1329,17 @@ impl PrimitiveStore {
return self.get_opacity_collapse_prim(pic_index);
}
}
// If we find a single rect or image, we can use that
// as the primitive to collapse the opacity into.
BrushKind::Solid { .. } | BrushKind::Image { .. } => {
return Some(run.base_prim_index)
}
+ BrushKind::Border { .. } |
BrushKind::YuvImage { .. } |
BrushKind::LinearGradient { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::Clear => {}
}
}
PrimitiveKind::TextRun |
PrimitiveKind::Image |
@@ -1315,16 +1380,17 @@ impl PrimitiveStore {
match brush.kind {
BrushKind::Solid { ref mut opacity_binding, .. } |
BrushKind::Image { ref mut opacity_binding, .. } => {
opacity_binding.push(binding);
}
BrushKind::Clear { .. } |
BrushKind::Picture { .. } |
BrushKind::YuvImage { .. } |
+ BrushKind::Border { .. } |
BrushKind::LinearGradient { .. } |
BrushKind::RadialGradient { .. } => {
unreachable!("bug: invalid prim type for opacity collapse");
}
};
}
PrimitiveKind::TextRun |
PrimitiveKind::Image |
@@ -1607,16 +1673,33 @@ 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);
+
+ 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,
+ );
+ }
+ }
BrushKind::RadialGradient { gradient_index, stops_range, .. } => {
let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
let gradient_builder = GradientGpuBlockBuilder::new(
stops_range,
pic_context.display_list,
);
gradient_builder.build(
@@ -1681,43 +1764,33 @@ impl PrimitiveStore {
image.write_gpu_blocks(request);
}
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
text.write_gpu_blocks(&mut request);
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
- brush.write_gpu_blocks(&mut request);
-
- let repeat = match brush.kind {
- BrushKind::Image { stretch_size, .. } |
- BrushKind::LinearGradient { stretch_size, .. } |
- BrushKind::RadialGradient { stretch_size, .. } => {
- [
- metadata.local_rect.size.width / stretch_size.width,
- metadata.local_rect.size.height / stretch_size.height,
- 0.0,
- 0.0,
- ]
- }
- _ => {
- [1.0, 1.0, 0.0, 0.0]
- }
- };
+ brush.write_gpu_blocks(&mut request, metadata.local_rect);
match brush.segment_desc {
Some(ref segment_desc) => {
for segment in &segment_desc.segments {
// has to match VECS_PER_SEGMENT
- request.write_segment(segment.local_rect, repeat);
+ request.write_segment(
+ segment.local_rect,
+ segment.extra_data,
+ );
}
}
None => {
- request.write_segment(metadata.local_rect, repeat);
+ request.write_segment(
+ metadata.local_rect,
+ [0.0; 4],
+ );
}
}
}
}
}
}
fn write_brush_segment_description(
@@ -1866,16 +1939,18 @@ impl PrimitiveStore {
segment_builder.build(|segment| {
segments.push(
BrushSegment::new(
segment.rect.origin,
segment.rect.size,
segment.has_mask,
segment.edge_flags,
+ [0.0; 4],
+ BrushFlags::empty(),
),
);
});
brush.segment_desc = Some(BrushSegmentDescriptor {
segments,
clip_mask_kind,
});
@@ -1918,55 +1993,59 @@ impl PrimitiveStore {
let segment_desc = match brush.segment_desc {
Some(ref mut description) => description,
None => return false,
};
let clip_mask_kind = segment_desc.clip_mask_kind;
for segment in &mut segment_desc.segments {
if !segment.may_need_clip_mask && clip_mask_kind != BrushClipMaskKind::Global {
- segment.clip_task_id = None;
+ segment.clip_task_id = BrushSegmentTaskId::Opaque;
continue;
}
let segment_screen_rect = calculate_screen_bounding_rect(
&prim_run_context.scroll_node.world_content_transform,
&segment.local_rect,
frame_context.device_pixel_scale,
);
- let intersected_rect = combined_outer_rect.intersection(&segment_screen_rect);
- segment.clip_task_id = intersected_rect.map(|bounds| {
- let clip_task = RenderTask::new_mask(
- bounds,
- clips.clone(),
- prim_run_context.scroll_node.coordinate_system_id,
- frame_state.clip_store,
- frame_state.gpu_cache,
- frame_state.resource_cache,
- frame_state.render_tasks,
- );
+ let bounds = match combined_outer_rect.intersection(&segment_screen_rect) {
+ Some(bounds) => bounds,
+ None => {
+ segment.clip_task_id = BrushSegmentTaskId::Empty;
+ continue;
+ }
+ };
- let clip_task_id = frame_state.render_tasks.add(clip_task);
- pic_state.tasks.push(clip_task_id);
+ let clip_task = RenderTask::new_mask(
+ bounds,
+ clips.clone(),
+ prim_run_context.scroll_node.coordinate_system_id,
+ frame_state.clip_store,
+ frame_state.gpu_cache,
+ frame_state.resource_cache,
+ frame_state.render_tasks,
+ );
- clip_task_id
- })
+ let clip_task_id = frame_state.render_tasks.add(clip_task);
+ pic_state.tasks.push(clip_task_id);
+ segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id);
}
true
}
fn reset_clip_task(&mut self, prim_index: PrimitiveIndex) {
let metadata = &mut self.cpu_metadata[prim_index.0];
metadata.clip_task_id = None;
if metadata.prim_kind == PrimitiveKind::Brush {
if let Some(ref mut desc) = self.cpu_brushes[metadata.cpu_prim_index.0].segment_desc {
for segment in &mut desc.segments {
- segment.clip_task_id = None;
+ segment.clip_task_id = BrushSegmentTaskId::Opaque;
}
}
}
}
fn update_clip_task(
&mut self,
prim_index: PrimitiveIndex,
@@ -2470,21 +2549,17 @@ fn get_local_clip_rect_for_nodes(
match local_rect {
Some(local_rect) => scroll_node.coordinate_system_relative_transform.unapply(&local_rect),
None => None,
}
}
impl<'a> GpuDataRequest<'a> {
// Write the GPU cache data for an individual segment.
- // TODO(gw): The second block is currently unused. In
- // the future, it will be used to store a
- // UV rect, allowing segments to reference
- // part of an image.
fn write_segment(
&mut self,
local_rect: LayoutRect,
- extra_params: [f32; 4],
+ extra_data: [f32; 4],
) {
self.push(local_rect);
- self.push(extra_params);
+ self.push(extra_data);
}
}
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -35,17 +35,17 @@ use scene_builder::*;
#[cfg(feature = "serialize")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "debugger")]
use serde_json;
#[cfg(any(feature = "capture", feature = "replay"))]
use std::path::PathBuf;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::mem::replace;
-use std::sync::mpsc::{Sender, Receiver};
+use std::sync::mpsc::{channel, Sender, Receiver};
use std::u32;
use tiling::Frame;
use time::precise_time_ns;
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone)]
pub struct DocumentView {
@@ -259,17 +259,16 @@ impl Document {
};
scene_tx.send(SceneBuilderRequest::Transaction {
scene: scene_request,
resource_updates: transaction_msg.resource_updates,
frame_ops: transaction_msg.frame_ops,
render: transaction_msg.generate_frame,
document_id,
- current_epochs: self.current.scene.pipeline_epochs.clone(),
}).unwrap();
}
fn render(
&mut self,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
resource_profile: &mut ResourceProfileCounters,
@@ -701,22 +700,32 @@ impl RenderBackend {
render,
result_tx,
} => {
if let Some(doc) = self.documents.get_mut(&document_id) {
if let Some(mut built_scene) = built_scene.take() {
doc.new_async_scene_ready(built_scene);
doc.render_on_hittest = true;
}
- result_tx.send(SceneSwapResult::Complete).unwrap();
+ if let Some(tx) = result_tx {
+ let (resume_tx, resume_rx) = channel();
+ tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
+ // Block until the post-swap hook has completed on
+ // the scene builder thread. We need to do this before
+ // we can sample from the sampler hook which might happen
+ // in the update_document call below.
+ resume_rx.recv().ok();
+ }
} else {
// The document was removed while we were building it, skip it.
// TODO: we might want to just ensure that removed documents are
// always forwarded to the scene builder thread to avoid this case.
- result_tx.send(SceneSwapResult::Aborted).unwrap();
+ if let Some(tx) = result_tx {
+ tx.send(SceneSwapResult::Aborted).unwrap();
+ }
continue;
}
let transaction_msg = TransactionMsg {
scene_ops: Vec::new(),
frame_ops,
resource_updates,
generate_frame: render,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -2886,16 +2886,23 @@ impl Renderer {
for alpha_batch_container in &target.alpha_batch_containers {
if let Some(target_rect) = alpha_batch_container.target_rect {
self.device.enable_scissor();
self.device.set_scissor_rect(target_rect);
}
for batch in &alpha_batch_container.alpha_batches {
+ self.shaders
+ .get(&batch.key)
+ .bind(
+ &mut self.device, projection,
+ &mut self.renderer_errors,
+ );
+
if batch.key.blend_mode != prev_blend_mode {
match batch.key.blend_mode {
BlendMode::None => {
unreachable!("bug: opaque blend in alpha pass");
}
BlendMode::Alpha => {
self.device.set_blend_mode_alpha();
}
@@ -2919,23 +2926,16 @@ impl Renderer {
//
self.device.set_blend_mode_subpixel_with_bg_color_pass0();
self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass0 as _);
}
}
prev_blend_mode = batch.key.blend_mode;
}
- self.shaders
- .get(&batch.key)
- .bind(
- &mut self.device, projection,
- &mut self.renderer_errors,
- );
-
// Handle special case readback for composites.
if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, source_id, backdrop_id }) = batch.key.kind {
// composites can't be grouped together because
// they may overlap and affect each other.
debug_assert_eq!(batch.instances.len(), 1);
self.handle_readback_composite(
render_target,
target_size,
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -1,58 +1,57 @@
/* 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, Epoch, PipelineId, ApiMsg, FrameMsg, ResourceUpdates};
+use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdates};
use api::channel::MsgSender;
use display_list_flattener::build_scene;
use frame_builder::{FrameBuilderConfig, FrameBuilder};
use clip_scroll_tree::ClipScrollTree;
-use internal_types::{FastHashMap, FastHashSet};
+use internal_types::FastHashSet;
use resource_cache::{FontInstanceMap, TiledImageMap};
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,
frame_ops: Vec<FrameMsg>,
render: bool,
- current_epochs: FastHashMap<PipelineId, Epoch>,
},
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,
frame_ops: Vec<FrameMsg>,
render: bool,
- result_tx: Sender<SceneSwapResult>,
+ result_tx: Option<Sender<SceneSwapResult>>,
},
FlushComplete(MsgSender<()>),
Stopped,
}
// Message from render backend to scene builder to indicate the
// scene swap was completed. We need a separate channel for this
// so that they don't get mixed with SceneBuilderRequest messages.
pub enum SceneSwapResult {
- Complete,
+ Complete(Sender<()>),
Aborted,
}
/// Contains the render backend data needed to build a scene.
pub struct SceneRequest {
pub scene: Scene,
pub view: DocumentView,
pub font_instances: FontInstanceMap,
@@ -132,54 +131,65 @@ impl SceneBuilder {
let _ = self.api_tx.send(ApiMsg::WakeUp);
}
SceneBuilderRequest::Transaction {
document_id,
scene,
resource_updates,
frame_ops,
render,
- current_epochs,
} => {
let built_scene = scene.map(|request|{
build_scene(&self.config, request)
});
- let pipeline_info = if let Some(ref built) = built_scene {
- PipelineInfo {
- epochs: built.scene.pipeline_epochs.clone(),
- removed_pipelines: built.removed_pipelines.clone(),
- }
- } else {
- PipelineInfo {
- epochs: current_epochs,
- removed_pipelines: vec![],
- }
- };
// TODO: pre-rasterization.
- if let Some(ref hooks) = self.hooks {
- hooks.pre_scene_swap();
- }
- let (result_tx, result_rx) = channel();
+ // We only need the pipeline info and the result channel if we
+ // have a hook callback *and* if this transaction actually built
+ // a new scene that is going to get swapped in. In other cases
+ // pipeline_info can be None and we can avoid some overhead from
+ // invoking the hooks and blocking on the channel.
+ let (pipeline_info, result_tx, result_rx) = match (&self.hooks, &built_scene) {
+ (&Some(ref hooks), &Some(ref built)) => {
+ let info = PipelineInfo {
+ epochs: built.scene.pipeline_epochs.clone(),
+ removed_pipelines: built.removed_pipelines.clone(),
+ };
+ let (tx, rx) = channel();
+
+ hooks.pre_scene_swap();
+
+ (Some(info), Some(tx), Some(rx))
+ }
+ _ => (None, None, None),
+ };
+
self.tx.send(SceneBuilderResult::Transaction {
document_id,
built_scene,
resource_updates,
frame_ops,
render,
result_tx,
}).unwrap();
let _ = self.api_tx.send(ApiMsg::WakeUp);
- // Block until the swap is done, then invoke the hook
- let _ = result_rx.recv();
- if let Some(ref hooks) = self.hooks {
- hooks.post_scene_swap(pipeline_info);
+ if let Some(pipeline_info) = pipeline_info {
+ // Block until the swap is done, then invoke the hook.
+ let swap_result = result_rx.unwrap().recv();
+ self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info);
+ // Once the hook is done, allow the RB thread to resume
+ match swap_result {
+ Ok(SceneSwapResult::Complete(resume_tx)) => {
+ resume_tx.send(()).ok();
+ },
+ _ => (),
+ };
}
}
SceneBuilderRequest::Stop => {
self.tx.send(SceneBuilderResult::Stopped).unwrap();
// We don't need to send a WakeUp to api_tx because we only
// get the Stop when the RenderBackend loop is exiting.
return false;
}
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -84,17 +84,17 @@ impl LayoutPrimitiveInfo {
is_backface_visible: true,
tag: None,
}
}
}
pub type LayoutPrimitiveInfo = PrimitiveInfo<LayoutPixel>;
-#[repr(u8)]
+#[repr(u64)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum SpecificDisplayItem {
Clip(ClipDisplayItem),
ScrollFrame(ScrollFrameDisplayItem),
StickyFrame(StickyFrameDisplayItem),
Rectangle(RectangleDisplayItem),
ClearRectangle,
Line(LineDisplayItem),
@@ -467,17 +467,17 @@ pub enum TransformStyle {
Flat = 0,
Preserve3D = 1,
}
// TODO(gw): In the future, we may modify this to apply to all elements
// within a stacking context, rather than just the glyphs. If
// this change occurs, we'll update the naming of this.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-#[repr(C, u8)]
+#[repr(u32)]
pub enum GlyphRasterSpace {
// Rasterize glyphs in local-space, applying supplied scale to glyph sizes.
// Best performance, but lower quality.
Local(f32),
// Rasterize the glyphs in screen-space, including rotation / skew etc in
// the rasterized glyph. Best quality, but slower performance. Note that
// any stacking context with a perspective transform will be rasterized
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -231,45 +231,56 @@ impl<'a> BuiltDisplayListIter<'a> {
}
// Don't let these bleed into another item
self.cur_stops = ItemRange::default();
self.cur_complex_clip = (ItemRange::default(), 0);
self.cur_clip_chain_items = ItemRange::default();
loop {
- if self.data.is_empty() {
- return None;
+ self.next_raw();
+ if let SetGradientStops = self.cur_item.item {
+ // SetGradientStops is a dummy item that most consumers should ignore
+ continue;
}
+ break;
+ }
- {
- let reader = bincode::IoReader::new(UnsafeReader::new(&mut self.data));
- bincode::deserialize_in_place(reader, &mut self.cur_item)
- .expect("MEH: malicious process?");
- }
+ Some(self.as_ref())
+ }
- match self.cur_item.item {
- SetGradientStops => {
- self.cur_stops = skip_slice::<GradientStop>(self.list, &mut self.data).0;
+ /// Gets the next display item, even if it's a dummy. Also doesn't handle peeking
+ /// and may leave irrelevant ranges live (so a Clip may have GradientStops if
+ /// for some reason you ask).
+ pub fn next_raw<'b>(&'b mut self) -> Option<DisplayItemRef<'a, 'b>> {
+ use SpecificDisplayItem::*;
- // This is a dummy item, skip over it
- continue;
- }
- ClipChain(_) => {
- self.cur_clip_chain_items = skip_slice::<ClipId>(self.list, &mut self.data).0;
- }
- Clip(_) | ScrollFrame(_) => {
- self.cur_complex_clip = self.skip_slice::<ComplexClipRegion>()
- }
- Text(_) => self.cur_glyphs = self.skip_slice::<GlyphInstance>().0,
- PushStackingContext(_) => self.cur_filters = self.skip_slice::<FilterOp>().0,
- _ => { /* do nothing */ }
+ if self.data.is_empty() {
+ return None;
+ }
+
+ {
+ let reader = bincode::IoReader::new(UnsafeReader::new(&mut self.data));
+ bincode::deserialize_in_place(reader, &mut self.cur_item)
+ .expect("MEH: malicious process?");
+ }
+
+ match self.cur_item.item {
+ SetGradientStops => {
+ self.cur_stops = skip_slice::<GradientStop>(self.list, &mut self.data).0;
}
-
- break;
+ ClipChain(_) => {
+ self.cur_clip_chain_items = skip_slice::<ClipId>(self.list, &mut self.data).0;
+ }
+ Clip(_) | ScrollFrame(_) => {
+ self.cur_complex_clip = self.skip_slice::<ComplexClipRegion>()
+ }
+ Text(_) => self.cur_glyphs = self.skip_slice::<GlyphInstance>().0,
+ PushStackingContext(_) => self.cur_filters = self.skip_slice::<FilterOp>().0,
+ _ => { /* do nothing */ }
}
Some(self.as_ref())
}
fn skip_slice<T: for<'de> Deserialize<'de>>(&mut self) -> (ItemRange<T>, usize) {
skip_slice::<T>(self.list, &mut self.data)
}
@@ -429,17 +440,17 @@ impl<'a, T: for<'de> Deserialize<'de>> :
#[cfg(feature = "serialize")]
impl Serialize for BuiltDisplayList {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use display_item::CompletelySpecificDisplayItem::*;
use display_item::GenericDisplayItem;
let mut seq = serializer.serialize_seq(None)?;
let mut traversal = self.iter();
- while let Some(item) = traversal.next() {
+ while let Some(item) = traversal.next_raw() {
let display_item = item.display_item();
let serial_di = GenericDisplayItem {
item: match display_item.item {
SpecificDisplayItem::Clip(v) => Clip(
v,
item.iter.list.get(item.iter.cur_complex_clip.0).collect()
),
SpecificDisplayItem::ClipChain(v) => ClipChain(
@@ -881,17 +892,17 @@ impl DisplayListBuilder {
}
pub fn print_display_list(&mut self) {
let mut temp = BuiltDisplayList::default();
mem::swap(&mut temp.data, &mut self.data);
{
let mut iter = BuiltDisplayListIter::new(&temp);
- while let Some(item) = iter.next() {
+ while let Some(item) = iter.next_raw() {
println!("{:?}", item.display_item());
}
}
self.data = temp.data;
}
fn push_item(&mut self, item: SpecificDisplayItem, info: &LayoutPrimitiveInfo) {
@@ -1272,17 +1283,17 @@ impl DisplayListBuilder {
clip_node_id: Option<ClipId>,
scroll_policy: ScrollPolicy,
transform: Option<PropertyBinding<LayoutTransform>>,
transform_style: TransformStyle,
perspective: Option<LayoutTransform>,
mix_blend_mode: MixBlendMode,
filters: Vec<FilterOp>,
glyph_raster_space: GlyphRasterSpace,
- ) {
+ ) -> Option<ClipId> {
let reference_frame_id = if transform.is_some() || perspective.is_some() {
Some(self.generate_clip_id())
} else {
None
};
let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
stacking_context: StackingContext {
@@ -1294,16 +1305,18 @@ impl DisplayListBuilder {
reference_frame_id,
clip_node_id,
glyph_raster_space,
},
});
self.push_item(item, info);
self.push_iter(&filters);
+
+ reference_frame_id
}
pub fn pop_stacking_context(&mut self) {
self.push_new_empty_item(SpecificDisplayItem::PopStackingContext);
}
pub fn push_stops(&mut self, stops: &[GradientStop]) {
if stops.is_empty() {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-4b65822a2f7e1fed246a492f9fe193ede2f37d74
+8da531dc2cd77a4dadbfe632b04e76454f51ac9f
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -462,24 +462,28 @@ impl Wrench {
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 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);
key
}
#[allow(dead_code)]
pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
let mut update = ResourceUpdates::new();
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -193,17 +193,17 @@ pub struct YamlFrameReader {
/// A HashMap of offsets which specify what scroll offsets particular
/// scroll layers should be initialized with.
scroll_offsets: HashMap<ExternalScrollId, LayoutPoint>,
image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>,
fonts: HashMap<FontDescriptor, FontKey>,
- font_instances: HashMap<(FontKey, Au, FontInstanceFlags), FontInstanceKey>,
+ font_instances: HashMap<(FontKey, Au, FontInstanceFlags, Option<ColorU>), FontInstanceKey>,
font_render_mode: Option<FontRenderMode>,
allow_mipmaps: bool,
/// A HashMap that allows specifying a numeric id for clip and clip chains in YAML
/// and having each of those ids correspond to a unique ClipId.
clip_id_map: HashMap<u64, ClipId>,
}
@@ -539,29 +539,31 @@ impl YamlFrameReader {
pub fn set_font_render_mode(&mut self, render_mode: Option<FontRenderMode>) {
self.font_render_mode = render_mode;
}
fn get_or_create_font_instance(
&mut self,
font_key: FontKey,
size: Au,
+ bg_color: Option<ColorU>,
flags: FontInstanceFlags,
wrench: &mut Wrench,
) -> FontInstanceKey {
let font_render_mode = self.font_render_mode;
*self.font_instances
- .entry((font_key, size, flags))
+ .entry((font_key, size, flags, bg_color))
.or_insert_with(|| {
wrench.add_font_instance(
font_key,
size,
flags,
font_render_mode,
+ bg_color,
)
})
}
fn to_image_mask(&mut self, item: &Yaml, wrench: &mut Wrench) -> Option<ImageMask> {
if item.as_hash().is_none() {
return None;
}
@@ -1112,16 +1114,18 @@ impl YamlFrameReader {
&mut self,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
item: &Yaml,
info: &mut LayoutPrimitiveInfo,
) {
let size = item["size"].as_pt_to_au().unwrap_or(Au::from_f32_px(16.0));
let color = item["color"].as_colorf().unwrap_or(*BLACK_COLOR);
+ let bg_color = item["bg-color"].as_colorf().map(|c| c.into());
+
let mut flags = FontInstanceFlags::empty();
if item["synthetic-italics"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::SYNTHETIC_ITALICS;
}
if item["synthetic-bold"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::SYNTHETIC_BOLD;
}
if item["embedded-bitmaps"].as_bool().unwrap_or(false) {
@@ -1141,16 +1145,17 @@ impl YamlFrameReader {
item["blur-radius"].is_badvalue(),
"text no longer has a blur radius, use PushShadow and PopAllShadows"
);
let desc = FontDescriptor::from_yaml(item, &self.aux_dir);
let font_key = self.get_or_create_font(desc, wrench);
let font_instance_key = self.get_or_create_font_instance(font_key,
size,
+ bg_color,
flags,
wrench);
assert!(
!(item["glyphs"].is_badvalue() && item["text"].is_badvalue()),
"text item had neither text nor glyphs!"
);