mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Vendor the current version of WebRender
This is a step toward upgrading WebRender, which will be upgraded and patched in the `third_party` directory. This change vendors the current private branch of WebRender that we use and adds a `patches` directory which tracks the changes on top of the upstream WebRender commit described by third_party/webrender/patches/head.
This commit is contained in:
parent
c19eb800de
commit
49277f5c3f
1215 changed files with 185677 additions and 34 deletions
32
third_party/webrender/webrender_api/Cargo.toml
vendored
Normal file
32
third_party/webrender/webrender_api/Cargo.toml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
[package]
|
||||
name = "webrender_api"
|
||||
version = "0.61.0"
|
||||
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/webrender"
|
||||
description = "Public API for WebRender"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
nightly = ["euclid/unstable", "serde/unstable"]
|
||||
serialize = []
|
||||
deserialize = []
|
||||
display_list_stats = []
|
||||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
bitflags = "1.2"
|
||||
byteorder = "1.2.1"
|
||||
derive_more = "0.99"
|
||||
euclid = { version = "0.22.0", features = ["serde"] }
|
||||
malloc_size_of_derive = "0.1"
|
||||
serde = { version = "1.0", features = ["rc"] }
|
||||
serde_derive = "1.0"
|
||||
serde_bytes = "0.11"
|
||||
time = "0.1"
|
||||
malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "wr_malloc_size_of" }
|
||||
peek-poke = { version = "0.2", path = "../peek-poke", features = ["extras"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = "0.9"
|
||||
core-graphics = "0.22"
|
2123
third_party/webrender/webrender_api/src/api.rs
vendored
Normal file
2123
third_party/webrender/webrender_api/src/api.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
131
third_party/webrender/webrender_api/src/channel.rs
vendored
Normal file
131
third_party/webrender/webrender_api/src/channel.rs
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::api::{Epoch, PipelineId};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::io::{self, Cursor, Error, ErrorKind, Read};
|
||||
use std::mem;
|
||||
use std::sync::mpsc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Payload {
|
||||
/// An epoch used to get the proper payload for a pipeline id frame request.
|
||||
///
|
||||
/// TODO(emilio): Is this still relevant? We send the messages for the same
|
||||
/// pipeline in order, so we shouldn't need it. Seems like this was only
|
||||
/// wallpapering (in most cases) the underlying problem in #991.
|
||||
pub epoch: Epoch,
|
||||
/// A pipeline id to key the payload with, along with the epoch.
|
||||
pub pipeline_id: PipelineId,
|
||||
pub display_list_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Payload {
|
||||
/// Convert the payload to a raw byte vector, in order for it to be
|
||||
/// efficiently shared via shmem, for example.
|
||||
/// This is a helper static method working on a slice.
|
||||
pub fn construct_data(epoch: Epoch, pipeline_id: PipelineId, dl_data: &[u8]) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(
|
||||
mem::size_of::<u32>() + 2 * mem::size_of::<u32>() + mem::size_of::<u64>() + dl_data.len(),
|
||||
);
|
||||
data.write_u32::<LittleEndian>(epoch.0).unwrap();
|
||||
data.write_u32::<LittleEndian>(pipeline_id.0).unwrap();
|
||||
data.write_u32::<LittleEndian>(pipeline_id.1).unwrap();
|
||||
data.write_u64::<LittleEndian>(dl_data.len() as u64)
|
||||
.unwrap();
|
||||
data.extend_from_slice(dl_data);
|
||||
data
|
||||
}
|
||||
/// Convert the payload to a raw byte vector, in order for it to be
|
||||
/// efficiently shared via shmem, for example.
|
||||
pub fn to_data(&self) -> Vec<u8> {
|
||||
Self::construct_data(self.epoch, self.pipeline_id, &self.display_list_data)
|
||||
}
|
||||
|
||||
/// Deserializes the given payload from a raw byte vector.
|
||||
pub fn from_data(data: &[u8]) -> Payload {
|
||||
let mut payload_reader = Cursor::new(data);
|
||||
let epoch = Epoch(payload_reader.read_u32::<LittleEndian>().unwrap());
|
||||
let pipeline_id = PipelineId(
|
||||
payload_reader.read_u32::<LittleEndian>().unwrap(),
|
||||
payload_reader.read_u32::<LittleEndian>().unwrap(),
|
||||
);
|
||||
|
||||
let dl_size = payload_reader.read_u64::<LittleEndian>().unwrap() as usize;
|
||||
let mut built_display_list_data = vec![0; dl_size];
|
||||
payload_reader
|
||||
.read_exact(&mut built_display_list_data[..])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(payload_reader.position(), data.len() as u64);
|
||||
|
||||
Payload {
|
||||
epoch,
|
||||
pipeline_id,
|
||||
display_list_data: built_display_list_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type PayloadSender = MsgSender<Payload>;
|
||||
|
||||
pub type PayloadReceiver = MsgReceiver<Payload>;
|
||||
|
||||
pub struct MsgReceiver<T> {
|
||||
rx: mpsc::Receiver<T>,
|
||||
}
|
||||
|
||||
impl<T> MsgReceiver<T> {
|
||||
pub fn recv(&self) -> Result<T, Error> {
|
||||
self.rx.recv().map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))
|
||||
}
|
||||
|
||||
pub fn to_mpsc_receiver(self) -> mpsc::Receiver<T> {
|
||||
self.rx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MsgSender<T> {
|
||||
tx: mpsc::Sender<T>,
|
||||
}
|
||||
|
||||
impl<T> MsgSender<T> {
|
||||
pub fn send(&self, data: T) -> Result<(), Error> {
|
||||
self.tx.send(data).map_err(|_| Error::new(ErrorKind::Other, "cannot send on closed channel"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn payload_channel() -> Result<(PayloadSender, PayloadReceiver), Error> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
Ok((PayloadSender { tx }, PayloadReceiver { rx }))
|
||||
}
|
||||
|
||||
pub fn msg_channel<T>() -> Result<(MsgSender<T>, MsgReceiver<T>), Error> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
Ok((MsgSender { tx }, MsgReceiver { rx }))
|
||||
}
|
||||
|
||||
///
|
||||
/// These serialize methods are needed to satisfy the compiler
|
||||
/// which uses these implementations for the recording tool.
|
||||
/// The recording tool only outputs messages that don't contain
|
||||
/// Senders or Receivers, so in theory these should never be
|
||||
/// called in the in-process config. If they are called,
|
||||
/// there may be a bug in the messages that the replay tool is writing.
|
||||
///
|
||||
|
||||
impl<T> Serialize for MsgSender<T> {
|
||||
fn serialize<S: Serializer>(&self, _: S) -> Result<S::Ok, S::Error> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for MsgSender<T> {
|
||||
fn deserialize<D>(_: D) -> Result<MsgSender<T>, D::Error>
|
||||
where D: Deserializer<'de> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
160
third_party/webrender/webrender_api/src/color.rs
vendored
Normal file
160
third_party/webrender/webrender_api/src/color.rs
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use peek_poke::PeekPoke;
|
||||
use std::cmp;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// Represents pre-multiplied RGBA colors with floating point numbers.
|
||||
///
|
||||
/// All components must be between 0.0 and 1.0.
|
||||
/// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
|
||||
///
|
||||
/// In premultiplied colors transitions to transparent always look "nice"
|
||||
/// therefore they are used in CSS gradients.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct PremultipliedColorF {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl PremultipliedColorF {
|
||||
pub const BLACK: PremultipliedColorF = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
|
||||
pub const TRANSPARENT: PremultipliedColorF = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
|
||||
pub const WHITE: PremultipliedColorF = PremultipliedColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
|
||||
|
||||
pub fn to_array(&self) -> [f32; 4] {
|
||||
[self.r, self.g, self.b, self.a]
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents RGBA screen colors with floating point numbers.
|
||||
///
|
||||
/// All components must be between 0.0 and 1.0.
|
||||
/// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct ColorF {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl ColorF {
|
||||
pub const BLACK: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
|
||||
pub const TRANSPARENT: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
|
||||
pub const WHITE: ColorF = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
|
||||
|
||||
/// Constructs a new `ColorF` from its components.
|
||||
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
ColorF { r, g, b, a }
|
||||
}
|
||||
|
||||
/// Multiply the RGB channels (but not alpha) with a given factor.
|
||||
pub fn scale_rgb(&self, scale: f32) -> Self {
|
||||
ColorF {
|
||||
r: self.r * scale,
|
||||
g: self.g * scale,
|
||||
b: self.b * scale,
|
||||
a: self.a,
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the alpha by a given factor.
|
||||
pub fn scale_alpha(&self, scale: f32) -> Self {
|
||||
ColorF {
|
||||
r: self.r,
|
||||
g: self.g,
|
||||
b: self.b,
|
||||
a: self.a * scale,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_array(&self) -> [f32; 4] {
|
||||
[self.r, self.g, self.b, self.a]
|
||||
}
|
||||
|
||||
/// Multiply the RGB components with the alpha channel.
|
||||
pub fn premultiplied(&self) -> PremultipliedColorF {
|
||||
let c = self.scale_rgb(self.a);
|
||||
PremultipliedColorF { r: c.r, g: c.g, b: c.b, a: c.a }
|
||||
}
|
||||
}
|
||||
|
||||
// Floats don't impl Hash/Eq/Ord...
|
||||
impl Eq for PremultipliedColorF {}
|
||||
impl Ord for PremultipliedColorF {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::derive_hash_xor_eq))]
|
||||
impl Hash for PremultipliedColorF {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Note: this is inconsistent with the Eq impl for -0.0 (don't care).
|
||||
self.r.to_bits().hash(state);
|
||||
self.g.to_bits().hash(state);
|
||||
self.b.to_bits().hash(state);
|
||||
self.a.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents RGBA screen colors with one byte per channel.
|
||||
///
|
||||
/// If the alpha value `a` is 255 the color is opaque.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct ColorU {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
impl ColorU {
|
||||
/// Constructs a new additive `ColorU` from its components.
|
||||
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
ColorU { r, g, b, a }
|
||||
}
|
||||
}
|
||||
|
||||
fn round_to_int(x: f32) -> u8 {
|
||||
debug_assert!((0.0 <= x) && (x <= 1.0), "{} should be between 0 and 1", x);
|
||||
let f = (255.0 * x) + 0.5;
|
||||
let val = f.floor();
|
||||
debug_assert!(val <= 255.0);
|
||||
val as u8
|
||||
}
|
||||
|
||||
// TODO: We shouldn't really convert back to `ColorU` ever,
|
||||
// since it's lossy. One of the blockers is that all of our debug colors
|
||||
// are specified in `ColorF`. Changing it to `ColorU` would be nice.
|
||||
impl From<ColorF> for ColorU {
|
||||
fn from(color: ColorF) -> Self {
|
||||
ColorU {
|
||||
r: round_to_int(color.r),
|
||||
g: round_to_int(color.g),
|
||||
b: round_to_int(color.b),
|
||||
a: round_to_int(color.a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorU> for ColorF {
|
||||
fn from(color: ColorU) -> Self {
|
||||
ColorF {
|
||||
r: color.r as f32 / 255.0,
|
||||
g: color.g as f32 / 255.0,
|
||||
b: color.b as f32 / 255.0,
|
||||
a: color.a as f32 / 255.0,
|
||||
}
|
||||
}
|
||||
}
|
1589
third_party/webrender/webrender_api/src/display_item.rs
vendored
Normal file
1589
third_party/webrender/webrender_api/src/display_item.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
115
third_party/webrender/webrender_api/src/display_item_cache.rs
vendored
Normal file
115
third_party/webrender/webrender_api/src/display_item_cache.rs
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::display_item::*;
|
||||
use crate::display_list::*;
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct CachedDisplayItem {
|
||||
item: DisplayItem,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CachedDisplayItem {
|
||||
pub fn display_item(&self) -> &DisplayItem {
|
||||
&self.item
|
||||
}
|
||||
|
||||
pub fn data_as_item_range<T>(&self) -> ItemRange<T> {
|
||||
ItemRange::new(&self.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl MallocSizeOf for CachedDisplayItem {
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
self.data.size_of(ops)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DisplayItemRef<'_, '_>> for CachedDisplayItem {
|
||||
fn from(item_ref: DisplayItemRef) -> Self {
|
||||
let item = item_ref.item();
|
||||
|
||||
match item {
|
||||
DisplayItem::Text(..) => CachedDisplayItem {
|
||||
item: *item,
|
||||
data: item_ref.glyphs().bytes().to_vec(),
|
||||
},
|
||||
_ => CachedDisplayItem {
|
||||
item: *item,
|
||||
data: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
|
||||
struct CacheEntry {
|
||||
items: Vec<CachedDisplayItem>,
|
||||
occupied: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct DisplayItemCache {
|
||||
entries: Vec<CacheEntry>,
|
||||
}
|
||||
|
||||
impl DisplayItemCache {
|
||||
fn add_item(&mut self, key: ItemKey, item: CachedDisplayItem) {
|
||||
let mut entry = &mut self.entries[key as usize];
|
||||
entry.items.push(item);
|
||||
entry.occupied = true;
|
||||
}
|
||||
|
||||
fn clear_entry(&mut self, key: ItemKey) {
|
||||
let mut entry = &mut self.entries[key as usize];
|
||||
entry.items.clear();
|
||||
entry.occupied = false;
|
||||
}
|
||||
|
||||
fn grow_if_needed(&mut self, capacity: usize) {
|
||||
if capacity > self.entries.len() {
|
||||
self.entries.resize_with(capacity, || CacheEntry {
|
||||
items: Vec::new(),
|
||||
occupied: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_items(&self, key: ItemKey) -> &[CachedDisplayItem] {
|
||||
let entry = &self.entries[key as usize];
|
||||
debug_assert!(entry.occupied);
|
||||
entry.items.as_slice()
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
entries: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, display_list: &BuiltDisplayList) {
|
||||
self.grow_if_needed(display_list.cache_size());
|
||||
|
||||
let mut iter = display_list.extra_data_iter();
|
||||
let mut current_key: Option<ItemKey> = None;
|
||||
loop {
|
||||
let item = match iter.next() {
|
||||
Some(item) => item,
|
||||
None => break,
|
||||
};
|
||||
|
||||
if let DisplayItem::RetainedItems(key) = item.item() {
|
||||
current_key = Some(*key);
|
||||
self.clear_entry(*key);
|
||||
continue;
|
||||
}
|
||||
|
||||
let key = current_key.expect("Missing RetainedItems marker");
|
||||
let cached_item = CachedDisplayItem::from(item);
|
||||
self.add_item(key, cached_item);
|
||||
}
|
||||
}
|
||||
}
|
1969
third_party/webrender/webrender_api/src/display_list.rs
vendored
Normal file
1969
third_party/webrender/webrender_api/src/display_list.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
604
third_party/webrender/webrender_api/src/font.rs
vendored
Normal file
604
third_party/webrender/webrender_api/src/font.rs
vendored
Normal file
|
@ -0,0 +1,604 @@
|
|||
/* 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/. */
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use core_foundation::string::CFString;
|
||||
#[cfg(target_os = "macos")]
|
||||
use core_graphics::font::CGFont;
|
||||
use peek_poke::PeekPoke;
|
||||
#[cfg(target_os = "macos")]
|
||||
use serde::de::{self, Deserialize, Deserializer};
|
||||
#[cfg(target_os = "macos")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, mpsc::Sender};
|
||||
use std::collections::HashMap;
|
||||
// local imports
|
||||
use crate::api::IdNamespace;
|
||||
use crate::color::ColorU;
|
||||
use crate::units::LayoutPoint;
|
||||
|
||||
/// Hashable floating-point storage for font size.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||
pub struct FontSize(pub f32);
|
||||
|
||||
impl Ord for FontSize {
|
||||
fn cmp(&self, other: &FontSize) -> Ordering {
|
||||
self.partial_cmp(other).unwrap_or(Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FontSize {}
|
||||
|
||||
impl Hash for FontSize {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for FontSize {
|
||||
fn from(size: f32) -> Self { FontSize(size) }
|
||||
}
|
||||
|
||||
impl From<FontSize> for f32 {
|
||||
fn from(size: FontSize) -> Self { size.0 }
|
||||
}
|
||||
|
||||
impl FontSize {
|
||||
pub fn zero() -> Self { FontSize(0.0) }
|
||||
|
||||
pub fn from_f32_px(size: f32) -> Self { FontSize(size) }
|
||||
|
||||
pub fn to_f32_px(&self) -> f32 { self.0 }
|
||||
|
||||
pub fn from_f64_px(size: f64) -> Self { FontSize(size as f32) }
|
||||
|
||||
pub fn to_f64_px(&self) -> f64 { self.0 as f64 }
|
||||
}
|
||||
|
||||
/// Immutable description of a font instance requested by the user of the API.
|
||||
///
|
||||
/// `BaseFontInstance` can be identified by a `FontInstanceKey` so we should
|
||||
/// never need to hash it.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd, MallocSizeOf)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
||||
pub struct BaseFontInstance {
|
||||
///
|
||||
pub instance_key: FontInstanceKey,
|
||||
///
|
||||
pub font_key: FontKey,
|
||||
///
|
||||
pub size: FontSize,
|
||||
///
|
||||
pub bg_color: ColorU,
|
||||
///
|
||||
pub render_mode: FontRenderMode,
|
||||
///
|
||||
pub flags: FontInstanceFlags,
|
||||
///
|
||||
pub synthetic_italics: SyntheticItalics,
|
||||
///
|
||||
#[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))]
|
||||
pub platform_options: Option<FontInstancePlatformOptions>,
|
||||
///
|
||||
pub variations: Vec<FontVariation>,
|
||||
}
|
||||
|
||||
pub type FontInstanceMap = HashMap<FontInstanceKey, Arc<BaseFontInstance>>;
|
||||
/// A map of font instance data accessed concurrently from multiple threads.
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
||||
pub struct SharedFontInstanceMap {
|
||||
map: Arc<RwLock<FontInstanceMap>>,
|
||||
}
|
||||
|
||||
impl SharedFontInstanceMap {
|
||||
/// Creates an empty shared map.
|
||||
pub fn new() -> Self {
|
||||
SharedFontInstanceMap {
|
||||
map: Arc::new(RwLock::new(HashMap::default()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Acquires a write lock on the shared map.
|
||||
pub fn lock(&mut self) -> Option<RwLockReadGuard<FontInstanceMap>> {
|
||||
self.map.read().ok()
|
||||
}
|
||||
|
||||
///
|
||||
pub fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
|
||||
match self.map.read().unwrap().get(&key) {
|
||||
Some(instance) => Some(FontInstanceData {
|
||||
font_key: instance.font_key,
|
||||
size: instance.size.into(),
|
||||
options: Some(FontInstanceOptions {
|
||||
render_mode: instance.render_mode,
|
||||
flags: instance.flags,
|
||||
bg_color: instance.bg_color,
|
||||
synthetic_italics: instance.synthetic_italics,
|
||||
}),
|
||||
platform_options: instance.platform_options,
|
||||
variations: instance.variations.clone(),
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace the shared map with the provided map.
|
||||
pub fn set(&mut self, map: FontInstanceMap) {
|
||||
*self.map.write().unwrap() = map;
|
||||
}
|
||||
|
||||
///
|
||||
pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<Arc<BaseFontInstance>> {
|
||||
let instance_map = self.map.read().unwrap();
|
||||
instance_map.get(&instance_key).map(|instance| { Arc::clone(instance) })
|
||||
}
|
||||
|
||||
///
|
||||
pub fn add_font_instance(
|
||||
&mut self,
|
||||
instance_key: FontInstanceKey,
|
||||
font_key: FontKey,
|
||||
size: f32,
|
||||
options: Option<FontInstanceOptions>,
|
||||
platform_options: Option<FontInstancePlatformOptions>,
|
||||
variations: Vec<FontVariation>,
|
||||
) {
|
||||
let FontInstanceOptions {
|
||||
render_mode,
|
||||
flags,
|
||||
bg_color,
|
||||
synthetic_italics,
|
||||
..
|
||||
} = options.unwrap_or_default();
|
||||
|
||||
let instance = Arc::new(BaseFontInstance {
|
||||
instance_key,
|
||||
font_key,
|
||||
size: size.into(),
|
||||
bg_color,
|
||||
render_mode,
|
||||
flags,
|
||||
synthetic_italics,
|
||||
platform_options,
|
||||
variations,
|
||||
});
|
||||
|
||||
self.map
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(instance_key, instance);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
|
||||
self.map.write().unwrap().remove(&instance_key);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn clear_namespace(&mut self, namespace: IdNamespace) {
|
||||
self.map
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|key, _| key.0 != namespace);
|
||||
}
|
||||
|
||||
///
|
||||
pub fn clone_map(&self) -> FontInstanceMap {
|
||||
self.map.read().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct NativeFontHandle {
|
||||
pub path: PathBuf,
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[derive(Clone)]
|
||||
pub struct NativeFontHandle(pub CGFont);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl Serialize for NativeFontHandle {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0
|
||||
.postscript_name()
|
||||
.to_string()
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl<'de> Deserialize<'de> for NativeFontHandle {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let postscript_name: String = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
match CGFont::from_name(&CFString::new(&*postscript_name)) {
|
||||
Ok(font) => Ok(NativeFontHandle(font)),
|
||||
Err(_) => Err(de::Error::custom(
|
||||
"Couldn't find a font with that PostScript name!",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct GlyphDimensions {
|
||||
pub left: i32,
|
||||
pub top: i32,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub advance: f32,
|
||||
}
|
||||
|
||||
pub struct GlyphDimensionRequest {
|
||||
pub key: FontInstanceKey,
|
||||
pub glyph_indices: Vec<GlyphIndex>,
|
||||
pub sender: Sender<Vec<Option<GlyphDimensions>>>,
|
||||
}
|
||||
|
||||
pub struct GlyphIndexRequest {
|
||||
pub key: FontKey,
|
||||
pub text: String,
|
||||
pub sender: Sender<Vec<Option<u32>>>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, Ord, PartialOrd)]
|
||||
pub struct FontKey(pub IdNamespace, pub u32);
|
||||
|
||||
impl FontKey {
|
||||
pub fn new(namespace: IdNamespace, key: u32) -> FontKey {
|
||||
FontKey(namespace, key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Container for the raw data describing a font. This might be a stream of
|
||||
/// bytes corresponding to a downloaded font, or a handle to a native font from
|
||||
/// the operating system.
|
||||
///
|
||||
/// Note that fonts need to be instantiated before being used, which involves
|
||||
/// assigning size and various other options. The word 'template' here is
|
||||
/// intended to distinguish this data from instance-specific data.
|
||||
#[derive(Clone)]
|
||||
pub enum FontTemplate {
|
||||
Raw(Arc<Vec<u8>>, u32),
|
||||
Native(NativeFontHandle),
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, MallocSizeOf, PartialEq, Serialize, Deserialize, Ord, PartialOrd, PeekPoke)]
|
||||
pub enum FontRenderMode {
|
||||
Mono = 0,
|
||||
Alpha,
|
||||
Subpixel,
|
||||
}
|
||||
|
||||
impl Default for FontRenderMode {
|
||||
fn default() -> Self {
|
||||
FontRenderMode::Mono
|
||||
}
|
||||
}
|
||||
|
||||
impl FontRenderMode {
|
||||
// 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) {
|
||||
(FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, Deserialize, Serialize)]
|
||||
pub struct FontVariation {
|
||||
pub tag: u32,
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
impl Ord for FontVariation {
|
||||
fn cmp(&self, other: &FontVariation) -> Ordering {
|
||||
self.tag.cmp(&other.tag)
|
||||
.then(self.value.to_bits().cmp(&other.value.to_bits()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FontVariation {
|
||||
fn eq(&self, other: &FontVariation) -> bool {
|
||||
self.tag == other.tag &&
|
||||
self.value.to_bits() == other.value.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FontVariation {}
|
||||
|
||||
impl Hash for FontVariation {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.tag.hash(state);
|
||||
self.value.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize, PeekPoke)]
|
||||
pub struct GlyphOptions {
|
||||
pub render_mode: FontRenderMode,
|
||||
pub flags: FontInstanceFlags,
|
||||
}
|
||||
|
||||
impl Default for GlyphOptions {
|
||||
fn default() -> Self {
|
||||
GlyphOptions {
|
||||
render_mode: FontRenderMode::Subpixel,
|
||||
flags: FontInstanceFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
|
||||
pub struct FontInstanceFlags: u32 {
|
||||
// Common flags
|
||||
const SYNTHETIC_BOLD = 1 << 1;
|
||||
const EMBEDDED_BITMAPS = 1 << 2;
|
||||
const SUBPIXEL_BGR = 1 << 3;
|
||||
const TRANSPOSE = 1 << 4;
|
||||
const FLIP_X = 1 << 5;
|
||||
const FLIP_Y = 1 << 6;
|
||||
const SUBPIXEL_POSITION = 1 << 7;
|
||||
const VERTICAL = 1 << 8;
|
||||
|
||||
// Internal flags
|
||||
const TRANSFORM_GLYPHS = 1 << 12;
|
||||
const TEXTURE_PADDING = 1 << 13;
|
||||
|
||||
// Windows flags
|
||||
const FORCE_GDI = 1 << 16;
|
||||
const FORCE_SYMMETRIC = 1 << 17;
|
||||
const NO_SYMMETRIC = 1 << 18;
|
||||
|
||||
// Mac flags
|
||||
const FONT_SMOOTHING = 1 << 16;
|
||||
|
||||
// FreeType flags
|
||||
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::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::SUBPIXEL_POSITION
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct SyntheticItalics {
|
||||
// Angle in degrees (-90..90) for synthetic italics in 8.8 fixed-point.
|
||||
pub angle: i16,
|
||||
}
|
||||
|
||||
impl SyntheticItalics {
|
||||
pub const ANGLE_SCALE: f32 = 256.0;
|
||||
|
||||
pub fn from_degrees(degrees: f32) -> Self {
|
||||
SyntheticItalics { angle: (degrees.max(-89.0).min(89.0) * Self::ANGLE_SCALE) as i16 }
|
||||
}
|
||||
|
||||
pub fn to_degrees(self) -> f32 {
|
||||
self.angle as f32 / Self::ANGLE_SCALE
|
||||
}
|
||||
|
||||
pub fn to_radians(self) -> f32 {
|
||||
self.to_degrees().to_radians()
|
||||
}
|
||||
|
||||
pub fn to_skew(self) -> f32 {
|
||||
self.to_radians().tan()
|
||||
}
|
||||
|
||||
pub fn enabled() -> Self {
|
||||
Self::from_degrees(14.0)
|
||||
}
|
||||
|
||||
pub fn disabled() -> Self {
|
||||
SyntheticItalics { angle: 0 }
|
||||
}
|
||||
|
||||
pub fn is_enabled(self) -> bool {
|
||||
self.angle != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SyntheticItalics {
|
||||
fn default() -> Self {
|
||||
SyntheticItalics::disabled()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct FontInstanceOptions {
|
||||
pub render_mode: FontRenderMode,
|
||||
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
|
||||
/// background color.
|
||||
pub bg_color: ColorU,
|
||||
pub synthetic_italics: SyntheticItalics,
|
||||
}
|
||||
|
||||
impl Default for FontInstanceOptions {
|
||||
fn default() -> FontInstanceOptions {
|
||||
FontInstanceOptions {
|
||||
render_mode: FontRenderMode::Subpixel,
|
||||
flags: Default::default(),
|
||||
bg_color: ColorU::new(0, 0, 0, 0),
|
||||
synthetic_italics: SyntheticItalics::disabled(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct FontInstancePlatformOptions {
|
||||
pub gamma: u16, // percent
|
||||
pub contrast: u8, // percent
|
||||
pub cleartype_level: u8, // percent
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl Default for FontInstancePlatformOptions {
|
||||
fn default() -> FontInstancePlatformOptions {
|
||||
FontInstancePlatformOptions {
|
||||
gamma: 180, // Default DWrite gamma
|
||||
contrast: 100,
|
||||
cleartype_level: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct FontInstancePlatformOptions {
|
||||
pub unused: u32,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl Default for FontInstancePlatformOptions {
|
||||
fn default() -> FontInstancePlatformOptions {
|
||||
FontInstancePlatformOptions {
|
||||
unused: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub enum FontLCDFilter {
|
||||
None,
|
||||
Default,
|
||||
Light,
|
||||
Legacy,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub enum FontHinting {
|
||||
None,
|
||||
Mono,
|
||||
Light,
|
||||
Normal,
|
||||
LCD,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
|
||||
pub struct FontInstancePlatformOptions {
|
||||
pub lcd_filter: FontLCDFilter,
|
||||
pub hinting: FontHinting,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
impl Default for FontInstancePlatformOptions {
|
||||
fn default() -> FontInstancePlatformOptions {
|
||||
FontInstancePlatformOptions {
|
||||
lcd_filter: FontLCDFilter::Default,
|
||||
hinting: FontHinting::LCD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd, MallocSizeOf, PeekPoke)]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct FontInstanceKey(pub IdNamespace, pub u32);
|
||||
|
||||
impl FontInstanceKey {
|
||||
pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
|
||||
FontInstanceKey(namespace, key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Data corresponding to an instantiation of a font, with size and
|
||||
/// other options specified.
|
||||
///
|
||||
/// Note that the actual font is stored out-of-band in `FontTemplate`.
|
||||
#[derive(Clone)]
|
||||
pub struct FontInstanceData {
|
||||
pub font_key: FontKey,
|
||||
pub size: f32,
|
||||
pub options: Option<FontInstanceOptions>,
|
||||
pub platform_options: Option<FontInstancePlatformOptions>,
|
||||
pub variations: Vec<FontVariation>,
|
||||
}
|
||||
|
||||
pub type GlyphIndex = u32;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct GlyphInstance {
|
||||
pub index: GlyphIndex,
|
||||
pub point: LayoutPoint,
|
||||
}
|
||||
|
||||
impl Default for GlyphInstance {
|
||||
fn default() -> Self {
|
||||
GlyphInstance {
|
||||
index: 0,
|
||||
point: LayoutPoint::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for GlyphInstance {}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::derive_hash_xor_eq))]
|
||||
impl Hash for GlyphInstance {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Note: this is inconsistent with the Eq impl for -0.0 (don't care).
|
||||
self.index.hash(state);
|
||||
self.point.x.to_bits().hash(state);
|
||||
self.point.y.to_bits().hash(state);
|
||||
}
|
||||
}
|
178
third_party/webrender/webrender_api/src/gradient_builder.rs
vendored
Normal file
178
third_party/webrender/webrender_api/src/gradient_builder.rs
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::display_item as di;
|
||||
use crate::units::*;
|
||||
|
||||
|
||||
/// Construct a gradient to be used in display lists.
|
||||
///
|
||||
/// Each gradient needs at least two stops.
|
||||
pub struct GradientBuilder {
|
||||
stops: Vec<di::GradientStop>,
|
||||
}
|
||||
|
||||
impl GradientBuilder {
|
||||
/// Create a new gradient builder.
|
||||
pub fn new() -> Self {
|
||||
GradientBuilder {
|
||||
stops: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a gradient builder with a list of stops.
|
||||
pub fn with_stops(stops: Vec<di::GradientStop>) -> GradientBuilder {
|
||||
GradientBuilder { stops }
|
||||
}
|
||||
|
||||
/// Push an additional stop for the gradient.
|
||||
pub fn push(&mut self, stop: di::GradientStop) {
|
||||
self.stops.push(stop);
|
||||
}
|
||||
|
||||
/// Get a reference to the list of stops.
|
||||
pub fn stops(&self) -> &[di::GradientStop] {
|
||||
self.stops.as_ref()
|
||||
}
|
||||
|
||||
/// Return the gradient stops vector.
|
||||
pub fn into_stops(self) -> Vec<di::GradientStop> {
|
||||
self.stops
|
||||
}
|
||||
|
||||
/// Produce a linear gradient, normalize the stops.
|
||||
pub fn gradient(
|
||||
&mut self,
|
||||
start_point: LayoutPoint,
|
||||
end_point: LayoutPoint,
|
||||
extend_mode: di::ExtendMode,
|
||||
) -> di::Gradient {
|
||||
let (start_offset, end_offset) = self.normalize(extend_mode);
|
||||
let start_to_end = end_point - start_point;
|
||||
|
||||
di::Gradient {
|
||||
start_point: start_point + start_to_end * start_offset,
|
||||
end_point: start_point + start_to_end * end_offset,
|
||||
extend_mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a radial gradient, normalize the stops.
|
||||
///
|
||||
/// Will replace the gradient with a single color
|
||||
/// if the radius negative.
|
||||
pub fn radial_gradient(
|
||||
&mut self,
|
||||
center: LayoutPoint,
|
||||
radius: LayoutSize,
|
||||
extend_mode: di::ExtendMode,
|
||||
) -> di::RadialGradient {
|
||||
if radius.width <= 0.0 || radius.height <= 0.0 {
|
||||
// The shader cannot handle a non positive radius. So
|
||||
// reuse the stops vector and construct an equivalent
|
||||
// gradient.
|
||||
let last_color = self.stops.last().unwrap().color;
|
||||
|
||||
self.stops.clear();
|
||||
self.stops.push(di::GradientStop { offset: 0.0, color: last_color, });
|
||||
self.stops.push(di::GradientStop { offset: 1.0, color: last_color, });
|
||||
|
||||
return di::RadialGradient {
|
||||
center,
|
||||
radius: LayoutSize::new(1.0, 1.0),
|
||||
start_offset: 0.0,
|
||||
end_offset: 1.0,
|
||||
extend_mode,
|
||||
};
|
||||
}
|
||||
|
||||
let (start_offset, end_offset) =
|
||||
self.normalize(extend_mode);
|
||||
|
||||
di::RadialGradient {
|
||||
center,
|
||||
radius,
|
||||
start_offset,
|
||||
end_offset,
|
||||
extend_mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a conic gradient, normalize the stops.
|
||||
pub fn conic_gradient(
|
||||
&mut self,
|
||||
center: LayoutPoint,
|
||||
angle: f32,
|
||||
extend_mode: di::ExtendMode,
|
||||
) -> di::ConicGradient {
|
||||
let (start_offset, end_offset) =
|
||||
self.normalize(extend_mode);
|
||||
|
||||
di::ConicGradient {
|
||||
center,
|
||||
angle,
|
||||
start_offset,
|
||||
end_offset,
|
||||
extend_mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradients can be defined with stops outside the range of [0, 1]
|
||||
/// when this happens the gradient needs to be normalized by adjusting
|
||||
/// the gradient stops and gradient line into an equivalent gradient
|
||||
/// with stops in the range [0, 1]. this is done by moving the beginning
|
||||
/// of the gradient line to where stop[0] and the end of the gradient line
|
||||
/// to stop[n-1]. this function adjusts the stops in place, and returns
|
||||
/// the amount to adjust the gradient line start and stop.
|
||||
fn normalize(&mut self, extend_mode: di::ExtendMode) -> (f32, f32) {
|
||||
let stops = &mut self.stops;
|
||||
assert!(stops.len() >= 2);
|
||||
|
||||
let first = *stops.first().unwrap();
|
||||
let last = *stops.last().unwrap();
|
||||
|
||||
assert!(first.offset <= last.offset);
|
||||
|
||||
let stops_delta = last.offset - first.offset;
|
||||
|
||||
if stops_delta > 0.000001 {
|
||||
for stop in stops {
|
||||
stop.offset = (stop.offset - first.offset) / stops_delta;
|
||||
}
|
||||
|
||||
(first.offset, last.offset)
|
||||
} else {
|
||||
// We have a degenerate gradient and can't accurately transform the stops
|
||||
// what happens here depends on the repeat behavior, but in any case
|
||||
// we reconstruct the gradient stops to something simpler and equivalent
|
||||
stops.clear();
|
||||
|
||||
match extend_mode {
|
||||
di::ExtendMode::Clamp => {
|
||||
// This gradient is two colors split at the offset of the stops,
|
||||
// so create a gradient with two colors split at 0.5 and adjust
|
||||
// the gradient line so 0.5 is at the offset of the stops
|
||||
stops.push(di::GradientStop { color: first.color, offset: 0.0, });
|
||||
stops.push(di::GradientStop { color: first.color, offset: 0.5, });
|
||||
stops.push(di::GradientStop { color: last.color, offset: 0.5, });
|
||||
stops.push(di::GradientStop { color: last.color, offset: 1.0, });
|
||||
|
||||
let offset = last.offset;
|
||||
|
||||
(offset - 0.5, offset + 0.5)
|
||||
}
|
||||
di::ExtendMode::Repeat => {
|
||||
// A repeating gradient with stops that are all in the same
|
||||
// position should just display the last color. I believe the
|
||||
// spec says that it should be the average color of the gradient,
|
||||
// but this matches what Gecko and Blink does
|
||||
stops.push(di::GradientStop { color: last.color, offset: 0.0, });
|
||||
stops.push(di::GradientStop { color: last.color, offset: 1.0, });
|
||||
|
||||
(0.0, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
595
third_party/webrender/webrender_api/src/image.rs
vendored
Normal file
595
third_party/webrender/webrender_api/src/image.rs
vendored
Normal file
|
@ -0,0 +1,595 @@
|
|||
/* 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/. */
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use euclid::{size2, Rect, num::Zero};
|
||||
use peek_poke::PeekPoke;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::sync::Arc;
|
||||
// local imports
|
||||
use crate::api::{IdNamespace, PipelineId, TileSize};
|
||||
use crate::display_item::ImageRendering;
|
||||
use crate::font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate};
|
||||
use crate::units::*;
|
||||
|
||||
/// The default tile size for blob images and regular images larger than
|
||||
/// the maximum texture size.
|
||||
pub const DEFAULT_TILE_SIZE: TileSize = 512;
|
||||
|
||||
/// An opaque identifier describing an image registered with WebRender.
|
||||
/// This is used as a handle to reference images, and is used as the
|
||||
/// hash map key for the actual image storage in the `ResourceCache`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub struct ImageKey(pub IdNamespace, pub u32);
|
||||
|
||||
impl Default for ImageKey {
|
||||
fn default() -> Self {
|
||||
ImageKey::DUMMY
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageKey {
|
||||
/// Placeholder Image key, used to represent None.
|
||||
pub const DUMMY: Self = ImageKey(IdNamespace(0), 0);
|
||||
|
||||
/// Mints a new ImageKey. The given ID must be unique.
|
||||
pub fn new(namespace: IdNamespace, key: u32) -> Self {
|
||||
ImageKey(namespace, key)
|
||||
}
|
||||
}
|
||||
|
||||
/// An opaque identifier describing a blob image registered with WebRender.
|
||||
/// This is used as a handle to reference blob images, and can be used as an
|
||||
/// image in display items.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct BlobImageKey(pub ImageKey);
|
||||
|
||||
impl BlobImageKey {
|
||||
/// Interpret this blob image as an image for a display item.
|
||||
pub fn as_image(self) -> ImageKey {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary identifier for an external image provided by the
|
||||
/// application. It must be a unique identifier for each external
|
||||
/// image.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct ExternalImageId(pub u64);
|
||||
|
||||
/// The source for an external image.
|
||||
pub enum ExternalImageSource<'a> {
|
||||
/// A raw pixel buffer.
|
||||
RawData(&'a [u8]),
|
||||
/// A gl::GLuint texture handle.
|
||||
NativeTexture(u32),
|
||||
/// An invalid source.
|
||||
Invalid,
|
||||
}
|
||||
|
||||
/// The data that an external client should provide about
|
||||
/// an external image. For instance, if providing video frames,
|
||||
/// the application could call wr.render() whenever a new
|
||||
/// video frame is ready. Note that the UV coords are supplied
|
||||
/// in texel-space!
|
||||
pub struct ExternalImage<'a> {
|
||||
/// UV coordinates for the image.
|
||||
pub uv: TexelRect,
|
||||
/// The source for this image's contents.
|
||||
pub source: ExternalImageSource<'a>,
|
||||
}
|
||||
|
||||
/// The interfaces that an application can implement to support providing
|
||||
/// external image buffers.
|
||||
/// When the application passes an external image to WR, it should keep that
|
||||
/// external image life time. People could check the epoch id in RenderNotifier
|
||||
/// at the client side to make sure that the external image is not used by WR.
|
||||
/// Then, do the clean up for that external image.
|
||||
pub trait ExternalImageHandler {
|
||||
/// Lock the external image. Then, WR could start to read the image content.
|
||||
/// The WR client should not change the image content until the unlock()
|
||||
/// call. Provide ImageRendering for NativeTexture external images.
|
||||
fn lock(&mut self, key: ExternalImageId, channel_index: u8, rendering: ImageRendering) -> ExternalImage;
|
||||
/// Unlock the external image. WR should not read the image content
|
||||
/// after this call.
|
||||
fn unlock(&mut self, key: ExternalImageId, channel_index: u8);
|
||||
}
|
||||
|
||||
/// Allows callers to receive a texture with the contents of a specific
|
||||
/// pipeline copied to it.
|
||||
pub trait OutputImageHandler {
|
||||
/// Return the native texture handle and the size of the texture.
|
||||
fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, FramebufferIntSize)>;
|
||||
/// Unlock will only be called if the lock() call succeeds, when WR has issued
|
||||
/// the GL commands to copy the output to the texture handle.
|
||||
fn unlock(&mut self, pipeline_id: PipelineId);
|
||||
}
|
||||
|
||||
/// Specifies the type of texture target in driver terms.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum TextureTarget {
|
||||
/// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
|
||||
Default = 0,
|
||||
/// Array texture. This maps to GL_TEXTURE_2D_ARRAY in OpenGL. See
|
||||
/// https://www.khronos.org/opengl/wiki/Array_Texture for background
|
||||
/// on Array textures.
|
||||
Array = 1,
|
||||
/// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
|
||||
/// is similar to a standard texture, with a few subtle differences
|
||||
/// (no mipmaps, non-power-of-two dimensions, different coordinate space)
|
||||
/// that make it useful for representing the kinds of textures we use
|
||||
/// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
|
||||
/// for background on Rectangle textures.
|
||||
Rect = 2,
|
||||
/// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
|
||||
/// is an extension. This is used for image formats that OpenGL doesn't
|
||||
/// understand, particularly YUV. See
|
||||
/// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
|
||||
External = 3,
|
||||
}
|
||||
|
||||
/// Storage format identifier for externally-managed images.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ExternalImageType {
|
||||
/// The image is texture-backed.
|
||||
TextureHandle(TextureTarget),
|
||||
/// The image is heap-allocated by the embedding.
|
||||
Buffer,
|
||||
}
|
||||
|
||||
/// Descriptor for external image resources. See `ImageData`.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ExternalImageData {
|
||||
/// The identifier of this external image, provided by the embedding.
|
||||
pub id: ExternalImageId,
|
||||
/// For multi-plane images (i.e. YUV), indicates the plane of the
|
||||
/// original image that this struct represents. 0 for single-plane images.
|
||||
pub channel_index: u8,
|
||||
/// Storage format identifier.
|
||||
pub image_type: ExternalImageType,
|
||||
}
|
||||
|
||||
/// Specifies the format of a series of pixels, in driver terms.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum ImageFormat {
|
||||
/// One-channel, byte storage. The "red" doesn't map to the color
|
||||
/// red per se, and is just the way that OpenGL has historically referred
|
||||
/// to single-channel buffers.
|
||||
R8 = 1,
|
||||
/// One-channel, short storage
|
||||
R16 = 2,
|
||||
/// Four channels, byte storage.
|
||||
BGRA8 = 3,
|
||||
/// Four channels, float storage.
|
||||
RGBAF32 = 4,
|
||||
/// Two-channels, byte storage. Similar to `R8`, this just means
|
||||
/// "two channels" rather than "red and green".
|
||||
RG8 = 5,
|
||||
/// Two-channels, byte storage. Similar to `R16`, this just means
|
||||
/// "two channels" rather than "red and green".
|
||||
RG16 = 6,
|
||||
|
||||
/// Four channels, signed integer storage.
|
||||
RGBAI32 = 7,
|
||||
/// Four channels, byte storage.
|
||||
RGBA8 = 8,
|
||||
}
|
||||
|
||||
impl ImageFormat {
|
||||
/// Returns the number of bytes per pixel for the given format.
|
||||
pub fn bytes_per_pixel(self) -> i32 {
|
||||
match self {
|
||||
ImageFormat::R8 => 1,
|
||||
ImageFormat::R16 => 2,
|
||||
ImageFormat::BGRA8 => 4,
|
||||
ImageFormat::RGBAF32 => 16,
|
||||
ImageFormat::RG8 => 2,
|
||||
ImageFormat::RG16 => 4,
|
||||
ImageFormat::RGBAI32 => 16,
|
||||
ImageFormat::RGBA8 => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the color depth of an image. Currently only used for YUV images.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
|
||||
pub enum ColorDepth {
|
||||
/// 8 bits image (most common)
|
||||
Color8,
|
||||
/// 10 bits image
|
||||
Color10,
|
||||
/// 12 bits image
|
||||
Color12,
|
||||
/// 16 bits image
|
||||
Color16,
|
||||
}
|
||||
|
||||
impl Default for ColorDepth {
|
||||
fn default() -> Self {
|
||||
ColorDepth::Color8
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorDepth {
|
||||
/// Return the numerical bit depth value for the type.
|
||||
pub fn bit_depth(self) -> u32 {
|
||||
match self {
|
||||
ColorDepth::Color8 => 8,
|
||||
ColorDepth::Color10 => 10,
|
||||
ColorDepth::Color12 => 12,
|
||||
ColorDepth::Color16 => 16,
|
||||
}
|
||||
}
|
||||
/// 10 and 12 bits images are encoded using 16 bits integer, we need to
|
||||
/// rescale the 10 or 12 bits value to extend to 16 bits.
|
||||
pub fn rescaling_factor(self) -> f32 {
|
||||
match self {
|
||||
ColorDepth::Color8 => 1.0,
|
||||
ColorDepth::Color10 => 64.0,
|
||||
ColorDepth::Color12 => 16.0,
|
||||
ColorDepth::Color16 => 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Various flags that are part of an image descriptor.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ImageDescriptorFlags: u32 {
|
||||
/// Whether this image is opaque, or has an alpha channel. Avoiding blending
|
||||
/// for opaque surfaces is an important optimization.
|
||||
const IS_OPAQUE = 1;
|
||||
/// Whether to allow the driver to automatically generate mipmaps. If images
|
||||
/// are already downscaled appropriately, mipmap generation can be wasted
|
||||
/// work, and cause performance problems on some cards/drivers.
|
||||
///
|
||||
/// See https://github.com/servo/webrender/pull/2555/
|
||||
const ALLOW_MIPMAPS = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata (but not storage) describing an image In WebRender.
|
||||
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct ImageDescriptor {
|
||||
/// Format of the image data.
|
||||
pub format: ImageFormat,
|
||||
/// Width and length of the image data, in pixels.
|
||||
pub size: DeviceIntSize,
|
||||
/// The number of bytes from the start of one row to the next. If non-None,
|
||||
/// `compute_stride` will return this value, otherwise it returns
|
||||
/// `width * bpp`. Different source of images have different alignment
|
||||
/// constraints for rows, so the stride isn't always equal to width * bpp.
|
||||
pub stride: Option<i32>,
|
||||
/// Offset in bytes of the first pixel of this image in its backing buffer.
|
||||
/// This is used for tiling, wherein WebRender extracts chunks of input images
|
||||
/// in order to cache, manipulate, and render them individually. This offset
|
||||
/// tells the texture upload machinery where to find the bytes to upload for
|
||||
/// this tile. Non-tiled images generally set this to zero.
|
||||
pub offset: i32,
|
||||
/// Various bool flags related to this descriptor.
|
||||
pub flags: ImageDescriptorFlags,
|
||||
}
|
||||
|
||||
impl ImageDescriptor {
|
||||
/// Mints a new ImageDescriptor.
|
||||
pub fn new(
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: ImageFormat,
|
||||
flags: ImageDescriptorFlags,
|
||||
) -> Self {
|
||||
ImageDescriptor {
|
||||
size: size2(width, height),
|
||||
format,
|
||||
stride: None,
|
||||
offset: 0,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the stride, either via an explicit stride stashed on the object
|
||||
/// or by the default computation.
|
||||
pub fn compute_stride(&self) -> i32 {
|
||||
self.stride.unwrap_or(self.size.width * self.format.bytes_per_pixel())
|
||||
}
|
||||
|
||||
/// Computes the total size of the image, in bytes.
|
||||
pub fn compute_total_size(&self) -> i32 {
|
||||
self.compute_stride() * self.size.height
|
||||
}
|
||||
|
||||
/// Computes the bounding rectangle for the image, rooted at (0, 0).
|
||||
pub fn full_rect(&self) -> DeviceIntRect {
|
||||
DeviceIntRect::new(
|
||||
DeviceIntPoint::zero(),
|
||||
self.size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this descriptor is opaque
|
||||
pub fn is_opaque(&self) -> bool {
|
||||
self.flags.contains(ImageDescriptorFlags::IS_OPAQUE)
|
||||
}
|
||||
|
||||
/// Returns true if this descriptor allows mipmaps
|
||||
pub fn allow_mipmaps(&self) -> bool {
|
||||
self.flags.contains(ImageDescriptorFlags::ALLOW_MIPMAPS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the backing store of an arbitrary series of pixels for display by
|
||||
/// WebRender. This storage can take several forms.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ImageData {
|
||||
/// A simple series of bytes, provided by the embedding and owned by WebRender.
|
||||
/// The format is stored out-of-band, currently in ImageDescriptor.
|
||||
Raw(#[serde(with = "serde_image_data_raw")] Arc<Vec<u8>>),
|
||||
/// An image owned by the embedding, and referenced by WebRender. This may
|
||||
/// take the form of a texture or a heap-allocated buffer.
|
||||
External(ExternalImageData),
|
||||
}
|
||||
|
||||
mod serde_image_data_raw {
|
||||
extern crate serde_bytes;
|
||||
|
||||
use std::sync::Arc;
|
||||
use serde::{Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S: Serializer>(bytes: &Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serde_bytes::serialize(bytes.as_slice(), serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Arc<Vec<u8>>, D::Error> {
|
||||
serde_bytes::deserialize(deserializer).map(Arc::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageData {
|
||||
/// Mints a new raw ImageData, taking ownership of the bytes.
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
ImageData::Raw(Arc::new(bytes))
|
||||
}
|
||||
|
||||
/// Mints a new raw ImageData from Arc-ed bytes.
|
||||
pub fn new_shared(bytes: Arc<Vec<u8>>) -> Self {
|
||||
ImageData::Raw(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// The resources exposed by the resource cache available for use by the blob rasterizer.
|
||||
pub trait BlobImageResources {
|
||||
/// Returns the `FontTemplate` for the given key.
|
||||
fn get_font_data(&self, key: FontKey) -> &FontTemplate;
|
||||
/// Returns the `FontInstanceData` for the given key, if found.
|
||||
fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData>;
|
||||
}
|
||||
|
||||
/// A handler on the render backend that can create rasterizer objects which will
|
||||
/// be sent to the scene builder thread to execute the rasterization.
|
||||
///
|
||||
/// The handler is responsible for collecting resources, managing/updating blob commands
|
||||
/// and creating the rasterizer objects, but isn't expected to do any rasterization itself.
|
||||
pub trait BlobImageHandler: Send {
|
||||
/// Creates a snapshot of the current state of blob images in the handler.
|
||||
fn create_blob_rasterizer(&mut self) -> Box<dyn AsyncBlobImageRasterizer>;
|
||||
|
||||
/// Creates an empty blob handler of the same type.
|
||||
///
|
||||
/// This is used to allow creating new API endpoints with blob handlers installed on them.
|
||||
fn create_similar(&self) -> Box<dyn BlobImageHandler>;
|
||||
|
||||
/// A hook to let the blob image handler update any state related to resources that
|
||||
/// are not bundled in the blob recording itself.
|
||||
fn prepare_resources(
|
||||
&mut self,
|
||||
services: &dyn BlobImageResources,
|
||||
requests: &[BlobImageParams],
|
||||
);
|
||||
|
||||
/// Register a blob image.
|
||||
fn add(&mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect,
|
||||
tile_size: TileSize);
|
||||
|
||||
/// Update an already registered blob image.
|
||||
fn update(&mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect,
|
||||
dirty_rect: &BlobDirtyRect);
|
||||
|
||||
/// Delete an already registered blob image.
|
||||
fn delete(&mut self, key: BlobImageKey);
|
||||
|
||||
/// A hook to let the handler clean up any state related to a font which the resource
|
||||
/// cache is about to delete.
|
||||
fn delete_font(&mut self, key: FontKey);
|
||||
|
||||
/// A hook to let the handler clean up any state related to a font instance which the
|
||||
/// resource cache is about to delete.
|
||||
fn delete_font_instance(&mut self, key: FontInstanceKey);
|
||||
|
||||
/// A hook to let the handler clean up any state related a given namespace before the
|
||||
/// resource cache deletes them.
|
||||
fn clear_namespace(&mut self, namespace: IdNamespace);
|
||||
|
||||
/// Whether to allow rendering blobs on multiple threads.
|
||||
fn enable_multithreading(&mut self, enable: bool);
|
||||
}
|
||||
|
||||
/// A group of rasterization requests to execute synchronously on the scene builder thread.
|
||||
pub trait AsyncBlobImageRasterizer : Send {
|
||||
/// Rasterize the requests.
|
||||
///
|
||||
/// Gecko uses te priority hint to schedule work in a way that minimizes the risk
|
||||
/// of high priority work being blocked by (or enqued behind) low priority work.
|
||||
fn rasterize(
|
||||
&mut self,
|
||||
requests: &[BlobImageParams],
|
||||
low_priority: bool
|
||||
) -> Vec<(BlobImageRequest, BlobImageResult)>;
|
||||
}
|
||||
|
||||
|
||||
/// Input parameters for the BlobImageRasterizer.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BlobImageParams {
|
||||
/// A key that identifies the blob image rasterization request.
|
||||
pub request: BlobImageRequest,
|
||||
/// Description of the format of the blob's output image.
|
||||
pub descriptor: BlobImageDescriptor,
|
||||
/// An optional sub-rectangle of the image to avoid re-rasterizing
|
||||
/// the entire image when only a portion is updated.
|
||||
///
|
||||
/// If set to None the entire image is rasterized.
|
||||
pub dirty_rect: BlobDirtyRect,
|
||||
}
|
||||
|
||||
/// The possible states of a Dirty rect.
|
||||
///
|
||||
/// This exists because people kept getting confused with `Option<Rect>`.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum DirtyRect<T: Copy, U> {
|
||||
/// Everything is Dirty, equivalent to Partial(image_bounds)
|
||||
All,
|
||||
/// Some specific amount is dirty
|
||||
Partial(Rect<T, U>)
|
||||
}
|
||||
|
||||
impl<T, U> DirtyRect<T, U>
|
||||
where
|
||||
T: Copy + Clone
|
||||
+ PartialOrd + PartialEq
|
||||
+ Add<T, Output = T>
|
||||
+ Sub<T, Output = T>
|
||||
+ Zero
|
||||
{
|
||||
/// Creates an empty DirtyRect (indicating nothing is invalid)
|
||||
pub fn empty() -> Self {
|
||||
DirtyRect::Partial(Rect::zero())
|
||||
}
|
||||
|
||||
/// Returns whether the dirty rect is empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
DirtyRect::All => false,
|
||||
DirtyRect::Partial(rect) => rect.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces self with the empty rect and returns the old value.
|
||||
pub fn replace_with_empty(&mut self) -> Self {
|
||||
::std::mem::replace(self, DirtyRect::empty())
|
||||
}
|
||||
|
||||
/// Maps over the contents of Partial.
|
||||
pub fn map<F>(self, func: F) -> Self
|
||||
where F: FnOnce(Rect<T, U>) -> Rect<T, U>,
|
||||
{
|
||||
use crate::DirtyRect::*;
|
||||
|
||||
match self {
|
||||
All => All,
|
||||
Partial(rect) => Partial(func(rect)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unions the dirty rects.
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
use crate::DirtyRect::*;
|
||||
|
||||
match (*self, *other) {
|
||||
(All, _) | (_, All) => All,
|
||||
(Partial(rect1), Partial(rect2)) => Partial(rect1.union(&rect2)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Intersects the dirty rects.
|
||||
pub fn intersection(&self, other: &Self) -> Self {
|
||||
use crate::DirtyRect::*;
|
||||
|
||||
match (*self, *other) {
|
||||
(All, rect) | (rect, All) => rect,
|
||||
(Partial(rect1), Partial(rect2)) => {
|
||||
Partial(rect1.intersection(&rect2).unwrap_or_else(Rect::zero))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the dirty rect into a subrect of the given one via intersection.
|
||||
pub fn to_subrect_of(&self, rect: &Rect<T, U>) -> Rect<T, U> {
|
||||
use crate::DirtyRect::*;
|
||||
|
||||
match *self {
|
||||
All => *rect,
|
||||
Partial(dirty_rect) => {
|
||||
dirty_rect.intersection(rect).unwrap_or_else(Rect::zero)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for DirtyRect<T, U> {}
|
||||
impl<T: Copy, U> Clone for DirtyRect<T, U> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<Rect<T, U>> for DirtyRect<T, U> {
|
||||
fn from(rect: Rect<T, U>) -> Self {
|
||||
DirtyRect::Partial(rect)
|
||||
}
|
||||
}
|
||||
|
||||
/// Backing store for blob image command streams.
|
||||
pub type BlobImageData = Vec<u8>;
|
||||
|
||||
/// Result type for blob raserization.
|
||||
pub type BlobImageResult = Result<RasterizedBlobImage, BlobImageError>;
|
||||
|
||||
/// Metadata (but not storage) for a blob image.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BlobImageDescriptor {
|
||||
/// Surface of the image or tile to render in the same coordinate space as
|
||||
/// the drawing commands.
|
||||
pub rect: LayoutIntRect,
|
||||
/// Format for the data in the backing store.
|
||||
pub format: ImageFormat,
|
||||
}
|
||||
|
||||
/// Representation of a rasterized blob image. This is obtained by passing
|
||||
/// `BlobImageData` to the embedding via the rasterization callback.
|
||||
pub struct RasterizedBlobImage {
|
||||
/// The rectangle that was rasterized in device pixels, relative to the
|
||||
/// image or tile.
|
||||
pub rasterized_rect: DeviceIntRect,
|
||||
/// Backing store. The format is stored out of band in `BlobImageDescriptor`.
|
||||
pub data: Arc<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Error code for when blob rasterization failed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BlobImageError {
|
||||
/// Out of memory.
|
||||
Oom,
|
||||
/// Other failure, embedding-specified.
|
||||
Other(String),
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// A key identifying blob image rasterization work requested from the blob
|
||||
/// image rasterizer.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct BlobImageRequest {
|
||||
/// Unique handle to the image.
|
||||
pub key: BlobImageKey,
|
||||
/// Tiling offset in number of tiles.
|
||||
pub tile: TileOffset,
|
||||
}
|
815
third_party/webrender/webrender_api/src/image_tiling.rs
vendored
Normal file
815
third_party/webrender/webrender_api/src/image_tiling.rs
vendored
Normal file
|
@ -0,0 +1,815 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{TileSize, EdgeAaSegmentMask};
|
||||
use crate::units::*;
|
||||
use euclid::{point2, size2};
|
||||
use std::i32;
|
||||
use std::ops::Range;
|
||||
|
||||
/// If repetitions are far enough apart that only one is within
|
||||
/// the primitive rect, then we can simplify the parameters and
|
||||
/// treat the primitive as not repeated.
|
||||
/// This can let us avoid unnecessary work later to handle some
|
||||
/// of the parameters.
|
||||
pub fn simplify_repeated_primitive(
|
||||
stretch_size: &LayoutSize,
|
||||
tile_spacing: &mut LayoutSize,
|
||||
prim_rect: &mut LayoutRect,
|
||||
) {
|
||||
let stride = *stretch_size + *tile_spacing;
|
||||
|
||||
if stride.width >= prim_rect.size.width {
|
||||
tile_spacing.width = 0.0;
|
||||
prim_rect.size.width = f32::min(prim_rect.size.width, stretch_size.width);
|
||||
}
|
||||
if stride.height >= prim_rect.size.height {
|
||||
tile_spacing.height = 0.0;
|
||||
prim_rect.size.height = f32::min(prim_rect.size.height, stretch_size.height);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Repetition {
|
||||
pub origin: LayoutPoint,
|
||||
pub edge_flags: EdgeAaSegmentMask,
|
||||
}
|
||||
|
||||
pub struct RepetitionIterator {
|
||||
current_x: i32,
|
||||
x_count: i32,
|
||||
current_y: i32,
|
||||
y_count: i32,
|
||||
row_flags: EdgeAaSegmentMask,
|
||||
current_origin: LayoutPoint,
|
||||
initial_origin: LayoutPoint,
|
||||
stride: LayoutSize,
|
||||
}
|
||||
|
||||
impl Iterator for RepetitionIterator {
|
||||
type Item = Repetition;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current_x == self.x_count {
|
||||
self.current_y += 1;
|
||||
if self.current_y >= self.y_count {
|
||||
return None;
|
||||
}
|
||||
self.current_x = 0;
|
||||
|
||||
self.row_flags = EdgeAaSegmentMask::empty();
|
||||
if self.current_y == self.y_count - 1 {
|
||||
self.row_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
|
||||
self.current_origin.x = self.initial_origin.x;
|
||||
self.current_origin.y += self.stride.height;
|
||||
}
|
||||
|
||||
let mut edge_flags = self.row_flags;
|
||||
if self.current_x == 0 {
|
||||
edge_flags |= EdgeAaSegmentMask::LEFT;
|
||||
}
|
||||
|
||||
if self.current_x == self.x_count - 1 {
|
||||
edge_flags |= EdgeAaSegmentMask::RIGHT;
|
||||
}
|
||||
|
||||
let repetition = Repetition {
|
||||
origin: self.current_origin,
|
||||
edge_flags,
|
||||
};
|
||||
|
||||
self.current_origin.x += self.stride.width;
|
||||
self.current_x += 1;
|
||||
|
||||
Some(repetition)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repetitions(
|
||||
prim_rect: &LayoutRect,
|
||||
visible_rect: &LayoutRect,
|
||||
stride: LayoutSize,
|
||||
) -> RepetitionIterator {
|
||||
assert!(stride.width > 0.0);
|
||||
assert!(stride.height > 0.0);
|
||||
|
||||
let visible_rect = match prim_rect.intersection(&visible_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return RepetitionIterator {
|
||||
current_origin: LayoutPoint::zero(),
|
||||
initial_origin: LayoutPoint::zero(),
|
||||
current_x: 0,
|
||||
current_y: 0,
|
||||
x_count: 0,
|
||||
y_count: 0,
|
||||
stride,
|
||||
row_flags: EdgeAaSegmentMask::empty(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let nx = if visible_rect.origin.x > prim_rect.origin.x {
|
||||
f32::floor((visible_rect.origin.x - prim_rect.origin.x) / stride.width)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let ny = if visible_rect.origin.y > prim_rect.origin.y {
|
||||
f32::floor((visible_rect.origin.y - prim_rect.origin.y) / stride.height)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let x0 = prim_rect.origin.x + nx * stride.width;
|
||||
let y0 = prim_rect.origin.y + ny * stride.height;
|
||||
|
||||
let x_most = visible_rect.max_x();
|
||||
let y_most = visible_rect.max_y();
|
||||
|
||||
let x_count = f32::ceil((x_most - x0) / stride.width) as i32;
|
||||
let y_count = f32::ceil((y_most - y0) / stride.height) as i32;
|
||||
|
||||
let mut row_flags = EdgeAaSegmentMask::TOP;
|
||||
if y_count == 1 {
|
||||
row_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
|
||||
RepetitionIterator {
|
||||
current_origin: LayoutPoint::new(x0, y0),
|
||||
initial_origin: LayoutPoint::new(x0, y0),
|
||||
current_x: 0,
|
||||
current_y: 0,
|
||||
x_count,
|
||||
y_count,
|
||||
row_flags,
|
||||
stride,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tile {
|
||||
pub rect: LayoutRect,
|
||||
pub offset: TileOffset,
|
||||
pub edge_flags: EdgeAaSegmentMask,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TileIteratorExtent {
|
||||
/// Range of visible tiles to iterate over in number of tiles.
|
||||
tile_range: Range<i32>,
|
||||
/// Range of tiles of the full image including tiles that are culled out.
|
||||
image_tiles: Range<i32>,
|
||||
/// Size of the first tile in layout space.
|
||||
first_tile_layout_size: f32,
|
||||
/// Size of the last tile in layout space.
|
||||
last_tile_layout_size: f32,
|
||||
/// Position of blob point (0, 0) in layout space.
|
||||
layout_tiling_origin: f32,
|
||||
/// Position of the top-left corner of the primitive rect in layout space.
|
||||
layout_prim_start: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TileIterator {
|
||||
current_tile: TileOffset,
|
||||
x: TileIteratorExtent,
|
||||
y: TileIteratorExtent,
|
||||
regular_tile_size: LayoutSize,
|
||||
}
|
||||
|
||||
impl Iterator for TileIterator {
|
||||
type Item = Tile;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// If we reach the end of a row, reset to the beginning of the next row.
|
||||
if self.current_tile.x >= self.x.tile_range.end {
|
||||
self.current_tile.y += 1;
|
||||
self.current_tile.x = self.x.tile_range.start;
|
||||
}
|
||||
|
||||
// Stop iterating if we reach the last tile. We may start here if there
|
||||
// were no tiles to iterate over.
|
||||
if self.current_tile.x >= self.x.tile_range.end || self.current_tile.y >= self.y.tile_range.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
let tile_offset = self.current_tile;
|
||||
|
||||
let mut segment_rect = LayoutRect {
|
||||
origin: LayoutPoint::new(
|
||||
self.x.layout_tiling_origin + tile_offset.x as f32 * self.regular_tile_size.width,
|
||||
self.y.layout_tiling_origin + tile_offset.y as f32 * self.regular_tile_size.height,
|
||||
),
|
||||
size: self.regular_tile_size,
|
||||
};
|
||||
|
||||
let mut edge_flags = EdgeAaSegmentMask::empty();
|
||||
|
||||
if tile_offset.x == self.x.image_tiles.start {
|
||||
edge_flags |= EdgeAaSegmentMask::LEFT;
|
||||
segment_rect.size.width = self.x.first_tile_layout_size;
|
||||
segment_rect.origin.x = self.x.layout_prim_start;
|
||||
}
|
||||
if tile_offset.x == self.x.image_tiles.end - 1 {
|
||||
edge_flags |= EdgeAaSegmentMask::RIGHT;
|
||||
segment_rect.size.width = self.x.last_tile_layout_size;
|
||||
}
|
||||
|
||||
if tile_offset.y == self.y.image_tiles.start {
|
||||
segment_rect.size.height = self.y.first_tile_layout_size;
|
||||
segment_rect.origin.y = self.y.layout_prim_start;
|
||||
edge_flags |= EdgeAaSegmentMask::TOP;
|
||||
}
|
||||
if tile_offset.y == self.y.image_tiles.end - 1 {
|
||||
segment_rect.size.height = self.y.last_tile_layout_size;
|
||||
edge_flags |= EdgeAaSegmentMask::BOTTOM;
|
||||
}
|
||||
|
||||
assert!(tile_offset.y < self.y.tile_range.end);
|
||||
let tile = Tile {
|
||||
rect: segment_rect,
|
||||
offset: tile_offset,
|
||||
edge_flags,
|
||||
};
|
||||
|
||||
self.current_tile.x += 1;
|
||||
|
||||
Some(tile)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tiles(
|
||||
prim_rect: &LayoutRect,
|
||||
visible_rect: &LayoutRect,
|
||||
image_rect: &DeviceIntRect,
|
||||
device_tile_size: i32,
|
||||
) -> TileIterator {
|
||||
// The image resource is tiled. We have to generate an image primitive
|
||||
// for each tile.
|
||||
// We need to do this because the image is broken up into smaller tiles in the texture
|
||||
// cache and the image shader is not able to work with this type of sparse representation.
|
||||
|
||||
// The tiling logic works as follows:
|
||||
//
|
||||
// +-#################-+ -+
|
||||
// | #//| | |//# | | image size
|
||||
// | #//| | |//# | |
|
||||
// +-#--+----+----+--#-+ | -+
|
||||
// | #//| | |//# | | | regular tile size
|
||||
// | #//| | |//# | | |
|
||||
// +-#--+----+----+--#-+ | -+-+
|
||||
// | #//|////|////|//# | | | "leftover" height
|
||||
// | ################# | -+ ---+
|
||||
// +----+----+----+----+
|
||||
//
|
||||
// In the ascii diagram above, a large image is split into tiles of almost regular size.
|
||||
// The tiles on the edges (hatched in the diagram) can be smaller than the regular tiles
|
||||
// and are handled separately in the code (we'll call them boundary tiles).
|
||||
//
|
||||
// Each generated segment corresponds to a tile in the texture cache, with the
|
||||
// assumption that the boundary tiles are sized to fit their own irregular size in the
|
||||
// texture cache.
|
||||
//
|
||||
// Because we can have very large virtual images we iterate over the visible portion of
|
||||
// the image in layer space instead of iterating over all device tiles.
|
||||
|
||||
let visible_rect = match prim_rect.intersection(&visible_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return TileIterator {
|
||||
current_tile: TileOffset::zero(),
|
||||
x: TileIteratorExtent {
|
||||
tile_range: 0..0,
|
||||
image_tiles: 0..0,
|
||||
first_tile_layout_size: 0.0,
|
||||
last_tile_layout_size: 0.0,
|
||||
layout_tiling_origin: 0.0,
|
||||
layout_prim_start: prim_rect.origin.x,
|
||||
},
|
||||
y: TileIteratorExtent {
|
||||
tile_range: 0..0,
|
||||
image_tiles: 0..0,
|
||||
first_tile_layout_size: 0.0,
|
||||
last_tile_layout_size: 0.0,
|
||||
layout_tiling_origin: 0.0,
|
||||
layout_prim_start: prim_rect.origin.y,
|
||||
},
|
||||
regular_tile_size: LayoutSize::zero(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Size of regular tiles in layout space.
|
||||
let layout_tile_size = LayoutSize::new(
|
||||
device_tile_size as f32 / image_rect.size.width as f32 * prim_rect.size.width,
|
||||
device_tile_size as f32 / image_rect.size.height as f32 * prim_rect.size.height,
|
||||
);
|
||||
|
||||
// The decomposition logic is exactly the same on each axis so we reduce
|
||||
// this to a 1-dimensional problem in an attempt to make the code simpler.
|
||||
|
||||
let x_extent = tiles_1d(
|
||||
layout_tile_size.width,
|
||||
visible_rect.x_range(),
|
||||
prim_rect.min_x(),
|
||||
image_rect.x_range(),
|
||||
device_tile_size,
|
||||
);
|
||||
|
||||
let y_extent = tiles_1d(
|
||||
layout_tile_size.height,
|
||||
visible_rect.y_range(),
|
||||
prim_rect.min_y(),
|
||||
image_rect.y_range(),
|
||||
device_tile_size,
|
||||
);
|
||||
|
||||
TileIterator {
|
||||
current_tile: point2(
|
||||
x_extent.tile_range.start,
|
||||
y_extent.tile_range.start,
|
||||
),
|
||||
x: x_extent,
|
||||
y: y_extent,
|
||||
regular_tile_size: layout_tile_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompose tiles along an arbitrary axis.
|
||||
///
|
||||
/// This does most of the heavy lifting needed for `tiles` but in a single dimension for
|
||||
/// the sake of simplicity since the problem is independent on the x and y axes.
|
||||
fn tiles_1d(
|
||||
layout_tile_size: f32,
|
||||
layout_visible_range: Range<f32>,
|
||||
layout_prim_start: f32,
|
||||
device_image_range: Range<i32>,
|
||||
device_tile_size: i32,
|
||||
) -> TileIteratorExtent {
|
||||
// A few sanity checks.
|
||||
debug_assert!(layout_tile_size > 0.0);
|
||||
debug_assert!(layout_visible_range.end >= layout_visible_range.start);
|
||||
debug_assert!(device_image_range.end > device_image_range.start);
|
||||
debug_assert!(device_tile_size > 0);
|
||||
|
||||
// Sizes of the boundary tiles in pixels.
|
||||
let first_tile_device_size = first_tile_size_1d(&device_image_range, device_tile_size);
|
||||
let last_tile_device_size = last_tile_size_1d(&device_image_range, device_tile_size);
|
||||
|
||||
// [start..end[ Range of tiles of this row/column (in number of tiles) without
|
||||
// taking culling into account.
|
||||
let image_tiles = tile_range_1d(&device_image_range, device_tile_size);
|
||||
|
||||
// Layout offset of tile (0, 0) with respect to the top-left corner of the display item.
|
||||
let layout_offset = device_image_range.start as f32 * layout_tile_size / device_tile_size as f32;
|
||||
// Position in layout space of tile (0, 0).
|
||||
let layout_tiling_origin = layout_prim_start - layout_offset;
|
||||
|
||||
// [start..end[ Range of the visible tiles (because of culling).
|
||||
let visible_tiles_start = f32::floor((layout_visible_range.start - layout_tiling_origin) / layout_tile_size) as i32;
|
||||
let visible_tiles_end = f32::ceil((layout_visible_range.end - layout_tiling_origin) / layout_tile_size) as i32;
|
||||
|
||||
// Combine the above two to get the tiles in the image that are visible this frame.
|
||||
let mut tiles_start = i32::max(image_tiles.start, visible_tiles_start);
|
||||
let tiles_end = i32::min(image_tiles.end, visible_tiles_end);
|
||||
if tiles_start > tiles_end {
|
||||
tiles_start = tiles_end;
|
||||
}
|
||||
|
||||
// The size in layout space of the boundary tiles.
|
||||
let first_tile_layout_size = if tiles_start == image_tiles.start {
|
||||
first_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
|
||||
} else {
|
||||
// boundary tile was culled out, so the new first tile is a regularly sized tile.
|
||||
layout_tile_size
|
||||
};
|
||||
|
||||
// Same here.
|
||||
let last_tile_layout_size = if tiles_end == image_tiles.end {
|
||||
last_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
|
||||
} else {
|
||||
layout_tile_size
|
||||
};
|
||||
|
||||
TileIteratorExtent {
|
||||
tile_range: tiles_start..tiles_end,
|
||||
image_tiles,
|
||||
first_tile_layout_size,
|
||||
last_tile_layout_size,
|
||||
layout_tiling_origin,
|
||||
layout_prim_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the range of tiles (in number of tiles) that intersect the provided
|
||||
/// image range (in pixels) in an arbitrary dimension.
|
||||
///
|
||||
/// ```ignore
|
||||
///
|
||||
/// 0
|
||||
/// :
|
||||
/// #-+---+---+---+---+---+--#
|
||||
/// # | | | | | | #
|
||||
/// #-+---+---+---+---+---+--#
|
||||
/// ^ : ^
|
||||
///
|
||||
/// +------------------------+ image_range
|
||||
/// +---+ regular_tile_size
|
||||
///
|
||||
/// ```
|
||||
fn tile_range_1d(
|
||||
image_range: &Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
) -> Range<i32> {
|
||||
// Integer division truncates towards zero so with negative values if the first/last
|
||||
// tile isn't a full tile we can get offset by one which we account for here.
|
||||
|
||||
let mut start = image_range.start / regular_tile_size;
|
||||
if image_range.start % regular_tile_size < 0 {
|
||||
start -= 1;
|
||||
}
|
||||
|
||||
let mut end = image_range.end / regular_tile_size;
|
||||
if image_range.end % regular_tile_size > 0 {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
start..end
|
||||
}
|
||||
|
||||
// Sizes of the first boundary tile in pixels.
|
||||
//
|
||||
// It can be smaller than the regular tile size if the image is not a multiple
|
||||
// of the regular tile size.
|
||||
fn first_tile_size_1d(
|
||||
image_range: &Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
) -> i32 {
|
||||
// We have to account for how the % operation behaves for negative values.
|
||||
let image_size = image_range.end - image_range.start;
|
||||
i32::min(
|
||||
match image_range.start % regular_tile_size {
|
||||
// . #------+------+ .
|
||||
// . #//////| | .
|
||||
0 => regular_tile_size,
|
||||
// (zero) -> 0 . #--+------+ .
|
||||
// . . #//| | .
|
||||
// %(m): ~~>
|
||||
m if m > 0 => regular_tile_size - m,
|
||||
// . . #--+------+ 0 <- (zero)
|
||||
// . . #//| | .
|
||||
// %(m): <~~
|
||||
m => -m,
|
||||
},
|
||||
image_size
|
||||
)
|
||||
}
|
||||
|
||||
// Sizes of the last boundary tile in pixels.
|
||||
//
|
||||
// It can be smaller than the regular tile size if the image is not a multiple
|
||||
// of the regular tile size.
|
||||
fn last_tile_size_1d(
|
||||
image_range: &Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
) -> i32 {
|
||||
// We have to account for how the modulo operation behaves for negative values.
|
||||
let image_size = image_range.end - image_range.start;
|
||||
i32::min(
|
||||
match image_range.end % regular_tile_size {
|
||||
// +------+------# .
|
||||
// tiles: . | |//////# .
|
||||
0 => regular_tile_size,
|
||||
// . +------+--# . 0 <- (zero)
|
||||
// . | |//# . .
|
||||
// modulo (m): <~~
|
||||
m if m < 0 => regular_tile_size + m,
|
||||
// (zero) -> 0 +------+--# . .
|
||||
// . | |//# . .
|
||||
// modulo (m): ~~>
|
||||
m => m,
|
||||
},
|
||||
image_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn compute_tile_rect(
|
||||
image_rect: &DeviceIntRect,
|
||||
regular_tile_size: TileSize,
|
||||
tile: TileOffset,
|
||||
) -> DeviceIntRect {
|
||||
let regular_tile_size = regular_tile_size as i32;
|
||||
DeviceIntRect {
|
||||
origin: point2(
|
||||
compute_tile_origin_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
|
||||
compute_tile_origin_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
|
||||
),
|
||||
size: size2(
|
||||
compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
|
||||
compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_tile_origin_1d(
|
||||
img_range: Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
tile_offset: i32,
|
||||
) -> i32 {
|
||||
let tile_range = tile_range_1d(&img_range, regular_tile_size);
|
||||
if tile_offset == tile_range.start {
|
||||
img_range.start
|
||||
} else {
|
||||
tile_offset * regular_tile_size
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the width and height in pixels of a tile depending on its position in the image.
|
||||
pub fn compute_tile_size(
|
||||
image_rect: &DeviceIntRect,
|
||||
regular_tile_size: TileSize,
|
||||
tile: TileOffset,
|
||||
) -> DeviceIntSize {
|
||||
let regular_tile_size = regular_tile_size as i32;
|
||||
size2(
|
||||
compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
|
||||
compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
|
||||
)
|
||||
}
|
||||
|
||||
fn compute_tile_size_1d(
|
||||
img_range: Range<i32>,
|
||||
regular_tile_size: i32,
|
||||
tile_offset: i32,
|
||||
) -> i32 {
|
||||
let tile_range = tile_range_1d(&img_range, regular_tile_size);
|
||||
|
||||
// Most tiles are going to have base_size as width and height,
|
||||
// except for tiles around the edges that are shrunk to fit the image data.
|
||||
let actual_size = if tile_offset == tile_range.start {
|
||||
first_tile_size_1d(&img_range, regular_tile_size)
|
||||
} else if tile_offset == tile_range.end - 1 {
|
||||
last_tile_size_1d(&img_range, regular_tile_size)
|
||||
} else {
|
||||
regular_tile_size
|
||||
};
|
||||
|
||||
assert!(actual_size > 0);
|
||||
|
||||
actual_size
|
||||
}
|
||||
|
||||
pub fn compute_tile_range(
|
||||
visible_area: &DeviceIntRect,
|
||||
tile_size: u16,
|
||||
) -> TileRange {
|
||||
let tile_size = tile_size as i32;
|
||||
let x_range = tile_range_1d(&visible_area.x_range(), tile_size);
|
||||
let y_range = tile_range_1d(&visible_area.y_range(), tile_size);
|
||||
|
||||
TileRange {
|
||||
origin: point2(x_range.start, y_range.start),
|
||||
size: size2(x_range.end - x_range.start, y_range.end - y_range.start),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each_tile_in_range(
|
||||
range: &TileRange,
|
||||
mut callback: impl FnMut(TileOffset),
|
||||
) {
|
||||
for y in range.y_range() {
|
||||
for x in range.x_range() {
|
||||
callback(point2(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_valid_tiles_if_bounds_change(
|
||||
prev_rect: &DeviceIntRect,
|
||||
new_rect: &DeviceIntRect,
|
||||
tile_size: u16,
|
||||
) -> Option<TileRange> {
|
||||
let intersection = match prev_rect.intersection(new_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return Some(TileRange::zero());
|
||||
}
|
||||
};
|
||||
|
||||
let left = prev_rect.min_x() != new_rect.min_x();
|
||||
let right = prev_rect.max_x() != new_rect.max_x();
|
||||
let top = prev_rect.min_y() != new_rect.min_y();
|
||||
let bottom = prev_rect.max_y() != new_rect.max_y();
|
||||
|
||||
if !left && !right && !top && !bottom {
|
||||
// Bounds have not changed.
|
||||
return None;
|
||||
}
|
||||
|
||||
let tw = 1.0 / (tile_size as f32);
|
||||
let th = 1.0 / (tile_size as f32);
|
||||
|
||||
let tiles = intersection
|
||||
.cast::<f32>()
|
||||
.scale(tw, th);
|
||||
|
||||
let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
|
||||
let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
|
||||
let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
|
||||
let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };
|
||||
|
||||
Some(TileRange {
|
||||
origin: point2(min_x as i32, min_y as i32),
|
||||
size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
use euclid::rect;
|
||||
|
||||
// this checks some additional invariants
|
||||
fn checked_for_each_tile(
|
||||
prim_rect: &LayoutRect,
|
||||
visible_rect: &LayoutRect,
|
||||
device_image_rect: &DeviceIntRect,
|
||||
device_tile_size: i32,
|
||||
callback: &mut dyn FnMut(&LayoutRect, TileOffset, EdgeAaSegmentMask),
|
||||
) {
|
||||
let mut coverage = LayoutRect::zero();
|
||||
let mut seen_tiles = HashSet::new();
|
||||
for tile in tiles(
|
||||
prim_rect,
|
||||
visible_rect,
|
||||
device_image_rect,
|
||||
device_tile_size,
|
||||
) {
|
||||
// make sure we don't get sent duplicate tiles
|
||||
assert!(!seen_tiles.contains(&tile.offset));
|
||||
seen_tiles.insert(tile.offset);
|
||||
coverage = coverage.union(&tile.rect);
|
||||
assert!(prim_rect.contains_rect(&tile.rect));
|
||||
callback(&tile.rect, tile.offset, tile.edge_flags);
|
||||
}
|
||||
assert!(prim_rect.contains_rect(&coverage));
|
||||
assert!(coverage.contains_rect(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut count = 0;
|
||||
checked_for_each_tile(&rect(0., 0., 1000., 1000.),
|
||||
&rect(75., 75., 400., 400.),
|
||||
&rect(0, 0, 400, 400),
|
||||
36,
|
||||
&mut |_tile_rect, _tile_offset, _tile_flags| {
|
||||
count += 1;
|
||||
},
|
||||
);
|
||||
assert_eq!(count, 36);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let mut count = 0;
|
||||
checked_for_each_tile(&rect(0., 0., 74., 74.),
|
||||
&rect(75., 75., 400., 400.),
|
||||
&rect(0, 0, 400, 400),
|
||||
36,
|
||||
&mut |_tile_rect, _tile_offset, _tile_flags| {
|
||||
count += 1;
|
||||
},
|
||||
);
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tiles_1d() {
|
||||
// Exactly one full tile at positive offset.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 0..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Exactly one full tile at negative offset.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..0, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 0);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Two full tiles at negative and positive offsets.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..64, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// One partial tile at positive offset, non-zero origin, culled out.
|
||||
let result = tiles_1d(64.0, -100.0..10.0, 64.0, 64..310, 64);
|
||||
assert_eq!(result.tile_range.start, result.tile_range.end);
|
||||
|
||||
// Two tiles at negative and positive offsets, one of which is culled out.
|
||||
// The remaining tile is partially culled but it should still generate a full tile.
|
||||
let result = tiles_1d(64.0, 10.0..10000.0, -64.0, -64..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
let result = tiles_1d(64.0, -10000.0..-10.0, -64.0, -64..64, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 0);
|
||||
assert_eq!(result.first_tile_layout_size, 64.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Stretched tile in layout space device tile size is 64 and layout tile size is 128.
|
||||
// So the resulting tile sizes in layout space should be multiplied by two.
|
||||
let result = tiles_1d(128.0, -10000.0..10000.0, -64.0, -64..32, 64);
|
||||
assert_eq!(result.tile_range.start, -1);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 128.0);
|
||||
assert_eq!(result.last_tile_layout_size, 64.0);
|
||||
|
||||
// Two visible tiles (the rest is culled out).
|
||||
let result = tiles_1d(10.0, 0.0..20.0, 0.0, 0..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 10.0);
|
||||
assert_eq!(result.last_tile_layout_size, 10.0);
|
||||
|
||||
// Two visible tiles at negative layout offsets (the rest is culled out).
|
||||
let result = tiles_1d(10.0, -20.0..0.0, -20.0, 0..64, 64);
|
||||
assert_eq!(result.tile_range.start, 0);
|
||||
assert_eq!(result.tile_range.end, 1);
|
||||
assert_eq!(result.first_tile_layout_size, 10.0);
|
||||
assert_eq!(result.last_tile_layout_size, 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tile_range_1d() {
|
||||
assert_eq!(tile_range_1d(&(0..256), 256), 0..1);
|
||||
assert_eq!(tile_range_1d(&(0..257), 256), 0..2);
|
||||
assert_eq!(tile_range_1d(&(-1..257), 256), -1..2);
|
||||
assert_eq!(tile_range_1d(&(-256..256), 256), -1..1);
|
||||
assert_eq!(tile_range_1d(&(-20..-10), 6), -4..-1);
|
||||
assert_eq!(tile_range_1d(&(20..100), 256), 0..1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_first_last_tile_size_1d() {
|
||||
assert_eq!(first_tile_size_1d(&(0..10), 64), 10);
|
||||
assert_eq!(first_tile_size_1d(&(-20..0), 64), 20);
|
||||
|
||||
assert_eq!(last_tile_size_1d(&(0..10), 64), 10);
|
||||
assert_eq!(last_tile_size_1d(&(-20..0), 64), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doubly_partial_tiles() {
|
||||
// In the following tests the image is a single tile and none of the sides of the tile
|
||||
// align with the tile grid.
|
||||
// This can only happen when we have a single non-aligned partial tile and no regular
|
||||
// tiles.
|
||||
assert_eq!(first_tile_size_1d(&(300..310), 64), 10);
|
||||
assert_eq!(first_tile_size_1d(&(-20..-10), 64), 10);
|
||||
|
||||
assert_eq!(last_tile_size_1d(&(300..310), 64), 10);
|
||||
assert_eq!(last_tile_size_1d(&(-20..-10), 64), 10);
|
||||
|
||||
|
||||
// One partial tile at positve offset, non-zero origin.
|
||||
let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 300..310, 64);
|
||||
assert_eq!(result.tile_range.start, 4);
|
||||
assert_eq!(result.tile_range.end, 5);
|
||||
assert_eq!(result.first_tile_layout_size, 10.0);
|
||||
assert_eq!(result.last_tile_layout_size, 10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smaller_than_tile_size_at_origin() {
|
||||
let r = compute_tile_rect(
|
||||
&rect(0, 0, 80, 80),
|
||||
256,
|
||||
point2(0, 0),
|
||||
);
|
||||
|
||||
assert_eq!(r, rect(0, 0, 80, 80));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smaller_than_tile_size_with_offset() {
|
||||
let r = compute_tile_rect(
|
||||
&rect(20, 20, 80, 80),
|
||||
256,
|
||||
point2(0, 0),
|
||||
);
|
||||
|
||||
assert_eq!(r, rect(20, 20, 80, 80));
|
||||
}
|
||||
}
|
64
third_party/webrender/webrender_api/src/lib.rs
vendored
Normal file
64
third_party/webrender/webrender_api/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* 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/. */
|
||||
|
||||
//! The `webrender_api` crate contains an assortment types and functions used
|
||||
//! by WebRender consumers as well as, in many cases, WebRender itself.
|
||||
//!
|
||||
//! This separation allows Servo to parallelize compilation across `webrender`
|
||||
//! and other crates that depend on `webrender_api`. So in practice, we put
|
||||
//! things in this crate when Servo needs to use them. Firefox depends on the
|
||||
//! `webrender` crate directly, and so this distinction is not really relevant
|
||||
//! there.
|
||||
|
||||
#![cfg_attr(feature = "nightly", feature(nonzero))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp, clippy::too_many_arguments))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::new_without_default))]
|
||||
|
||||
extern crate app_units;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate byteorder;
|
||||
#[cfg(feature = "nightly")]
|
||||
extern crate core;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate core_foundation;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate core_graphics;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
pub extern crate euclid;
|
||||
#[macro_use]
|
||||
extern crate malloc_size_of_derive;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate time;
|
||||
|
||||
extern crate malloc_size_of;
|
||||
extern crate peek_poke;
|
||||
|
||||
mod api;
|
||||
pub mod channel;
|
||||
mod color;
|
||||
mod display_item;
|
||||
mod display_item_cache;
|
||||
mod display_list;
|
||||
mod font;
|
||||
mod gradient_builder;
|
||||
mod image;
|
||||
mod resources;
|
||||
pub mod units;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod image_tiling;
|
||||
|
||||
pub use crate::api::*;
|
||||
pub use crate::color::*;
|
||||
pub use crate::display_item::*;
|
||||
pub use crate::display_item_cache::DisplayItemCache;
|
||||
pub use crate::display_list::*;
|
||||
pub use crate::font::*;
|
||||
pub use crate::gradient_builder::*;
|
||||
pub use crate::image::*;
|
||||
pub use crate::resources::DEFAULT_TILE_SIZE;
|
327
third_party/webrender/webrender_api/src/resources.rs
vendored
Normal file
327
third_party/webrender/webrender_api/src/resources.rs
vendored
Normal file
|
@ -0,0 +1,327 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, ResourceUpdate};
|
||||
use crate::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams};
|
||||
use crate::{BlobImageRequest, BlobImageDescriptor, BlobImageResources, TransactionMsg};
|
||||
use crate::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey, AddFont};
|
||||
use crate::image_tiling::*;
|
||||
use crate::units::*;
|
||||
use crate::font::SharedFontInstanceMap;
|
||||
use crate::euclid::{point2, size2};
|
||||
|
||||
pub const DEFAULT_TILE_SIZE: TileSize = 512;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// We use this to generate the async blob rendering requests.
|
||||
struct BlobImageTemplate {
|
||||
descriptor: ImageDescriptor,
|
||||
tile_size: TileSize,
|
||||
dirty_rect: BlobDirtyRect,
|
||||
/// See ImageResource::visible_rect.
|
||||
visible_rect: DeviceIntRect,
|
||||
// If the active rect of the blob changes, this represents the
|
||||
// range of tiles that remain valid. This must be taken into
|
||||
// account in addition to the valid rect when submitting blob
|
||||
// rasterization requests.
|
||||
// `None` means the bounds have not changed (tiles are still valid).
|
||||
// `Some(TileRange::zero())` means all of the tiles are invalid.
|
||||
valid_tiles_after_bounds_change: Option<TileRange>,
|
||||
}
|
||||
|
||||
struct FontResources {
|
||||
templates: HashMap<FontKey, FontTemplate>,
|
||||
instances: SharedFontInstanceMap,
|
||||
}
|
||||
|
||||
pub struct ApiResources {
|
||||
blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>,
|
||||
pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
|
||||
fonts: FontResources,
|
||||
}
|
||||
|
||||
impl BlobImageResources for FontResources {
|
||||
fn get_font_data(&self, key: FontKey) -> &FontTemplate {
|
||||
self.templates.get(&key).unwrap()
|
||||
}
|
||||
fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
|
||||
self.instances.get_font_instance_data(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiResources {
|
||||
pub fn new(
|
||||
blob_image_handler: Option<Box<dyn BlobImageHandler>>,
|
||||
instances: SharedFontInstanceMap,
|
||||
) -> Self {
|
||||
ApiResources {
|
||||
blob_image_templates: HashMap::new(),
|
||||
blob_image_handler,
|
||||
fonts: FontResources {
|
||||
templates: HashMap::new(),
|
||||
instances,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_shared_font_instances(&self) -> SharedFontInstanceMap {
|
||||
self.fonts.instances.clone()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, transaction: &mut TransactionMsg) {
|
||||
let mut blobs_to_rasterize = Vec::new();
|
||||
for update in &transaction.resource_updates {
|
||||
match *update {
|
||||
ResourceUpdate::AddBlobImage(ref img) => {
|
||||
self.blob_image_handler
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size);
|
||||
|
||||
self.blob_image_templates.insert(
|
||||
img.key,
|
||||
BlobImageTemplate {
|
||||
descriptor: img.descriptor,
|
||||
tile_size: img.tile_size,
|
||||
dirty_rect: DirtyRect::All,
|
||||
valid_tiles_after_bounds_change: None,
|
||||
visible_rect: img.visible_rect,
|
||||
},
|
||||
);
|
||||
blobs_to_rasterize.push(img.key);
|
||||
}
|
||||
ResourceUpdate::UpdateBlobImage(ref img) => {
|
||||
debug_assert_eq!(img.visible_rect.size, img.descriptor.size);
|
||||
self.update_blob_image(
|
||||
img.key,
|
||||
Some(&img.descriptor),
|
||||
Some(&img.dirty_rect),
|
||||
Some(Arc::clone(&img.data)),
|
||||
&img.visible_rect,
|
||||
);
|
||||
blobs_to_rasterize.push(img.key);
|
||||
}
|
||||
ResourceUpdate::DeleteBlobImage(key) => {
|
||||
self.blob_image_templates.remove(&key);
|
||||
}
|
||||
ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => {
|
||||
self.update_blob_image(*key, None, None, None, area);
|
||||
blobs_to_rasterize.push(*key);
|
||||
}
|
||||
ResourceUpdate::AddFont(ref font) => {
|
||||
match font {
|
||||
AddFont::Raw(key, bytes, index) => {
|
||||
self.fonts.templates.insert(
|
||||
*key,
|
||||
FontTemplate::Raw(Arc::clone(bytes), *index),
|
||||
);
|
||||
}
|
||||
AddFont::Native(key, native_font_handle) => {
|
||||
self.fonts.templates.insert(
|
||||
*key,
|
||||
FontTemplate::Native(native_font_handle.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ResourceUpdate::AddFontInstance(ref instance) => {
|
||||
// TODO(nical): Don't clone these.
|
||||
self.fonts.instances.add_font_instance(
|
||||
instance.key,
|
||||
instance.font_key,
|
||||
instance.glyph_size,
|
||||
instance.options.clone(),
|
||||
instance.platform_options.clone(),
|
||||
instance.variations.clone(),
|
||||
);
|
||||
}
|
||||
ResourceUpdate::DeleteFont(key) => {
|
||||
self.fonts.templates.remove(&key);
|
||||
if let Some(ref mut handler) = self.blob_image_handler {
|
||||
handler.delete_font(key);
|
||||
}
|
||||
}
|
||||
ResourceUpdate::DeleteFontInstance(key) => {
|
||||
// We will delete from the shared font instance map in the resource cache
|
||||
// after scene swap.
|
||||
|
||||
if let Some(ref mut r) = self.blob_image_handler {
|
||||
r.delete_font_instance(key);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize);
|
||||
transaction.blob_rasterizer = rasterizer;
|
||||
transaction.blob_requests = requests;
|
||||
}
|
||||
|
||||
pub fn enable_multithreading(&mut self, enable: bool) {
|
||||
if let Some(ref mut handler) = self.blob_image_handler {
|
||||
handler.enable_multithreading(enable);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_blob_image(
|
||||
&mut self,
|
||||
key: BlobImageKey,
|
||||
descriptor: Option<&ImageDescriptor>,
|
||||
dirty_rect: Option<&BlobDirtyRect>,
|
||||
data: Option<Arc<BlobImageData>>,
|
||||
visible_rect: &DeviceIntRect,
|
||||
) {
|
||||
if let Some(data) = data {
|
||||
let dirty_rect = dirty_rect.unwrap();
|
||||
self.blob_image_handler.as_mut().unwrap().update(key, data, visible_rect, dirty_rect);
|
||||
}
|
||||
|
||||
let image = self.blob_image_templates
|
||||
.get_mut(&key)
|
||||
.expect("Attempt to update non-existent blob image");
|
||||
|
||||
let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change(
|
||||
&image.visible_rect,
|
||||
visible_rect,
|
||||
image.tile_size,
|
||||
);
|
||||
|
||||
match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) {
|
||||
(Some(old), Some(ref mut new)) => {
|
||||
*new = new.intersection(&old).unwrap_or_else(TileRange::zero);
|
||||
}
|
||||
(Some(old), None) => {
|
||||
valid_tiles_after_bounds_change = Some(old);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let blob_size = visible_rect.size;
|
||||
|
||||
if let Some(descriptor) = descriptor {
|
||||
image.descriptor = *descriptor;
|
||||
} else {
|
||||
// make sure the descriptor size matches the visible rect.
|
||||
// This might not be necessary but let's stay on the safe side.
|
||||
image.descriptor.size = blob_size;
|
||||
}
|
||||
|
||||
if let Some(dirty_rect) = dirty_rect {
|
||||
image.dirty_rect = image.dirty_rect.union(dirty_rect);
|
||||
}
|
||||
|
||||
image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change;
|
||||
image.visible_rect = *visible_rect;
|
||||
}
|
||||
|
||||
pub fn create_blob_scene_builder_requests(
|
||||
&mut self,
|
||||
keys: &[BlobImageKey]
|
||||
) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) {
|
||||
if self.blob_image_handler.is_none() || keys.is_empty() {
|
||||
return (None, Vec::new());
|
||||
}
|
||||
|
||||
let mut blob_request_params = Vec::new();
|
||||
for key in keys {
|
||||
let template = self.blob_image_templates.get_mut(key).unwrap();
|
||||
|
||||
// If we know that only a portion of the blob image is in the viewport,
|
||||
// only request these visible tiles since blob images can be huge.
|
||||
let tiles = compute_tile_range(
|
||||
&template.visible_rect,
|
||||
template.tile_size,
|
||||
);
|
||||
|
||||
// Don't request tiles that weren't invalidated.
|
||||
let dirty_tiles = match template.dirty_rect {
|
||||
DirtyRect::Partial(dirty_rect) => {
|
||||
compute_tile_range(
|
||||
&dirty_rect.cast_unit(),
|
||||
template.tile_size,
|
||||
)
|
||||
}
|
||||
DirtyRect::All => tiles,
|
||||
};
|
||||
|
||||
for_each_tile_in_range(&tiles, |tile| {
|
||||
let still_valid = template.valid_tiles_after_bounds_change
|
||||
.map(|valid_tiles| valid_tiles.contains(tile))
|
||||
.unwrap_or(true);
|
||||
|
||||
if still_valid && !dirty_tiles.contains(tile) {
|
||||
return;
|
||||
}
|
||||
|
||||
let descriptor = BlobImageDescriptor {
|
||||
rect: compute_tile_rect(
|
||||
&template.visible_rect,
|
||||
template.tile_size,
|
||||
tile,
|
||||
).cast_unit(),
|
||||
format: template.descriptor.format,
|
||||
};
|
||||
|
||||
assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height > 0);
|
||||
blob_request_params.push(
|
||||
BlobImageParams {
|
||||
request: BlobImageRequest { key: *key, tile },
|
||||
descriptor,
|
||||
dirty_rect: DirtyRect::All,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
template.dirty_rect = DirtyRect::empty();
|
||||
template.valid_tiles_after_bounds_change = None;
|
||||
}
|
||||
|
||||
let handler = self.blob_image_handler.as_mut().unwrap();
|
||||
handler.prepare_resources(&self.fonts, &blob_request_params);
|
||||
(Some(handler.create_blob_rasterizer()), blob_request_params)
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_valid_tiles_if_bounds_change(
|
||||
prev_rect: &DeviceIntRect,
|
||||
new_rect: &DeviceIntRect,
|
||||
tile_size: u16,
|
||||
) -> Option<TileRange> {
|
||||
let intersection = match prev_rect.intersection(new_rect) {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return Some(TileRange::zero());
|
||||
}
|
||||
};
|
||||
|
||||
let left = prev_rect.min_x() != new_rect.min_x();
|
||||
let right = prev_rect.max_x() != new_rect.max_x();
|
||||
let top = prev_rect.min_y() != new_rect.min_y();
|
||||
let bottom = prev_rect.max_y() != new_rect.max_y();
|
||||
|
||||
if !left && !right && !top && !bottom {
|
||||
// Bounds have not changed.
|
||||
return None;
|
||||
}
|
||||
|
||||
let tw = 1.0 / (tile_size as f32);
|
||||
let th = 1.0 / (tile_size as f32);
|
||||
|
||||
let tiles = intersection
|
||||
.cast::<f32>()
|
||||
.scale(tw, th);
|
||||
|
||||
let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
|
||||
let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
|
||||
let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
|
||||
let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };
|
||||
|
||||
Some(TileRange {
|
||||
origin: point2(min_x as i32, min_y as i32),
|
||||
size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
|
||||
})
|
||||
}
|
324
third_party/webrender/webrender_api/src/units.rs
vendored
Normal file
324
third_party/webrender/webrender_api/src/units.rs
vendored
Normal file
|
@ -0,0 +1,324 @@
|
|||
/* 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/. */
|
||||
|
||||
//! A collection of coordinate spaces and their corresponding Point, Size and Rect types.
|
||||
//!
|
||||
//! Physical pixels take into account the device pixel ratio and their dimensions tend
|
||||
//! to correspond to the allocated size of resources in memory, while logical pixels
|
||||
//! don't have the device pixel ratio applied which means they are agnostic to the usage
|
||||
//! of hidpi screens and the like.
|
||||
//!
|
||||
//! The terms "layer" and "stacking context" can be used interchangeably
|
||||
//! in the context of coordinate systems.
|
||||
|
||||
pub use app_units::Au;
|
||||
use euclid::{Length, Rect, Scale, Size2D, Transform3D, Translation2D};
|
||||
use euclid::{Point2D, Point3D, Vector2D, Vector3D, SideOffsets2D, Box2D};
|
||||
use euclid::HomogeneousVector;
|
||||
use peek_poke::PeekPoke;
|
||||
// local imports
|
||||
use crate::image::DirtyRect;
|
||||
|
||||
/// Geometry in the coordinate system of the render target (screen or intermediate
|
||||
/// surface) in physical pixels.
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct DevicePixel;
|
||||
|
||||
pub type DeviceIntRect = Rect<i32, DevicePixel>;
|
||||
pub type DeviceIntPoint = Point2D<i32, DevicePixel>;
|
||||
pub type DeviceIntSize = Size2D<i32, DevicePixel>;
|
||||
pub type DeviceIntLength = Length<i32, DevicePixel>;
|
||||
pub type DeviceIntSideOffsets = SideOffsets2D<i32, DevicePixel>;
|
||||
|
||||
pub type DeviceRect = Rect<f32, DevicePixel>;
|
||||
pub type DevicePoint = Point2D<f32, DevicePixel>;
|
||||
pub type DeviceVector2D = Vector2D<f32, DevicePixel>;
|
||||
pub type DeviceSize = Size2D<f32, DevicePixel>;
|
||||
pub type DeviceHomogeneousVector = HomogeneousVector<f32, DevicePixel>;
|
||||
|
||||
/// Geometry in the coordinate system of the framebuffer in physical pixels.
|
||||
/// It's Y-flipped comparing to DevicePixel.
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct FramebufferPixel;
|
||||
|
||||
pub type FramebufferIntPoint = Point2D<i32, FramebufferPixel>;
|
||||
pub type FramebufferIntSize = Size2D<i32, FramebufferPixel>;
|
||||
pub type FramebufferIntRect = Rect<i32, FramebufferPixel>;
|
||||
|
||||
/// Geometry in the coordinate system of a Picture (intermediate
|
||||
/// surface) in physical pixels.
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct PicturePixel;
|
||||
|
||||
pub type PictureIntRect = Rect<i32, PicturePixel>;
|
||||
pub type PictureIntPoint = Point2D<i32, PicturePixel>;
|
||||
pub type PictureIntSize = Size2D<i32, PicturePixel>;
|
||||
pub type PictureRect = Rect<f32, PicturePixel>;
|
||||
pub type PicturePoint = Point2D<f32, PicturePixel>;
|
||||
pub type PictureSize = Size2D<f32, PicturePixel>;
|
||||
pub type PicturePoint3D = Point3D<f32, PicturePixel>;
|
||||
pub type PictureVector2D = Vector2D<f32, PicturePixel>;
|
||||
pub type PictureVector3D = Vector3D<f32, PicturePixel>;
|
||||
pub type PictureBox2D = Box2D<f32, PicturePixel>;
|
||||
|
||||
/// Geometry gets rasterized in a given root coordinate space. This
|
||||
/// is often the root spatial node (world space), but may be a local
|
||||
/// space for a variety of reasons (e.g. perspective).
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct RasterPixel;
|
||||
|
||||
pub type RasterIntRect = Rect<i32, RasterPixel>;
|
||||
pub type RasterIntPoint = Point2D<i32, RasterPixel>;
|
||||
pub type RasterIntSize = Size2D<i32, RasterPixel>;
|
||||
pub type RasterRect = Rect<f32, RasterPixel>;
|
||||
pub type RasterPoint = Point2D<f32, RasterPixel>;
|
||||
pub type RasterSize = Size2D<f32, RasterPixel>;
|
||||
pub type RasterPoint3D = Point3D<f32, RasterPixel>;
|
||||
pub type RasterVector2D = Vector2D<f32, RasterPixel>;
|
||||
pub type RasterVector3D = Vector3D<f32, RasterPixel>;
|
||||
|
||||
/// Geometry in a stacking context's local coordinate space (logical pixels).
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Ord, PartialOrd, Deserialize, Serialize, PeekPoke)]
|
||||
pub struct LayoutPixel;
|
||||
|
||||
pub type LayoutRect = Rect<f32, LayoutPixel>;
|
||||
pub type LayoutPoint = Point2D<f32, LayoutPixel>;
|
||||
pub type LayoutPoint3D = Point3D<f32, LayoutPixel>;
|
||||
pub type LayoutVector2D = Vector2D<f32, LayoutPixel>;
|
||||
pub type LayoutVector3D = Vector3D<f32, LayoutPixel>;
|
||||
pub type LayoutSize = Size2D<f32, LayoutPixel>;
|
||||
pub type LayoutSideOffsets = SideOffsets2D<f32, LayoutPixel>;
|
||||
|
||||
pub type LayoutIntRect = Rect<i32, LayoutPixel>;
|
||||
pub type LayoutIntPoint = Point2D<i32, LayoutPixel>;
|
||||
pub type LayoutIntSize = Size2D<i32, LayoutPixel>;
|
||||
|
||||
/// Geometry in the document's coordinate space (logical pixels).
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Ord, PartialOrd)]
|
||||
pub struct WorldPixel;
|
||||
|
||||
pub type WorldRect = Rect<f32, WorldPixel>;
|
||||
pub type WorldPoint = Point2D<f32, WorldPixel>;
|
||||
pub type WorldSize = Size2D<f32, WorldPixel>;
|
||||
pub type WorldPoint3D = Point3D<f32, WorldPixel>;
|
||||
pub type WorldVector2D = Vector2D<f32, WorldPixel>;
|
||||
pub type WorldVector3D = Vector3D<f32, WorldPixel>;
|
||||
|
||||
/// Offset in number of tiles.
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Tiles;
|
||||
pub type TileOffset = Point2D<i32, Tiles>;
|
||||
pub type TileRange = Rect<i32, Tiles>;
|
||||
|
||||
/// Scaling ratio from world pixels to device pixels.
|
||||
pub type DevicePixelScale = Scale<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 = Scale<f32, LayoutPixel, WorldPixel>;
|
||||
/// A complete scaling ratio from layout space to device pixel space.
|
||||
pub type LayoutToDeviceScale = Scale<f32, LayoutPixel, DevicePixel>;
|
||||
|
||||
pub type LayoutTransform = Transform3D<f32, LayoutPixel, LayoutPixel>;
|
||||
pub type LayoutToWorldTransform = Transform3D<f32, LayoutPixel, WorldPixel>;
|
||||
pub type WorldToLayoutTransform = Transform3D<f32, WorldPixel, LayoutPixel>;
|
||||
|
||||
pub type LayoutToPictureTransform = Transform3D<f32, LayoutPixel, PicturePixel>;
|
||||
pub type PictureToLayoutTransform = Transform3D<f32, PicturePixel, LayoutPixel>;
|
||||
|
||||
pub type LayoutToRasterTransform = Transform3D<f32, LayoutPixel, RasterPixel>;
|
||||
pub type RasterToLayoutTransform = Transform3D<f32, RasterPixel, LayoutPixel>;
|
||||
|
||||
pub type PictureToRasterTransform = Transform3D<f32, PicturePixel, RasterPixel>;
|
||||
pub type RasterToPictureTransform = Transform3D<f32, RasterPixel, PicturePixel>;
|
||||
|
||||
// Fixed position coordinates, to avoid float precision errors.
|
||||
pub type LayoutPointAu = Point2D<Au, LayoutPixel>;
|
||||
pub type LayoutRectAu = Rect<Au, LayoutPixel>;
|
||||
pub type LayoutSizeAu = Size2D<Au, LayoutPixel>;
|
||||
pub type LayoutVector2DAu = Vector2D<Au, LayoutPixel>;
|
||||
pub type LayoutSideOffsetsAu = SideOffsets2D<Au, LayoutPixel>;
|
||||
|
||||
pub type ImageDirtyRect = DirtyRect<i32, DevicePixel>;
|
||||
pub type BlobDirtyRect = DirtyRect<i32, LayoutPixel>;
|
||||
|
||||
pub type BlobToDeviceTranslation = Translation2D<i32, LayoutPixel, DevicePixel>;
|
||||
|
||||
/// Stores two coordinates in texel space. The coordinates
|
||||
/// are stored in texel coordinates because the texture atlas
|
||||
/// may grow. Storing them as texel coords and normalizing
|
||||
/// the UVs in the vertex shader means nothing needs to be
|
||||
/// updated on the CPU when the texture size changes.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TexelRect {
|
||||
pub uv0: DevicePoint,
|
||||
pub uv1: DevicePoint,
|
||||
}
|
||||
|
||||
impl TexelRect {
|
||||
pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> Self {
|
||||
TexelRect {
|
||||
uv0: DevicePoint::new(u0, v0),
|
||||
uv1: DevicePoint::new(u1, v1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid() -> Self {
|
||||
TexelRect {
|
||||
uv0: DevicePoint::new(-1.0, -1.0),
|
||||
uv1: DevicePoint::new(-1.0, -1.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<TexelRect> for DeviceIntRect {
|
||||
fn into(self) -> TexelRect {
|
||||
TexelRect {
|
||||
uv0: self.min().to_f32(),
|
||||
uv1: self.max().to_f32(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_AU_FLOAT: f32 = 1.0e6;
|
||||
|
||||
pub trait AuHelpers<T> {
|
||||
fn from_au(data: T) -> Self;
|
||||
fn to_au(&self) -> T;
|
||||
}
|
||||
|
||||
impl AuHelpers<LayoutSizeAu> for LayoutSize {
|
||||
fn from_au(size: LayoutSizeAu) -> Self {
|
||||
LayoutSize::new(
|
||||
size.width.to_f32_px(),
|
||||
size.height.to_f32_px(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_au(&self) -> LayoutSizeAu {
|
||||
let width = self.width.min(2.0 * MAX_AU_FLOAT);
|
||||
let height = self.height.min(2.0 * MAX_AU_FLOAT);
|
||||
|
||||
LayoutSizeAu::new(
|
||||
Au::from_f32_px(width),
|
||||
Au::from_f32_px(height),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AuHelpers<LayoutVector2DAu> for LayoutVector2D {
|
||||
fn from_au(size: LayoutVector2DAu) -> Self {
|
||||
LayoutVector2D::new(
|
||||
size.x.to_f32_px(),
|
||||
size.y.to_f32_px(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_au(&self) -> LayoutVector2DAu {
|
||||
LayoutVector2DAu::new(
|
||||
Au::from_f32_px(self.x),
|
||||
Au::from_f32_px(self.y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AuHelpers<LayoutPointAu> for LayoutPoint {
|
||||
fn from_au(point: LayoutPointAu) -> Self {
|
||||
LayoutPoint::new(
|
||||
point.x.to_f32_px(),
|
||||
point.y.to_f32_px(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_au(&self) -> LayoutPointAu {
|
||||
let x = self.x.min(MAX_AU_FLOAT).max(-MAX_AU_FLOAT);
|
||||
let y = self.y.min(MAX_AU_FLOAT).max(-MAX_AU_FLOAT);
|
||||
|
||||
LayoutPointAu::new(
|
||||
Au::from_f32_px(x),
|
||||
Au::from_f32_px(y),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AuHelpers<LayoutRectAu> for LayoutRect {
|
||||
fn from_au(rect: LayoutRectAu) -> Self {
|
||||
LayoutRect::new(
|
||||
LayoutPoint::from_au(rect.origin),
|
||||
LayoutSize::from_au(rect.size),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_au(&self) -> LayoutRectAu {
|
||||
LayoutRectAu::new(
|
||||
self.origin.to_au(),
|
||||
self.size.to_au(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AuHelpers<LayoutSideOffsetsAu> for LayoutSideOffsets {
|
||||
fn from_au(offsets: LayoutSideOffsetsAu) -> Self {
|
||||
LayoutSideOffsets::new(
|
||||
offsets.top.to_f32_px(),
|
||||
offsets.right.to_f32_px(),
|
||||
offsets.bottom.to_f32_px(),
|
||||
offsets.left.to_f32_px(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_au(&self) -> LayoutSideOffsetsAu {
|
||||
LayoutSideOffsetsAu::new(
|
||||
Au::from_f32_px(self.top),
|
||||
Au::from_f32_px(self.right),
|
||||
Au::from_f32_px(self.bottom),
|
||||
Au::from_f32_px(self.left),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RectExt {
|
||||
type Point;
|
||||
fn top_left(&self) -> Self::Point;
|
||||
fn top_right(&self) -> Self::Point;
|
||||
fn bottom_left(&self) -> Self::Point;
|
||||
fn bottom_right(&self) -> Self::Point;
|
||||
}
|
||||
|
||||
impl<U> RectExt for Rect<f32, U> {
|
||||
type Point = Point2D<f32, U>;
|
||||
fn top_left(&self) -> Self::Point {
|
||||
self.min()
|
||||
}
|
||||
fn top_right(&self) -> Self::Point {
|
||||
Point2D::new(self.max_x(), self.min_y())
|
||||
}
|
||||
fn bottom_left(&self) -> Self::Point {
|
||||
Point2D::new(self.min_x(), self.max_y())
|
||||
}
|
||||
fn bottom_right(&self) -> Self::Point {
|
||||
self.max()
|
||||
}
|
||||
}
|
||||
|
||||
// A few helpers to convert to cast between coordinate spaces that are often equivalent.
|
||||
|
||||
#[inline]
|
||||
pub fn layout_rect_as_picture_rect(layout_rect: &LayoutRect) -> PictureRect {
|
||||
layout_rect.cast_unit()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn layout_vector_as_picture_vector(layout_vector: LayoutVector2D) -> PictureVector2D {
|
||||
layout_vector.cast_unit()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn device_size_as_framebuffer_size(framebuffer_size: DeviceIntSize) -> FramebufferIntSize {
|
||||
framebuffer_size.cast_unit()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn device_rect_as_framebuffer_rect(framebuffer_rect: &DeviceIntRect) -> FramebufferIntRect {
|
||||
framebuffer_rect.cast_unit()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue