mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
Fixed scaling artefacts in paint worklets caused by zoom and hidpi.
This commit is contained in:
parent
e19fefcb47
commit
caa3585219
24 changed files with 160 additions and 77 deletions
|
@ -43,7 +43,7 @@ use dom::bindings::str::{DOMString, USVString};
|
|||
use dom::bindings::utils::WindowProxyHandler;
|
||||
use dom::document::PendingRestyle;
|
||||
use encoding::types::EncodingRef;
|
||||
use euclid::{Transform2D, Transform3D, Point2D, Vector2D, Rect, Size2D};
|
||||
use euclid::{Transform2D, Transform3D, Point2D, Vector2D, Rect, Size2D, ScaleFactor};
|
||||
use euclid::Length as EuclidLength;
|
||||
use html5ever::{Prefix, LocalName, Namespace, QualName};
|
||||
use html5ever::buffer_queue::BufferQueue;
|
||||
|
@ -484,6 +484,13 @@ unsafe impl JSTraceable for Point2D<f32> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<T, U> JSTraceable for ScaleFactor<f32, T, U> {
|
||||
#[inline]
|
||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl JSTraceable for Vector2D<f32> {
|
||||
#[inline]
|
||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||
|
|
|
@ -76,7 +76,8 @@ impl MediaQueryList {
|
|||
pub fn evaluate(&self) -> bool {
|
||||
if let Some(window_size) = self.document.window().window_size() {
|
||||
let viewport_size = window_size.initial_viewport;
|
||||
let device = Device::new(MediaType::Screen, viewport_size);
|
||||
let device_pixel_ratio = window_size.device_pixel_ratio;
|
||||
let device = Device::new(MediaType::Screen, viewport_size, device_pixel_ratio);
|
||||
self.media_query_list.evaluate(&device, self.document.quirks_mode())
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* 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 app_units::Au;
|
||||
use canvas_traits::CanvasData;
|
||||
use canvas_traits::CanvasMsg;
|
||||
use canvas_traits::FromLayoutMsg;
|
||||
|
@ -26,12 +25,18 @@ use dom::canvaspattern::CanvasPattern;
|
|||
use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
|
||||
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::ScaleFactor;
|
||||
use euclid::Size2D;
|
||||
use euclid::TypedSize2D;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use std::cell::Cell;
|
||||
use style_traits::CSSPixel;
|
||||
use style_traits::DevicePixel;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct PaintRenderingContext2D {
|
||||
context: CanvasRenderingContext2D,
|
||||
device_pixel_ratio: Cell<ScaleFactor<f32, CSSPixel, DevicePixel>>,
|
||||
}
|
||||
|
||||
impl PaintRenderingContext2D {
|
||||
|
@ -39,6 +44,7 @@ impl PaintRenderingContext2D {
|
|||
let size = Size2D::zero();
|
||||
PaintRenderingContext2D {
|
||||
context: CanvasRenderingContext2D::new_inherited(global.upcast(), None, size),
|
||||
device_pixel_ratio: Cell::new(ScaleFactor::new(1.0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,9 +59,21 @@ impl PaintRenderingContext2D {
|
|||
let _ = self.context.ipc_renderer().send(msg);
|
||||
}
|
||||
|
||||
pub fn set_bitmap_dimensions(&self, size: Size2D<Au>) {
|
||||
let size = Size2D::new(size.width.to_px(), size.height.to_px());
|
||||
self.context.set_bitmap_dimensions(size);
|
||||
pub fn set_bitmap_dimensions(&self,
|
||||
size: TypedSize2D<f32, CSSPixel>,
|
||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>)
|
||||
{
|
||||
let size = size * device_pixel_ratio;
|
||||
self.device_pixel_ratio.set(device_pixel_ratio);
|
||||
self.context.set_bitmap_dimensions(size.to_untyped().to_i32());
|
||||
self.scale_by_device_pixel_ratio();
|
||||
}
|
||||
|
||||
fn scale_by_device_pixel_ratio(&self) {
|
||||
let device_pixel_ratio = self.device_pixel_ratio.get().get() as f64;
|
||||
if device_pixel_ratio != 1.0 {
|
||||
self.Scale(device_pixel_ratio, device_pixel_ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,12 +110,14 @@ impl PaintRenderingContext2DMethods for PaintRenderingContext2D {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-settransform
|
||||
fn SetTransform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
|
||||
self.context.SetTransform(a, b, c, d, e, f)
|
||||
self.context.SetTransform(a, b, c, d, e, f);
|
||||
self.scale_by_device_pixel_ratio();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform
|
||||
fn ResetTransform(&self) {
|
||||
self.context.ResetTransform()
|
||||
self.context.ResetTransform();
|
||||
self.scale_by_device_pixel_ratio();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* 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 app_units::Au;
|
||||
use dom::bindings::codegen::Bindings::PaintSizeBinding;
|
||||
use dom::bindings::codegen::Bindings::PaintSizeBinding::PaintSizeMethods;
|
||||
use dom::bindings::js::Root;
|
||||
|
@ -11,7 +10,8 @@ use dom::bindings::reflector::Reflector;
|
|||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::Size2D;
|
||||
use euclid::TypedSize2D;
|
||||
use style_traits::CSSPixel;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct PaintSize {
|
||||
|
@ -21,15 +21,15 @@ pub struct PaintSize {
|
|||
}
|
||||
|
||||
impl PaintSize {
|
||||
fn new_inherited(size: Size2D<Au>) -> PaintSize {
|
||||
fn new_inherited(size: TypedSize2D<f32, CSSPixel>) -> PaintSize {
|
||||
PaintSize {
|
||||
reflector: Reflector::new(),
|
||||
width: Finite::wrap(size.width.to_px().abs() as f64),
|
||||
height: Finite::wrap(size.height.to_px().abs() as f64),
|
||||
width: Finite::wrap(size.width as f64),
|
||||
height: Finite::wrap(size.height as f64),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(global: &PaintWorkletGlobalScope, size: Size2D<Au>) -> Root<PaintSize> {
|
||||
pub fn new(global: &PaintWorkletGlobalScope, size: TypedSize2D<f32, CSSPixel>) -> Root<PaintSize> {
|
||||
reflect_dom_object(box PaintSize::new_inherited(size), global, PaintSizeBinding::Wrap)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
* 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 app_units::Au;
|
||||
use canvas_traits::CanvasData;
|
||||
use canvas_traits::CanvasImageData;
|
||||
use dom::bindings::callback::CallbackContainer;
|
||||
|
@ -27,7 +26,8 @@ use dom::workletglobalscope::WorkletGlobalScope;
|
|||
use dom::workletglobalscope::WorkletGlobalScopeInit;
|
||||
use dom::workletglobalscope::WorkletTask;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::Size2D;
|
||||
use euclid::ScaleFactor;
|
||||
use euclid::TypedSize2D;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::ipc::IpcSharedMemory;
|
||||
use js::jsapi::Call;
|
||||
|
@ -59,6 +59,8 @@ use std::ptr::null_mut;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use style_traits::CSSPixel;
|
||||
use style_traits::DevicePixel;
|
||||
|
||||
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
|
||||
#[dom_struct]
|
||||
|
@ -94,9 +96,9 @@ impl PaintWorkletGlobalScope {
|
|||
|
||||
pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
|
||||
match task {
|
||||
PaintWorkletTask::DrawAPaintImage(name, size, properties, sender) => {
|
||||
PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender) => {
|
||||
let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties);
|
||||
self.draw_a_paint_image(name, size, &*properties, sender);
|
||||
self.draw_a_paint_image(name, size, device_pixel_ratio, &*properties, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,25 +106,28 @@ impl PaintWorkletGlobalScope {
|
|||
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
||||
fn draw_a_paint_image(&self,
|
||||
name: Atom,
|
||||
size: Size2D<Au>,
|
||||
size_in_px: TypedSize2D<f32, CSSPixel>,
|
||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||
properties: &StylePropertyMapReadOnly,
|
||||
sender: IpcSender<CanvasData>)
|
||||
{
|
||||
// TODO: document paint definitions.
|
||||
self.invoke_a_paint_callback(name, size, properties, sender);
|
||||
self.invoke_a_paint_callback(name, size_in_px, device_pixel_ratio, properties, sender);
|
||||
}
|
||||
|
||||
/// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback
|
||||
#[allow(unsafe_code)]
|
||||
fn invoke_a_paint_callback(&self,
|
||||
name: Atom,
|
||||
size: Size2D<Au>,
|
||||
size_in_px: TypedSize2D<f32, CSSPixel>,
|
||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||
properties: &StylePropertyMapReadOnly,
|
||||
sender: IpcSender<CanvasData>)
|
||||
{
|
||||
let width = size.width.to_px().abs() as u32;
|
||||
let height = size.height.to_px().abs() as u32;
|
||||
debug!("Invoking a paint callback {}({},{}).", name, width, height);
|
||||
let size_in_dpx = size_in_px * device_pixel_ratio;
|
||||
let size_in_dpx = TypedSize2D::new(size_in_dpx.width.abs() as u32, size_in_dpx.height.abs() as u32);
|
||||
debug!("Invoking a paint callback {}({},{}) at {}.",
|
||||
name, size_in_px.width, size_in_px.height, device_pixel_ratio);
|
||||
|
||||
let cx = self.worklet_global.get_cx();
|
||||
let _ac = JSAutoCompartment::new(cx, self.worklet_global.reflector().get_jsobject().get());
|
||||
|
@ -135,13 +140,13 @@ impl PaintWorkletGlobalScope {
|
|||
None => {
|
||||
// Step 2.2.
|
||||
warn!("Drawing un-registered paint definition {}.", name);
|
||||
return self.send_invalid_image(size, sender);
|
||||
return self.send_invalid_image(size_in_dpx, sender);
|
||||
}
|
||||
Some(definition) => {
|
||||
// Step 5.1
|
||||
if !definition.constructor_valid_flag.get() {
|
||||
debug!("Drawing invalid paint definition {}.", name);
|
||||
return self.send_invalid_image(size, sender);
|
||||
return self.send_invalid_image(size_in_dpx, sender);
|
||||
}
|
||||
class_constructor.set(definition.class_constructor.get());
|
||||
paint_function.set(definition.paint_function.get());
|
||||
|
@ -169,7 +174,7 @@ impl PaintWorkletGlobalScope {
|
|||
self.paint_definitions.borrow_mut().get_mut(&name)
|
||||
.expect("Vanishing paint definition.")
|
||||
.constructor_valid_flag.set(false);
|
||||
return self.send_invalid_image(size, sender);
|
||||
return self.send_invalid_image(size_in_dpx, sender);
|
||||
}
|
||||
// Step 5.4
|
||||
entry.insert(Box::new(Heap::default())).set(paint_instance.get());
|
||||
|
@ -180,10 +185,10 @@ impl PaintWorkletGlobalScope {
|
|||
// Step 8
|
||||
// TODO: the spec requires creating a new paint rendering context each time,
|
||||
// this code recycles the same one.
|
||||
rendering_context.set_bitmap_dimensions(size);
|
||||
rendering_context.set_bitmap_dimensions(size_in_px, device_pixel_ratio);
|
||||
|
||||
// Step 9
|
||||
let paint_size = PaintSize::new(self, size);
|
||||
let paint_size = PaintSize::new(self, size_in_px);
|
||||
|
||||
// TODO: Step 10
|
||||
// Steps 11-12
|
||||
|
@ -202,17 +207,19 @@ impl PaintWorkletGlobalScope {
|
|||
if unsafe { JS_IsExceptionPending(cx) } {
|
||||
debug!("Paint function threw an exception {}.", name);
|
||||
unsafe { JS_ClearPendingException(cx); }
|
||||
return self.send_invalid_image(size, sender);
|
||||
return self.send_invalid_image(size_in_dpx, sender);
|
||||
}
|
||||
|
||||
rendering_context.send_data(sender);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-images-4/#invalid-image
|
||||
fn send_invalid_image(&self, size: Size2D<Au>, sender: IpcSender<CanvasData>) {
|
||||
fn send_invalid_image(&self,
|
||||
size: TypedSize2D<u32, DevicePixel>,
|
||||
sender: IpcSender<CanvasData>)
|
||||
{
|
||||
debug!("Sending an invalid image.");
|
||||
let width = size.width.to_px().abs() as u32;
|
||||
let height = size.height.to_px().abs() as u32;
|
||||
let width = size.width as u32;
|
||||
let height = size.height as u32;
|
||||
let len = (width as usize) * (height as usize) * 4;
|
||||
let pixel = [0x00, 0x00, 0x00, 0x00];
|
||||
let bytes: Vec<u8> = pixel.iter().cloned().cycle().take(len).collect();
|
||||
|
@ -235,12 +242,13 @@ impl PaintWorkletGlobalScope {
|
|||
struct WorkletPainter(Atom, Mutex<WorkletExecutor>);
|
||||
impl Painter for WorkletPainter {
|
||||
fn draw_a_paint_image(&self,
|
||||
size: Size2D<Au>,
|
||||
size: TypedSize2D<f32, CSSPixel>,
|
||||
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||
properties: Vec<(Atom, String)>,
|
||||
sender: IpcSender<CanvasData>)
|
||||
{
|
||||
let name = self.0.clone();
|
||||
let task = PaintWorkletTask::DrawAPaintImage(name, size, properties, sender);
|
||||
let task = PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, sender);
|
||||
self.1.lock().expect("Locking a painter.")
|
||||
.schedule_a_worklet_task(WorkletTask::Paint(task));
|
||||
}
|
||||
|
@ -334,7 +342,11 @@ impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
|
|||
|
||||
/// Tasks which can be peformed by a paint worklet
|
||||
pub enum PaintWorkletTask {
|
||||
DrawAPaintImage(Atom, Size2D<Au>, Vec<(Atom, String)>, IpcSender<CanvasData>)
|
||||
DrawAPaintImage(Atom,
|
||||
TypedSize2D<f32, CSSPixel>,
|
||||
ScaleFactor<f32, CSSPixel, DevicePixel>,
|
||||
Vec<(Atom, String)>,
|
||||
IpcSender<CanvasData>)
|
||||
}
|
||||
|
||||
/// A paint definition
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue