Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CanvasRenderingContext2d.fillText #26697

Merged
merged 13 commits into from Jun 12, 2020
Next

Check for valid arguments before processing canvas.fillText

  • Loading branch information
utsavoza committed Jun 10, 2020
commit 726f7d72092d5b8908cc8d8fdefe51ebea8dadf8

Some generated files are not rendered by default. Learn more.

@@ -22,6 +22,7 @@ canvas_traits = { path = "../canvas_traits" }
crossbeam-channel = "0.4"
cssparser = "0.27"
euclid = "0.20"
font-kit = "0.7"
fnv = "1.0"
gleam = "0.11"
half = "1"
@@ -30,7 +31,7 @@ log = "0.4"
lyon_geom = "0.14"
num-traits = "0.2"
pixels = { path = "../pixels" }
raqote = "0.8"
raqote = { version = "0.8", features = ["text"] }
servo_config = { path = "../config" }
sparkle = "0.1.24"
# NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms!
@@ -362,7 +362,7 @@ pub enum Filter {

pub struct CanvasData<'a> {
backend: Box<dyn Backend>,
drawtarget: Box<dyn GenericDrawTarget>,
draw_target: Box<dyn GenericDrawTarget>,
path_state: Option<PathState>,
state: CanvasPaintState<'a>,
saved_states: Vec<CanvasPaintState<'a>>,
@@ -372,7 +372,7 @@ pub struct CanvasData<'a> {
old_image_key: Option<webrender_api::ImageKey>,
/// An old webrender image key that can be deleted when the current epoch ends.
very_old_image_key: Option<webrender_api::ImageKey>,
pub canvas_id: CanvasId,
_canvas_id: CanvasId,
This conversation was marked as resolved by utsavoza

This comment has been minimized.

Copy link
@jdm

jdm Jun 11, 2020

Member

Is this CanvasId field necessary?

This comment has been minimized.

Copy link
@utsavoza

utsavoza Jun 12, 2020

Author Contributor

Hmm, I don't think so. We can probably add it back whenever we might need it.

}

fn create_backend() -> Box<dyn Backend> {
@@ -390,15 +390,15 @@ impl<'a> CanvasData<'a> {
let draw_target = backend.create_drawtarget(size);
CanvasData {
backend,
drawtarget: draw_target,
draw_target,
path_state: None,
state: CanvasPaintState::new(antialias),
saved_states: vec![],
webrender_api,
image_key: None,
old_image_key: None,
very_old_image_key: None,
canvas_id: canvas_id,
_canvas_id: canvas_id,
}
}

@@ -440,7 +440,7 @@ impl<'a> CanvasData<'a> {
// TODO(pylbrecht) pass another closure for raqote
self.draw_with_shadow(&rect, writer);
} else {
writer(&mut *self.drawtarget);
writer(&mut *self.draw_target);
}
}

@@ -451,8 +451,8 @@ impl<'a> CanvasData<'a> {
pub fn restore_context_state(&mut self) {
if let Some(state) = self.saved_states.pop() {
let _ = mem::replace(&mut self.state, state);
self.drawtarget.set_transform(&self.state.transform);
self.drawtarget.pop_clip();
self.draw_target.set_transform(&self.state.transform);
self.draw_target.pop_clip();
}
}

@@ -513,7 +513,7 @@ impl<'a> CanvasData<'a> {
);
});
} else {
self.drawtarget.fill_rect(
self.draw_target.fill_rect(
&draw_rect,
self.state.fill_style.clone(),
Some(&self.state.draw_options),
@@ -522,7 +522,7 @@ impl<'a> CanvasData<'a> {
}

pub fn clear_rect(&mut self, rect: &Rect<f32>) {
self.drawtarget.clear_rect(rect);
self.draw_target.clear_rect(rect);
}

pub fn stroke_rect(&mut self, rect: &Rect<f32>) {
@@ -542,15 +542,15 @@ impl<'a> CanvasData<'a> {
} else if rect.size.width == 0. || rect.size.height == 0. {
let mut stroke_opts = self.state.stroke_opts.clone();
stroke_opts.set_line_cap(LineCapStyle::Butt);
self.drawtarget.stroke_line(
self.draw_target.stroke_line(
rect.origin,
rect.bottom_right(),
self.state.stroke_style.clone(),
&stroke_opts,
&self.state.draw_options,
);
} else {
self.drawtarget.stroke_rect(
self.draw_target.stroke_rect(
rect,
self.state.stroke_style.clone(),
&self.state.stroke_opts,
@@ -572,7 +572,7 @@ impl<'a> CanvasData<'a> {
// If there's no record of any path yet, create a new builder in user-space.
if self.path_state.is_none() {
self.path_state = Some(PathState::UserSpacePathBuilder(
self.drawtarget.create_path_builder(),
self.draw_target.create_path_builder(),
None,
));
}
@@ -607,7 +607,7 @@ impl<'a> CanvasData<'a> {
let new_state = match *self.path_state.as_mut().unwrap() {
PathState::DeviceSpacePathBuilder(ref mut builder) => {
let path = builder.finish();
let inverse = match self.drawtarget.get_transform().inverse() {
let inverse = match self.draw_target.get_transform().inverse() {
Some(m) => m,
None => {
warn!("Couldn't invert canvas transformation.");
@@ -639,7 +639,7 @@ impl<'a> CanvasData<'a> {
}

self.ensure_path();
self.drawtarget.fill(
self.draw_target.fill(
&self.path().clone(),
self.state.fill_style.clone(),
&self.state.draw_options,
@@ -652,7 +652,7 @@ impl<'a> CanvasData<'a> {
}

self.ensure_path();
self.drawtarget.stroke(
self.draw_target.stroke(
&self.path().clone(),
self.state.stroke_style.clone(),
&self.state.stroke_opts,
@@ -663,7 +663,7 @@ impl<'a> CanvasData<'a> {
pub fn clip(&mut self) {
self.ensure_path();
let path = self.path().clone();
self.drawtarget.push_clip(&path);
self.draw_target.push_clip(&path);
}

pub fn is_point_in_path(
@@ -676,7 +676,7 @@ impl<'a> CanvasData<'a> {
self.ensure_path();
let result = match self.path_state.as_ref() {
Some(PathState::UserSpacePath(ref path, ref transform)) => {
let target_transform = self.drawtarget.get_transform();
let target_transform = self.draw_target.get_transform();
let path_transform = transform.as_ref().unwrap_or(&target_transform);
path.contains_point(x, y, path_transform)
},
@@ -696,7 +696,7 @@ impl<'a> CanvasData<'a> {
fn path_builder(&mut self) -> PathBuilderRef {
if self.path_state.is_none() {
self.path_state = Some(PathState::UserSpacePathBuilder(
self.drawtarget.create_path_builder(),
self.draw_target.create_path_builder(),
None,
));
}
@@ -738,7 +738,7 @@ impl<'a> CanvasData<'a> {
PathState::DeviceSpacePathBuilder(ref mut builder) => {
return PathBuilderRef {
builder,
transform: self.drawtarget.get_transform(),
transform: self.draw_target.get_transform(),
};
},
_ => unreachable!(),
@@ -752,7 +752,7 @@ impl<'a> CanvasData<'a> {
},
PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef {
builder,
transform: self.drawtarget.get_transform(),
transform: self.draw_target.get_transform(),
},
PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => unreachable!(),
}
@@ -882,12 +882,12 @@ impl<'a> CanvasData<'a> {

pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) {
self.backend
.set_fill_style(style, &mut self.state, &*self.drawtarget);
.set_fill_style(style, &mut self.state, &*self.draw_target);
}

pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) {
self.backend
.set_stroke_style(style, &mut self.state, &*self.drawtarget);
.set_stroke_style(style, &mut self.state, &*self.draw_target);
}

pub fn set_line_width(&mut self, width: f32) {
@@ -907,7 +907,7 @@ impl<'a> CanvasData<'a> {
}

pub fn get_transform(&self) -> Transform2D<f32> {
self.drawtarget.get_transform()
self.draw_target.get_transform()
}

pub fn set_transform(&mut self, transform: &Transform2D<f32>) {
@@ -918,12 +918,12 @@ impl<'a> CanvasData<'a> {
Some(PathState::UserSpacePathBuilder(_, ref mut transform)) |
Some(PathState::UserSpacePath(_, ref mut transform)) => {
if transform.is_none() {
*transform = Some(self.drawtarget.get_transform());
*transform = Some(self.draw_target.get_transform());
}
},
}
self.state.transform = transform.clone();
self.drawtarget.set_transform(transform)
self.draw_target.set_transform(transform)
}

pub fn set_global_alpha(&mut self, alpha: f32) {
@@ -935,7 +935,7 @@ impl<'a> CanvasData<'a> {
}

pub fn recreate(&mut self, size: Size2D<u64>) {
self.drawtarget = self
self.draw_target = self
.backend
.create_drawtarget(Size2D::new(size.width, size.height));
self.state = self.backend.recreate_paint_state(&self.state);
@@ -954,15 +954,15 @@ impl<'a> CanvasData<'a> {
}

pub fn send_pixels(&mut self, chan: IpcSender<IpcSharedMemory>) {
self.drawtarget.snapshot_data(&|bytes| {
self.draw_target.snapshot_data(&|bytes| {
let data = IpcSharedMemory::from_bytes(bytes);
chan.send(data).unwrap();
vec![]
});
}

pub fn send_data(&mut self, chan: IpcSender<CanvasImageData>) {
let size = self.drawtarget.get_size();
let size = self.draw_target.get_size();

let descriptor = webrender_api::ImageDescriptor {
size: webrender_api::units::DeviceIntSize::new(size.width, size.height),
@@ -971,7 +971,7 @@ impl<'a> CanvasData<'a> {
offset: 0,
flags: webrender_api::ImageDescriptorFlags::empty(),
};
let data = self.drawtarget.snapshot_data_owned();
let data = self.draw_target.snapshot_data_owned();
let data = webrender_api::ImageData::Raw(Arc::new(data));

let mut updates = vec![];
@@ -1009,14 +1009,14 @@ impl<'a> CanvasData<'a> {
assert_eq!(rect.size.area() as usize, imagedata.len() / 4);
pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata);
let source_surface = self
.drawtarget
.draw_target
.create_source_surface_from_data(
&imagedata,
rect.size.to_i32(),
rect.size.width as i32 * 4,
)
.unwrap();
self.drawtarget.copy_surface(
self.draw_target.copy_surface(
source_surface,
Rect::from_size(rect.size.to_i32()),
rect.origin.to_i32(),
@@ -1048,12 +1048,12 @@ impl<'a> CanvasData<'a> {
}

fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> Box<dyn GenericDrawTarget> {
let mut draw_target = self.drawtarget.create_similar_draw_target(
let mut draw_target = self.draw_target.create_similar_draw_target(
&Size2D::new(
source_rect.size.width as i32,
source_rect.size.height as i32,
),
self.drawtarget.get_format(),
self.draw_target.get_format(),
);
let matrix = Transform2D::identity()
.pre_translate(-source_rect.origin.to_vector().cast::<f32>())
@@ -1069,7 +1069,7 @@ impl<'a> CanvasData<'a> {
let shadow_src_rect = self.state.transform.transform_rect(rect);
let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect);
draw_shadow_source(&mut *new_draw_target);
self.drawtarget.draw_surface_with_shadow(
self.draw_target.draw_surface_with_shadow(
new_draw_target.snapshot(),
&Point2D::new(
shadow_src_rect.origin.x as f32,
@@ -1098,7 +1098,7 @@ impl<'a> CanvasData<'a> {
return vec![];
}

self.drawtarget.snapshot_data(&|bytes| {
self.draw_target.snapshot_data(&|bytes| {
pixels::rgba8_get_rect(bytes, canvas_size, read_rect).into_owned()
})
}
@@ -988,9 +988,13 @@ impl CanvasState {

// https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext
pub fn fill_text(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
let parsed_text: String = text.into();
let is_max_width_finite = max_width.map_or(true, |max_width| max_width.is_finite());
if !(x.is_finite() && y.is_finite() && is_max_width_finite) {
return;
}

let style = self.state.borrow().fill_style.to_fill_or_stroke_style();
self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width, style));
self.send_canvas_2d_msg(Canvas2dMsg::FillText(text.into(), x, y, max_width, style));
}

// https://html.spec.whatwg.org/multipage/#textmetrics
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.