Bug 1463416 - Update webrender to cset 63c71ca9bbe4dec0ebc9c9bc8ab65b06a6b40641. r=Gankro

MozReview-Commit-ID: 21gmlBlV8En

--HG--
extra : rebase_source : 89e3746c4254b9a20ca811be6419eb3901b4f319
This commit is contained in:
Kartikaya Gupta 2018-05-26 11:09:20 -04:00
parent 08f7efc25a
commit ca48941951
51 changed files with 1843 additions and 702 deletions

View File

@ -23,7 +23,7 @@ impl Example for App {
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,

View File

@ -34,7 +34,7 @@ impl Example for App {
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,

View File

@ -182,7 +182,7 @@ impl Example for App {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
txn: &mut Transaction,
_: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
@ -201,7 +201,7 @@ impl Example for App {
);
let image_mask_key = api.generate_image_key();
resources.add_image(
txn.add_image(
image_mask_key,
ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
ImageData::new(vec![0, 80, 180, 255]),

View File

@ -16,7 +16,7 @@ use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, ResourceUpdates};
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction};
// This example shows how to implement a very basic BlobImageRenderer that can only render
// a checkerboard pattern.
@ -224,13 +224,13 @@ impl Example for App {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
txn: &mut Transaction,
_framebuffer_size: api::DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let blob_img1 = api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img1,
api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
@ -238,7 +238,7 @@ impl Example for App {
);
let blob_img2 = api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img2,
api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),

View File

@ -71,7 +71,7 @@ pub trait Example {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
txn: &mut Transaction,
framebuffer_size: DeviceUintSize,
pipeline_id: PipelineId,
document_id: DocumentId,
@ -169,17 +169,16 @@ pub fn main_wrapper<E: Example>(
let pipeline_id = PipelineId(0, 0);
let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
example.render(
&api,
&mut builder,
&mut resources,
&mut txn,
framebuffer_size,
pipeline_id,
document_id,
);
let mut txn = Transaction::new();
txn.set_display_list(
epoch,
None,
@ -187,7 +186,6 @@ pub fn main_wrapper<E: Example>(
builder.finalize(),
true,
);
txn.update_resources(resources);
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
api.send_transaction(document_id, txn);
@ -251,12 +249,11 @@ pub fn main_wrapper<E: Example>(
if custom_event {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut resources = ResourceUpdates::new();
example.render(
&api,
&mut builder,
&mut resources,
&mut txn,
framebuffer_size,
pipeline_id,
document_id,
@ -268,7 +265,6 @@ pub fn main_wrapper<E: Example>(
builder.finalize(),
true,
);
txn.update_resources(resources);
txn.generate_frame();
}
api.send_transaction(document_id, txn);

View File

@ -87,7 +87,7 @@ impl Example for App {
&mut self,
api: &RenderApi,
base_builder: &mut DisplayListBuilder,
_: &mut ResourceUpdates,
_txn: &mut Transaction,
framebuffer_size: DeviceUintSize,
_: PipelineId,
_: DocumentId,

View File

@ -67,8 +67,8 @@ impl App {
) {
// Generate the external image key that will be used to render the output document to the root document.
self.external_image_key = Some(api.generate_image_key());
let mut resources = ResourceUpdates::new();
resources.add_image(
let mut txn = Transaction::new();
txn.add_image(
self.external_image_key.unwrap(),
ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false),
ImageData::External(ExternalImageData {
@ -112,10 +112,8 @@ impl App {
builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
builder.pop_stacking_context();
let mut txn = Transaction::new();
txn.set_root_pipeline(pipeline_id);
txn.enable_frame_output(document.pipeline_id, true);
txn.update_resources(resources);
txn.set_display_list(
Epoch(0),
Some(document.color),
@ -134,7 +132,7 @@ impl Example for App {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_txn: &mut Transaction,
framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,

View File

@ -23,7 +23,7 @@ impl Example for App {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
pipeline_id: PipelineId,
document_id: DocumentId,

View File

@ -23,13 +23,13 @@ impl Example for App {
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32);
resources.add_image(
txn.add_image(
self.image_key,
image_descriptor,
image_data,
@ -99,15 +99,14 @@ impl Example for App {
}
}
let mut updates = ResourceUpdates::new();
updates.update_image(
let mut txn = Transaction::new();
txn.update_image(
self.image_key,
ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false),
ImageData::new(image_data),
None,
);
let mut txn = Transaction::new();
txn.update_resources(updates);
txn.generate_frame();
api.send_transaction(document_id, txn);
}

View File

@ -102,17 +102,15 @@ impl Window {
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
let font_key = api.generate_font_key();
let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
resources.add_raw_font(font_key, font_bytes, 0);
txn.add_raw_font(font_key, font_bytes, 0);
let font_instance_key = api.generate_font_instance_key();
resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
txn.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
let mut txn = Transaction::new();
txn.update_resources(resources);
api.send_transaction(document_id, txn);
Window {

View File

@ -23,7 +23,7 @@ impl Example for App {
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,

View File

@ -83,7 +83,7 @@ impl Example for App {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
@ -110,7 +110,7 @@ impl Example for App {
let key1 = api.generate_image_key();
self.image_generator.generate_image(128);
resources.add_image(
txn.add_image(
key0,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
@ -118,7 +118,7 @@ impl Example for App {
);
self.image_generator.generate_image(128);
resources.add_image(
txn.add_image(
key1,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
@ -200,7 +200,7 @@ impl Example for App {
},
..
} => {
let mut updates = ResourceUpdates::new();
let mut txn = Transaction::new();
match key {
glutin::VirtualKeyCode::S => {
@ -214,7 +214,7 @@ impl Example for App {
self.image_generator.generate_image(size);
updates.add_image(
txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
@ -226,13 +226,13 @@ impl Example for App {
}
}
glutin::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
updates.delete_image(image_key);
txn.delete_image(image_key);
},
glutin::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
let size = 128;
self.image_generator.generate_image(size);
updates.update_image(
txn.update_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
@ -241,7 +241,7 @@ impl Example for App {
},
glutin::VirtualKeyCode::E => {
if let Some(image_key) = self.image_key.take() {
updates.delete_image(image_key);
txn.delete_image(image_key);
}
let size = 32;
@ -253,7 +253,7 @@ impl Example for App {
image_type: ExternalImageType::Buffer,
};
updates.add_image(
txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::External(image_data),
@ -264,14 +264,14 @@ impl Example for App {
}
glutin::VirtualKeyCode::R => {
if let Some(image_key) = self.image_key.take() {
updates.delete_image(image_key);
txn.delete_image(image_key);
}
let image_key = api.generate_image_key();
let size = 32;
self.image_generator.generate_image(size);
updates.add_image(
txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
@ -283,7 +283,7 @@ impl Example for App {
_ => {}
}
api.update_resources(updates);
api.update_resources(txn.resource_updates);
return true;
}
_ => {}

View File

@ -78,7 +78,7 @@ impl Example for App {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
txn: &mut Transaction,
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
@ -100,7 +100,7 @@ impl Example for App {
let yuv_chanel2 = api.generate_image_key();
let yuv_chanel2_1 = api.generate_image_key();
let yuv_chanel3 = api.generate_image_key();
resources.add_image(
txn.add_image(
yuv_chanel1,
ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
@ -112,7 +112,7 @@ impl Example for App {
}),
None,
);
resources.add_image(
txn.add_image(
yuv_chanel2,
ImageDescriptor::new(100, 100, ImageFormat::RG8, true, false),
ImageData::External(ExternalImageData {
@ -124,7 +124,7 @@ impl Example for App {
}),
None,
);
resources.add_image(
txn.add_image(
yuv_chanel2_1,
ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
@ -136,7 +136,7 @@ impl Example for App {
}),
None,
);
resources.add_image(
txn.add_image(
yuv_chanel3,
ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {

View File

@ -48,7 +48,7 @@ vec2 transform_point_snapped(
RectWithSize local_rect,
mat4 transform
) {
vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect);
vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect, vec2(0.5));
vec4 world_pos = transform * vec4(local_pos, 0.0, 1.0);
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;

View File

@ -67,7 +67,8 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
scroll_node.transform,
local_clip_rect,
RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
snap_positions
snap_positions,
vec2(0.5)
);
actual_pos -= snap_offsets;

View File

@ -0,0 +1,127 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include shared,prim_shared,ellipse,shared_border
flat varying vec4 vColor0;
flat varying vec4 vColor1;
flat varying vec4 vColorLine;
flat varying int vFeatures;
flat varying vec2 vClipCenter;
flat varying vec4 vClipRadii;
flat varying vec2 vClipSign;
varying vec2 vPos;
#define CLIP_RADII 1
#define MIX_COLOR 2
#ifdef WR_VERTEX_SHADER
in vec2 aTaskOrigin;
in vec4 aRect;
in vec4 aColor0;
in vec4 aColor1;
in int aFlags;
in vec2 aWidths;
in vec2 aRadii;
#define SEGMENT_TOP_LEFT 0
#define SEGMENT_TOP_RIGHT 1
#define SEGMENT_BOTTOM_RIGHT 2
#define SEGMENT_BOTTOM_LEFT 3
#define SEGMENT_LEFT 4
#define SEGMENT_TOP 5
#define SEGMENT_RIGHT 6
#define SEGMENT_BOTTOM 7
vec2 get_outer_corner_scale(int segment) {
vec2 p;
switch (segment) {
case SEGMENT_TOP_LEFT:
p = vec2(0.0, 0.0);
break;
case SEGMENT_TOP_RIGHT:
p = vec2(1.0, 0.0);
break;
case SEGMENT_BOTTOM_RIGHT:
p = vec2(1.0, 1.0);
break;
case SEGMENT_BOTTOM_LEFT:
p = vec2(0.0, 1.0);
break;
default:
// Should never get hit
p = vec2(0.0);
break;
}
return p;
}
void main(void) {
vec2 pos = aRect.xy + aRect.zw * aPosition.xy;
int segment = aFlags & 0xff;
int style = (aFlags >> 8) & 0xff;
vec2 outer_scale = get_outer_corner_scale(segment);
vec2 outer = aRect.xy + outer_scale * aRect.zw;
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
vColor0 = aColor0;
vColor1 = aColor1;
vPos = pos;
vFeatures = 0;
vClipSign = clip_sign;
vClipCenter = outer + clip_sign * aRadii;
vClipRadii = vec4(aRadii, aRadii - aWidths);
vColorLine = vec4(0.0);
switch (segment) {
case SEGMENT_TOP_LEFT:
case SEGMENT_TOP_RIGHT:
case SEGMENT_BOTTOM_RIGHT:
case SEGMENT_BOTTOM_LEFT:
vFeatures |= (CLIP_RADII | MIX_COLOR);
vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
break;
default:
break;
}
gl_Position = uTransform * vec4(aTaskOrigin + pos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
float aa_range = compute_aa_range(vPos);
float d = -1.0;
// Apply color mix
float mix_factor = 0.0;
if ((vFeatures & MIX_COLOR) != 0) {
float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
mix_factor = distance_aa(aa_range, -d_line);
}
// Apply clip radii
if ((vFeatures & CLIP_RADII) != 0) {
vec2 p = vPos - vClipCenter;
if (vClipSign.x * p.x < 0.0 && vClipSign.y * p.y < 0.0) {
float d_radii_a = distance_to_ellipse(p, vClipRadii.xy, aa_range);
float d_radii_b = distance_to_ellipse(p, vClipRadii.zw, aa_range);
float d_radii = max(d_radii_a, -d_radii_b);
d = max(d, d_radii);
}
}
float alpha = distance_aa(aa_range, d);
vec4 color = mix(vColor0, vColor1, mix_factor);
oFragColor = color * alpha;
}
#endif

View File

@ -10,6 +10,7 @@
#define SUBPX_DIR_NONE 0
#define SUBPX_DIR_HORIZONTAL 1
#define SUBPX_DIR_VERTICAL 2
#define SUBPX_DIR_MIXED 3
#define RASTER_LOCAL 0
#define RASTER_SCREEN 1
@ -68,8 +69,7 @@ struct Glyph {
};
Glyph fetch_glyph(int specific_prim_address,
int glyph_index,
int subpx_dir) {
int glyph_index) {
// Two glyphs are packed in each texel in the GPU cache.
int glyph_address = specific_prim_address +
VECS_PER_TEXT_RUN +
@ -80,24 +80,6 @@ Glyph fetch_glyph(int specific_prim_address,
// bug with equality comparisons on integers.
vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 != 0));
// In subpixel mode, the subpixel offset has already been
// accounted for while rasterizing the glyph.
switch (subpx_dir) {
case SUBPX_DIR_NONE:
break;
case SUBPX_DIR_HORIZONTAL:
// Glyphs positioned [-0.125, 0.125] get a
// subpx position of zero. So include that
// offset in the glyph position to ensure
// we round to the correct whole position.
glyph.x = floor(glyph.x + 0.125);
break;
case SUBPX_DIR_VERTICAL:
glyph.y = floor(glyph.y + 0.125);
break;
default: break;
}
return Glyph(glyph);
}
@ -231,7 +213,8 @@ VertexInfo write_vertex(RectWithSize instance_rect,
vec2 snap_offset = compute_snap_offset(
clamped_local_pos,
scroll_node.transform,
snap_rect
snap_rect,
vec2(0.5)
);
// Transform the current vertex to world space.

View File

@ -20,7 +20,14 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
float z,
ClipScrollNode scroll_node,
PictureTask task,
RectWithSize snap_rect) {
RectWithSize snap_rect,
vec2 snap_bias) {
#if defined(WR_FEATURE_GLYPH_TRANSFORM) || !defined(WR_FEATURE_TRANSFORM)
// Ensure the transform does not contain a subpixel translation to ensure
// that glyph snapping is stable for equivalent glyph subpixel positions.
scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5);
#endif
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
@ -34,15 +41,14 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos,
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// For transformed subpixels, we just need to align the glyph origin to a device pixel.
// Only check the scroll node transform's translation since the scales and axes match.
vec2 world_snap_p0 = snap_rect.p0 + scroll_node.transform[3].xy * uDevicePixelRatio;
final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0;
final_pos += floor(snap_rect.p0 + snap_bias) - snap_rect.p0;
#elif !defined(WR_FEATURE_TRANSFORM)
// Compute the snapping offset only if the scroll node transform is axis-aligned.
final_pos += compute_snap_offset(
clamped_local_pos,
scroll_node.transform,
snap_rect
snap_rect,
snap_bias
);
#endif
@ -71,9 +77,7 @@ void main(void) {
color_mode = uMode;
}
Glyph glyph = fetch_glyph(prim.specific_prim_address,
glyph_index,
subpx_dir);
Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
GlyphResource res = fetch_glyph_resource(resource_address);
#ifdef WR_FEATURE_GLYPH_TRANSFORM
@ -112,12 +116,38 @@ void main(void) {
local_pos = clamp_rect(local_pos, prim.local_clip_rect);
#endif
vec2 snap_bias;
// In subpixel mode, the subpixel offset has already been
// accounted for while rasterizing the glyph. However, we
// must still round with a subpixel bias rather than rounding
// to the nearest whole pixel, depending on subpixel direciton.
switch (subpx_dir) {
case SUBPX_DIR_NONE:
default:
snap_bias = vec2(0.5);
break;
case SUBPX_DIR_HORIZONTAL:
// Glyphs positioned [-0.125, 0.125] get a
// subpx position of zero. So include that
// offset in the glyph position to ensure
// we round to the correct whole position.
snap_bias = vec2(0.125, 0.5);
break;
case SUBPX_DIR_VERTICAL:
snap_bias = vec2(0.5, 0.125);
break;
case SUBPX_DIR_MIXED:
snap_bias = vec2(0.125);
break;
}
VertexInfo vi = write_text_vertex(local_pos,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
glyph_rect);
glyph_rect,
snap_bias);
#ifdef WR_FEATURE_GLYPH_TRANSFORM
vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size;

View File

@ -27,10 +27,11 @@ vec2 compute_snap_offset_impl(
mat4 transform,
RectWithSize snap_rect,
RectWithSize reference_rect,
vec4 snap_positions) {
vec4 snap_positions,
vec2 snap_bias) {
/// World offsets applied to the corners of the snap rectangle.
vec4 snap_offsets = floor(snap_positions + 0.5) - snap_positions;
vec4 snap_offsets = floor(snap_positions + snap_bias.xyxy) - snap_positions;
/// Compute the position of this vertex inside the snap rectangle.
vec2 normalized_snap_pos = (reference_pos - reference_rect.p0) / reference_rect.size;
@ -43,7 +44,8 @@ vec2 compute_snap_offset_impl(
// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
mat4 transform,
RectWithSize snap_rect) {
RectWithSize snap_rect,
vec2 snap_bias) {
vec4 snap_positions = compute_snap_positions(
transform,
snap_rect
@ -54,7 +56,8 @@ vec2 compute_snap_offset(vec2 local_pos,
transform,
snap_rect,
snap_rect,
snap_positions
snap_positions,
snap_bias
);
return snap_offsets;

View File

@ -4,7 +4,7 @@
use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering, LayoutRect};
use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{DeviceIntPoint, YuvColorSpace, YuvFormat};
use api::{LayoutToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore, ClipWorkItem};
@ -22,7 +22,7 @@ use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, CachedGradient, DeferredResolve};
use prim_store::{EdgeAaSegmentMask, ImageSource, PictureIndex, PrimitiveIndex, PrimitiveKind};
use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore, VisibleGradientTile};
use prim_store::CachedGradientIndex;
use prim_store::{BorderSource, CachedGradientIndex};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::{BLOCKS_PER_UV_RECT, ShaderColorMode};
@ -1153,6 +1153,7 @@ impl AlphaBatchBuilder {
ctx.device_pixel_scale,
Some(scroll_node.transform),
);
let subpx_dir = font.get_subpx_dir();
let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
let batch_list = &mut self.batch_list;
@ -1170,11 +1171,7 @@ impl AlphaBatchBuilder {
glyph_format = glyph_format.ignore_color();
}
let subpx_dir = match glyph_format {
GlyphFormat::Bitmap |
GlyphFormat::ColorBitmap => SubpixelDirection::None,
_ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
};
let subpx_dir = subpx_dir.limit_by(glyph_format);
let textures = BatchTextures {
colors: [
@ -1524,13 +1521,25 @@ impl BrushPrimitive {
))
}
}
BrushKind::Border { request, .. } => {
let cache_item = resolve_image(
request,
resource_cache,
gpu_cache,
deferred_resolves,
);
BrushKind::Border { ref source, .. } => {
let cache_item = match *source {
BorderSource::Image(request) => {
resolve_image(
request,
resource_cache,
gpu_cache,
deferred_resolves,
)
}
BorderSource::Border { ref handle, .. } => {
let rt_handle = handle
.as_ref()
.expect("bug: render task handle not allocated");
let rt_cache_entry = resource_cache
.get_cached_render_task(rt_handle);
resource_cache.get_texture_cache_item(&rt_cache_entry.handle)
}
};
if cache_item.texture_id == SourceTexture::Invalid {
None

View File

@ -2,16 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipMode, ColorF, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, NormalBorder};
use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayoutPoint};
use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
use api::{DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
use app_units::Au;
use clip::ClipSource;
use ellipse::Ellipse;
use display_list_flattener::DisplayListFlattener;
use gpu_types::BrushFlags;
use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
use gpu_cache::GpuDataRequest;
use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor};
use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
use util::{lerp, pack_as_float};
use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentDescriptor};
use prim_store::{BorderSource, EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
use util::{lerp, pack_as_float, RectHelpers};
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
@ -36,6 +38,96 @@ enum BorderCorner {
BottomRight,
}
trait AuSizeConverter {
fn to_au(&self) -> LayoutSizeAu;
}
impl AuSizeConverter for LayoutSize {
fn to_au(&self) -> LayoutSizeAu {
LayoutSizeAu::new(
Au::from_f32_px(self.width),
Au::from_f32_px(self.height),
)
}
}
// TODO(gw): Perhaps there is a better way to store
// the border cache key than duplicating
// all the border structs with hashable
// variants...
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BorderRadiusAu {
pub top_left: LayoutSizeAu,
pub top_right: LayoutSizeAu,
pub bottom_left: LayoutSizeAu,
pub bottom_right: LayoutSizeAu,
}
impl From<BorderRadius> for BorderRadiusAu {
fn from(radius: BorderRadius) -> BorderRadiusAu {
BorderRadiusAu {
top_left: radius.top_left.to_au(),
top_right: radius.top_right.to_au(),
bottom_right: radius.bottom_right.to_au(),
bottom_left: radius.bottom_left.to_au(),
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BorderWidthsAu {
pub left: Au,
pub top: Au,
pub right: Au,
pub bottom: Au,
}
impl From<BorderWidths> for BorderWidthsAu {
fn from(widths: BorderWidths) -> Self {
BorderWidthsAu {
left: Au::from_f32_px(widths.left),
top: Au::from_f32_px(widths.top),
right: Au::from_f32_px(widths.right),
bottom: Au::from_f32_px(widths.bottom),
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BorderSideAu {
pub color: ColorU,
pub style: BorderStyle,
}
impl From<BorderSide> for BorderSideAu {
fn from(side: BorderSide) -> Self {
BorderSideAu {
color: side.color.into(),
style: side.style,
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BorderCacheKey {
pub left: BorderSideAu,
pub right: BorderSideAu,
pub top: BorderSideAu,
pub bottom: BorderSideAu,
pub radius: BorderRadiusAu,
pub widths: BorderWidthsAu,
pub scale: Au,
}
#[derive(Clone, Debug, PartialEq)]
pub enum BorderCornerKind {
None,
@ -374,60 +466,54 @@ impl<'a> DisplayListFlattener<'a> {
let top = &border.top;
let bottom = &border.bottom;
let constant_color = left.color;
let is_simple_border = [left, top, right, bottom].iter().all(|edge| {
edge.style == BorderStyle::Solid &&
edge.color == constant_color
let brush_border_supported = [left, top, right, bottom].iter().all(|edge| {
match edge.style {
BorderStyle::Solid |
BorderStyle::Hidden |
BorderStyle::None |
BorderStyle::Inset |
BorderStyle::Outset => {
true
}
BorderStyle::Double |
BorderStyle::Dotted |
BorderStyle::Dashed |
BorderStyle::Groove |
BorderStyle::Ridge => {
false
}
}
});
if is_simple_border {
let extra_clips = vec![
ClipSource::new_rounded_rect(
info.rect,
border.radius,
ClipMode::Clip,
),
ClipSource::new_rounded_rect(
LayoutRect::new(
LayoutPoint::new(
info.rect.origin.x + widths.left,
info.rect.origin.y + widths.top,
),
LayoutSize::new(
info.rect.size.width - widths.left - widths.right,
info.rect.size.height - widths.top - widths.bottom,
),
),
BorderRadius {
top_left: LayoutSize::new(
(border.radius.top_left.width - widths.left).max(0.0),
(border.radius.top_left.height - widths.top).max(0.0),
),
top_right: LayoutSize::new(
(border.radius.top_right.width - widths.right).max(0.0),
(border.radius.top_right.height - widths.top).max(0.0),
),
bottom_left: LayoutSize::new(
(border.radius.bottom_left.width - widths.left).max(0.0),
(border.radius.bottom_left.height - widths.bottom).max(0.0),
),
bottom_right: LayoutSize::new(
(border.radius.bottom_right.width - widths.right).max(0.0),
(border.radius.bottom_right.height - widths.bottom).max(0.0),
),
if brush_border_supported {
let prim = BrushPrimitive::new(
BrushKind::Border {
source: BorderSource::Border {
border,
widths: *widths,
cache_key: BorderCacheKey {
left: border.left.into(),
top: border.top.into(),
right: border.right.into(),
bottom: border.bottom.into(),
widths: (*widths).into(),
radius: border.radius.into(),
scale: Au::from_f32_px(0.0),
},
task_info: None,
handle: None,
},
ClipMode::ClipOut,
),
];
self.add_solid_rectangle(
clip_and_scroll,
info,
border.top.color,
},
None,
extra_clips,
);
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
return;
}
@ -505,8 +591,7 @@ impl<'a> DisplayListFlattener<'a> {
let p3 = info.rect.bottom_right();
let segment = |x0, y0, x1, y1| BrushSegment::new(
LayoutPoint::new(x0, y0),
LayoutSize::new(x1-x0, y1-y0),
LayoutRect::from_floats(x0, y0, x1, y1),
true,
EdgeAaSegmentMask::all(), // Note: this doesn't seem right, needs revision
[0.0; 4],
@ -929,3 +1014,434 @@ impl DotInfo {
DotInfo { arc_pos, diameter }
}
}
#[derive(Debug)]
pub struct BorderRenderTaskInfo {
pub instances: Vec<BorderInstance>,
pub segments: Vec<BrushSegment>,
pub size: DeviceIntSize,
}
impl BorderRenderTaskInfo {
pub fn new(
rect: &LayoutRect,
border: &NormalBorder,
widths: &BorderWidths,
scale: LayoutToDeviceScale,
) -> Self {
let mut instances = Vec::new();
let mut segments = Vec::new();
let dp_width_top = (widths.top * scale.0).ceil();
let dp_width_bottom = (widths.bottom * scale.0).ceil();
let dp_width_left = (widths.left * scale.0).ceil();
let dp_width_right = (widths.right * scale.0).ceil();
let dp_corner_tl = (border.radius.top_left * scale).ceil();
let dp_corner_tr = (border.radius.top_right * scale).ceil();
let dp_corner_bl = (border.radius.bottom_left * scale).ceil();
let dp_corner_br = (border.radius.bottom_right * scale).ceil();
let dp_size_tl = DeviceSize::new(
dp_corner_tl.width.max(dp_width_left),
dp_corner_tl.height.max(dp_width_top),
);
let dp_size_tr = DeviceSize::new(
dp_corner_tr.width.max(dp_width_right),
dp_corner_tr.height.max(dp_width_top),
);
let dp_size_br = DeviceSize::new(
dp_corner_br.width.max(dp_width_right),
dp_corner_br.height.max(dp_width_bottom),
);
let dp_size_bl = DeviceSize::new(
dp_corner_bl.width.max(dp_width_left),
dp_corner_bl.height.max(dp_width_bottom),
);
let local_size_tl = LayoutSize::new(
border.radius.top_left.width.max(widths.left),
border.radius.top_left.height.max(widths.top),
);
let local_size_tr = LayoutSize::new(
border.radius.top_right.width.max(widths.right),
border.radius.top_right.height.max(widths.top),
);
let local_size_br = LayoutSize::new(
border.radius.bottom_right.width.max(widths.right),
border.radius.bottom_right.height.max(widths.bottom),
);
let local_size_bl = LayoutSize::new(
border.radius.bottom_left.width.max(widths.left),
border.radius.bottom_left.height.max(widths.bottom),
);
// TODO(gw): The inner and outer widths don't matter for simple
// border types. Once we push dashing and dotted styles
// through border brushes, we need to calculate an
// appropriate length here.
let width_inner = 16.0;
let height_inner = 16.0;
let size = DeviceSize::new(
dp_size_tl.width.max(dp_size_bl.width) + width_inner + dp_size_tr.width.max(dp_size_br.width),
dp_size_tl.height.max(dp_size_tr.height) + height_inner + dp_size_bl.height.max(dp_size_br.height),
);
// These modulate colors are not part of the specification. They
// are derived from the Gecko source code and experimentation, and
// used to modulate the colors in order to generate colors for
// the inset/outset and groove/ridge border styles.
let left_color = border.left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
let top_color = border.top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
let right_color = border.right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
let bottom_color = border.bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x,
rect.origin.y + local_size_tl.height,
rect.origin.x + widths.left,
rect.origin.y + rect.size.height - local_size_bl.height,
),
DeviceRect::from_floats(
0.0,
dp_size_tl.height,
dp_width_left,
size.height - dp_size_bl.height,
),
border.left.style,
left_color,
BorderSegment::Left,
EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
&mut instances,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
&mut segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + local_size_tl.width,
rect.origin.y,
rect.origin.x + rect.size.width - local_size_tr.width,
rect.origin.y + widths.top,
),
DeviceRect::from_floats(
dp_size_tl.width,
0.0,
size.width - dp_size_tr.width,
dp_width_top,
),
border.top.style,
top_color,
BorderSegment::Top,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
&mut instances,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
&mut segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - widths.right,
rect.origin.y + local_size_tr.height,
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height - local_size_br.height,
),
DeviceRect::from_floats(
size.width - dp_width_right,
dp_size_tr.height,
size.width,
size.height - dp_size_br.height,
),
border.right.style,
right_color,
BorderSegment::Right,
EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
&mut instances,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
&mut segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + local_size_bl.width,
rect.origin.y + rect.size.height - widths.bottom,
rect.origin.x + rect.size.width - local_size_br.width,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
dp_size_bl.width,
size.height - dp_width_bottom,
size.width - dp_size_br.width,
size.height,
),
border.bottom.style,
bottom_color,
BorderSegment::Bottom,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
&mut instances,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
&mut segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x,
rect.origin.y,
rect.origin.x + local_size_tl.width,
rect.origin.y + local_size_tl.height,
),
DeviceRect::from_floats(
0.0,
0.0,
dp_size_tl.width,
dp_size_tl.height,
),
border.left.style,
left_color,
border.top.style,
top_color,
DeviceSize::new(dp_width_left, dp_width_top),
dp_corner_tl,
BorderSegment::TopLeft,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
&mut instances,
&mut segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - local_size_tr.width,
rect.origin.y,
rect.origin.x + rect.size.width,
rect.origin.y + local_size_tr.height,
),
DeviceRect::from_floats(
size.width - dp_size_tr.width,
0.0,
size.width,
dp_size_tr.height,
),
border.top.style,
top_color,
border.right.style,
right_color,
DeviceSize::new(dp_width_right, dp_width_top),
dp_corner_tr,
BorderSegment::TopRight,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
&mut instances,
&mut segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - local_size_br.width,
rect.origin.y + rect.size.height - local_size_br.height,
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
size.width - dp_size_br.width,
size.height - dp_size_br.height,
size.width,
size.height,
),
border.right.style,
right_color,
border.bottom.style,
bottom_color,
DeviceSize::new(dp_width_right, dp_width_bottom),
dp_corner_br,
BorderSegment::BottomRight,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
&mut instances,
&mut segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x,
rect.origin.y + rect.size.height - local_size_bl.height,
rect.origin.x + local_size_bl.width,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
0.0,
size.height - dp_size_bl.height,
dp_size_bl.width,
size.height,
),
border.bottom.style,
bottom_color,
border.left.style,
left_color,
DeviceSize::new(dp_width_left, dp_width_bottom),
dp_corner_bl,
BorderSegment::BottomLeft,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
&mut instances,
&mut segments,
);
BorderRenderTaskInfo {
segments,
instances,
size: size.to_i32(),
}
}
}
fn add_brush_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
brush_flags: BrushFlags,
edge_flags: EdgeAaSegmentMask,
brush_segments: &mut Vec<BrushSegment>,
) {
brush_segments.push(
BrushSegment::new(
image_rect,
true,
edge_flags,
[
task_rect.origin.x,
task_rect.origin.y,
task_rect.origin.x + task_rect.size.width,
task_rect.origin.y + task_rect.size.height,
],
brush_flags,
)
);
}
fn add_segment(
task_rect: DeviceRect,
style0: BorderStyle,
style1: BorderStyle,
color0: ColorF,
color1: ColorF,
segment: BorderSegment,
instances: &mut Vec<BorderInstance>,
widths: DeviceSize,
radius: DeviceSize,
) {
let flags = (segment as i32) |
((style0 as i32) << 8) |
((style1 as i32) << 16);
let base_instance = BorderInstance {
task_origin: DevicePoint::zero(),
local_rect: task_rect,
flags,
color0: color0.premultiplied(),
color1: color1.premultiplied(),
widths,
radius,
};
instances.push(base_instance);
}
fn add_corner_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
mut style0: BorderStyle,
color0: ColorF,
mut style1: BorderStyle,
color1: ColorF,
widths: DeviceSize,
radius: DeviceSize,
segment: BorderSegment,
edge_flags: EdgeAaSegmentMask,
instances: &mut Vec<BorderInstance>,
brush_segments: &mut Vec<BrushSegment>,
) {
// TODO(gw): This will need to be a bit more involved when
// we support other border types here. For example,
// groove / ridge borders will always need to
// use two instances.
if color0.a <= 0.0 && color1.a <= 0.0 {
return;
}
if widths.width <= 0.0 && widths.height <= 0.0 {
return;
}
let style0_hidden = style0 == BorderStyle::Hidden || style0 == BorderStyle::None;
let style1_hidden = style1 == BorderStyle::Hidden || style1 == BorderStyle::None;
if style0_hidden && style1_hidden {
return;
}
if style0_hidden {
style0 = style1;
}
if style1_hidden {
style1 = style0;
}
add_segment(
task_rect,
style0,
style1,
color0,
color1,
segment,
instances,
widths,
radius,
);
add_brush_segment(
image_rect,
task_rect,
BrushFlags::SEGMENT_RELATIVE,
edge_flags,
brush_segments,
);
}
fn add_edge_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
style: BorderStyle,
color: ColorF,
segment: BorderSegment,
edge_flags: EdgeAaSegmentMask,
instances: &mut Vec<BorderInstance>,
brush_flags: BrushFlags,
brush_segments: &mut Vec<BrushSegment>,
) {
if color.a <= 0.0 {
return;
}
if style == BorderStyle::Hidden || style == BorderStyle::None {
return;
}
add_segment(
task_rect,
style,
style,
color,
color,
segment,
instances,
DeviceSize::zero(),
DeviceSize::zero(),
);
add_brush_segment(
image_rect,
task_rect,
brush_flags,
edge_flags,
brush_segments,
);
}

View File

@ -27,7 +27,7 @@ use internal_types::{FastHashMap, FastHashSet};
use picture::PictureCompositeMode;
use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageSource};
use prim_store::{BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{BorderSource, BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
@ -1636,8 +1636,7 @@ impl<'a> DisplayListFlattener<'a> {
}
let segment = BrushSegment::new(
rect.origin,
rect.size,
rect,
true,
EdgeAaSegmentMask::empty(),
[
@ -1741,13 +1740,15 @@ impl<'a> DisplayListFlattener<'a> {
let prim = PrimitiveContainer::Brush(match border.source {
NinePatchBorderSource::Image(image_key) => {
let source = BorderSource::Image(ImageRequest {
key: image_key,
rendering: ImageRendering::Auto,
tile: None,
});
BrushPrimitive::new(
BrushKind::Border {
request: ImageRequest {
key: image_key,
rendering: ImageRendering::Auto,
tile: None,
},
source
},
Some(descriptor),
)
@ -1966,20 +1967,18 @@ impl<'a> DisplayListFlattener<'a> {
*text_color,
font_instance.bg_color,
render_mode,
font_instance.subpx_dir,
flags,
font_instance.platform_options,
font_instance.variations.clone(),
);
TextRunPrimitiveCpu {
font: prim_font,
TextRunPrimitiveCpu::new(
prim_font,
run_offset,
glyph_range,
glyph_gpu_blocks: Vec::new(),
glyph_keys: Vec::new(),
offset: run_offset,
shadow: false,
Vec::new(),
false,
glyph_raster_space,
}
)
};
self.add_primitive(

View File

@ -4,8 +4,7 @@
#[cfg(feature = "pathfinder")]
use api::DeviceIntPoint;
use api::GlyphKey;
use glyph_rasterizer::{FontInstance, GlyphFormat};
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey};
use internal_types::FastHashMap;
use render_task::RenderTaskCache;
#[cfg(feature = "pathfinder")]

View File

@ -2,11 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, ColorU};
use api::{ColorF, ColorU, DevicePoint};
use api::{FontInstanceFlags, FontInstancePlatformOptions};
use api::{FontKey, FontRenderMode, FontTemplate, FontVariation};
use api::{GlyphDimensions, GlyphKey, LayoutToWorldTransform, SubpixelDirection};
use api::{GlyphIndex, GlyphDimensions};
use api::{LayoutPoint, LayoutToWorldTransform, WorldPoint};
use app_units::Au;
use euclid::approxeq::ApproxEq;
use internal_types::ResourceCacheError;
use platform::font::FontContext;
use rayon::ThreadPool;
@ -127,7 +129,27 @@ impl FontTransform {
}
pub fn flip_y(&self) -> Self {
FontTransform::new(self.scale_x, -self.skew_y, self.skew_y, -self.scale_y)
FontTransform::new(self.scale_x, -self.skew_x, self.skew_y, -self.scale_y)
}
pub fn transform(&self, point: &LayoutPoint) -> WorldPoint {
WorldPoint::new(
self.scale_x * point.x + self.skew_x * point.y,
self.skew_y * point.x + self.scale_y * point.y,
)
}
pub fn get_subpx_dir(&self) -> SubpixelDirection {
if self.skew_y.approx_eq(&0.0) {
// The X axis is not projected onto the Y axis
SubpixelDirection::Horizontal
} else if self.scale_x.approx_eq(&0.0) {
// The X axis has been swapped with the Y axis
SubpixelDirection::Vertical
} else {
// Use subpixel precision on all axes
SubpixelDirection::Mixed
}
}
}
@ -151,7 +173,6 @@ pub struct FontInstance {
pub color: ColorU,
pub bg_color: ColorU,
pub render_mode: FontRenderMode,
pub subpx_dir: SubpixelDirection,
pub flags: FontInstanceFlags,
pub platform_options: Option<FontInstancePlatformOptions>,
pub variations: Vec<FontVariation>,
@ -165,7 +186,6 @@ impl FontInstance {
color: ColorF,
bg_color: ColorU,
render_mode: FontRenderMode,
subpx_dir: SubpixelDirection,
flags: FontInstanceFlags,
platform_options: Option<FontInstancePlatformOptions>,
variations: Vec<FontVariation>,
@ -180,7 +200,6 @@ impl FontInstance {
color: color.into(),
bg_color,
render_mode,
subpx_dir,
flags,
platform_options,
variations,
@ -196,12 +215,38 @@ impl FontInstance {
if self.transform.is_identity() { GlyphFormat::Subpixel } else { GlyphFormat::TransformedSubpixel }
}
pub fn disable_subpixel_aa(&mut self) {
self.render_mode = self.render_mode.limit_by(FontRenderMode::Alpha);
}
pub fn disable_subpixel_position(&mut self) {
self.flags.remove(FontInstanceFlags::SUBPIXEL_POSITION);
}
pub fn use_subpixel_position(&self) -> bool {
self.flags.contains(FontInstanceFlags::SUBPIXEL_POSITION) &&
self.render_mode != FontRenderMode::Mono
}
pub fn get_subpx_dir(&self) -> SubpixelDirection {
if self.use_subpixel_position() {
let mut subpx_dir = self.transform.get_subpx_dir();
if self.flags.contains(FontInstanceFlags::TRANSPOSE) {
subpx_dir = subpx_dir.swap_xy();
}
subpx_dir
} else {
SubpixelDirection::None
}
}
#[allow(dead_code)]
pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
match self.subpx_dir {
SubpixelDirection::None => (0.0, 0.0),
SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
if self.use_subpixel_position() {
let (dx, dy) = glyph.subpixel_offset;
(dx.into(), dy.into())
} else {
(0.0, 0.0)
}
}
@ -227,6 +272,111 @@ impl FontInstance {
}
}
#[repr(u32)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub enum SubpixelDirection {
None = 0,
Horizontal,
Vertical,
Mixed,
}
impl SubpixelDirection {
// Limit the subpixel direction to what is supported by the glyph format.
pub fn limit_by(self, glyph_format: GlyphFormat) -> Self {
match glyph_format {
GlyphFormat::Bitmap |
GlyphFormat::ColorBitmap => SubpixelDirection::None,
_ => self,
}
}
pub fn swap_xy(self) -> Self {
match self {
SubpixelDirection::None | SubpixelDirection::Mixed => self,
SubpixelDirection::Horizontal => SubpixelDirection::Vertical,
SubpixelDirection::Vertical => SubpixelDirection::Horizontal,
}
}
}
#[repr(u8)]
#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum SubpixelOffset {
Zero = 0,
Quarter = 1,
Half = 2,
ThreeQuarters = 3,
}
impl SubpixelOffset {
// Skia quantizes subpixel offsets into 1/4 increments.
// Given the absolute position, return the quantized increment
fn quantize(pos: f32) -> Self {
// Following the conventions of Gecko and Skia, we want
// to quantize the subpixel position, such that abs(pos) gives:
// [0.0, 0.125) -> Zero
// [0.125, 0.375) -> Quarter
// [0.375, 0.625) -> Half
// [0.625, 0.875) -> ThreeQuarters,
// [0.875, 1.0) -> Zero
// The unit tests below check for this.
let apos = ((pos - pos.floor()) * 8.0) as i32;
match apos {
0 | 7 => SubpixelOffset::Zero,
1...2 => SubpixelOffset::Quarter,
3...4 => SubpixelOffset::Half,
5...6 => SubpixelOffset::ThreeQuarters,
_ => unreachable!("bug: unexpected quantized result"),
}
}
}
impl Into<f64> for SubpixelOffset {
fn into(self) -> f64 {
match self {
SubpixelOffset::Zero => 0.0,
SubpixelOffset::Quarter => 0.25,
SubpixelOffset::Half => 0.5,
SubpixelOffset::ThreeQuarters => 0.75,
}
}
}
#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GlyphKey {
pub index: u32,
pub subpixel_offset: (SubpixelOffset, SubpixelOffset),
}
impl GlyphKey {
pub fn new(
index: u32,
point: DevicePoint,
subpx_dir: SubpixelDirection,
) -> GlyphKey {
let (dx, dy) = match subpx_dir {
SubpixelDirection::None => (0.0, 0.0),
SubpixelDirection::Horizontal => (point.x, 0.0),
SubpixelDirection::Vertical => (0.0, point.y),
SubpixelDirection::Mixed => (point.x, point.y),
};
GlyphKey {
index,
subpixel_offset: (
SubpixelOffset::quantize(dx),
SubpixelOffset::quantize(dy),
),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -395,11 +545,17 @@ impl GlyphRasterizer {
pub fn get_glyph_dimensions(
&mut self,
font: &FontInstance,
glyph_key: &GlyphKey,
glyph_index: GlyphIndex,
) -> Option<GlyphDimensions> {
let glyph_key = GlyphKey::new(
glyph_index,
DevicePoint::zero(),
SubpixelDirection::None,
);
self.font_contexts
.lock_shared_context()
.get_glyph_dimensions(font, glyph_key)
.get_glyph_dimensions(font, &glyph_key)
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
@ -454,23 +610,6 @@ impl AddFont for FontContext {
}
}
#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GlyphRequest {
pub key: GlyphKey,
pub font: FontInstance,
}
impl GlyphRequest {
pub fn new(font: &FontInstance, key: &GlyphKey) -> Self {
GlyphRequest {
key: key.clone(),
font: font.clone(),
}
}
}
#[allow(dead_code)]
pub(in glyph_rasterizer) struct GlyphRasterJob {
key: GlyphKey,
@ -511,13 +650,13 @@ mod test_glyph_rasterizer {
use api::DeviceIntSize;
use render_task::{RenderTaskCache, RenderTaskTree};
use profiler::TextureCacheProfileCounters;
use api::{FontKey, FontTemplate, FontRenderMode, GlyphKey,
IdNamespace, LayoutPoint, ColorF, ColorU, SubpixelDirection};
use api::{FontKey, FontTemplate, FontRenderMode,
IdNamespace, ColorF, ColorU, DevicePoint};
use render_backend::FrameId;
use app_units::Au;
use thread_profiler::register_thread_with_profiler;
use std::sync::Arc;
use glyph_rasterizer::{GlyphRasterizer, FontInstance};
use glyph_rasterizer::{FontInstance, GlyphKey, GlyphRasterizer};
let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
@ -550,19 +689,18 @@ mod test_glyph_rasterizer {
ColorF::new(0.0, 0.0, 0.0, 1.0),
ColorU::new(0, 0, 0, 0),
FontRenderMode::Subpixel,
SubpixelDirection::Horizontal,
Default::default(),
None,
Vec::new(),
);
let subpx_dir = font.get_subpx_dir();
let mut glyph_keys = Vec::with_capacity(200);
for i in 0 .. 200 {
glyph_keys.push(GlyphKey::new(
i,
LayoutPoint::zero(),
font.render_mode,
font.subpx_dir,
DevicePoint::zero(),
subpx_dir,
));
}
@ -590,4 +728,48 @@ mod test_glyph_rasterizer {
&mut TextureCacheProfileCounters::new(),
);
}
}
#[test]
fn test_subpx_quantize() {
use glyph_rasterizer::SubpixelOffset;
assert_eq!(SubpixelOffset::quantize(0.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(-0.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.1), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.01), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.05), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.12), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.124), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.125), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.2), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.25), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.33), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.374), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.375), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.4), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.5), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.58), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.624), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.625), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.67), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.7), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.78), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.874), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.875), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.89), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.91), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.967), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.999), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(-1.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(1.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(1.5), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(-1.625), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(-4.33), SubpixelOffset::ThreeQuarters);
}
}

View File

@ -5,14 +5,15 @@
//! Module only available when pathfinder is deactivated when webrender is
//! compiled regularly (i.e. any configuration without feature = "pathfinder")
use api::{GlyphKey, ImageData, ImageDescriptor, ImageFormat};
use api::{ImageData, ImageDescriptor, ImageFormat};
use device::TextureFilter;
use euclid::size2;
use gpu_types::UvRectKind;
use rayon::prelude::*;
use std::sync::{Arc, MutexGuard};
use platform::font::FontContext;
use glyph_rasterizer::{FontInstance, FontContexts, GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult};
use glyph_rasterizer::{FontInstance, FontContexts, GlyphKey};
use glyph_rasterizer::{GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult};
use glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry};
use texture_cache::{TextureCache, TextureCacheHandle};
use gpu_cache::GpuCache;
@ -202,4 +203,4 @@ impl GlyphRasterizer {
// we can schedule removing the fonts if needed.
self.remove_dead_fonts();
}
}
}

View File

@ -4,7 +4,7 @@
//! Module only available when pathfinder is activated
use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, GlyphKey, NativeFontHandle};
use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, NativeFontHandle};
use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D};
use pathfinder_font_renderer;
use pathfinder_partitioner::mesh::Mesh as PathfinderMesh;
@ -20,7 +20,7 @@ use internal_types::ResourceCacheError;
use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
use std::collections::hash_map::Entry;
use std::f32;
use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, FontContexts};
use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, GlyphKey, FontContexts};
use texture_cache::TextureCache;
use gpu_cache::GpuCache;
use profiler::TextureCacheProfileCounters;
@ -190,8 +190,9 @@ impl GlyphRasterizer {
size: font.size,
};
// TODO: pathfinder will need to support 2D subpixel offset
let pathfinder_subpixel_offset =
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8);
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
let pathfinder_glyph_key =
pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
pathfinder_subpixel_offset);
@ -272,9 +273,10 @@ fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
size: font.size,
};
// TODO: pathfinder will need to support 2D subpixel offset
let pathfinder_subpixel_offset =
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8);
let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.into();
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into();
let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
pathfinder_subpixel_offset);
@ -311,4 +313,4 @@ fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
Ok(root_task_id)
}
pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);

View File

@ -2,7 +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::{DevicePoint, LayoutToWorldTransform, WorldToLayoutTransform};
use api::{DevicePoint, DeviceSize, DeviceRect, LayoutToWorldTransform};
use api::{PremultipliedColorF, WorldToLayoutTransform};
use gpu_cache::{GpuCacheAddress, GpuDataRequest};
use prim_store::{VECS_PER_SEGMENT, EdgeAaSegmentMask};
use render_task::RenderTaskAddress;
@ -79,6 +80,35 @@ pub struct BlurInstance {
pub blur_direction: BlurDirection,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BorderSegment {
TopLeft,
TopRight,
BottomRight,
BottomLeft,
Left,
Top,
Right,
Bottom,
}
#[derive(Debug, Clone)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BorderInstance {
pub task_origin: DevicePoint,
pub local_rect: DeviceRect,
pub color0: PremultipliedColorF,
pub color1: PremultipliedColorF,
pub flags: i32,
pub widths: DeviceSize,
pub radius: DeviceSize,
}
/// A clipping primitive drawn into the clipping mask.
/// Could be an image or a rectangle, which defines the
/// way `address` is treated.

View File

@ -4,7 +4,6 @@
use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
use api::{FontInstanceFlags, FontVariation, NativeFontHandle};
use api::{GlyphKey, SubpixelDirection};
use app_units::Au;
use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::TCFType;
@ -27,7 +26,7 @@ use core_text;
use core_text::font::{CTFont, CTFontRef};
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
use gamma_lut::{ColorLut, GammaLut};
use glyph_rasterizer::{FontInstance, FontTransform};
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
#[cfg(feature = "pathfinder")]
use glyph_rasterizer::NativeFontHandleWrapper;
#[cfg(not(feature = "pathfinder"))]
@ -463,7 +462,7 @@ impl FontContext {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono mode.
font.subpx_dir = SubpixelDirection::None;
font.disable_subpixel_position();
}
FontRenderMode::Alpha => {
font.color = if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) {

View File

@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorU, GlyphDimensions, GlyphKey, FontKey, FontRenderMode};
use api::{ColorU, GlyphDimensions, FontKey, FontRenderMode};
use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
use api::{FontInstanceFlags, NativeFontHandle, SubpixelDirection};
use api::{FontInstanceFlags, NativeFontHandle};
use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
@ -18,7 +18,7 @@ use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHIN
use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES};
use freetype::succeeded;
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterResult, RasterizedGlyph};
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterResult, RasterizedGlyph};
#[cfg(feature = "pathfinder")]
use glyph_rasterizer::NativeFontHandleWrapper;
use internal_types::{FastHashMap, ResourceCacheError};
@ -253,9 +253,10 @@ impl FontContext {
(FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO,
(FontHinting::Light, _) => load_flags = FT_LOAD_TARGET_LIGHT,
(FontHinting::LCD, FontRenderMode::Subpixel) => {
load_flags = match font.subpx_dir {
SubpixelDirection::Vertical => FT_LOAD_TARGET_LCD_V,
_ => FT_LOAD_TARGET_LCD,
load_flags = if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
FT_LOAD_TARGET_LCD_V
} else {
FT_LOAD_TARGET_LCD
};
if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) {
load_flags |= FT_LOAD_FORCE_AUTOHINT;
@ -375,31 +376,29 @@ impl FontContext {
}
}
// Convert the subpixel offset to floats.
let (dx, dy) = font.get_subpx_offset(glyph);
// Apply extra pixel of padding for subpixel AA, due to the filter.
let padding = match font.render_mode {
FontRenderMode::Subpixel => (self.lcd_extra_pixels * 64) as FT_Pos,
FontRenderMode::Alpha |
FontRenderMode::Mono => 0 as FT_Pos,
};
if font.render_mode == FontRenderMode::Subpixel {
let padding = (self.lcd_extra_pixels * 64) as FT_Pos;
if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
cbox.yMin -= padding;
cbox.yMax += padding;
} else {
cbox.xMin -= padding;
cbox.xMax += padding;
}
}
// Offset the bounding box by subpixel positioning.
// Convert to 26.6 fixed point format for FT.
match font.subpx_dir {
SubpixelDirection::None => {}
SubpixelDirection::Horizontal => {
let dx = (dx * 64.0 + 0.5) as FT_Long;
cbox.xMin += dx - padding;
cbox.xMax += dx + padding;
}
SubpixelDirection::Vertical => {
let dy = (dy * 64.0 + 0.5) as FT_Long;
cbox.yMin += dy - padding;
cbox.yMax += dy + padding;
}
}
let (dx, dy) = font.get_subpx_offset(glyph);
let (dx, dy) = (
(dx * 64.0 + 0.5) as FT_Pos,
-(dy * 64.0 + 0.5) as FT_Pos,
);
cbox.xMin += dx;
cbox.xMax += dx;
cbox.yMin += dy;
cbox.yMax += dy;
// Outset the box to device pixel boundaries
cbox.xMin &= !63;
@ -522,7 +521,7 @@ impl FontContext {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF);
// Subpixel positioning is disabled in mono mode.
font.subpx_dir = SubpixelDirection::None;
font.disable_subpixel_position();
}
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
// We don't do any preblending with FreeType currently, so the color is not used.
@ -539,8 +538,10 @@ impl FontContext {
) -> bool {
// Get the subpixel offsets in FT 26.6 format.
let (dx, dy) = font.get_subpx_offset(key);
let dx = (dx * 64.0 + 0.5) as FT_Long;
let dy = (dy * 64.0 + 0.5) as FT_Long;
let (dx, dy) = (
(dx * 64.0 + 0.5) as FT_Pos,
-(dy * 64.0 + 0.5) as FT_Pos,
);
// Move the outline curves to be at the origin, taking
// into account the subpixel positioning.
@ -565,11 +566,14 @@ impl FontContext {
};
unsafe { FT_Library_SetLcdFilter(self.lib, filter) };
}
let render_mode = match (font.render_mode, font.subpx_dir) {
(FontRenderMode::Mono, _) => FT_Render_Mode::FT_RENDER_MODE_MONO,
(FontRenderMode::Alpha, _) => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
(FontRenderMode::Subpixel, SubpixelDirection::Vertical) => FT_Render_Mode::FT_RENDER_MODE_LCD_V,
(FontRenderMode::Subpixel, _) => FT_Render_Mode::FT_RENDER_MODE_LCD,
let render_mode = match font.render_mode {
FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO,
FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL,
FontRenderMode::Subpixel => if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) {
FT_Render_Mode::FT_RENDER_MODE_LCD_V
} else {
FT_Render_Mode::FT_RENDER_MODE_LCD
},
};
let result = unsafe { FT_Render_Glyph(slot, render_mode) };
if !succeeded(result) {

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{FontInstanceFlags, FontKey, FontRenderMode};
use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
use api::{ColorU, GlyphDimensions};
use dwrote;
use gamma_lut::ColorLut;
use glyph_rasterizer::{FontInstance, FontTransform};
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
use internal_types::{FastHashMap, ResourceCacheError};
use std::collections::hash_map::Entry;
use std::sync::Arc;
@ -372,7 +372,7 @@ impl FontContext {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono mode.
font.subpx_dir = SubpixelDirection::None;
font.disable_subpixel_position();
}
FontRenderMode::Alpha => {
font.color = font.color.luminance_color().quantize();
@ -469,4 +469,4 @@ impl<'a> From<NativeFontHandleWrapper<'a>> for PathfinderComPtr<IDWriteFontFace>
let face = font.create_font_face();
unsafe { PathfinderComPtr::new(face.as_ptr()) }
}
}
}

View File

@ -3,11 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode};
use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets};
use border::{BorderCornerInstance, BorderEdgeKind};
use api::{BorderWidths, LayoutToWorldScale, NormalBorder};
use app_units::Au;
use border::{BorderCacheKey, BorderCornerInstance, BorderRenderTaskInfo, BorderEdgeKind};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
@ -15,7 +17,7 @@ use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSo
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
use glyph_rasterizer::{FontInstance, FontTransform};
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{BrushFlags, ClipChainRectIndex};
@ -255,6 +257,18 @@ pub struct VisibleGradientTile {
pub handle: GpuCacheHandle,
}
#[derive(Debug)]
pub enum BorderSource {
Image(ImageRequest),
Border {
handle: Option<RenderTaskCacheEntryHandle>,
cache_key: BorderCacheKey,
task_info: Option<BorderRenderTaskInfo>,
border: NormalBorder,
widths: BorderWidths,
},
}
#[derive(Debug)]
pub enum BrushKind {
Solid {
@ -307,7 +321,7 @@ pub enum BrushKind {
visible_tiles: Vec<VisibleGradientTile>,
},
Border {
request: ImageRequest,
source: BorderSource,
},
}
@ -353,7 +367,7 @@ bitflags! {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum BrushSegmentTaskId {
RenderTaskId(RenderTaskId),
Opaque,
@ -369,7 +383,7 @@ impl BrushSegmentTaskId {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct BrushSegment {
pub local_rect: LayoutRect,
pub clip_task_id: BrushSegmentTaskId,
@ -381,15 +395,14 @@ pub struct BrushSegment {
impl BrushSegment {
pub fn new(
origin: LayoutPoint,
size: LayoutSize,
rect: LayoutRect,
may_need_clip_mask: bool,
edge_flags: EdgeAaSegmentMask,
extra_data: [f32; 4],
brush_flags: BrushFlags,
) -> BrushSegment {
BrushSegment {
local_rect: LayoutRect::new(origin, size),
local_rect: rect,
clip_task_id: BrushSegmentTaskId::Opaque,
may_need_clip_mask,
edge_flags,
@ -761,11 +774,32 @@ pub struct TextRunPrimitiveCpu {
pub glyph_range: ItemRange<GlyphInstance>,
pub glyph_keys: Vec<GlyphKey>,
pub glyph_gpu_blocks: Vec<GpuBlockData>,
pub glyph_transform: (DevicePixelScale, FontTransform),
pub shadow: bool,
pub glyph_raster_space: GlyphRasterSpace,
}
impl TextRunPrimitiveCpu {
pub fn new(
font: FontInstance,
offset: LayoutVector2D,
glyph_range: ItemRange<GlyphInstance>,
glyph_keys: Vec<GlyphKey>,
shadow: bool,
glyph_raster_space: GlyphRasterSpace,
) -> Self {
TextRunPrimitiveCpu {
font,
offset,
glyph_range,
glyph_keys,
glyph_gpu_blocks: Vec::new(),
glyph_transform: (DevicePixelScale::new(1.0), FontTransform::identity()),
shadow,
glyph_raster_space,
}
}
pub fn get_font(
&self,
device_pixel_scale: DevicePixelScale,
@ -777,7 +811,8 @@ impl TextRunPrimitiveCpu {
if transform.has_perspective_component() ||
!transform.has_2d_inverse() ||
self.glyph_raster_space != GlyphRasterSpace::Screen {
font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
font.disable_subpixel_aa();
font.disable_subpixel_position();
} else {
font.transform = FontTransform::from(&transform).quantize();
}
@ -794,7 +829,7 @@ impl TextRunPrimitiveCpu {
frame_building_state: &mut FrameBuildingState,
) {
if !allow_subpixel_aa && self.font.bg_color.a == 0 {
self.font.render_mode = self.font.render_mode.limit_by(FontRenderMode::Alpha);
self.font.disable_subpixel_aa();
}
let font = self.get_font(device_pixel_scale, transform);
@ -803,8 +838,8 @@ impl TextRunPrimitiveCpu {
// TODO(gw): In the future, remove `glyph_instances`
// completely, and just reference the glyphs
// directly from the display list.
if self.glyph_keys.is_empty() {
let subpx_dir = font.subpx_dir.limit_by(font.render_mode);
if self.glyph_keys.is_empty() || self.glyph_transform != (device_pixel_scale, font.transform) {
let subpx_dir = font.get_subpx_dir();
let src_glyphs = display_list.get(self.glyph_range);
// TODO(gw): If we support chunks() on AuxIter
@ -812,7 +847,10 @@ impl TextRunPrimitiveCpu {
// be much simpler...
let mut gpu_block = [0.0; 4];
for (i, src) in src_glyphs.enumerate() {
let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir);
let layout_offset = src.point + self.offset;
let world_offset = font.transform.transform(&layout_offset);
let device_offset = device_pixel_scale.transform_point(&world_offset);
let key = GlyphKey::new(src.index, device_offset, subpx_dir);
self.glyph_keys.push(key);
// Two glyphs are packed per GPU block.
@ -832,6 +870,8 @@ impl TextRunPrimitiveCpu {
if (self.glyph_keys.len() & 1) != 0 {
self.glyph_gpu_blocks.push(gpu_block.into());
}
self.glyph_transform = (device_pixel_scale, font.transform);
}
frame_building_state.resource_cache
@ -1090,25 +1130,22 @@ impl PrimitiveContainer {
pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer {
match *self {
PrimitiveContainer::TextRun(ref info) => {
let mut render_mode = info.font.render_mode;
let mut font = FontInstance {
color: shadow.color.into(),
..info.font.clone()
};
if shadow.blur_radius > 0.0 {
render_mode = render_mode.limit_by(FontRenderMode::Alpha);
font.disable_subpixel_aa();
}
PrimitiveContainer::TextRun(TextRunPrimitiveCpu {
font: FontInstance {
color: shadow.color.into(),
render_mode,
..info.font.clone()
},
offset: info.offset + shadow.offset,
glyph_range: info.glyph_range,
glyph_keys: info.glyph_keys.clone(),
glyph_gpu_blocks: Vec::new(),
shadow: true,
glyph_raster_space: info.glyph_raster_space,
})
PrimitiveContainer::TextRun(TextRunPrimitiveCpu::new(
font,
info.offset + shadow.offset,
info.glyph_range,
info.glyph_keys.clone(),
true,
info.glyph_raster_space,
))
}
PrimitiveContainer::Brush(ref brush) => {
match brush.kind {
@ -1401,6 +1438,84 @@ impl PrimitiveStore {
self.cpu_metadata.len()
}
fn build_prim_segments_if_needed(
&mut self,
prim_index: PrimitiveIndex,
pic_state: &mut PictureState,
frame_state: &mut FrameBuildingState,
frame_context: &FrameBuildingContext,
) {
let metadata = &self.cpu_metadata[prim_index.0];
if metadata.prim_kind != PrimitiveKind::Brush {
return;
}
let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
if let BrushKind::Border { ref mut source, .. } = brush.kind {
if let BorderSource::Border {
ref border,
ref mut cache_key,
ref widths,
ref mut handle,
ref mut task_info,
..
} = *source {
// TODO(gw): When drawing in screen raster mode, we should also incorporate a
// scale factor from the world transform to get an appropriately
// sized border task.
let world_scale = LayoutToWorldScale::new(1.0);
let scale = world_scale * frame_context.device_pixel_scale;
let scale_au = Au::from_f32_px(scale.0);
let needs_update = scale_au != cache_key.scale;
if needs_update {
cache_key.scale = scale_au;
*task_info = Some(BorderRenderTaskInfo::new(
&metadata.local_rect,
border,
widths,
scale,
));
}
let task_info = task_info.as_ref().unwrap();
*handle = Some(frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size: DeviceIntSize::zero(),
kind: RenderTaskCacheKeyKind::Border(cache_key.clone()),
},
frame_state.gpu_cache,
frame_state.render_tasks,
None,
false, // todo
|render_tasks| {
let task = RenderTask::new_border(
task_info.size,
task_info.instances.clone(),
);
let task_id = render_tasks.add(task);
pic_state.tasks.push(task_id);
task_id
}
));
if needs_update {
brush.segment_desc = Some(BrushSegmentDescriptor {
segments: task_info.segments.clone(),
clip_mask_kind: BrushClipMaskKind::Unknown,
});
}
}
}
}
fn prepare_prim_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
@ -1663,21 +1778,29 @@ impl PrimitiveStore {
);
}
}
BrushKind::Border { request, .. } => {
let image_properties = frame_state
.resource_cache
.get_image_properties(request.key);
BrushKind::Border { ref mut source, .. } => {
match *source {
BorderSource::Image(request) => {
let image_properties = frame_state
.resource_cache
.get_image_properties(request.key);
if let Some(image_properties) = image_properties {
// Update opacity for this primitive to ensure the correct
// batching parameters are used.
metadata.opacity.is_opaque =
image_properties.descriptor.is_opaque;
if let Some(image_properties) = image_properties {
// Update opacity for this primitive to ensure the correct
// batching parameters are used.
metadata.opacity.is_opaque =
image_properties.descriptor.is_opaque;
frame_state.resource_cache.request_image(
request,
frame_state.gpu_cache,
);
frame_state.resource_cache.request_image(
request,
frame_state.gpu_cache,
);
}
}
BorderSource::Border { .. } => {
// Handled earlier since we need to update the segment
// descriptor *before* update_clip_task() is called.
}
}
}
BrushKind::RadialGradient {
@ -2004,8 +2127,7 @@ impl PrimitiveStore {
segment_builder.build(|segment| {
segments.push(
BrushSegment::new(
segment.rect.origin,
segment.rect.size,
segment.rect,
segment.has_mask,
segment.edge_flags,
[0.0; 4],
@ -2390,6 +2512,13 @@ impl PrimitiveStore {
(local_rect, unclipped)
};
self.build_prim_segments_if_needed(
prim_index,
pic_state,
frame_state,
frame_context,
);
if may_need_clip_mask && !self.update_clip_task(
prim_index,
prim_run_context,

View File

@ -813,11 +813,11 @@ impl RenderBackend {
self.resource_cache
.update_resources(updates, &mut profile_counters.resources);
}
ApiMsg::GetGlyphDimensions(instance_key, glyph_keys, tx) => {
let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
ApiMsg::GetGlyphDimensions(instance_key, glyph_indices, tx) => {
let mut glyph_dimensions = Vec::with_capacity(glyph_indices.len());
if let Some(font) = self.resource_cache.get_font_instance(instance_key) {
for glyph_key in &glyph_keys {
let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key);
for glyph_index in &glyph_indices {
let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index);
glyph_dimensions.push(glyph_dim);
}
}

View File

@ -5,6 +5,7 @@
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets, ImageDescriptor, ImageFormat};
#[cfg(feature = "pathfinder")]
use api::FontRenderMode;
use border::BorderCacheKey;
use box_shadow::{BoxShadowCacheKey};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::CoordinateSystemId;
@ -14,7 +15,7 @@ use euclid::{TypedPoint2D, TypedVector2D};
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use glyph_rasterizer::GpuGlyphCacheKey;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::{ImageSource, RasterizationSpace, UvRectKind};
use gpu_types::{BorderInstance, ImageSource, RasterizationSpace, UvRectKind};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
@ -249,6 +250,13 @@ pub enum BlitSource {
},
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BorderTask {
pub instances: Vec<BorderInstance>,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -278,6 +286,7 @@ pub enum RenderTaskKind {
Readback(DeviceIntRect),
Scaling(RenderTargetKind),
Blit(BlitTask),
Border(BorderTask),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@ -557,6 +566,21 @@ impl RenderTask {
}
}
pub fn new_border(
size: DeviceIntSize,
instances: Vec<BorderInstance>,
) -> Self {
RenderTask {
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, Some(size)),
kind: RenderTaskKind::Border(BorderTask {
instances,
}),
clear_mode: ClearMode::Transparent,
saved_index: None,
}
}
pub fn new_scaling(
target_kind: RenderTargetKind,
src_task_id: RenderTaskId,
@ -616,6 +640,7 @@ impl RenderTask {
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Glyph(_) |
RenderTaskKind::Border(..) |
RenderTaskKind::Blit(..) => {
UvRectKind::Rect
}
@ -670,6 +695,7 @@ impl RenderTask {
}
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::Blit(..) => {
[0.0; 3]
}
@ -704,6 +730,7 @@ impl RenderTask {
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Glyph(..) => {
panic!("texture handle not supported for this task kind");
@ -776,6 +803,7 @@ impl RenderTask {
target_kind
}
RenderTaskKind::Border(..) |
RenderTaskKind::Picture(..) => {
RenderTargetKind::Color
}
@ -801,6 +829,7 @@ impl RenderTask {
RenderTaskKind::Scaling(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::Glyph(..) => false,
// TODO(gw): For now, we've disabled the shared clip mask
@ -837,6 +866,7 @@ impl RenderTask {
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::Glyph(..) => {
return;
@ -887,6 +917,9 @@ impl RenderTask {
pt.new_level("Scaling".to_owned());
pt.add_item(format!("kind: {:?}", kind));
}
RenderTaskKind::Border(..) => {
pt.new_level("Border".to_owned());
}
RenderTaskKind::Blit(ref task) => {
pt.new_level("Blit".to_owned());
pt.add_item(format!("source: {:?}", task.source));
@ -931,6 +964,7 @@ pub enum RenderTaskCacheKeyKind {
#[allow(dead_code)]
Glyph(GpuGlyphCacheKey),
Picture(PictureCacheKey),
Border(BorderCacheKey),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]

View File

@ -128,6 +128,10 @@ const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
label: "C_Clip",
color: debug_colors::PURPLE,
};
const GPU_TAG_CACHE_BORDER: GpuProfileTag = GpuProfileTag {
label: "C_Border",
color: debug_colors::CORNSILK,
};
const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag {
label: "target init",
color: debug_colors::SLATEGREY,
@ -390,6 +394,53 @@ pub(crate) mod desc {
],
};
pub const BORDER: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[
VertexAttribute {
name: "aPosition",
count: 2,
kind: VertexAttributeKind::F32,
},
],
instance_attributes: &[
VertexAttribute {
name: "aTaskOrigin",
count: 2,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aRect",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aColor0",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aColor1",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aFlags",
count: 1,
kind: VertexAttributeKind::I32,
},
VertexAttribute {
name: "aWidths",
count: 2,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aRadii",
count: 2,
kind: VertexAttributeKind::F32,
},
],
};
pub const CLIP: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[
VertexAttribute {
@ -573,6 +624,7 @@ pub(crate) enum VertexArrayKind {
DashAndDot,
VectorStencil,
VectorCover,
Border,
}
#[derive(Clone, Debug, PartialEq)]
@ -1293,6 +1345,7 @@ pub struct RendererVAOs {
blur_vao: VAO,
clip_vao: VAO,
dash_and_dot_vao: VAO,
border_vao: VAO,
}
/// The renderer is responsible for submitting to the GPU the work prepared by the
@ -1579,6 +1632,8 @@ impl Renderer {
let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
let border_vao =
device.create_vao_with_new_instances(&desc::BORDER, &prim_vao);
let dash_and_dot_vao =
device.create_vao_with_new_instances(&desc::BORDER_CORNER_DASH_AND_DOT, &prim_vao);
let texture_cache_upload_pbo = device.create_pbo();
@ -1734,6 +1789,7 @@ impl Renderer {
blur_vao,
clip_vao,
dash_and_dot_vao,
border_vao,
},
node_data_texture,
local_clip_rects_texture,
@ -3248,9 +3304,36 @@ impl Renderer {
self.device.disable_depth_write();
self.device.set_blend(false);
for rect in &target.clears {
self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, Some(*rect));
}
// Handle any blits to this texture from child tasks.
self.handle_blits(&target.blits, render_tasks);
// Draw any borders for this target.
if !target.border_segments.is_empty() {
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_BORDER);
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
self.shaders.cs_border_segment.bind(
&mut self.device,
&projection,
&mut self.renderer_errors,
);
self.draw_instanced_batch(
&target.border_segments,
VertexArrayKind::Border,
&BatchTextures::no_texture(),
stats,
);
self.device.set_blend(false);
}
// Draw any blurs for this target.
if !target.horizontal_blurs.is_empty() {
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
@ -3853,6 +3936,7 @@ impl Renderer {
self.device.delete_vao(self.vaos.clip_vao);
self.device.delete_vao(self.vaos.blur_vao);
self.device.delete_vao(self.vaos.dash_and_dot_vao);
self.device.delete_vao(self.vaos.border_vao);
#[cfg(feature = "debug_renderer")]
{
@ -4448,6 +4532,7 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao,
VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao,
VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao,
VertexArrayKind::Border => &vaos.border_vao,
}
}
@ -4462,5 +4547,6 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
VertexArrayKind::Blur => &vaos.blur_vao,
VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao,
VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(),
VertexArrayKind::Border => &vaos.border_vao,
}
}

View File

@ -2,13 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AddFont, BlobImageResources, ResourceUpdate, ResourceUpdates};
use api::{AddFont, BlobImageResources, ResourceUpdate};
use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{Epoch, FontInstanceKey, FontKey, FontTemplate};
use api::{Epoch, FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
use api::{ExternalImageData, ExternalImageType};
use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
use api::{GlyphDimensions, GlyphKey, IdNamespace};
use api::{GlyphDimensions, IdNamespace};
use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering};
use api::{TileOffset, TileSize};
use app_units::Au;
@ -23,7 +23,7 @@ use euclid::size2;
use glyph_cache::GlyphCache;
#[cfg(not(feature = "pathfinder"))]
use glyph_cache::GlyphCacheEntry;
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest};
use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::UvRectKind;
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
@ -174,7 +174,7 @@ where
K: Clone + Hash + Eq + Debug,
U: Default,
{
pub fn new() -> ResourceClassCache<K, V, U> {
pub fn new() -> Self {
ResourceClassCache {
resources: FastHashMap::default(),
user_data: Default::default(),
@ -282,7 +282,7 @@ impl BlobImageResources for Resources {
}
}
pub type GlyphDimensionsCache = FastHashMap<GlyphRequest, Option<GlyphDimensions>>;
pub type GlyphDimensionsCache = FastHashMap<(FontInstance, GlyphIndex), Option<GlyphDimensions>>;
pub struct ResourceCache {
cached_glyphs: GlyphCache,
@ -371,14 +371,14 @@ impl ResourceCache {
pub fn update_resources(
&mut self,
updates: ResourceUpdates,
updates: Vec<ResourceUpdate>,
profile_counters: &mut ResourceProfileCounters,
) {
// TODO, there is potential for optimization here, by processing updates in
// bulk rather than one by one (for example by sorting allocations by size or
// in a way that reduces fragmentation in the atlas).
for update in updates.updates {
for update in updates {
match update {
ResourceUpdate::AddImage(img) => {
if let ImageData::Raw(ref bytes) = img.data {
@ -449,7 +449,6 @@ impl ResourceCache {
) {
let FontInstanceOptions {
render_mode,
subpx_dir,
flags,
bg_color,
..
@ -460,7 +459,6 @@ impl ResourceCache {
ColorF::new(0.0, 0.0, 0.0, 1.0),
bg_color,
render_mode,
subpx_dir,
flags,
platform_options,
variations,
@ -510,16 +508,14 @@ impl ResourceCache {
tiling,
);
}
let dirty_rect = Some(descriptor.full_rect());
let resource = ImageResource {
descriptor,
data,
epoch: Epoch(0),
tiling,
dirty_rect: Some(DeviceUintRect::new(
DeviceUintPoint::zero(),
descriptor.size,
)),
dirty_rect,
};
self.resources.image_templates.insert(image_key, resource);
@ -636,9 +632,14 @@ impl ResourceCache {
let needs_upload = self.texture_cache
.request(&entry.as_ref().unwrap().texture_cache_handle, gpu_cache);
if !needs_upload && !needs_update {
return;
}
let dirty_rect = if needs_upload {
// the texture cache entry has been evicted, treat it as all dirty
Some(template.descriptor.full_rect())
} else if needs_update {
template.dirty_rect
} else {
return
};
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
@ -658,7 +659,7 @@ impl ResourceCache {
tile_offset.y as f32 * tile_size as f32,
);
if let Some(dirty) = template.dirty_rect {
if let Some(dirty) = dirty_rect {
if intersect_for_tile(dirty, actual_size, tile_size, tile_offset).is_none() {
// don't bother requesting unchanged tiles
return
@ -678,7 +679,7 @@ impl ResourceCache {
offset,
format: template.descriptor.format,
},
template.dirty_rect,
dirty_rect,
);
}
}
@ -812,15 +813,13 @@ impl ResourceCache {
pub fn get_glyph_dimensions(
&mut self,
font: &FontInstance,
key: &GlyphKey,
glyph_index: GlyphIndex,
) -> Option<GlyphDimensions> {
let key = GlyphRequest::new(font, key);
match self.cached_glyph_dimensions.entry(key.clone()) {
match self.cached_glyph_dimensions.entry((font.clone(), glyph_index)) {
Occupied(entry) => *entry.get(),
Vacant(entry) => *entry.insert(
self.glyph_rasterizer
.get_glyph_dimensions(&key.font, &key.key),
.get_glyph_dimensions(font, glyph_index),
),
}
}

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdates};
use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate};
use api::channel::MsgSender;
use display_list_flattener::build_scene;
use frame_builder::{FrameBuilderConfig, FrameBuilder};
@ -19,7 +19,7 @@ pub enum SceneBuilderRequest {
Transaction {
document_id: DocumentId,
scene: Option<SceneRequest>,
resource_updates: ResourceUpdates,
resource_updates: Vec<ResourceUpdate>,
frame_ops: Vec<FrameMsg>,
render: bool,
},
@ -33,7 +33,7 @@ pub enum SceneBuilderResult {
Transaction {
document_id: DocumentId,
built_scene: Option<BuiltScene>,
resource_updates: ResourceUpdates,
resource_updates: Vec<ResourceUpdate>,
frame_ops: Vec<FrameMsg>,
render: bool,
result_tx: Option<Sender<SceneSwapResult>>,

View File

@ -403,6 +403,7 @@ fn create_prim_shader(
VertexArrayKind::DashAndDot => desc::BORDER_CORNER_DASH_AND_DOT,
VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL,
VertexArrayKind::VectorCover => desc::VECTOR_COVER,
VertexArrayKind::Border => desc::BORDER,
};
let program = device.create_program(name, &prefix, &vertex_descriptor);
@ -464,6 +465,7 @@ pub struct Shaders {
// of these shaders are then used by the primitive shaders.
pub cs_blur_a8: LazilyCompiledShader,
pub cs_blur_rgba8: LazilyCompiledShader,
pub cs_border_segment: LazilyCompiledShader,
// Brush shaders
brush_solid: BrushShader,
@ -711,6 +713,14 @@ impl Shaders {
options.precache_shaders,
)?;
let cs_border_segment = LazilyCompiledShader::new(
ShaderKind::Cache(VertexArrayKind::Border),
"cs_border_segment",
&[],
device,
options.precache_shaders,
)?;
let ps_split_composite = LazilyCompiledShader::new(
ShaderKind::Primitive,
"ps_split_composite",
@ -726,6 +736,7 @@ impl Shaders {
Ok(Shaders {
cs_blur_a8,
cs_blur_rgba8,
cs_border_segment,
brush_solid,
brush_image,
brush_blend,
@ -844,6 +855,7 @@ impl Shaders {
}
self.ps_border_corner.deinit(device);
self.ps_border_edge.deinit(device);
self.cs_border_segment.deinit(device);
self.ps_split_composite.deinit(device);
}
}

View File

@ -12,7 +12,7 @@ use device::{FrameId, Texture};
#[cfg(feature = "pathfinder")]
use euclid::{TypedPoint2D, TypedVector2D};
use gpu_cache::{GpuCache};
use gpu_types::{BlurDirection, BlurInstance};
use gpu_types::{BorderInstance, BlurDirection, BlurInstance};
use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
@ -23,7 +23,7 @@ use profiler::FrameProfileCounters;
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskTree};
use resource_cache::ResourceCache;
use std::{cmp, usize, f32, i32};
use std::{cmp, usize, f32, i32, mem};
use texture_allocator::GuillotineAllocator;
#[cfg(feature = "pathfinder")]
use webrender_api::{DevicePixel, FontRenderMode};
@ -433,6 +433,7 @@ impl RenderTarget for ColorRenderTarget {
}
}
RenderTaskKind::ClipRegion(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::CacheMask(..) => {
panic!("Should not be added to color target!");
}
@ -565,6 +566,7 @@ impl RenderTarget for AlphaRenderTarget {
RenderTaskKind::Readback(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::Blit(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::Glyph(..) => {
panic!("BUG: should not be added to alpha target!");
}
@ -627,6 +629,8 @@ pub struct TextureCacheRenderTarget {
pub horizontal_blurs: Vec<BlurInstance>,
pub blits: Vec<BlitJob>,
pub glyphs: Vec<GlyphJob>,
pub border_segments: Vec<BorderInstance>,
pub clears: Vec<DeviceIntRect>,
}
impl TextureCacheRenderTarget {
@ -636,6 +640,8 @@ impl TextureCacheRenderTarget {
horizontal_blurs: vec![],
blits: vec![],
glyphs: vec![],
border_segments: vec![],
clears: vec![],
}
}
@ -678,6 +684,20 @@ impl TextureCacheRenderTarget {
}
}
}
RenderTaskKind::Border(ref mut task_info) => {
self.clears.push(target_rect.0);
// TODO(gw): It may be better to store the task origin in
// the render task data instead of per instance.
let task_origin = target_rect.0.origin.to_f32();
for instance in &mut task_info.instances {
instance.task_origin = task_origin;
}
let instances = mem::replace(&mut task_info.instances, Vec::new());
self.border_segments.extend(instances);
}
RenderTaskKind::Glyph(ref mut task_info) => {
self.add_glyph_task(task_info, target_rect.0)
}

View File

@ -18,8 +18,8 @@ bitflags = "1.0"
byteorder = "1.2.1"
ipc-channel = {version = "0.10.0", optional = true}
euclid = { version = "0.17", features = ["serde"] }
serde = { version = "=1.0.37", features = ["rc"] }
serde_derive = { version = "=1.0.37", features = ["deserialize_in_place"] }
serde = { version = "=1.0.58", features = ["rc"] }
serde_derive = { version = "=1.0.58", features = ["deserialize_in_place"] }
serde_bytes = "0.10"
time = "0.1"

View File

@ -13,7 +13,7 @@ use std::path::PathBuf;
use std::u32;
use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceUintRect};
use {DeviceUintSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions};
use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData};
use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData};
use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
use {NativeFontHandle, WorldPoint};
@ -21,12 +21,6 @@ pub type TileSize = u16;
/// Documents are rendered in the ascending order of their associated layer values.
pub type DocumentLayer = i8;
/// The resource updates for a given transaction (they must be applied in the same frame).
#[derive(Clone, Deserialize, Serialize)]
pub struct ResourceUpdates {
pub updates: Vec<ResourceUpdate>,
}
#[derive(Clone, Deserialize, Serialize)]
pub enum ResourceUpdate {
AddImage(AddImage),
@ -38,94 +32,6 @@ pub enum ResourceUpdate {
DeleteFontInstance(FontInstanceKey),
}
impl ResourceUpdates {
pub fn new() -> Self {
ResourceUpdates {
updates: Vec::new(),
}
}
pub fn add_image(
&mut self,
key: ImageKey,
descriptor: ImageDescriptor,
data: ImageData,
tiling: Option<TileSize>,
) {
self.updates.push(ResourceUpdate::AddImage(AddImage {
key,
descriptor,
data,
tiling,
}));
}
pub fn update_image(
&mut self,
key: ImageKey,
descriptor: ImageDescriptor,
data: ImageData,
dirty_rect: Option<DeviceUintRect>,
) {
self.updates.push(ResourceUpdate::UpdateImage(UpdateImage {
key,
descriptor,
data,
dirty_rect,
}));
}
pub fn delete_image(&mut self, key: ImageKey) {
self.updates.push(ResourceUpdate::DeleteImage(key));
}
pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec<u8>, index: u32) {
self.updates
.push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index)));
}
pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) {
self.updates
.push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
}
pub fn delete_font(&mut self, key: FontKey) {
self.updates.push(ResourceUpdate::DeleteFont(key));
}
pub fn add_font_instance(
&mut self,
key: FontInstanceKey,
font_key: FontKey,
glyph_size: Au,
options: Option<FontInstanceOptions>,
platform_options: Option<FontInstancePlatformOptions>,
variations: Vec<FontVariation>,
) {
self.updates
.push(ResourceUpdate::AddFontInstance(AddFontInstance {
key,
font_key,
glyph_size,
options,
platform_options,
variations,
}));
}
pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
self.updates.push(ResourceUpdate::DeleteFontInstance(key));
}
pub fn merge(&mut self, mut other: ResourceUpdates) {
self.updates.append(&mut other.updates);
}
pub fn clear(&mut self) {
self.updates.clear()
}
}
/// A Transaction is a group of commands to apply atomically to a document.
///
/// This mechanism ensures that:
@ -142,7 +48,7 @@ pub struct Transaction {
payloads: Vec<Payload>,
// Resource updates are applied after scene building.
resource_updates: ResourceUpdates,
pub resource_updates: Vec<ResourceUpdate>,
// If true the transaction is piped through the scene building thread, if false
// it will be applied directly on the render backend.
@ -156,7 +62,7 @@ impl Transaction {
Transaction {
scene_ops: Vec::new(),
frame_ops: Vec::new(),
resource_updates: ResourceUpdates::new(),
resource_updates: Vec::new(),
payloads: Vec::new(),
use_scene_builder_thread: false, // TODO: make this true by default.
generate_frame: false,
@ -178,7 +84,7 @@ impl Transaction {
!self.generate_frame &&
self.scene_ops.is_empty() &&
self.frame_ops.is_empty() &&
self.resource_updates.updates.is_empty()
self.resource_updates.is_empty()
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
@ -257,8 +163,8 @@ impl Transaction {
self.payloads.push(Payload { epoch, pipeline_id, display_list_data });
}
pub fn update_resources(&mut self, resources: ResourceUpdates) {
self.resource_updates.merge(resources);
pub fn update_resources(&mut self, resources: Vec<ResourceUpdate>) {
self.merge(resources);
}
pub fn set_window_parameters(
@ -353,6 +259,86 @@ impl Transaction {
self.payloads,
)
}
pub fn add_image(
&mut self,
key: ImageKey,
descriptor: ImageDescriptor,
data: ImageData,
tiling: Option<TileSize>,
) {
self.resource_updates.push(ResourceUpdate::AddImage(AddImage {
key,
descriptor,
data,
tiling,
}));
}
pub fn update_image(
&mut self,
key: ImageKey,
descriptor: ImageDescriptor,
data: ImageData,
dirty_rect: Option<DeviceUintRect>,
) {
self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage {
key,
descriptor,
data,
dirty_rect,
}));
}
pub fn delete_image(&mut self, key: ImageKey) {
self.resource_updates.push(ResourceUpdate::DeleteImage(key));
}
pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec<u8>, index: u32) {
self.resource_updates
.push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index)));
}
pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) {
self.resource_updates
.push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
}
pub fn delete_font(&mut self, key: FontKey) {
self.resource_updates.push(ResourceUpdate::DeleteFont(key));
}
pub fn add_font_instance(
&mut self,
key: FontInstanceKey,
font_key: FontKey,
glyph_size: Au,
options: Option<FontInstanceOptions>,
platform_options: Option<FontInstancePlatformOptions>,
variations: Vec<FontVariation>,
) {
self.resource_updates
.push(ResourceUpdate::AddFontInstance(AddFontInstance {
key,
font_key,
glyph_size,
options,
platform_options,
variations,
}));
}
pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
self.resource_updates.push(ResourceUpdate::DeleteFontInstance(key));
}
pub fn merge(&mut self, mut other: Vec<ResourceUpdate>) {
self.resource_updates.append(&mut other);
}
pub fn clear(&mut self) {
self.resource_updates.clear()
}
}
/// Represents a transaction in the format sent through the channel.
@ -360,7 +346,7 @@ impl Transaction {
pub struct TransactionMsg {
pub scene_ops: Vec<SceneMsg>,
pub frame_ops: Vec<FrameMsg>,
pub resource_updates: ResourceUpdates,
pub resource_updates: Vec<ResourceUpdate>,
pub generate_frame: bool,
pub use_scene_builder_thread: bool,
}
@ -370,7 +356,7 @@ impl TransactionMsg {
!self.generate_frame &&
self.scene_ops.is_empty() &&
self.frame_ops.is_empty() &&
self.resource_updates.updates.is_empty()
self.resource_updates.is_empty()
}
// TODO: We only need this for a few RenderApi methods which we should remove.
@ -378,7 +364,7 @@ impl TransactionMsg {
TransactionMsg {
scene_ops: Vec::new(),
frame_ops: vec![msg],
resource_updates: ResourceUpdates::new(),
resource_updates: Vec::new(),
generate_frame: false,
use_scene_builder_thread: false,
}
@ -388,7 +374,7 @@ impl TransactionMsg {
TransactionMsg {
scene_ops: vec![msg],
frame_ops: Vec::new(),
resource_updates: ResourceUpdates::new(),
resource_updates: Vec::new(),
generate_frame: false,
use_scene_builder_thread: false,
}
@ -598,11 +584,11 @@ pub enum DebugCommand {
#[derive(Clone, Deserialize, Serialize)]
pub enum ApiMsg {
/// Add/remove/update images and fonts.
UpdateResources(ResourceUpdates),
UpdateResources(Vec<ResourceUpdate>),
/// Gets the glyph dimensions
GetGlyphDimensions(
FontInstanceKey,
Vec<GlyphKey>,
Vec<GlyphIndex>,
MsgSender<Vec<Option<GlyphDimensions>>>,
),
/// Gets the glyph indices from a string
@ -811,10 +797,10 @@ impl RenderApi {
pub fn get_glyph_dimensions(
&self,
font: FontInstanceKey,
glyph_keys: Vec<GlyphKey>,
glyph_indices: Vec<GlyphIndex>,
) -> Vec<Option<GlyphDimensions>> {
let (tx, rx) = channel::msg_channel().unwrap();
let msg = ApiMsg::GetGlyphDimensions(font, glyph_keys, tx);
let msg = ApiMsg::GetGlyphDimensions(font, glyph_indices, tx);
self.api_sender.send(msg).unwrap();
rx.recv().unwrap()
}
@ -835,8 +821,8 @@ impl RenderApi {
}
/// Add/remove/update resources such as images and fonts.
pub fn update_resources(&self, resources: ResourceUpdates) {
if resources.updates.is_empty() {
pub fn update_resources(&self, resources: Vec<ResourceUpdate>) {
if resources.is_empty() {
return;
}
self.api_sender

View File

@ -372,7 +372,7 @@ pub struct BorderSide {
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Hash, Eq)]
pub enum BorderStyle {
None = 0,
Solid = 1,

View File

@ -94,37 +94,7 @@ pub enum FontRenderMode {
Subpixel,
}
#[repr(u32)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
pub enum SubpixelDirection {
None = 0,
Horizontal,
Vertical,
}
impl FontRenderMode {
// Skia quantizes subpixel offsets into 1/4 increments.
// Given the absolute position, return the quantized increment
fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset {
// Following the conventions of Gecko and Skia, we want
// to quantize the subpixel position, such that abs(pos) gives:
// [0.0, 0.125) -> Zero
// [0.125, 0.375) -> Quarter
// [0.375, 0.625) -> Half
// [0.625, 0.875) -> ThreeQuarters,
// [0.875, 1.0) -> Zero
// The unit tests below check for this.
let apos = ((pos - pos.floor()) * 8.0) as i32;
match apos {
0 | 7 => SubpixelOffset::Zero,
1...2 => SubpixelOffset::Quarter,
3...4 => SubpixelOffset::Half,
5...6 => SubpixelOffset::ThreeQuarters,
_ => unreachable!("bug: unexpected quantized result"),
}
}
// Combine two font render modes such that the lesser amount of AA limits the AA of the result.
pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode {
match (self, other) {
@ -134,36 +104,6 @@ impl FontRenderMode {
}
}
impl SubpixelDirection {
// Limit the subpixel direction to what is supported by the render mode.
pub fn limit_by(self, render_mode: FontRenderMode) -> SubpixelDirection {
match render_mode {
FontRenderMode::Mono => SubpixelDirection::None,
FontRenderMode::Alpha | FontRenderMode::Subpixel => self,
}
}
}
#[repr(u8)]
#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum SubpixelOffset {
Zero = 0,
Quarter = 1,
Half = 2,
ThreeQuarters = 3,
}
impl Into<f64> for SubpixelOffset {
fn into(self) -> f64 {
match self {
SubpixelOffset::Zero => 0.0,
SubpixelOffset::Quarter => 0.25,
SubpixelOffset::Half => 0.5,
SubpixelOffset::ThreeQuarters => 0.75,
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialOrd, Deserialize, Serialize)]
pub struct FontVariation {
@ -222,6 +162,7 @@ bitflags! {
const TRANSPOSE = 1 << 4;
const FLIP_X = 1 << 5;
const FLIP_Y = 1 << 6;
const SUBPIXEL_POSITION = 1 << 7;
// Windows flags
const FORCE_GDI = 1 << 16;
@ -233,23 +174,25 @@ bitflags! {
const FORCE_AUTOHINT = 1 << 16;
const NO_AUTOHINT = 1 << 17;
const VERTICAL_LAYOUT = 1 << 18;
const LCD_VERTICAL = 1 << 19;
}
}
impl Default for FontInstanceFlags {
#[cfg(target_os = "windows")]
fn default() -> FontInstanceFlags {
FontInstanceFlags::empty()
FontInstanceFlags::SUBPIXEL_POSITION
}
#[cfg(target_os = "macos")]
fn default() -> FontInstanceFlags {
FontInstanceFlags::SUBPIXEL_POSITION |
FontInstanceFlags::FONT_SMOOTHING
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
fn default() -> FontInstanceFlags {
FontInstanceFlags::empty()
FontInstanceFlags::SUBPIXEL_POSITION
}
}
@ -258,7 +201,6 @@ impl Default for FontInstanceFlags {
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct FontInstanceOptions {
pub render_mode: FontRenderMode,
pub subpx_dir: SubpixelDirection,
pub flags: FontInstanceFlags,
/// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel,
/// the text will be rendered with bg_color.r/g/b as an opaque estimated
@ -270,7 +212,6 @@ impl Default for FontInstanceOptions {
fn default() -> FontInstanceOptions {
FontInstanceOptions {
render_mode: FontRenderMode::Subpixel,
subpx_dir: SubpixelDirection::Horizontal,
flags: Default::default(),
bg_color: ColorU::new(0, 0, 0, 0),
}
@ -358,32 +299,6 @@ impl FontInstanceKey {
}
}
#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)]
pub struct GlyphKey {
pub index: u32,
pub subpixel_offset: SubpixelOffset,
}
impl GlyphKey {
pub fn new(
index: u32,
point: LayoutPoint,
render_mode: FontRenderMode,
subpx_dir: SubpixelDirection,
) -> GlyphKey {
let pos = match subpx_dir {
SubpixelDirection::None => 0.0,
SubpixelDirection::Horizontal => point.x,
SubpixelDirection::Vertical => point.y,
};
GlyphKey {
index,
subpixel_offset: render_mode.subpixel_quantize_offset(pos),
}
}
}
pub type GlyphIndex = u32;
#[repr(C)]
@ -393,52 +308,3 @@ pub struct GlyphInstance {
pub point: LayoutPoint,
}
#[cfg(test)]
mod test {
use super::{FontRenderMode, SubpixelOffset};
#[test]
fn test_subpx_quantize() {
let rm = FontRenderMode::Subpixel;
assert_eq!(rm.subpixel_quantize_offset(0.0), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(-0.0), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.1), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.01), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.05), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.12), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.124), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.125), SubpixelOffset::Quarter);
assert_eq!(rm.subpixel_quantize_offset(0.2), SubpixelOffset::Quarter);
assert_eq!(rm.subpixel_quantize_offset(0.25), SubpixelOffset::Quarter);
assert_eq!(rm.subpixel_quantize_offset(0.33), SubpixelOffset::Quarter);
assert_eq!(rm.subpixel_quantize_offset(0.374), SubpixelOffset::Quarter);
assert_eq!(rm.subpixel_quantize_offset(0.375), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(0.4), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(0.5), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(0.58), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(0.624), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(0.625), SubpixelOffset::ThreeQuarters);
assert_eq!(rm.subpixel_quantize_offset(0.67), SubpixelOffset::ThreeQuarters);
assert_eq!(rm.subpixel_quantize_offset(0.7), SubpixelOffset::ThreeQuarters);
assert_eq!(rm.subpixel_quantize_offset(0.78), SubpixelOffset::ThreeQuarters);
assert_eq!(rm.subpixel_quantize_offset(0.874), SubpixelOffset::ThreeQuarters);
assert_eq!(rm.subpixel_quantize_offset(0.875), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.89), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.91), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.967), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(0.999), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(-1.0), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(1.0), SubpixelOffset::Zero);
assert_eq!(rm.subpixel_quantize_offset(1.5), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(-1.625), SubpixelOffset::Half);
assert_eq!(rm.subpixel_quantize_offset(-4.33), SubpixelOffset::ThreeQuarters);
}
}

View File

@ -6,7 +6,8 @@ extern crate serde_bytes;
use font::{FontInstanceKey, FontKey, FontTemplate};
use std::sync::Arc;
use {DevicePoint, DeviceUintRect, DeviceUintSize, IdNamespace, TileOffset, TileSize};
use {DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use {IdNamespace, TileOffset, TileSize};
use euclid::size2;
#[repr(C)]
@ -106,6 +107,13 @@ impl ImageDescriptor {
pub fn compute_total_size(&self) -> u32 {
self.compute_stride() * self.size.height
}
pub fn full_rect(&self) -> DeviceUintRect {
DeviceUintRect::new(
DeviceUintPoint::zero(),
self.size,
)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]

View File

@ -92,6 +92,8 @@ pub type DevicePixelScale = TypedScale<f32, WorldPixel, DevicePixel>;
/// Scaling ratio from layout to world. Used for cases where we know the layout
/// is in world space, or specifically want to treat it this way.
pub type LayoutToWorldScale = TypedScale<f32, LayoutPixel, WorldPixel>;
/// A complete scaling ratio from layout space to device pixel space.
pub type LayoutToDeviceScale = TypedScale<f32, LayoutPixel, DevicePixel>;
pub type LayoutTransform = TypedTransform3D<f32, LayoutPixel, LayoutPixel>;
pub type LayoutToScrollTransform = TypedTransform3D<f32, LayoutPixel, ScrollLayerPixel>;

View File

@ -1 +1 @@
bb354abbf84602d3d8357c63c4f0b1139ec4deb1
63c71ca9bbe4dec0ebc9c9bc8ab65b06a6b40641

View File

@ -111,8 +111,8 @@ impl JsonFrameWriter {
file.write_all(b"\n").unwrap();
}
fn update_resources(&mut self, updates: &ResourceUpdates) {
for update in &updates.updates {
fn update_resources(&mut self, updates: &[ResourceUpdate]) {
for update in updates {
match *update {
ResourceUpdate::AddImage(ref img) => {
let stride = img.descriptor.stride.unwrap_or(

View File

@ -30,7 +30,9 @@ fn rect<T: Copy, U>(x: T, y: T, width: T, height: T) -> TypedRect<T, U> {
}
impl<'a> RawtestHarness<'a> {
pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: &'a Receiver<NotifierEvent>) -> Self {
pub fn new(wrench: &'a mut Wrench,
window: &'a mut WindowWrapper,
rx: &'a Receiver<NotifierEvent>) -> Self {
RawtestHarness {
wrench,
rx,
@ -45,6 +47,7 @@ impl<'a> RawtestHarness<'a> {
self.test_blob_update_epoch_test();
self.test_tile_decomposition();
self.test_very_large_blob();
self.test_offscreen_blob();
self.test_save_restore();
self.test_capture();
self.test_zero_height_window();
@ -61,13 +64,13 @@ impl<'a> RawtestHarness<'a> {
epoch: &mut Epoch,
layout_size: LayoutSize,
builder: DisplayListBuilder,
resources: Option<ResourceUpdates>
resources: &[ResourceUpdate]
) {
let mut txn = Transaction::new();
let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0));
txn.use_scene_builder_thread();
if let Some(resources) = resources {
txn.update_resources(resources);
if !resources.is_empty() {
txn.resource_updates = resources.to_vec();
}
txn.set_display_list(
*epoch,
@ -82,14 +85,15 @@ impl<'a> RawtestHarness<'a> {
self.wrench.api.send_transaction(self.wrench.document_id, txn);
}
fn test_tile_decomposition(&mut self) {
println!("\ttile decomposition...");
// This exposes a crash in tile decomposition
let layout_size = LayoutSize::new(800., 800.);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
let blob_img = self.wrench.api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img,
ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
@ -112,16 +116,16 @@ impl<'a> RawtestHarness<'a> {
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
self.rx.recv().unwrap();
self.wrench.render();
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
resources = ResourceUpdates::new();
resources.delete_image(blob_img);
self.wrench.api.update_resources(resources);
txn = Transaction::new();
txn.delete_image(blob_img);
self.wrench.api.update_resources(txn.resource_updates);
}
fn test_very_large_blob(&mut self) {
@ -140,10 +144,10 @@ impl<'a> RawtestHarness<'a> {
// This exposes a crash in tile decomposition
let layout_size = LayoutSize::new(800., 800.);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
let blob_img = self.wrench.api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img,
ImageDescriptor::new(1510, 111256, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
@ -172,7 +176,7 @@ impl<'a> RawtestHarness<'a> {
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let called = Arc::new(AtomicIsize::new(0));
let called_inner = Arc::clone(&called);
@ -188,23 +192,149 @@ impl<'a> RawtestHarness<'a> {
// make sure things are in the right spot
assert!(
pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4] == 255 &&
pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 1] == 255 &&
pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 2] == 255 &&
pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 3] == 255
pixels[(148 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4] == 255 &&
pixels[(148 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4 + 1] == 255 &&
pixels[(148 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4 + 2] == 255 &&
pixels[(148 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4 + 3] == 255
);
assert!(
pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4] == 50 &&
pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 1] == 50 &&
pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 2] == 150 &&
pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 3] == 255
pixels[(132 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4] == 50 &&
pixels[(132 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4 + 1] == 50 &&
pixels[(132 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4 + 2] == 150 &&
pixels[(132 +
(window_rect.size.height as usize - 148) *
window_rect.size.width as usize) * 4 + 3] == 255
);
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
resources = ResourceUpdates::new();
resources.delete_image(blob_img);
self.wrench.api.update_resources(resources);
txn = Transaction::new();
txn.delete_image(blob_img);
self.wrench.api.update_resources(txn.resource_updates);
}
fn test_offscreen_blob(&mut self) {
println!("\toffscreen blob update.");
assert_eq!(self.wrench.device_pixel_ratio, 1.);
let window_size = self.window.get_inner_size();
let test_size = DeviceUintSize::new(800, 800);
let window_rect = DeviceUintRect::new(
DeviceUintPoint::new(0, window_size.height - test_size.height),
test_size,
);
// This exposes a crash in tile decomposition
let mut txn = Transaction::new();
let layout_size = LayoutSize::new(800., 800.);
let blob_img = self.wrench.api.generate_image_key();
txn.add_image(
blob_img,
ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.));
let image_size = size(1510., 1510.);
// setup some malicious image size parameters
builder.push_image(
&info,
image_size,
image_size,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let original_pixels = self.render_and_get_pixels(window_rect);
let mut epoch = Epoch(1);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(-10000., 0.0, 1510., 1510.));
let image_size = size(1510., 1510.);
// setup some malicious image size parameters
builder.push_image(
&info,
image_size,
image_size,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
self.submit_dl(&mut epoch, layout_size, builder, &[]);
let _offscreen_pixels = self.render_and_get_pixels(window_rect);
let mut txn = Transaction::new();
txn.update_image(
blob_img,
ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(rect(10, 10, 100, 100)),
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.));
let image_size = size(1510., 1510.);
// setup some malicious image size parameters
builder.push_image(
&info,
image_size,
image_size,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
blob_img,
);
let mut epoch = Epoch(2);
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels = self.render_and_get_pixels(window_rect);
assert!(pixels == original_pixels);
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
txn = Transaction::new();
txn.delete_image(blob_img);
self.wrench.api.update_resources(txn.resource_updates);
}
fn test_retained_blob_images_test(&mut self) {
@ -219,12 +349,12 @@ impl<'a> RawtestHarness<'a> {
test_size,
);
let layout_size = LayoutSize::new(400., 400.);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
{
let api = &self.wrench.api;
blob_img = api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
@ -247,7 +377,7 @@ impl<'a> RawtestHarness<'a> {
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let called = Arc::new(AtomicIsize::new(0));
let called_inner = Arc::clone(&called);
@ -274,7 +404,9 @@ impl<'a> RawtestHarness<'a> {
blob_img,
);
self.submit_dl(&mut epoch, layout_size, builder, None);
txn.resource_updates.clear();
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_second = self.render_and_get_pixels(window_rect);
@ -300,19 +432,19 @@ impl<'a> RawtestHarness<'a> {
test_size,
);
let layout_size = LayoutSize::new(400., 400.);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
let (blob_img, blob_img2) = {
let api = &self.wrench.api;
blob_img = api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
blob_img2 = api.generate_image_key();
resources.add_image(
txn.add_image(
blob_img2,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(80, 50, 150, 255))),
@ -364,19 +496,19 @@ impl<'a> RawtestHarness<'a> {
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_first = self.render_and_get_pixels(window_rect);
// update and redraw both images
let mut resources = ResourceUpdates::new();
resources.update_image(
let mut txn = Transaction::new();
txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
resources.update_image(
txn.update_image(
blob_img2,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(59, 50, 150, 255))),
@ -385,13 +517,13 @@ impl<'a> RawtestHarness<'a> {
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
push_images(&mut builder);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_second = self.render_and_get_pixels(window_rect);
// only update the first image
let mut resources = ResourceUpdates::new();
resources.update_image(
let mut txn = Transaction::new();
txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))),
@ -400,7 +532,7 @@ impl<'a> RawtestHarness<'a> {
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
push_images(&mut builder);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_third = self.render_and_get_pixels(window_rect);
// the first image should be requested 3 times
@ -420,11 +552,11 @@ impl<'a> RawtestHarness<'a> {
test_size,
);
let layout_size = LayoutSize::new(400., 400.);
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
let blob_img = {
let img = self.wrench.api.generate_image_key();
resources.add_image(
txn.add_image(
img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
@ -448,12 +580,12 @@ impl<'a> RawtestHarness<'a> {
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_first = self.render_and_get_pixels(window_rect);
// draw the blob image a second time after updating it with the same color
let mut resources = ResourceUpdates::new();
resources.update_image(
let mut txn = Transaction::new();
txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
@ -472,12 +604,12 @@ impl<'a> RawtestHarness<'a> {
blob_img,
);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_second = self.render_and_get_pixels(window_rect);
// draw the blob image a third time after updating it with a different color
let mut resources = ResourceUpdates::new();
resources.update_image(
let mut txn = Transaction::new();
txn.update_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))),
@ -496,7 +628,7 @@ impl<'a> RawtestHarness<'a> {
blob_img,
);
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_third = self.render_and_get_pixels(window_rect);
assert!(pixels_first == pixels_second);
@ -537,13 +669,13 @@ impl<'a> RawtestHarness<'a> {
);
builder.push_clip_id(clip);
builder.push_rect(&PrimitiveInfo::new(rect(110., 110., 50., 50.)),
ColorF::new(0.0, 1.0, 0.0, 1.0));
ColorF::new(0.0, 1.0, 0.0, 1.0));
builder.push_shadow(&PrimitiveInfo::new(rect(100., 100., 100., 100.)),
Shadow {
offset: LayoutVector2D::new(1.0, 1.0),
blur_radius: 1.0,
color: ColorF::new(0.0, 0.0, 0.0, 1.0),
});
Shadow {
offset: LayoutVector2D::new(1.0, 1.0),
blur_radius: 1.0,
color: ColorF::new(0.0, 0.0, 0.0, 1.0),
});
builder.push_line(&PrimitiveInfo::new(rect(110., 110., 50., 2.)),
0.0, LineOrientation::Horizontal,
&ColorF::new(0.0, 0.0, 0.0, 1.0), LineStyle::Solid);
@ -567,7 +699,9 @@ impl<'a> RawtestHarness<'a> {
builder.pop_clip_id();
self.submit_dl(&mut Epoch(0), layout_size, builder, None);
let txn = Transaction::new();
self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates);
self.render_and_get_pixels(window_rect)
};
@ -590,9 +724,9 @@ impl<'a> RawtestHarness<'a> {
// 1. render some scene
let mut resources = ResourceUpdates::new();
let mut txn = Transaction::new();
let image = self.wrench.api.generate_image_key();
resources.add_image(
txn.add_image(
image,
ImageDescriptor::new(1, 1, ImageFormat::BGRA8, true, false),
ImageData::new(vec![0xFF, 0, 0, 0xFF]),
@ -668,7 +802,8 @@ impl<'a> RawtestHarness<'a> {
let doc_id = self.wrench.api.add_document(window_size, 1);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0)));
let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(),
LayoutSize::new(100.0, 100.0)));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
let mut txn = Transaction::new();
@ -709,7 +844,7 @@ impl<'a> RawtestHarness<'a> {
info.tag = Some((0, 2));
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
let make_rounded_complex_clip = | rect: &LayoutRect, radius: f32 | -> ComplexClipRegion {
let make_rounded_complex_clip = |rect: &LayoutRect, radius: f32| -> ComplexClipRegion {
ComplexClipRegion::new(
*rect,
BorderRadius::uniform_size(LayoutSize::new(radius, radius)),
@ -743,13 +878,14 @@ impl<'a> RawtestHarness<'a> {
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, None);
let txn = Transaction::new();
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
// We render to ensure that the hit tester is up to date with the current scene.
self.rx.recv().unwrap();
self.wrench.render();
let hit_test = | point: WorldPoint | -> HitTestResult {
let hit_test = |point: WorldPoint| -> HitTestResult {
self.wrench.api.hit_test(
self.wrench.document_id,
None,
@ -758,7 +894,7 @@ impl<'a> RawtestHarness<'a> {
)
};
let assert_hit_test = | point: WorldPoint, tags: Vec<ItemTag> | {
let assert_hit_test = |point: WorldPoint, tags: Vec<ItemTag>| {
let result = hit_test(point);
assert_eq!(result.items.len(), tags.len());
@ -779,7 +915,7 @@ impl<'a> RawtestHarness<'a> {
// The middle of the normal rectangle should be hit.
assert_hit_test(WorldPoint::new(150., 50.), vec![(0, 2), (0, 1)]);
let test_rounded_rectangle = | point: WorldPoint, size: WorldSize, tag: ItemTag | {
let test_rounded_rectangle = |point: WorldPoint, size: WorldSize, tag: ItemTag| {
// The cut out corners of the rounded rectangle should not be hit.
let top_left = point + WorldVector2D::new(5., 5.);
let bottom_right = point + size.to_vector() - WorldVector2D::new(5., 5.);

View File

@ -87,8 +87,8 @@ impl RonFrameWriter {
file.write_all(b"\n").unwrap();
}
fn update_resources(&mut self, updates: &ResourceUpdates) {
for update in &updates.updates {
fn update_resources(&mut self, updates: &[ResourceUpdate]) {
for update in updates {
match *update {
ResourceUpdate::AddImage(ref img) => {
let bytes = match img.data {

View File

@ -280,7 +280,6 @@ impl Wrench {
&mut self,
font_key: FontKey,
instance_key: FontInstanceKey,
render_mode: Option<FontRenderMode>,
text: &str,
size: Au,
origin: LayoutPoint,
@ -294,20 +293,8 @@ impl Wrench {
.filter_map(|idx| *idx)
.collect();
let render_mode = render_mode.unwrap_or(<FontInstanceOptions as Default>::default().render_mode);
let subpx_dir = SubpixelDirection::Horizontal.limit_by(render_mode);
// Retrieve the metrics for each glyph.
let mut keys = Vec::new();
for glyph_index in &indices {
keys.push(GlyphKey::new(
*glyph_index,
LayoutPoint::zero(),
render_mode,
subpx_dir,
));
}
let metrics = self.api.get_glyph_dimensions(instance_key, keys);
let metrics = self.api.get_glyph_dimensions(instance_key, indices.clone());
let mut bounding_rect = LayoutRect::zero();
let mut positions = Vec::new();
@ -385,9 +372,9 @@ impl Wrench {
#[cfg(target_os = "windows")]
pub fn font_key_from_native_handle(&mut self, descriptor: &NativeFontHandle) -> FontKey {
let key = self.api.generate_font_key();
let mut resources = ResourceUpdates::new();
resources.add_native_font(key, descriptor.clone());
self.api.update_resources(resources);
let mut txn = Transaction::new();
txn.add_native_font(key, descriptor.clone());
self.api.update_resources(txn.resource_updates);
key
}
@ -456,9 +443,9 @@ impl Wrench {
pub fn font_key_from_bytes(&mut self, bytes: Vec<u8>, index: u32) -> FontKey {
let key = self.api.generate_font_key();
let mut update = ResourceUpdates::new();
update.add_raw_font(key, bytes, index);
self.api.update_resources(update);
let mut txn = Transaction::new();
txn.add_raw_font(key, bytes, index);
self.api.update_resources(txn.resource_updates);
key
}
@ -470,7 +457,7 @@ impl Wrench {
bg_color: Option<ColorU>,
) -> FontInstanceKey {
let key = self.api.generate_font_instance_key();
let mut update = ResourceUpdates::new();
let mut txn = Transaction::new();
let mut options: FontInstanceOptions = Default::default();
options.flags |= flags;
if let Some(render_mode) = render_mode {
@ -479,16 +466,16 @@ impl Wrench {
if let Some(bg_color) = bg_color {
options.bg_color = bg_color;
}
update.add_font_instance(key, font_key, size, Some(options), None, Vec::new());
self.api.update_resources(update);
txn.add_font_instance(key, font_key, size, Some(options), None, Vec::new());
self.api.update_resources(txn.resource_updates);
key
}
#[allow(dead_code)]
pub fn delete_font_instance(&mut self, key: FontInstanceKey) {
let mut update = ResourceUpdates::new();
update.delete_font_instance(key);
self.api.update_resources(update);
let mut txn = Transaction::new();
txn.delete_font_instance(key);
self.api.update_resources(txn.resource_updates);
}
pub fn update(&mut self, dim: DeviceUintSize) {

View File

@ -230,17 +230,17 @@ impl YamlFrameReader {
}
pub fn deinit(mut self, wrench: &mut Wrench) {
let mut updates = ResourceUpdates::new();
let mut txn = Transaction::new();
for (_, font_instance) in self.font_instances.drain() {
updates.delete_font_instance(font_instance);
txn.delete_font_instance(font_instance);
}
for (_, font) in self.fonts.drain() {
updates.delete_font(font);
txn.delete_font(font);
}
wrench.api.update_resources(updates);
wrench.api.update_resources(txn.resource_updates);
}
pub fn yaml_path(&self) -> &PathBuf {
@ -511,9 +511,9 @@ impl YamlFrameReader {
};
let tiling = tiling.map(|tile_size| tile_size as u16);
let image_key = wrench.api.generate_image_key();
let mut resources = ResourceUpdates::new();
resources.add_image(image_key, descriptor, image_data, tiling);
wrench.api.update_resources(resources);
let mut txn = Transaction::new();
txn.add_image(image_key, descriptor, image_data, tiling);
wrench.api.update_resources(txn.resource_updates);
let val = (
image_key,
LayoutSize::new(descriptor.size.width as f32, descriptor.size.height as f32),
@ -1210,7 +1210,6 @@ impl YamlFrameReader {
let (glyph_indices, glyph_positions, bounds) = wrench.layout_simple_ascii(
font_key,
font_instance_key,
self.font_render_mode,
text,
size,
origin,

View File

@ -503,8 +503,8 @@ impl YamlFrameWriter {
scene.finish_display_list(self.pipeline_id.unwrap(), dl);
}
fn update_resources(&mut self, updates: &ResourceUpdates) {
for update in &updates.updates {
fn update_resources(&mut self, updates: &[ResourceUpdate]) {
for update in updates {
match *update {
ResourceUpdate::AddImage(ref img) => {
if let Some(ref data) = self.images.get(&img.key) {
@ -545,7 +545,8 @@ impl YamlFrameWriter {
data.path = None;
data.bytes = Some((**bytes).clone());
} else {
// Other existing image types only make sense within the gecko integration.
// Other existing image types only make sense
// within the gecko integration.
println!(
"Wrench only supports updating buffer images ({}).",
"ignoring update command"