├── .cargo
└── config.toml
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── assets
├── images
│ ├── juice-updated.png
│ ├── juice.png
│ ├── logo.png
│ ├── paris-30k-rendered.png
│ ├── spaceship-damage.webp
│ └── spaceship.webp
└── svgs
│ └── paris-30k.svg
├── demo
├── Cargo.toml
└── src
│ ├── demos
│ ├── circles.rs
│ ├── mod.rs
│ ├── spaceship.rs
│ ├── svg.rs
│ └── texture.rs
│ ├── main.rs
│ └── runner.rs
├── docs
├── code-of-conduct.md
└── contributing.md
├── e2e-tests
├── Cargo.toml
├── expected
│ ├── tests__blend_modes__ColorBurn__cpu.png
│ ├── tests__blend_modes__ColorDodge__cpu.png
│ ├── tests__blend_modes__Color__cpu.png
│ ├── tests__blend_modes__Darken__cpu.png
│ ├── tests__blend_modes__Difference__cpu.png
│ ├── tests__blend_modes__Exclusion__cpu.png
│ ├── tests__blend_modes__HardLight__cpu.png
│ ├── tests__blend_modes__Hue__cpu.png
│ ├── tests__blend_modes__Lighten__cpu.png
│ ├── tests__blend_modes__Luminosity__cpu.png
│ ├── tests__blend_modes__Multiply__cpu.png
│ ├── tests__blend_modes__Over__cpu.png
│ ├── tests__blend_modes__Overlay__cpu.png
│ ├── tests__blend_modes__Saturation__cpu.png
│ ├── tests__blend_modes__Screen__cpu.png
│ ├── tests__blend_modes__SoftLight__cpu.png
│ ├── tests__clipping2__cpu.png
│ ├── tests__clipping__cpu.png
│ ├── tests__covers__cpu.png
│ ├── tests__fill_rules__EvenOdd__cpu.png
│ ├── tests__fill_rules__NonZero__cpu.png
│ ├── tests__linear_gradient__cpu.png
│ ├── tests__pixel__cpu.png
│ ├── tests__radial_gradient__cpu.png
│ ├── tests__radial_gradient__gpu.png
│ ├── tests__solid_color__blue__cpu.png
│ ├── tests__solid_color__dark_blue__cpu.png
│ ├── tests__solid_color__dark_green__cpu.png
│ ├── tests__solid_color__dark_red__cpu.png
│ ├── tests__solid_color__green__cpu.png
│ ├── tests__solid_color__red__cpu.png
│ ├── tests__solid_color__transparent_black__cpu.png
│ ├── tests__texture__cpu.png
│ └── tests__texture__gpu.png
└── tests
│ ├── report.html
│ ├── test_env.rs
│ └── tests.rs
└── forma
├── Cargo.toml
└── src
├── composition
├── interner.rs
├── layer.rs
├── mod.rs
└── state.rs
├── consts.rs
├── cpu
├── buffer
│ ├── layout
│ │ ├── mod.rs
│ │ └── slice_cache.rs
│ └── mod.rs
├── channel.rs
├── mod.rs
├── painter
│ ├── layer_workbench
│ │ ├── mod.rs
│ │ └── passes
│ │ │ ├── mod.rs
│ │ │ ├── skip_fully_covered_layers.rs
│ │ │ ├── skip_trivial_clips.rs
│ │ │ └── tile_unchanged.rs
│ ├── mod.rs
│ └── styling.rs
├── pixel_segment.rs
├── rasterizer.rs
└── renderer.rs
├── gpu
├── conveyor_sort
│ ├── mod.rs
│ └── sort.wgsl
├── mod.rs
├── painter
│ ├── mod.rs
│ └── paint.wgsl
├── rasterizer
│ ├── mod.rs
│ └── rasterizer.wgsl
├── renderer
│ ├── draw_texture.wgsl
│ └── mod.rs
└── style_map.rs
├── lib.rs
├── math
├── mod.rs
├── point.rs
└── transform.rs
├── path.rs
├── segment.rs
├── styling.rs
└── utils
├── extend.rs
├── mod.rs
├── order.rs
├── prefix_scan.rs
├── simd
├── aarch64.rs
├── auto.rs
├── avx.rs
├── mod.rs
└── wasm32.rs
└── small_bit_set.rs
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | # Copyright 2022 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | [build]
16 | rustflags = ["-C", "target-cpu=native"]
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 |
3 | name: Continuous integration
4 |
5 | jobs:
6 | check:
7 | name: Check
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - uses: actions-rs/toolchain@v1
12 | with:
13 | profile: minimal
14 | toolchain: stable
15 | override: true
16 | - uses: actions-rs/cargo@v1
17 | with:
18 | command: check
19 | args: --all
20 |
21 | test:
22 | name: Test Suite
23 | runs-on: ubuntu-latest
24 | steps:
25 | - uses: actions/checkout@v2
26 | - name: install llvmpipe and lavapipe
27 | run: |
28 | sudo apt-get update -y -qq
29 | sudo add-apt-repository ppa:oibaf/graphics-drivers -y
30 | sudo apt-get update
31 | sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers
32 | - uses: actions-rs/toolchain@v1
33 | with:
34 | profile: minimal
35 | toolchain: stable
36 | override: true
37 | - uses: actions-rs/cargo@v1
38 | with:
39 | command: test
40 |
41 | fmt:
42 | name: Rustfmt
43 | runs-on: ubuntu-latest
44 | steps:
45 | - uses: actions/checkout@v2
46 | - uses: actions-rs/toolchain@v1
47 | with:
48 | profile: minimal
49 | toolchain: stable
50 | override: true
51 | - run: rustup component add rustfmt
52 | - uses: actions-rs/cargo@v1
53 | with:
54 | command: fmt
55 | args: --all -- --check
56 |
57 | clippy:
58 | name: Clippy
59 | runs-on: ubuntu-latest
60 | steps:
61 | - uses: actions/checkout@v2
62 | - uses: actions-rs/toolchain@v1
63 | with:
64 | profile: minimal
65 | toolchain: stable
66 | override: true
67 | - run: rustup component add clippy
68 | - uses: actions-rs/cargo@v1
69 | with:
70 | command: clippy
71 | args: -- -D warnings
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Cargo specific.
2 | /target
3 | Cargo.lock
4 | /web/pkg
5 |
6 | # Editors and IDEs.
7 | .idea
8 | .vscode
9 |
10 | # Misc.
11 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Unreleased
2 |
3 | * Added line-culling to `SegmentBuffer` in #29
4 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | # Copyright 2022 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | [workspace]
16 | default-members = [
17 | "e2e-tests",
18 | "forma",
19 | ]
20 | members = [
21 | "demo",
22 | "e2e-tests",
23 | "forma",
24 | ]
25 | resolver = "2"
26 |
27 | [profile.release-with-debug-info]
28 | inherits = "release"
29 | debug = true
30 |
31 | [workspace.dependencies]
32 | anyhow = "1.0.66"
33 | bytemuck = { version = "1.12.3", features = ["derive"] }
34 | pollster = "0.2.5"
35 | rand = { version = "0.8.5", features = ["small_rng"] }
36 | wgpu = "0.14.0"
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://crates.io/crates/forma-render) [](https://discord.gg/CYtcmqgh)
4 |
5 | A (thoroughly) parallelized **experimental** Rust vector-graphics renderer with both a software (CPU) and hardware (GPU)
6 | back-end having the following goals, in this order:
7 |
8 | 1. **Portability**; supporting Fuchsia, Linux, macOS, Windows, Android & iOS.
9 | 2. **Performance**; making use of compute-focused pipeline that is highly parallelized both at the instruction-level and the thread-level.
10 | 3. **Simplicity**; implementing an easy-to-understand 4-stage pipeline.
11 | 4. **Size**; minimizing the number of dependencies and focusing on vector-graphics only.
12 |
13 | It relies on Rust's SIMD auto-vectorization/intrinsics and [Rayon] to have good performance on the CPU, while using [WebGPU] ([wgpu]) to take advantage of the GPU.
14 |
15 | [Rayon]: https://github.com/rayon-rs/rayon
16 | [WebGPU]: https://github.com/gpuweb/gpuweb
17 | [wgpu]: https://wgpu.rs/
18 |
19 | ## Getting started
20 |
21 | Add the following to your `Cargo.toml` dependencies:
22 |
23 | ```toml
24 | forma = { version = "0.1.0", package = "forma-render" }
25 | ```
26 |
27 | ## 4-stage Pipeline
28 |
29 | | 1. Curve flattening | 2. Line segment rasterization | 3. Sorting | 4. Painting |
30 | |:-------------------:|:-----------------------------:|:---------------------:|:--------------------------------:|
31 | | Bézier curves | line segments | pixel segments | sorted pixel segments, old tiles |
32 | | ⬇️⬇️⬇️ | ⬇️⬇️⬇️ | ⬇️⬇️⬇️ | ⬇️⬇️⬇️ |
33 | | line segments | pixel segments | sorted pixel segments | freshly painted tiles |
34 |
35 | ## Implementation Highlights ✨
36 |
37 | Here are a few implementation highlights that make forma stand out from commonly used vector renderers.
38 |
39 |
40 | Curvature-aware flattening
41 |
42 | All higher cubic Béziers are approximated by quadratic ones, then, in parallel, flattened to line segments according to their curvature. This [technique] was developed by Raph Levien.
43 |
44 | [technique]: https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html
45 |
46 |
47 |
48 |
49 | Cheap translations and rotations
50 |
51 | Translations and rotations can be rendered without having to re-flatten the curves, all the while maintaining full quality.
52 |
53 |
54 |
55 |
56 | Parallel pixel grid intersection
57 |
58 | Line segments are transformed into pixel segments by intersecting them with the pixel grid. We developed a simple method that performs this computation in *O(1)* and which is run in parallel.
59 |
60 |
61 |
62 |
63 | Efficient sorting
64 |
65 | We ported [crumsort] to Rust and parallelized it with Rayon, delivering improved performance over its pdqsort implementation for 64-bit random data. Scattering pixel segments with a sort was inspired from Allan MacKinnon's work on [Spinel].
66 |
67 | [crumsort]: https://github.com/google/crumsort-rs
68 | [Spinel]: https://cs.opensource.google/fuchsia/fuchsia/+/main:src/graphics/lib/compute/spinel/
69 |
70 |
71 |
72 |
73 | Update only the tiles that change (currently CPU-only)
74 |
75 | We implemented a fail-fast per-tile optimizer that tries to skip the painting step entirely. A similar approach could also be tested on the GPU.
76 |
77 |
78 |
79 | | Animation as it appears on the screen | Updated tiles only |
80 | |:------------------------------------------:|:------------------------------------------------------------------------------:|
81 | |  |  |
82 |
83 | You can run the demo above with:
84 |
85 | ```sh
86 | cargo run --release -p demo -- spaceship
87 | ```
88 |
89 | ## Similar Projects
90 |
91 | forma draws heavy inspiration from the following projects:
92 |
93 | * [Spinel], with a Vulkan 1.2 back-end
94 | * [vello], with a wgpu back-end
95 |
96 | [vello]: https://github.com/linebender/vello
97 |
98 | ## Example
99 |
100 | You can use the included `demo` example to render a few examples, one of which is a non-compliant & incomplete SVG renderer:
101 |
102 | ```sh
103 | cargo run --release -p demo -- svg assets/svgs/paris-30k.svg
104 | ```
105 |
106 | It renders enormous SVGs at interactive framerates, even on CPU: ([compare to your web browser])
107 |
108 | [compare to your web browser]: assets/svgs/paris-30k.svg?raw=true
109 |
110 | 
111 |
112 | ## (Currently) Missing Pieces 🧩
113 |
114 | Since this project is work-in-progress, breakage in the API, while not drastic, is expected. The performance on the GPU back-end is also expected to improve especially on mobile where performance is known to be poor and where the CPU back-end is currently advised instead.
115 |
116 | Other than that:
117 |
118 | * Automated layer ordering
119 | * Strokes
120 | * More color spaces for blends & gradients
121 | * Faster GPU sorter
122 | * Use of `f16` for great mobile GPU performance
123 |
124 | ## Note
125 |
126 | This is not an officially supported Google product.
127 |
--------------------------------------------------------------------------------
/assets/images/juice-updated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/assets/images/juice-updated.png
--------------------------------------------------------------------------------
/assets/images/juice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/assets/images/juice.png
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/assets/images/logo.png
--------------------------------------------------------------------------------
/assets/images/paris-30k-rendered.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/assets/images/paris-30k-rendered.png
--------------------------------------------------------------------------------
/assets/images/spaceship-damage.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/assets/images/spaceship-damage.webp
--------------------------------------------------------------------------------
/assets/images/spaceship.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/assets/images/spaceship.webp
--------------------------------------------------------------------------------
/demo/Cargo.toml:
--------------------------------------------------------------------------------
1 | # Copyright 2022 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | [package]
16 | name = "demo"
17 | version = "0.1.0"
18 | edition = "2021"
19 |
20 | [dependencies]
21 | bytemuck.workspace = true
22 | clap = { version = "3.1", features = ["derive"] }
23 | forma = { path = "../forma", features = ["gpu"], package = "forma-render"}
24 | image = "0.23"
25 | nalgebra = "0.31.4"
26 | pollster.workspace = true
27 | rand.workspace = true
28 | svg = "0.5"
29 | svgtypes = "0.4"
30 | winit = "0.27.1"
31 | wgpu.workspace = true
32 |
--------------------------------------------------------------------------------
/demo/src/demos/circles.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use std::time::Duration;
16 |
17 | use forma::prelude::*;
18 | use rand::prelude::*;
19 |
20 | use crate::{App, Keyboard};
21 |
22 | fn circle(x: f32, y: f32, radius: f32) -> Path {
23 | let weight = 2.0f32.sqrt() / 2.0;
24 |
25 | let mut builder = PathBuilder::new();
26 |
27 | builder.move_to(Point::new(x + radius, y));
28 | builder.rat_quad_to(
29 | Point::new(x + radius, y - radius),
30 | Point::new(x, y - radius),
31 | weight,
32 | );
33 | builder.rat_quad_to(
34 | Point::new(x - radius, y - radius),
35 | Point::new(x - radius, y),
36 | weight,
37 | );
38 | builder.rat_quad_to(
39 | Point::new(x - radius, y + radius),
40 | Point::new(x, y + radius),
41 | weight,
42 | );
43 | builder.rat_quad_to(
44 | Point::new(x + radius, y + radius),
45 | Point::new(x + radius, y),
46 | weight,
47 | );
48 |
49 | builder.build()
50 | }
51 |
52 | pub struct Circles {
53 | count: usize,
54 | width: usize,
55 | height: usize,
56 | needs_composition: bool,
57 | }
58 |
59 | impl Circles {
60 | pub fn new(count: usize) -> Self {
61 | Self {
62 | count,
63 | width: 1000,
64 | height: 1000,
65 | needs_composition: true,
66 | }
67 | }
68 | }
69 |
70 | impl App for Circles {
71 | fn width(&self) -> usize {
72 | self.width
73 | }
74 |
75 | fn height(&self) -> usize {
76 | self.height
77 | }
78 |
79 | fn set_width(&mut self, width: usize) {
80 | if self.width == width {
81 | return;
82 | }
83 |
84 | self.width = width;
85 | self.needs_composition = true;
86 | }
87 |
88 | fn set_height(&mut self, height: usize) {
89 | if self.height == height {
90 | return;
91 | }
92 |
93 | self.height = height;
94 | self.needs_composition = true;
95 | }
96 |
97 | fn compose(&mut self, composition: &mut Composition, _: Duration, _: &Keyboard) {
98 | if !self.needs_composition {
99 | return;
100 | }
101 |
102 | let radius_range = 10.0..50.0;
103 |
104 | let mut rng = StdRng::seed_from_u64(42);
105 |
106 | for order in 0..self.count {
107 | let color = Color {
108 | r: rng.gen(),
109 | g: rng.gen(),
110 | b: rng.gen(),
111 | a: 0.2,
112 | };
113 |
114 | composition
115 | .get_mut_or_insert_default(Order::new(order as u32).unwrap())
116 | .clear()
117 | .insert(&circle(
118 | rng.gen_range(0.0..App::width(self) as f32),
119 | rng.gen_range(0.0..App::height(self) as f32),
120 | rng.gen_range(radius_range.clone()),
121 | ))
122 | .set_props(Props {
123 | fill_rule: FillRule::NonZero,
124 | func: Func::Draw(Style {
125 | fill: Fill::Solid(color),
126 | ..Default::default()
127 | }),
128 | });
129 | }
130 |
131 | self.needs_composition = false;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/demo/src/demos/mod.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | mod circles;
16 | mod spaceship;
17 | mod svg;
18 | mod texture;
19 |
20 | pub use self::svg::Svg;
21 | pub use circles::Circles;
22 | pub use spaceship::Spaceship;
23 | pub use texture::Texture;
24 |
--------------------------------------------------------------------------------
/demo/src/demos/texture.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use std::{
16 | path::{self, PathBuf},
17 | time::Duration,
18 | };
19 |
20 | use forma::{prelude::*, styling};
21 | use image::GenericImageView;
22 |
23 | use crate::{App, Keyboard};
24 |
25 | #[derive(Debug)]
26 | pub struct Texture {
27 | width: usize,
28 | height: usize,
29 | time: Duration,
30 | image: Image,
31 | }
32 |
33 | impl Texture {
34 | pub fn new() -> Self {
35 | Self {
36 | width: 1000,
37 | height: 1000,
38 | time: Duration::ZERO,
39 | image: load_image(&PathBuf::from("assets/images/logo.png")),
40 | }
41 | }
42 |
43 | fn transform(&self, t: f32) -> AffineTransform {
44 | let scale = 1.5 - t.cos();
45 | let mut t = AffineTransform {
46 | ux: t.cos() * scale,
47 | uy: t.sin() * scale,
48 | vx: -t.sin() * scale,
49 | vy: t.cos() * scale,
50 | tx: 0.0,
51 | ty: 0.0,
52 | };
53 |
54 | let (x, y) = (self.width as f32 * 0.5, self.height as f32 * 0.5);
55 | (t.tx, t.ty) = (
56 | self.image.width() as f32 * 0.5 - t.ux * x - t.vx * y,
57 | self.image.height() as f32 * 0.5 - t.uy * x - t.vy * y,
58 | );
59 | t
60 | }
61 | }
62 |
63 | impl Default for Texture {
64 | fn default() -> Self {
65 | Self::new()
66 | }
67 | }
68 |
69 | fn load_image(file_path: &path::Path) -> Image {
70 | let img = image::io::Reader::open(file_path)
71 | .expect("Unable to open file")
72 | .decode()
73 | .expect("Unable to decode file");
74 |
75 | let data: Vec<_> = img
76 | .to_rgb8()
77 | .pixels()
78 | .map(|p| [p.0[0], p.0[1], p.0[2], 255])
79 | .collect();
80 | Image::from_srgba(&data[..], img.width() as usize, img.height() as usize).unwrap()
81 | }
82 |
83 | impl App for Texture {
84 | fn width(&self) -> usize {
85 | self.width
86 | }
87 |
88 | fn height(&self) -> usize {
89 | self.height
90 | }
91 |
92 | fn set_width(&mut self, width: usize) {
93 | self.width = width;
94 | }
95 |
96 | fn set_height(&mut self, height: usize) {
97 | self.height = height;
98 | }
99 |
100 | fn compose(&mut self, composition: &mut Composition, elapsed: Duration, _: &Keyboard) {
101 | const PAD: f32 = 32.0;
102 |
103 | let (w, h) = (self.width as f32, self.height as f32);
104 | let layer = composition
105 | .get_mut_or_insert_default(Order::new(0).unwrap())
106 | .clear()
107 | .insert(
108 | &PathBuilder::new()
109 | .move_to(Point { x: PAD, y: PAD })
110 | .line_to(Point { x: w - PAD, y: PAD })
111 | .line_to(Point {
112 | x: w - PAD,
113 | y: h - PAD,
114 | })
115 | .line_to(Point { x: PAD, y: h - PAD })
116 | .build(),
117 | );
118 |
119 | self.time += elapsed;
120 |
121 | layer.set_props(Props {
122 | fill_rule: FillRule::NonZero,
123 | func: Func::Draw(Style {
124 | fill: Fill::Texture(styling::Texture {
125 | transform: self.transform(self.time.as_secs_f32()),
126 | image: self.image.clone(),
127 | }),
128 | ..Default::default()
129 | }),
130 | });
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/demo/src/main.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use std::{
16 | collections::HashSet,
17 | fmt,
18 | path::PathBuf,
19 | str::FromStr,
20 | time::{Duration, Instant},
21 | };
22 |
23 | use clap::{Parser, Subcommand};
24 | use forma::prelude::*;
25 | use runner::{CpuRunner, GpuRunner};
26 | use winit::{
27 | event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
28 | event_loop::{ControlFlow, EventLoop},
29 | };
30 |
31 | pub mod demos;
32 | mod runner;
33 |
34 | enum Device {
35 | Cpu,
36 | GpuLowPower,
37 | GpuHighPerformance,
38 | }
39 |
40 | impl fmt::Display for Device {
41 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 | match self {
43 | Device::Cpu => write!(f, "cpu"),
44 | Device::GpuLowPower => write!(f, "gpu low power"),
45 | Device::GpuHighPerformance => write!(f, "gpu high performance"),
46 | }
47 | }
48 | }
49 |
50 | impl FromStr for Device {
51 | type Err = &'static str;
52 |
53 | fn from_str(s: &str) -> Result {
54 | match s.to_lowercase().as_str() {
55 | "c" | "cpu" => Ok(Device::Cpu),
56 | "l" | "gpu-low" => Ok(Device::GpuLowPower),
57 | "h" | "gpu-high" => Ok(Device::GpuHighPerformance),
58 | _ => Err("must be c|cpu or l|gpu-low or h|gpu-high"),
59 | }
60 | }
61 | }
62 |
63 | #[derive(Parser)]
64 | #[clap(about = "forma demo with multiple modes")]
65 | struct Demo {
66 | /// Device to run the demo on
67 | #[clap(default_value = "cpu")]
68 | device: Device,
69 | #[clap(subcommand)]
70 | mode: Mode,
71 | }
72 |
73 | #[derive(Subcommand)]
74 | enum Mode {
75 | /// Renders random circles
76 | Circles {
77 | /// Amount of circles to draw
78 | #[clap(default_value = "100")]
79 | count: usize,
80 | },
81 | /// Renders an SVG
82 | Svg {
83 | /// .svg input file
84 | #[clap(parse(from_os_str))]
85 | file: PathBuf,
86 | /// Scale of the SVG
87 | #[clap(short, long, default_value = "1.0")]
88 | scale: f32,
89 | },
90 | /// Renders a spaceship game
91 | Spaceship,
92 | /// Renders a rotating texture
93 | Texture,
94 | }
95 |
96 | trait App {
97 | fn width(&self) -> usize;
98 | fn height(&self) -> usize;
99 | fn set_width(&mut self, width: usize);
100 | fn set_height(&mut self, height: usize);
101 | fn compose(&mut self, composition: &mut Composition, elapsed: Duration, keyboard: &Keyboard);
102 | }
103 |
104 | trait Runner {
105 | fn resize(&mut self, width: u32, height: u32);
106 | fn render(&mut self, app: &mut dyn App, elapsed: Duration, keyboard: &Keyboard);
107 | }
108 |
109 | struct Keyboard {
110 | pressed: HashSet,
111 | }
112 |
113 | impl Keyboard {
114 | fn new() -> Self {
115 | Self {
116 | pressed: HashSet::new(),
117 | }
118 | }
119 |
120 | fn is_key_down(&self, key: VirtualKeyCode) -> bool {
121 | self.pressed.contains(&key)
122 | }
123 |
124 | fn on_keyboard_input(&mut self, input: winit::event::KeyboardInput) {
125 | if let Some(code) = input.virtual_keycode {
126 | match input.state {
127 | ElementState::Pressed => self.pressed.insert(code),
128 | ElementState::Released => self.pressed.remove(&code),
129 | };
130 | }
131 | }
132 | }
133 |
134 | pub fn to_linear(rgb: [u8; 3]) -> Color {
135 | fn conv(l: u8) -> f32 {
136 | let l = f32::from(l) * 255.0f32.recip();
137 |
138 | if l <= 0.04045 {
139 | l * 12.92f32.recip()
140 | } else {
141 | ((l + 0.055) * 1.055f32.recip()).powf(2.4)
142 | }
143 | }
144 |
145 | Color {
146 | r: conv(rgb[0]),
147 | g: conv(rgb[1]),
148 | b: conv(rgb[2]),
149 | a: 1.0,
150 | }
151 | }
152 |
153 | fn main() {
154 | let opts = Demo::parse();
155 |
156 | let mut app: Box = match opts.mode {
157 | Mode::Circles { count } => Box::new(demos::Circles::new(count)),
158 | Mode::Svg { file, scale } => Box::new(demos::Svg::new(file, scale)),
159 | Mode::Spaceship => Box::new(demos::Spaceship::new()),
160 | Mode::Texture {} => Box::new(demos::Texture::new()),
161 | };
162 |
163 | let width = app.width();
164 | let height = app.height();
165 |
166 | let event_loop = EventLoop::new();
167 | let mut runner: Box = match opts.device {
168 | Device::Cpu => Box::new(CpuRunner::new(&event_loop, width as u32, height as u32)),
169 | Device::GpuLowPower => Box::new(GpuRunner::new(
170 | &event_loop,
171 | width as u32,
172 | height as u32,
173 | wgpu::PowerPreference::LowPower,
174 | )),
175 | Device::GpuHighPerformance => Box::new(GpuRunner::new(
176 | &event_loop,
177 | width as u32,
178 | height as u32,
179 | wgpu::PowerPreference::HighPerformance,
180 | )),
181 | };
182 |
183 | let mut instant = Instant::now();
184 | let mut keyboard = Keyboard::new();
185 | event_loop.run(move |event, _, control_flow| {
186 | *control_flow = ControlFlow::Poll;
187 |
188 | match event {
189 | Event::WindowEvent {
190 | event:
191 | WindowEvent::CloseRequested
192 | | WindowEvent::KeyboardInput {
193 | input:
194 | KeyboardInput {
195 | virtual_keycode: Some(VirtualKeyCode::Escape),
196 | ..
197 | },
198 | ..
199 | },
200 | ..
201 | } => {
202 | *control_flow = ControlFlow::Exit;
203 | }
204 | Event::WindowEvent {
205 | event: WindowEvent::KeyboardInput { input, .. },
206 | ..
207 | } => {
208 | keyboard.on_keyboard_input(input);
209 | }
210 | Event::WindowEvent {
211 | event:
212 | WindowEvent::Resized(size)
213 | | WindowEvent::ScaleFactorChanged {
214 | new_inner_size: &mut size,
215 | ..
216 | },
217 | ..
218 | } => {
219 | runner.resize(size.width, size.height);
220 |
221 | app.set_width(size.width as usize);
222 | app.set_height(size.height as usize);
223 | }
224 | Event::MainEventsCleared => {
225 | let elapsed = instant.elapsed();
226 | instant = Instant::now();
227 |
228 | runner.render(&mut *app, elapsed, &keyboard);
229 | }
230 | _ => (),
231 | }
232 | });
233 | }
234 |
--------------------------------------------------------------------------------
/docs/code-of-conduct.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of
9 | experience, education, socio-economic status, nationality, personal appearance,
10 | race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or reject
41 | comments, commits, code, wiki edits, issues, and other contributions that are
42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any
43 | contributor for other behaviors that they deem inappropriate, threatening,
44 | offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | This Code of Conduct also applies outside the project spaces when the Project
56 | Steward has a reasonable belief that an individual's behavior may have a
57 | negative impact on the project or its community.
58 |
59 | ## Conflict Resolution
60 |
61 | We do not believe that all conflict is bad; healthy debate and disagreement
62 | often yield positive results. However, it is never okay to be disrespectful or
63 | to engage in behavior that violates the project’s code of conduct.
64 |
65 | If you see someone violating the code of conduct, you are encouraged to address
66 | the behavior directly with those involved. Many issues can be resolved quickly
67 | and easily, and this gives people more control over the outcome of their
68 | dispute. If you are unable to resolve the matter for any reason, or if the
69 | behavior is threatening or harassing, report it. We are dedicated to providing
70 | an environment where participants feel welcome and safe.
71 |
72 | Reports should be directed to Dragoș Tiselice , the
73 | Project Steward(s) for crumsort-rs. It is the Project Steward’s duty to
74 | receive and address reported violations of the code of conduct. They will then
75 | work with a committee consisting of representatives from the Open Source
76 | Programs Office and the Google Open Source Strategy team. If for any reason you
77 | are uncomfortable reaching out to the Project Steward, please email
78 | opensource@google.com.
79 |
80 | We will investigate every complaint, but you may not receive a direct response.
81 | We will use our discretion in determining when and how to follow up on reported
82 | incidents, which may range from not taking action to permanent expulsion from
83 | the project and project-sponsored spaces. We will notify the accused of the
84 | report and provide them an opportunity to discuss it before any action is taken.
85 | The identity of the reporter will be omitted from the details of the report
86 | supplied to the accused. In potentially harmful situations, such as ongoing
87 | harassment or threats to anyone's safety, we may take action without notice.
88 |
89 | ## Attribution
90 |
91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
92 | available at
93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
94 |
--------------------------------------------------------------------------------
/docs/contributing.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code Reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google/conduct/).
29 |
--------------------------------------------------------------------------------
/e2e-tests/Cargo.toml:
--------------------------------------------------------------------------------
1 | # Copyright 2022 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | [package]
16 | name = "e2e-tests"
17 | version = "0.1.0"
18 | edition = "2021"
19 |
20 | [dependencies]
21 | anyhow.workspace = true
22 | base64 = "0.13.0"
23 | forma = { path = "../forma", features = ["gpu"], package = "forma-render"}
24 | image = "0.23"
25 | pollster = "0.2"
26 | once_cell = "1.12.0"
27 | serde = { version = "1.0", features = ["derive"] }
28 | serde_json = "1.0.81"
29 | wgpu.workspace = true
30 |
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__ColorBurn__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__ColorBurn__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__ColorDodge__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__ColorDodge__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Color__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Color__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Darken__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Darken__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Difference__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Difference__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Exclusion__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Exclusion__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__HardLight__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__HardLight__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Hue__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Hue__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Lighten__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Lighten__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Luminosity__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Luminosity__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Multiply__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Multiply__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Over__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Over__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Overlay__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Overlay__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Saturation__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Saturation__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__Screen__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__Screen__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__blend_modes__SoftLight__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__blend_modes__SoftLight__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__clipping2__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__clipping2__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__clipping__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__clipping__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__covers__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__covers__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__fill_rules__EvenOdd__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__fill_rules__EvenOdd__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__fill_rules__NonZero__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__fill_rules__NonZero__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__linear_gradient__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__linear_gradient__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__pixel__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__pixel__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__radial_gradient__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__radial_gradient__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__radial_gradient__gpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__radial_gradient__gpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__blue__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__blue__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__dark_blue__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__dark_blue__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__dark_green__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__dark_green__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__dark_red__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__dark_red__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__green__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__green__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__red__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__red__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__solid_color__transparent_black__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__solid_color__transparent_black__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__texture__cpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__texture__cpu.png
--------------------------------------------------------------------------------
/e2e-tests/expected/tests__texture__gpu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/forma/681e8bfd348caa61aab47437e7d857764c2ce522/e2e-tests/expected/tests__texture__gpu.png
--------------------------------------------------------------------------------
/e2e-tests/tests/report.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Forma test report
6 |
114 |
115 |
116 |
117 |
118 |