mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-04 04:58:00 +00:00
Bug 1410893 - Update webrender to commit 4b8493d6bdc64d2d83202ac15b06b0d4b14c6e76. r=jrmuizel
MozReview-Commit-ID: DoUZXZtRyDY --HG-- extra : rebase_source : c6b6fac4767e641ac7207bc03dbf206417f7a655
This commit is contained in:
parent
05d1c44747
commit
d639eb0193
@ -175,4 +175,4 @@ Troubleshooting tips:
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The version of WebRender currently in the tree is:
|
||||
d741f472dd3d6c3441646f7bf4e714c71bea39b7
|
||||
4b8493d6bdc64d2d83202ac15b06b0d4b14c6e76
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "webrender"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/webrender"
|
||||
|
@ -2,14 +2,28 @@
|
||||
* 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/. */
|
||||
|
||||
varying vec2 vLocalPos;
|
||||
flat varying vec4 vLocalRect;
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
void brush_vs(
|
||||
int prim_address,
|
||||
vec2 local_pos,
|
||||
RectWithSize local_rect,
|
||||
ivec2 user_data
|
||||
);
|
||||
|
||||
// Whether this brush is being drawn on a Picture
|
||||
// task (new) or an alpha batch task (legacy).
|
||||
// Can be removed once everything uses pictures.
|
||||
#define BRUSH_FLAG_USES_PICTURE (1 << 0)
|
||||
|
||||
struct BrushInstance {
|
||||
int picture_address;
|
||||
int prim_address;
|
||||
int layer_address;
|
||||
int clip_address;
|
||||
int z;
|
||||
int flags;
|
||||
ivec2 user_data;
|
||||
};
|
||||
|
||||
BrushInstance load_brush() {
|
||||
@ -17,63 +31,95 @@ BrushInstance load_brush() {
|
||||
|
||||
bi.picture_address = aData0.x;
|
||||
bi.prim_address = aData0.y;
|
||||
bi.layer_address = aData0.z;
|
||||
bi.clip_address = aData0.w;
|
||||
bi.z = aData1.x;
|
||||
bi.flags = aData1.y;
|
||||
bi.user_data = aData1.zw;
|
||||
|
||||
return bi;
|
||||
}
|
||||
|
||||
/*
|
||||
The dynamic picture that this brush exists on. Right now, it
|
||||
contains minimal information. In the future, it will describe
|
||||
the transform mode of primitives on this picture, among other things.
|
||||
*/
|
||||
struct PictureTask {
|
||||
RectWithSize target_rect;
|
||||
};
|
||||
|
||||
PictureTask fetch_picture_task(int index) {
|
||||
ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
|
||||
|
||||
vec4 target_rect = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
|
||||
|
||||
PictureTask task = PictureTask(RectWithSize(target_rect.xy, target_rect.zw));
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
// Load the brush instance from vertex attributes.
|
||||
BrushInstance brush = load_brush();
|
||||
|
||||
// Fetch the dynamic picture that we are drawing on.
|
||||
PictureTask pic_task = fetch_picture_task(brush.picture_address);
|
||||
|
||||
// Load the geometry for this brush. For now, this is simply the
|
||||
// local rect of the primitive. In the future, this will support
|
||||
// loading segment rects, and other rect formats (glyphs).
|
||||
PrimitiveGeometry geom = fetch_primitive_geometry(brush.prim_address);
|
||||
|
||||
// Write the (p0,p1) form of the primitive rect and the local position
|
||||
// of this vertex. Specific brush shaders can use this information to
|
||||
// interpolate texture coordinates etc.
|
||||
vLocalRect = vec4(geom.local_rect.p0, geom.local_rect.p0 + geom.local_rect.size);
|
||||
vec2 device_pos, local_pos;
|
||||
RectWithSize local_rect = geom.local_rect;
|
||||
|
||||
// Right now - pictures only support local positions. In the future, this
|
||||
// will be expanded to support transform picture types (the common kind).
|
||||
vec2 pos = pic_task.target_rect.p0 + aPosition.xy * pic_task.target_rect.size;
|
||||
vLocalPos = aPosition.xy * pic_task.target_rect.size / uDevicePixelRatio;
|
||||
if ((brush.flags & BRUSH_FLAG_USES_PICTURE) != 0) {
|
||||
// Fetch the dynamic picture that we are drawing on.
|
||||
PictureTask pic_task = fetch_picture_task(brush.picture_address);
|
||||
|
||||
// Right now - pictures only support local positions. In the future, this
|
||||
// will be expanded to support transform picture types (the common kind).
|
||||
device_pos = pic_task.target_rect.p0 + aPosition.xy * pic_task.target_rect.size;
|
||||
local_pos = aPosition.xy * pic_task.target_rect.size / uDevicePixelRatio;
|
||||
|
||||
// Write the final position transformed by the orthographic device-pixel projection.
|
||||
gl_Position = uTransform * vec4(device_pos, 0.0, 1.0);
|
||||
} else {
|
||||
AlphaBatchTask alpha_task = fetch_alpha_batch_task(brush.picture_address);
|
||||
Layer layer = fetch_layer(brush.layer_address);
|
||||
ClipArea clip_area = fetch_clip_area(brush.clip_address);
|
||||
|
||||
// Write the normal vertex information out.
|
||||
// TODO(gw): Support transform types in brushes. For now,
|
||||
// the old cache image shader didn't support
|
||||
// them yet anyway, so we're not losing any
|
||||
// existing functionality.
|
||||
VertexInfo vi = write_vertex(
|
||||
geom.local_rect,
|
||||
geom.local_clip_rect,
|
||||
float(brush.z),
|
||||
layer,
|
||||
alpha_task,
|
||||
geom.local_rect
|
||||
);
|
||||
|
||||
local_pos = vi.local_pos;
|
||||
|
||||
// For brush instances in the alpha pass, always write
|
||||
// out clip information.
|
||||
// TODO(gw): It's possible that we might want alpha
|
||||
// shaders that don't clip in the future,
|
||||
// but it's reasonable to assume that one
|
||||
// implies the other, for now.
|
||||
#ifdef WR_FEATURE_ALPHA_PASS
|
||||
write_clip(
|
||||
vi.screen_pos,
|
||||
clip_area
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Run the specific brush VS code to write interpolators.
|
||||
brush_vs(brush.prim_address, vLocalRect);
|
||||
|
||||
// Write the final position transformed by the orthographic device-pixel projection.
|
||||
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
|
||||
brush_vs(
|
||||
brush.prim_address + VECS_PER_PRIM_HEADER,
|
||||
local_pos,
|
||||
local_rect,
|
||||
brush.user_data
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
|
||||
vec4 brush_fs();
|
||||
|
||||
void main(void) {
|
||||
// Run the specific brush FS code to output the color.
|
||||
vec4 color = brush_fs(vLocalPos, vLocalRect);
|
||||
vec4 color = brush_fs();
|
||||
|
||||
#ifdef WR_FEATURE_ALPHA_PASS
|
||||
// Apply the clip mask
|
||||
color *= do_clip();
|
||||
#endif
|
||||
|
||||
// TODO(gw): Handle pre-multiply common code here as required.
|
||||
oFragColor = color;
|
||||
|
62
gfx/webrender/res/brush_image.glsl
Normal file
62
gfx/webrender/res/brush_image.glsl
Normal file
@ -0,0 +1,62 @@
|
||||
/* 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,brush
|
||||
|
||||
varying vec3 vUv;
|
||||
flat varying vec4 vUvBounds;
|
||||
|
||||
#if defined WR_FEATURE_ALPHA_TARGET
|
||||
flat varying vec4 vColor;
|
||||
#endif
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
void brush_vs(
|
||||
int prim_address,
|
||||
vec2 local_pos,
|
||||
RectWithSize local_rect,
|
||||
ivec2 user_data
|
||||
) {
|
||||
// TODO(gw): For now, this brush_image shader is only
|
||||
// being used to draw items from the intermediate
|
||||
// surface cache (render tasks). In the future
|
||||
// we can expand this to support items from
|
||||
// the normal texture cache and unify this
|
||||
// with the normal image shader.
|
||||
BlurTask task = fetch_blur_task(user_data.x);
|
||||
vUv.z = task.render_target_layer_index;
|
||||
|
||||
#if defined WR_FEATURE_COLOR_TARGET
|
||||
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
|
||||
#else
|
||||
vec2 texture_size = vec2(textureSize(sColor1, 0).xy);
|
||||
vColor = task.color;
|
||||
#endif
|
||||
|
||||
vec2 uv0 = task.target_rect.p0;
|
||||
vec2 uv1 = (task.target_rect.p0 + task.target_rect.size);
|
||||
|
||||
vec2 f = (local_pos - local_rect.p0) / local_rect.size;
|
||||
|
||||
vUv.xy = mix(uv0 / texture_size,
|
||||
uv1 / texture_size,
|
||||
f);
|
||||
|
||||
vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
vec4 brush_fs() {
|
||||
vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
|
||||
|
||||
#if defined WR_FEATURE_COLOR_TARGET
|
||||
vec4 color = texture(sColor0, vec3(uv, vUv.z));
|
||||
#else
|
||||
vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r;
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
#endif
|
@ -2,13 +2,15 @@
|
||||
* 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
|
||||
#include shared,prim_shared,ellipse,brush
|
||||
|
||||
flat varying float vClipMode;
|
||||
flat varying vec4 vClipCenter_Radius_TL;
|
||||
flat varying vec4 vClipCenter_Radius_TR;
|
||||
flat varying vec4 vClipCenter_Radius_BR;
|
||||
flat varying vec4 vClipCenter_Radius_BL;
|
||||
flat varying vec4 vLocalRect;
|
||||
varying vec2 vLocalPos;
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
@ -25,31 +27,45 @@ BrushPrimitive fetch_brush_primitive(int address) {
|
||||
return BrushPrimitive(data[0].x, data[1].xy, data[1].zw, data[2].xy, data[2].zw);
|
||||
}
|
||||
|
||||
void brush_vs(int prim_address, vec4 prim_rect) {
|
||||
void brush_vs(
|
||||
int prim_address,
|
||||
vec2 local_pos,
|
||||
RectWithSize local_rect,
|
||||
ivec2 user_data
|
||||
) {
|
||||
// Load the specific primitive.
|
||||
BrushPrimitive prim = fetch_brush_primitive(prim_address + 2);
|
||||
BrushPrimitive prim = fetch_brush_primitive(prim_address);
|
||||
|
||||
// Write clip parameters
|
||||
vClipMode = prim.clip_mode;
|
||||
|
||||
// TODO(gw): In the future, when brush primitives may be segment rects
|
||||
// we need to account for that here, and differentiate between
|
||||
// the segment rect (geometry) amd the primitive rect (which
|
||||
// defines where the clip radii are relative to).
|
||||
vec4 prim_rect = vec4(local_rect.p0, local_rect.p0 + local_rect.size);
|
||||
|
||||
vClipCenter_Radius_TL = vec4(prim_rect.xy + prim.radius_tl, prim.radius_tl);
|
||||
vClipCenter_Radius_TR = vec4(prim_rect.zy + vec2(-prim.radius_tr.x, prim.radius_tr.y), prim.radius_tr);
|
||||
vClipCenter_Radius_BR = vec4(prim_rect.zw - prim.radius_br, prim.radius_br);
|
||||
vClipCenter_Radius_BL = vec4(prim_rect.xw + vec2(prim.radius_bl.x, -prim.radius_bl.y), prim.radius_bl);
|
||||
|
||||
vLocalRect = prim_rect;
|
||||
vLocalPos = local_pos;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
vec4 brush_fs(vec2 local_pos, vec4 local_rect) {
|
||||
vec4 brush_fs() {
|
||||
// TODO(gw): The mask code below is super-inefficient. Once we
|
||||
// start using primitive segments in brush shaders, this can
|
||||
// be made much faster.
|
||||
float d = 0.0;
|
||||
// Check if in valid clip region.
|
||||
if (local_pos.x >= local_rect.x && local_pos.x < local_rect.z &&
|
||||
local_pos.y >= local_rect.y && local_pos.y < local_rect.w) {
|
||||
if (vLocalPos.x >= vLocalRect.x && vLocalPos.x < vLocalRect.z &&
|
||||
vLocalPos.y >= vLocalRect.y && vLocalPos.y < vLocalRect.w) {
|
||||
// Apply ellipse clip on each corner.
|
||||
d = rounded_rect(local_pos,
|
||||
d = rounded_rect(vLocalPos,
|
||||
vClipCenter_Radius_TL,
|
||||
vClipCenter_Radius_TR,
|
||||
vClipCenter_Radius_BR,
|
||||
@ -59,5 +75,3 @@ vec4 brush_fs(vec2 local_pos, vec4 local_rect) {
|
||||
return vec4(mix(d, 1.0 - d, vClipMode));
|
||||
}
|
||||
#endif
|
||||
|
||||
#include brush
|
||||
|
@ -29,13 +29,13 @@ void main(void) {
|
||||
vec4 src_rect = src_task.data0;
|
||||
vec4 target_rect = task.data0;
|
||||
|
||||
#if defined WR_FEATURE_COLOR
|
||||
#if defined WR_FEATURE_COLOR_TARGET
|
||||
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0).xy);
|
||||
#else
|
||||
vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
|
||||
#endif
|
||||
vUv.z = src_task.data1.x;
|
||||
vBlurRadius = 3 * int(task.data1.y);
|
||||
vBlurRadius = int(3.0 * task.data1.y);
|
||||
vSigma = task.data1.y;
|
||||
|
||||
switch (aBlurDirection) {
|
||||
@ -69,7 +69,7 @@ void main(void) {
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
|
||||
#if defined WR_FEATURE_COLOR
|
||||
#if defined WR_FEATURE_COLOR_TARGET
|
||||
#define SAMPLE_TYPE vec4
|
||||
#define SAMPLE_TEXTURE(uv) texture(sCacheRGBA8, uv)
|
||||
#else
|
||||
@ -106,7 +106,7 @@ void main(void) {
|
||||
gauss_coefficient_sum += gauss_coefficient.x;
|
||||
gauss_coefficient.xy *= gauss_coefficient.yz;
|
||||
|
||||
for (int i=1 ; i <= vBlurRadius/2 ; ++i) {
|
||||
for (int i=1 ; i <= vBlurRadius ; ++i) {
|
||||
vec2 offset = vOffsetScale * float(i);
|
||||
|
||||
vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw);
|
||||
|
@ -56,6 +56,6 @@ void main(void) {
|
||||
vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
|
||||
float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8
|
||||
|
||||
oFragColor = vec4(min(alpha, clip_alpha), 1.0, 1.0, 1.0);
|
||||
oFragColor = vec4(alpha * clip_alpha, 1.0, 1.0, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
@ -27,7 +27,10 @@ struct ClipCorner {
|
||||
vec4 outer_inner_radius;
|
||||
};
|
||||
|
||||
ClipCorner fetch_clip_corner(ivec2 address) {
|
||||
// index is of type float instead of int because using an int led to shader
|
||||
// miscompilations with a macOS 10.12 Intel driver.
|
||||
ClipCorner fetch_clip_corner(ivec2 address, float index) {
|
||||
address += ivec2(2 + 2 * int(index), 0);
|
||||
vec4 data[2] = fetch_from_resource_cache_2_direct(address);
|
||||
return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
|
||||
}
|
||||
@ -44,22 +47,10 @@ ClipData fetch_clip(ivec2 address) {
|
||||
ClipData clip;
|
||||
|
||||
clip.rect = fetch_clip_rect(address);
|
||||
|
||||
// Read the corners in groups of two texels, and adjust the read address
|
||||
// before every read.
|
||||
// The address adjustment is done inside this function, and not by passing
|
||||
// the corner index to fetch_clip_corner and computing the correct address
|
||||
// there, because doing so was hitting a driver bug on certain Intel macOS
|
||||
// drivers which creates wrong results when doing arithmetic with integer
|
||||
// variables (under certain, unknown, circumstances).
|
||||
address.x += 2;
|
||||
clip.top_left = fetch_clip_corner(address);
|
||||
address.x += 2;
|
||||
clip.top_right = fetch_clip_corner(address);
|
||||
address.x += 2;
|
||||
clip.bottom_left = fetch_clip_corner(address);
|
||||
address.x += 2;
|
||||
clip.bottom_right = fetch_clip_corner(address);
|
||||
clip.top_left = fetch_clip_corner(address, 0.0);
|
||||
clip.top_right = fetch_clip_corner(address, 1.0);
|
||||
clip.bottom_left = fetch_clip_corner(address, 2.0);
|
||||
clip.bottom_right = fetch_clip_corner(address, 3.0);
|
||||
|
||||
return clip;
|
||||
}
|
||||
@ -111,7 +102,7 @@ void main(void) {
|
||||
vClipCenter_Radius_BR,
|
||||
vClipCenter_Radius_BL);
|
||||
|
||||
float combined_alpha = min(alpha, clip_alpha);
|
||||
float combined_alpha = alpha * clip_alpha;
|
||||
|
||||
// Select alpha or inverse alpha depending on clip in/out.
|
||||
float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);
|
||||
|
@ -2,6 +2,8 @@
|
||||
* 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 PRIMITIVE_HAS_PICTURE_TASK
|
||||
|
||||
#include shared,prim_shared
|
||||
|
||||
varying vec3 vUv;
|
||||
@ -18,16 +20,6 @@ void main(void) {
|
||||
|
||||
int glyph_index = prim.user_data0;
|
||||
int resource_address = prim.user_data1;
|
||||
int picture_address = prim.user_data2;
|
||||
|
||||
// Fetch the owning picture for this primitive. This allows the code
|
||||
// below to normalize the glyph offsets relative to the original text
|
||||
// shadow rect, which is the union of all elements that make up this
|
||||
// text shadow. This allows the text shadow to be rendered at an
|
||||
// arbitrary location in a render target (provided by the render
|
||||
// task render_target_origin field).
|
||||
PrimitiveGeometry shadow_geom = fetch_primitive_geometry(picture_address);
|
||||
Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER);
|
||||
|
||||
Glyph glyph = fetch_glyph(prim.specific_prim_address,
|
||||
glyph_index,
|
||||
@ -40,8 +32,8 @@ void main(void) {
|
||||
// the glyph offset, relative to its primitive bounding rect.
|
||||
vec2 size = (res.uv_rect.zw - res.uv_rect.xy) * res.scale;
|
||||
vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
|
||||
vec2 origin = prim.task.render_target_origin +
|
||||
uDevicePixelRatio * (local_pos + pic.offset - shadow_geom.local_rect.p0);
|
||||
vec2 origin = prim.task.target_rect.p0 +
|
||||
uDevicePixelRatio * (local_pos - prim.task.content_origin);
|
||||
vec4 local_rect = vec4(origin, size);
|
||||
|
||||
vec2 texture_size = vec2(textureSize(sColor0, 0));
|
||||
@ -53,7 +45,7 @@ void main(void) {
|
||||
aPosition.xy);
|
||||
|
||||
vUv = vec3(mix(st0, st1, aPosition.xy), res.layer);
|
||||
vColor = pic.color;
|
||||
vColor = prim.task.color;
|
||||
|
||||
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
|
||||
}
|
||||
|
@ -194,6 +194,47 @@ RenderTaskData fetch_render_task(int index) {
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
The dynamic picture that this brush exists on. Right now, it
|
||||
contains minimal information. In the future, it will describe
|
||||
the transform mode of primitives on this picture, among other things.
|
||||
*/
|
||||
struct PictureTask {
|
||||
RectWithSize target_rect;
|
||||
float render_target_layer_index;
|
||||
vec2 content_origin;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
PictureTask fetch_picture_task(int address) {
|
||||
RenderTaskData task_data = fetch_render_task(address);
|
||||
|
||||
return PictureTask(
|
||||
RectWithSize(task_data.data0.xy, task_data.data0.zw),
|
||||
task_data.data1.x,
|
||||
task_data.data1.yz,
|
||||
task_data.data2
|
||||
);
|
||||
}
|
||||
|
||||
struct BlurTask {
|
||||
RectWithSize target_rect;
|
||||
float render_target_layer_index;
|
||||
float blur_radius;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
BlurTask fetch_blur_task(int address) {
|
||||
RenderTaskData task_data = fetch_render_task(address);
|
||||
|
||||
return BlurTask(
|
||||
RectWithSize(task_data.data0.xy, task_data.data0.zw),
|
||||
task_data.data1.x,
|
||||
task_data.data1.y,
|
||||
task_data.data2
|
||||
);
|
||||
}
|
||||
|
||||
struct AlphaBatchTask {
|
||||
vec2 screen_space_origin;
|
||||
vec2 render_target_origin;
|
||||
@ -359,7 +400,11 @@ CompositeInstance fetch_composite_instance() {
|
||||
struct Primitive {
|
||||
Layer layer;
|
||||
ClipArea clip_area;
|
||||
#ifdef PRIMITIVE_HAS_PICTURE_TASK
|
||||
PictureTask task;
|
||||
#else
|
||||
AlphaBatchTask task;
|
||||
#endif
|
||||
RectWithSize local_rect;
|
||||
RectWithSize local_clip_rect;
|
||||
int specific_prim_address;
|
||||
@ -387,7 +432,11 @@ Primitive load_primitive() {
|
||||
|
||||
prim.layer = fetch_layer(pi.layer_index);
|
||||
prim.clip_area = fetch_clip_area(pi.clip_task_index);
|
||||
#ifdef PRIMITIVE_HAS_PICTURE_TASK
|
||||
prim.task = fetch_picture_task(pi.render_task_index);
|
||||
#else
|
||||
prim.task = fetch_alpha_batch_task(pi.render_task_index);
|
||||
#endif
|
||||
|
||||
PrimitiveGeometry geom = fetch_primitive_geometry(pi.prim_address);
|
||||
prim.local_rect = geom.local_rect;
|
||||
@ -659,17 +708,6 @@ Rectangle fetch_rectangle(int address) {
|
||||
return Rectangle(data);
|
||||
}
|
||||
|
||||
struct Picture {
|
||||
vec4 color;
|
||||
vec2 offset;
|
||||
float blur_radius;
|
||||
};
|
||||
|
||||
Picture fetch_picture(int address) {
|
||||
vec4 data[2] = fetch_from_resource_cache_2(address);
|
||||
return Picture(data[0], data[1].xy, data[1].z);
|
||||
}
|
||||
|
||||
struct TextRun {
|
||||
vec4 color;
|
||||
vec2 offset;
|
||||
|
@ -63,17 +63,6 @@ void main(void) {
|
||||
offset,
|
||||
vGradientRepeat);
|
||||
|
||||
// Un-premultiply the color from sampling the gradient.
|
||||
if (color.a > 0.0) {
|
||||
color.rgb /= color.a;
|
||||
|
||||
// Apply the clip mask
|
||||
color.a = min(color.a, do_clip());
|
||||
|
||||
// Pre-multiply the result.
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
|
||||
oFragColor = color;
|
||||
oFragColor = color * do_clip();
|
||||
}
|
||||
#endif
|
||||
|
@ -110,10 +110,10 @@ void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_k
|
||||
break;
|
||||
}
|
||||
|
||||
vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(1.0)), color0.a);
|
||||
vColor01 = vec4(clamp(color0.rgb * modulate.y, vec3(0.0), vec3(1.0)), color0.a);
|
||||
vColor10 = vec4(clamp(color1.rgb * modulate.z, vec3(0.0), vec3(1.0)), color1.a);
|
||||
vColor11 = vec4(clamp(color1.rgb * modulate.w, vec3(0.0), vec3(1.0)), color1.a);
|
||||
vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(color0.a)), color0.a);
|
||||
vColor01 = vec4(clamp(color0.rgb * modulate.y, vec3(0.0), vec3(color0.a)), color0.a);
|
||||
vColor10 = vec4(clamp(color1.rgb * modulate.z, vec3(0.0), vec3(color1.a)), color1.a);
|
||||
vColor11 = vec4(clamp(color1.rgb * modulate.w, vec3(0.0), vec3(color1.a)), color1.a);
|
||||
}
|
||||
|
||||
int select_style(int color_select, vec2 fstyle) {
|
||||
@ -322,7 +322,7 @@ void main(void) {
|
||||
vec2 local_pos = vLocalPos;
|
||||
#endif
|
||||
|
||||
alpha = min(alpha, do_clip());
|
||||
alpha *= do_clip();
|
||||
|
||||
float aa_range = compute_aa_range(local_pos);
|
||||
|
||||
@ -399,6 +399,6 @@ void main(void) {
|
||||
float m = distance_aa(aa_range, -ld);
|
||||
vec4 color = mix(color0, color1, m);
|
||||
|
||||
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
|
||||
oFragColor = color * alpha;
|
||||
}
|
||||
#endif
|
||||
|
@ -75,7 +75,7 @@ void write_color0(vec4 color, float style, bool flip) {
|
||||
break;
|
||||
}
|
||||
|
||||
vColor0 = vec4(color.rgb * modulate.x, color.a);
|
||||
vColor0 = vec4(min(color.rgb * modulate.x, vec3(color.a)), color.a);
|
||||
}
|
||||
|
||||
void write_color1(vec4 color, float style, bool flip) {
|
||||
@ -97,7 +97,7 @@ void write_color1(vec4 color, float style, bool flip) {
|
||||
break;
|
||||
}
|
||||
|
||||
vColor1 = vec4(color.rgb * modulate.y, color.a);
|
||||
vColor1 = vec4(min(color.rgb * modulate.y, vec3(color.a)), color.a);
|
||||
}
|
||||
|
||||
void write_clip_params(float style,
|
||||
@ -250,7 +250,7 @@ void main(void) {
|
||||
vec2 local_pos = vLocalPos;
|
||||
#endif
|
||||
|
||||
alpha = min(alpha, do_clip());
|
||||
alpha *= do_clip();
|
||||
|
||||
// Find the appropriate distance to apply the step over.
|
||||
float aa_range = compute_aa_range(local_pos);
|
||||
@ -295,6 +295,6 @@ void main(void) {
|
||||
// Select between dot/dash alpha based on clip mode.
|
||||
alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect));
|
||||
|
||||
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
|
||||
oFragColor = color * alpha;
|
||||
}
|
||||
#endif
|
||||
|
@ -1,76 +0,0 @@
|
||||
/* 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
|
||||
|
||||
varying vec3 vUv;
|
||||
flat varying vec4 vUvBounds;
|
||||
|
||||
#if defined WR_FEATURE_ALPHA
|
||||
flat varying vec4 vColor;
|
||||
#endif
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
// Draw a cached primitive (e.g. a blurred text run) from the
|
||||
// target cache to the framebuffer, applying tile clip boundaries.
|
||||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
|
||||
VertexInfo vi = write_vertex(prim.local_rect,
|
||||
prim.local_clip_rect,
|
||||
prim.z,
|
||||
prim.layer,
|
||||
prim.task,
|
||||
prim.local_rect);
|
||||
|
||||
RenderTaskData child_task = fetch_render_task(prim.user_data1);
|
||||
vUv.z = child_task.data1.x;
|
||||
|
||||
#if defined WR_FEATURE_COLOR
|
||||
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
|
||||
#else
|
||||
Picture pic = fetch_picture(prim.specific_prim_address);
|
||||
|
||||
vec2 texture_size = vec2(textureSize(sColor1, 0).xy);
|
||||
vColor = pic.color;
|
||||
#endif
|
||||
vec2 uv0 = child_task.data0.xy;
|
||||
vec2 uv1 = (child_task.data0.xy + child_task.data0.zw);
|
||||
|
||||
vec2 f = (vi.local_pos - prim.local_rect.p0) / prim.local_rect.size;
|
||||
|
||||
vUv.xy = mix(uv0 / texture_size,
|
||||
uv1 / texture_size,
|
||||
f);
|
||||
vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
|
||||
|
||||
write_clip(vi.screen_pos, prim.clip_area);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
void main(void) {
|
||||
vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
|
||||
|
||||
#if defined WR_FEATURE_COLOR
|
||||
vec4 color = texture(sColor0, vec3(uv, vUv.z));
|
||||
#else
|
||||
vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r;
|
||||
#endif
|
||||
|
||||
// Un-premultiply the color from sampling the gradient.
|
||||
if (color.a > 0.0) {
|
||||
color.rgb /= color.a;
|
||||
|
||||
// Apply the clip mask
|
||||
color.a = min(color.a, do_clip());
|
||||
|
||||
// Pre-multiply the result.
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
|
||||
oFragColor = color;
|
||||
}
|
||||
#endif
|
@ -107,7 +107,7 @@ void main(void) {
|
||||
vec2 local_pos = vPos;
|
||||
#endif
|
||||
|
||||
alpha = min(alpha, do_clip());
|
||||
alpha *= do_clip();
|
||||
oFragColor = dither(vColor * alpha);
|
||||
}
|
||||
#endif
|
||||
|
@ -99,7 +99,7 @@ void main(void) {
|
||||
vec2 upper_bound_mask = vec2(0.0);
|
||||
#endif
|
||||
|
||||
alpha = min(alpha, do_clip());
|
||||
alpha *= do_clip();
|
||||
|
||||
// We calculate the particular tile this fragment belongs to, taking into
|
||||
// account the spacing in between tiles. We only paint if our fragment does
|
||||
|
@ -2,6 +2,10 @@
|
||||
* 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/. */
|
||||
|
||||
#ifdef WR_FEATURE_CACHE
|
||||
#define PRIMITIVE_HAS_PICTURE_TASK
|
||||
#endif
|
||||
|
||||
#include shared,prim_shared
|
||||
|
||||
varying vec4 vColor;
|
||||
@ -22,13 +26,14 @@ varying vec2 vLocalPos;
|
||||
|
||||
struct Line {
|
||||
vec4 color;
|
||||
float wavyLineThickness;
|
||||
float style;
|
||||
float orientation;
|
||||
};
|
||||
|
||||
Line fetch_line(int address) {
|
||||
vec4 data[2] = fetch_from_resource_cache_2(address);
|
||||
return Line(data[0], data[1].x, data[1].y);
|
||||
return Line(data[0], data[1].x, data[1].y, data[1].z);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
@ -58,59 +63,51 @@ void main(void) {
|
||||
break;
|
||||
}
|
||||
case LINE_STYLE_DASHED: {
|
||||
// y = dash on + off length
|
||||
// z = dash length
|
||||
// w = center line of edge cross-axis (for dots only)
|
||||
float desired_dash_length = size.y * 3.0;
|
||||
// Consider half total length since there is an equal on/off for each dash.
|
||||
float dash_count = 1.0 + ceil(size.x / desired_dash_length);
|
||||
float dash_length = size.x / dash_count;
|
||||
vParams = vec4(2.0 * dash_length,
|
||||
dash_length,
|
||||
float dash_length = size.y * 3.0;
|
||||
vParams = vec4(2.0 * dash_length, // period
|
||||
dash_length, // dash length
|
||||
0.0,
|
||||
0.0);
|
||||
break;
|
||||
}
|
||||
case LINE_STYLE_DOTTED: {
|
||||
float diameter = size.y;
|
||||
float radius = 0.5 * diameter;
|
||||
float dot_count = ceil(0.5 * size.x / diameter);
|
||||
float empty_space = size.x - dot_count * diameter;
|
||||
float distance_between_centers = diameter + empty_space / dot_count;
|
||||
float period = diameter * 2.0;
|
||||
float center_line = pos.y + 0.5 * size.y;
|
||||
vParams = vec4(distance_between_centers,
|
||||
radius,
|
||||
float max_x = floor(size.x / period) * period;
|
||||
vParams = vec4(period,
|
||||
diameter / 2.0, // radius
|
||||
center_line,
|
||||
0.0);
|
||||
max_x);
|
||||
break;
|
||||
}
|
||||
case LINE_STYLE_WAVY: {
|
||||
// Choose some arbitrary values to scale thickness,
|
||||
// wave period etc.
|
||||
// TODO(gw): Tune these to get closer to what Gecko uses.
|
||||
float thickness = 0.15 * size.y;
|
||||
vParams = vec4(thickness,
|
||||
size.y * 0.5,
|
||||
size.y * 0.75,
|
||||
size.y * 0.5);
|
||||
// This logic copied from gecko to get the same results
|
||||
float line_thickness = max(line.wavyLineThickness, 1.0);
|
||||
// Difference in height between peaks and troughs
|
||||
// (and since slopes are 45 degrees, the length of each slope)
|
||||
float slope_length = size.y - line_thickness;
|
||||
// Length of flat runs
|
||||
float flat_length = max((line_thickness - 1.0) * 2.0, 1.0);
|
||||
|
||||
vParams = vec4(line_thickness / 2.0,
|
||||
slope_length,
|
||||
flat_length,
|
||||
size.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WR_FEATURE_CACHE
|
||||
int picture_address = prim.user_data0;
|
||||
PrimitiveGeometry picture_geom = fetch_primitive_geometry(picture_address);
|
||||
Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER);
|
||||
|
||||
vec2 device_origin = prim.task.render_target_origin +
|
||||
uDevicePixelRatio * (prim.local_rect.p0 + pic.offset - picture_geom.local_rect.p0);
|
||||
vec2 device_origin = prim.task.target_rect.p0 +
|
||||
uDevicePixelRatio * (prim.local_rect.p0 - prim.task.content_origin);
|
||||
vec2 device_size = uDevicePixelRatio * prim.local_rect.size;
|
||||
|
||||
vec2 device_pos = mix(device_origin,
|
||||
device_origin + device_size,
|
||||
aPosition.xy);
|
||||
|
||||
vColor = pic.color;
|
||||
vColor = prim.task.color;
|
||||
vLocalPos = mix(prim.local_rect.p0,
|
||||
prim.local_rect.p0 + prim.local_rect.size,
|
||||
aPosition.xy);
|
||||
@ -142,6 +139,9 @@ void main(void) {
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
|
||||
#define MAGIC_WAVY_LINE_AA_SNAP 0.7
|
||||
|
||||
float det(vec2 a, vec2 b) {
|
||||
return a.x * b.y - b.x * a.y;
|
||||
}
|
||||
@ -215,40 +215,51 @@ void main(void) {
|
||||
vec2 dot_relative_pos = vec2(x, pos.y) - vParams.yz;
|
||||
float dot_distance = length(dot_relative_pos) - vParams.y;
|
||||
alpha = min(alpha, distance_aa(aa_range, dot_distance));
|
||||
// Clip off partial dots
|
||||
alpha *= step(pos.x - vLocalOrigin.x, vParams.w);
|
||||
break;
|
||||
}
|
||||
case LINE_STYLE_WAVY: {
|
||||
vec2 normalized_local_pos = pos - vLocalOrigin.xy;
|
||||
|
||||
float y0 = vParams.y;
|
||||
float dy = vParams.z;
|
||||
float dx = vParams.w;
|
||||
float half_line_thickness = vParams.x;
|
||||
float slope_length = vParams.y;
|
||||
float flat_length = vParams.z;
|
||||
float vertical_bounds = vParams.w;
|
||||
// Our pattern is just two slopes and two flats
|
||||
float half_period = slope_length + flat_length;
|
||||
|
||||
// Flip the position of the bezier center points each
|
||||
// wave period.
|
||||
dy *= step(mod(normalized_local_pos.x, 4.0 * dx), 2.0 * dx) * 2.0 - 1.0;
|
||||
float mid_height = vertical_bounds / 2.0;
|
||||
float peak_offset = mid_height - half_line_thickness;
|
||||
// Flip the wave every half period
|
||||
float flip = -2.0 * (step(mod(normalized_local_pos.x, 2.0 * half_period), half_period) - 0.5);
|
||||
// float flip = -1.0;
|
||||
peak_offset *= flip;
|
||||
float peak_height = mid_height + peak_offset;
|
||||
|
||||
// Convert pos to a local position within one wave period.
|
||||
normalized_local_pos.x = dx + mod(normalized_local_pos.x, 2.0 * dx);
|
||||
// Convert pos to a local position within one half period
|
||||
normalized_local_pos.x = mod(normalized_local_pos.x, half_period);
|
||||
|
||||
// Evaluate SDF to the first bezier.
|
||||
vec2 b0_0 = vec2(0.0 * dx, y0);
|
||||
vec2 b1_0 = vec2(1.0 * dx, y0 - dy);
|
||||
vec2 b2_0 = vec2(2.0 * dx, y0);
|
||||
float d1 = approx_distance(normalized_local_pos, b0_0, b1_0, b2_0);
|
||||
// Compute signed distance to the 3 lines that make up an arc
|
||||
float dist1 = distance_to_line(vec2(0.0, peak_height),
|
||||
vec2(1.0, -flip),
|
||||
normalized_local_pos);
|
||||
float dist2 = distance_to_line(vec2(0.0, peak_height),
|
||||
vec2(0, -flip),
|
||||
normalized_local_pos);
|
||||
float dist3 = distance_to_line(vec2(flat_length, peak_height),
|
||||
vec2(-1.0, -flip),
|
||||
normalized_local_pos);
|
||||
float dist = abs(max(max(dist1, dist2), dist3));
|
||||
|
||||
// Evaluate SDF to the second bezier.
|
||||
vec2 b0_1 = vec2(2.0 * dx, y0);
|
||||
vec2 b1_1 = vec2(3.0 * dx, y0 + dy);
|
||||
vec2 b2_1 = vec2(4.0 * dx, y0);
|
||||
float d2 = approx_distance(normalized_local_pos, b0_1, b1_1, b2_1);
|
||||
// Apply AA based on the thickness of the wave
|
||||
alpha = distance_aa(aa_range, dist - half_line_thickness);
|
||||
|
||||
// SDF union - this is needed to avoid artifacts where the
|
||||
// bezier curves join.
|
||||
float d = min(d1, d2);
|
||||
// Disable AA for thin lines
|
||||
if (half_line_thickness <= 1.0) {
|
||||
alpha = 1.0 - step(alpha, MAGIC_WAVY_LINE_AA_SNAP);
|
||||
}
|
||||
|
||||
// Apply AA based on the thickness of the wave.
|
||||
alpha = distance_aa(aa_range, d - vParams.x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -110,17 +110,6 @@ void main(void) {
|
||||
offset,
|
||||
vGradientRepeat);
|
||||
|
||||
// Un-premultiply the color from sampling the gradient.
|
||||
if (color.a > 0.0) {
|
||||
color.rgb /= color.a;
|
||||
|
||||
// Apply the clip mask
|
||||
color.a = min(color.a, do_clip());
|
||||
|
||||
// Pre-multiply the result.
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
|
||||
oFragColor = color;
|
||||
oFragColor = color * do_clip();
|
||||
}
|
||||
#endif
|
||||
|
@ -47,8 +47,8 @@ void main(void) {
|
||||
#endif
|
||||
|
||||
#ifdef WR_FEATURE_CLIP
|
||||
alpha = min(alpha, do_clip());
|
||||
alpha *= do_clip();
|
||||
#endif
|
||||
oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
|
||||
oFragColor = vColor * alpha;
|
||||
}
|
||||
#endif
|
||||
|
@ -13,6 +13,12 @@ varying vec3 vLocalPos;
|
||||
#endif
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
#define MODE_ALPHA 0
|
||||
#define MODE_SUBPX_PASS0 1
|
||||
#define MODE_SUBPX_PASS1 2
|
||||
#define MODE_COLOR_BITMAP 3
|
||||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
TextRun text = fetch_text_run(prim.specific_prim_address);
|
||||
@ -53,22 +59,27 @@ void main(void) {
|
||||
|
||||
write_clip(vi.screen_pos, prim.clip_area);
|
||||
|
||||
switch (uMode) {
|
||||
case MODE_ALPHA:
|
||||
case MODE_SUBPX_PASS1:
|
||||
vColor = text.color;
|
||||
break;
|
||||
case MODE_SUBPX_PASS0:
|
||||
case MODE_COLOR_BITMAP:
|
||||
vColor = vec4(text.color.a);
|
||||
break;
|
||||
}
|
||||
|
||||
vec2 texture_size = vec2(textureSize(sColor0, 0));
|
||||
vec2 st0 = res.uv_rect.xy / texture_size;
|
||||
vec2 st1 = res.uv_rect.zw / texture_size;
|
||||
|
||||
vColor = vec4(text.color.rgb * text.color.a, text.color.a);
|
||||
vUv = vec3(mix(st0, st1, f), res.layer);
|
||||
vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
|
||||
#define MODE_ALPHA 0
|
||||
#define MODE_SUBPX_PASS0 1
|
||||
#define MODE_SUBPX_PASS1 2
|
||||
|
||||
void main(void) {
|
||||
vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z);
|
||||
vec4 color = texture(sColor0, tc);
|
||||
@ -79,23 +90,6 @@ void main(void) {
|
||||
#endif
|
||||
alpha *= do_clip();
|
||||
|
||||
// TODO(gw): It would be worth profiling this and seeing
|
||||
// if we should instead handle the mode via
|
||||
// a combination of mix() etc. Branching on
|
||||
// a uniform is probably fast in most GPUs now though?
|
||||
vec4 modulate_color = vec4(0.0);
|
||||
switch (uMode) {
|
||||
case MODE_ALPHA:
|
||||
modulate_color = alpha * vColor;
|
||||
break;
|
||||
case MODE_SUBPX_PASS0:
|
||||
modulate_color = vec4(alpha) * vColor.a;
|
||||
break;
|
||||
case MODE_SUBPX_PASS1:
|
||||
modulate_color = alpha * vColor;
|
||||
break;
|
||||
}
|
||||
|
||||
oFragColor = color * modulate_color;
|
||||
oFragColor = color * vColor * alpha;
|
||||
}
|
||||
#endif
|
||||
|
@ -162,7 +162,7 @@ void main(void) {
|
||||
vec2 relative_pos_in_rect = vLocalPos;
|
||||
#endif
|
||||
|
||||
alpha = min(alpha, do_clip());
|
||||
alpha *= do_clip();
|
||||
|
||||
// We clamp the texture coordinates to the half-pixel offset from the borders
|
||||
// in order to avoid sampling outside of the texture area.
|
||||
|
@ -27,6 +27,10 @@
|
||||
// Vertex shader attributes and uniforms
|
||||
//======================================================================================
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
// A generic uniform that shaders can optionally use to configure
|
||||
// an operation mode for this batch.
|
||||
uniform int uMode;
|
||||
|
||||
// Uniform inputs
|
||||
uniform mat4 uTransform; // Orthographic projection
|
||||
uniform float uDevicePixelRatio;
|
||||
@ -39,10 +43,6 @@
|
||||
// Fragment shader attributes and uniforms
|
||||
//======================================================================================
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
// A generic uniform that shaders can optionally use to configure
|
||||
// an operation mode for this batch.
|
||||
uniform int uMode;
|
||||
|
||||
// Uniform inputs
|
||||
|
||||
// Fragment shader outputs
|
||||
|
@ -243,10 +243,10 @@ impl FrameBuilder {
|
||||
let bottom = &border.bottom;
|
||||
|
||||
// These colors are used during inset/outset scaling.
|
||||
let left_color = left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
|
||||
let top_color = top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
|
||||
let right_color = right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
|
||||
let bottom_color = bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
|
||||
let left_color = left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied();
|
||||
let top_color = top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied();
|
||||
let right_color = right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied();
|
||||
let bottom_color = bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied();
|
||||
|
||||
let prim_cpu = BorderPrimitiveCpu {
|
||||
corner_instances,
|
||||
|
307
gfx/webrender/src/box_shadow.rs
Normal file
307
gfx/webrender/src/box_shadow.rs
Normal file
@ -0,0 +1,307 @@
|
||||
/* 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, LayerPoint, LayerRect, LayerSize, LayerVector2D};
|
||||
use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
|
||||
use api::{ClipMode, ComplexClipRegion, LocalClip, ClipAndScrollInfo};
|
||||
use clip::ClipSource;
|
||||
use frame_builder::FrameBuilder;
|
||||
use prim_store::{PrimitiveContainer, RectanglePrimitive, BrushPrimitive};
|
||||
use picture::PicturePrimitive;
|
||||
use util::RectHelpers;
|
||||
|
||||
// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
|
||||
pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
|
||||
|
||||
impl FrameBuilder {
|
||||
pub fn add_box_shadow(
|
||||
&mut self,
|
||||
clip_and_scroll: ClipAndScrollInfo,
|
||||
prim_info: &LayerPrimitiveInfo,
|
||||
box_offset: &LayerVector2D,
|
||||
color: &ColorF,
|
||||
blur_radius: f32,
|
||||
spread_radius: f32,
|
||||
border_radius: BorderRadius,
|
||||
clip_mode: BoxShadowClipMode,
|
||||
) {
|
||||
if color.a == 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let spread_amount = match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
spread_radius
|
||||
}
|
||||
BoxShadowClipMode::Inset => {
|
||||
-spread_radius
|
||||
}
|
||||
};
|
||||
|
||||
let shadow_radius = adjust_border_radius_for_box_shadow(
|
||||
border_radius,
|
||||
spread_amount,
|
||||
);
|
||||
let shadow_rect = prim_info.rect
|
||||
.translate(box_offset)
|
||||
.inflate(spread_amount, spread_amount);
|
||||
|
||||
if blur_radius == 0.0 {
|
||||
let mut clips = Vec::new();
|
||||
|
||||
let fast_info = match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
// TODO(gw): Add a fast path for ClipOut + zero border radius!
|
||||
clips.push(ClipSource::RoundedRectangle(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::ClipOut
|
||||
));
|
||||
|
||||
LayerPrimitiveInfo::with_clip(
|
||||
shadow_rect,
|
||||
LocalClip::RoundedRect(
|
||||
shadow_rect,
|
||||
ComplexClipRegion::new(
|
||||
shadow_rect,
|
||||
shadow_radius,
|
||||
ClipMode::Clip,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
BoxShadowClipMode::Inset => {
|
||||
clips.push(ClipSource::RoundedRectangle(
|
||||
shadow_rect,
|
||||
shadow_radius,
|
||||
ClipMode::ClipOut
|
||||
));
|
||||
|
||||
LayerPrimitiveInfo::with_clip(
|
||||
prim_info.rect,
|
||||
LocalClip::RoundedRect(
|
||||
prim_info.rect,
|
||||
ComplexClipRegion::new(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::Clip
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
&fast_info,
|
||||
clips,
|
||||
PrimitiveContainer::Rectangle(RectanglePrimitive {
|
||||
color: *color,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
let blur_offset = 2.0 * blur_radius;
|
||||
let mut extra_clips = vec![];
|
||||
let mut blur_regions = vec![];
|
||||
|
||||
match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
let brush_prim = BrushPrimitive {
|
||||
clip_mode: ClipMode::Clip,
|
||||
radius: shadow_radius,
|
||||
};
|
||||
|
||||
let brush_rect = LayerRect::new(LayerPoint::new(blur_offset, blur_offset),
|
||||
shadow_rect.size);
|
||||
|
||||
let brush_info = LayerPrimitiveInfo::new(brush_rect);
|
||||
|
||||
let brush_prim_index = self.create_primitive(
|
||||
clip_and_scroll,
|
||||
&brush_info,
|
||||
Vec::new(),
|
||||
PrimitiveContainer::Brush(brush_prim),
|
||||
);
|
||||
|
||||
let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
|
||||
let blur_range = BLUR_SAMPLE_SCALE * blur_radius;
|
||||
|
||||
let size = pic_rect.size;
|
||||
|
||||
let tl = LayerSize::new(
|
||||
blur_radius.max(border_radius.top_left.width),
|
||||
blur_radius.max(border_radius.top_left.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
let tr = LayerSize::new(
|
||||
blur_radius.max(border_radius.top_right.width),
|
||||
blur_radius.max(border_radius.top_right.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
let br = LayerSize::new(
|
||||
blur_radius.max(border_radius.bottom_right.width),
|
||||
blur_radius.max(border_radius.bottom_right.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
let bl = LayerSize::new(
|
||||
blur_radius.max(border_radius.bottom_left.width),
|
||||
blur_radius.max(border_radius.bottom_left.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
|
||||
let max_width = tl.width.max(tr.width.max(bl.width.max(br.width)));
|
||||
let max_height = tl.height.max(tr.height.max(bl.height.max(br.height)));
|
||||
|
||||
// Apply a conservative test that if any of the blur regions below
|
||||
// will overlap, we won't bother applying the region optimization
|
||||
// and will just blur the entire thing. This should only happen
|
||||
// in rare cases, where either the blur radius or border radius
|
||||
// is very large, in which case there's no real point in trying
|
||||
// to only blur a small region anyway.
|
||||
if max_width < 0.5 * size.width && max_height < 0.5 * size.height {
|
||||
blur_regions.push(LayerRect::from_floats(0.0, 0.0, tl.width, tl.height));
|
||||
blur_regions.push(LayerRect::from_floats(size.width - tr.width, 0.0, size.width, tr.height));
|
||||
blur_regions.push(LayerRect::from_floats(size.width - br.width, size.height - br.height, size.width, size.height));
|
||||
blur_regions.push(LayerRect::from_floats(0.0, size.height - bl.height, bl.width, size.height));
|
||||
|
||||
blur_regions.push(LayerRect::from_floats(0.0, tl.height, blur_range, size.height - bl.height));
|
||||
blur_regions.push(LayerRect::from_floats(size.width - blur_range, tr.height, size.width, size.height - br.height));
|
||||
blur_regions.push(LayerRect::from_floats(tl.width, 0.0, size.width - tr.width, blur_range));
|
||||
blur_regions.push(LayerRect::from_floats(bl.width, size.height - blur_range, size.width - br.width, size.height));
|
||||
}
|
||||
|
||||
let mut pic_prim = PicturePrimitive::new_box_shadow(
|
||||
blur_radius,
|
||||
*color,
|
||||
blur_regions,
|
||||
BoxShadowClipMode::Outset,
|
||||
);
|
||||
|
||||
pic_prim.add_primitive(
|
||||
brush_prim_index,
|
||||
&brush_rect,
|
||||
clip_and_scroll
|
||||
);
|
||||
|
||||
pic_prim.build();
|
||||
|
||||
extra_clips.push(ClipSource::RoundedRectangle(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::ClipOut,
|
||||
));
|
||||
|
||||
let pic_info = LayerPrimitiveInfo::new(pic_rect);
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
&pic_info,
|
||||
extra_clips,
|
||||
PrimitiveContainer::Picture(pic_prim),
|
||||
);
|
||||
}
|
||||
BoxShadowClipMode::Inset => {
|
||||
let brush_prim = BrushPrimitive {
|
||||
clip_mode: ClipMode::ClipOut,
|
||||
radius: shadow_radius,
|
||||
};
|
||||
|
||||
let mut brush_rect = shadow_rect;
|
||||
brush_rect.origin.x = brush_rect.origin.x - prim_info.rect.origin.x + blur_offset;
|
||||
brush_rect.origin.y = brush_rect.origin.y - prim_info.rect.origin.y + blur_offset;
|
||||
|
||||
let brush_info = LayerPrimitiveInfo::new(brush_rect);
|
||||
|
||||
let brush_prim_index = self.create_primitive(
|
||||
clip_and_scroll,
|
||||
&brush_info,
|
||||
Vec::new(),
|
||||
PrimitiveContainer::Brush(brush_prim),
|
||||
);
|
||||
|
||||
let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
|
||||
|
||||
// TODO(gw): Apply minimal blur regions for inset box shadows.
|
||||
|
||||
let mut pic_prim = PicturePrimitive::new_box_shadow(
|
||||
blur_radius,
|
||||
*color,
|
||||
blur_regions,
|
||||
BoxShadowClipMode::Inset,
|
||||
);
|
||||
|
||||
pic_prim.add_primitive(
|
||||
brush_prim_index,
|
||||
&prim_info.rect,
|
||||
clip_and_scroll
|
||||
);
|
||||
|
||||
pic_prim.build();
|
||||
|
||||
extra_clips.push(ClipSource::RoundedRectangle(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::Clip,
|
||||
));
|
||||
|
||||
let pic_info = LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect);
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
&pic_info,
|
||||
extra_clips,
|
||||
PrimitiveContainer::Picture(pic_prim),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_border_radius_for_box_shadow(
|
||||
radius: BorderRadius,
|
||||
spread_amount: f32,
|
||||
) -> BorderRadius {
|
||||
BorderRadius {
|
||||
top_left: adjust_corner_for_box_shadow(
|
||||
radius.top_left,
|
||||
spread_amount,
|
||||
),
|
||||
top_right: adjust_corner_for_box_shadow(
|
||||
radius.top_right,
|
||||
spread_amount,
|
||||
),
|
||||
bottom_right: adjust_corner_for_box_shadow(
|
||||
radius.bottom_right,
|
||||
spread_amount,
|
||||
),
|
||||
bottom_left: adjust_corner_for_box_shadow(
|
||||
radius.bottom_left,
|
||||
spread_amount,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_corner_for_box_shadow(
|
||||
corner: LayoutSize,
|
||||
spread_amount: f32,
|
||||
) -> LayoutSize {
|
||||
LayoutSize::new(
|
||||
adjust_radius_for_box_shadow(
|
||||
corner.width,
|
||||
spread_amount
|
||||
),
|
||||
adjust_radius_for_box_shadow(
|
||||
corner.height,
|
||||
spread_amount
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn adjust_radius_for_box_shadow(
|
||||
border_radius: f32,
|
||||
spread_amount: f32,
|
||||
) -> f32 {
|
||||
if border_radius > 0.0 {
|
||||
(border_radius + spread_amount).max(0.0)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
@ -513,6 +513,7 @@ impl ClipScrollTree {
|
||||
NodeType::Clip(ref info) => {
|
||||
pt.new_level("Clip".to_owned());
|
||||
|
||||
pt.add_item(format!("id: {:?}", id));
|
||||
let clips = clip_store.get(&info.clip_sources).clips();
|
||||
pt.new_level(format!("Clip Sources [{}]", clips.len()));
|
||||
for source in clips {
|
||||
@ -522,14 +523,17 @@ impl ClipScrollTree {
|
||||
}
|
||||
NodeType::ReferenceFrame(ref info) => {
|
||||
pt.new_level(format!("ReferenceFrame {:?}", info.transform));
|
||||
pt.add_item(format!("id: {:?}", id));
|
||||
}
|
||||
NodeType::ScrollFrame(scrolling_info) => {
|
||||
pt.new_level(format!("ScrollFrame"));
|
||||
pt.add_item(format!("id: {:?}", id));
|
||||
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
|
||||
pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset));
|
||||
}
|
||||
NodeType::StickyFrame(sticky_frame_info, sticky_offset) => {
|
||||
pt.new_level(format!("StickyFrame"));
|
||||
pt.add_item(format!("id: {:?}", id));
|
||||
pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
|
||||
pt.add_item(format!("sticky offset: {:?}", sticky_offset));
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,8 @@
|
||||
* 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::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
|
||||
use api::{ClipMode, ComplexClipRegion, ClipAndScrollInfo, ClipId, ColorF, LayoutSize};
|
||||
use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList};
|
||||
use api::{ClipAndScrollInfo, ClipId, ColorF};
|
||||
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
|
||||
use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode};
|
||||
use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
|
||||
@ -23,7 +23,7 @@ use gpu_cache::GpuCache;
|
||||
use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
|
||||
use picture::{PicturePrimitive};
|
||||
use plane_split::{BspSplitter, Polygon, Splitter};
|
||||
use prim_store::{BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
|
||||
use prim_store::{TexelRect, YuvImagePrimitiveCpu};
|
||||
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
|
||||
use prim_store::{PrimitiveContainer, PrimitiveIndex};
|
||||
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
|
||||
@ -39,9 +39,7 @@ use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex};
|
||||
use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
|
||||
use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext};
|
||||
use util::{self, pack_as_float, RectHelpers, recycle_vec};
|
||||
|
||||
// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
|
||||
const BLUR_SAMPLE_SCALE: f32 = 3.0;
|
||||
use box_shadow::BLUR_SAMPLE_SCALE;
|
||||
|
||||
/// Construct a polygon from stacking context boundaries.
|
||||
/// `anchor` here is an index that's going to be preserved in all the
|
||||
@ -104,6 +102,7 @@ impl HitTestingItem {
|
||||
|
||||
pub struct HitTestingRun(Vec<HitTestingItem>, ClipAndScrollInfo);
|
||||
|
||||
/// A builder structure for `RendererFrame`
|
||||
pub struct FrameBuilder {
|
||||
screen_size: DeviceUintSize,
|
||||
background_color: Option<ColorF>,
|
||||
@ -111,7 +110,7 @@ pub struct FrameBuilder {
|
||||
pub clip_store: ClipStore,
|
||||
cmds: Vec<PrimitiveRunCmd>,
|
||||
hit_testing_runs: Vec<HitTestingRun>,
|
||||
config: FrameBuilderConfig,
|
||||
pub config: FrameBuilderConfig,
|
||||
|
||||
stacking_context_store: Vec<StackingContext>,
|
||||
clip_scroll_group_store: Vec<ClipScrollGroup>,
|
||||
@ -178,11 +177,11 @@ impl<'a> PrimitiveContext<'a> {
|
||||
|
||||
impl FrameBuilder {
|
||||
pub fn new(
|
||||
previous: Option<FrameBuilder>,
|
||||
previous: Option<Self>,
|
||||
screen_size: DeviceUintSize,
|
||||
background_color: Option<ColorF>,
|
||||
config: FrameBuilderConfig,
|
||||
) -> FrameBuilder {
|
||||
) -> Self {
|
||||
match previous {
|
||||
Some(prev) => FrameBuilder {
|
||||
stacking_context_store: recycle_vec(prev.stacking_context_store),
|
||||
@ -228,7 +227,7 @@ impl FrameBuilder {
|
||||
/// Create a primitive and add it to the prim store. This method doesn't
|
||||
/// add the primitive to the draw list, so can be used for creating
|
||||
/// sub-primitives.
|
||||
fn create_primitive(
|
||||
pub fn create_primitive(
|
||||
&mut self,
|
||||
clip_and_scroll: ClipAndScrollInfo,
|
||||
info: &LayerPrimitiveInfo,
|
||||
@ -589,10 +588,8 @@ impl FrameBuilder {
|
||||
// safe to offset the local rect by the offset of the shadow, which
|
||||
// is then used when blitting the shadow to the final location.
|
||||
let metadata = &mut self.prim_store.cpu_metadata[prim_index.0];
|
||||
let prim = &self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
|
||||
let shadow = prim.as_text_shadow();
|
||||
|
||||
metadata.local_rect = metadata.local_rect.translate(&shadow.offset);
|
||||
let prim = &mut self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
|
||||
metadata.local_rect = prim.build();
|
||||
}
|
||||
|
||||
// Push any fast-path shadows now
|
||||
@ -650,26 +647,13 @@ impl FrameBuilder {
|
||||
&mut self,
|
||||
clip_and_scroll: ClipAndScrollInfo,
|
||||
info: &LayerPrimitiveInfo,
|
||||
baseline: f32,
|
||||
start: f32,
|
||||
end: f32,
|
||||
wavy_line_thickness: f32,
|
||||
orientation: LineOrientation,
|
||||
width: f32,
|
||||
color: &ColorF,
|
||||
style: LineStyle,
|
||||
) {
|
||||
let new_rect = match orientation {
|
||||
LineOrientation::Horizontal => LayerRect::new(
|
||||
LayerPoint::new(start, baseline),
|
||||
LayerSize::new(end - start, width),
|
||||
),
|
||||
LineOrientation::Vertical => LayerRect::new(
|
||||
LayerPoint::new(baseline, start),
|
||||
LayerSize::new(width, end - start),
|
||||
),
|
||||
};
|
||||
|
||||
let line = LinePrimitive {
|
||||
wavy_line_thickness,
|
||||
color: *color,
|
||||
style: style,
|
||||
orientation: orientation,
|
||||
@ -689,7 +673,7 @@ impl FrameBuilder {
|
||||
let mut line = line.clone();
|
||||
line.color = shadow.color;
|
||||
let mut info = info.clone();
|
||||
info.rect = new_rect.translate(&shadow.offset);
|
||||
info.rect = info.rect.translate(&shadow.offset);
|
||||
let prim_index = self.create_primitive(
|
||||
clip_and_scroll,
|
||||
&info,
|
||||
@ -699,8 +683,6 @@ impl FrameBuilder {
|
||||
self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
|
||||
}
|
||||
|
||||
let mut info = info.clone();
|
||||
info.rect = new_rect;
|
||||
let prim_index = self.create_primitive(
|
||||
clip_and_scroll,
|
||||
&info,
|
||||
@ -713,7 +695,7 @@ impl FrameBuilder {
|
||||
self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
|
||||
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
|
||||
} else {
|
||||
self.pending_shadow_contents.push((prim_index, clip_and_scroll, info));
|
||||
self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,12 +708,11 @@ impl FrameBuilder {
|
||||
|
||||
// Only run real blurs here (fast path zero blurs are handled above).
|
||||
if blur_radius > 0.0 {
|
||||
let shadow_rect = new_rect.inflate(
|
||||
blur_radius,
|
||||
blur_radius,
|
||||
picture.add_primitive(
|
||||
prim_index,
|
||||
&info.rect,
|
||||
clip_and_scroll,
|
||||
);
|
||||
shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
|
||||
picture.add_primitive(prim_index, clip_and_scroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1243,240 +1224,11 @@ impl FrameBuilder {
|
||||
// Only run real blurs here (fast path zero blurs are handled above).
|
||||
let blur_radius = picture_prim.as_text_shadow().blur_radius;
|
||||
if blur_radius > 0.0 {
|
||||
let shadow_rect = rect.inflate(
|
||||
blur_radius,
|
||||
blur_radius,
|
||||
picture_prim.add_primitive(
|
||||
prim_index,
|
||||
&rect,
|
||||
clip_and_scroll,
|
||||
);
|
||||
shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
|
||||
picture_prim.add_primitive(prim_index, clip_and_scroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_box_shadow(
|
||||
&mut self,
|
||||
clip_and_scroll: ClipAndScrollInfo,
|
||||
prim_info: &LayerPrimitiveInfo,
|
||||
box_offset: &LayerVector2D,
|
||||
color: &ColorF,
|
||||
blur_radius: f32,
|
||||
spread_radius: f32,
|
||||
border_radius: BorderRadius,
|
||||
clip_mode: BoxShadowClipMode,
|
||||
) {
|
||||
if color.a == 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let spread_amount = match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
spread_radius
|
||||
}
|
||||
BoxShadowClipMode::Inset => {
|
||||
-spread_radius
|
||||
}
|
||||
};
|
||||
|
||||
let shadow_radius = adjust_border_radius_for_box_shadow(
|
||||
border_radius,
|
||||
spread_amount,
|
||||
spread_radius
|
||||
);
|
||||
let shadow_rect = prim_info.rect
|
||||
.translate(box_offset)
|
||||
.inflate(spread_amount, spread_amount);
|
||||
|
||||
if blur_radius == 0.0 {
|
||||
let mut clips = Vec::new();
|
||||
|
||||
let fast_info = match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
// TODO(gw): Add a fast path for ClipOut + zero border radius!
|
||||
clips.push(ClipSource::RoundedRectangle(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::ClipOut
|
||||
));
|
||||
|
||||
LayerPrimitiveInfo::with_clip(
|
||||
shadow_rect,
|
||||
LocalClip::RoundedRect(
|
||||
shadow_rect,
|
||||
ComplexClipRegion::new(
|
||||
shadow_rect,
|
||||
shadow_radius,
|
||||
ClipMode::Clip,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
BoxShadowClipMode::Inset => {
|
||||
clips.push(ClipSource::RoundedRectangle(
|
||||
shadow_rect,
|
||||
shadow_radius,
|
||||
ClipMode::ClipOut
|
||||
));
|
||||
|
||||
LayerPrimitiveInfo::with_clip(
|
||||
prim_info.rect,
|
||||
LocalClip::RoundedRect(
|
||||
prim_info.rect,
|
||||
ComplexClipRegion::new(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::Clip
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
&fast_info,
|
||||
clips,
|
||||
PrimitiveContainer::Rectangle(RectanglePrimitive {
|
||||
color: *color,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
let blur_offset = 2.0 * blur_radius;
|
||||
let mut extra_clips = vec![];
|
||||
let mut blur_regions = vec![];
|
||||
|
||||
match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
let brush_prim = BrushPrimitive {
|
||||
clip_mode: ClipMode::Clip,
|
||||
radius: shadow_radius,
|
||||
};
|
||||
|
||||
let brush_rect = LayerRect::new(LayerPoint::new(blur_offset, blur_offset),
|
||||
shadow_rect.size);
|
||||
|
||||
let brush_info = LayerPrimitiveInfo::new(brush_rect);
|
||||
|
||||
let brush_prim_index = self.create_primitive(
|
||||
clip_and_scroll,
|
||||
&brush_info,
|
||||
Vec::new(),
|
||||
PrimitiveContainer::Brush(brush_prim),
|
||||
);
|
||||
|
||||
let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
|
||||
let blur_range = BLUR_SAMPLE_SCALE * blur_radius;
|
||||
|
||||
let size = pic_rect.size;
|
||||
|
||||
let tl = LayerSize::new(
|
||||
blur_radius.max(border_radius.top_left.width),
|
||||
blur_radius.max(border_radius.top_left.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
let tr = LayerSize::new(
|
||||
blur_radius.max(border_radius.top_right.width),
|
||||
blur_radius.max(border_radius.top_right.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
let br = LayerSize::new(
|
||||
blur_radius.max(border_radius.bottom_right.width),
|
||||
blur_radius.max(border_radius.bottom_right.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
let bl = LayerSize::new(
|
||||
blur_radius.max(border_radius.bottom_left.width),
|
||||
blur_radius.max(border_radius.bottom_left.height)
|
||||
) * BLUR_SAMPLE_SCALE;
|
||||
|
||||
let max_width = tl.width.max(tr.width.max(bl.width.max(br.width)));
|
||||
let max_height = tl.height.max(tr.height.max(bl.height.max(br.height)));
|
||||
|
||||
// Apply a conservative test that if any of the blur regions below
|
||||
// will overlap, we won't bother applying the region optimization
|
||||
// and will just blur the entire thing. This should only happen
|
||||
// in rare cases, where either the blur radius or border radius
|
||||
// is very large, in which case there's no real point in trying
|
||||
// to only blur a small region anyway.
|
||||
if max_width < 0.5 * size.width && max_height < 0.5 * size.height {
|
||||
blur_regions.push(LayerRect::from_floats(0.0, 0.0, tl.width, tl.height));
|
||||
blur_regions.push(LayerRect::from_floats(size.width - tr.width, 0.0, size.width, tr.height));
|
||||
blur_regions.push(LayerRect::from_floats(size.width - br.width, size.height - br.height, size.width, size.height));
|
||||
blur_regions.push(LayerRect::from_floats(0.0, size.height - bl.height, bl.width, size.height));
|
||||
|
||||
blur_regions.push(LayerRect::from_floats(0.0, tl.height, blur_range, size.height - bl.height));
|
||||
blur_regions.push(LayerRect::from_floats(size.width - blur_range, tr.height, size.width, size.height - br.height));
|
||||
blur_regions.push(LayerRect::from_floats(tl.width, 0.0, size.width - tr.width, blur_range));
|
||||
blur_regions.push(LayerRect::from_floats(bl.width, size.height - blur_range, size.width - br.width, size.height));
|
||||
}
|
||||
|
||||
let mut pic_prim = PicturePrimitive::new_box_shadow(
|
||||
blur_radius,
|
||||
*color,
|
||||
blur_regions,
|
||||
BoxShadowClipMode::Outset,
|
||||
);
|
||||
|
||||
pic_prim.add_primitive(brush_prim_index, clip_and_scroll);
|
||||
|
||||
extra_clips.push(ClipSource::RoundedRectangle(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::ClipOut,
|
||||
));
|
||||
|
||||
let pic_info = LayerPrimitiveInfo::new(pic_rect);
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
&pic_info,
|
||||
extra_clips,
|
||||
PrimitiveContainer::Picture(pic_prim),
|
||||
);
|
||||
}
|
||||
BoxShadowClipMode::Inset => {
|
||||
let brush_prim = BrushPrimitive {
|
||||
clip_mode: ClipMode::ClipOut,
|
||||
radius: shadow_radius,
|
||||
};
|
||||
|
||||
let mut brush_rect = shadow_rect;
|
||||
brush_rect.origin.x = brush_rect.origin.x - prim_info.rect.origin.x + blur_offset;
|
||||
brush_rect.origin.y = brush_rect.origin.y - prim_info.rect.origin.y + blur_offset;
|
||||
|
||||
let brush_info = LayerPrimitiveInfo::new(brush_rect);
|
||||
|
||||
let brush_prim_index = self.create_primitive(
|
||||
clip_and_scroll,
|
||||
&brush_info,
|
||||
Vec::new(),
|
||||
PrimitiveContainer::Brush(brush_prim),
|
||||
);
|
||||
|
||||
let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
|
||||
|
||||
// TODO(gw): Apply minimal blur regions for inset box shadows.
|
||||
|
||||
let mut pic_prim = PicturePrimitive::new_box_shadow(
|
||||
blur_radius,
|
||||
*color,
|
||||
blur_regions,
|
||||
BoxShadowClipMode::Inset,
|
||||
);
|
||||
|
||||
pic_prim.add_primitive(brush_prim_index, clip_and_scroll);
|
||||
|
||||
extra_clips.push(ClipSource::RoundedRectangle(
|
||||
prim_info.rect,
|
||||
border_radius,
|
||||
ClipMode::Clip,
|
||||
));
|
||||
|
||||
let pic_info = LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect);
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
&pic_info,
|
||||
extra_clips,
|
||||
PrimitiveContainer::Picture(pic_prim),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2106,15 +1858,18 @@ impl FrameBuilder {
|
||||
match *filter {
|
||||
FilterOp::Blur(blur_radius) => {
|
||||
let blur_radius = device_length(blur_radius, device_pixel_ratio);
|
||||
let blur_std_deviation = blur_radius.0 as f32;
|
||||
let inflate_size = blur_std_deviation * BLUR_SAMPLE_SCALE;
|
||||
render_tasks.get_mut(current_task_id)
|
||||
.inflate(blur_radius.0);
|
||||
.inflate(inflate_size as i32);
|
||||
let blur_render_task = RenderTask::new_blur(
|
||||
blur_radius,
|
||||
blur_std_deviation,
|
||||
current_task_id,
|
||||
render_tasks,
|
||||
RenderTargetKind::Color,
|
||||
&[],
|
||||
ClearMode::Transparent,
|
||||
ColorF::new(0.0, 0.0, 0.0, 0.0),
|
||||
);
|
||||
let blur_render_task_id = render_tasks.add(blur_render_task);
|
||||
let item = AlphaRenderItem::HardwareComposite(
|
||||
@ -2122,8 +1877,8 @@ impl FrameBuilder {
|
||||
blur_render_task_id,
|
||||
HardwareCompositeOp::PremultipliedAlpha,
|
||||
DeviceIntPoint::new(
|
||||
screen_origin.x - blur_radius.0,
|
||||
screen_origin.y - blur_radius.0,
|
||||
screen_origin.x - inflate_size as i32,
|
||||
screen_origin.y - inflate_size as i32,
|
||||
),
|
||||
next_z,
|
||||
);
|
||||
@ -2396,67 +2151,3 @@ impl FrameBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_border_radius_for_box_shadow(
|
||||
radius: BorderRadius,
|
||||
spread_amount: f32,
|
||||
spread_radius: f32,
|
||||
) -> BorderRadius {
|
||||
BorderRadius {
|
||||
top_left: adjust_corner_for_box_shadow(
|
||||
radius.top_left,
|
||||
spread_radius,
|
||||
spread_amount,
|
||||
),
|
||||
top_right: adjust_corner_for_box_shadow(
|
||||
radius.top_right,
|
||||
spread_radius,
|
||||
spread_amount,
|
||||
),
|
||||
bottom_right: adjust_corner_for_box_shadow(
|
||||
radius.bottom_right,
|
||||
spread_radius,
|
||||
spread_amount,
|
||||
),
|
||||
bottom_left: adjust_corner_for_box_shadow(
|
||||
radius.bottom_left,
|
||||
spread_radius,
|
||||
spread_amount,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_corner_for_box_shadow(
|
||||
corner: LayoutSize,
|
||||
spread_amount: f32,
|
||||
spread_radius: f32,
|
||||
) -> LayoutSize {
|
||||
LayoutSize::new(
|
||||
adjust_radius_for_box_shadow(
|
||||
corner.width,
|
||||
spread_radius,
|
||||
spread_amount
|
||||
),
|
||||
adjust_radius_for_box_shadow(
|
||||
corner.height,
|
||||
spread_radius,
|
||||
spread_amount
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn adjust_radius_for_box_shadow(
|
||||
border_radius: f32,
|
||||
spread_amount: f32,
|
||||
spread_radius: f32,
|
||||
) -> f32 {
|
||||
// Adjust the shadow box radius as per:
|
||||
// https://drafts.csswg.org/css-backgrounds-3/#shadow-shape
|
||||
let sharpness_scale = if border_radius < spread_radius {
|
||||
let r = border_radius / spread_amount;
|
||||
1.0 + (r - 1.0) * (r - 1.0) * (r - 1.0)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
(border_radius + spread_amount * sharpness_scale).max(0.0)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{DevicePoint, DeviceUintSize, FontInstance, GlyphKey};
|
||||
use glyph_rasterizer::GlyphFormat;
|
||||
use internal_types::FastHashMap;
|
||||
use resource_cache::ResourceClassCache;
|
||||
use std::sync::Arc;
|
||||
@ -14,6 +15,7 @@ pub struct CachedGlyphInfo {
|
||||
pub size: DeviceUintSize,
|
||||
pub offset: DevicePoint,
|
||||
pub scale: f32,
|
||||
pub format: GlyphFormat,
|
||||
}
|
||||
|
||||
pub type GlyphKeyCache = ResourceClassCache<GlyphKey, Option<CachedGlyphInfo>>;
|
||||
|
@ -3,10 +3,9 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[cfg(test)]
|
||||
use api::{ColorF, IdNamespace, LayoutPoint};
|
||||
use api::{DevicePoint, DeviceUintSize, FontInstance};
|
||||
use api::{FontKey, FontTemplate, FontRenderMode, ColorU};
|
||||
use api::{GlyphDimensions, GlyphKey, SubpixelDirection};
|
||||
use api::{ColorF, IdNamespace, LayoutPoint, SubpixelDirection};
|
||||
use api::{DevicePoint, DeviceUintSize, FontInstance, FontRenderMode};
|
||||
use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey};
|
||||
use api::{ImageData, ImageDescriptor, ImageFormat};
|
||||
#[cfg(test)]
|
||||
use app_units::Au;
|
||||
@ -14,7 +13,7 @@ use device::TextureFilter;
|
||||
use glyph_cache::{CachedGlyphInfo, GlyphCache};
|
||||
use gpu_cache::GpuCache;
|
||||
use internal_types::FastHashSet;
|
||||
use platform::font::{FontContext, RasterizedGlyph};
|
||||
use platform::font::FontContext;
|
||||
use profiler::TextureCacheProfileCounters;
|
||||
use rayon::ThreadPool;
|
||||
use rayon::prelude::*;
|
||||
@ -24,6 +23,35 @@ use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use texture_cache::{TextureCache, TextureCacheHandle};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum GlyphFormat {
|
||||
Mono,
|
||||
Alpha,
|
||||
Subpixel,
|
||||
ColorBitmap,
|
||||
}
|
||||
|
||||
impl From<FontRenderMode> for GlyphFormat {
|
||||
fn from(render_mode: FontRenderMode) -> GlyphFormat {
|
||||
match render_mode {
|
||||
FontRenderMode::Mono => GlyphFormat::Mono,
|
||||
FontRenderMode::Alpha => GlyphFormat::Alpha,
|
||||
FontRenderMode::Subpixel => GlyphFormat::Subpixel,
|
||||
FontRenderMode::Bitmap => GlyphFormat::ColorBitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RasterizedGlyph {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub scale: f32,
|
||||
pub format: GlyphFormat,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct FontContexts {
|
||||
// These worker are mostly accessed from their corresponding worker threads.
|
||||
// The goal is that there should be no noticeable contention on the muteces.
|
||||
@ -145,26 +173,7 @@ impl GlyphRasterizer {
|
||||
}
|
||||
|
||||
pub fn prepare_font(&self, font: &mut FontInstance) {
|
||||
// In alpha/mono mode, the color of the font is irrelevant.
|
||||
// Forcing it to black in those cases saves rasterizing glyphs
|
||||
// of different colors when not needed.
|
||||
match font.render_mode {
|
||||
FontRenderMode::Mono | FontRenderMode::Bitmap => {
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
// Subpixel positioning is disabled in mono and bitmap modes.
|
||||
font.subpx_dir = SubpixelDirection::None;
|
||||
}
|
||||
FontRenderMode::Alpha => {
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
}
|
||||
FontRenderMode::Subpixel => {
|
||||
// In subpixel mode, we only actually need the color if preblending
|
||||
// is used in the font backend.
|
||||
if !FontContext::has_gamma_correct_subpixel_aa() {
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
FontContext::prepare_font(font);
|
||||
}
|
||||
|
||||
pub fn request_glyphs(
|
||||
@ -345,6 +354,7 @@ impl GlyphRasterizer {
|
||||
size: DeviceUintSize::new(glyph.width, glyph.height),
|
||||
offset: DevicePoint::new(glyph.left, glyph.top),
|
||||
scale: glyph.scale,
|
||||
format: glyph.format,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -140,22 +140,28 @@ impl From<CompositePrimitiveInstance> for PrimitiveInstance {
|
||||
}
|
||||
}
|
||||
|
||||
// Whether this brush is being drawn on a Picture
|
||||
// task (new) or an alpha batch task (legacy).
|
||||
// Can be removed once everything uses pictures.
|
||||
pub const BRUSH_FLAG_USES_PICTURE: i32 = (1 << 0);
|
||||
|
||||
// TODO(gw): While we are comverting 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
|
||||
// addresses etc can reasonably become
|
||||
// a u16 type.
|
||||
#[repr(C)]
|
||||
pub struct BrushInstance {
|
||||
picture_address: RenderTaskAddress,
|
||||
prim_address: GpuCacheAddress,
|
||||
}
|
||||
|
||||
impl BrushInstance {
|
||||
pub fn new(
|
||||
picture_address: RenderTaskAddress,
|
||||
prim_address: GpuCacheAddress
|
||||
) -> BrushInstance {
|
||||
BrushInstance {
|
||||
picture_address,
|
||||
prim_address,
|
||||
}
|
||||
}
|
||||
pub picture_address: RenderTaskAddress,
|
||||
pub prim_address: GpuCacheAddress,
|
||||
pub layer_address: PackedLayerAddress,
|
||||
pub clip_task_address: RenderTaskAddress,
|
||||
pub z: i32,
|
||||
pub flags: i32,
|
||||
pub user_data0: i32,
|
||||
pub user_data1: i32,
|
||||
}
|
||||
|
||||
impl From<BrushInstance> for PrimitiveInstance {
|
||||
@ -164,12 +170,12 @@ impl From<BrushInstance> for PrimitiveInstance {
|
||||
data: [
|
||||
instance.picture_address.0 as i32,
|
||||
instance.prim_address.as_int(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
instance.layer_address.0,
|
||||
instance.clip_task_address.0 as i32,
|
||||
instance.z,
|
||||
instance.flags,
|
||||
instance.user_data0,
|
||||
instance.user_data1,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ impl RendererFrame {
|
||||
pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
|
||||
layers_bouncing_back: FastHashSet<ClipId>,
|
||||
frame: Option<tiling::Frame>,
|
||||
) -> RendererFrame {
|
||||
) -> Self {
|
||||
RendererFrame {
|
||||
pipeline_epoch_map,
|
||||
layers_bouncing_back,
|
||||
|
@ -50,6 +50,7 @@ extern crate log;
|
||||
extern crate thread_profiler;
|
||||
|
||||
mod border;
|
||||
mod box_shadow;
|
||||
mod clip;
|
||||
mod clip_scroll_node;
|
||||
mod clip_scroll_tree;
|
||||
|
@ -4,9 +4,10 @@
|
||||
|
||||
use api::{ColorF, ClipAndScrollInfo, device_length, DeviceIntSize};
|
||||
use api::{BoxShadowClipMode, LayerRect, Shadow};
|
||||
use box_shadow::BLUR_SAMPLE_SCALE;
|
||||
use frame_builder::PrimitiveContext;
|
||||
use gpu_cache::GpuDataRequest;
|
||||
use prim_store::{PrimitiveIndex, PrimitiveMetadata};
|
||||
use prim_store::PrimitiveIndex;
|
||||
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
|
||||
use tiling::RenderTargetKind;
|
||||
|
||||
@ -45,6 +46,7 @@ pub struct PicturePrimitive {
|
||||
pub prim_runs: Vec<PrimitiveRun>,
|
||||
pub render_task_id: Option<RenderTaskId>,
|
||||
pub kind: PictureKind,
|
||||
pub content_rect: LayerRect,
|
||||
|
||||
// TODO(gw): Add a mode that specifies if this
|
||||
// picture should be rasterized in
|
||||
@ -56,6 +58,7 @@ impl PicturePrimitive {
|
||||
PicturePrimitive {
|
||||
prim_runs: Vec::new(),
|
||||
render_task_id: None,
|
||||
content_rect: LayerRect::zero(),
|
||||
kind: PictureKind::TextShadow {
|
||||
shadow,
|
||||
},
|
||||
@ -71,9 +74,10 @@ impl PicturePrimitive {
|
||||
PicturePrimitive {
|
||||
prim_runs: Vec::new(),
|
||||
render_task_id: None,
|
||||
content_rect: LayerRect::zero(),
|
||||
kind: PictureKind::BoxShadow {
|
||||
blur_radius,
|
||||
color,
|
||||
color: color.premultiplied(),
|
||||
blur_regions,
|
||||
clip_mode,
|
||||
},
|
||||
@ -90,8 +94,19 @@ impl PicturePrimitive {
|
||||
pub fn add_primitive(
|
||||
&mut self,
|
||||
prim_index: PrimitiveIndex,
|
||||
local_rect: &LayerRect,
|
||||
clip_and_scroll: ClipAndScrollInfo
|
||||
) {
|
||||
// TODO(gw): Accumulating the primitive local rect
|
||||
// into the content rect here is fine, for now.
|
||||
// The only way pictures are currently used,
|
||||
// all the items added to a picture are known
|
||||
// to be in the same local space. Once we start
|
||||
// using pictures for other uses, we will need
|
||||
// to consider the space of a primitive in order
|
||||
// to build a correct contect rect!
|
||||
self.content_rect = self.content_rect.union(local_rect);
|
||||
|
||||
if let Some(ref mut run) = self.prim_runs.last_mut() {
|
||||
if run.clip_and_scroll == clip_and_scroll &&
|
||||
run.prim_index.0 + run.count == prim_index.0 {
|
||||
@ -107,10 +122,38 @@ impl PicturePrimitive {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> LayerRect {
|
||||
match self.kind {
|
||||
PictureKind::TextShadow { ref shadow } => {
|
||||
let blur_offset = shadow.blur_radius * BLUR_SAMPLE_SCALE;
|
||||
|
||||
self.content_rect = self.content_rect.inflate(
|
||||
blur_offset,
|
||||
blur_offset,
|
||||
);
|
||||
|
||||
self.content_rect.translate(&shadow.offset)
|
||||
}
|
||||
PictureKind::BoxShadow { blur_radius, .. } => {
|
||||
// TODO(gw): The 2.0 here should actually be BLUR_SAMPLE_SCALE.
|
||||
// I'm leaving it as is for now, to avoid having to
|
||||
// change the code in box_shadow.rs. As I work on
|
||||
// the box shadow optimizations, I'll fix this up.
|
||||
let blur_offset = blur_radius * 2.0;
|
||||
|
||||
self.content_rect = self.content_rect.inflate(
|
||||
blur_offset,
|
||||
blur_offset,
|
||||
);
|
||||
|
||||
self.content_rect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_for_render(
|
||||
&mut self,
|
||||
prim_index: PrimitiveIndex,
|
||||
prim_metadata: &PrimitiveMetadata,
|
||||
prim_context: &PrimitiveContext,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
) {
|
||||
@ -119,64 +162,63 @@ impl PicturePrimitive {
|
||||
// blur to that text run in order to build the actual primitive
|
||||
// which will be blitted to the framebuffer.
|
||||
let cache_width =
|
||||
(prim_metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
|
||||
(self.content_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
|
||||
let cache_height =
|
||||
(prim_metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
|
||||
(self.content_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
|
||||
let cache_size = DeviceIntSize::new(cache_width, cache_height);
|
||||
|
||||
let (blur_radius, target_kind, blur_regions, clear_mode) = match self.kind {
|
||||
let (blur_radius, target_kind, blur_regions, clear_mode, color) = match self.kind {
|
||||
PictureKind::TextShadow { ref shadow } => {
|
||||
let dummy: &[LayerRect] = &[];
|
||||
(shadow.blur_radius, RenderTargetKind::Color, dummy, ClearMode::Transparent)
|
||||
(shadow.blur_radius,
|
||||
RenderTargetKind::Color,
|
||||
dummy,
|
||||
ClearMode::Transparent,
|
||||
shadow.color)
|
||||
}
|
||||
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, .. } => {
|
||||
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, .. } => {
|
||||
let clear_mode = match clip_mode {
|
||||
BoxShadowClipMode::Outset => ClearMode::One,
|
||||
BoxShadowClipMode::Inset => ClearMode::Zero,
|
||||
};
|
||||
(blur_radius, RenderTargetKind::Alpha, blur_regions.as_slice(), clear_mode)
|
||||
(blur_radius,
|
||||
RenderTargetKind::Alpha,
|
||||
blur_regions.as_slice(),
|
||||
clear_mode,
|
||||
color)
|
||||
}
|
||||
};
|
||||
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
|
||||
|
||||
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
|
||||
// "the image that would be generated by applying to the shadow a
|
||||
// Gaussian blur with a standard deviation equal to half the blur radius."
|
||||
let blur_std_deviation = blur_radius.0 as f32 * 0.5;
|
||||
|
||||
let picture_task = RenderTask::new_picture(
|
||||
cache_size,
|
||||
prim_index,
|
||||
target_kind,
|
||||
self.content_rect.origin,
|
||||
color,
|
||||
);
|
||||
let picture_task_id = render_tasks.add(picture_task);
|
||||
let render_task = RenderTask::new_blur(
|
||||
blur_radius,
|
||||
blur_std_deviation,
|
||||
picture_task_id,
|
||||
render_tasks,
|
||||
target_kind,
|
||||
blur_regions,
|
||||
clear_mode,
|
||||
color,
|
||||
);
|
||||
self.render_task_id = Some(render_tasks.add(render_task));
|
||||
}
|
||||
|
||||
pub fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
|
||||
match self.kind {
|
||||
PictureKind::TextShadow { ref shadow } => {
|
||||
request.push(shadow.color);
|
||||
request.push([
|
||||
shadow.offset.x,
|
||||
shadow.offset.y,
|
||||
shadow.blur_radius,
|
||||
0.0,
|
||||
]);
|
||||
}
|
||||
PictureKind::BoxShadow { blur_radius, color, .. } => {
|
||||
request.push(color);
|
||||
request.push([
|
||||
0.0,
|
||||
0.0,
|
||||
blur_radius,
|
||||
0.0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) {
|
||||
// TODO(gw): We'll need to write the GPU blocks
|
||||
// here specific to a brush primitive
|
||||
// once we start drawing pictures as brushes!
|
||||
}
|
||||
|
||||
pub fn target_kind(&self) -> RenderTargetKind {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
|
||||
use api::{FontInstance, FontVariation, NativeFontHandle};
|
||||
use api::GlyphKey;
|
||||
use api::{GlyphKey, SubpixelDirection};
|
||||
use app_units::Au;
|
||||
use core_foundation::array::{CFArray, CFArrayRef};
|
||||
use core_foundation::base::TCFType;
|
||||
@ -22,6 +22,7 @@ use core_text;
|
||||
use core_text::font::{CTFont, CTFontRef};
|
||||
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
|
||||
use gamma_lut::{Color as ColorLut, GammaLut};
|
||||
use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
|
||||
use internal_types::FastHashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::ptr;
|
||||
@ -37,28 +38,6 @@ pub struct FontContext {
|
||||
// all hidden inside their font context.
|
||||
unsafe impl Send for FontContext {}
|
||||
|
||||
pub struct RasterizedGlyph {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub scale: f32,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl RasterizedGlyph {
|
||||
pub fn blank() -> RasterizedGlyph {
|
||||
RasterizedGlyph {
|
||||
top: 0.0,
|
||||
left: 0.0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
scale: 1.0,
|
||||
bytes: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GlyphMetrics {
|
||||
rasterized_left: i32,
|
||||
rasterized_descent: i32,
|
||||
@ -94,6 +73,14 @@ fn supports_subpixel_aa() -> bool {
|
||||
data[0] != data[1] || data[1] != data[2]
|
||||
}
|
||||
|
||||
fn should_use_white_on_black(color: ColorU) -> bool {
|
||||
let r = color.r as f32 / 255.0;
|
||||
let g = color.g as f32 / 255.0;
|
||||
let b = color.b as f32 / 255.0;
|
||||
// These thresholds were determined on 10.12 by observing what CG does.
|
||||
r >= 0.333 && g >= 0.333 && b >= 0.333 && r + g + b >= 2.0
|
||||
}
|
||||
|
||||
fn get_glyph_metrics(
|
||||
ct_font: &CTFont,
|
||||
glyph: CGGlyph,
|
||||
@ -434,8 +421,24 @@ impl FontContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_gamma_correct_subpixel_aa() -> bool {
|
||||
true
|
||||
pub fn prepare_font(font: &mut FontInstance) {
|
||||
match font.render_mode {
|
||||
FontRenderMode::Mono | FontRenderMode::Bitmap => {
|
||||
// In mono/bitmap modes the color of the font is irrelevant.
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
// Subpixel positioning is disabled in mono and bitmap modes.
|
||||
font.subpx_dir = SubpixelDirection::None;
|
||||
}
|
||||
FontRenderMode::Alpha => {
|
||||
font.color = if font.platform_options.unwrap_or_default().font_smoothing &&
|
||||
should_use_white_on_black(font.color) {
|
||||
ColorU::new(255, 255, 255, 255)
|
||||
} else {
|
||||
ColorU::new(0, 0, 0, 255)
|
||||
};
|
||||
}
|
||||
FontRenderMode::Subpixel => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rasterize_glyph(
|
||||
@ -445,14 +448,14 @@ impl FontContext {
|
||||
) -> Option<RasterizedGlyph> {
|
||||
let ct_font = match self.get_ct_font(font.font_key, font.size, &font.variations) {
|
||||
Some(font) => font,
|
||||
None => return Some(RasterizedGlyph::blank()),
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let glyph = key.index as CGGlyph;
|
||||
let (x_offset, y_offset) = font.get_subpx_offset(key);
|
||||
let metrics = get_glyph_metrics(&ct_font, glyph, x_offset, y_offset);
|
||||
if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
|
||||
return Some(RasterizedGlyph::blank());
|
||||
return None;
|
||||
}
|
||||
|
||||
let context_flags = match font.render_mode {
|
||||
@ -497,13 +500,29 @@ impl FontContext {
|
||||
// At the end of all this, WR expects individual RGB channels and ignores alpha
|
||||
// for subpixel AA.
|
||||
// For alpha/mono, WR ignores all channels other than alpha.
|
||||
// Also note that WR expects text to be black bg with white text, so invert
|
||||
// when we draw the glyphs.
|
||||
let (antialias, smooth, bg_color) = match font.render_mode {
|
||||
FontRenderMode::Subpixel => (true, true, 1.0),
|
||||
FontRenderMode::Alpha => (true, false, 1.0),
|
||||
FontRenderMode::Bitmap => (true, false, 0.0),
|
||||
FontRenderMode::Mono => (false, false, 1.0),
|
||||
// Also note that WR expects text to be white text on black bg, so invert
|
||||
// when we draw the glyphs as black on white.
|
||||
//
|
||||
// Unless platform_options.font_smoothing is false, the grayscale AA'd version
|
||||
// of the glyph will actually be rasterized with subpixel AA. The color channels
|
||||
// will be then converted to luminance in gamma_correct_pixels to produce the
|
||||
// final grayscale AA. This ensures that the dilation of the glyph from grayscale
|
||||
// AA more closely resembles the dilation from subpixel AA in the general case.
|
||||
let use_white_on_black = should_use_white_on_black(font.color);
|
||||
let use_font_smoothing = font.platform_options.unwrap_or_default().font_smoothing;
|
||||
let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = match font.render_mode {
|
||||
FontRenderMode::Subpixel => if use_white_on_black {
|
||||
(true, true, 1.0, 0.0, 1.0, false)
|
||||
} else {
|
||||
(true, true, 0.0, 1.0, 1.0, true)
|
||||
},
|
||||
FontRenderMode::Alpha => if use_font_smoothing && use_white_on_black {
|
||||
(true, use_font_smoothing, 1.0, 0.0, 1.0, false)
|
||||
} else {
|
||||
(true, use_font_smoothing, 0.0, 1.0, 1.0, true)
|
||||
},
|
||||
FontRenderMode::Bitmap => (true, false, 0.0, 0.0, 0.0, false),
|
||||
FontRenderMode::Mono => (false, false, 0.0, 1.0, 1.0, true),
|
||||
};
|
||||
|
||||
// These are always true in Gecko, even for non-AA fonts
|
||||
@ -527,7 +546,7 @@ impl FontContext {
|
||||
|
||||
// Always draw black text on a white background
|
||||
// Fill the background
|
||||
cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_color);
|
||||
cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha);
|
||||
let rect = CGRect {
|
||||
origin: CGPoint { x: 0.0, y: 0.0 },
|
||||
size: CGSize {
|
||||
@ -538,7 +557,7 @@ impl FontContext {
|
||||
cg_context.fill_rect(rect);
|
||||
|
||||
// Set the text color
|
||||
cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
|
||||
cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0);
|
||||
cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
|
||||
ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone());
|
||||
|
||||
@ -547,7 +566,7 @@ impl FontContext {
|
||||
if font.render_mode != FontRenderMode::Bitmap {
|
||||
// Convert to linear space for subpixel AA.
|
||||
// We explicitly do not do this for grayscale AA
|
||||
if font.render_mode == FontRenderMode::Subpixel {
|
||||
if smooth {
|
||||
self.gamma_lut.coregraphics_convert_to_linear_bgra(
|
||||
&mut rasterized_pixels,
|
||||
metrics.rasterized_width as usize,
|
||||
@ -555,16 +574,16 @@ impl FontContext {
|
||||
);
|
||||
}
|
||||
|
||||
// We need to invert the pixels back since right now
|
||||
// transparent pixels are actually opaque white.
|
||||
for i in 0 .. metrics.rasterized_height {
|
||||
let current_height = (i * metrics.rasterized_width * 4) as usize;
|
||||
let end_row = current_height + (metrics.rasterized_width as usize * 4);
|
||||
|
||||
for pixel in rasterized_pixels[current_height .. end_row].chunks_mut(4) {
|
||||
pixel[0] = 255 - pixel[0];
|
||||
pixel[1] = 255 - pixel[1];
|
||||
pixel[2] = 255 - pixel[2];
|
||||
if invert {
|
||||
pixel[0] = 255 - pixel[0];
|
||||
pixel[1] = 255 - pixel[1];
|
||||
pixel[2] = 255 - pixel[2];
|
||||
}
|
||||
|
||||
pixel[3] = match font.render_mode {
|
||||
FontRenderMode::Subpixel => 255,
|
||||
@ -575,13 +594,15 @@ impl FontContext {
|
||||
} // end row
|
||||
} // end height
|
||||
|
||||
self.gamma_correct_pixels(
|
||||
&mut rasterized_pixels,
|
||||
metrics.rasterized_width as usize,
|
||||
metrics.rasterized_height as usize,
|
||||
font.render_mode,
|
||||
font.color,
|
||||
);
|
||||
if smooth {
|
||||
self.gamma_correct_pixels(
|
||||
&mut rasterized_pixels,
|
||||
metrics.rasterized_width as usize,
|
||||
metrics.rasterized_height as usize,
|
||||
font.render_mode,
|
||||
font.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(RasterizedGlyph {
|
||||
@ -590,6 +611,7 @@ impl FontContext {
|
||||
width: metrics.rasterized_width,
|
||||
height: metrics.rasterized_height,
|
||||
scale: 1.0,
|
||||
format: GlyphFormat::from(font.render_mode),
|
||||
bytes: rasterized_pixels,
|
||||
})
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions};
|
||||
use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
|
||||
use api::{NativeFontHandle, SubpixelDirection, GlyphKey};
|
||||
use api::{NativeFontHandle, SubpixelDirection, GlyphKey, ColorU};
|
||||
use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP};
|
||||
use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR};
|
||||
use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
|
||||
@ -18,6 +18,7 @@ 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, FT_Err_Cannot_Render_Glyph};
|
||||
use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
|
||||
use internal_types::FastHashMap;
|
||||
use std::{cmp, mem, ptr, slice};
|
||||
use std::sync::Arc;
|
||||
@ -49,15 +50,6 @@ pub struct FontContext {
|
||||
// a given FontContext so it is safe to move the latter between threads.
|
||||
unsafe impl Send for FontContext {}
|
||||
|
||||
pub struct RasterizedGlyph {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub scale: f32,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot);
|
||||
fn FT_GlyphSlot_Oblique(slot: FT_GlyphSlot);
|
||||
@ -378,9 +370,19 @@ impl FontContext {
|
||||
unsafe { FT_Select_Size(face, best_size) }
|
||||
}
|
||||
|
||||
pub fn has_gamma_correct_subpixel_aa() -> bool {
|
||||
// We don't do any preblending with FreeType currently, so the color is not used.
|
||||
false
|
||||
pub fn prepare_font(font: &mut FontInstance) {
|
||||
match font.render_mode {
|
||||
FontRenderMode::Mono | FontRenderMode::Bitmap => {
|
||||
// In mono/bitmap modes the color of the font is irrelevant.
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
// Subpixel positioning is disabled in mono and bitmap modes.
|
||||
font.subpx_dir = SubpixelDirection::None;
|
||||
}
|
||||
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
|
||||
// We don't do any preblending with FreeType currently, so the color is not used.
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize_glyph_outline(
|
||||
@ -489,17 +491,23 @@ impl FontContext {
|
||||
dimensions
|
||||
);
|
||||
|
||||
let (actual_width, actual_height) = match pixel_mode {
|
||||
let (format, actual_width, actual_height) = match pixel_mode {
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
|
||||
assert!(bitmap.width % 3 == 0);
|
||||
((bitmap.width / 3) as i32, bitmap.rows as i32)
|
||||
(GlyphFormat::Subpixel, (bitmap.width / 3) as i32, bitmap.rows as i32)
|
||||
}
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
|
||||
assert!(bitmap.rows % 3 == 0);
|
||||
(bitmap.width as i32, (bitmap.rows / 3) as i32)
|
||||
(GlyphFormat::Subpixel, bitmap.width as i32, (bitmap.rows / 3) as i32)
|
||||
}
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_MONO | FT_Pixel_Mode::FT_PIXEL_MODE_GRAY | FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
|
||||
(bitmap.width as i32, bitmap.rows as i32)
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
|
||||
(GlyphFormat::Mono, bitmap.width as i32, bitmap.rows as i32)
|
||||
}
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
|
||||
(GlyphFormat::Alpha, bitmap.width as i32, bitmap.rows as i32)
|
||||
}
|
||||
FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
|
||||
(GlyphFormat::ColorBitmap, bitmap.width as i32, bitmap.rows as i32)
|
||||
}
|
||||
_ => panic!("Unsupported {:?}", pixel_mode),
|
||||
};
|
||||
@ -605,6 +613,7 @@ impl FontContext {
|
||||
width: actual_width as u32,
|
||||
height: actual_height as u32,
|
||||
scale,
|
||||
format,
|
||||
bytes: final_buffer,
|
||||
})
|
||||
}
|
||||
|
@ -3,9 +3,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode};
|
||||
use api::{GlyphDimensions, GlyphKey};
|
||||
use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
|
||||
use dwrote;
|
||||
use gamma_lut::{Color as ColorLut, GammaLut};
|
||||
use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
|
||||
use internal_types::FastHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -28,15 +29,6 @@ pub struct FontContext {
|
||||
// all hidden inside their font context.
|
||||
unsafe impl Send for FontContext {}
|
||||
|
||||
pub struct RasterizedGlyph {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub scale: f32,
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TYPE {
|
||||
match render_mode {
|
||||
FontRenderMode::Mono | FontRenderMode::Bitmap => dwrote::DWRITE_TEXTURE_ALIASED_1x1,
|
||||
@ -310,8 +302,20 @@ impl FontContext {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn has_gamma_correct_subpixel_aa() -> bool {
|
||||
true
|
||||
pub fn prepare_font(font: &mut FontInstance) {
|
||||
match font.render_mode {
|
||||
FontRenderMode::Mono | FontRenderMode::Bitmap => {
|
||||
// In mono/bitmap modes the color of the font is irrelevant.
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
// Subpixel positioning is disabled in mono and bitmap modes.
|
||||
font.subpx_dir = SubpixelDirection::None;
|
||||
}
|
||||
FontRenderMode::Alpha => {
|
||||
// In alpha mode the color of the font is irrelevant.
|
||||
font.color = ColorU::new(255, 255, 255, 255);
|
||||
}
|
||||
FontRenderMode::Subpixel => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rasterize_glyph(
|
||||
@ -363,6 +367,7 @@ impl FontContext {
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
scale: 1.0,
|
||||
format: GlyphFormat::from(font.render_mode),
|
||||
bytes: rgba_pixels,
|
||||
})
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ pub struct RectanglePrimitive {
|
||||
|
||||
impl ToGpuBlocks for RectanglePrimitive {
|
||||
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
|
||||
request.push(self.color);
|
||||
request.push(self.color.premultiplied());
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,6 +197,7 @@ impl ToGpuBlocks for BrushPrimitive {
|
||||
#[repr(C)]
|
||||
pub struct LinePrimitive {
|
||||
pub color: ColorF,
|
||||
pub wavy_line_thickness: f32,
|
||||
pub style: LineStyle,
|
||||
pub orientation: LineOrientation,
|
||||
}
|
||||
@ -205,10 +206,10 @@ impl ToGpuBlocks for LinePrimitive {
|
||||
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
|
||||
request.push(self.color);
|
||||
request.push([
|
||||
self.wavy_line_thickness,
|
||||
pack_as_float(self.style as u32),
|
||||
pack_as_float(self.orientation as u32),
|
||||
0.0,
|
||||
0.0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -586,7 +587,7 @@ impl TextRunPrimitiveCpu {
|
||||
}
|
||||
|
||||
fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
|
||||
request.push(ColorF::from(self.font.color));
|
||||
request.push(ColorF::from(self.font.color).premultiplied());
|
||||
request.push([
|
||||
self.offset.x,
|
||||
self.offset.y,
|
||||
@ -1059,7 +1060,6 @@ impl PrimitiveStore {
|
||||
self.cpu_pictures[metadata.cpu_prim_index.0]
|
||||
.prepare_for_render(
|
||||
prim_index,
|
||||
metadata,
|
||||
prim_context,
|
||||
render_tasks
|
||||
);
|
||||
|
@ -6,13 +6,13 @@ use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DebugCommand, DeviceIntPo
|
||||
#[cfg(feature = "debugger")]
|
||||
use api::{BuiltDisplayListIter, SpecificDisplayItem};
|
||||
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg};
|
||||
use api::{IdNamespace, LayerPoint, PipelineId, RenderNotifier};
|
||||
use api::{HitTestResult, IdNamespace, LayerPoint, PipelineId, RenderNotifier};
|
||||
use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
|
||||
use api::channel::{PayloadSender, PayloadSenderHelperMethods};
|
||||
#[cfg(feature = "debugger")]
|
||||
use debug_server;
|
||||
use frame::Frame;
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
use frame::FrameContext;
|
||||
use frame_builder::{FrameBuilder, FrameBuilderConfig};
|
||||
use gpu_cache::GpuCache;
|
||||
use internal_types::{DebugOutput, FastHashMap, FastHashSet, RendererFrame, ResultMsg};
|
||||
use profiler::{BackendProfileCounters, ResourceProfileCounters};
|
||||
@ -32,7 +32,8 @@ use time::precise_time_ns;
|
||||
|
||||
struct Document {
|
||||
scene: Scene,
|
||||
frame: Frame,
|
||||
frame_ctx: FrameContext,
|
||||
frame_builder: Option<FrameBuilder>,
|
||||
window_size: DeviceUintSize,
|
||||
inner_rect: DeviceUintRect,
|
||||
pan: DeviceIntPoint,
|
||||
@ -64,7 +65,8 @@ impl Document {
|
||||
};
|
||||
Document {
|
||||
scene: Scene::new(),
|
||||
frame: Frame::new(config),
|
||||
frame_ctx: FrameContext::new(config),
|
||||
frame_builder: None,
|
||||
window_size: initial_size,
|
||||
inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_size),
|
||||
pan: DeviceIntPoint::zero(),
|
||||
@ -84,7 +86,8 @@ impl Document {
|
||||
|
||||
fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
|
||||
let accumulated_scale_factor = self.accumulated_scale_factor();
|
||||
self.frame.create(
|
||||
self.frame_builder = self.frame_ctx.create(
|
||||
self.frame_builder.take(),
|
||||
&self.scene,
|
||||
resource_cache,
|
||||
self.window_size,
|
||||
@ -104,16 +107,24 @@ impl Document {
|
||||
self.pan.x as f32 / accumulated_scale_factor,
|
||||
self.pan.y as f32 / accumulated_scale_factor,
|
||||
);
|
||||
self.frame.build_renderer_frame(
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
&self.scene.pipelines,
|
||||
accumulated_scale_factor,
|
||||
pan,
|
||||
&self.output_pipelines,
|
||||
&mut resource_profile.texture_cache,
|
||||
&mut resource_profile.gpu_cache,
|
||||
)
|
||||
match self.frame_builder {
|
||||
Some(ref mut builder) => {
|
||||
self.frame_ctx.build_renderer_frame(
|
||||
builder,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
&self.scene.pipelines,
|
||||
accumulated_scale_factor,
|
||||
pan,
|
||||
&self.output_pipelines,
|
||||
&mut resource_profile.texture_cache,
|
||||
&mut resource_profile.gpu_cache,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
self.frame_ctx.get_renderer_frame()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,7 +272,7 @@ impl RenderBackend {
|
||||
BuiltDisplayList::from_data(data.display_list_data, list_descriptor);
|
||||
|
||||
if !preserve_frame_state {
|
||||
doc.frame.discard_frame_state_for_pipeline(pipeline_id);
|
||||
doc.frame_ctx.discard_frame_state_for_pipeline(pipeline_id);
|
||||
}
|
||||
|
||||
let display_list_len = built_display_list.data().len();
|
||||
@ -309,7 +320,7 @@ impl RenderBackend {
|
||||
.update_resources(resources, &mut profile_counters.resources);
|
||||
|
||||
doc.scene.update_epoch(pipeline_id, epoch);
|
||||
doc.frame.update_epoch(pipeline_id, epoch);
|
||||
doc.frame_ctx.update_epoch(pipeline_id, epoch);
|
||||
|
||||
DocumentOp::Nop
|
||||
}
|
||||
@ -335,7 +346,7 @@ impl RenderBackend {
|
||||
profile_scope!("Scroll");
|
||||
let _timer = profile_counters.total_time.timer();
|
||||
|
||||
if doc.frame.scroll(delta, cursor, move_phase) && doc.render_on_scroll == Some(true)
|
||||
if doc.frame_ctx.scroll(delta, cursor, move_phase) && doc.render_on_scroll == Some(true)
|
||||
{
|
||||
let frame = doc.render(
|
||||
&mut self.resource_cache,
|
||||
@ -349,7 +360,13 @@ impl RenderBackend {
|
||||
}
|
||||
DocumentMsg::HitTest(pipeline_id, point, flags, tx) => {
|
||||
profile_scope!("HitTest");
|
||||
let result = doc.frame.hit_test(pipeline_id, point, flags);
|
||||
let result = match doc.frame_builder {
|
||||
Some(ref builder) => {
|
||||
let cst = doc.frame_ctx.get_clip_scroll_tree();
|
||||
builder.hit_test(cst, pipeline_id, point, flags)
|
||||
},
|
||||
None => HitTestResult::default(),
|
||||
};
|
||||
tx.send(result).unwrap();
|
||||
DocumentOp::Nop
|
||||
}
|
||||
@ -357,7 +374,7 @@ impl RenderBackend {
|
||||
profile_scope!("ScrollNodeWithScrollId");
|
||||
let _timer = profile_counters.total_time.timer();
|
||||
|
||||
if doc.frame.scroll_node(origin, id, clamp) && doc.render_on_scroll == Some(true) {
|
||||
if doc.frame_ctx.scroll_node(origin, id, clamp) && doc.render_on_scroll == Some(true) {
|
||||
let frame = doc.render(
|
||||
&mut self.resource_cache,
|
||||
&mut self.gpu_cache,
|
||||
@ -372,7 +389,7 @@ impl RenderBackend {
|
||||
profile_scope!("TickScrollingBounce");
|
||||
let _timer = profile_counters.total_time.timer();
|
||||
|
||||
doc.frame.tick_scrolling_bounce_animations();
|
||||
doc.frame_ctx.tick_scrolling_bounce_animations();
|
||||
if doc.render_on_scroll == Some(true) {
|
||||
let frame = doc.render(
|
||||
&mut self.resource_cache,
|
||||
@ -386,7 +403,7 @@ impl RenderBackend {
|
||||
}
|
||||
DocumentMsg::GetScrollNodeState(tx) => {
|
||||
profile_scope!("GetScrollNodeState");
|
||||
tx.send(doc.frame.get_scroll_node_state()).unwrap();
|
||||
tx.send(doc.frame_ctx.get_scroll_node_state()).unwrap();
|
||||
DocumentOp::Nop
|
||||
}
|
||||
DocumentMsg::GenerateFrame(property_bindings) => {
|
||||
@ -664,12 +681,14 @@ impl RenderBackend {
|
||||
for (_, doc) in &self.documents {
|
||||
let debug_node = debug_server::TreeNode::new("document clip_scroll tree");
|
||||
let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
|
||||
|
||||
// TODO(gw): Restructure the storage of clip-scroll tree, clip store
|
||||
// etc so this isn't so untidy.
|
||||
let clip_store = &doc.frame.frame_builder.as_ref().unwrap().clip_store;
|
||||
doc.frame
|
||||
.clip_scroll_tree
|
||||
.print_with(clip_store, &mut builder);
|
||||
if let Some(ref frame_builder) = doc.frame_builder {
|
||||
doc.frame_ctx
|
||||
.get_clip_scroll_tree()
|
||||
.print_with(&frame_builder.clip_store, &mut builder);
|
||||
}
|
||||
|
||||
debug_root.add(builder.build());
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
* 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::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||
use api::{FilterOp, MixBlendMode};
|
||||
use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||
use api::{ColorF, FilterOp, LayerPoint, MixBlendMode};
|
||||
use api::{LayerRect, PipelineId};
|
||||
use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
|
||||
use clip_scroll_tree::CoordinateSystemId;
|
||||
@ -258,13 +258,16 @@ pub struct CacheMaskTask {
|
||||
pub struct PictureTask {
|
||||
pub prim_index: PrimitiveIndex,
|
||||
pub target_kind: RenderTargetKind,
|
||||
pub content_origin: LayerPoint,
|
||||
pub color: ColorF,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlurTask {
|
||||
pub blur_radius: DeviceIntLength,
|
||||
pub blur_std_deviation: f32,
|
||||
pub target_kind: RenderTargetKind,
|
||||
pub regions: Vec<LayerRect>,
|
||||
pub color: ColorF,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -333,6 +336,8 @@ impl RenderTask {
|
||||
size: DeviceIntSize,
|
||||
prim_index: PrimitiveIndex,
|
||||
target_kind: RenderTargetKind,
|
||||
content_origin: LayerPoint,
|
||||
color: ColorF,
|
||||
) -> RenderTask {
|
||||
let clear_mode = match target_kind {
|
||||
RenderTargetKind::Color => ClearMode::Transparent,
|
||||
@ -346,6 +351,8 @@ impl RenderTask {
|
||||
kind: RenderTaskKind::Picture(PictureTask {
|
||||
prim_index,
|
||||
target_kind,
|
||||
content_origin,
|
||||
color,
|
||||
}),
|
||||
clear_mode,
|
||||
}
|
||||
@ -460,12 +467,13 @@ impl RenderTask {
|
||||
// +---- This is stored as the input task to the primitive shader.
|
||||
//
|
||||
pub fn new_blur(
|
||||
blur_radius: DeviceIntLength,
|
||||
blur_std_deviation: f32,
|
||||
src_task_id: RenderTaskId,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
target_kind: RenderTargetKind,
|
||||
regions: &[LayerRect],
|
||||
clear_mode: ClearMode,
|
||||
color: ColorF,
|
||||
) -> RenderTask {
|
||||
let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size();
|
||||
|
||||
@ -474,9 +482,10 @@ impl RenderTask {
|
||||
children: vec![src_task_id],
|
||||
location: RenderTaskLocation::Dynamic(None, blur_target_size),
|
||||
kind: RenderTaskKind::VerticalBlur(BlurTask {
|
||||
blur_radius,
|
||||
blur_std_deviation,
|
||||
target_kind,
|
||||
regions: regions.to_vec(),
|
||||
color,
|
||||
}),
|
||||
clear_mode,
|
||||
};
|
||||
@ -488,9 +497,10 @@ impl RenderTask {
|
||||
children: vec![blur_task_v_id],
|
||||
location: RenderTaskLocation::Dynamic(None, blur_target_size),
|
||||
kind: RenderTaskKind::HorizontalBlur(BlurTask {
|
||||
blur_radius,
|
||||
blur_std_deviation,
|
||||
target_kind,
|
||||
regions: regions.to_vec(),
|
||||
color,
|
||||
}),
|
||||
clear_mode,
|
||||
};
|
||||
@ -554,7 +564,7 @@ impl RenderTask {
|
||||
],
|
||||
}
|
||||
}
|
||||
RenderTaskKind::Picture(..) => {
|
||||
RenderTaskKind::Picture(ref task) => {
|
||||
let (target_rect, target_index) = self.get_target_rect();
|
||||
RenderTaskData {
|
||||
data: [
|
||||
@ -563,13 +573,13 @@ impl RenderTask {
|
||||
target_rect.size.width as f32,
|
||||
target_rect.size.height as f32,
|
||||
target_index.0 as f32,
|
||||
task.content_origin.x,
|
||||
task.content_origin.y,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
task.color.r,
|
||||
task.color.g,
|
||||
task.color.b,
|
||||
task.color.a,
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -602,13 +612,13 @@ impl RenderTask {
|
||||
target_rect.size.width as f32,
|
||||
target_rect.size.height as f32,
|
||||
target_index.0 as f32,
|
||||
task_info.blur_radius.0 as f32,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
task_info.blur_std_deviation,
|
||||
0.0,
|
||||
0.0,
|
||||
task_info.color.r,
|
||||
task_info.color.g,
|
||||
task_info.color.b,
|
||||
task_info.color.a,
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ use device::{FileWatcherHandler, GpuTimer, ShaderError, TextureFilter, TextureTa
|
||||
use euclid::{rect, Transform3D};
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
use gleam::gl;
|
||||
use glyph_rasterizer::GlyphFormat;
|
||||
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
|
||||
use gpu_types::PrimitiveInstance;
|
||||
use internal_types::{BatchTextures, SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
|
||||
@ -61,7 +62,7 @@ use std::thread;
|
||||
use texture_cache::TextureCache;
|
||||
use thread_profiler::{register_thread_with_profiler, write_profile};
|
||||
use tiling::{AlphaRenderTarget, ColorRenderTarget, RenderTargetKind};
|
||||
use tiling::{BatchKey, BatchKind, Frame, RenderTarget, TransformBatchKind};
|
||||
use tiling::{BatchKey, BatchKind, BrushBatchKind, Frame, RenderTarget, TransformBatchKind};
|
||||
use time::precise_time_ns;
|
||||
use util::TransformedRectKind;
|
||||
|
||||
@ -71,6 +72,10 @@ const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag {
|
||||
label: "B_Mask",
|
||||
color: debug_colors::BLACK,
|
||||
};
|
||||
const GPU_TAG_BRUSH_IMAGE: GpuProfileTag = GpuProfileTag {
|
||||
label: "B_Image",
|
||||
color: debug_colors::SILVER,
|
||||
};
|
||||
const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
|
||||
label: "C_Clip",
|
||||
color: debug_colors::PURPLE,
|
||||
@ -147,10 +152,6 @@ const GPU_TAG_PRIM_BORDER_EDGE: GpuProfileTag = GpuProfileTag {
|
||||
label: "BorderEdge",
|
||||
color: debug_colors::LAVENDER,
|
||||
};
|
||||
const GPU_TAG_PRIM_CACHE_IMAGE: GpuProfileTag = GpuProfileTag {
|
||||
label: "CacheImage",
|
||||
color: debug_colors::SILVER,
|
||||
};
|
||||
const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag {
|
||||
label: "Blur",
|
||||
color: debug_colors::VIOLET,
|
||||
@ -177,9 +178,14 @@ impl BatchKind {
|
||||
BatchKind::HardwareComposite => "HardwareComposite",
|
||||
BatchKind::SplitComposite => "SplitComposite",
|
||||
BatchKind::Blend => "Blend",
|
||||
BatchKind::Brush(kind) => {
|
||||
match kind {
|
||||
BrushBatchKind::Image(..) => "Brush (Image)",
|
||||
}
|
||||
}
|
||||
BatchKind::Transformable(_, kind) => match kind {
|
||||
TransformBatchKind::Rectangle(..) => "Rectangle",
|
||||
TransformBatchKind::TextRun => "TextRun",
|
||||
TransformBatchKind::TextRun(..) => "TextRun",
|
||||
TransformBatchKind::Image(image_buffer_kind, ..) => match image_buffer_kind {
|
||||
ImageBufferKind::Texture2D => "Image (2D)",
|
||||
ImageBufferKind::TextureRect => "Image (Rect)",
|
||||
@ -190,7 +196,6 @@ impl BatchKind {
|
||||
TransformBatchKind::AlignedGradient => "AlignedGradient",
|
||||
TransformBatchKind::AngleGradient => "AngleGradient",
|
||||
TransformBatchKind::RadialGradient => "RadialGradient",
|
||||
TransformBatchKind::CacheImage(..) => "CacheImage",
|
||||
TransformBatchKind::BorderCorner => "BorderCorner",
|
||||
TransformBatchKind::BorderEdge => "BorderEdge",
|
||||
TransformBatchKind::Line => "Line",
|
||||
@ -218,6 +223,7 @@ enum TextShaderMode {
|
||||
Alpha = 0,
|
||||
SubpixelPass0 = 1,
|
||||
SubpixelPass1 = 2,
|
||||
ColorBitmap = 3,
|
||||
}
|
||||
|
||||
impl Into<ShaderMode> for TextShaderMode {
|
||||
@ -226,6 +232,18 @@ impl Into<ShaderMode> for TextShaderMode {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GlyphFormat> for TextShaderMode {
|
||||
fn from(format: GlyphFormat) -> TextShaderMode {
|
||||
match format {
|
||||
GlyphFormat::Mono | GlyphFormat::Alpha => TextShaderMode::Alpha,
|
||||
GlyphFormat::Subpixel => {
|
||||
panic!("Subpixel glyph format must be handled separately.");
|
||||
}
|
||||
GlyphFormat::ColorBitmap => TextShaderMode::ColorBitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum TextureSampler {
|
||||
Color0,
|
||||
@ -835,6 +853,7 @@ impl VertexDataTexture {
|
||||
|
||||
const TRANSFORM_FEATURE: &str = "TRANSFORM";
|
||||
const CLIP_FEATURE: &str = "CLIP";
|
||||
const ALPHA_FEATURE: &str = "ALPHA_PASS";
|
||||
|
||||
enum ShaderKind {
|
||||
Primitive,
|
||||
@ -929,6 +948,77 @@ struct PrimitiveShader {
|
||||
transform: LazilyCompiledShader,
|
||||
}
|
||||
|
||||
// A brush shader supports two modes:
|
||||
// opaque:
|
||||
// Used for completely opaque primitives,
|
||||
// or inside segments of partially
|
||||
// opaque primitives. Assumes no need
|
||||
// for clip masks, AA etc.
|
||||
// alpha:
|
||||
// Used for brush primitives in the alpha
|
||||
// pass. Assumes that AA should be applied
|
||||
// along the primitive edge, and also that
|
||||
// clip mask is present.
|
||||
struct BrushShader {
|
||||
opaque: LazilyCompiledShader,
|
||||
alpha: LazilyCompiledShader,
|
||||
}
|
||||
|
||||
impl BrushShader {
|
||||
fn new(
|
||||
name: &'static str,
|
||||
device: &mut Device,
|
||||
features: &[&'static str],
|
||||
precache: bool,
|
||||
) -> Result<BrushShader, ShaderError> {
|
||||
let opaque = try!{
|
||||
LazilyCompiledShader::new(ShaderKind::Brush,
|
||||
name,
|
||||
features,
|
||||
device,
|
||||
precache)
|
||||
};
|
||||
|
||||
let mut alpha_features = features.to_vec();
|
||||
alpha_features.push(ALPHA_FEATURE);
|
||||
|
||||
let alpha = try!{
|
||||
LazilyCompiledShader::new(ShaderKind::Brush,
|
||||
name,
|
||||
&alpha_features,
|
||||
device,
|
||||
precache)
|
||||
};
|
||||
|
||||
Ok(BrushShader { opaque, alpha })
|
||||
}
|
||||
|
||||
fn bind<M>(
|
||||
&mut self,
|
||||
device: &mut Device,
|
||||
blend_mode: BlendMode,
|
||||
projection: &Transform3D<f32>,
|
||||
mode: M,
|
||||
renderer_errors: &mut Vec<RendererError>,
|
||||
) where M: Into<ShaderMode> {
|
||||
match blend_mode {
|
||||
BlendMode::None => {
|
||||
self.opaque.bind(device, projection, mode, renderer_errors)
|
||||
}
|
||||
BlendMode::Alpha |
|
||||
BlendMode::PremultipliedAlpha |
|
||||
BlendMode::Subpixel => {
|
||||
self.alpha.bind(device, projection, mode, renderer_errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deinit(self, device: &mut Device) {
|
||||
self.opaque.deinit(device);
|
||||
self.alpha.deinit(device);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileWatcher {
|
||||
notifier: Box<RenderNotifier>,
|
||||
result_tx: Sender<ResultMsg>,
|
||||
@ -1096,7 +1186,11 @@ pub struct Renderer {
|
||||
cs_line: LazilyCompiledShader,
|
||||
cs_blur_a8: LazilyCompiledShader,
|
||||
cs_blur_rgba8: LazilyCompiledShader,
|
||||
|
||||
// Brush shaders
|
||||
brush_mask: LazilyCompiledShader,
|
||||
brush_image_rgba8: BrushShader,
|
||||
brush_image_a8: BrushShader,
|
||||
|
||||
/// These are "cache clip shaders". These shaders are used to
|
||||
/// draw clip instances into the cached clip mask. The results
|
||||
@ -1122,8 +1216,6 @@ pub struct Renderer {
|
||||
ps_gradient: PrimitiveShader,
|
||||
ps_angle_gradient: PrimitiveShader,
|
||||
ps_radial_gradient: PrimitiveShader,
|
||||
ps_cache_image_rgba8: PrimitiveShader,
|
||||
ps_cache_image_a8: PrimitiveShader,
|
||||
ps_line: PrimitiveShader,
|
||||
|
||||
ps_blend: LazilyCompiledShader,
|
||||
@ -1296,10 +1388,24 @@ impl Renderer {
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let brush_image_a8 = try!{
|
||||
BrushShader::new("brush_image",
|
||||
&mut device,
|
||||
&["ALPHA_TARGET"],
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let brush_image_rgba8 = try!{
|
||||
BrushShader::new("brush_image",
|
||||
&mut device,
|
||||
&["COLOR_TARGET"],
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let cs_blur_a8 = try!{
|
||||
LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur),
|
||||
"cs_blur",
|
||||
&["ALPHA"],
|
||||
&["ALPHA_TARGET"],
|
||||
&mut device,
|
||||
options.precache_shaders)
|
||||
};
|
||||
@ -1307,7 +1413,7 @@ impl Renderer {
|
||||
let cs_blur_rgba8 = try!{
|
||||
LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur),
|
||||
"cs_blur",
|
||||
&["COLOR"],
|
||||
&["COLOR_TARGET"],
|
||||
&mut device,
|
||||
options.precache_shaders)
|
||||
};
|
||||
@ -1481,20 +1587,6 @@ impl Renderer {
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let ps_cache_image_a8 = try!{
|
||||
PrimitiveShader::new("ps_cache_image",
|
||||
&mut device,
|
||||
&["ALPHA"],
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let ps_cache_image_rgba8 = try!{
|
||||
PrimitiveShader::new("ps_cache_image",
|
||||
&mut device,
|
||||
&["COLOR"],
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let ps_blend = try!{
|
||||
LazilyCompiledShader::new(ShaderKind::Primitive,
|
||||
"ps_blend",
|
||||
@ -1712,6 +1804,8 @@ impl Renderer {
|
||||
cs_blur_a8,
|
||||
cs_blur_rgba8,
|
||||
brush_mask,
|
||||
brush_image_rgba8,
|
||||
brush_image_a8,
|
||||
cs_clip_rectangle,
|
||||
cs_clip_border,
|
||||
cs_clip_image,
|
||||
@ -1725,8 +1819,6 @@ impl Renderer {
|
||||
ps_gradient,
|
||||
ps_angle_gradient,
|
||||
ps_radial_gradient,
|
||||
ps_cache_image_rgba8,
|
||||
ps_cache_image_a8,
|
||||
ps_blend,
|
||||
ps_hw_composite,
|
||||
ps_split_composite,
|
||||
@ -2344,6 +2436,24 @@ impl Renderer {
|
||||
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
|
||||
GPU_TAG_PRIM_BLEND
|
||||
}
|
||||
BatchKind::Brush(brush_kind) => {
|
||||
match brush_kind {
|
||||
BrushBatchKind::Image(target_kind) => {
|
||||
let shader = match target_kind {
|
||||
RenderTargetKind::Alpha => &mut self.brush_image_a8,
|
||||
RenderTargetKind::Color => &mut self.brush_image_rgba8,
|
||||
};
|
||||
shader.bind(
|
||||
&mut self.device,
|
||||
key.blend_mode,
|
||||
projection,
|
||||
0,
|
||||
&mut self.renderer_errors,
|
||||
);
|
||||
GPU_TAG_BRUSH_IMAGE
|
||||
}
|
||||
}
|
||||
}
|
||||
BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
|
||||
TransformBatchKind::Rectangle(needs_clipping) => {
|
||||
debug_assert!(
|
||||
@ -2384,7 +2494,7 @@ impl Renderer {
|
||||
);
|
||||
GPU_TAG_PRIM_LINE
|
||||
}
|
||||
TransformBatchKind::TextRun => {
|
||||
TransformBatchKind::TextRun(..) => {
|
||||
unreachable!("bug: text batches are special cased");
|
||||
}
|
||||
TransformBatchKind::Image(image_buffer_kind) => {
|
||||
@ -2465,29 +2575,6 @@ impl Renderer {
|
||||
);
|
||||
GPU_TAG_PRIM_RADIAL_GRADIENT
|
||||
}
|
||||
TransformBatchKind::CacheImage(target_kind) => {
|
||||
match target_kind {
|
||||
RenderTargetKind::Alpha => {
|
||||
self.ps_cache_image_a8.bind(
|
||||
&mut self.device,
|
||||
transform_kind,
|
||||
projection,
|
||||
0,
|
||||
&mut self.renderer_errors,
|
||||
);
|
||||
}
|
||||
RenderTargetKind::Color => {
|
||||
self.ps_cache_image_rgba8.bind(
|
||||
&mut self.device,
|
||||
transform_kind,
|
||||
projection,
|
||||
0,
|
||||
&mut self.renderer_errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
GPU_TAG_PRIM_CACHE_IMAGE
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -2723,7 +2810,7 @@ impl Renderer {
|
||||
}
|
||||
|
||||
match batch.key.kind {
|
||||
BatchKind::Transformable(transform_kind, TransformBatchKind::TextRun) => {
|
||||
BatchKind::Transformable(transform_kind, TransformBatchKind::TextRun(glyph_format)) => {
|
||||
// Text run batches are handled by this special case branch.
|
||||
// In the case of subpixel text, we draw it as a two pass
|
||||
// effect, to ensure we can apply clip masks correctly.
|
||||
@ -2743,7 +2830,7 @@ impl Renderer {
|
||||
&mut self.device,
|
||||
transform_kind,
|
||||
projection,
|
||||
TextShaderMode::Alpha,
|
||||
TextShaderMode::from(glyph_format),
|
||||
&mut self.renderer_errors,
|
||||
);
|
||||
|
||||
@ -3353,21 +3440,22 @@ impl Renderer {
|
||||
let mut spacing = 16;
|
||||
let mut size = 512;
|
||||
let fb_width = framebuffer_size.width as i32;
|
||||
let num_textures = self.color_render_targets
|
||||
let num_layers: i32 = self.color_render_targets
|
||||
.iter()
|
||||
.chain(self.alpha_render_targets.iter())
|
||||
.count() as i32;
|
||||
.map(|texture| texture.get_render_target_layer_count() as i32)
|
||||
.sum();
|
||||
|
||||
if num_textures * (size + spacing) > fb_width {
|
||||
let factor = fb_width as f32 / (num_textures * (size + spacing)) as f32;
|
||||
if num_layers * (size + spacing) > fb_width {
|
||||
let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32;
|
||||
size = (size as f32 * factor) as i32;
|
||||
spacing = (spacing as f32 * factor) as i32;
|
||||
}
|
||||
|
||||
for (i, texture) in self.color_render_targets
|
||||
let mut target_index = 0;
|
||||
for texture in self.color_render_targets
|
||||
.iter()
|
||||
.chain(self.alpha_render_targets.iter())
|
||||
.enumerate()
|
||||
{
|
||||
let dimensions = texture.get_dimensions();
|
||||
let src_rect = DeviceIntRect::new(DeviceIntPoint::zero(), dimensions.to_i32());
|
||||
@ -3376,11 +3464,12 @@ impl Renderer {
|
||||
for layer_index in 0 .. layer_count {
|
||||
self.device
|
||||
.bind_read_target(Some((texture, layer_index as i32)));
|
||||
let x = fb_width - (spacing + size) * (i as i32 + 1);
|
||||
let x = fb_width - (spacing + size) * (target_index + 1);
|
||||
let y = spacing;
|
||||
|
||||
let dest_rect = rect(x, y, size, size);
|
||||
self.device.blit_render_target(src_rect, dest_rect);
|
||||
target_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3495,6 +3584,8 @@ impl Renderer {
|
||||
self.cs_blur_a8.deinit(&mut self.device);
|
||||
self.cs_blur_rgba8.deinit(&mut self.device);
|
||||
self.brush_mask.deinit(&mut self.device);
|
||||
self.brush_image_rgba8.deinit(&mut self.device);
|
||||
self.brush_image_a8.deinit(&mut self.device);
|
||||
self.cs_clip_rectangle.deinit(&mut self.device);
|
||||
self.cs_clip_image.deinit(&mut self.device);
|
||||
self.cs_clip_border.deinit(&mut self.device);
|
||||
@ -3519,8 +3610,6 @@ impl Renderer {
|
||||
self.ps_gradient.deinit(&mut self.device);
|
||||
self.ps_angle_gradient.deinit(&mut self.device);
|
||||
self.ps_radial_gradient.deinit(&mut self.device);
|
||||
self.ps_cache_image_rgba8.deinit(&mut self.device);
|
||||
self.ps_cache_image_a8.deinit(&mut self.device);
|
||||
self.ps_line.deinit(&mut self.device);
|
||||
self.ps_blend.deinit(&mut self.device);
|
||||
self.ps_hw_composite.deinit(&mut self.device);
|
||||
|
@ -16,7 +16,7 @@ use app_units::Au;
|
||||
use device::TextureFilter;
|
||||
use frame::FrameId;
|
||||
use glyph_cache::GlyphCache;
|
||||
use glyph_rasterizer::{GlyphRasterizer, GlyphRequest};
|
||||
use glyph_rasterizer::{GlyphFormat, GlyphRasterizer, GlyphRequest};
|
||||
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
|
||||
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
|
||||
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
|
||||
@ -26,7 +26,7 @@ use std::cmp;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use texture_cache::{TextureCache, TextureCacheHandle};
|
||||
|
||||
const DEFAULT_TILE_SIZE: TileSize = 512;
|
||||
@ -73,7 +73,7 @@ struct ImageResource {
|
||||
dirty_rect: Option<DeviceUintRect>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImageTiling {
|
||||
pub image_size: DeviceUintSize,
|
||||
pub tile_size: TileSize,
|
||||
@ -189,9 +189,12 @@ impl Into<BlobImageRequest> for ImageRequest {
|
||||
}
|
||||
}
|
||||
|
||||
type ImageCache = ResourceClassCache<ImageRequest, CachedImageInfo>;
|
||||
pub type FontInstanceMap = Arc<RwLock<FastHashMap<FontInstanceKey, FontInstance>>>;
|
||||
|
||||
struct Resources {
|
||||
font_templates: FastHashMap<FontKey, FontTemplate>,
|
||||
font_instances: FastHashMap<FontInstanceKey, FontInstance>,
|
||||
font_instances: FontInstanceMap,
|
||||
image_templates: ImageTemplates,
|
||||
}
|
||||
|
||||
@ -208,7 +211,7 @@ impl BlobImageResources for Resources {
|
||||
|
||||
pub struct ResourceCache {
|
||||
cached_glyphs: GlyphCache,
|
||||
cached_images: ResourceClassCache<ImageRequest, CachedImageInfo>,
|
||||
cached_images: ImageCache,
|
||||
|
||||
resources: Resources,
|
||||
state: State,
|
||||
@ -239,7 +242,7 @@ impl ResourceCache {
|
||||
cached_images: ResourceClassCache::new(),
|
||||
resources: Resources {
|
||||
font_templates: FastHashMap::default(),
|
||||
font_instances: FastHashMap::default(),
|
||||
font_instances: Arc::new(RwLock::new(FastHashMap::default())),
|
||||
image_templates: ImageTemplates::new(),
|
||||
},
|
||||
cached_glyph_dimensions: FastHashMap::default(),
|
||||
@ -256,8 +259,7 @@ impl ResourceCache {
|
||||
self.texture_cache.max_texture_size()
|
||||
}
|
||||
|
||||
fn should_tile(&self, descriptor: &ImageDescriptor, data: &ImageData) -> bool {
|
||||
let limit = self.max_texture_size();
|
||||
fn should_tile(limit: u32, descriptor: &ImageDescriptor, data: &ImageData) -> bool {
|
||||
let size_check = descriptor.width > limit || descriptor.height > limit;
|
||||
match *data {
|
||||
ImageData::Raw(_) | ImageData::Blob(_) => size_check,
|
||||
@ -365,18 +367,24 @@ impl ResourceCache {
|
||||
if self.glyph_rasterizer.is_bitmap_font(&instance) {
|
||||
instance.render_mode = instance.render_mode.limit_by(FontRenderMode::Bitmap);
|
||||
}
|
||||
self.resources.font_instances.insert(instance_key, instance);
|
||||
self.resources.font_instances
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(instance_key, instance);
|
||||
}
|
||||
|
||||
pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
|
||||
self.resources.font_instances.remove(&instance_key);
|
||||
self.resources.font_instances
|
||||
.write()
|
||||
.unwrap()
|
||||
.remove(&instance_key);
|
||||
if let Some(ref mut r) = self.blob_image_renderer {
|
||||
r.delete_font_instance(instance_key);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<&FontInstance> {
|
||||
self.resources.font_instances.get(&instance_key)
|
||||
pub fn get_font_instances(&self) -> FontInstanceMap {
|
||||
self.resources.font_instances.clone()
|
||||
}
|
||||
|
||||
pub fn add_image_template(
|
||||
@ -386,7 +394,7 @@ impl ResourceCache {
|
||||
mut data: ImageData,
|
||||
mut tiling: Option<TileSize>,
|
||||
) {
|
||||
if tiling.is_none() && self.should_tile(&descriptor, &data) {
|
||||
if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) {
|
||||
// We aren't going to be able to upload a texture this big, so tile it, even
|
||||
// if tiling was not requested.
|
||||
tiling = Some(DEFAULT_TILE_SIZE);
|
||||
@ -418,40 +426,38 @@ impl ResourceCache {
|
||||
mut data: ImageData,
|
||||
dirty_rect: Option<DeviceUintRect>,
|
||||
) {
|
||||
let resource = if let Some(image) = self.resources.image_templates.get(image_key) {
|
||||
let next_epoch = Epoch(image.epoch.0 + 1);
|
||||
|
||||
let mut tiling = image.tiling;
|
||||
if tiling.is_none() && self.should_tile(&descriptor, &data) {
|
||||
tiling = Some(DEFAULT_TILE_SIZE);
|
||||
}
|
||||
|
||||
if let ImageData::Blob(ref mut blob) = data {
|
||||
self.blob_image_renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.update(image_key, mem::replace(blob, BlobImageData::new()), dirty_rect);
|
||||
}
|
||||
|
||||
ImageResource {
|
||||
descriptor,
|
||||
data,
|
||||
epoch: next_epoch,
|
||||
tiling,
|
||||
dirty_rect: match (dirty_rect, image.dirty_rect) {
|
||||
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
|
||||
(Some(rect), None) => Some(rect),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
panic!(
|
||||
"Attempt to update non-existant image (key {:?}).",
|
||||
let max_texture_size = self.max_texture_size();
|
||||
let image = match self.resources.image_templates.get_mut(image_key) {
|
||||
Some(res) => res,
|
||||
None => panic!(
|
||||
"Attempt to update non-existent image (key {:?}).",
|
||||
image_key
|
||||
);
|
||||
),
|
||||
};
|
||||
|
||||
self.resources.image_templates.insert(image_key, resource);
|
||||
let mut tiling = image.tiling;
|
||||
if tiling.is_none() && Self::should_tile(max_texture_size, &descriptor, &data) {
|
||||
tiling = Some(DEFAULT_TILE_SIZE);
|
||||
}
|
||||
|
||||
if let ImageData::Blob(ref mut blob) = data {
|
||||
self.blob_image_renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.update(image_key, mem::replace(blob, BlobImageData::new()), dirty_rect);
|
||||
}
|
||||
|
||||
*image = ImageResource {
|
||||
descriptor,
|
||||
data,
|
||||
epoch: Epoch(image.epoch.0 + 1),
|
||||
tiling,
|
||||
dirty_rect: match (dirty_rect, image.dirty_rect) {
|
||||
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
|
||||
(Some(rect), None) => Some(rect),
|
||||
(None, _) => None,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn delete_image_template(&mut self, image_key: ImageKey) {
|
||||
@ -484,98 +490,99 @@ impl ResourceCache {
|
||||
tile,
|
||||
};
|
||||
|
||||
match self.resources.image_templates.get(key) {
|
||||
Some(template) => {
|
||||
// Images that don't use the texture cache can early out.
|
||||
if !template.data.uses_texture_cache() {
|
||||
return;
|
||||
}
|
||||
|
||||
let side_size =
|
||||
template.tiling.map_or(cmp::max(template.descriptor.width, template.descriptor.height),
|
||||
|tile_size| tile_size as u32);
|
||||
if side_size > self.texture_cache.max_texture_size() {
|
||||
// The image or tiling size is too big for hardware texture size.
|
||||
warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
|
||||
template.descriptor.width, template.descriptor.height, template.tiling.unwrap_or(0));
|
||||
self.cached_images.insert(request, Err(ResourceClassCacheError::OverLimitSize));
|
||||
return;
|
||||
}
|
||||
|
||||
// If this image exists in the texture cache, *and* the epoch
|
||||
// in the cache matches that of the template, then it is
|
||||
// valid to use as-is.
|
||||
let (entry, needs_update) = match self.cached_images.entry(request) {
|
||||
Occupied(entry) => {
|
||||
let needs_update = entry.get().as_ref().unwrap().epoch != template.epoch;
|
||||
(entry.into_mut(), needs_update)
|
||||
}
|
||||
Vacant(entry) => (
|
||||
entry.insert(Ok(
|
||||
CachedImageInfo {
|
||||
epoch: template.epoch,
|
||||
texture_cache_handle: TextureCacheHandle::new(),
|
||||
}
|
||||
)),
|
||||
true,
|
||||
),
|
||||
};
|
||||
|
||||
let needs_upload = self.texture_cache
|
||||
.request(&mut entry.as_mut().unwrap().texture_cache_handle, gpu_cache);
|
||||
|
||||
if !needs_upload && !needs_update {
|
||||
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) {
|
||||
if template.data.is_blob() {
|
||||
if let Some(ref mut renderer) = self.blob_image_renderer {
|
||||
let (offset, w, h) = match template.tiling {
|
||||
Some(tile_size) => {
|
||||
let tile_offset = request.tile.unwrap();
|
||||
let (w, h) = compute_tile_size(
|
||||
&template.descriptor,
|
||||
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,
|
||||
);
|
||||
|
||||
(offset, w, h)
|
||||
}
|
||||
None => (
|
||||
DevicePoint::zero(),
|
||||
template.descriptor.width,
|
||||
template.descriptor.height,
|
||||
),
|
||||
};
|
||||
|
||||
renderer.request(
|
||||
&self.resources,
|
||||
request.into(),
|
||||
&BlobImageDescriptor {
|
||||
width: w,
|
||||
height: h,
|
||||
offset,
|
||||
format: template.descriptor.format,
|
||||
},
|
||||
template.dirty_rect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let template = match self.resources.image_templates.get(key) {
|
||||
Some(template) => template,
|
||||
None => {
|
||||
warn!(
|
||||
"ERROR: Trying to render deleted / non-existent key {:?}",
|
||||
key
|
||||
);
|
||||
return
|
||||
}
|
||||
};
|
||||
|
||||
// Images that don't use the texture cache can early out.
|
||||
if !template.data.uses_texture_cache() {
|
||||
return;
|
||||
}
|
||||
|
||||
let side_size =
|
||||
template.tiling.map_or(cmp::max(template.descriptor.width, template.descriptor.height),
|
||||
|tile_size| tile_size as u32);
|
||||
if side_size > self.texture_cache.max_texture_size() {
|
||||
// The image or tiling size is too big for hardware texture size.
|
||||
warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
|
||||
template.descriptor.width, template.descriptor.height, template.tiling.unwrap_or(0));
|
||||
self.cached_images.insert(request, Err(ResourceClassCacheError::OverLimitSize));
|
||||
return;
|
||||
}
|
||||
|
||||
// If this image exists in the texture cache, *and* the epoch
|
||||
// in the cache matches that of the template, then it is
|
||||
// valid to use as-is.
|
||||
let (entry, needs_update) = match self.cached_images.entry(request) {
|
||||
Occupied(entry) => {
|
||||
let needs_update = entry.get().as_ref().unwrap().epoch != template.epoch;
|
||||
(entry.into_mut(), needs_update)
|
||||
}
|
||||
Vacant(entry) => (
|
||||
entry.insert(Ok(
|
||||
CachedImageInfo {
|
||||
epoch: template.epoch,
|
||||
texture_cache_handle: TextureCacheHandle::new(),
|
||||
}
|
||||
)),
|
||||
true,
|
||||
),
|
||||
};
|
||||
|
||||
let needs_upload = self.texture_cache
|
||||
.request(&mut entry.as_mut().unwrap().texture_cache_handle, gpu_cache);
|
||||
|
||||
if !needs_upload && !needs_update {
|
||||
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) {
|
||||
if template.data.is_blob() {
|
||||
if let Some(ref mut renderer) = self.blob_image_renderer {
|
||||
let (offset, w, h) = match template.tiling {
|
||||
Some(tile_size) => {
|
||||
let tile_offset = request.tile.unwrap();
|
||||
let (w, h) = compute_tile_size(
|
||||
&template.descriptor,
|
||||
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,
|
||||
);
|
||||
|
||||
(offset, w, h)
|
||||
}
|
||||
None => (
|
||||
DevicePoint::zero(),
|
||||
template.descriptor.width,
|
||||
template.descriptor.height,
|
||||
),
|
||||
};
|
||||
|
||||
renderer.request(
|
||||
&self.resources,
|
||||
request.into(),
|
||||
&BlobImageDescriptor {
|
||||
width: w,
|
||||
height: h,
|
||||
offset,
|
||||
format: template.descriptor.format,
|
||||
},
|
||||
template.dirty_rect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -610,7 +617,7 @@ impl ResourceCache {
|
||||
gpu_cache: &GpuCache,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(SourceTexture, &[GlyphFetchResult]),
|
||||
F: FnMut(SourceTexture, GlyphFormat, &[GlyphFetchResult]),
|
||||
{
|
||||
debug_assert_eq!(self.state, State::QueryResources);
|
||||
|
||||
@ -618,17 +625,20 @@ impl ResourceCache {
|
||||
let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font);
|
||||
|
||||
let mut current_texture_id = SourceTexture::Invalid;
|
||||
let mut current_glyph_format = GlyphFormat::Subpixel;
|
||||
debug_assert!(fetch_buffer.is_empty());
|
||||
|
||||
for (loop_index, key) in glyph_keys.iter().enumerate() {
|
||||
if let Ok(Some(ref glyph)) = *glyph_key_cache.get(key) {
|
||||
let cache_item = self.texture_cache.get(&glyph.texture_cache_handle);
|
||||
if current_texture_id != cache_item.texture_id {
|
||||
if current_texture_id != cache_item.texture_id ||
|
||||
current_glyph_format != glyph.format {
|
||||
if !fetch_buffer.is_empty() {
|
||||
f(current_texture_id, fetch_buffer);
|
||||
f(current_texture_id, current_glyph_format, fetch_buffer);
|
||||
fetch_buffer.clear();
|
||||
}
|
||||
current_texture_id = cache_item.texture_id;
|
||||
current_glyph_format = glyph.format;
|
||||
}
|
||||
fetch_buffer.push(GlyphFetchResult {
|
||||
index_in_text_run: loop_index as i32,
|
||||
@ -638,7 +648,7 @@ impl ResourceCache {
|
||||
}
|
||||
|
||||
if !fetch_buffer.is_empty() {
|
||||
f(current_texture_id, fetch_buffer);
|
||||
f(current_texture_id, current_glyph_format, fetch_buffer);
|
||||
fetch_buffer.clear();
|
||||
}
|
||||
}
|
||||
@ -879,27 +889,14 @@ impl ResourceCache {
|
||||
}
|
||||
|
||||
pub fn clear_namespace(&mut self, namespace: IdNamespace) {
|
||||
//TODO: use `retain` when we are on Rust-1.18
|
||||
let image_keys: Vec<_> = self.resources
|
||||
self.resources
|
||||
.image_templates
|
||||
.images
|
||||
.keys()
|
||||
.filter(|&key| key.0 == namespace)
|
||||
.cloned()
|
||||
.collect();
|
||||
for key in &image_keys {
|
||||
self.resources.image_templates.images.remove(key);
|
||||
}
|
||||
.retain(|key, _| key.0 != namespace);
|
||||
|
||||
let font_keys: Vec<_> = self.resources
|
||||
self.resources
|
||||
.font_templates
|
||||
.keys()
|
||||
.filter(|&key| key.0 == namespace)
|
||||
.cloned()
|
||||
.collect();
|
||||
for key in &font_keys {
|
||||
self.resources.font_templates.remove(key);
|
||||
}
|
||||
.retain(|key, _| key.0 != namespace);
|
||||
|
||||
self.cached_images
|
||||
.clear_keys(|request| request.key.0 == namespace);
|
||||
|
@ -11,9 +11,11 @@ use border::{BorderCornerInstance, BorderCornerSide};
|
||||
use clip::{ClipSource, ClipStore};
|
||||
use clip_scroll_tree::CoordinateSystemId;
|
||||
use device::Texture;
|
||||
use glyph_rasterizer::GlyphFormat;
|
||||
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
|
||||
use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipMaskInstance};
|
||||
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
|
||||
use gpu_types::{BRUSH_FLAG_USES_PICTURE};
|
||||
use internal_types::{FastHashMap, SourceTexture};
|
||||
use internal_types::BatchTextures;
|
||||
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
|
||||
@ -62,6 +64,8 @@ impl AlphaBatchHelpers for PrimitiveStore {
|
||||
FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
|
||||
}
|
||||
}
|
||||
PrimitiveKind::Rectangle |
|
||||
PrimitiveKind::Border |
|
||||
PrimitiveKind::Image |
|
||||
PrimitiveKind::AlignedGradient |
|
||||
PrimitiveKind::AngleGradient |
|
||||
@ -71,7 +75,9 @@ impl AlphaBatchHelpers for PrimitiveStore {
|
||||
} else {
|
||||
BlendMode::None
|
||||
},
|
||||
_ => if needs_blending {
|
||||
PrimitiveKind::YuvImage |
|
||||
PrimitiveKind::Line |
|
||||
PrimitiveKind::Brush => if needs_blending {
|
||||
BlendMode::Alpha
|
||||
} else {
|
||||
BlendMode::None
|
||||
@ -137,7 +143,7 @@ impl AlphaBatchList {
|
||||
// the input to the next composite. Perhaps we can
|
||||
// optimize this in the future.
|
||||
}
|
||||
BatchKind::Transformable(_, TransformBatchKind::TextRun) => {
|
||||
BatchKind::Transformable(_, TransformBatchKind::TextRun(_)) => {
|
||||
'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
|
||||
// Subpixel text is drawn in two passes. Because of this, we need
|
||||
// to check for overlaps with every batch (which is a bit different
|
||||
@ -544,7 +550,7 @@ impl AlphaRenderItem {
|
||||
&text_cpu.glyph_keys,
|
||||
glyph_fetch_buffer,
|
||||
gpu_cache,
|
||||
|texture_id, glyphs| {
|
||||
|texture_id, glyph_format, glyphs| {
|
||||
debug_assert_ne!(texture_id, SourceTexture::Invalid);
|
||||
|
||||
let textures = BatchTextures {
|
||||
@ -557,7 +563,7 @@ impl AlphaRenderItem {
|
||||
|
||||
let kind = BatchKind::Transformable(
|
||||
transform_kind,
|
||||
TransformBatchKind::TextRun,
|
||||
TransformBatchKind::TextRun(glyph_format),
|
||||
);
|
||||
|
||||
let key = BatchKey::new(kind, blend_mode, textures);
|
||||
@ -579,13 +585,22 @@ impl AlphaRenderItem {
|
||||
let cache_task_id = picture.render_task_id.expect("no render task!");
|
||||
let cache_task_address = render_tasks.get_task_address(cache_task_id);
|
||||
let textures = BatchTextures::render_target_cache();
|
||||
let kind = BatchKind::Transformable(
|
||||
transform_kind,
|
||||
TransformBatchKind::CacheImage(picture.target_kind()),
|
||||
let kind = BatchKind::Brush(
|
||||
BrushBatchKind::Image(picture.target_kind()),
|
||||
);
|
||||
let key = BatchKey::new(kind, blend_mode, textures);
|
||||
let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
|
||||
batch.push(base_instance.build(0, cache_task_address.0 as i32, 0));
|
||||
let instance = BrushInstance {
|
||||
picture_address: task_address,
|
||||
prim_address: prim_cache_address,
|
||||
layer_address: packed_layer_index.into(),
|
||||
clip_task_address,
|
||||
z,
|
||||
flags: 0,
|
||||
user_data0: cache_task_address.0 as i32,
|
||||
user_data1: 0,
|
||||
};
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
PrimitiveKind::AlignedGradient => {
|
||||
let gradient_cpu =
|
||||
@ -1158,8 +1173,6 @@ impl RenderTarget for ColorRenderTarget {
|
||||
}
|
||||
RenderTaskKind::Picture(ref task_info) => {
|
||||
let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index);
|
||||
let prim_address = prim_metadata.gpu_location.as_int(gpu_cache);
|
||||
|
||||
match prim_metadata.prim_kind {
|
||||
PrimitiveKind::Picture => {
|
||||
let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
|
||||
@ -1197,7 +1210,7 @@ impl RenderTarget for ColorRenderTarget {
|
||||
&text.glyph_keys,
|
||||
&mut self.glyph_fetch_buffer,
|
||||
gpu_cache,
|
||||
|texture_id, glyphs| {
|
||||
|texture_id, _glyph_format, glyphs| {
|
||||
let batch = text_run_cache_prims
|
||||
.entry(texture_id)
|
||||
.or_insert(Vec::new());
|
||||
@ -1206,7 +1219,7 @@ impl RenderTarget for ColorRenderTarget {
|
||||
batch.push(instance.build(
|
||||
glyph.index_in_text_run,
|
||||
glyph.uv_rect_address.as_int(),
|
||||
prim_address,
|
||||
0
|
||||
));
|
||||
}
|
||||
},
|
||||
@ -1214,7 +1227,7 @@ impl RenderTarget for ColorRenderTarget {
|
||||
}
|
||||
PrimitiveKind::Line => {
|
||||
self.line_cache_prims
|
||||
.push(instance.build(prim_address, 0, 0));
|
||||
.push(instance.build(0, 0, 0));
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Unexpected sub primitive type");
|
||||
@ -1334,7 +1347,21 @@ impl RenderTarget for AlphaRenderTarget {
|
||||
|
||||
match sub_metadata.prim_kind {
|
||||
PrimitiveKind::Brush => {
|
||||
let instance = BrushInstance::new(task_index, sub_prim_address);
|
||||
let instance = BrushInstance {
|
||||
picture_address: task_index,
|
||||
prim_address: sub_prim_address,
|
||||
// TODO(gw): In the future, when brush
|
||||
// primitives on picture backed
|
||||
// tasks support clip masks and
|
||||
// transform primitives, these
|
||||
// will need to be filled out!
|
||||
layer_address: PackedLayerIndex(0).into(),
|
||||
clip_task_address: RenderTaskAddress(0),
|
||||
z: 0,
|
||||
flags: BRUSH_FLAG_USES_PICTURE,
|
||||
user_data0: 0,
|
||||
user_data1: 0,
|
||||
};
|
||||
self.rect_cache_prims.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
_ => {
|
||||
@ -1533,18 +1560,22 @@ impl RenderPass {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TransformBatchKind {
|
||||
Rectangle(bool),
|
||||
TextRun,
|
||||
TextRun(GlyphFormat),
|
||||
Image(ImageBufferKind),
|
||||
YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
|
||||
AlignedGradient,
|
||||
AngleGradient,
|
||||
RadialGradient,
|
||||
CacheImage(RenderTargetKind),
|
||||
BorderCorner,
|
||||
BorderEdge,
|
||||
Line,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum BrushBatchKind {
|
||||
Image(RenderTargetKind)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum BatchKind {
|
||||
Composite {
|
||||
@ -1556,6 +1587,7 @@ pub enum BatchKind {
|
||||
SplitComposite,
|
||||
Blend,
|
||||
Transformable(TransformedRectKind, TransformBatchKind),
|
||||
Brush(BrushBatchKind),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -66,10 +66,6 @@ const SHADERS: &[Shader] = &[
|
||||
name: "ps_radial_gradient",
|
||||
features: PRIM_FEATURES,
|
||||
},
|
||||
Shader {
|
||||
name: "ps_cache_image",
|
||||
features: &["COLOR", "ALPHA"],
|
||||
},
|
||||
Shader {
|
||||
name: "ps_blend",
|
||||
features: PRIM_FEATURES,
|
||||
@ -107,6 +103,10 @@ const SHADERS: &[Shader] = &[
|
||||
name: "brush_mask",
|
||||
features: &[],
|
||||
},
|
||||
Shader {
|
||||
name: "brush_image",
|
||||
features: &["COLOR_TARGET", "ALPHA_TARGET"],
|
||||
},
|
||||
];
|
||||
|
||||
const VERSION_STRING: &str = "#version 300 es\n";
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "webrender_api"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/webrender"
|
||||
|
@ -148,11 +148,8 @@ pub struct RectangleDisplayItem {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct LineDisplayItem {
|
||||
pub baseline: f32, // LayerPixel
|
||||
pub start: f32,
|
||||
pub end: f32,
|
||||
pub orientation: LineOrientation, // toggles whether above values are interpreted as x/y values
|
||||
pub width: f32,
|
||||
pub wavy_line_thickness: f32,
|
||||
pub color: ColorF,
|
||||
pub style: LineStyle,
|
||||
}
|
||||
|
@ -491,6 +491,7 @@ impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
|
||||
struct UnsafeVecWriter(*mut u8);
|
||||
|
||||
impl Write for UnsafeVecWriter {
|
||||
#[inline(always)]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len());
|
||||
@ -498,16 +499,36 @@ impl Write for UnsafeVecWriter {
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len());
|
||||
self.0 = self.0.offset(buf.len() as isize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
struct SizeCounter(usize);
|
||||
|
||||
impl<'a> Write for SizeCounter {
|
||||
#[inline(always)]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0 += buf.len();
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.0 += buf.len();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
@ -773,21 +794,15 @@ impl DisplayListBuilder {
|
||||
pub fn push_line(
|
||||
&mut self,
|
||||
info: &LayoutPrimitiveInfo,
|
||||
baseline: f32,
|
||||
start: f32,
|
||||
end: f32,
|
||||
wavy_line_thickness: f32,
|
||||
orientation: LineOrientation,
|
||||
width: f32,
|
||||
color: ColorF,
|
||||
color: &ColorF,
|
||||
style: LineStyle,
|
||||
) {
|
||||
let item = SpecificDisplayItem::Line(LineDisplayItem {
|
||||
baseline,
|
||||
start,
|
||||
end,
|
||||
wavy_line_thickness,
|
||||
orientation,
|
||||
width,
|
||||
color,
|
||||
color: *color,
|
||||
style,
|
||||
});
|
||||
|
||||
|
@ -243,14 +243,14 @@ impl Default for FontInstancePlatformOptions {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct FontInstancePlatformOptions {
|
||||
pub unused: u32,
|
||||
pub font_smoothing: bool,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl Default for FontInstancePlatformOptions {
|
||||
fn default() -> FontInstancePlatformOptions {
|
||||
FontInstancePlatformOptions {
|
||||
unused: 0,
|
||||
font_smoothing: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ authors = ["The Mozilla Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
webrender_api = {path = "../webrender_api", version = "0.53.0"}
|
||||
webrender_api = {path = "../webrender_api", version = "0.53.1"}
|
||||
bincode = "0.8"
|
||||
rayon = "0.8"
|
||||
thread_profiler = "0.1.1"
|
||||
@ -15,5 +15,5 @@ gleam = "0.4"
|
||||
|
||||
[dependencies.webrender]
|
||||
path = "../webrender"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
default-features = false
|
||||
|
Loading…
x
Reference in New Issue
Block a user