├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── rust-toolchain ├── rustfmt.toml ├── src ├── build │ ├── bincode │ │ ├── display_list_builder.rs │ │ ├── display_list_differs.rs │ │ ├── export.rs │ │ ├── types.rs │ │ └── util.rs │ ├── json │ │ ├── display_list_builder.rs │ │ ├── display_list_differs.rs │ │ ├── export.rs │ │ ├── types.rs │ │ └── util.rs │ ├── mod.rs │ ├── traits.rs │ └── webrender │ │ ├── display_list_builder.rs │ │ ├── display_list_differs.rs │ │ ├── types.rs │ │ └── util.rs ├── compare │ ├── export.rs │ ├── mod.rs │ ├── traits.rs │ └── types.rs ├── convert │ ├── convert_image_node.rs │ ├── convert_inline_text_node.rs │ ├── convert_view_node.rs │ └── mod.rs ├── debug.rs ├── lib.rs ├── macros.rs ├── prelude.rs └── types.rs └── tests ├── fixtures ├── FiraMono-Regular.ttf ├── FreeSans.ttf ├── Quantum.png ├── test_1.css └── test_2.css ├── spec_bincode.rs ├── spec_diff.rs ├── spec_json.rs └── spec_webrender.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | Cargo.lock 3 | **/*.rs.bk 4 | .vscode 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | os: 3 | - linux 4 | # - osx 5 | rust: 6 | - nightly 7 | notifications: 8 | email: false 9 | before_script: 10 | - cargo +nightly install rustfmt-nightly --force 11 | - cargo +nightly install clippy --force 12 | - export PATH=$PATH:~/.cargo/bin 13 | script: 14 | - cargo build --verbose 15 | - cargo test --no-default-features --features json-display-list,dummy-api-mode,hashmap-arena,link-freetype --verbose 16 | - cargo test --no-default-features --features webrender-display-list,dummy-api-mode,hashmap-arena,link-freetype --verbose 17 | - cargo +nightly fmt --all -- --write-mode=diff 18 | - cargo +nightly clippy --all -- --deny warnings 19 | after_success: 20 | - | 21 | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" ]]; then 22 | cargo doc && 23 | echo "" > target/doc/index.html && 24 | git clone https://github.com/davisp/ghp-import.git && 25 | ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc && 26 | echo "Uploaded documentation" 27 | fi 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rsx-primitives" 3 | version = "0.1.0" 4 | authors = ["Victor Porof "] 5 | 6 | [lib] 7 | name = "rsx_primitives" 8 | 9 | [features] 10 | default = ["bincode-display-list", "json-display-list-types", "webrender-display-list-types", "hashmap-arena"] 11 | 12 | bincode-display-list = ["bincode-display-list-types", "bincode-display-list-aliases"] 13 | bincode-display-list-types = [] 14 | bincode-display-list-aliases = [] 15 | 16 | json-display-list = ["json-display-list-types", "json-display-list-aliases"] 17 | json-display-list-types = [] 18 | json-display-list-aliases = [] 19 | 20 | webrender-display-list = ["app_units", "euclid", "webrender", "link-freetype", "image-rgb-to-bgr", "webrender-display-list-types", "webrender-display-list-aliases"] 21 | webrender-display-list-types = ["rsx-shared/impl-external-webrender"] 22 | webrender-display-list-aliases = [] 23 | 24 | display-list-optimize-mode = [] 25 | display-list-debug-mode = [] 26 | dummy-api-mode = ["rand"] 27 | no-json-glyphs-mode = [] 28 | no-json-images-mode = [] 29 | 30 | vec-arena = ["rsx-dom/vec-arena"] 31 | hashmap-arena = ["rsx-dom/hashmap-arena"] 32 | 33 | link-freetype = ["rsx-resources/link-freetype"] 34 | image-dummy-decode = ["rsx-resources/image-dummy-decode"] 35 | image-rgb-to-bgr = ["rsx-resources/image-rgb-to-bgr"] 36 | pretty-json-mode = ["rsx-resources/pretty-json-mode"] 37 | 38 | [dependencies] 39 | rsx-dom = { git = "https://github.com/victorporof/rsx-dom.git", default-features = false } 40 | rsx-event-manager = { git = "https://github.com/victorporof/rsx-event-manager.git", default-features = false } 41 | rsx-layout = { git = "https://github.com/victorporof/rsx-layout.git", default-features = false } 42 | rsx-resources = { git = "https://github.com/victorporof/rsx-resources.git", default-features = false } 43 | rsx-shared = { git = "https://github.com/victorporof/rsx-shared.git", default-features = false } 44 | rsx-stylesheet = { git = "https://github.com/victorporof/rsx-stylesheet.git", default-features = false } 45 | serde = "1.0.27" 46 | serde_derive = "1.0.27" 47 | serde_json = "1.0.9" 48 | bincode = "1.0.0" 49 | smallvec = "0.6.0" 50 | 51 | # Optional 52 | app_units = { version = "0.6", optional = true } 53 | euclid = { version = "0.16", optional = true } 54 | rand = { version = "0.4.2", optional = true } 55 | webrender = { git = "https://github.com/victorporof/webrender.git", branch = "rsx-ready-latest", optional = true } 56 | 57 | [dev-dependencies] 58 | rsx = { git = "https://github.com/victorporof/rsx.git", default-features = false } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Under heavy research and development, please don't use this yet!** 2 | 3 | # rsx-primitives 4 | [![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0) 5 | [![Build Status](https://travis-ci.org/victorporof/rsx-primitives.svg?branch=master)](https://travis-ci.org/victorporof/rsx-primitives) 6 | 7 | Basic component primitives necessary to render any [RSX code](https://github.com/victorporof/rsx), inspired by [react-primitives](https://github.com/lelandrichardson/react-primitives). Turns a [RSX LayoutTree](https://github.com/victorporof/rsx-layout) into a [Servo WebRender DisplayList](http://doc.servo.org/webrender/api/struct.BuiltDisplayList.html) or a simpler JSON-based DisplayList backed by [Serde](https://github.com/serde-rs/json). 8 | 9 | ## Purpose 10 | Display lists are a final abstraction layer for all the data structures generated when writing RSX code in a project, before being sent to a renderer. 11 | 12 | ## How to use 13 | [Documentation](https://victorporof.github.io/rsx-primitives) 14 | 15 | This crate concerns itself strictly generating a display list. If you're just looking to write RSX in your project, take a look at the [RSX compiler plugin](https://github.com/victorporof/rsx-compiler-plugin) instead. 16 | 17 | Otherwise, add this to your `Cargo.toml` file: 18 | 19 | ```toml 20 | [dependencies] 21 | rsx = { git = "https://github.com/victorporof/rsx.git" } 22 | rsx-primitives = { git = "https://github.com/victorporof/rsx-primitives.git" } 23 | ``` 24 | 25 | By default, this crate builds Servo Webrender specific display lists. To opt into other kinds of data structures, use the `json-display-list` or `webrender-display-list` feature gates. 26 | 27 | For example, to only build JSON-based display lists: 28 | 29 | ```toml 30 | [dependencies] 31 | rsx-primitives = { git = "https://github.com/victorporof/rsx-primitives.git", default-features = false, features = ["json-display-list"] } 32 | ``` 33 | 34 | Then, simply import the library into your code to generate a Servo WebRender-powered `webrender::api::BuiltDisplayList` (wrapped as `rsx_primitives::DisplayList`) from RSX layout trees, or a `String` JSON representing a display list using Serde. See the [WebRender's documentation](https://github.com/servo/webrender/wiki) for a complete list of all the data structures used in WebRender, and the [Serde JSON documentation](https://github.com/serde-rs/json#operating-on-untyped-json-values) for the data structures used for representing JSON. 35 | 36 | ```rust 37 | #![feature(proc_macro)] 38 | 39 | extern crate rsx; 40 | extern crate rsx_primitives; 41 | 42 | use rsx::{rsx, css}; 43 | use rsx_primitives::types::DisplayList; 44 | use rsx_primitives::rsx_layout::types::{LayoutDirection, LayoutTree, LayoutRectTree}; 45 | use rsx_primitives::rsx_resources::files::types::*; 46 | use rsx_primitives::rsx_resources::fonts::types::*; 47 | use rsx_primitives::rsx_stylesheet::types::*; 48 | use rsx_primitives::rsx_dom::types::*; 49 | 50 | let mut stylesheet: Stylesheet = css! { ... }; 51 | let node: Node = rsx! { ... }; 52 | 53 | // Create a layout tree with a DOM node as root. 54 | let mut layout: LayoutTree = LayoutTree::from(&node); 55 | 56 | // Calculate bounding client rects for subtree starting with a DOM node. 57 | let width = 1024.0; 58 | let height = 768.0; 59 | layout.reflow_subtree(&node, width, height, LayoutDirection::LTR); 60 | 61 | // Get a list of all bounding client rects for subtree starting with a DOM node. 62 | let rects: Option = layout.get_bounding_client_rects_for_subtree(&node); 63 | 64 | // Get a list of display instructions from a DOM node and computed bounding client rects. 65 | let display_list: DisplayList = DisplayList::from(&rects.unwrap(), width, height); 66 | ``` 67 | 68 | *Note: `rsx-primitives` also re-exports the `rsx-dom` [crate](https://github.com/victorporof/rsx-dom), the `rsx-stylesheet` [crate](https://github.com/victorporof/rsx-stylesheet), the `rsx-layout` [crate](https://github.com/victorporof/rsx-layout) and the `rsx-resources` [crate](https://github.com/victorporof/rsx-resources).* 69 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2017-11-14 -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | error_on_line_overflow = false 2 | format_strings = true 3 | imports_indent = "Block" 4 | imports_layout = "HorizontalVertical" 5 | max_width = 140 6 | reorder_extern_crates = true 7 | reorder_extern_crates_in_group = true 8 | reorder_imported_names = true 9 | reorder_imports = true 10 | reorder_imports_in_group = true 11 | trailing_comma = "Never" -------------------------------------------------------------------------------- /src/build/bincode/display_list_builder.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_dom::types::DOMText; 13 | use rsx_layout::types::LayoutBoundingClientRect; 14 | use rsx_stylesheet::types::{BorderStyle, Color}; 15 | use smallvec::SmallVec; 16 | 17 | use build::bincode_util::serialize_fast; 18 | use build::types::{ 19 | BorderDisplayItem, 20 | BorderDisplayProps, 21 | BuiltDisplayList, 22 | DisplayListBuilder, 23 | ImageDisplayItem, 24 | ImageDisplayProps, 25 | RectDisplayItem, 26 | RectDisplayProps, 27 | SpecificDisplayItem, 28 | TextDisplayItem, 29 | TextDisplayProps 30 | }; 31 | use prelude::{MeasuredImage, ShapedText}; 32 | use traits::TDisplayListBuilder; 33 | 34 | impl TDisplayListBuilder for DisplayListBuilder { 35 | // Serialize 36 | 37 | type Serialized = BuiltDisplayList; 38 | 39 | fn serialize(self) -> Self::Serialized { 40 | BuiltDisplayList(self.0) 41 | } 42 | 43 | // General 44 | 45 | type ClientRect = LayoutBoundingClientRect; 46 | type TransformMatrix = !; 47 | type PerspectiveMatrix = !; 48 | 49 | fn push_stacking_context(&mut self, _: Option, _: Option) { 50 | unreachable!() 51 | } 52 | 53 | fn pop_stacking_context(&mut self) { 54 | unreachable!() 55 | } 56 | 57 | // Memory (unsupported) 58 | 59 | type DisplayListItem = !; 60 | 61 | fn get_first(&mut self) -> Option<&mut Self::DisplayListItem> { 62 | unreachable!() 63 | } 64 | 65 | fn get_previous(&mut self) -> Option<&mut Self::DisplayListItem> { 66 | unreachable!() 67 | } 68 | 69 | // Rect 70 | 71 | type RectColor = Color; 72 | 73 | fn push_rect(&mut self, bounding_client_rect: Self::ClientRect, background_color: Self::RectColor) { 74 | return_if_nil!(@rect: background_color); 75 | 76 | serialize_fast( 77 | &mut self.0, 78 | &SpecificDisplayItem::Rect(RectDisplayItem { 79 | bounds: bounding_client_rect, 80 | display: RectDisplayProps { 81 | color: background_color 82 | } 83 | }) 84 | ); 85 | } 86 | 87 | // Border 88 | 89 | type BorderSize = u32; 90 | type BorderColor = Color; 91 | type BorderStyle = BorderStyle; 92 | 93 | fn push_border( 94 | &mut self, 95 | bounding_client_rect: Self::ClientRect, 96 | border_widths: [Self::BorderSize; 4], 97 | border_colors: [Self::BorderColor; 4], 98 | border_styles: [Self::BorderStyle; 4] 99 | ) { 100 | return_if_nil!(@border: border_widths, border_colors, border_styles); 101 | debug_item!(@bounds: self, bounding_client_rect); 102 | 103 | serialize_fast( 104 | &mut self.0, 105 | &SpecificDisplayItem::Border(BorderDisplayItem { 106 | bounds: bounding_client_rect, 107 | display: BorderDisplayProps { 108 | widths: border_widths, 109 | colors: border_colors, 110 | styles: border_styles 111 | } 112 | }) 113 | ); 114 | } 115 | 116 | // Image 117 | 118 | type MeasuredImage = MeasuredImage; 119 | 120 | fn push_image( 121 | &mut self, 122 | bounding_client_rect: Self::ClientRect, 123 | measured_image: &Self::MeasuredImage, 124 | image_src: &Self::DebugImageSrc 125 | ) { 126 | let image_key = measured_image.image_key(); 127 | 128 | return_if_nil!(@image: image_key, image_src); 129 | debug_item!(@bounds: self, bounding_client_rect); 130 | 131 | if let Some(_) = image_key { 132 | serialize_fast( 133 | &mut self.0, 134 | &SpecificDisplayItem::Image(ImageDisplayItem { 135 | bounds: bounding_client_rect, 136 | display: ImageDisplayProps { 137 | measured_image: MeasuredImage::clone(measured_image), 138 | image_src: DOMText::clone(image_src) 139 | } 140 | }) 141 | ); 142 | } else { 143 | println!("Warning: Image data missing for \"{}\"", image_src.as_ref()); 144 | } 145 | } 146 | 147 | // Text 148 | 149 | type TextColor = Color; 150 | type TextRun = ShapedText; 151 | 152 | fn push_text( 153 | &mut self, 154 | bounding_client_rect: Self::ClientRect, 155 | text_color: Self::TextColor, 156 | text_run: &Self::TextRun, 157 | text_content: &Self::DebugTextContent 158 | ) { 159 | let font_instance_key = text_run.font_instance_key(); 160 | let _text_glyphs = text_run.glyphs(); 161 | 162 | return_if_nil!(@text: font_instance_key, text_color, _text_glyphs, text_content); 163 | debug_item!(@bounds: self, bounding_client_rect); 164 | 165 | if let Some(_) = font_instance_key { 166 | serialize_fast( 167 | &mut self.0, 168 | &SpecificDisplayItem::Text(TextDisplayItem { 169 | bounds: bounding_client_rect, 170 | display: TextDisplayProps { 171 | color: text_color, 172 | shaped_text: SmallVec::from_buf([ShapedText::clone(text_run)]), 173 | source_text: SmallVec::from_buf([DOMText::clone(text_content)]) 174 | } 175 | }) 176 | ); 177 | } else { 178 | println!("Warning: Font missing for \"{}\"", text_content.as_ref()); 179 | } 180 | } 181 | 182 | // Debug 183 | 184 | type DebugTextContent = DOMText; 185 | type DebugImageSrc = DOMText; 186 | } 187 | -------------------------------------------------------------------------------- /src/build/bincode/display_list_differs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use build::types::DisplayListBuilder; 13 | use compare::traits::TDisplayListDiffer; 14 | use compare::types::{DisplayItemDiff, DisplayListDiff}; 15 | 16 | impl TDisplayListDiffer for DisplayListBuilder { 17 | type ListDiff = DisplayListDiff; 18 | type ItemDiff = DisplayItemDiff; 19 | type TextDisplayItem = (); 20 | type RectDisplayItem = (); 21 | type BorderDisplayItem = (); 22 | type ImageDisplayItem = (); 23 | 24 | fn diff(&self, _: &Self) -> Self::ListDiff { 25 | unimplemented!() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/build/bincode/export.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use std::mem; 13 | use std::os::raw::c_uchar; 14 | 15 | use build::bincode_types::BuiltDisplayList; 16 | 17 | impl Into> for BuiltDisplayList { 18 | fn into(self) -> Vec { 19 | self.0 20 | } 21 | } 22 | 23 | impl Into<(*mut c_uchar, usize)> for BuiltDisplayList { 24 | fn into(self) -> (*mut c_uchar, usize) { 25 | let mut vec: Vec = self.into(); 26 | vec.shrink_to_fit(); 27 | 28 | let ptr = vec.as_mut_ptr(); 29 | let len = vec.len(); 30 | mem::forget(vec); 31 | 32 | (ptr, len) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/build/bincode/types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use prelude::{MeasuredImage, ShapedText}; 13 | use rsx_dom::types::DOMText; 14 | use rsx_layout::types::LayoutBoundingClientRect; 15 | use rsx_resources::updates::types::ResourceUpdates as TResourceUpdates; 16 | use rsx_stylesheet::types::{BorderStyle, Color}; 17 | use smallvec::SmallVec; 18 | 19 | // Images 20 | 21 | pub use rsx_resources::updates::types::DefaultImageKey as BincodeImageKey; 22 | pub use rsx_resources::updates::types::DefaultImageKeysAPI as BincodeImageKeysAPI; 23 | 24 | // Images aliases 25 | 26 | #[cfg(feature = "bincode-display-list-aliases")] 27 | pub use self::BincodeImageKey as ImageKey; 28 | #[cfg(feature = "bincode-display-list-aliases")] 29 | pub use self::BincodeImageKeysAPI as ImageKeysAPI; 30 | 31 | // Fonts 32 | 33 | pub use rsx_resources::updates::types::DefaultFontInstanceKey as BincodeFontInstanceKey; 34 | pub use rsx_resources::updates::types::DefaultFontKey as BincodeFontKey; 35 | pub use rsx_resources::updates::types::DefaultFontKeysAPI as BincodeFontKeysAPI; 36 | 37 | // Fonts aliases 38 | 39 | #[cfg(feature = "bincode-display-list-aliases")] 40 | pub use self::BincodeFontInstanceKey as FontInstanceKey; 41 | #[cfg(feature = "bincode-display-list-aliases")] 42 | pub use self::BincodeFontKey as FontKey; 43 | #[cfg(feature = "bincode-display-list-aliases")] 44 | pub use self::BincodeFontKeysAPI as FontKeysAPI; 45 | 46 | // Glyphs 47 | 48 | pub use rsx_resources::updates::types::DefaultGlyphInstance as BincodeGlyphInstance; 49 | 50 | // Glyphs aliases 51 | 52 | #[cfg(feature = "bincode-display-list-aliases")] 53 | pub use self::BincodeGlyphInstance as GlyphInstance; 54 | 55 | // Updates 56 | 57 | pub type BincodeResourceUpdates = TResourceUpdates; 58 | 59 | // Updates aliases 60 | 61 | #[cfg(feature = "bincode-display-list-aliases")] 62 | pub use self::BincodeResourceUpdates as ResourceUpdates; 63 | 64 | // List 65 | 66 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 67 | pub struct BincodeBuiltDisplayList(pub(crate) Vec); 68 | 69 | // List aliases 70 | 71 | #[cfg(feature = "bincode-display-list-aliases")] 72 | pub use self::BincodeBuiltDisplayList as BuiltDisplayList; 73 | 74 | // Builder 75 | 76 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 77 | pub struct BincodeDisplayListBuilder(pub(crate) Vec); 78 | 79 | // Builder aliases 80 | 81 | #[cfg(feature = "bincode-display-list-aliases")] 82 | pub use self::BincodeDisplayListBuilder as DisplayListBuilder; 83 | 84 | // Items 85 | 86 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 87 | pub enum BincodeSpecificDisplayItem { 88 | Rect(RectDisplayItem), 89 | Border(BorderDisplayItem), 90 | Image(ImageDisplayItem), 91 | Text(TextDisplayItem) 92 | } 93 | 94 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 95 | pub struct BincodeRectDisplayItem { 96 | pub bounds: LayoutBoundingClientRect, 97 | pub display: RectDisplayProps 98 | } 99 | 100 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 101 | pub struct BincodeRectDisplayProps { 102 | pub color: Color 103 | } 104 | 105 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 106 | pub struct BincodeBorderDisplayItem { 107 | pub bounds: LayoutBoundingClientRect, 108 | pub display: BorderDisplayProps 109 | } 110 | 111 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 112 | pub struct BincodeBorderDisplayProps { 113 | pub widths: [u32; 4], 114 | pub colors: [Color; 4], 115 | pub styles: [BorderStyle; 4] 116 | } 117 | 118 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 119 | pub struct BincodeImageDisplayItem { 120 | pub bounds: LayoutBoundingClientRect, 121 | pub display: ImageDisplayProps 122 | } 123 | 124 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 125 | pub struct BincodeImageDisplayProps { 126 | pub image_src: DOMText, 127 | pub measured_image: MeasuredImage 128 | } 129 | 130 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 131 | pub struct BincodeTextDisplayItem { 132 | pub bounds: LayoutBoundingClientRect, 133 | pub display: TextDisplayProps 134 | } 135 | 136 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 137 | pub struct BincodeTextDisplayProps { 138 | pub color: Color, 139 | pub source_text: SmallVec<[DOMText; 1]>, 140 | pub shaped_text: SmallVec<[ShapedText; 1]> 141 | } 142 | 143 | // Items aliases 144 | 145 | #[cfg(feature = "bincode-display-list-aliases")] 146 | pub use self::BincodeBorderDisplayItem as BorderDisplayItem; 147 | #[cfg(feature = "bincode-display-list-aliases")] 148 | pub use self::BincodeBorderDisplayProps as BorderDisplayProps; 149 | #[cfg(feature = "bincode-display-list-aliases")] 150 | pub use self::BincodeImageDisplayItem as ImageDisplayItem; 151 | #[cfg(feature = "bincode-display-list-aliases")] 152 | pub use self::BincodeImageDisplayProps as ImageDisplayProps; 153 | #[cfg(feature = "bincode-display-list-aliases")] 154 | pub use self::BincodeRectDisplayItem as RectDisplayItem; 155 | #[cfg(feature = "bincode-display-list-aliases")] 156 | pub use self::BincodeRectDisplayProps as RectDisplayProps; 157 | #[cfg(feature = "bincode-display-list-aliases")] 158 | pub use self::BincodeSpecificDisplayItem as SpecificDisplayItem; 159 | #[cfg(feature = "bincode-display-list-aliases")] 160 | pub use self::BincodeTextDisplayItem as TextDisplayItem; 161 | #[cfg(feature = "bincode-display-list-aliases")] 162 | pub use self::BincodeTextDisplayProps as TextDisplayProps; 163 | -------------------------------------------------------------------------------- /src/build/bincode/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use std::io; 13 | use std::io::Write; 14 | use std::ptr; 15 | 16 | use bincode; 17 | use serde::Serialize; 18 | 19 | struct UnsafeVecWriter(*mut u8); 20 | 21 | impl Write for UnsafeVecWriter { 22 | fn write(&mut self, buf: &[u8]) -> io::Result { 23 | unsafe { 24 | ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len()); 25 | self.0 = self.0.offset(buf.len() as isize); 26 | } 27 | Ok(buf.len()) 28 | } 29 | 30 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { 31 | unsafe { 32 | ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len()); 33 | self.0 = self.0.offset(buf.len() as isize); 34 | } 35 | Ok(()) 36 | } 37 | 38 | fn flush(&mut self) -> io::Result<()> { 39 | Ok(()) 40 | } 41 | } 42 | 43 | pub fn serialize_fast(vec: &mut Vec, e: &T) { 44 | let size = bincode::serialized_size(&e).unwrap() as usize; 45 | vec.reserve(size); 46 | 47 | let old_len = vec.len(); 48 | let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) }; 49 | 50 | let mut w = UnsafeVecWriter(ptr); 51 | bincode::serialize_into(&mut w, e).unwrap(); 52 | 53 | unsafe { 54 | vec.set_len(old_len + size); 55 | } 56 | 57 | debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len()); 58 | } 59 | -------------------------------------------------------------------------------- /src/build/json/display_list_builder.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use serde_json; 13 | 14 | use rsx_dom::types::DOMText; 15 | use rsx_layout::types::LayoutBoundingClientRect; 16 | use rsx_stylesheet::types::{BorderStyle, Color}; 17 | use smallvec::SmallVec; 18 | 19 | use build::json_util::{are_sibling_text_display_items, extend_text_display_item_with}; 20 | use build::types::{ 21 | BorderDisplayItem, 22 | BorderDisplayProps, 23 | BuiltDisplayList, 24 | DisplayListBuilder, 25 | ImageDisplayItem, 26 | ImageDisplayProps, 27 | RectDisplayItem, 28 | RectDisplayProps, 29 | SpecificDisplayItem, 30 | TextDisplayItem, 31 | TextDisplayProps 32 | }; 33 | use prelude::{MeasuredImage, ShapedText}; 34 | use traits::TDisplayListBuilder; 35 | 36 | impl TDisplayListBuilder for DisplayListBuilder { 37 | // Serialize 38 | 39 | type Serialized = BuiltDisplayList; 40 | 41 | #[cfg(not(feature = "pretty-json-mode"))] 42 | fn serialize(self) -> Self::Serialized { 43 | BuiltDisplayList(serde_json::to_string(&self.0).unwrap_or_else(|_| "".to_string())) 44 | } 45 | 46 | #[cfg(feature = "pretty-json-mode")] 47 | fn serialize(self) -> Self::Serialized { 48 | BuiltDisplayList(serde_json::to_string_pretty(&self.0).unwrap_or_else(|_| "".to_string())) 49 | } 50 | 51 | // General 52 | 53 | type ClientRect = LayoutBoundingClientRect; 54 | type TransformMatrix = !; 55 | type PerspectiveMatrix = !; 56 | 57 | fn push_stacking_context(&mut self, _: Option, _: Option) { 58 | unreachable!() 59 | } 60 | 61 | fn pop_stacking_context(&mut self) { 62 | unreachable!() 63 | } 64 | 65 | // Memory 66 | 67 | type DisplayListItem = SpecificDisplayItem; 68 | 69 | fn get_first(&mut self) -> Option<&mut Self::DisplayListItem> { 70 | self.0.first_mut() 71 | } 72 | 73 | fn get_previous(&mut self) -> Option<&mut Self::DisplayListItem> { 74 | self.0.last_mut() 75 | } 76 | 77 | // Rect 78 | 79 | type RectColor = Color; 80 | 81 | fn push_rect(&mut self, bounding_client_rect: Self::ClientRect, background_color: Self::RectColor) { 82 | return_if_nil!(@rect: background_color); 83 | 84 | self.0.push(SpecificDisplayItem::Rect(RectDisplayItem { 85 | bounds: bounding_client_rect, 86 | display: RectDisplayProps { 87 | color: background_color 88 | } 89 | })); 90 | } 91 | 92 | // Border 93 | 94 | type BorderSize = u32; 95 | type BorderColor = Color; 96 | type BorderStyle = BorderStyle; 97 | 98 | fn push_border( 99 | &mut self, 100 | bounding_client_rect: Self::ClientRect, 101 | border_widths: [Self::BorderSize; 4], 102 | border_colors: [Self::BorderColor; 4], 103 | border_styles: [Self::BorderStyle; 4] 104 | ) { 105 | return_if_nil!(@border: border_widths, border_colors, border_styles); 106 | debug_item!(@bounds: self, bounding_client_rect); 107 | 108 | self.0.push(SpecificDisplayItem::Border(BorderDisplayItem { 109 | bounds: bounding_client_rect, 110 | display: BorderDisplayProps { 111 | widths: border_widths, 112 | colors: border_colors, 113 | styles: border_styles 114 | } 115 | })); 116 | } 117 | 118 | // Image 119 | 120 | type MeasuredImage = MeasuredImage; 121 | 122 | fn push_image( 123 | &mut self, 124 | bounding_client_rect: Self::ClientRect, 125 | measured_image: &Self::MeasuredImage, 126 | image_src: &Self::DebugImageSrc 127 | ) { 128 | let image_key = measured_image.image_key(); 129 | 130 | return_if_nil!(@image: image_key, image_src); 131 | debug_item!(@bounds: self, bounding_client_rect); 132 | 133 | if let Some(_) = image_key { 134 | self.0.push(SpecificDisplayItem::Image(ImageDisplayItem { 135 | bounds: bounding_client_rect, 136 | display: ImageDisplayProps { 137 | measured_image: MeasuredImage::clone(measured_image), 138 | image_src: DOMText::clone(image_src) 139 | } 140 | })); 141 | } else { 142 | println!("Warning: Image data missing for \"{}\"", image_src.as_ref()); 143 | } 144 | } 145 | 146 | // Text 147 | 148 | type TextColor = Color; 149 | type TextRun = ShapedText; 150 | 151 | fn push_text( 152 | &mut self, 153 | bounding_client_rect: Self::ClientRect, 154 | text_color: Self::TextColor, 155 | text_run: &Self::TextRun, 156 | text_content: &Self::DebugTextContent 157 | ) { 158 | let font_instance_key = text_run.font_instance_key(); 159 | let _text_glyphs = text_run.glyphs(); 160 | 161 | if let Some(&mut SpecificDisplayItem::Text(ref mut previous)) = self.get_previous() { 162 | if are_sibling_text_display_items(previous, bounding_client_rect) { 163 | extend_text_display_item_with(previous, bounding_client_rect, text_run, text_content); 164 | return; 165 | } 166 | } 167 | 168 | return_if_nil!(@text: font_instance_key, text_color, _text_glyphs, text_content); 169 | debug_item!(@bounds: self, bounding_client_rect); 170 | 171 | if let Some(_) = font_instance_key { 172 | self.0.push(SpecificDisplayItem::Text(TextDisplayItem { 173 | bounds: bounding_client_rect, 174 | display: TextDisplayProps { 175 | color: text_color, 176 | shaped_text: SmallVec::from_buf([ShapedText::clone(text_run)]), 177 | source_text: SmallVec::from_buf([DOMText::clone(text_content)]) 178 | } 179 | })); 180 | } else { 181 | println!("Warning: Font missing for \"{}\"", text_content.as_ref()); 182 | } 183 | } 184 | 185 | // Debug 186 | 187 | type DebugTextContent = DOMText; 188 | type DebugImageSrc = DOMText; 189 | } 190 | -------------------------------------------------------------------------------- /src/build/json/display_list_differs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use build::types::{BorderDisplayItem, DisplayListBuilder, ImageDisplayItem, RectDisplayItem, SpecificDisplayItem, TextDisplayItem}; 13 | use compare::traits::{ 14 | TBorderDisplayItemDiffer, 15 | TDisplayListDiffer, 16 | TImageDisplayItemDiffer, 17 | TRectDisplayItemDiffer, 18 | TTextDisplayItemDiffer 19 | }; 20 | use compare::types::{ 21 | BorderUpdate, 22 | BoundsUpdate, 23 | DisplayItemDiff, 24 | DisplayItemUpdate, 25 | DisplayListDiff, 26 | ImageUpdate, 27 | RectUpdate, 28 | SingleBorderUpdate, 29 | TextUpdate 30 | }; 31 | 32 | impl TDisplayListDiffer for DisplayListBuilder { 33 | type ListDiff = DisplayListDiff; 34 | type ItemDiff = DisplayItemDiff; 35 | type TextDisplayItem = TextDisplayItem; 36 | type RectDisplayItem = RectDisplayItem; 37 | type BorderDisplayItem = BorderDisplayItem; 38 | type ImageDisplayItem = ImageDisplayItem; 39 | 40 | fn diff(&self, other: &Self) -> Self::ListDiff { 41 | let mut this = self.0.iter().fuse(); 42 | let mut other = other.0.iter().fuse(); 43 | let mut changes = Vec::with_capacity(usize::max(this.len(), other.len())); 44 | 45 | let mut i = 0; 46 | 47 | loop { 48 | use self::DisplayItemDiff::*; 49 | use self::SpecificDisplayItem::*; 50 | 51 | match (this.next(), other.next()) { 52 | // Add 53 | (None, Some(&Rect(ref rect_b))) => { 54 | changes.push(AddRect(*rect_b)); 55 | } 56 | (None, Some(&Border(ref border_b))) => { 57 | changes.push(AddBorder(*border_b)); 58 | } 59 | (None, Some(&Image(ref image_b))) => { 60 | changes.push(AddImage(ImageDisplayItem::clone(image_b))); 61 | } 62 | (None, Some(&Text(ref text_b))) => { 63 | changes.push(AddText(TextDisplayItem::clone(text_b))); 64 | } 65 | 66 | // Remove 67 | (Some(_), None) => { 68 | changes.push(RemoveSelf(i)); 69 | } 70 | 71 | // Update 72 | (Some(&Rect(ref rect_a)), Some(&Rect(ref rect_b))) => { 73 | let updates = rect_a.diff(rect_b); 74 | if !updates.is_empty() { 75 | changes.push(UpdateSelf(i, updates)); 76 | } 77 | } 78 | (Some(&Border(ref border_a)), Some(&Border(ref border_b))) => { 79 | let updates = border_a.diff(border_b); 80 | if !updates.is_empty() { 81 | changes.push(UpdateSelf(i, updates)); 82 | } 83 | } 84 | (Some(&Image(ref image_a)), Some(&Image(ref image_b))) => { 85 | let updates = image_a.diff(image_b); 86 | if !updates.is_empty() { 87 | changes.push(UpdateSelf(i, updates)); 88 | } 89 | } 90 | (Some(&Text(ref text_a)), Some(&Text(ref text_b))) => { 91 | let updates = text_a.diff(text_b); 92 | if !updates.is_empty() { 93 | changes.push(UpdateSelf(i, updates)); 94 | } 95 | } 96 | 97 | // Replace 98 | (Some(_), Some(&Rect(ref rect_b))) => { 99 | changes.push(ReplaceSelfWithRect(i, *rect_b)); 100 | } 101 | (Some(_), Some(&Border(ref border_b))) => { 102 | changes.push(ReplaceSelfWithBorder(i, *border_b)); 103 | } 104 | (Some(_), Some(&Image(ref image_b))) => { 105 | changes.push(ReplaceSelfWithImage(i, ImageDisplayItem::clone(image_b))); 106 | } 107 | (Some(_), Some(&Text(ref text_b))) => { 108 | changes.push(ReplaceSelfWithText(i, TextDisplayItem::clone(text_b))); 109 | } 110 | 111 | // Done. 112 | (None, None) => break 113 | } 114 | 115 | i += 1; 116 | } 117 | 118 | DisplayListDiff { changes } 119 | } 120 | } 121 | 122 | impl TRectDisplayItemDiffer for RectDisplayItem { 123 | type ItemUpdate = DisplayItemUpdate; 124 | 125 | fn diff(&self, other: &Self) -> Vec { 126 | use self::BoundsUpdate::{ChangePositionX, ChangePositionY, ChangeSizeHeight, ChangeSizeWidth}; 127 | use self::DisplayItemUpdate::{BoundsUpdate, RectUpdate}; 128 | use self::RectUpdate::ChangeColor; 129 | 130 | let mut changes = Vec::new(); 131 | 132 | if self.bounds.position.left != other.bounds.position.left { 133 | changes.push(BoundsUpdate(ChangePositionX(other.bounds.position.left))); 134 | } 135 | if self.bounds.position.top != other.bounds.position.top { 136 | changes.push(BoundsUpdate(ChangePositionY(other.bounds.position.top))); 137 | } 138 | if self.bounds.size.width != other.bounds.size.width { 139 | changes.push(BoundsUpdate(ChangeSizeWidth(other.bounds.size.width))); 140 | } 141 | if self.bounds.size.height != other.bounds.size.height { 142 | changes.push(BoundsUpdate(ChangeSizeHeight(other.bounds.size.height))); 143 | } 144 | if self.display.color != other.display.color { 145 | changes.push(RectUpdate(ChangeColor(other.display.color))); 146 | } 147 | 148 | changes 149 | } 150 | } 151 | 152 | impl TBorderDisplayItemDiffer for BorderDisplayItem { 153 | type ItemUpdate = DisplayItemUpdate; 154 | 155 | fn diff(&self, other: &Self) -> Vec { 156 | use self::BorderUpdate::{ 157 | ChangeBottomBorder as Bottom, 158 | ChangeLeftBorder as Left, 159 | ChangeRightBorder as Right, 160 | ChangeTopBorder as Top 161 | }; 162 | use self::BoundsUpdate::{ChangePositionX, ChangePositionY, ChangeSizeHeight, ChangeSizeWidth}; 163 | use self::DisplayItemUpdate::{BorderUpdate, BoundsUpdate}; 164 | use self::SingleBorderUpdate::*; 165 | 166 | let mut changes = Vec::new(); 167 | 168 | if self.bounds.position.left != other.bounds.position.left { 169 | changes.push(BoundsUpdate(ChangePositionX(other.bounds.position.left))); 170 | } 171 | if self.bounds.position.top != other.bounds.position.top { 172 | changes.push(BoundsUpdate(ChangePositionY(other.bounds.position.top))); 173 | } 174 | if self.bounds.size.width != other.bounds.size.width { 175 | changes.push(BoundsUpdate(ChangeSizeWidth(other.bounds.size.width))); 176 | } 177 | if self.bounds.size.height != other.bounds.size.height { 178 | changes.push(BoundsUpdate(ChangeSizeHeight(other.bounds.size.height))); 179 | } 180 | if self.display.widths[0] != other.display.widths[0] { 181 | changes.push(BorderUpdate(Top(ChangeSize(other.display.widths[0])))); 182 | } 183 | if self.display.widths[1] != other.display.widths[1] { 184 | changes.push(BorderUpdate(Right(ChangeSize(other.display.widths[1])))); 185 | } 186 | if self.display.widths[2] != other.display.widths[2] { 187 | changes.push(BorderUpdate(Bottom(ChangeSize(other.display.widths[2])))); 188 | } 189 | if self.display.widths[3] != other.display.widths[3] { 190 | changes.push(BorderUpdate(Left(ChangeSize(other.display.widths[3])))); 191 | } 192 | if self.display.colors[0] != other.display.colors[0] { 193 | changes.push(BorderUpdate(Top(ChangeColor(other.display.colors[0])))); 194 | } 195 | if self.display.colors[1] != other.display.colors[1] { 196 | changes.push(BorderUpdate(Right(ChangeColor(other.display.colors[1])))); 197 | } 198 | if self.display.colors[2] != other.display.colors[2] { 199 | changes.push(BorderUpdate(Bottom(ChangeColor(other.display.colors[2])))); 200 | } 201 | if self.display.colors[3] != other.display.colors[3] { 202 | changes.push(BorderUpdate(Left(ChangeColor(other.display.colors[3])))); 203 | } 204 | if self.display.styles[0] != other.display.styles[0] { 205 | changes.push(BorderUpdate(Top(ChangeStyle(other.display.styles[0])))); 206 | } 207 | if self.display.styles[1] != other.display.styles[1] { 208 | changes.push(BorderUpdate(Right(ChangeStyle(other.display.styles[1])))); 209 | } 210 | if self.display.styles[2] != other.display.styles[2] { 211 | changes.push(BorderUpdate(Bottom(ChangeStyle(other.display.styles[2])))); 212 | } 213 | if self.display.styles[3] != other.display.styles[3] { 214 | changes.push(BorderUpdate(Left(ChangeStyle(other.display.styles[3])))); 215 | } 216 | 217 | changes 218 | } 219 | } 220 | 221 | impl TImageDisplayItemDiffer for ImageDisplayItem { 222 | type ItemUpdate = DisplayItemUpdate; 223 | 224 | fn diff(&self, other: &Self) -> Vec { 225 | use self::BoundsUpdate::{ChangePositionX, ChangePositionY, ChangeSizeHeight, ChangeSizeWidth}; 226 | use self::DisplayItemUpdate::{BoundsUpdate, ImageUpdate}; 227 | use self::ImageUpdate::ChangeImage; 228 | 229 | let mut changes = Vec::new(); 230 | 231 | if self.bounds.position.left != other.bounds.position.left { 232 | changes.push(BoundsUpdate(ChangePositionX(other.bounds.position.left))); 233 | } 234 | if self.bounds.position.top != other.bounds.position.top { 235 | changes.push(BoundsUpdate(ChangePositionY(other.bounds.position.top))); 236 | } 237 | if self.bounds.size.width != other.bounds.size.width { 238 | changes.push(BoundsUpdate(ChangeSizeWidth(other.bounds.size.width))); 239 | } 240 | if self.bounds.size.height != other.bounds.size.height { 241 | changes.push(BoundsUpdate(ChangeSizeHeight(other.bounds.size.height))); 242 | } 243 | if self.display.measured_image != other.display.measured_image { 244 | changes.push(ImageUpdate(ChangeImage { 245 | raw: other.display.image_src.clone(), 246 | #[cfg(not(feature = "no-json-images-mode"))] 247 | measured_image: other.display.measured_image.clone() 248 | })); 249 | } 250 | 251 | changes 252 | } 253 | } 254 | 255 | impl TTextDisplayItemDiffer for TextDisplayItem { 256 | type ItemUpdate = DisplayItemUpdate; 257 | 258 | fn diff(&self, other: &Self) -> Vec { 259 | use self::BoundsUpdate::{ChangePositionX, ChangePositionY, ChangeSizeHeight, ChangeSizeWidth}; 260 | use self::DisplayItemUpdate::{BoundsUpdate, TextUpdate}; 261 | use self::TextUpdate::{ChangeColor, ChangeText}; 262 | 263 | let mut changes = Vec::new(); 264 | 265 | if self.bounds.position.left != other.bounds.position.left { 266 | changes.push(BoundsUpdate(ChangePositionX(other.bounds.position.left))); 267 | } 268 | if self.bounds.position.top != other.bounds.position.top { 269 | changes.push(BoundsUpdate(ChangePositionY(other.bounds.position.top))); 270 | } 271 | if self.bounds.size.width != other.bounds.size.width { 272 | changes.push(BoundsUpdate(ChangeSizeWidth(other.bounds.size.width))); 273 | } 274 | if self.bounds.size.height != other.bounds.size.height { 275 | changes.push(BoundsUpdate(ChangeSizeHeight(other.bounds.size.height))); 276 | } 277 | if self.display.color != other.display.color { 278 | changes.push(TextUpdate(ChangeColor(other.display.color))); 279 | } 280 | if self.display.shaped_text != other.display.shaped_text { 281 | changes.push(TextUpdate(ChangeText { 282 | raw: other.display.source_text.clone(), 283 | #[cfg(not(feature = "no-json-glyphs-mode"))] 284 | text_run: other.display.shaped_text.clone() 285 | })); 286 | } 287 | 288 | changes 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/build/json/export.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use std::ffi::CString; 13 | use std::os::raw::c_char; 14 | 15 | use build::json_types::BuiltDisplayList; 16 | 17 | impl Into for BuiltDisplayList { 18 | fn into(self) -> String { 19 | self.0 20 | } 21 | } 22 | 23 | impl Into<*mut c_char> for BuiltDisplayList { 24 | fn into(self) -> *mut c_char { 25 | let string: String = self.into(); 26 | unsafe { CString::from_vec_unchecked(string.into_bytes()) }.into_raw() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/build/json/types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use prelude::{MeasuredImage, ShapedText}; 13 | use rsx_dom::types::DOMText; 14 | use rsx_layout::types::LayoutBoundingClientRect; 15 | use rsx_resources::updates::types::ResourceUpdates as TResourceUpdates; 16 | use rsx_stylesheet::types::{BorderStyle, Color}; 17 | use smallvec::SmallVec; 18 | 19 | // Images 20 | 21 | pub use rsx_resources::updates::types::DefaultImageKey as JsonImageKey; 22 | pub use rsx_resources::updates::types::DefaultImageKeysAPI as JsonImageKeysAPI; 23 | 24 | // Images aliases 25 | 26 | #[cfg(feature = "json-display-list-aliases")] 27 | pub use self::JsonImageKey as ImageKey; 28 | #[cfg(feature = "json-display-list-aliases")] 29 | pub use self::JsonImageKeysAPI as ImageKeysAPI; 30 | 31 | // Fonts 32 | 33 | pub use rsx_resources::updates::types::DefaultFontInstanceKey as JsonFontInstanceKey; 34 | pub use rsx_resources::updates::types::DefaultFontKey as JsonFontKey; 35 | pub use rsx_resources::updates::types::DefaultFontKeysAPI as JsonFontKeysAPI; 36 | 37 | // Fonts aliases 38 | 39 | #[cfg(feature = "json-display-list-aliases")] 40 | pub use self::JsonFontInstanceKey as FontInstanceKey; 41 | #[cfg(feature = "json-display-list-aliases")] 42 | pub use self::JsonFontKey as FontKey; 43 | #[cfg(feature = "json-display-list-aliases")] 44 | pub use self::JsonFontKeysAPI as FontKeysAPI; 45 | 46 | // Glyphs 47 | 48 | pub use rsx_resources::updates::types::DefaultGlyphInstance as JsonGlyphInstance; 49 | 50 | // Glyphs aliases 51 | 52 | #[cfg(feature = "json-display-list-aliases")] 53 | pub use self::JsonGlyphInstance as GlyphInstance; 54 | 55 | // Updates 56 | 57 | pub type JsonResourceUpdates = TResourceUpdates; 58 | 59 | // Updates aliases 60 | 61 | #[cfg(feature = "json-display-list-aliases")] 62 | pub use self::JsonResourceUpdates as ResourceUpdates; 63 | 64 | // List 65 | 66 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 67 | pub struct JsonBuiltDisplayList(pub(crate) String); 68 | 69 | // List aliases 70 | 71 | #[cfg(feature = "json-display-list-aliases")] 72 | pub use self::JsonBuiltDisplayList as BuiltDisplayList; 73 | 74 | // Builder 75 | 76 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 77 | pub struct JsonDisplayListBuilder(pub(crate) Vec); 78 | 79 | // Builder aliases 80 | 81 | #[cfg(feature = "json-display-list-aliases")] 82 | pub use self::JsonDisplayListBuilder as DisplayListBuilder; 83 | 84 | // Items 85 | 86 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 87 | pub enum JsonSpecificDisplayItem { 88 | Rect(RectDisplayItem), 89 | Border(BorderDisplayItem), 90 | Image(ImageDisplayItem), 91 | Text(TextDisplayItem) 92 | } 93 | 94 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 95 | pub struct JsonRectDisplayItem { 96 | pub bounds: LayoutBoundingClientRect, 97 | pub display: RectDisplayProps 98 | } 99 | 100 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 101 | pub struct JsonRectDisplayProps { 102 | pub color: Color 103 | } 104 | 105 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 106 | pub struct JsonBorderDisplayItem { 107 | pub bounds: LayoutBoundingClientRect, 108 | pub display: BorderDisplayProps 109 | } 110 | 111 | #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] 112 | pub struct JsonBorderDisplayProps { 113 | pub widths: [u32; 4], 114 | pub colors: [Color; 4], 115 | pub styles: [BorderStyle; 4] 116 | } 117 | 118 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 119 | pub struct JsonImageDisplayItem { 120 | pub bounds: LayoutBoundingClientRect, 121 | pub display: ImageDisplayProps 122 | } 123 | 124 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 125 | pub struct JsonImageDisplayProps { 126 | pub image_src: DOMText, 127 | pub measured_image: MeasuredImage 128 | } 129 | 130 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 131 | pub struct JsonTextDisplayItem { 132 | pub bounds: LayoutBoundingClientRect, 133 | pub display: TextDisplayProps 134 | } 135 | 136 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 137 | pub struct JsonTextDisplayProps { 138 | pub color: Color, 139 | pub source_text: SmallVec<[DOMText; 1]>, 140 | pub shaped_text: SmallVec<[ShapedText; 1]> 141 | } 142 | 143 | // Items aliases 144 | 145 | #[cfg(feature = "json-display-list-aliases")] 146 | pub use self::JsonBorderDisplayItem as BorderDisplayItem; 147 | #[cfg(feature = "json-display-list-aliases")] 148 | pub use self::JsonBorderDisplayProps as BorderDisplayProps; 149 | #[cfg(feature = "json-display-list-aliases")] 150 | pub use self::JsonImageDisplayItem as ImageDisplayItem; 151 | #[cfg(feature = "json-display-list-aliases")] 152 | pub use self::JsonImageDisplayProps as ImageDisplayProps; 153 | #[cfg(feature = "json-display-list-aliases")] 154 | pub use self::JsonRectDisplayItem as RectDisplayItem; 155 | #[cfg(feature = "json-display-list-aliases")] 156 | pub use self::JsonRectDisplayProps as RectDisplayProps; 157 | #[cfg(feature = "json-display-list-aliases")] 158 | pub use self::JsonSpecificDisplayItem as SpecificDisplayItem; 159 | #[cfg(feature = "json-display-list-aliases")] 160 | pub use self::JsonTextDisplayItem as TextDisplayItem; 161 | #[cfg(feature = "json-display-list-aliases")] 162 | pub use self::JsonTextDisplayProps as TextDisplayProps; 163 | -------------------------------------------------------------------------------- /src/build/json/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_dom::types::DOMText; 13 | use rsx_layout::types::LayoutBoundingClientRect; 14 | 15 | use build::types::TextDisplayItem; 16 | use prelude::ShapedText; 17 | 18 | // TODO: properly handle text direction and don't assume it's 19 | // laid out horizontally and LTR. 20 | pub fn are_sibling_text_display_items(target: &TextDisplayItem, next_bounding_client_rect: LayoutBoundingClientRect) -> bool { 21 | let &TextDisplayItem { ref bounds, .. } = target; 22 | 23 | if (bounds.position.top as f32 - next_bounding_client_rect.position.top as f32) 24 | .abs() 25 | .round() > 2.0 26 | { 27 | false 28 | } else if ((bounds.position.left + bounds.size.width) as f32 - next_bounding_client_rect.position.left as f32) 29 | .abs() 30 | .round() > 2.0 31 | { 32 | false 33 | } else { 34 | true 35 | } 36 | } 37 | 38 | // TODO: properly handle text direction and don't assume it's 39 | // laid out horizontally and LTR. 40 | pub fn extend_text_display_item_with( 41 | target: &mut TextDisplayItem, 42 | bounding_client_rect: LayoutBoundingClientRect, 43 | text_run: &ShapedText, 44 | text_content: &DOMText 45 | ) { 46 | let &mut TextDisplayItem { 47 | ref mut bounds, 48 | ref mut display 49 | } = target; 50 | 51 | bounds.size.width += bounding_client_rect.size.width - 1; 52 | bounds.size.height = u32::max(bounds.size.height, bounding_client_rect.size.height); 53 | 54 | display.source_text.push(DOMText::clone(text_content)); 55 | display.shaped_text.push(ShapedText::clone(text_run)); 56 | } 57 | -------------------------------------------------------------------------------- /src/build/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | pub mod traits; 13 | 14 | // Bincode 15 | 16 | #[cfg(feature = "bincode-display-list")] 17 | #[path = "bincode/display_list_builder.rs"] 18 | mod bincode_display_list_builder; 19 | 20 | #[cfg(feature = "bincode-display-list")] 21 | #[path = "bincode/display_list_differs.rs"] 22 | mod bincode_display_list_differs; 23 | 24 | #[cfg(feature = "bincode-display-list")] 25 | #[path = "bincode/types.rs"] 26 | mod bincode_types; 27 | 28 | #[cfg(feature = "bincode-display-list")] 29 | #[path = "bincode/export.rs"] 30 | mod bincode_export; 31 | 32 | #[cfg(feature = "bincode-display-list")] 33 | #[path = "bincode/util.rs"] 34 | mod bincode_util; 35 | 36 | // Json 37 | 38 | #[cfg(feature = "json-display-list")] 39 | #[path = "json/display_list_builder.rs"] 40 | mod json_display_list_builder; 41 | 42 | #[cfg(feature = "json-display-list")] 43 | #[path = "json/display_list_differs.rs"] 44 | mod json_display_list_differs; 45 | 46 | #[cfg(feature = "json-display-list")] 47 | #[path = "json/types.rs"] 48 | mod json_types; 49 | 50 | #[cfg(feature = "json-display-list")] 51 | #[path = "json/export.rs"] 52 | mod json_export; 53 | 54 | #[cfg(feature = "json-display-list")] 55 | #[path = "json/util.rs"] 56 | mod json_util; 57 | 58 | // WebRender 59 | 60 | #[cfg(feature = "webrender-display-list")] 61 | #[path = "webrender/display_list_builder.rs"] 62 | mod webrender_display_list_builder; 63 | 64 | #[cfg(feature = "webrender-display-list")] 65 | #[path = "webrender/display_list_differs.rs"] 66 | mod webrender_display_list_differs; 67 | 68 | #[cfg(feature = "webrender-display-list")] 69 | #[path = "webrender/types.rs"] 70 | mod webrender_types; 71 | 72 | #[cfg(feature = "webrender-display-list")] 73 | #[path = "webrender/util.rs"] 74 | mod webrender_util; 75 | 76 | // Pub 77 | 78 | pub mod util { 79 | #[cfg(feature = "json-display-list")] 80 | pub use super::json_util::*; 81 | #[cfg(feature = "webrender-display-list")] 82 | pub use super::webrender_util::*; 83 | } 84 | 85 | pub mod types { 86 | #[cfg(feature = "bincode-display-list")] 87 | pub use super::bincode_display_list_builder::*; 88 | #[cfg(feature = "bincode-display-list")] 89 | pub use super::bincode_types::*; 90 | #[cfg(feature = "json-display-list")] 91 | pub use super::json_display_list_builder::*; 92 | #[cfg(feature = "json-display-list")] 93 | pub use super::json_types::*; 94 | #[cfg(feature = "webrender-display-list")] 95 | pub use super::webrender_display_list_builder::*; 96 | #[cfg(feature = "webrender-display-list")] 97 | pub use super::webrender_types::*; 98 | } 99 | -------------------------------------------------------------------------------- /src/build/traits.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use serde::{Deserialize, Serialize}; 13 | 14 | pub trait TDisplayListBuilder 15 | where 16 | Self::TextColor: Serialize + for<'a> Deserialize<'a>, 17 | Self::TextRun: Serialize + for<'a> Deserialize<'a>, 18 | Self::RectColor: Serialize + for<'a> Deserialize<'a>, 19 | Self::BorderSize: Serialize + for<'a> Deserialize<'a>, 20 | Self::BorderColor: Serialize + for<'a> Deserialize<'a>, 21 | Self::BorderStyle: Serialize + for<'a> Deserialize<'a>, 22 | Self::MeasuredImage: Serialize + for<'a> Deserialize<'a>, 23 | Self::DebugTextContent: Serialize + for<'a> Deserialize<'a>, 24 | Self::DebugImageSrc: Serialize + for<'a> Deserialize<'a> 25 | { 26 | // Serialize 27 | 28 | type Serialized; 29 | fn serialize(self) -> Self::Serialized; 30 | 31 | // General 32 | 33 | type ClientRect; 34 | type TransformMatrix; 35 | type PerspectiveMatrix; 36 | fn push_stacking_context(&mut self, Option, Option); 37 | fn pop_stacking_context(&mut self); 38 | 39 | // Memory 40 | 41 | type DisplayListItem; 42 | fn get_first(&mut self) -> Option<&mut Self::DisplayListItem>; 43 | fn get_previous(&mut self) -> Option<&mut Self::DisplayListItem>; 44 | 45 | // Rect 46 | 47 | type RectColor; 48 | fn push_rect(&mut self, Self::ClientRect, Self::RectColor); 49 | 50 | // Border 51 | 52 | type BorderSize; 53 | type BorderColor; 54 | type BorderStyle; 55 | fn push_border(&mut self, Self::ClientRect, [Self::BorderSize; 4], [Self::BorderColor; 4], [Self::BorderStyle; 4]); 56 | 57 | // Image 58 | 59 | type MeasuredImage; 60 | fn push_image(&mut self, Self::ClientRect, &Self::MeasuredImage, &Self::DebugImageSrc); 61 | 62 | // Text 63 | 64 | type TextColor; 65 | type TextRun; 66 | fn push_text(&mut self, Self::ClientRect, Self::TextColor, &Self::TextRun, &Self::DebugTextContent); 67 | 68 | // Debug 69 | 70 | type DebugTextContent; 71 | type DebugImageSrc; 72 | } 73 | -------------------------------------------------------------------------------- /src/build/webrender/display_list_builder.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #[cfg(not(feature = "dummy-api-mode"))] 13 | use std::mem; 14 | use std::ops::Deref; 15 | use std::rc::Rc; 16 | #[cfg(not(feature = "dummy-api-mode"))] 17 | use std::sync::Arc; 18 | 19 | #[cfg(not(feature = "dummy-api-mode"))] 20 | use app_units::Au; 21 | use euclid::TypedTransform3D; 22 | #[cfg(feature = "dummy-api-mode")] 23 | use rand::{self, Rng}; 24 | use rsx_dom::types::DOMText; 25 | use rsx_layout::types::LayoutBoundingClientRect; 26 | #[cfg(not(feature = "dummy-api-mode"))] 27 | use rsx_resources::images::types::ImagePixelFormat; 28 | use rsx_shared::traits::{TFontKeysAPI, TImageKeysAPI}; 29 | use rsx_shared::types::{FontEncodedData, FontInstanceResourceData, FontResourceData, ImageEncodedData, ImageResourceData}; 30 | use rsx_stylesheet::types::{BorderStyle, Color}; 31 | use webrender::api; 32 | 33 | use super::util::{ 34 | make_border_details, 35 | make_border_widths, 36 | make_color_f, 37 | make_glyph_options, 38 | make_image_rendering_style, 39 | make_image_stretch_size, 40 | make_image_tile_spacing, 41 | make_primitive_info 42 | }; 43 | use build::types::{ 44 | BuiltDisplayList, 45 | DisplayListBuilder, 46 | FontInstanceKey, 47 | FontKey, 48 | GlyphInstance, 49 | ImageKey, 50 | ResourceKeysAPI, 51 | ResourceUpdates 52 | }; 53 | use prelude::{MeasuredImage, ShapedText}; 54 | use traits::TDisplayListBuilder; 55 | 56 | impl ResourceKeysAPI { 57 | #[cfg(feature = "dummy-api-mode")] 58 | pub fn dummy() -> Self { 59 | ResourceKeysAPI {} 60 | } 61 | } 62 | 63 | impl TImageKeysAPI for ResourceKeysAPI { 64 | type RootRendererAPI = Rc; 65 | type ResourceUpdates = ResourceUpdates; 66 | type ImageKey = ImageKey; 67 | 68 | #[cfg(not(feature = "dummy-api-mode"))] 69 | fn new(api: Self::RootRendererAPI) -> Self { 70 | ResourceKeysAPI { 71 | api, 72 | updates: ResourceUpdates::new() 73 | } 74 | } 75 | 76 | #[cfg(feature = "dummy-api-mode")] 77 | fn new(_: Self::RootRendererAPI) -> Self { 78 | ResourceKeysAPI::dummy() 79 | } 80 | 81 | #[cfg(not(feature = "dummy-api-mode"))] 82 | fn add_image(&mut self, _: ImageEncodedData, resource: ImageResourceData) -> Self::ImageKey { 83 | let image_key = self.api.as_ref().generate_image_key(); 84 | 85 | let format = match resource.format { 86 | ImagePixelFormat::Gray(8) => api::ImageFormat::A8, 87 | ImagePixelFormat::BGRA(8) => api::ImageFormat::BGRA8, 88 | _ => unimplemented!() 89 | }; 90 | 91 | let descriptor = api::ImageDescriptor::new(resource.size.0, resource.size.1, format, false); 92 | let data = api::ImageData::new_shared(Arc::clone(resource.pixels)); 93 | let tiling = None; 94 | 95 | self.updates.add_image(image_key, descriptor, data, tiling); 96 | 97 | image_key 98 | } 99 | 100 | #[cfg(feature = "dummy-api-mode")] 101 | fn add_image(&mut self, _: ImageEncodedData, _: ImageResourceData) -> Self::ImageKey { 102 | return ImageKey::new(api::IdNamespace(0), rand::thread_rng().gen::()); 103 | } 104 | 105 | #[cfg(feature = "dummy-api-mode")] 106 | fn take_resource_updates(&mut self) -> Self::ResourceUpdates { 107 | ResourceUpdates::new() 108 | } 109 | 110 | #[cfg(not(feature = "dummy-api-mode"))] 111 | fn take_resource_updates(&mut self) -> Self::ResourceUpdates { 112 | mem::replace(&mut self.updates, ResourceUpdates::new()) 113 | } 114 | } 115 | 116 | impl TFontKeysAPI for ResourceKeysAPI { 117 | type RootRendererAPI = Rc; 118 | type ResourceUpdates = ResourceUpdates; 119 | type FontKey = FontKey; 120 | type FontInstanceKey = FontInstanceKey; 121 | type GlyphInstance = GlyphInstance; 122 | 123 | #[cfg(not(feature = "dummy-api-mode"))] 124 | fn new(api: Self::RootRendererAPI) -> Self { 125 | ResourceKeysAPI { 126 | api, 127 | updates: ResourceUpdates::new() 128 | } 129 | } 130 | 131 | #[cfg(feature = "dummy-api-mode")] 132 | fn new(_: Self::RootRendererAPI) -> Self { 133 | ResourceKeysAPI::dummy() 134 | } 135 | 136 | #[cfg(not(feature = "dummy-api-mode"))] 137 | fn add_font(&mut self, _: FontEncodedData, resource: FontResourceData) -> Self::FontKey { 138 | let font_key = self.api.as_ref().generate_font_key(); 139 | 140 | let bytes = unsafe { (resource.bytes.deref() as *const Vec).read() }; // Fonts are never removed. 141 | let face_index = resource.face_index as u32; 142 | self.updates.add_raw_font(font_key, bytes, face_index); 143 | 144 | font_key 145 | } 146 | 147 | #[cfg(feature = "dummy-api-mode")] 148 | fn add_font(&mut self, _: FontEncodedData, _: FontResourceData) -> Self::FontKey { 149 | return FontKey::new(api::IdNamespace(0), rand::thread_rng().gen::()); 150 | } 151 | 152 | #[cfg(not(feature = "dummy-api-mode"))] 153 | fn add_font_instance(&mut self, font_key: Self::FontKey, resource: FontInstanceResourceData) -> Self::FontInstanceKey { 154 | let font_instance_key = self.api.as_ref().generate_font_instance_key(); 155 | 156 | let size = Au::from_f32_px(resource.size as f32); 157 | let options = None; 158 | let platform_options = None; 159 | let variations = vec![]; 160 | 161 | self.updates.add_font_instance( 162 | font_instance_key, 163 | font_key, 164 | size, 165 | options, 166 | platform_options, 167 | variations 168 | ); 169 | 170 | font_instance_key 171 | } 172 | 173 | #[cfg(feature = "dummy-api-mode")] 174 | fn add_font_instance(&mut self, _: Self::FontKey, _: FontInstanceResourceData) -> Self::FontInstanceKey { 175 | return FontInstanceKey::new(api::IdNamespace(0), rand::thread_rng().gen::()); 176 | } 177 | 178 | #[cfg(feature = "dummy-api-mode")] 179 | fn take_resource_updates(&mut self) -> Self::ResourceUpdates { 180 | ResourceUpdates::new() 181 | } 182 | 183 | #[cfg(not(feature = "dummy-api-mode"))] 184 | fn take_resource_updates(&mut self) -> Self::ResourceUpdates { 185 | mem::replace(&mut self.updates, ResourceUpdates::new()) 186 | } 187 | } 188 | 189 | impl TDisplayListBuilder for DisplayListBuilder { 190 | // Serialize 191 | 192 | type Serialized = BuiltDisplayList; 193 | 194 | fn serialize(self) -> Self::Serialized { 195 | let (.., serialized) = self.finalize(); 196 | serialized 197 | } 198 | 199 | // General 200 | 201 | type ClientRect = LayoutBoundingClientRect; 202 | type TransformMatrix = TypedTransform3D; 203 | type PerspectiveMatrix = TypedTransform3D; 204 | 205 | fn push_stacking_context(&mut self, transform: Option, perspective: Option) { 206 | self.push_stacking_context( 207 | &make_primitive_info(Default::default()), 208 | api::ScrollPolicy::Scrollable, 209 | transform.map(|v| v.into()), 210 | api::TransformStyle::Flat, 211 | perspective, 212 | api::MixBlendMode::Normal, 213 | vec![] 214 | ); 215 | } 216 | 217 | fn pop_stacking_context(&mut self) { 218 | self.pop_stacking_context(); 219 | } 220 | 221 | // Memory (unsupported) 222 | 223 | type DisplayListItem = !; 224 | 225 | fn get_first(&mut self) -> Option<&mut Self::DisplayListItem> { 226 | unreachable!() 227 | } 228 | 229 | fn get_previous(&mut self) -> Option<&mut Self::DisplayListItem> { 230 | unreachable!() 231 | } 232 | 233 | // Rect 234 | 235 | type RectColor = Color; 236 | 237 | fn push_rect(&mut self, bounding_client_rect: Self::ClientRect, background_color: Self::RectColor) { 238 | return_if_nil!(@rect: background_color); 239 | 240 | self.push_rect( 241 | &make_primitive_info(bounding_client_rect), 242 | make_color_f(background_color) 243 | ); 244 | } 245 | 246 | // Border 247 | 248 | type BorderSize = u32; 249 | type BorderColor = Color; 250 | type BorderStyle = BorderStyle; 251 | 252 | fn push_border( 253 | &mut self, 254 | bounding_client_rect: Self::ClientRect, 255 | border_widths: [Self::BorderSize; 4], 256 | border_colors: [Self::BorderColor; 4], 257 | border_styles: [Self::BorderStyle; 4] 258 | ) { 259 | return_if_nil!(@border: border_widths, border_colors, border_styles); 260 | debug_item!(@bounds: self, bounding_client_rect); 261 | 262 | self.push_border( 263 | &make_primitive_info(bounding_client_rect), 264 | make_border_widths(border_widths), 265 | make_border_details(border_colors, border_styles) 266 | ); 267 | } 268 | 269 | // Image 270 | 271 | type MeasuredImage = MeasuredImage; 272 | 273 | fn push_image( 274 | &mut self, 275 | bounding_client_rect: Self::ClientRect, 276 | measured_image: &Self::MeasuredImage, 277 | image_src: &Self::DebugImageSrc 278 | ) { 279 | let image_key = measured_image.image_key(); 280 | 281 | return_if_nil!(@image: image_key, image_src); 282 | debug_item!(@bounds: self, bounding_client_rect); 283 | 284 | if let Some(image_key) = image_key { 285 | self.push_image( 286 | &make_primitive_info(bounding_client_rect), 287 | make_image_stretch_size(bounding_client_rect.size), 288 | make_image_tile_spacing(), 289 | make_image_rendering_style(), 290 | image_key 291 | ); 292 | } else { 293 | println!("Warning: Image data missing for \"{}\"", image_src.as_ref()); 294 | } 295 | } 296 | 297 | // Text 298 | 299 | type TextColor = Color; 300 | type TextRun = ShapedText; 301 | 302 | fn push_text( 303 | &mut self, 304 | bounding_client_rect: Self::ClientRect, 305 | text_color: Self::TextColor, 306 | text_run: &Self::TextRun, 307 | text_content: &Self::DebugTextContent 308 | ) { 309 | let font_instance_key = text_run.font_instance_key(); 310 | let text_glyphs = text_run.glyphs(); 311 | 312 | return_if_nil!(@text: font_instance_key, text_color, text_glyphs, text_content); 313 | debug_item!(@bounds: self, bounding_client_rect); 314 | 315 | if let Some(font_instance_key) = font_instance_key { 316 | let transform = Some(TypedTransform3D::create_translation( 317 | bounding_client_rect.position.left as f32, 318 | bounding_client_rect.position.top as f32, 319 | 0.0 320 | )); 321 | 322 | TDisplayListBuilder::push_stacking_context(self, transform, None); 323 | 324 | self.push_text( 325 | &make_primitive_info(bounding_client_rect.zero_position()), 326 | text_glyphs.deref(), 327 | font_instance_key, 328 | make_color_f(text_color), 329 | make_glyph_options() 330 | ); 331 | 332 | TDisplayListBuilder::pop_stacking_context(self); 333 | } else { 334 | println!("Warning: Font missing for \"{}\"", text_content.as_ref()); 335 | } 336 | } 337 | 338 | // Debug 339 | 340 | type DebugTextContent = DOMText; 341 | type DebugImageSrc = DOMText; 342 | } 343 | -------------------------------------------------------------------------------- /src/build/webrender/display_list_differs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use build::types::DisplayListBuilder; 13 | use compare::traits::TDisplayListDiffer; 14 | use compare::types::{DisplayItemDiff, DisplayListDiff}; 15 | 16 | impl TDisplayListDiffer for DisplayListBuilder { 17 | type ListDiff = DisplayListDiff; 18 | type ItemDiff = DisplayItemDiff; 19 | type TextDisplayItem = (); 20 | type RectDisplayItem = (); 21 | type BorderDisplayItem = (); 22 | type ImageDisplayItem = (); 23 | 24 | fn diff(&self, _: &Self) -> Self::ListDiff { 25 | unimplemented!() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/build/webrender/types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #[cfg(not(feature = "dummy-api-mode"))] 13 | use std::rc::Rc; 14 | 15 | use webrender::api; 16 | 17 | // Images 18 | 19 | pub use self::ResourceKeysAPI as WebRenderImageKeysAPI; 20 | pub use self::api::ImageKey as WebRenderImageKey; 21 | 22 | // Images aliases 23 | 24 | pub use self::WebRenderImageKey as ImageKey; 25 | pub use self::WebRenderImageKeysAPI as ImageKeysAPI; 26 | 27 | // Fonts 28 | 29 | pub use self::ResourceKeysAPI as WebRenderFontKeysAPI; 30 | pub use self::api::FontInstanceKey as WebRenderFontInstanceKey; 31 | pub use self::api::FontKey as WebRenderFontKey; 32 | 33 | // Fonts aliases 34 | 35 | pub use self::WebRenderFontInstanceKey as FontInstanceKey; 36 | pub use self::WebRenderFontKey as FontKey; 37 | pub use self::WebRenderFontKeysAPI as FontKeysAPI; 38 | 39 | // Glyphs 40 | 41 | pub use self::api::GlyphInstance as WebRenderGlyphInstance; 42 | 43 | // Glyphs aliases 44 | 45 | pub use self::WebRenderGlyphInstance as GlyphInstance; 46 | 47 | // Updates 48 | 49 | pub use self::api::ResourceUpdates as WebRenderResourceUpdates; 50 | 51 | // Updates aliases 52 | 53 | pub use self::WebRenderResourceUpdates as ResourceUpdates; 54 | 55 | // List 56 | 57 | pub use self::api::BuiltDisplayList as WebRenderBuiltDisplayList; 58 | pub use self::api::DisplayListBuilder as WebRenderDisplayListBuilder; 59 | 60 | // List aliases 61 | 62 | pub use self::WebRenderBuiltDisplayList as BuiltDisplayList; 63 | pub use self::WebRenderDisplayListBuilder as DisplayListBuilder; 64 | 65 | // Keys 66 | 67 | pub struct WebRenderResourceKeysAPI { 68 | #[cfg(not(feature = "dummy-api-mode"))] 69 | pub(crate) api: Rc, 70 | #[cfg(not(feature = "dummy-api-mode"))] 71 | pub(crate) updates: api::ResourceUpdates 72 | } 73 | 74 | // Keys aliases 75 | 76 | pub use self::WebRenderResourceKeysAPI as ResourceKeysAPI; 77 | -------------------------------------------------------------------------------- /src/build/webrender/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_layout::types::{LayoutBoundingClientRect, LayoutClientSize}; 13 | use rsx_stylesheet::types::{BorderStyle, Color}; 14 | use webrender::api; 15 | 16 | pub fn make_primitive_info(bounding_client_rect: LayoutBoundingClientRect) -> api::LayoutPrimitiveInfo { 17 | api::PrimitiveInfo::new(make_layer_rect(bounding_client_rect)) 18 | } 19 | 20 | pub fn make_layer_rect(bounding_client_rect: LayoutBoundingClientRect) -> api::LayoutRect { 21 | api::LayoutRect::new( 22 | api::LayoutPoint::new( 23 | bounding_client_rect.position.left as f32, 24 | bounding_client_rect.position.top as f32 25 | ), 26 | api::LayoutSize::new( 27 | bounding_client_rect.size.width as f32, 28 | bounding_client_rect.size.height as f32 29 | ) 30 | ) 31 | } 32 | 33 | pub fn make_color_f(color: Color) -> api::ColorF { 34 | api::ColorF::new( 35 | f32::from(color.red) / 255.0, 36 | f32::from(color.green) / 255.0, 37 | f32::from(color.blue) / 255.0, 38 | f32::from(color.alpha) / 255.0 39 | ) 40 | } 41 | 42 | pub fn make_glyph_options() -> Option { 43 | Some(api::GlyphOptions { 44 | render_mode: api::FontRenderMode::Alpha 45 | }) 46 | } 47 | 48 | pub fn make_image_stretch_size(bounding_client_size: LayoutClientSize) -> api::LayoutSize { 49 | api::LayoutSize::new( 50 | bounding_client_size.width as f32, 51 | bounding_client_size.height as f32 52 | ) 53 | } 54 | 55 | pub fn make_image_tile_spacing() -> api::LayoutSize { 56 | api::LayoutSize::zero() 57 | } 58 | 59 | pub fn make_image_rendering_style() -> api::ImageRendering { 60 | api::ImageRendering::Auto 61 | } 62 | 63 | pub fn make_border_widths(border_widths: [u32; 4]) -> api::BorderWidths { 64 | api::BorderWidths { 65 | top: border_widths[0] as f32, 66 | right: border_widths[1] as f32, 67 | bottom: border_widths[2] as f32, 68 | left: border_widths[3] as f32 69 | } 70 | } 71 | 72 | pub fn make_border_style(border_style: BorderStyle) -> api::BorderStyle { 73 | match border_style { 74 | BorderStyle::None => api::BorderStyle::None, 75 | BorderStyle::Solid => api::BorderStyle::Solid, 76 | BorderStyle::Double => api::BorderStyle::Double, 77 | BorderStyle::Dotted => api::BorderStyle::Dotted, 78 | BorderStyle::Dashed => api::BorderStyle::Dashed, 79 | BorderStyle::Hidden => api::BorderStyle::Hidden, 80 | BorderStyle::Groove => api::BorderStyle::Groove, 81 | BorderStyle::Ridge => api::BorderStyle::Ridge, 82 | BorderStyle::Inset => api::BorderStyle::Inset, 83 | BorderStyle::Outset => api::BorderStyle::Outset 84 | } 85 | } 86 | 87 | pub fn make_border_details(border_colors: [Color; 4], border_styles: [BorderStyle; 4]) -> api::BorderDetails { 88 | api::BorderDetails::Normal(api::NormalBorder { 89 | top: api::BorderSide { 90 | color: make_color_f(border_colors[0]), 91 | style: make_border_style(border_styles[0]) 92 | }, 93 | right: api::BorderSide { 94 | color: make_color_f(border_colors[1]), 95 | style: make_border_style(border_styles[1]) 96 | }, 97 | bottom: api::BorderSide { 98 | color: make_color_f(border_colors[2]), 99 | style: make_border_style(border_styles[2]) 100 | }, 101 | left: api::BorderSide { 102 | color: make_color_f(border_colors[3]), 103 | style: make_border_style(border_styles[3]) 104 | }, 105 | radius: api::BorderRadius::uniform(0.0) 106 | }) 107 | } 108 | -------------------------------------------------------------------------------- /src/compare/export.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use std::ffi::CString; 13 | use std::os::raw::c_char; 14 | 15 | use rsx_dom::types::DOMText; 16 | use rsx_stylesheet::types::{BorderStyle, Color}; 17 | use serde::{Serialize, Serializer}; 18 | use serde_json; 19 | 20 | use build::traits::TDisplayListBuilder; 21 | use compare::traits::TDisplayListDiffer; 22 | use compare::types::{ 23 | BorderUpdate, 24 | BoundsUpdate, 25 | DisplayItemDiff, 26 | DisplayItemUpdate, 27 | DisplayListDiff, 28 | ImageUpdate, 29 | RectUpdate, 30 | SingleBorderUpdate, 31 | TextUpdate 32 | }; 33 | use prelude::{MeasuredImage, ShapedText}; 34 | 35 | pub const DISPLAY_ITEM_DIFF_KEY: &str = "Diff"; 36 | 37 | pub const ADD_RECT_KEY: &str = "AddRect"; 38 | pub const ADD_BORDER_KEY: &str = "AddBorder"; 39 | pub const ADD_IMAGE_KEY: &str = "AddImage"; 40 | pub const ADD_TEXT_KEY: &str = "AddText"; 41 | pub const REMOVE_SELF_KEY: &str = "D"; 42 | pub const REPLACE_SELF_WITH_RECT_KEY: &str = "IntoRect"; 43 | pub const REPLACE_SELF_WITH_BORDER_KEY: &str = "IntoBorder"; 44 | pub const REPLACE_SELF_WITH_IMAGE_KEY: &str = "IntoImage"; 45 | pub const REPLACE_SELF_WITH_TEXT_KEY: &str = "IntoText"; 46 | pub const UPDATE_SELF_KEY: &str = "M"; 47 | 48 | pub const BOUNDS_UPDATE_KEY: &str = "Z"; 49 | pub const BOUNDS_UPDATE_CHANGE_X_KEY: &str = "X"; 50 | pub const BOUNDS_UPDATE_CHANGE_Y_KEY: &str = "Y"; 51 | pub const BOUNDS_UPDATE_CHANGE_WIDTH_KEY: &str = "W"; 52 | pub const BOUNDS_UPDATE_CHANGE_HEIGHT_KEY: &str = "H"; 53 | 54 | pub const RECT_UPDATE_KEY: &str = "R"; 55 | pub const RECT_UPDATE_CHANGE_COLOR_KEY: &str = "C"; 56 | 57 | pub const BORDER_UPDATE_KEY: &str = "B"; 58 | pub const BORDER_UPDATE_CHANGE_TOP_KEY: &str = "T"; 59 | pub const BORDER_UPDATE_CHANGE_RIGHT_KEY: &str = "R"; 60 | pub const BORDER_UPDATE_CHANGE_BOTTOM_KEY: &str = "B"; 61 | pub const BORDER_UPDATE_CHANGE_LEFT_KEY: &str = "L"; 62 | pub const BORDER_UPDATE_CHANGE_WIDTH_KEY: &str = "W"; 63 | pub const BORDER_UPDATE_CHANGE_COLOR_KEY: &str = "C"; 64 | pub const BORDER_UPDATE_CHANGE_STYLE_KEY: &str = "S"; 65 | 66 | pub const IMAGE_UPDATE_KEY: &str = "I"; 67 | pub const IMAGE_UPDATE_CHANGE_DATA_KEY: &str = "I"; 68 | 69 | pub const TEXT_UPDATE_KEY: &str = "T"; 70 | pub const TEXT_UPDATE_CHANGE_COLOR_KEY: &str = "C"; 71 | pub const TEXT_UPDATE_CHANGE_CONTENT_KEY: &str = "T"; 72 | 73 | impl Into for DisplayListDiff 74 | where 75 | T: TDisplayListDiffer< 76 | // Rect 77 | RectColor = Color, 78 | // Border 79 | BorderSize = u32, 80 | BorderColor = Color, 81 | BorderStyle = BorderStyle, 82 | // Image 83 | MeasuredImage = MeasuredImage, 84 | // Text 85 | TextColor = Color, 86 | TextRun = ShapedText, 87 | // Debug 88 | DebugImageSrc = DOMText, 89 | DebugTextContent = DOMText 90 | > 91 | { 92 | #[cfg(not(feature = "pretty-json-mode"))] 93 | fn into(self) -> String { 94 | serde_json::to_string(&self.changes).unwrap_or_else(|_| "".to_string()) 95 | } 96 | 97 | #[cfg(feature = "pretty-json-mode")] 98 | fn into(self) -> String { 99 | serde_json::to_string_pretty(&self.changes).unwrap_or_else(|_| "".to_string()) 100 | } 101 | } 102 | 103 | impl Into<*mut c_char> for DisplayListDiff 104 | where 105 | T: TDisplayListDiffer< 106 | // Rect 107 | RectColor = Color, 108 | // Border 109 | BorderSize = u32, 110 | BorderColor = Color, 111 | BorderStyle = BorderStyle, 112 | // Image 113 | MeasuredImage = MeasuredImage, 114 | // Text 115 | TextColor = Color, 116 | TextRun = ShapedText, 117 | // Debug 118 | DebugImageSrc = DOMText, 119 | DebugTextContent = DOMText 120 | > 121 | { 122 | fn into(self) -> *mut c_char { 123 | let string: String = self.into(); 124 | unsafe { CString::from_vec_unchecked(string.into_bytes()) }.into_raw() 125 | } 126 | } 127 | 128 | impl Serialize for DisplayItemDiff 129 | where 130 | T: TDisplayListDiffer< 131 | // Rect 132 | RectColor = Color, 133 | // Border 134 | BorderSize = u32, 135 | BorderColor = Color, 136 | BorderStyle = BorderStyle, 137 | // Image 138 | MeasuredImage = MeasuredImage, 139 | // Text 140 | TextColor = Color, 141 | TextRun = ShapedText, 142 | // Debug 143 | DebugImageSrc = DOMText, 144 | DebugTextContent = DOMText 145 | > 146 | { 147 | fn serialize(&self, serializer: S) -> Result 148 | where 149 | S: Serializer 150 | { 151 | match self { 152 | &DisplayItemDiff::AddRect(ref item) => serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 0, ADD_RECT_KEY, item), 153 | &DisplayItemDiff::AddBorder(ref item) => serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 1, ADD_BORDER_KEY, item), 154 | &DisplayItemDiff::AddImage(ref item) => serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 2, ADD_IMAGE_KEY, item), 155 | &DisplayItemDiff::AddText(ref item) => serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 3, ADD_TEXT_KEY, item), 156 | &DisplayItemDiff::RemoveSelf(ref index) => { 157 | serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 4, REMOVE_SELF_KEY, index) 158 | } 159 | &DisplayItemDiff::ReplaceSelfWithRect(ref index, ref item) => { 160 | let v = &(index, item); 161 | serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 5, REPLACE_SELF_WITH_RECT_KEY, v) 162 | } 163 | &DisplayItemDiff::ReplaceSelfWithBorder(ref index, ref item) => { 164 | let v = &(index, item); 165 | serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 6, REPLACE_SELF_WITH_BORDER_KEY, v) 166 | } 167 | &DisplayItemDiff::ReplaceSelfWithImage(ref index, ref item) => { 168 | let v = &(index, item); 169 | serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 7, REPLACE_SELF_WITH_IMAGE_KEY, v) 170 | } 171 | &DisplayItemDiff::ReplaceSelfWithText(ref index, ref item) => { 172 | let v = &(index, item); 173 | serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 8, REPLACE_SELF_WITH_TEXT_KEY, v) 174 | } 175 | &DisplayItemDiff::UpdateSelf(ref index, ref vec) => { 176 | let v = &(index, vec); 177 | serializer.serialize_newtype_variant(DISPLAY_ITEM_DIFF_KEY, 9, UPDATE_SELF_KEY, v) 178 | } 179 | } 180 | } 181 | } 182 | 183 | impl Serialize for DisplayItemUpdate 184 | where 185 | T: TDisplayListBuilder< 186 | // Rect 187 | RectColor = Color, 188 | // Border 189 | BorderSize = u32, 190 | BorderColor = Color, 191 | BorderStyle = BorderStyle, 192 | // Image 193 | MeasuredImage = MeasuredImage, 194 | // Text 195 | TextColor = Color, 196 | TextRun = ShapedText, 197 | // Debug 198 | DebugImageSrc = DOMText, 199 | DebugTextContent = DOMText 200 | > 201 | { 202 | fn serialize(&self, serializer: S) -> Result 203 | where 204 | S: Serializer 205 | { 206 | match self { 207 | &DisplayItemUpdate::BoundsUpdate(ref bounds_update) => { 208 | serializer.serialize_newtype_variant(UPDATE_SELF_KEY, 0, BOUNDS_UPDATE_KEY, bounds_update) 209 | } 210 | &DisplayItemUpdate::RectUpdate(ref rect_update) => { 211 | serializer.serialize_newtype_variant(UPDATE_SELF_KEY, 1, RECT_UPDATE_KEY, rect_update) 212 | } 213 | &DisplayItemUpdate::BorderUpdate(ref border_update) => { 214 | serializer.serialize_newtype_variant(UPDATE_SELF_KEY, 2, BORDER_UPDATE_KEY, border_update) 215 | } 216 | &DisplayItemUpdate::ImageUpdate(ref image_update) => { 217 | serializer.serialize_newtype_variant(UPDATE_SELF_KEY, 3, IMAGE_UPDATE_KEY, image_update) 218 | } 219 | &DisplayItemUpdate::TextUpdate(ref text_update) => { 220 | serializer.serialize_newtype_variant(UPDATE_SELF_KEY, 4, TEXT_UPDATE_KEY, text_update) 221 | } 222 | } 223 | } 224 | } 225 | 226 | impl Serialize for BoundsUpdate { 227 | fn serialize(&self, serializer: S) -> Result 228 | where 229 | S: Serializer 230 | { 231 | match self { 232 | &BoundsUpdate::ChangePositionX(ref l) => { 233 | serializer.serialize_newtype_variant(BOUNDS_UPDATE_KEY, 0, BOUNDS_UPDATE_CHANGE_X_KEY, l) 234 | } 235 | &BoundsUpdate::ChangePositionY(ref t) => { 236 | serializer.serialize_newtype_variant(BOUNDS_UPDATE_KEY, 1, BOUNDS_UPDATE_CHANGE_Y_KEY, t) 237 | } 238 | &BoundsUpdate::ChangeSizeWidth(ref w) => { 239 | serializer.serialize_newtype_variant(BOUNDS_UPDATE_KEY, 2, BOUNDS_UPDATE_CHANGE_WIDTH_KEY, w) 240 | } 241 | &BoundsUpdate::ChangeSizeHeight(ref h) => { 242 | serializer.serialize_newtype_variant(BOUNDS_UPDATE_KEY, 3, BOUNDS_UPDATE_CHANGE_HEIGHT_KEY, h) 243 | } 244 | } 245 | } 246 | } 247 | 248 | impl Serialize for RectUpdate 249 | where 250 | T: TDisplayListBuilder 251 | { 252 | fn serialize(&self, serializer: S) -> Result 253 | where 254 | S: Serializer 255 | { 256 | match self { 257 | &RectUpdate::ChangeColor(ref color) => { 258 | let v = &[color.red, color.green, color.blue, color.alpha]; 259 | serializer.serialize_newtype_variant(RECT_UPDATE_KEY, 0, RECT_UPDATE_CHANGE_COLOR_KEY, v) 260 | } 261 | } 262 | } 263 | } 264 | 265 | impl Serialize for BorderUpdate 266 | where 267 | T: TDisplayListBuilder 268 | { 269 | fn serialize(&self, serializer: S) -> Result 270 | where 271 | S: Serializer 272 | { 273 | match self { 274 | &BorderUpdate::ChangeTopBorder(ref c) => { 275 | serializer.serialize_newtype_variant(BORDER_UPDATE_KEY, 0, BORDER_UPDATE_CHANGE_TOP_KEY, c) 276 | } 277 | &BorderUpdate::ChangeRightBorder(ref c) => { 278 | serializer.serialize_newtype_variant(BORDER_UPDATE_KEY, 1, BORDER_UPDATE_CHANGE_RIGHT_KEY, c) 279 | } 280 | &BorderUpdate::ChangeBottomBorder(ref c) => { 281 | serializer.serialize_newtype_variant(BORDER_UPDATE_KEY, 2, BORDER_UPDATE_CHANGE_BOTTOM_KEY, c) 282 | } 283 | &BorderUpdate::ChangeLeftBorder(ref c) => { 284 | serializer.serialize_newtype_variant(BORDER_UPDATE_KEY, 3, BORDER_UPDATE_CHANGE_LEFT_KEY, c) 285 | } 286 | } 287 | } 288 | } 289 | 290 | impl Serialize for SingleBorderUpdate 291 | where 292 | T: TDisplayListBuilder 293 | { 294 | fn serialize(&self, serializer: S) -> Result 295 | where 296 | S: Serializer 297 | { 298 | match self { 299 | &SingleBorderUpdate::ChangeSize(ref width) => { 300 | serializer.serialize_newtype_variant("", 0, BORDER_UPDATE_CHANGE_WIDTH_KEY, width) 301 | } 302 | &SingleBorderUpdate::ChangeColor(ref color) => { 303 | let value = &[color.red, color.green, color.blue, color.alpha]; 304 | serializer.serialize_newtype_variant("", 1, BORDER_UPDATE_CHANGE_COLOR_KEY, value) 305 | } 306 | &SingleBorderUpdate::ChangeStyle(ref style) => { 307 | serializer.serialize_newtype_variant("", 2, BORDER_UPDATE_CHANGE_STYLE_KEY, style) 308 | } 309 | } 310 | } 311 | } 312 | 313 | impl Serialize for ImageUpdate 314 | where 315 | T: TDisplayListBuilder 316 | { 317 | fn serialize(&self, serializer: S) -> Result 318 | where 319 | S: Serializer 320 | { 321 | match self { 322 | &ImageUpdate::ChangeImage { ref raw, .. } => { 323 | let v = raw.as_ref(); 324 | serializer.serialize_newtype_variant(IMAGE_UPDATE_KEY, 0, IMAGE_UPDATE_CHANGE_DATA_KEY, v) 325 | } 326 | } 327 | } 328 | } 329 | 330 | impl Serialize for TextUpdate 331 | where 332 | T: TDisplayListBuilder 333 | { 334 | fn serialize(&self, serializer: S) -> Result 335 | where 336 | S: Serializer 337 | { 338 | match self { 339 | &TextUpdate::ChangeColor(ref color) => { 340 | let v = &[color.red, color.green, color.blue, color.alpha]; 341 | serializer.serialize_newtype_variant(TEXT_UPDATE_KEY, 0, TEXT_UPDATE_CHANGE_COLOR_KEY, v) 342 | } 343 | &TextUpdate::ChangeText { ref raw, .. } => { 344 | let v = &raw.join(""); 345 | serializer.serialize_newtype_variant(TEXT_UPDATE_KEY, 1, TEXT_UPDATE_CHANGE_CONTENT_KEY, v) 346 | } 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/compare/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | pub mod traits; 13 | pub mod types; 14 | pub mod export; 15 | -------------------------------------------------------------------------------- /src/compare/traits.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use serde::{Deserialize, Serialize}; 13 | 14 | use build::traits::TDisplayListBuilder; 15 | 16 | pub trait TDisplayListDiffer: TDisplayListBuilder 17 | where 18 | Self::TextDisplayItem: Serialize + for<'a> Deserialize<'a>, 19 | Self::RectDisplayItem: Serialize + for<'a> Deserialize<'a>, 20 | Self::BorderDisplayItem: Serialize + for<'a> Deserialize<'a>, 21 | Self::ImageDisplayItem: Serialize + for<'a> Deserialize<'a> 22 | { 23 | type ListDiff; 24 | type ItemDiff; 25 | type TextDisplayItem; 26 | type RectDisplayItem; 27 | type BorderDisplayItem; 28 | type ImageDisplayItem; 29 | 30 | fn diff(&self, other: &Self) -> Self::ListDiff; 31 | } 32 | 33 | pub trait TTextDisplayItemDiffer { 34 | type ItemUpdate; 35 | 36 | fn diff(&self, other: &Self) -> Vec; 37 | } 38 | 39 | pub trait TRectDisplayItemDiffer { 40 | type ItemUpdate; 41 | 42 | fn diff(&self, other: &Self) -> Vec; 43 | } 44 | 45 | pub trait TBorderDisplayItemDiffer { 46 | type ItemUpdate; 47 | 48 | fn diff(&self, other: &Self) -> Vec; 49 | } 50 | 51 | pub trait TImageDisplayItemDiffer { 52 | type ItemUpdate; 53 | 54 | fn diff(&self, other: &Self) -> Vec; 55 | } 56 | -------------------------------------------------------------------------------- /src/compare/types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use smallvec::SmallVec; 13 | 14 | use build::traits::TDisplayListBuilder; 15 | use compare::traits::TDisplayListDiffer; 16 | 17 | pub struct DisplayListDiff { 18 | pub changes: Vec> 19 | } 20 | 21 | pub enum DisplayItemDiff { 22 | AddRect(T::RectDisplayItem), 23 | AddBorder(T::BorderDisplayItem), 24 | AddImage(T::ImageDisplayItem), 25 | AddText(T::TextDisplayItem), 26 | RemoveSelf(usize), 27 | ReplaceSelfWithRect(usize, T::RectDisplayItem), 28 | ReplaceSelfWithBorder(usize, T::BorderDisplayItem), 29 | ReplaceSelfWithImage(usize, T::ImageDisplayItem), 30 | ReplaceSelfWithText(usize, T::TextDisplayItem), 31 | UpdateSelf(usize, Vec>) 32 | } 33 | 34 | pub enum DisplayItemUpdate { 35 | BoundsUpdate(BoundsUpdate), 36 | RectUpdate(RectUpdate), 37 | BorderUpdate(BorderUpdate), 38 | ImageUpdate(ImageUpdate), 39 | TextUpdate(TextUpdate) 40 | } 41 | 42 | pub enum BoundsUpdate { 43 | ChangePositionX(u32), 44 | ChangePositionY(u32), 45 | ChangeSizeWidth(u32), 46 | ChangeSizeHeight(u32) 47 | } 48 | 49 | pub enum RectUpdate { 50 | ChangeColor(T::RectColor) 51 | } 52 | 53 | pub enum BorderUpdate { 54 | ChangeTopBorder(SingleBorderUpdate), 55 | ChangeRightBorder(SingleBorderUpdate), 56 | ChangeBottomBorder(SingleBorderUpdate), 57 | ChangeLeftBorder(SingleBorderUpdate) 58 | } 59 | 60 | pub enum SingleBorderUpdate { 61 | ChangeSize(T::BorderSize), 62 | ChangeColor(T::BorderColor), 63 | ChangeStyle(T::BorderStyle) 64 | } 65 | 66 | pub enum ImageUpdate { 67 | ChangeImage { 68 | raw: T::DebugImageSrc, 69 | #[cfg(not(feature = "no-json-images-mode"))] 70 | measured_image: T::MeasuredImage 71 | } 72 | } 73 | 74 | pub enum TextUpdate { 75 | ChangeColor(T::TextColor), 76 | ChangeText { 77 | raw: SmallVec<[T::DebugTextContent; 1]>, 78 | #[cfg(not(feature = "no-json-glyphs-mode"))] 79 | text_run: SmallVec<[T::TextRun; 1]> 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/convert/convert_image_node.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_dom::types::DOMText; 13 | use rsx_dom::util::find_src; 14 | use rsx_layout::types::LayoutBoundingClientRect; 15 | use rsx_shared::traits::TDOMNode; 16 | use rsx_stylesheet::types::{BorderStyle, Color}; 17 | 18 | use convert::push_view_dom_node; 19 | use prelude::{DOMNodeRef, MeasuredImage}; 20 | use traits::TDisplayListBuilder; 21 | 22 | pub fn push_image_dom_node(display_list_builder: &mut T, bounding_client_rect: LayoutBoundingClientRect, node_ref: &DOMNodeRef) 23 | where 24 | T: TDisplayListBuilder< 25 | // General 26 | ClientRect = LayoutBoundingClientRect, 27 | // Rect 28 | RectColor = Color, 29 | // Border 30 | BorderSize = u32, 31 | BorderColor = Color, 32 | BorderStyle = BorderStyle, 33 | // Image 34 | MeasuredImage = MeasuredImage, 35 | // Debug 36 | DebugImageSrc = DOMText 37 | > 38 | { 39 | let measured_image = node_ref.get_measured_image(); 40 | let image_src = find_src(node_ref.data().get_attributes()); 41 | 42 | push_view_dom_node(display_list_builder, bounding_client_rect, node_ref); 43 | 44 | display_list_builder.push_image( 45 | bounding_client_rect, 46 | measured_image, 47 | image_src.unwrap_or(&DOMText::from("")) 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/convert/convert_inline_text_node.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_dom::types::DOMText; 13 | use rsx_layout::types::LayoutBoundingClientRect; 14 | use rsx_shared::traits::{TDOMNode, TInheritedStyles}; 15 | use rsx_stylesheet::types::Color; 16 | 17 | use prelude::{DOMNodeRef, ShapedText}; 18 | use traits::TDisplayListBuilder; 19 | 20 | pub fn push_inline_text_node(display_list_builder: &mut T, bounding_client_rect: LayoutBoundingClientRect, node_ref: &DOMNodeRef) 21 | where 22 | T: TDisplayListBuilder< 23 | // General 24 | ClientRect = LayoutBoundingClientRect, 25 | // Text 26 | TextColor = Color, 27 | TextRun = ShapedText, 28 | // Debug 29 | DebugTextContent = DOMText 30 | > 31 | { 32 | let computed_color = node_ref.computed_styles().color(); 33 | let shaped_text = node_ref.get_shaped_text(); 34 | let source_text = node_ref.data().text().unwrap(); 35 | 36 | display_list_builder.push_text( 37 | bounding_client_rect, 38 | computed_color, 39 | shaped_text, 40 | source_text 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/convert/convert_view_node.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_layout::types::LayoutBoundingClientRect; 13 | use rsx_shared::traits::{TComputedStyles, TDOMNode}; 14 | use rsx_stylesheet::types::{BorderStyle, Color}; 15 | 16 | use prelude::DOMNodeRef; 17 | use traits::TDisplayListBuilder; 18 | 19 | #[cfg_attr(feature = "cargo-clippy", allow(single_match))] 20 | pub fn push_view_dom_node(display_list_builder: &mut T, bounding_client_rect: LayoutBoundingClientRect, node_ref: &DOMNodeRef) 21 | where 22 | T: TDisplayListBuilder< 23 | // General 24 | ClientRect = LayoutBoundingClientRect, 25 | // Rect 26 | RectColor = Color, 27 | // Border 28 | BorderSize = u32, 29 | BorderColor = Color, 30 | BorderStyle = BorderStyle 31 | > 32 | { 33 | let computed_styles = node_ref.computed_styles(); 34 | let background_color = computed_styles.background_color(); 35 | let border_widths = [ 36 | computed_styles.border_top_width(), 37 | computed_styles.border_right_width(), 38 | computed_styles.border_bottom_width(), 39 | computed_styles.border_left_width(), 40 | ]; 41 | let border_colors = [ 42 | computed_styles.border_top_color(), 43 | computed_styles.border_right_color(), 44 | computed_styles.border_bottom_color(), 45 | computed_styles.border_left_color(), 46 | ]; 47 | let border_styles = [ 48 | computed_styles.border_top_style(), 49 | computed_styles.border_right_style(), 50 | computed_styles.border_bottom_style(), 51 | computed_styles.border_left_style(), 52 | ]; 53 | 54 | display_list_builder.push_rect(bounding_client_rect, background_color); 55 | 56 | display_list_builder.push_border( 57 | bounding_client_rect, 58 | border_widths, 59 | border_colors, 60 | border_styles 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /src/convert/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | mod convert_image_node; 13 | mod convert_inline_text_node; 14 | mod convert_view_node; 15 | 16 | pub use self::convert_image_node::*; 17 | pub use self::convert_inline_text_node::*; 18 | pub use self::convert_view_node::*; 19 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #[cfg(feature = "webrender-display-list")] 13 | pub fn webrender_display_list_to_string(display_list: &::webrender::api::BuiltDisplayList) -> String { 14 | let mut iter = display_list.iter(); 15 | let mut ret = vec![]; 16 | 17 | while let Some(item) = iter.next() { 18 | ret.push(( 19 | *item.display_item(), 20 | display_list.get(item.glyphs()).collect::>() 21 | )); 22 | } 23 | 24 | format!("{:#?}", ret) 25 | } 26 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #![cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))] 13 | #![feature(never_type)] 14 | #![feature(pointer_methods)] 15 | #![feature(macro_reexport)] 16 | 17 | #[macro_reexport(fragment)] 18 | pub extern crate rsx_dom; 19 | pub extern crate rsx_event_manager; 20 | pub extern crate rsx_layout; 21 | pub extern crate rsx_resources; 22 | pub extern crate rsx_shared; 23 | pub extern crate rsx_stylesheet; 24 | 25 | #[cfg(feature = "webrender-display-list")] 26 | pub extern crate app_units; 27 | #[cfg(feature = "webrender-display-list")] 28 | pub extern crate euclid; 29 | #[cfg(feature = "webrender-display-list")] 30 | pub extern crate webrender; 31 | 32 | extern crate bincode; 33 | extern crate serde; 34 | #[macro_use] 35 | extern crate serde_derive; 36 | extern crate serde_json; 37 | extern crate smallvec; 38 | 39 | #[cfg(feature = "dummy-api-mode")] 40 | extern crate rand; 41 | 42 | #[macro_use] 43 | mod macros; 44 | mod convert; 45 | 46 | pub mod debug; 47 | pub mod build; 48 | pub mod compare; 49 | pub mod prelude; 50 | pub mod types; 51 | 52 | pub mod traits { 53 | pub use build::traits::*; 54 | } 55 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | pub use rsx_stylesheet::types::Color; 13 | 14 | #[cfg(feature = "display-list-debug-mode")] 15 | pub const DEBUG_COLOR: Color = Color { 16 | red: 128, 17 | green: 0, 18 | blue: 0, 19 | alpha: 128 20 | }; 21 | 22 | #[cfg(not(feature = "display-list-optimize-mode"))] 23 | macro_rules! return_if_nil { 24 | ( $( $tt:tt )* ) => {}; 25 | } 26 | 27 | #[cfg(feature = "display-list-optimize-mode")] 28 | macro_rules! return_if_nil { 29 | (@rect: $background_color:expr) => { 30 | if $background_color.alpha == 0 { 31 | return; 32 | } 33 | }; 34 | (@border: $widths:expr, $colors:expr, $styles:expr) => { 35 | if $widths[0] == 0 && $widths[1] == 0 && $widths[2] == 0 && $widths[3] == 0 { 36 | return; 37 | } 38 | if $colors[0].alpha == 0 && $colors[1].alpha == 0 && $colors[2].alpha == 0 && $colors[3].alpha == 0 { 39 | return; 40 | } 41 | if $styles[0] == BorderStyle::None && $styles[1] == BorderStyle::None && $styles[2] == BorderStyle::None && $styles[3] == BorderStyle::None { 42 | return; 43 | } 44 | }; 45 | (@image: $image_key:expr, $image_src:expr) => { 46 | if $image_key.is_none() { 47 | // println!("Warning: Image data missing for \"{}\"", $image_src.as_ref()); 48 | return; 49 | } 50 | }; 51 | (@text: $font_instance_key:expr, $text_color:expr, $text_glyphs:expr, $text_content:expr) => { 52 | if $font_instance_key.is_none() { 53 | // println!("Warning: Font data missing for \"{}\"", $text_content.as_ref()); 54 | return; 55 | } 56 | }; 57 | } 58 | 59 | #[cfg(not(feature = "display-list-debug-mode"))] 60 | macro_rules! debug_item { 61 | ( $( $tt:tt )* ) => {}; 62 | } 63 | 64 | #[cfg(feature = "display-list-debug-mode")] 65 | macro_rules! debug_item { 66 | (@bounds: $self:expr, $bounding_client_rect:expr) => { 67 | TDisplayListBuilder::push_rect($self, $bounding_client_rect, ::macros::DEBUG_COLOR) 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_dom::types::{ 13 | DOMArenaRef as TDOMNodeRef, 14 | DOMArenaRefMut as TDOMNodeRefMut, 15 | DOMAttribute as TDOMAttribute, 16 | DOMAttributeValue as TDOMAttributeValue, 17 | DOMAttributes as TDOMAttributes, 18 | DOMChildren as TDOMChildren, 19 | DOMData as TDOMData, 20 | DOMNode as TDOMNode, 21 | DOMNodeId as TDOMNodeId, 22 | DOMText, 23 | DOMTree as TDOMTree 24 | }; 25 | use rsx_event_manager::types::{Event, EventManager as TEventManager}; 26 | use rsx_layout::types::{LayoutNode as TLayoutNode, MeasuredImage as TMeasuredImage, ShapedText as TShapedText}; 27 | use rsx_resources::files::types::FileCache as TFileCache; 28 | use rsx_resources::fonts::types::{FontCache as TFontCache, GlyphStore as TGlyphStore}; 29 | use rsx_resources::images::types::{ImageCache as TImageCache, ImageDimensionsInfo as TImageDimensionsInfo}; 30 | use rsx_resources::types::ResourceGroup as TResourceGroup; 31 | use rsx_stylesheet::types::{ComputedStyles, StyleDeclarations}; 32 | 33 | use build::types::{FontInstanceKey, FontKey, FontKeysAPI, GlyphInstance, ImageKey, ImageKeysAPI}; 34 | 35 | // Resources 36 | 37 | pub type FileCache = TFileCache; 38 | pub type ImageCache = TImageCache; 39 | pub type FontCache = TFontCache; 40 | pub type ResourceGroup = TResourceGroup; 41 | 42 | // Layout Nodes 43 | 44 | pub type LayoutNode = TLayoutNode, DOMText>; 45 | 46 | // Layout Images 47 | 48 | pub type ImageDimensionsInfo = TImageDimensionsInfo; 49 | pub type MeasuredImage = TMeasuredImage; 50 | 51 | // Layout Text 52 | 53 | pub type GlyphStore = TGlyphStore; 54 | pub type ShapedText = TShapedText; 55 | 56 | // DOM Tree 57 | 58 | pub type DOMNodeId = TDOMNodeId; 59 | pub type DOMNodeRef<'a> = TDOMNodeRef<'a, Event, StyleDeclarations, ComputedStyles, LayoutNode>; 60 | pub type DOMNodeRefMut<'a> = TDOMNodeRefMut<'a, Event, StyleDeclarations, ComputedStyles, LayoutNode>; 61 | 62 | // DOM Nodes 63 | 64 | pub type DOMTree = TDOMTree; 65 | pub type DOMNode = TDOMNode; 66 | pub type DOMData = TDOMData; 67 | pub type DOMChildren = TDOMChildren; 68 | pub type DOMAttribute = TDOMAttribute; 69 | pub type DOMAttributes = TDOMAttributes; 70 | pub type DOMAttributeValue = TDOMAttributeValue; 71 | 72 | // Event Manager 73 | 74 | pub type EventManager = TEventManager; 75 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | use rsx_dom::types::{DOMTagName, KnownElementName}; 13 | use rsx_layout::types::LayoutClientPosition; 14 | use rsx_shared::traits::TDOMNode; 15 | #[cfg(feature = "webrender-display-list")] 16 | use webrender::api::{LayoutSize, PipelineId}; 17 | 18 | use build::types::{BuiltDisplayList, DisplayListBuilder}; 19 | use compare::traits::TDisplayListDiffer; 20 | use compare::types::DisplayListDiff; 21 | use convert::{push_image_dom_node, push_inline_text_node, push_view_dom_node}; 22 | use prelude::{DOMNodeId, DOMTree}; 23 | use traits::TDisplayListBuilder; 24 | 25 | pub struct DisplayList { 26 | builder: DisplayListBuilder 27 | } 28 | 29 | #[cfg(feature = "bincode-display-list")] 30 | impl Default for DisplayList { 31 | fn default() -> Self { 32 | DisplayList::new() 33 | } 34 | } 35 | 36 | #[cfg(feature = "json-display-list")] 37 | impl Default for DisplayList { 38 | fn default() -> Self { 39 | DisplayList::new() 40 | } 41 | } 42 | 43 | impl DisplayList { 44 | #[cfg(feature = "bincode-display-list")] 45 | pub fn new() -> Self { 46 | DisplayList { 47 | builder: DisplayListBuilder(Vec::with_capacity(65535)) 48 | } 49 | } 50 | 51 | #[cfg(feature = "json-display-list")] 52 | pub fn new() -> Self { 53 | DisplayList { 54 | builder: DisplayListBuilder(Vec::with_capacity(65535)) 55 | } 56 | } 57 | 58 | #[cfg(feature = "webrender-display-list")] 59 | pub fn new(pipeline_id: PipelineId, layout_size: LayoutSize) -> Self { 60 | DisplayList { 61 | builder: DisplayListBuilder::new(pipeline_id, layout_size) 62 | } 63 | } 64 | 65 | #[cfg(feature = "bincode-display-list")] 66 | pub fn from(tree: &mut DOMTree) -> Self { 67 | let mut display_list = DisplayList::new(); 68 | let root = tree.root().id(); 69 | display_list.push(LayoutClientPosition::default(), tree, root); 70 | display_list 71 | } 72 | 73 | #[cfg(feature = "json-display-list")] 74 | pub fn from(tree: &mut DOMTree) -> Self { 75 | let mut display_list = DisplayList::new(); 76 | let root = tree.root().id(); 77 | display_list.push(LayoutClientPosition::default(), tree, root); 78 | display_list 79 | } 80 | 81 | #[cfg(all(feature = "webrender-display-list", feature = "dummy-api-mode"))] 82 | pub fn from(tree: &mut DOMTree, width: f32, height: f32) -> Self { 83 | let mut display_list = DisplayList::new(PipelineId::dummy(), LayoutSize::new(width, height)); 84 | let root = tree.root().id(); 85 | display_list.push(LayoutClientPosition::default(), tree, root); 86 | display_list 87 | } 88 | 89 | #[cfg(all(feature = "webrender-display-list", not(feature = "dummy-api-mode")))] 90 | pub fn from(tree: &mut DOMTree, pipeline_id: PipelineId, layout_size: LayoutSize) -> Self { 91 | let mut display_list = DisplayList::new(pipeline_id, layout_size); 92 | let root = tree.root().id(); 93 | display_list.push(LayoutClientPosition::default(), tree, root); 94 | display_list 95 | } 96 | 97 | pub fn push(&mut self, client_offsets: LayoutClientPosition, tree: &mut DOMTree, id: DOMNodeId) { 98 | use self::DOMTagName::*; 99 | use self::KnownElementName::*; 100 | 101 | let bounding_client_rect; 102 | let mut next_sibling_id; 103 | 104 | { 105 | let node_ref = tree.get(id); 106 | next_sibling_id = node_ref.first_child_id(); 107 | bounding_client_rect = node_ref.get_local_bounding_client_rect() + client_offsets; 108 | 109 | match node_ref.data().tag() { 110 | None => push_inline_text_node(&mut self.builder, bounding_client_rect, &node_ref), 111 | Some(&KnownName(Image)) => push_image_dom_node(&mut self.builder, bounding_client_rect, &node_ref), 112 | Some(&KnownName(Fragment)) => {} // noop 113 | Some(_) => push_view_dom_node(&mut self.builder, bounding_client_rect, &node_ref) 114 | } 115 | } 116 | 117 | while let Some(child_id) = next_sibling_id { 118 | self.push(bounding_client_rect.position, tree, child_id); 119 | next_sibling_id = tree.get(child_id).next_sibling_id(); 120 | } 121 | 122 | let mut node_mut = tree.get_mut(id); 123 | node_mut.set_computed_client_position(client_offsets); 124 | } 125 | 126 | pub fn diff(&self, other: &Self) -> DisplayListDiff { 127 | self.builder.diff(&other.builder) 128 | } 129 | 130 | pub fn serialize(self) -> BuiltDisplayList { 131 | self.builder.serialize() 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /tests/fixtures/FiraMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorporof/rsx-primitives/6e2103e90c6cb5fafe9471c3fba9aa17b31d6aa9/tests/fixtures/FiraMono-Regular.ttf -------------------------------------------------------------------------------- /tests/fixtures/FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorporof/rsx-primitives/6e2103e90c6cb5fafe9471c3fba9aa17b31d6aa9/tests/fixtures/FreeSans.ttf -------------------------------------------------------------------------------- /tests/fixtures/Quantum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorporof/rsx-primitives/6e2103e90c6cb5fafe9471c3fba9aa17b31d6aa9/tests/fixtures/Quantum.png -------------------------------------------------------------------------------- /tests/fixtures/test_1.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | .foo { 13 | font-size: 12px; 14 | } 15 | 16 | .root { 17 | width: 500px; 18 | height: 120px; 19 | flex-direction: row; 20 | padding: 20px; 21 | } 22 | 23 | .image { 24 | width: 80px; 25 | margin-right: 20px; 26 | } 27 | 28 | .text { 29 | height: 25px; 30 | align-self: center; 31 | flex-grow: 1; 32 | font-size: 12px; 33 | } 34 | -------------------------------------------------------------------------------- /tests/fixtures/test_2.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | .foo { 13 | font-size: 12px; 14 | } 15 | 16 | .bar { 17 | font-size: 12px; 18 | color: #f0f; 19 | } 20 | 21 | .root { 22 | background-color: red; 23 | width: 500px; 24 | height: 120px; 25 | flex-direction: row; 26 | padding: 20px; 27 | } 28 | 29 | .image { 30 | background-color: green; 31 | opacity: 0.5; 32 | width: 80px; 33 | margin-right: 20px; 34 | } 35 | 36 | .text { 37 | background-color: blue; 38 | color: #ff0; 39 | height: 25px; 40 | align-self: center; 41 | flex-grow: 1; 42 | font-size: 12px; 43 | } 44 | -------------------------------------------------------------------------------- /tests/spec_bincode.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #![cfg(feature = "bincode-display-list")] 13 | #![feature(proc_macro)] 14 | 15 | extern crate bincode; 16 | extern crate rsx; 17 | #[macro_use] 18 | extern crate rsx_primitives; 19 | 20 | use std::io::Cursor; 21 | 22 | use rsx::{css, rsx}; 23 | use rsx_primitives::build::types::*; 24 | use rsx_primitives::rsx_dom::types::*; 25 | use rsx_primitives::rsx_layout::types::*; 26 | use rsx_primitives::rsx_resources::files::types::*; 27 | use rsx_primitives::rsx_resources::fonts::types::*; 28 | use rsx_primitives::rsx_resources::images::types::*; 29 | use rsx_primitives::rsx_resources::types::ResourceGroup; 30 | use rsx_primitives::rsx_shared::traits::*; 31 | use rsx_primitives::rsx_stylesheet::types::*; 32 | use rsx_primitives::types::DisplayList; 33 | 34 | #[test] 35 | fn test_json_reflow_simple_resourceless() { 36 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 37 | 38 | let mut tree = rsx! { 39 |
40 | Hello world! 41 |
42 | }; 43 | 44 | let files = FileCache::new().unwrap(); 45 | 46 | let image_keys = ImageKeysAPI::new(()); 47 | let images = ImageCache::new(image_keys).unwrap(); 48 | 49 | let font_keys = FontKeysAPI::new(()); 50 | let fonts = FontCache::new(font_keys).unwrap(); 51 | 52 | let resources = ResourceGroup::new(files, images, fonts); 53 | tree.generate_layout_tree(&resources); 54 | 55 | let width = 100.0; 56 | let height = 100.0; 57 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 58 | 59 | let display_list = DisplayList::from(&mut tree).serialize(); 60 | let serialized: Vec = display_list.into(); 61 | let mut cursor = Cursor::new(serialized); 62 | 63 | { 64 | let next: BincodeSpecificDisplayItem = bincode::deserialize_from(&mut cursor).unwrap(); 65 | assert_eq!( 66 | next, 67 | BincodeSpecificDisplayItem::Rect(BincodeRectDisplayItem { 68 | bounds: LayoutBoundingClientRect { 69 | position: LayoutClientPosition { left: 0, top: 0 }, 70 | size: LayoutClientSize { 71 | width: 0, 72 | height: 100 73 | } 74 | }, 75 | display: BincodeRectDisplayProps { 76 | color: Color { 77 | red: 0, 78 | green: 0, 79 | blue: 0, 80 | alpha: 0 81 | } 82 | } 83 | }) 84 | ); 85 | } 86 | 87 | { 88 | let next: BincodeSpecificDisplayItem = bincode::deserialize_from(&mut cursor).unwrap(); 89 | assert_eq!( 90 | next, 91 | BincodeSpecificDisplayItem::Border(BincodeBorderDisplayItem { 92 | bounds: LayoutBoundingClientRect { 93 | position: LayoutClientPosition { left: 0, top: 0 }, 94 | size: LayoutClientSize { 95 | width: 0, 96 | height: 100 97 | } 98 | }, 99 | display: BincodeBorderDisplayProps { 100 | widths: [0, 0, 0, 0], 101 | colors: [ 102 | Color { 103 | red: 0, 104 | green: 0, 105 | blue: 0, 106 | alpha: 0 107 | }, 108 | Color { 109 | red: 0, 110 | green: 0, 111 | blue: 0, 112 | alpha: 0 113 | }, 114 | Color { 115 | red: 0, 116 | green: 0, 117 | blue: 0, 118 | alpha: 0 119 | }, 120 | Color { 121 | red: 0, 122 | green: 0, 123 | blue: 0, 124 | alpha: 0 125 | } 126 | ], 127 | styles: [ 128 | BorderStyle::Solid, 129 | BorderStyle::Solid, 130 | BorderStyle::Solid, 131 | BorderStyle::Solid 132 | ] 133 | } 134 | }) 135 | ); 136 | } 137 | 138 | { 139 | let next: Option = bincode::deserialize_from(&mut cursor).ok(); 140 | assert_eq!(next.is_some(), false); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/spec_diff.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #![cfg(feature = "json-display-list")] 13 | #![feature(proc_macro)] 14 | 15 | extern crate rsx; 16 | #[macro_use] 17 | extern crate rsx_primitives; 18 | extern crate serde_json; 19 | 20 | use rsx::{css, rsx}; 21 | use rsx_primitives::build::types::*; 22 | use rsx_primitives::rsx_dom::types::*; 23 | use rsx_primitives::rsx_layout::types::*; 24 | use rsx_primitives::rsx_resources::files::types::*; 25 | use rsx_primitives::rsx_resources::fonts::types::*; 26 | use rsx_primitives::rsx_resources::images::types::*; 27 | use rsx_primitives::rsx_resources::types::ResourceGroup; 28 | use rsx_primitives::rsx_shared::traits::*; 29 | use rsx_primitives::rsx_stylesheet::types::*; 30 | use rsx_primitives::types::DisplayList; 31 | 32 | #[test] 33 | fn test_diff_json_1() { 34 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 35 | 36 | let mut tree = rsx! { 37 |
38 | Hello world! 39 |
40 | }; 41 | 42 | let mut files = FileCache::new().unwrap(); 43 | 44 | let image_path = "tests/fixtures/Quantum.png"; 45 | assert!(files.add_file(image_path).is_ok()); 46 | 47 | let image_keys = ImageKeysAPI::new(()); 48 | let mut images = ImageCache::new(image_keys).unwrap(); 49 | 50 | let image_id = ImageId::new(image_path); 51 | let image_bytes = files.get_file(image_path).unwrap(); 52 | images.add_raw(image_id, image_bytes).unwrap(); 53 | 54 | let font_path = "tests/fixtures/FreeSans.ttf"; 55 | assert!(files.add_file(font_path).is_ok()); 56 | 57 | let font_keys = FontKeysAPI::new(()); 58 | let mut fonts = FontCache::new(font_keys).unwrap(); 59 | 60 | let font_id = FontId::new("FreeSans"); 61 | let font_bytes = files.get_file(font_path).unwrap(); 62 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 63 | 64 | let resources = ResourceGroup::new(files, images, fonts); 65 | tree.generate_layout_tree(&resources); 66 | 67 | let width = 100.0; 68 | let height = 100.0; 69 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 70 | 71 | let display_list_1 = DisplayList::new(); 72 | let display_list_2 = DisplayList::from(&mut tree); 73 | let diff = display_list_1.diff(&display_list_2); 74 | let stringified: String = diff.into(); 75 | 76 | let expected = r#" 77 | [ 78 | { 79 | "AddRect": { 80 | "bounds": { 81 | "position": { 82 | "left": 0, 83 | "top": 0 84 | }, 85 | "size": { 86 | "width": 66, 87 | "height": 100 88 | } 89 | }, 90 | "display": { 91 | "color": { 92 | "red": 0, 93 | "green": 0, 94 | "blue": 0, 95 | "alpha": 0 96 | } 97 | } 98 | } 99 | }, 100 | { 101 | "AddBorder": { 102 | "bounds": { 103 | "position": { 104 | "left": 0, 105 | "top": 0 106 | }, 107 | "size": { 108 | "width": 66, 109 | "height": 100 110 | } 111 | }, 112 | "display": { 113 | "widths": [ 114 | 0, 115 | 0, 116 | 0, 117 | 0 118 | ], 119 | "colors": [ 120 | { 121 | "red": 0, 122 | "green": 0, 123 | "blue": 0, 124 | "alpha": 0 125 | }, 126 | { 127 | "red": 0, 128 | "green": 0, 129 | "blue": 0, 130 | "alpha": 0 131 | }, 132 | { 133 | "red": 0, 134 | "green": 0, 135 | "blue": 0, 136 | "alpha": 0 137 | }, 138 | { 139 | "red": 0, 140 | "green": 0, 141 | "blue": 0, 142 | "alpha": 0 143 | } 144 | ], 145 | "styles": [ 146 | "Solid", 147 | "Solid", 148 | "Solid", 149 | "Solid" 150 | ] 151 | } 152 | } 153 | }, 154 | { 155 | "AddText": { 156 | "bounds": { 157 | "position": { 158 | "left": 0, 159 | "top": 0 160 | }, 161 | "size": { 162 | "width": 66, 163 | "height": 100 164 | } 165 | }, 166 | "display": { 167 | "color": { 168 | "red": 0, 169 | "green": 0, 170 | "blue": 0, 171 | "alpha": 255 172 | }, 173 | "source_text": [ 174 | { 175 | "Static": "Hello world !" 176 | } 177 | ], 178 | "shaped_text": [ 179 | { 180 | "font_key": 0, 181 | "font_instance_key": 1, 182 | "width_64": 4224, 183 | "height_64": 1088, 184 | "glyphs": [ 185 | { 186 | "glyph_index": 43, 187 | "x_64": 0, 188 | "y_64": 768 189 | }, 190 | { 191 | "glyph_index": 72, 192 | "x_64": 554, 193 | "y_64": 768 194 | }, 195 | { 196 | "glyph_index": 79, 197 | "x_64": 981, 198 | "y_64": 768 199 | }, 200 | { 201 | "glyph_index": 79, 202 | "x_64": 1151, 203 | "y_64": 768 204 | }, 205 | { 206 | "glyph_index": 82, 207 | "x_64": 1321, 208 | "y_64": 768 209 | }, 210 | { 211 | "glyph_index": 3, 212 | "x_64": 1748, 213 | "y_64": 768 214 | }, 215 | { 216 | "glyph_index": 90, 217 | "x_64": 1962, 218 | "y_64": 768 219 | }, 220 | { 221 | "glyph_index": 82, 222 | "x_64": 2516, 223 | "y_64": 768 224 | }, 225 | { 226 | "glyph_index": 85, 227 | "x_64": 2943, 228 | "y_64": 768 229 | }, 230 | { 231 | "glyph_index": 79, 232 | "x_64": 3199, 233 | "y_64": 768 234 | }, 235 | { 236 | "glyph_index": 71, 237 | "x_64": 3369, 238 | "y_64": 768 239 | }, 240 | { 241 | "glyph_index": 3, 242 | "x_64": 3796, 243 | "y_64": 768 244 | }, 245 | { 246 | "glyph_index": 4, 247 | "x_64": 4010, 248 | "y_64": 768 249 | } 250 | ], 251 | "generation_id": 14732137242825294601 252 | } 253 | ] 254 | } 255 | } 256 | } 257 | ] 258 | "#; 259 | 260 | assert_eq!( 261 | serde_json::from_str::(&stringified).unwrap(), 262 | serde_json::from_str::(expected).unwrap() 263 | ); 264 | } 265 | 266 | #[test] 267 | fn test_diff_json_2() { 268 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 269 | 270 | let mut tree_1 = rsx! { 271 |
272 | Hello world! 273 |
274 | }; 275 | 276 | let mut tree_2 = rsx! { 277 |
278 | Hello world! 279 |
280 | }; 281 | 282 | let mut files = FileCache::new().unwrap(); 283 | 284 | let image_path = "tests/fixtures/Quantum.png"; 285 | assert!(files.add_file(image_path).is_ok()); 286 | 287 | let image_keys = ImageKeysAPI::new(()); 288 | let mut images = ImageCache::new(image_keys).unwrap(); 289 | 290 | let image_id = ImageId::new(image_path); 291 | let image_bytes = files.get_file(image_path).unwrap(); 292 | images.add_raw(image_id, image_bytes).unwrap(); 293 | 294 | let font_path = "tests/fixtures/FreeSans.ttf"; 295 | assert!(files.add_file(font_path).is_ok()); 296 | 297 | let font_keys = FontKeysAPI::new(()); 298 | let mut fonts = FontCache::new(font_keys).unwrap(); 299 | 300 | let font_id = FontId::new("FreeSans"); 301 | let font_bytes = files.get_file(font_path).unwrap(); 302 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 303 | 304 | let resources = ResourceGroup::new(files, images, fonts); 305 | tree_1.generate_layout_tree(&resources); 306 | tree_2.generate_layout_tree(&resources); 307 | 308 | let width = 100.0; 309 | let height = 100.0; 310 | tree_1.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 311 | tree_2.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 312 | 313 | let display_list_1 = DisplayList::from(&mut tree_1); 314 | let display_list_2 = DisplayList::from(&mut tree_2); 315 | let diff = display_list_1.diff(&display_list_2); 316 | let stringified: String = diff.into(); 317 | 318 | let expected = "[]"; 319 | 320 | assert_eq!( 321 | serde_json::from_str::(&stringified).unwrap(), 322 | serde_json::from_str::(expected).unwrap() 323 | ); 324 | } 325 | 326 | #[test] 327 | fn test_diff_json_3() { 328 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 329 | 330 | let mut tree_1 = rsx! { 331 |
332 | Hello world! 333 |
334 | }; 335 | 336 | let mut tree_2 = rsx! { 337 |
338 | Goodbye cruel world! 339 |
340 | }; 341 | 342 | let mut files = FileCache::new().unwrap(); 343 | 344 | let image_path = "tests/fixtures/Quantum.png"; 345 | assert!(files.add_file(image_path).is_ok()); 346 | 347 | let image_keys = ImageKeysAPI::new(()); 348 | let mut images = ImageCache::new(image_keys).unwrap(); 349 | 350 | let image_id = ImageId::new(image_path); 351 | let image_bytes = files.get_file(image_path).unwrap(); 352 | images.add_raw(image_id, image_bytes).unwrap(); 353 | 354 | let font_path = "tests/fixtures/FreeSans.ttf"; 355 | assert!(files.add_file(font_path).is_ok()); 356 | 357 | let font_keys = FontKeysAPI::new(()); 358 | let mut fonts = FontCache::new(font_keys).unwrap(); 359 | 360 | let font_id = FontId::new("FreeSans"); 361 | let font_bytes = files.get_file(font_path).unwrap(); 362 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 363 | 364 | let resources = ResourceGroup::new(files, images, fonts); 365 | tree_1.generate_layout_tree(&resources); 366 | tree_2.generate_layout_tree(&resources); 367 | 368 | let width = 1024.0; 369 | let height = 768.0; 370 | tree_1.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 371 | tree_2.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 372 | 373 | let display_list_1 = DisplayList::from(&mut tree_1); 374 | let display_list_2 = DisplayList::from(&mut tree_2); 375 | let diff = display_list_1.diff(&display_list_2); 376 | let stringified: String = diff.into(); 377 | 378 | let expected = r#" 379 | [ 380 | { 381 | "M": [ 382 | 0, 383 | [ 384 | { 385 | "Z": { 386 | "W": 122 387 | } 388 | } 389 | ] 390 | ] 391 | }, 392 | { 393 | "M": [ 394 | 1, 395 | [ 396 | { 397 | "Z": { 398 | "W": 122 399 | } 400 | } 401 | ] 402 | ] 403 | }, 404 | { 405 | "M": [ 406 | 2, 407 | [ 408 | { 409 | "Z": { 410 | "W": 122 411 | } 412 | }, 413 | { 414 | "T": { 415 | "T": "Goodbye\ncruel world !" 416 | } 417 | } 418 | ] 419 | ] 420 | } 421 | ] 422 | "#; 423 | 424 | assert_eq!( 425 | serde_json::from_str::(&stringified).unwrap(), 426 | serde_json::from_str::(expected).unwrap() 427 | ); 428 | } 429 | 430 | #[test] 431 | fn test_diff_json_4() { 432 | let mut stylesheet = css!("tests/fixtures/test_2.css"); 433 | 434 | let mut tree_1 = rsx! { 435 |
436 | Hello world! 437 |
438 | }; 439 | 440 | let mut tree_2 = rsx! { 441 |
442 | Hello world! 443 |
444 | }; 445 | 446 | let mut files = FileCache::new().unwrap(); 447 | 448 | let image_path = "tests/fixtures/Quantum.png"; 449 | assert!(files.add_file(image_path).is_ok()); 450 | 451 | let image_keys = ImageKeysAPI::new(()); 452 | let mut images = ImageCache::new(image_keys).unwrap(); 453 | 454 | let image_id = ImageId::new(image_path); 455 | let image_bytes = files.get_file(image_path).unwrap(); 456 | images.add_raw(image_id, image_bytes).unwrap(); 457 | 458 | let font_path = "tests/fixtures/FreeSans.ttf"; 459 | assert!(files.add_file(font_path).is_ok()); 460 | 461 | let font_keys = FontKeysAPI::new(()); 462 | let mut fonts = FontCache::new(font_keys).unwrap(); 463 | 464 | let font_id = FontId::new("FreeSans"); 465 | let font_bytes = files.get_file(font_path).unwrap(); 466 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 467 | 468 | let resources = ResourceGroup::new(files, images, fonts); 469 | tree_1.generate_layout_tree(&resources); 470 | tree_2.generate_layout_tree(&resources); 471 | 472 | let width = 1024.0; 473 | let height = 768.0; 474 | tree_1.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 475 | tree_2.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 476 | 477 | let display_list_1 = DisplayList::from(&mut tree_1); 478 | let display_list_2 = DisplayList::from(&mut tree_2); 479 | let diff = display_list_1.diff(&display_list_2); 480 | let stringified: String = diff.into(); 481 | 482 | let expected = r#" 483 | [ 484 | { 485 | "M": [ 486 | 2, 487 | [ 488 | { 489 | "T": { 490 | "C": [ 491 | 255, 492 | 0, 493 | 255, 494 | 255 495 | ] 496 | } 497 | } 498 | ] 499 | ] 500 | } 501 | ] 502 | "#; 503 | 504 | assert_eq!( 505 | serde_json::from_str::(&stringified).unwrap(), 506 | serde_json::from_str::(expected).unwrap() 507 | ); 508 | } 509 | 510 | #[test] 511 | fn test_diff_json_5() { 512 | let mut stylesheet = css!("tests/fixtures/test_2.css"); 513 | 514 | let mut tree_1 = rsx! { 515 |
516 | Hello world! 517 |
518 | }; 519 | 520 | let mut tree_2 = rsx! { 521 | 522 | 523 | 524 | Hello world! 525 | 526 | 527 | }; 528 | 529 | let mut files = FileCache::new().unwrap(); 530 | 531 | let image_path = "tests/fixtures/Quantum.png"; 532 | assert!(files.add_file(image_path).is_ok()); 533 | 534 | let image_keys = ImageKeysAPI::new(()); 535 | let mut images = ImageCache::new(image_keys).unwrap(); 536 | 537 | let image_id = ImageId::new(image_path); 538 | let image_bytes = files.get_file(image_path).unwrap(); 539 | images.add_raw(image_id, image_bytes).unwrap(); 540 | 541 | let font_path = "tests/fixtures/FreeSans.ttf"; 542 | assert!(files.add_file(font_path).is_ok()); 543 | 544 | let font_keys = FontKeysAPI::new(()); 545 | let mut fonts = FontCache::new(font_keys).unwrap(); 546 | 547 | let font_id = FontId::new("FreeSans"); 548 | let font_bytes = files.get_file(font_path).unwrap(); 549 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 550 | 551 | let resources = ResourceGroup::new(files, images, fonts); 552 | tree_1.generate_layout_tree(&resources); 553 | tree_2.generate_layout_tree(&resources); 554 | 555 | let width = 1024.0; 556 | let height = 768.0; 557 | tree_1.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 558 | tree_2.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 559 | 560 | let display_list_1 = DisplayList::from(&mut tree_1); 561 | let display_list_2 = DisplayList::from(&mut tree_2); 562 | let diff = display_list_1.diff(&display_list_2); 563 | let stringified: String = diff.into(); 564 | 565 | let expected = r#" 566 | [ 567 | { 568 | "M": [ 569 | 0, 570 | [ 571 | { 572 | "Z": { 573 | "W": 500 574 | } 575 | }, 576 | { 577 | "Z": { 578 | "H": 120 579 | } 580 | }, 581 | { 582 | "R": { 583 | "C": [ 584 | 255, 585 | 0, 586 | 0, 587 | 255 588 | ] 589 | } 590 | } 591 | ] 592 | ] 593 | }, 594 | { 595 | "M": [ 596 | 1, 597 | [ 598 | { 599 | "Z": { 600 | "W": 500 601 | } 602 | }, 603 | { 604 | "Z": { 605 | "H": 120 606 | } 607 | } 608 | ] 609 | ] 610 | }, 611 | { 612 | "IntoRect": [ 613 | 2, 614 | { 615 | "bounds": { 616 | "position": { 617 | "left": 20, 618 | "top": 20 619 | }, 620 | "size": { 621 | "width": 80, 622 | "height": 83 623 | } 624 | }, 625 | "display": { 626 | "color": { 627 | "red": 0, 628 | "green": 128, 629 | "blue": 0, 630 | "alpha": 255 631 | } 632 | } 633 | } 634 | ] 635 | }, 636 | { 637 | "AddBorder": { 638 | "bounds": { 639 | "position": { 640 | "left": 20, 641 | "top": 20 642 | }, 643 | "size": { 644 | "width": 80, 645 | "height": 83 646 | } 647 | }, 648 | "display": { 649 | "widths": [ 650 | 0, 651 | 0, 652 | 0, 653 | 0 654 | ], 655 | "colors": [ 656 | { 657 | "red": 0, 658 | "green": 0, 659 | "blue": 0, 660 | "alpha": 0 661 | }, 662 | { 663 | "red": 0, 664 | "green": 0, 665 | "blue": 0, 666 | "alpha": 0 667 | }, 668 | { 669 | "red": 0, 670 | "green": 0, 671 | "blue": 0, 672 | "alpha": 0 673 | }, 674 | { 675 | "red": 0, 676 | "green": 0, 677 | "blue": 0, 678 | "alpha": 0 679 | } 680 | ], 681 | "styles": [ 682 | "Solid", 683 | "Solid", 684 | "Solid", 685 | "Solid" 686 | ] 687 | } 688 | } 689 | }, 690 | { 691 | "AddImage": { 692 | "bounds": { 693 | "position": { 694 | "left": 20, 695 | "top": 20 696 | }, 697 | "size": { 698 | "width": 80, 699 | "height": 83 700 | } 701 | }, 702 | "display": { 703 | "image_src": { 704 | "Static": "tests/fixtures/Quantum.png" 705 | }, 706 | "measured_image": { 707 | "image_key": 0, 708 | "size": [ 709 | 512, 710 | 529 711 | ] 712 | } 713 | } 714 | } 715 | }, 716 | { 717 | "AddRect": { 718 | "bounds": { 719 | "position": { 720 | "left": 120, 721 | "top": 48 722 | }, 723 | "size": { 724 | "width": 360, 725 | "height": 25 726 | } 727 | }, 728 | "display": { 729 | "color": { 730 | "red": 0, 731 | "green": 0, 732 | "blue": 255, 733 | "alpha": 255 734 | } 735 | } 736 | } 737 | }, 738 | { 739 | "AddBorder": { 740 | "bounds": { 741 | "position": { 742 | "left": 120, 743 | "top": 48 744 | }, 745 | "size": { 746 | "width": 360, 747 | "height": 25 748 | } 749 | }, 750 | "display": { 751 | "widths": [ 752 | 0, 753 | 0, 754 | 0, 755 | 0 756 | ], 757 | "colors": [ 758 | { 759 | "red": 0, 760 | "green": 0, 761 | "blue": 0, 762 | "alpha": 0 763 | }, 764 | { 765 | "red": 0, 766 | "green": 0, 767 | "blue": 0, 768 | "alpha": 0 769 | }, 770 | { 771 | "red": 0, 772 | "green": 0, 773 | "blue": 0, 774 | "alpha": 0 775 | }, 776 | { 777 | "red": 0, 778 | "green": 0, 779 | "blue": 0, 780 | "alpha": 0 781 | } 782 | ], 783 | "styles": [ 784 | "Solid", 785 | "Solid", 786 | "Solid", 787 | "Solid" 788 | ] 789 | } 790 | } 791 | }, 792 | { 793 | "AddText": { 794 | "bounds": { 795 | "position": { 796 | "left": 120, 797 | "top": 48 798 | }, 799 | "size": { 800 | "width": 66, 801 | "height": 25 802 | } 803 | }, 804 | "display": { 805 | "color": { 806 | "red": 255, 807 | "green": 255, 808 | "blue": 0, 809 | "alpha": 255 810 | }, 811 | "source_text": [ 812 | { 813 | "Static": "Hello world !" 814 | } 815 | ], 816 | "shaped_text": [ 817 | { 818 | "font_key": 0, 819 | "font_instance_key": 1, 820 | "width_64": 4224, 821 | "height_64": 1088, 822 | "glyphs": [ 823 | { 824 | "glyph_index": 43, 825 | "x_64": 0, 826 | "y_64": 768 827 | }, 828 | { 829 | "glyph_index": 72, 830 | "x_64": 554, 831 | "y_64": 768 832 | }, 833 | { 834 | "glyph_index": 79, 835 | "x_64": 981, 836 | "y_64": 768 837 | }, 838 | { 839 | "glyph_index": 79, 840 | "x_64": 1151, 841 | "y_64": 768 842 | }, 843 | { 844 | "glyph_index": 82, 845 | "x_64": 1321, 846 | "y_64": 768 847 | }, 848 | { 849 | "glyph_index": 3, 850 | "x_64": 1748, 851 | "y_64": 768 852 | }, 853 | { 854 | "glyph_index": 90, 855 | "x_64": 1962, 856 | "y_64": 768 857 | }, 858 | { 859 | "glyph_index": 82, 860 | "x_64": 2516, 861 | "y_64": 768 862 | }, 863 | { 864 | "glyph_index": 85, 865 | "x_64": 2943, 866 | "y_64": 768 867 | }, 868 | { 869 | "glyph_index": 79, 870 | "x_64": 3199, 871 | "y_64": 768 872 | }, 873 | { 874 | "glyph_index": 71, 875 | "x_64": 3369, 876 | "y_64": 768 877 | }, 878 | { 879 | "glyph_index": 3, 880 | "x_64": 3796, 881 | "y_64": 768 882 | }, 883 | { 884 | "glyph_index": 4, 885 | "x_64": 4010, 886 | "y_64": 768 887 | } 888 | ], 889 | "generation_id": 14732137242825294601 890 | } 891 | ] 892 | } 893 | } 894 | } 895 | ] 896 | "#; 897 | 898 | assert_eq!( 899 | serde_json::from_str::(&stringified).unwrap(), 900 | serde_json::from_str::(expected).unwrap() 901 | ); 902 | } 903 | -------------------------------------------------------------------------------- /tests/spec_json.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Mozilla 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed 7 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 8 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | specific language governing permissions and limitations under the License. 10 | */ 11 | 12 | #![cfg(feature = "json-display-list")] 13 | #![feature(proc_macro)] 14 | 15 | extern crate rsx; 16 | #[macro_use] 17 | extern crate rsx_primitives; 18 | extern crate serde_json; 19 | 20 | use rsx::{css, rsx}; 21 | use rsx_primitives::build::types::*; 22 | use rsx_primitives::rsx_dom::types::*; 23 | use rsx_primitives::rsx_layout::types::*; 24 | use rsx_primitives::rsx_resources::files::types::*; 25 | use rsx_primitives::rsx_resources::fonts::types::*; 26 | use rsx_primitives::rsx_resources::images::types::*; 27 | use rsx_primitives::rsx_resources::types::ResourceGroup; 28 | use rsx_primitives::rsx_shared::traits::*; 29 | use rsx_primitives::rsx_stylesheet::types::*; 30 | use rsx_primitives::types::DisplayList; 31 | 32 | #[test] 33 | fn test_json_reflow_simple_resourceless() { 34 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 35 | 36 | let mut tree = rsx! { 37 |
38 | Hello world! 39 |
40 | }; 41 | 42 | let files = FileCache::new().unwrap(); 43 | 44 | let image_keys = ImageKeysAPI::new(()); 45 | let images = ImageCache::new(image_keys).unwrap(); 46 | 47 | let font_keys = FontKeysAPI::new(()); 48 | let fonts = FontCache::new(font_keys).unwrap(); 49 | 50 | let resources = ResourceGroup::new(files, images, fonts); 51 | tree.generate_layout_tree(&resources); 52 | 53 | let width = 100.0; 54 | let height = 100.0; 55 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 56 | 57 | let display_list = DisplayList::from(&mut tree).serialize(); 58 | let stringified: String = display_list.into(); 59 | 60 | let expected = r#" 61 | [ 62 | { 63 | "Rect": { 64 | "bounds": { 65 | "position": { 66 | "left": 0, 67 | "top": 0 68 | }, 69 | "size": { 70 | "width": 0, 71 | "height": 100 72 | } 73 | }, 74 | "display": { 75 | "color": { 76 | "red": 0, 77 | "green": 0, 78 | "blue": 0, 79 | "alpha": 0 80 | } 81 | } 82 | } 83 | }, 84 | { 85 | "Border": { 86 | "bounds": { 87 | "position": { 88 | "left": 0, 89 | "top": 0 90 | }, 91 | "size": { 92 | "width": 0, 93 | "height": 100 94 | } 95 | }, 96 | "display": { 97 | "widths": [ 98 | 0, 99 | 0, 100 | 0, 101 | 0 102 | ], 103 | "colors": [ 104 | { 105 | "red": 0, 106 | "green": 0, 107 | "blue": 0, 108 | "alpha": 0 109 | }, 110 | { 111 | "red": 0, 112 | "green": 0, 113 | "blue": 0, 114 | "alpha": 0 115 | }, 116 | { 117 | "red": 0, 118 | "green": 0, 119 | "blue": 0, 120 | "alpha": 0 121 | }, 122 | { 123 | "red": 0, 124 | "green": 0, 125 | "blue": 0, 126 | "alpha": 0 127 | } 128 | ], 129 | "styles": [ 130 | "Solid", 131 | "Solid", 132 | "Solid", 133 | "Solid" 134 | ] 135 | } 136 | } 137 | } 138 | ] 139 | "#; 140 | 141 | assert_eq!( 142 | serde_json::from_str::(&stringified).unwrap(), 143 | serde_json::from_str::(expected).unwrap() 144 | ); 145 | } 146 | 147 | #[test] 148 | fn test_json_reflow_simple() { 149 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 150 | 151 | let mut tree = rsx! { 152 |
153 | Hello world! 154 |
155 | }; 156 | 157 | let mut files = FileCache::new().unwrap(); 158 | 159 | let image_path = "tests/fixtures/Quantum.png"; 160 | assert!(files.add_file(image_path).is_ok()); 161 | 162 | let image_keys = ImageKeysAPI::new(()); 163 | let mut images = ImageCache::new(image_keys).unwrap(); 164 | 165 | let image_id = ImageId::new(image_path); 166 | let image_bytes = files.get_file(image_path).unwrap(); 167 | images.add_raw(image_id, image_bytes).unwrap(); 168 | 169 | let font_path = "tests/fixtures/FreeSans.ttf"; 170 | assert!(files.add_file(font_path).is_ok()); 171 | 172 | let font_keys = FontKeysAPI::new(()); 173 | let mut fonts = FontCache::new(font_keys).unwrap(); 174 | 175 | let font_id = FontId::new("FreeSans"); 176 | let font_bytes = files.get_file(font_path).unwrap(); 177 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 178 | 179 | let resources = ResourceGroup::new(files, images, fonts); 180 | tree.generate_layout_tree(&resources); 181 | 182 | let width = 100.0; 183 | let height = 100.0; 184 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 185 | 186 | let display_list = DisplayList::from(&mut tree).serialize(); 187 | let stringified: String = display_list.into(); 188 | 189 | let expected = r#" 190 | [ 191 | { 192 | "Rect": { 193 | "bounds": { 194 | "position": { 195 | "left": 0, 196 | "top": 0 197 | }, 198 | "size": { 199 | "width": 66, 200 | "height": 100 201 | } 202 | }, 203 | "display": { 204 | "color": { 205 | "red": 0, 206 | "green": 0, 207 | "blue": 0, 208 | "alpha": 0 209 | } 210 | } 211 | } 212 | }, 213 | { 214 | "Border": { 215 | "bounds": { 216 | "position": { 217 | "left": 0, 218 | "top": 0 219 | }, 220 | "size": { 221 | "width": 66, 222 | "height": 100 223 | } 224 | }, 225 | "display": { 226 | "widths": [ 227 | 0, 228 | 0, 229 | 0, 230 | 0 231 | ], 232 | "colors": [ 233 | { 234 | "red": 0, 235 | "green": 0, 236 | "blue": 0, 237 | "alpha": 0 238 | }, 239 | { 240 | "red": 0, 241 | "green": 0, 242 | "blue": 0, 243 | "alpha": 0 244 | }, 245 | { 246 | "red": 0, 247 | "green": 0, 248 | "blue": 0, 249 | "alpha": 0 250 | }, 251 | { 252 | "red": 0, 253 | "green": 0, 254 | "blue": 0, 255 | "alpha": 0 256 | } 257 | ], 258 | "styles": [ 259 | "Solid", 260 | "Solid", 261 | "Solid", 262 | "Solid" 263 | ] 264 | } 265 | } 266 | }, 267 | { 268 | "Text": { 269 | "bounds": { 270 | "position": { 271 | "left": 0, 272 | "top": 0 273 | }, 274 | "size": { 275 | "width": 66, 276 | "height": 100 277 | } 278 | }, 279 | "display": { 280 | "color": { 281 | "red": 0, 282 | "green": 0, 283 | "blue": 0, 284 | "alpha": 255 285 | }, 286 | "source_text": [ 287 | { 288 | "Static": "Hello world !" 289 | } 290 | ], 291 | "shaped_text": [ 292 | { 293 | "font_key": 0, 294 | "font_instance_key": 1, 295 | "width_64": 4224, 296 | "height_64": 1088, 297 | "glyphs": [ 298 | { 299 | "glyph_index": 43, 300 | "x_64": 0, 301 | "y_64": 768 302 | }, 303 | { 304 | "glyph_index": 72, 305 | "x_64": 554, 306 | "y_64": 768 307 | }, 308 | { 309 | "glyph_index": 79, 310 | "x_64": 981, 311 | "y_64": 768 312 | }, 313 | { 314 | "glyph_index": 79, 315 | "x_64": 1151, 316 | "y_64": 768 317 | }, 318 | { 319 | "glyph_index": 82, 320 | "x_64": 1321, 321 | "y_64": 768 322 | }, 323 | { 324 | "glyph_index": 3, 325 | "x_64": 1748, 326 | "y_64": 768 327 | }, 328 | { 329 | "glyph_index": 90, 330 | "x_64": 1962, 331 | "y_64": 768 332 | }, 333 | { 334 | "glyph_index": 82, 335 | "x_64": 2516, 336 | "y_64": 768 337 | }, 338 | { 339 | "glyph_index": 85, 340 | "x_64": 2943, 341 | "y_64": 768 342 | }, 343 | { 344 | "glyph_index": 79, 345 | "x_64": 3199, 346 | "y_64": 768 347 | }, 348 | { 349 | "glyph_index": 71, 350 | "x_64": 3369, 351 | "y_64": 768 352 | }, 353 | { 354 | "glyph_index": 3, 355 | "x_64": 3796, 356 | "y_64": 768 357 | }, 358 | { 359 | "glyph_index": 4, 360 | "x_64": 4010, 361 | "y_64": 768 362 | } 363 | ], 364 | "generation_id": 14732137242825294601 365 | } 366 | ] 367 | } 368 | } 369 | } 370 | ] 371 | "#; 372 | 373 | assert_eq!( 374 | serde_json::from_str::(&stringified).unwrap(), 375 | serde_json::from_str::(expected).unwrap() 376 | ); 377 | } 378 | 379 | #[test] 380 | fn test_json_reflow_example() { 381 | let mut stylesheet = css!("tests/fixtures/test_1.css"); 382 | 383 | let mut tree = rsx! { 384 | 385 | 386 | 387 | Hello world! 388 | 389 | 390 | }; 391 | 392 | let mut files = FileCache::new().unwrap(); 393 | 394 | let image_path = "tests/fixtures/Quantum.png"; 395 | assert!(files.add_file(image_path).is_ok()); 396 | 397 | let image_keys = ImageKeysAPI::new(()); 398 | let mut images = ImageCache::new(image_keys).unwrap(); 399 | 400 | let image_id = ImageId::new(image_path); 401 | let image_bytes = files.get_file(image_path).unwrap(); 402 | images.add_raw(image_id, image_bytes).unwrap(); 403 | 404 | let font_path = "tests/fixtures/FreeSans.ttf"; 405 | assert!(files.add_file(font_path).is_ok()); 406 | 407 | let font_keys = FontKeysAPI::new(()); 408 | let mut fonts = FontCache::new(font_keys).unwrap(); 409 | 410 | let font_id = FontId::new("FreeSans"); 411 | let font_bytes = files.get_file(font_path).unwrap(); 412 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 413 | 414 | let resources = ResourceGroup::new(files, images, fonts); 415 | tree.generate_layout_tree(&resources); 416 | 417 | let width = 1024.0; 418 | let height = 768.0; 419 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 420 | 421 | let display_list = DisplayList::from(&mut tree).serialize(); 422 | let stringified: String = display_list.into(); 423 | 424 | let expected = r#" 425 | [ 426 | { 427 | "Rect": { 428 | "bounds": { 429 | "position": { 430 | "left": 0, 431 | "top": 0 432 | }, 433 | "size": { 434 | "width": 500, 435 | "height": 120 436 | } 437 | }, 438 | "display": { 439 | "color": { 440 | "red": 0, 441 | "green": 0, 442 | "blue": 0, 443 | "alpha": 0 444 | } 445 | } 446 | } 447 | }, 448 | { 449 | "Border": { 450 | "bounds": { 451 | "position": { 452 | "left": 0, 453 | "top": 0 454 | }, 455 | "size": { 456 | "width": 500, 457 | "height": 120 458 | } 459 | }, 460 | "display": { 461 | "widths": [ 462 | 0, 463 | 0, 464 | 0, 465 | 0 466 | ], 467 | "colors": [ 468 | { 469 | "red": 0, 470 | "green": 0, 471 | "blue": 0, 472 | "alpha": 0 473 | }, 474 | { 475 | "red": 0, 476 | "green": 0, 477 | "blue": 0, 478 | "alpha": 0 479 | }, 480 | { 481 | "red": 0, 482 | "green": 0, 483 | "blue": 0, 484 | "alpha": 0 485 | }, 486 | { 487 | "red": 0, 488 | "green": 0, 489 | "blue": 0, 490 | "alpha": 0 491 | } 492 | ], 493 | "styles": [ 494 | "Solid", 495 | "Solid", 496 | "Solid", 497 | "Solid" 498 | ] 499 | } 500 | } 501 | }, 502 | { 503 | "Rect": { 504 | "bounds": { 505 | "position": { 506 | "left": 20, 507 | "top": 20 508 | }, 509 | "size": { 510 | "width": 80, 511 | "height": 83 512 | } 513 | }, 514 | "display": { 515 | "color": { 516 | "red": 0, 517 | "green": 0, 518 | "blue": 0, 519 | "alpha": 0 520 | } 521 | } 522 | } 523 | }, 524 | { 525 | "Border": { 526 | "bounds": { 527 | "position": { 528 | "left": 20, 529 | "top": 20 530 | }, 531 | "size": { 532 | "width": 80, 533 | "height": 83 534 | } 535 | }, 536 | "display": { 537 | "widths": [ 538 | 0, 539 | 0, 540 | 0, 541 | 0 542 | ], 543 | "colors": [ 544 | { 545 | "red": 0, 546 | "green": 0, 547 | "blue": 0, 548 | "alpha": 0 549 | }, 550 | { 551 | "red": 0, 552 | "green": 0, 553 | "blue": 0, 554 | "alpha": 0 555 | }, 556 | { 557 | "red": 0, 558 | "green": 0, 559 | "blue": 0, 560 | "alpha": 0 561 | }, 562 | { 563 | "red": 0, 564 | "green": 0, 565 | "blue": 0, 566 | "alpha": 0 567 | } 568 | ], 569 | "styles": [ 570 | "Solid", 571 | "Solid", 572 | "Solid", 573 | "Solid" 574 | ] 575 | } 576 | } 577 | }, 578 | { 579 | "Image": { 580 | "bounds": { 581 | "position": { 582 | "left": 20, 583 | "top": 20 584 | }, 585 | "size": { 586 | "width": 80, 587 | "height": 83 588 | } 589 | }, 590 | "display": { 591 | "image_src": { 592 | "Static": "tests/fixtures/Quantum.png" 593 | }, 594 | "measured_image": { 595 | "image_key": 0, 596 | "size": [ 597 | 512, 598 | 529 599 | ] 600 | } 601 | } 602 | } 603 | }, 604 | { 605 | "Rect": { 606 | "bounds": { 607 | "position": { 608 | "left": 120, 609 | "top": 48 610 | }, 611 | "size": { 612 | "width": 360, 613 | "height": 25 614 | } 615 | }, 616 | "display": { 617 | "color": { 618 | "red": 0, 619 | "green": 0, 620 | "blue": 0, 621 | "alpha": 0 622 | } 623 | } 624 | } 625 | }, 626 | { 627 | "Border": { 628 | "bounds": { 629 | "position": { 630 | "left": 120, 631 | "top": 48 632 | }, 633 | "size": { 634 | "width": 360, 635 | "height": 25 636 | } 637 | }, 638 | "display": { 639 | "widths": [ 640 | 0, 641 | 0, 642 | 0, 643 | 0 644 | ], 645 | "colors": [ 646 | { 647 | "red": 0, 648 | "green": 0, 649 | "blue": 0, 650 | "alpha": 0 651 | }, 652 | { 653 | "red": 0, 654 | "green": 0, 655 | "blue": 0, 656 | "alpha": 0 657 | }, 658 | { 659 | "red": 0, 660 | "green": 0, 661 | "blue": 0, 662 | "alpha": 0 663 | }, 664 | { 665 | "red": 0, 666 | "green": 0, 667 | "blue": 0, 668 | "alpha": 0 669 | } 670 | ], 671 | "styles": [ 672 | "Solid", 673 | "Solid", 674 | "Solid", 675 | "Solid" 676 | ] 677 | } 678 | } 679 | }, 680 | { 681 | "Text": { 682 | "bounds": { 683 | "position": { 684 | "left": 120, 685 | "top": 48 686 | }, 687 | "size": { 688 | "width": 66, 689 | "height": 25 690 | } 691 | }, 692 | "display": { 693 | "color": { 694 | "red": 0, 695 | "green": 0, 696 | "blue": 0, 697 | "alpha": 255 698 | }, 699 | "source_text": [ 700 | { 701 | "Static": "Hello world !" 702 | } 703 | ], 704 | "shaped_text": [ 705 | { 706 | "font_key": 0, 707 | "font_instance_key": 1, 708 | "width_64": 4224, 709 | "height_64": 1088, 710 | "glyphs": [ 711 | { 712 | "glyph_index": 43, 713 | "x_64": 0, 714 | "y_64": 768 715 | }, 716 | { 717 | "glyph_index": 72, 718 | "x_64": 554, 719 | "y_64": 768 720 | }, 721 | { 722 | "glyph_index": 79, 723 | "x_64": 981, 724 | "y_64": 768 725 | }, 726 | { 727 | "glyph_index": 79, 728 | "x_64": 1151, 729 | "y_64": 768 730 | }, 731 | { 732 | "glyph_index": 82, 733 | "x_64": 1321, 734 | "y_64": 768 735 | }, 736 | { 737 | "glyph_index": 3, 738 | "x_64": 1748, 739 | "y_64": 768 740 | }, 741 | { 742 | "glyph_index": 90, 743 | "x_64": 1962, 744 | "y_64": 768 745 | }, 746 | { 747 | "glyph_index": 82, 748 | "x_64": 2516, 749 | "y_64": 768 750 | }, 751 | { 752 | "glyph_index": 85, 753 | "x_64": 2943, 754 | "y_64": 768 755 | }, 756 | { 757 | "glyph_index": 79, 758 | "x_64": 3199, 759 | "y_64": 768 760 | }, 761 | { 762 | "glyph_index": 71, 763 | "x_64": 3369, 764 | "y_64": 768 765 | }, 766 | { 767 | "glyph_index": 3, 768 | "x_64": 3796, 769 | "y_64": 768 770 | }, 771 | { 772 | "glyph_index": 4, 773 | "x_64": 4010, 774 | "y_64": 768 775 | } 776 | ], 777 | "generation_id": 14732137242825294601 778 | } 779 | ] 780 | } 781 | } 782 | } 783 | ] 784 | "#; 785 | 786 | assert_eq!( 787 | serde_json::from_str::(&stringified).unwrap(), 788 | serde_json::from_str::(expected).unwrap() 789 | ); 790 | } 791 | 792 | #[test] 793 | fn test_json_reflow_example_complex() { 794 | let mut stylesheet = css!("tests/fixtures/test_2.css"); 795 | 796 | let mut tree = rsx! { 797 | 798 | 799 | 800 | Hello world! 801 | 802 | 803 | }; 804 | 805 | let mut files = FileCache::new().unwrap(); 806 | 807 | let image_path = "tests/fixtures/Quantum.png"; 808 | assert!(files.add_file(image_path).is_ok()); 809 | 810 | let image_keys = ImageKeysAPI::new(()); 811 | let mut images = ImageCache::new(image_keys).unwrap(); 812 | 813 | let image_id = ImageId::new(image_path); 814 | let image_bytes = files.get_file(image_path).unwrap(); 815 | images.add_raw(image_id, image_bytes).unwrap(); 816 | 817 | let font_path = "tests/fixtures/FreeSans.ttf"; 818 | assert!(files.add_file(font_path).is_ok()); 819 | 820 | let font_keys = FontKeysAPI::new(()); 821 | let mut fonts = FontCache::new(font_keys).unwrap(); 822 | 823 | let font_id = FontId::new("FreeSans"); 824 | let font_bytes = files.get_file(font_path).unwrap(); 825 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 826 | 827 | let resources = ResourceGroup::new(files, images, fonts); 828 | tree.generate_layout_tree(&resources); 829 | 830 | let width = 1024.0; 831 | let height = 768.0; 832 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 833 | 834 | let display_list = DisplayList::from(&mut tree).serialize(); 835 | let stringified: String = display_list.into(); 836 | 837 | let expected = r#" 838 | [ 839 | { 840 | "Rect": { 841 | "bounds": { 842 | "position": { 843 | "left": 0, 844 | "top": 0 845 | }, 846 | "size": { 847 | "width": 500, 848 | "height": 120 849 | } 850 | }, 851 | "display": { 852 | "color": { 853 | "red": 255, 854 | "green": 0, 855 | "blue": 0, 856 | "alpha": 255 857 | } 858 | } 859 | } 860 | }, 861 | { 862 | "Border": { 863 | "bounds": { 864 | "position": { 865 | "left": 0, 866 | "top": 0 867 | }, 868 | "size": { 869 | "width": 500, 870 | "height": 120 871 | } 872 | }, 873 | "display": { 874 | "widths": [ 875 | 0, 876 | 0, 877 | 0, 878 | 0 879 | ], 880 | "colors": [ 881 | { 882 | "red": 0, 883 | "green": 0, 884 | "blue": 0, 885 | "alpha": 0 886 | }, 887 | { 888 | "red": 0, 889 | "green": 0, 890 | "blue": 0, 891 | "alpha": 0 892 | }, 893 | { 894 | "red": 0, 895 | "green": 0, 896 | "blue": 0, 897 | "alpha": 0 898 | }, 899 | { 900 | "red": 0, 901 | "green": 0, 902 | "blue": 0, 903 | "alpha": 0 904 | } 905 | ], 906 | "styles": [ 907 | "Solid", 908 | "Solid", 909 | "Solid", 910 | "Solid" 911 | ] 912 | } 913 | } 914 | }, 915 | { 916 | "Rect": { 917 | "bounds": { 918 | "position": { 919 | "left": 20, 920 | "top": 20 921 | }, 922 | "size": { 923 | "width": 80, 924 | "height": 83 925 | } 926 | }, 927 | "display": { 928 | "color": { 929 | "red": 0, 930 | "green": 128, 931 | "blue": 0, 932 | "alpha": 255 933 | } 934 | } 935 | } 936 | }, 937 | { 938 | "Border": { 939 | "bounds": { 940 | "position": { 941 | "left": 20, 942 | "top": 20 943 | }, 944 | "size": { 945 | "width": 80, 946 | "height": 83 947 | } 948 | }, 949 | "display": { 950 | "widths": [ 951 | 0, 952 | 0, 953 | 0, 954 | 0 955 | ], 956 | "colors": [ 957 | { 958 | "red": 0, 959 | "green": 0, 960 | "blue": 0, 961 | "alpha": 0 962 | }, 963 | { 964 | "red": 0, 965 | "green": 0, 966 | "blue": 0, 967 | "alpha": 0 968 | }, 969 | { 970 | "red": 0, 971 | "green": 0, 972 | "blue": 0, 973 | "alpha": 0 974 | }, 975 | { 976 | "red": 0, 977 | "green": 0, 978 | "blue": 0, 979 | "alpha": 0 980 | } 981 | ], 982 | "styles": [ 983 | "Solid", 984 | "Solid", 985 | "Solid", 986 | "Solid" 987 | ] 988 | } 989 | } 990 | }, 991 | { 992 | "Image": { 993 | "bounds": { 994 | "position": { 995 | "left": 20, 996 | "top": 20 997 | }, 998 | "size": { 999 | "width": 80, 1000 | "height": 83 1001 | } 1002 | }, 1003 | "display": { 1004 | "image_src": { 1005 | "Static": "tests/fixtures/Quantum.png" 1006 | }, 1007 | "measured_image": { 1008 | "image_key": 0, 1009 | "size": [ 1010 | 512, 1011 | 529 1012 | ] 1013 | } 1014 | } 1015 | } 1016 | }, 1017 | { 1018 | "Rect": { 1019 | "bounds": { 1020 | "position": { 1021 | "left": 120, 1022 | "top": 48 1023 | }, 1024 | "size": { 1025 | "width": 360, 1026 | "height": 25 1027 | } 1028 | }, 1029 | "display": { 1030 | "color": { 1031 | "red": 0, 1032 | "green": 0, 1033 | "blue": 255, 1034 | "alpha": 255 1035 | } 1036 | } 1037 | } 1038 | }, 1039 | { 1040 | "Border": { 1041 | "bounds": { 1042 | "position": { 1043 | "left": 120, 1044 | "top": 48 1045 | }, 1046 | "size": { 1047 | "width": 360, 1048 | "height": 25 1049 | } 1050 | }, 1051 | "display": { 1052 | "widths": [ 1053 | 0, 1054 | 0, 1055 | 0, 1056 | 0 1057 | ], 1058 | "colors": [ 1059 | { 1060 | "red": 0, 1061 | "green": 0, 1062 | "blue": 0, 1063 | "alpha": 0 1064 | }, 1065 | { 1066 | "red": 0, 1067 | "green": 0, 1068 | "blue": 0, 1069 | "alpha": 0 1070 | }, 1071 | { 1072 | "red": 0, 1073 | "green": 0, 1074 | "blue": 0, 1075 | "alpha": 0 1076 | }, 1077 | { 1078 | "red": 0, 1079 | "green": 0, 1080 | "blue": 0, 1081 | "alpha": 0 1082 | } 1083 | ], 1084 | "styles": [ 1085 | "Solid", 1086 | "Solid", 1087 | "Solid", 1088 | "Solid" 1089 | ] 1090 | } 1091 | } 1092 | }, 1093 | { 1094 | "Text": { 1095 | "bounds": { 1096 | "position": { 1097 | "left": 120, 1098 | "top": 48 1099 | }, 1100 | "size": { 1101 | "width": 66, 1102 | "height": 25 1103 | } 1104 | }, 1105 | "display": { 1106 | "color": { 1107 | "red": 255, 1108 | "green": 255, 1109 | "blue": 0, 1110 | "alpha": 255 1111 | }, 1112 | "source_text": [ 1113 | { 1114 | "Static": "Hello world !" 1115 | } 1116 | ], 1117 | "shaped_text": [ 1118 | { 1119 | "font_key": 0, 1120 | "font_instance_key": 1, 1121 | "width_64": 4224, 1122 | "height_64": 1088, 1123 | "glyphs": [ 1124 | { 1125 | "glyph_index": 43, 1126 | "x_64": 0, 1127 | "y_64": 768 1128 | }, 1129 | { 1130 | "glyph_index": 72, 1131 | "x_64": 554, 1132 | "y_64": 768 1133 | }, 1134 | { 1135 | "glyph_index": 79, 1136 | "x_64": 981, 1137 | "y_64": 768 1138 | }, 1139 | { 1140 | "glyph_index": 79, 1141 | "x_64": 1151, 1142 | "y_64": 768 1143 | }, 1144 | { 1145 | "glyph_index": 82, 1146 | "x_64": 1321, 1147 | "y_64": 768 1148 | }, 1149 | { 1150 | "glyph_index": 3, 1151 | "x_64": 1748, 1152 | "y_64": 768 1153 | }, 1154 | { 1155 | "glyph_index": 90, 1156 | "x_64": 1962, 1157 | "y_64": 768 1158 | }, 1159 | { 1160 | "glyph_index": 82, 1161 | "x_64": 2516, 1162 | "y_64": 768 1163 | }, 1164 | { 1165 | "glyph_index": 85, 1166 | "x_64": 2943, 1167 | "y_64": 768 1168 | }, 1169 | { 1170 | "glyph_index": 79, 1171 | "x_64": 3199, 1172 | "y_64": 768 1173 | }, 1174 | { 1175 | "glyph_index": 71, 1176 | "x_64": 3369, 1177 | "y_64": 768 1178 | }, 1179 | { 1180 | "glyph_index": 3, 1181 | "x_64": 3796, 1182 | "y_64": 768 1183 | }, 1184 | { 1185 | "glyph_index": 4, 1186 | "x_64": 4010, 1187 | "y_64": 768 1188 | } 1189 | ], 1190 | "generation_id": 14732137242825294601 1191 | } 1192 | ] 1193 | } 1194 | } 1195 | } 1196 | ] 1197 | "#; 1198 | 1199 | assert_eq!( 1200 | serde_json::from_str::(&stringified).unwrap(), 1201 | serde_json::from_str::(expected).unwrap() 1202 | ); 1203 | } 1204 | 1205 | #[test] 1206 | fn test_json_reflow_example_compound_1() { 1207 | let mut stylesheet = css!("tests/fixtures/test_2.css"); 1208 | 1209 | let mut tree = rsx! { 1210 | 1211 | 1212 | 1213 | {"Hello "}{"world !"} 1214 | 1215 | 1216 | }; 1217 | 1218 | let mut files = FileCache::new().unwrap(); 1219 | 1220 | let image_path = "tests/fixtures/Quantum.png"; 1221 | assert!(files.add_file(image_path).is_ok()); 1222 | 1223 | let image_keys = ImageKeysAPI::new(()); 1224 | let mut images = ImageCache::new(image_keys).unwrap(); 1225 | 1226 | let image_id = ImageId::new(image_path); 1227 | let image_bytes = files.get_file(image_path).unwrap(); 1228 | images.add_raw(image_id, image_bytes).unwrap(); 1229 | 1230 | let font_path = "tests/fixtures/FreeSans.ttf"; 1231 | assert!(files.add_file(font_path).is_ok()); 1232 | 1233 | let font_keys = FontKeysAPI::new(()); 1234 | let mut fonts = FontCache::new(font_keys).unwrap(); 1235 | 1236 | let font_id = FontId::new("FreeSans"); 1237 | let font_bytes = files.get_file(font_path).unwrap(); 1238 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 1239 | 1240 | let resources = ResourceGroup::new(files, images, fonts); 1241 | tree.generate_layout_tree(&resources); 1242 | 1243 | let width = 1024.0; 1244 | let height = 768.0; 1245 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 1246 | 1247 | let display_list = DisplayList::from(&mut tree).serialize(); 1248 | let stringified: String = display_list.into(); 1249 | 1250 | let expected = r#" 1251 | [ 1252 | { 1253 | "Rect": { 1254 | "bounds": { 1255 | "position": { 1256 | "left": 0, 1257 | "top": 0 1258 | }, 1259 | "size": { 1260 | "width": 500, 1261 | "height": 120 1262 | } 1263 | }, 1264 | "display": { 1265 | "color": { 1266 | "red": 255, 1267 | "green": 0, 1268 | "blue": 0, 1269 | "alpha": 255 1270 | } 1271 | } 1272 | } 1273 | }, 1274 | { 1275 | "Border": { 1276 | "bounds": { 1277 | "position": { 1278 | "left": 0, 1279 | "top": 0 1280 | }, 1281 | "size": { 1282 | "width": 500, 1283 | "height": 120 1284 | } 1285 | }, 1286 | "display": { 1287 | "widths": [ 1288 | 0, 1289 | 0, 1290 | 0, 1291 | 0 1292 | ], 1293 | "colors": [ 1294 | { 1295 | "red": 0, 1296 | "green": 0, 1297 | "blue": 0, 1298 | "alpha": 0 1299 | }, 1300 | { 1301 | "red": 0, 1302 | "green": 0, 1303 | "blue": 0, 1304 | "alpha": 0 1305 | }, 1306 | { 1307 | "red": 0, 1308 | "green": 0, 1309 | "blue": 0, 1310 | "alpha": 0 1311 | }, 1312 | { 1313 | "red": 0, 1314 | "green": 0, 1315 | "blue": 0, 1316 | "alpha": 0 1317 | } 1318 | ], 1319 | "styles": [ 1320 | "Solid", 1321 | "Solid", 1322 | "Solid", 1323 | "Solid" 1324 | ] 1325 | } 1326 | } 1327 | }, 1328 | { 1329 | "Rect": { 1330 | "bounds": { 1331 | "position": { 1332 | "left": 20, 1333 | "top": 20 1334 | }, 1335 | "size": { 1336 | "width": 80, 1337 | "height": 83 1338 | } 1339 | }, 1340 | "display": { 1341 | "color": { 1342 | "red": 0, 1343 | "green": 128, 1344 | "blue": 0, 1345 | "alpha": 255 1346 | } 1347 | } 1348 | } 1349 | }, 1350 | { 1351 | "Border": { 1352 | "bounds": { 1353 | "position": { 1354 | "left": 20, 1355 | "top": 20 1356 | }, 1357 | "size": { 1358 | "width": 80, 1359 | "height": 83 1360 | } 1361 | }, 1362 | "display": { 1363 | "widths": [ 1364 | 0, 1365 | 0, 1366 | 0, 1367 | 0 1368 | ], 1369 | "colors": [ 1370 | { 1371 | "red": 0, 1372 | "green": 0, 1373 | "blue": 0, 1374 | "alpha": 0 1375 | }, 1376 | { 1377 | "red": 0, 1378 | "green": 0, 1379 | "blue": 0, 1380 | "alpha": 0 1381 | }, 1382 | { 1383 | "red": 0, 1384 | "green": 0, 1385 | "blue": 0, 1386 | "alpha": 0 1387 | }, 1388 | { 1389 | "red": 0, 1390 | "green": 0, 1391 | "blue": 0, 1392 | "alpha": 0 1393 | } 1394 | ], 1395 | "styles": [ 1396 | "Solid", 1397 | "Solid", 1398 | "Solid", 1399 | "Solid" 1400 | ] 1401 | } 1402 | } 1403 | }, 1404 | { 1405 | "Image": { 1406 | "bounds": { 1407 | "position": { 1408 | "left": 20, 1409 | "top": 20 1410 | }, 1411 | "size": { 1412 | "width": 80, 1413 | "height": 83 1414 | } 1415 | }, 1416 | "display": { 1417 | "image_src": { 1418 | "Static": "tests/fixtures/Quantum.png" 1419 | }, 1420 | "measured_image": { 1421 | "image_key": 0, 1422 | "size": [ 1423 | 512, 1424 | 529 1425 | ] 1426 | } 1427 | } 1428 | } 1429 | }, 1430 | { 1431 | "Rect": { 1432 | "bounds": { 1433 | "position": { 1434 | "left": 120, 1435 | "top": 48 1436 | }, 1437 | "size": { 1438 | "width": 360, 1439 | "height": 25 1440 | } 1441 | }, 1442 | "display": { 1443 | "color": { 1444 | "red": 0, 1445 | "green": 0, 1446 | "blue": 255, 1447 | "alpha": 255 1448 | } 1449 | } 1450 | } 1451 | }, 1452 | { 1453 | "Border": { 1454 | "bounds": { 1455 | "position": { 1456 | "left": 120, 1457 | "top": 48 1458 | }, 1459 | "size": { 1460 | "width": 360, 1461 | "height": 25 1462 | } 1463 | }, 1464 | "display": { 1465 | "widths": [ 1466 | 0, 1467 | 0, 1468 | 0, 1469 | 0 1470 | ], 1471 | "colors": [ 1472 | { 1473 | "red": 0, 1474 | "green": 0, 1475 | "blue": 0, 1476 | "alpha": 0 1477 | }, 1478 | { 1479 | "red": 0, 1480 | "green": 0, 1481 | "blue": 0, 1482 | "alpha": 0 1483 | }, 1484 | { 1485 | "red": 0, 1486 | "green": 0, 1487 | "blue": 0, 1488 | "alpha": 0 1489 | }, 1490 | { 1491 | "red": 0, 1492 | "green": 0, 1493 | "blue": 0, 1494 | "alpha": 0 1495 | } 1496 | ], 1497 | "styles": [ 1498 | "Solid", 1499 | "Solid", 1500 | "Solid", 1501 | "Solid" 1502 | ] 1503 | } 1504 | } 1505 | }, 1506 | { 1507 | "Text": { 1508 | "bounds": { 1509 | "position": { 1510 | "left": 120, 1511 | "top": 48 1512 | }, 1513 | "size": { 1514 | "width": 66, 1515 | "height": 25 1516 | } 1517 | }, 1518 | "display": { 1519 | "color": { 1520 | "red": 255, 1521 | "green": 255, 1522 | "blue": 0, 1523 | "alpha": 255 1524 | }, 1525 | "source_text": [ 1526 | { 1527 | "Static": "Hello " 1528 | }, 1529 | { 1530 | "Static": "world !" 1531 | } 1532 | ], 1533 | "shaped_text": [ 1534 | { 1535 | "font_key": 0, 1536 | "font_instance_key": 1, 1537 | "width_64": 1962, 1538 | "height_64": 1088, 1539 | "glyphs": [ 1540 | { 1541 | "glyph_index": 43, 1542 | "x_64": 0, 1543 | "y_64": 768 1544 | }, 1545 | { 1546 | "glyph_index": 72, 1547 | "x_64": 554, 1548 | "y_64": 768 1549 | }, 1550 | { 1551 | "glyph_index": 79, 1552 | "x_64": 981, 1553 | "y_64": 768 1554 | }, 1555 | { 1556 | "glyph_index": 79, 1557 | "x_64": 1151, 1558 | "y_64": 768 1559 | }, 1560 | { 1561 | "glyph_index": 82, 1562 | "x_64": 1321, 1563 | "y_64": 768 1564 | }, 1565 | { 1566 | "glyph_index": 3, 1567 | "x_64": 1748, 1568 | "y_64": 768 1569 | } 1570 | ], 1571 | "generation_id": 12598805059617695050 1572 | }, 1573 | { 1574 | "font_key": 0, 1575 | "font_instance_key": 1, 1576 | "width_64": 2262, 1577 | "height_64": 1088, 1578 | "glyphs": [ 1579 | { 1580 | "glyph_index": 90, 1581 | "x_64": 0, 1582 | "y_64": 768 1583 | }, 1584 | { 1585 | "glyph_index": 82, 1586 | "x_64": 554, 1587 | "y_64": 768 1588 | }, 1589 | { 1590 | "glyph_index": 85, 1591 | "x_64": 981, 1592 | "y_64": 768 1593 | }, 1594 | { 1595 | "glyph_index": 79, 1596 | "x_64": 1237, 1597 | "y_64": 768 1598 | }, 1599 | { 1600 | "glyph_index": 71, 1601 | "x_64": 1407, 1602 | "y_64": 768 1603 | }, 1604 | { 1605 | "glyph_index": 3, 1606 | "x_64": 1834, 1607 | "y_64": 768 1608 | }, 1609 | { 1610 | "glyph_index": 4, 1611 | "x_64": 2048, 1612 | "y_64": 768 1613 | } 1614 | ], 1615 | "generation_id": 8713082891591790693 1616 | } 1617 | ] 1618 | } 1619 | } 1620 | } 1621 | ] 1622 | "#; 1623 | 1624 | assert_eq!( 1625 | serde_json::from_str::(&stringified).unwrap(), 1626 | serde_json::from_str::(expected).unwrap() 1627 | ); 1628 | } 1629 | 1630 | #[test] 1631 | fn test_json_reflow_example_compound_2() { 1632 | let mut stylesheet = css!("tests/fixtures/test_2.css"); 1633 | 1634 | let mut tree = rsx! { 1635 | 1636 | 1637 | 1638 | {"H"}{"e"}{"l"}{"l"}{"o"}{" "}{"w"}{"o"}{"r"}{"l"}{"d"}{" "}{"!"} 1639 | 1640 | 1641 | }; 1642 | 1643 | let mut files = FileCache::new().unwrap(); 1644 | 1645 | let image_path = "tests/fixtures/Quantum.png"; 1646 | assert!(files.add_file(image_path).is_ok()); 1647 | 1648 | let image_keys = ImageKeysAPI::new(()); 1649 | let mut images = ImageCache::new(image_keys).unwrap(); 1650 | 1651 | let image_id = ImageId::new(image_path); 1652 | let image_bytes = files.get_file(image_path).unwrap(); 1653 | images.add_raw(image_id, image_bytes).unwrap(); 1654 | 1655 | let font_path = "tests/fixtures/FreeSans.ttf"; 1656 | assert!(files.add_file(font_path).is_ok()); 1657 | 1658 | let font_keys = FontKeysAPI::new(()); 1659 | let mut fonts = FontCache::new(font_keys).unwrap(); 1660 | 1661 | let font_id = FontId::new("FreeSans"); 1662 | let font_bytes = files.get_file(font_path).unwrap(); 1663 | fonts.add_raw(font_id, font_bytes, 0).unwrap(); 1664 | 1665 | let resources = ResourceGroup::new(files, images, fonts); 1666 | tree.generate_layout_tree(&resources); 1667 | 1668 | let width = 1024.0; 1669 | let height = 768.0; 1670 | tree.reflow_subtree(width as u32, height as u32, LayoutReflowDirection::LTR); 1671 | 1672 | let display_list = DisplayList::from(&mut tree).serialize(); 1673 | let stringified: String = display_list.into(); 1674 | 1675 | let expected = r#" 1676 | [ 1677 | { 1678 | "Rect": { 1679 | "bounds": { 1680 | "position": { 1681 | "left": 0, 1682 | "top": 0 1683 | }, 1684 | "size": { 1685 | "width": 500, 1686 | "height": 120 1687 | } 1688 | }, 1689 | "display": { 1690 | "color": { 1691 | "red": 255, 1692 | "green": 0, 1693 | "blue": 0, 1694 | "alpha": 255 1695 | } 1696 | } 1697 | } 1698 | }, 1699 | { 1700 | "Border": { 1701 | "bounds": { 1702 | "position": { 1703 | "left": 0, 1704 | "top": 0 1705 | }, 1706 | "size": { 1707 | "width": 500, 1708 | "height": 120 1709 | } 1710 | }, 1711 | "display": { 1712 | "widths": [ 1713 | 0, 1714 | 0, 1715 | 0, 1716 | 0 1717 | ], 1718 | "colors": [ 1719 | { 1720 | "red": 0, 1721 | "green": 0, 1722 | "blue": 0, 1723 | "alpha": 0 1724 | }, 1725 | { 1726 | "red": 0, 1727 | "green": 0, 1728 | "blue": 0, 1729 | "alpha": 0 1730 | }, 1731 | { 1732 | "red": 0, 1733 | "green": 0, 1734 | "blue": 0, 1735 | "alpha": 0 1736 | }, 1737 | { 1738 | "red": 0, 1739 | "green": 0, 1740 | "blue": 0, 1741 | "alpha": 0 1742 | } 1743 | ], 1744 | "styles": [ 1745 | "Solid", 1746 | "Solid", 1747 | "Solid", 1748 | "Solid" 1749 | ] 1750 | } 1751 | } 1752 | }, 1753 | { 1754 | "Rect": { 1755 | "bounds": { 1756 | "position": { 1757 | "left": 20, 1758 | "top": 20 1759 | }, 1760 | "size": { 1761 | "width": 80, 1762 | "height": 83 1763 | } 1764 | }, 1765 | "display": { 1766 | "color": { 1767 | "red": 0, 1768 | "green": 128, 1769 | "blue": 0, 1770 | "alpha": 255 1771 | } 1772 | } 1773 | } 1774 | }, 1775 | { 1776 | "Border": { 1777 | "bounds": { 1778 | "position": { 1779 | "left": 20, 1780 | "top": 20 1781 | }, 1782 | "size": { 1783 | "width": 80, 1784 | "height": 83 1785 | } 1786 | }, 1787 | "display": { 1788 | "widths": [ 1789 | 0, 1790 | 0, 1791 | 0, 1792 | 0 1793 | ], 1794 | "colors": [ 1795 | { 1796 | "red": 0, 1797 | "green": 0, 1798 | "blue": 0, 1799 | "alpha": 0 1800 | }, 1801 | { 1802 | "red": 0, 1803 | "green": 0, 1804 | "blue": 0, 1805 | "alpha": 0 1806 | }, 1807 | { 1808 | "red": 0, 1809 | "green": 0, 1810 | "blue": 0, 1811 | "alpha": 0 1812 | }, 1813 | { 1814 | "red": 0, 1815 | "green": 0, 1816 | "blue": 0, 1817 | "alpha": 0 1818 | } 1819 | ], 1820 | "styles": [ 1821 | "Solid", 1822 | "Solid", 1823 | "Solid", 1824 | "Solid" 1825 | ] 1826 | } 1827 | } 1828 | }, 1829 | { 1830 | "Image": { 1831 | "bounds": { 1832 | "position": { 1833 | "left": 20, 1834 | "top": 20 1835 | }, 1836 | "size": { 1837 | "width": 80, 1838 | "height": 83 1839 | } 1840 | }, 1841 | "display": { 1842 | "image_src": { 1843 | "Static": "tests/fixtures/Quantum.png" 1844 | }, 1845 | "measured_image": { 1846 | "image_key": 0, 1847 | "size": [ 1848 | 512, 1849 | 529 1850 | ] 1851 | } 1852 | } 1853 | } 1854 | }, 1855 | { 1856 | "Rect": { 1857 | "bounds": { 1858 | "position": { 1859 | "left": 120, 1860 | "top": 48 1861 | }, 1862 | "size": { 1863 | "width": 360, 1864 | "height": 25 1865 | } 1866 | }, 1867 | "display": { 1868 | "color": { 1869 | "red": 0, 1870 | "green": 0, 1871 | "blue": 255, 1872 | "alpha": 255 1873 | } 1874 | } 1875 | } 1876 | }, 1877 | { 1878 | "Border": { 1879 | "bounds": { 1880 | "position": { 1881 | "left": 120, 1882 | "top": 48 1883 | }, 1884 | "size": { 1885 | "width": 360, 1886 | "height": 25 1887 | } 1888 | }, 1889 | "display": { 1890 | "widths": [ 1891 | 0, 1892 | 0, 1893 | 0, 1894 | 0 1895 | ], 1896 | "colors": [ 1897 | { 1898 | "red": 0, 1899 | "green": 0, 1900 | "blue": 0, 1901 | "alpha": 0 1902 | }, 1903 | { 1904 | "red": 0, 1905 | "green": 0, 1906 | "blue": 0, 1907 | "alpha": 0 1908 | }, 1909 | { 1910 | "red": 0, 1911 | "green": 0, 1912 | "blue": 0, 1913 | "alpha": 0 1914 | }, 1915 | { 1916 | "red": 0, 1917 | "green": 0, 1918 | "blue": 0, 1919 | "alpha": 0 1920 | } 1921 | ], 1922 | "styles": [ 1923 | "Solid", 1924 | "Solid", 1925 | "Solid", 1926 | "Solid" 1927 | ] 1928 | } 1929 | } 1930 | }, 1931 | { 1932 | "Text": { 1933 | "bounds": { 1934 | "position": { 1935 | "left": 120, 1936 | "top": 48 1937 | }, 1938 | "size": { 1939 | "width": 65, 1940 | "height": 25 1941 | } 1942 | }, 1943 | "display": { 1944 | "color": { 1945 | "red": 255, 1946 | "green": 255, 1947 | "blue": 0, 1948 | "alpha": 255 1949 | }, 1950 | "source_text": [ 1951 | { 1952 | "Static": "H" 1953 | }, 1954 | { 1955 | "Static": "e" 1956 | }, 1957 | { 1958 | "Static": "l" 1959 | }, 1960 | { 1961 | "Static": "l" 1962 | }, 1963 | { 1964 | "Static": "o" 1965 | }, 1966 | { 1967 | "Static": " " 1968 | }, 1969 | { 1970 | "Static": "w" 1971 | }, 1972 | { 1973 | "Static": "o" 1974 | }, 1975 | { 1976 | "Static": "r" 1977 | }, 1978 | { 1979 | "Static": "l" 1980 | }, 1981 | { 1982 | "Static": "d" 1983 | }, 1984 | { 1985 | "Static": " " 1986 | }, 1987 | { 1988 | "Static": "!" 1989 | } 1990 | ], 1991 | "shaped_text": [ 1992 | { 1993 | "font_key": 0, 1994 | "font_instance_key": 1, 1995 | "width_64": 554, 1996 | "height_64": 1088, 1997 | "glyphs": [ 1998 | { 1999 | "glyph_index": 43, 2000 | "x_64": 0, 2001 | "y_64": 768 2002 | } 2003 | ], 2004 | "generation_id": 659716905384036824 2005 | }, 2006 | { 2007 | "font_key": 0, 2008 | "font_instance_key": 1, 2009 | "width_64": 427, 2010 | "height_64": 1088, 2011 | "glyphs": [ 2012 | { 2013 | "glyph_index": 72, 2014 | "x_64": 0, 2015 | "y_64": 768 2016 | } 2017 | ], 2018 | "generation_id": 616369758961961485 2019 | }, 2020 | { 2021 | "font_key": 0, 2022 | "font_instance_key": 1, 2023 | "width_64": 170, 2024 | "height_64": 1088, 2025 | "glyphs": [ 2026 | { 2027 | "glyph_index": 79, 2028 | "x_64": 0, 2029 | "y_64": 768 2030 | } 2031 | ], 2032 | "generation_id": 625376958218562972 2033 | }, 2034 | { 2035 | "font_key": 0, 2036 | "font_instance_key": 1, 2037 | "width_64": 170, 2038 | "height_64": 1088, 2039 | "glyphs": [ 2040 | { 2041 | "glyph_index": 79, 2042 | "x_64": 0, 2043 | "y_64": 768 2044 | } 2045 | ], 2046 | "generation_id": 625376958218562972 2047 | }, 2048 | { 2049 | "font_key": 0, 2050 | "font_instance_key": 1, 2051 | "width_64": 427, 2052 | "height_64": 1088, 2053 | "glyphs": [ 2054 | { 2055 | "glyph_index": 82, 2056 | "x_64": 0, 2057 | "y_64": 768 2058 | } 2059 | ], 2060 | "generation_id": 625939908172017779 2061 | }, 2062 | { 2063 | "font_key": 0, 2064 | "font_instance_key": 1, 2065 | "width_64": 214, 2066 | "height_64": 1088, 2067 | "glyphs": [ 2068 | { 2069 | "glyph_index": 3, 2070 | "x_64": 0, 2071 | "y_64": 768 2072 | } 2073 | ], 2074 | "generation_id": 560074763608722560 2075 | }, 2076 | { 2077 | "font_key": 0, 2078 | "font_instance_key": 1, 2079 | "width_64": 554, 2080 | "height_64": 1088, 2081 | "glyphs": [ 2082 | { 2083 | "glyph_index": 90, 2084 | "x_64": 0, 2085 | "y_64": 768 2086 | } 2087 | ], 2088 | "generation_id": 633821207521520427 2089 | }, 2090 | { 2091 | "font_key": 0, 2092 | "font_instance_key": 1, 2093 | "width_64": 427, 2094 | "height_64": 1088, 2095 | "glyphs": [ 2096 | { 2097 | "glyph_index": 82, 2098 | "x_64": 0, 2099 | "y_64": 768 2100 | } 2101 | ], 2102 | "generation_id": 625939908172017779 2103 | }, 2104 | { 2105 | "font_key": 0, 2106 | "font_instance_key": 1, 2107 | "width_64": 256, 2108 | "height_64": 1088, 2109 | "glyphs": [ 2110 | { 2111 | "glyph_index": 85, 2112 | "x_64": 0, 2113 | "y_64": 768 2114 | } 2115 | ], 2116 | "generation_id": 638324807149726558 2117 | }, 2118 | { 2119 | "font_key": 0, 2120 | "font_instance_key": 1, 2121 | "width_64": 170, 2122 | "height_64": 1088, 2123 | "glyphs": [ 2124 | { 2125 | "glyph_index": 79, 2126 | "x_64": 0, 2127 | "y_64": 768 2128 | } 2129 | ], 2130 | "generation_id": 625376958218562972 2131 | }, 2132 | { 2133 | "font_key": 0, 2134 | "font_instance_key": 1, 2135 | "width_64": 427, 2136 | "height_64": 1088, 2137 | "glyphs": [ 2138 | { 2139 | "glyph_index": 71, 2140 | "x_64": 0, 2141 | "y_64": 768 2142 | } 2143 | ], 2144 | "generation_id": 617495658869060324 2145 | }, 2146 | { 2147 | "font_key": 0, 2148 | "font_instance_key": 1, 2149 | "width_64": 214, 2150 | "height_64": 1088, 2151 | "glyphs": [ 2152 | { 2153 | "glyph_index": 3, 2154 | "x_64": 0, 2155 | "y_64": 768 2156 | } 2157 | ], 2158 | "generation_id": 560074763608722560 2159 | }, 2160 | { 2161 | "font_key": 0, 2162 | "font_instance_key": 1, 2163 | "width_64": 214, 2164 | "height_64": 1088, 2165 | "glyphs": [ 2166 | { 2167 | "glyph_index": 4, 2168 | "x_64": 0, 2169 | "y_64": 768 2170 | } 2171 | ], 2172 | "generation_id": 558948863701623721 2173 | } 2174 | ] 2175 | } 2176 | } 2177 | } 2178 | ] 2179 | "#; 2180 | 2181 | assert_eq!( 2182 | serde_json::from_str::(&stringified).unwrap(), 2183 | serde_json::from_str::(expected).unwrap() 2184 | ); 2185 | } 2186 | --------------------------------------------------------------------------------