From 73cc20768cd27a01e49d7c0b19cc0a0a4e45b4d7 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 12 May 2020 16:35:49 -0700 Subject: [PATCH] remove pathfinder code its not ready yet so it shouldnt be on master :) --- .vscode/launch.json | 56 - Cargo.toml | 12 +- crates/bevy_pathfinder/Cargo.toml | 24 - crates/bevy_pathfinder/src/device.rs | 1274 - crates/bevy_pathfinder/src/lib.rs | 39 - crates/bevy_pathfinder/src/pathfinder_node.rs | 98 - crates/bevy_render/src/render_graph/graph.rs | 1 - .../bevy_render/src/render_graph/node_slot.rs | 2 +- .../src/render_graph/nodes/pass_node.rs | 2 +- .../nodes/window_swapchain_node.rs | 1 - .../render_graph/nodes/window_texture_node.rs | 1 - .../bevy_render/src/render_graph/schedule.rs | 1 - crates/pathfinder/Cargo.toml | 70 - crates/pathfinder/LICENSE-APACHE | 201 - crates/pathfinder/LICENSE-MIT | 25 - crates/pathfinder/README.md | 109 - crates/pathfinder/canvas/Cargo.toml | 44 - crates/pathfinder/canvas/src/lib.rs | 959 - crates/pathfinder/canvas/src/tests.rs | 19 - crates/pathfinder/canvas/src/text.rs | 507 - crates/pathfinder/color/Cargo.toml | 15 - crates/pathfinder/color/src/lib.rs | 272 - crates/pathfinder/content/Cargo.toml | 40 - crates/pathfinder/content/src/clip.rs | 555 - crates/pathfinder/content/src/dash.rs | 134 - crates/pathfinder/content/src/dilation.rs | 125 - crates/pathfinder/content/src/effects.rs | 205 - crates/pathfinder/content/src/fill.rs | 17 - crates/pathfinder/content/src/gradient.rs | 205 - crates/pathfinder/content/src/lib.rs | 35 - crates/pathfinder/content/src/orientation.rs | 43 - crates/pathfinder/content/src/outline.rs | 927 - crates/pathfinder/content/src/pattern.rs | 226 - .../pathfinder/content/src/render_target.rs | 17 - crates/pathfinder/content/src/segment.rs | 426 - .../pathfinder/content/src/sorted_vector.rs | 100 - crates/pathfinder/content/src/stroke.rs | 428 - crates/pathfinder/content/src/transform.rs | 106 - crates/pathfinder/content/src/util.rs | 47 - crates/pathfinder/export/Cargo.toml | 12 - crates/pathfinder/export/src/lib.rs | 194 - crates/pathfinder/export/src/pdf.rs | 260 - crates/pathfinder/geometry/Cargo.toml | 21 - crates/pathfinder/geometry/src/alignment.rs | 19 - crates/pathfinder/geometry/src/angle.rs | 19 - crates/pathfinder/geometry/src/lib.rs | 21 - .../pathfinder/geometry/src/line_segment.rs | 309 - crates/pathfinder/geometry/src/rect.rs | 371 - crates/pathfinder/geometry/src/transform2d.rs | 345 - crates/pathfinder/geometry/src/transform3d.rs | 504 - crates/pathfinder/geometry/src/unit_vector.rs | 47 - crates/pathfinder/geometry/src/util.rs | 39 - crates/pathfinder/geometry/src/vector.rs | 687 - crates/pathfinder/gpu/Cargo.toml | 37 - crates/pathfinder/gpu/src/lib.rs | 605 - crates/pathfinder/lottie/Cargo.toml | 12 - crates/pathfinder/lottie/src/lib.rs | 313 - crates/pathfinder/renderer/Cargo.toml | 58 - crates/pathfinder/renderer/src/allocator.rs | 327 - crates/pathfinder/renderer/src/builder.rs | 884 - .../renderer/src/concurrent/executor.rs | 31 - .../pathfinder/renderer/src/concurrent/mod.rs | 15 - .../renderer/src/concurrent/rayon.rs | 23 - .../renderer/src/concurrent/scene_proxy.rs | 151 - crates/pathfinder/renderer/src/gpu/debug.rs | 217 - crates/pathfinder/renderer/src/gpu/mod.rs | 17 - crates/pathfinder/renderer/src/gpu/options.rs | 58 - .../pathfinder/renderer/src/gpu/renderer.rs | 2040 -- crates/pathfinder/renderer/src/gpu/shaders.rs | 648 - crates/pathfinder/renderer/src/gpu_data.rs | 277 - crates/pathfinder/renderer/src/lib.rs | 29 - crates/pathfinder/renderer/src/options.rs | 153 - crates/pathfinder/renderer/src/paint.rs | 702 - crates/pathfinder/renderer/src/scene.rs | 419 - crates/pathfinder/renderer/src/tile_map.rs | 70 - crates/pathfinder/renderer/src/tiles.rs | 701 - crates/pathfinder/renderer/src/z_buffer.rs | 123 - crates/pathfinder/resources/Cargo.toml | 11 - crates/pathfinder/resources/MANIFEST | 106 - crates/pathfinder/resources/build.rs | 51 - .../resources/debug-fonts/regular.json | 105 - .../pathfinder/resources/fonts/LICENSE-APACHE | 202 - crates/pathfinder/resources/fonts/LICENSE-SIL | 49 - .../resources/fonts/NotoEmoji-Regular.ttf | Bin 418804 -> 0 bytes .../resources/fonts/Overpass-Regular.otf | Bin 69728 -> 0 bytes .../resources/fonts/Roboto-Bold.ttf | Bin 170348 -> 0 bytes .../resources/fonts/Roboto-Regular.ttf | Bin 171272 -> 0 bytes crates/pathfinder/resources/shaders/README.md | 7 - .../resources/shaders/gl3/blit.fs.glsl | 15 - .../resources/shaders/gl3/blit.vs.glsl | 14 - .../resources/shaders/gl3/clear.fs.glsl | 12 - .../resources/shaders/gl3/clear.vs.glsl | 14 - .../resources/shaders/gl3/debug_solid.fs.glsl | 12 - .../resources/shaders/gl3/debug_solid.vs.glsl | 13 - .../shaders/gl3/debug_texture.fs.glsl | 16 - .../shaders/gl3/debug_texture.vs.glsl | 17 - .../resources/shaders/gl3/demo_ground.fs.glsl | 24 - .../resources/shaders/gl3/demo_ground.vs.glsl | 15 - .../resources/shaders/gl3/fill.fs.glsl | 32 - .../resources/shaders/gl3/fill.vs.glsl | 54 - .../resources/shaders/gl3/reproject.fs.glsl | 17 - .../resources/shaders/gl3/reproject.vs.glsl | 15 - .../resources/shaders/gl3/stencil.fs.glsl | 11 - .../resources/shaders/gl3/stencil.vs.glsl | 11 - .../resources/shaders/gl3/tile.fs.glsl | 571 - .../resources/shaders/gl3/tile.vs.glsl | 41 - .../resources/shaders/gl3/tile_clip.fs.glsl | 15 - .../resources/shaders/gl3/tile_clip.vs.glsl | 20 - .../resources/shaders/gl3/tile_copy.fs.glsl | 15 - .../resources/shaders/gl3/tile_copy.vs.glsl | 14 - .../resources/shaders/gl4/blit.fs.glsl | 15 - .../resources/shaders/gl4/blit.vs.glsl | 14 - .../resources/shaders/gl4/clear.fs.glsl | 12 - .../resources/shaders/gl4/clear.vs.glsl | 14 - .../resources/shaders/gl4/debug_solid.fs.glsl | 12 - .../resources/shaders/gl4/debug_solid.vs.glsl | 13 - .../shaders/gl4/debug_texture.fs.glsl | 16 - .../shaders/gl4/debug_texture.vs.glsl | 17 - .../resources/shaders/gl4/demo_ground.fs.glsl | 24 - .../resources/shaders/gl4/demo_ground.vs.glsl | 15 - .../resources/shaders/gl4/fill.cs.glsl | 65 - .../resources/shaders/gl4/fill.fs.glsl | 32 - .../resources/shaders/gl4/fill.vs.glsl | 54 - .../resources/shaders/gl4/reproject.fs.glsl | 17 - .../resources/shaders/gl4/reproject.vs.glsl | 15 - .../resources/shaders/gl4/stencil.fs.glsl | 11 - .../resources/shaders/gl4/stencil.vs.glsl | 11 - .../resources/shaders/gl4/tile.fs.glsl | 571 - .../resources/shaders/gl4/tile.vs.glsl | 41 - .../resources/shaders/gl4/tile_clip.fs.glsl | 15 - .../resources/shaders/gl4/tile_clip.vs.glsl | 20 - .../resources/shaders/gl4/tile_copy.fs.glsl | 15 - .../resources/shaders/gl4/tile_copy.vs.glsl | 14 - .../resources/shaders/gl4/tile_fill.cs.glsl | 782 - .../resources/shaders/gl4/tile_fill.fs.glsl | 708 - .../resources/shaders/gl4/tile_fill.vs.glsl | 58 - .../resources/shaders/metal/blit.fs.metal | 24 - .../resources/shaders/metal/blit.vs.metal | 27 - .../resources/shaders/metal/clear.fs.metal | 23 - .../resources/shaders/metal/clear.vs.metal | 34 - .../shaders/metal/debug_solid.fs.metal | 23 - .../shaders/metal/debug_solid.vs.metal | 29 - .../shaders/metal/debug_texture.fs.metal | 29 - .../shaders/metal/debug_texture.vs.metal | 37 - .../shaders/metal/demo_ground.fs.metal | 43 - .../shaders/metal/demo_ground.vs.metal | 35 - .../resources/shaders/metal/fill.cs.metal | 70 - .../resources/shaders/metal/fill.fs.metal | 42 - .../resources/shaders/metal/fill.vs.metal | 78 - .../shaders/metal/reproject.fs.metal | 30 - .../shaders/metal/reproject.vs.metal | 32 - .../resources/shaders/metal/stencil.fs.metal | 18 - .../resources/shaders/metal/stencil.vs.metal | 23 - .../resources/shaders/metal/tile.fs.metal | 639 - .../resources/shaders/metal/tile.vs.metal | 63 - .../shaders/metal/tile_clip.fs.metal | 24 - .../shaders/metal/tile_clip.vs.metal | 32 - .../shaders/metal/tile_copy.fs.metal | 24 - .../shaders/metal/tile_copy.vs.metal | 34 - .../resources/shaders/vulkan/blit.fs.spv | Bin 1040 -> 0 bytes .../resources/shaders/vulkan/blit.vs.spv | Bin 1140 -> 0 bytes .../resources/shaders/vulkan/clear.fs.spv | Bin 792 -> 0 bytes .../resources/shaders/vulkan/clear.vs.spv | Bin 1656 -> 0 bytes .../shaders/vulkan/debug_solid.fs.spv | Bin 792 -> 0 bytes .../shaders/vulkan/debug_solid.vs.spv | Bin 1324 -> 0 bytes .../shaders/vulkan/debug_texture.fs.spv | Bin 1324 -> 0 bytes .../shaders/vulkan/debug_texture.vs.spv | Bin 1720 -> 0 bytes .../shaders/vulkan/demo_ground.fs.spv | Bin 1248 -> 0 bytes .../shaders/vulkan/demo_ground.vs.spv | Bin 1572 -> 0 bytes .../resources/shaders/vulkan/fill.cs.spv | Bin 7048 -> 0 bytes .../resources/shaders/vulkan/fill.fs.spv | Bin 3084 -> 0 bytes .../resources/shaders/vulkan/fill.vs.spv | Bin 4468 -> 0 bytes .../resources/shaders/vulkan/reproject.fs.spv | Bin 1512 -> 0 bytes .../resources/shaders/vulkan/reproject.vs.spv | Bin 1332 -> 0 bytes .../resources/shaders/vulkan/stencil.fs.spv | Bin 352 -> 0 bytes .../resources/shaders/vulkan/stencil.vs.spv | Bin 828 -> 0 bytes .../resources/shaders/vulkan/tile.fs.spv | Bin 34884 -> 0 bytes .../resources/shaders/vulkan/tile.vs.spv | Bin 4728 -> 0 bytes .../resources/shaders/vulkan/tile_clip.fs.spv | Bin 968 -> 0 bytes .../resources/shaders/vulkan/tile_clip.vs.spv | Bin 1668 -> 0 bytes .../resources/shaders/vulkan/tile_copy.fs.spv | Bin 1080 -> 0 bytes .../resources/shaders/vulkan/tile_copy.vs.spv | Bin 1464 -> 0 bytes crates/pathfinder/resources/src/embedded.rs | 34 - crates/pathfinder/resources/src/fs.rs | 62 - crates/pathfinder/resources/src/lib.rs | 29 - .../resources/svg/Ghostscript_Tiger.svg | 142 - .../resources/svg/julius-caesar-with-bg.svg | 10599 ------- .../resources/svg/julius-caesar.svg | 1478 - .../svg/magicleap-quickstart-p03.svg | 2685 -- .../resources/svg/material_design_icons.svg | 2862 -- .../pathfinder/resources/svg/nba-notext.svg | 77 - crates/pathfinder/resources/svg/paper.svg | 25021 ---------------- .../svg/pathfinder-magicleap-demo.svg | 4106 --- .../resources/svg/pathfinder_logo.svg | 115 - .../pathfinder/resources/swf/tiger-flat.swf | Bin 25311 -> 0 bytes crates/pathfinder/resources/swf/tiger.swf | Bin 18971 -> 0 bytes .../resources/textures/area-lut.png | Bin 35027 -> 0 bytes .../resources/textures/debug-corner-fill.png | Bin 263 -> 0 bytes .../textures/debug-corner-outline.png | Bin 397 -> 0 bytes .../resources/textures/debug-font.png | Bin 11814 -> 0 bytes .../resources/textures/demo-background.png | Bin 954 -> 0 bytes .../resources/textures/demo-effects.png | Bin 420 -> 0 bytes .../resources/textures/demo-open.png | Bin 218 -> 0 bytes .../resources/textures/demo-rotate.png | Bin 528 -> 0 bytes .../resources/textures/demo-screenshot.png | Bin 414 -> 0 bytes .../textures/demo-zoom-actual-size.png | Bin 729 -> 0 bytes .../resources/textures/demo-zoom-in.png | Bin 434 -> 0 bytes .../resources/textures/demo-zoom-out.png | Bin 429 -> 0 bytes .../resources/textures/example-nanovg.png | Bin 398461 -> 0 bytes .../resources/textures/gamma-lut.png | Bin 338 -> 0 bytes .../resources/textures/pathfinder-logo.png | Bin 45642 -> 0 bytes crates/pathfinder/shaders/Makefile | 98 - crates/pathfinder/shaders/blit.fs.glsl | 29 - crates/pathfinder/shaders/blit.vs.glsl | 30 - .../shaders/build/metal/blit.fs.spv | Bin 1040 -> 0 bytes .../shaders/build/metal/blit.vs.spv | Bin 1244 -> 0 bytes .../shaders/build/metal/clear.fs.spv | Bin 792 -> 0 bytes .../shaders/build/metal/clear.vs.spv | Bin 1656 -> 0 bytes .../shaders/build/metal/debug_solid.fs.spv | Bin 792 -> 0 bytes .../shaders/build/metal/debug_solid.vs.spv | Bin 1324 -> 0 bytes .../shaders/build/metal/debug_texture.fs.spv | Bin 1324 -> 0 bytes .../shaders/build/metal/debug_texture.vs.spv | Bin 1720 -> 0 bytes .../shaders/build/metal/demo_ground.fs.spv | Bin 1248 -> 0 bytes .../shaders/build/metal/demo_ground.vs.spv | Bin 1572 -> 0 bytes .../shaders/build/metal/fill.cs.spv | Bin 7048 -> 0 bytes .../shaders/build/metal/fill.fs.spv | Bin 3084 -> 0 bytes .../shaders/build/metal/fill.vs.spv | Bin 4552 -> 0 bytes .../shaders/build/metal/reproject.fs.spv | Bin 1512 -> 0 bytes .../shaders/build/metal/reproject.vs.spv | Bin 1436 -> 0 bytes .../shaders/build/metal/stencil.fs.spv | Bin 352 -> 0 bytes .../shaders/build/metal/stencil.vs.spv | Bin 828 -> 0 bytes .../shaders/build/metal/tile.fs.spv | Bin 34884 -> 0 bytes .../shaders/build/metal/tile.vs.spv | Bin 4728 -> 0 bytes .../shaders/build/metal/tile_clip.fs.spv | Bin 968 -> 0 bytes .../shaders/build/metal/tile_clip.vs.spv | Bin 1668 -> 0 bytes .../shaders/build/metal/tile_copy.fs.spv | Bin 1080 -> 0 bytes .../shaders/build/metal/tile_copy.vs.spv | Bin 1464 -> 0 bytes crates/pathfinder/shaders/clear.fs.glsl | 27 - crates/pathfinder/shaders/clear.vs.glsl | 31 - crates/pathfinder/shaders/debug_solid.fs.glsl | 27 - crates/pathfinder/shaders/debug_solid.vs.glsl | 28 - .../pathfinder/shaders/debug_texture.fs.glsl | 32 - .../pathfinder/shaders/debug_texture.vs.glsl | 35 - crates/pathfinder/shaders/demo_ground.fs.glsl | 33 - crates/pathfinder/shaders/demo_ground.vs.glsl | 33 - crates/pathfinder/shaders/fill.cs.glsl | 72 - crates/pathfinder/shaders/fill.fs.glsl | 33 - crates/pathfinder/shaders/fill.inc.glsl | 27 - crates/pathfinder/shaders/fill.vs.glsl | 72 - crates/pathfinder/shaders/reproject.fs.glsl | 33 - crates/pathfinder/shaders/reproject.vs.glsl | 36 - crates/pathfinder/shaders/stencil.fs.glsl | 24 - crates/pathfinder/shaders/stencil.vs.glsl | 23 - crates/pathfinder/shaders/tile.fs.glsl | 627 - crates/pathfinder/shaders/tile.vs.glsl | 63 - crates/pathfinder/shaders/tile_clip.fs.glsl | 29 - crates/pathfinder/shaders/tile_clip.vs.glsl | 33 - crates/pathfinder/shaders/tile_copy.fs.glsl | 30 - crates/pathfinder/shaders/tile_copy.vs.glsl | 31 - crates/pathfinder/simd/Cargo.toml | 18 - crates/pathfinder/simd/build.rs | 26 - crates/pathfinder/simd/src/arm/mod.rs | 849 - .../pathfinder/simd/src/arm/swizzle_f32x4.rs | 1805 -- .../pathfinder/simd/src/arm/swizzle_i32x4.rs | 1805 -- crates/pathfinder/simd/src/extras.rs | 260 - crates/pathfinder/simd/src/lib.rs | 41 - crates/pathfinder/simd/src/scalar/mod.rs | 895 - .../simd/src/scalar/swizzle_f32x4.rs | 1805 -- .../simd/src/scalar/swizzle_i32x4.rs | 1805 -- crates/pathfinder/simd/src/test.rs | 691 - crates/pathfinder/simd/src/x86/mod.rs | 952 - .../pathfinder/simd/src/x86/swizzle_f32x4.rs | 1298 - .../pathfinder/simd/src/x86/swizzle_i32x4.rs | 2578 -- .../site/assets/fonts/D-DIN-Bold.woff2 | Bin 22860 -> 0 bytes .../site/assets/fonts/D-DIN-Italic.woff2 | Bin 25460 -> 0 bytes .../pathfinder/site/assets/fonts/D-DIN.woff2 | Bin 22692 -> 0 bytes .../assets/fonts/SIL Open Font License.txt | 51 - .../site/assets/svg/pathfinder_logo_bw.svg | 192 - crates/pathfinder/site/index.html | 74 - crates/pathfinder/site/package.json | 5 - crates/pathfinder/svg/Cargo.toml | 35 - crates/pathfinder/svg/src/lib.rs | 557 - crates/pathfinder/swf/Cargo.toml | 27 - crates/pathfinder/swf/src/lib.rs | 201 - crates/pathfinder/swf/src/shapes.rs | 609 - crates/pathfinder/swf/src/timeline.rs | 42 - crates/pathfinder/text/Cargo.toml | 27 - crates/pathfinder/text/src/lib.rs | 249 - crates/pathfinder/ui/Cargo.toml | 38 - crates/pathfinder/ui/src/lib.rs | 837 - crates/pathfinder/utils/area-lut/Cargo.toml | 9 - crates/pathfinder/utils/area-lut/src/main.rs | 102 - crates/pathfinder/utils/convert/Cargo.toml | 12 - crates/pathfinder/utils/convert/src/main.rs | 27 - crates/pathfinder/utils/gamma-lut/Cargo.toml | 9 - .../utils/gamma-lut/src/gamma_lut.rs | 355 - crates/pathfinder/utils/gamma-lut/src/main.rs | 61 - .../pathfinder/utils/svg-to-skia/Cargo.toml | 8 - .../pathfinder/utils/svg-to-skia/src/main.rs | 87 - examples/2d/pathfinder.rs | 29 - src/add_default_plugins.rs | 3 - src/lib.rs | 2 - 302 files changed, 3 insertions(+), 90838 deletions(-) delete mode 100644 crates/bevy_pathfinder/Cargo.toml delete mode 100644 crates/bevy_pathfinder/src/device.rs delete mode 100644 crates/bevy_pathfinder/src/lib.rs delete mode 100644 crates/bevy_pathfinder/src/pathfinder_node.rs delete mode 100644 crates/pathfinder/Cargo.toml delete mode 100644 crates/pathfinder/LICENSE-APACHE delete mode 100644 crates/pathfinder/LICENSE-MIT delete mode 100644 crates/pathfinder/README.md delete mode 100644 crates/pathfinder/canvas/Cargo.toml delete mode 100644 crates/pathfinder/canvas/src/lib.rs delete mode 100644 crates/pathfinder/canvas/src/tests.rs delete mode 100644 crates/pathfinder/canvas/src/text.rs delete mode 100644 crates/pathfinder/color/Cargo.toml delete mode 100644 crates/pathfinder/color/src/lib.rs delete mode 100644 crates/pathfinder/content/Cargo.toml delete mode 100644 crates/pathfinder/content/src/clip.rs delete mode 100644 crates/pathfinder/content/src/dash.rs delete mode 100644 crates/pathfinder/content/src/dilation.rs delete mode 100644 crates/pathfinder/content/src/effects.rs delete mode 100644 crates/pathfinder/content/src/fill.rs delete mode 100644 crates/pathfinder/content/src/gradient.rs delete mode 100644 crates/pathfinder/content/src/lib.rs delete mode 100644 crates/pathfinder/content/src/orientation.rs delete mode 100644 crates/pathfinder/content/src/outline.rs delete mode 100644 crates/pathfinder/content/src/pattern.rs delete mode 100644 crates/pathfinder/content/src/render_target.rs delete mode 100644 crates/pathfinder/content/src/segment.rs delete mode 100644 crates/pathfinder/content/src/sorted_vector.rs delete mode 100644 crates/pathfinder/content/src/stroke.rs delete mode 100644 crates/pathfinder/content/src/transform.rs delete mode 100644 crates/pathfinder/content/src/util.rs delete mode 100644 crates/pathfinder/export/Cargo.toml delete mode 100644 crates/pathfinder/export/src/lib.rs delete mode 100644 crates/pathfinder/export/src/pdf.rs delete mode 100644 crates/pathfinder/geometry/Cargo.toml delete mode 100644 crates/pathfinder/geometry/src/alignment.rs delete mode 100644 crates/pathfinder/geometry/src/angle.rs delete mode 100644 crates/pathfinder/geometry/src/lib.rs delete mode 100644 crates/pathfinder/geometry/src/line_segment.rs delete mode 100644 crates/pathfinder/geometry/src/rect.rs delete mode 100644 crates/pathfinder/geometry/src/transform2d.rs delete mode 100644 crates/pathfinder/geometry/src/transform3d.rs delete mode 100644 crates/pathfinder/geometry/src/unit_vector.rs delete mode 100644 crates/pathfinder/geometry/src/util.rs delete mode 100644 crates/pathfinder/geometry/src/vector.rs delete mode 100644 crates/pathfinder/gpu/Cargo.toml delete mode 100644 crates/pathfinder/gpu/src/lib.rs delete mode 100644 crates/pathfinder/lottie/Cargo.toml delete mode 100644 crates/pathfinder/lottie/src/lib.rs delete mode 100644 crates/pathfinder/renderer/Cargo.toml delete mode 100644 crates/pathfinder/renderer/src/allocator.rs delete mode 100644 crates/pathfinder/renderer/src/builder.rs delete mode 100644 crates/pathfinder/renderer/src/concurrent/executor.rs delete mode 100644 crates/pathfinder/renderer/src/concurrent/mod.rs delete mode 100644 crates/pathfinder/renderer/src/concurrent/rayon.rs delete mode 100644 crates/pathfinder/renderer/src/concurrent/scene_proxy.rs delete mode 100644 crates/pathfinder/renderer/src/gpu/debug.rs delete mode 100644 crates/pathfinder/renderer/src/gpu/mod.rs delete mode 100644 crates/pathfinder/renderer/src/gpu/options.rs delete mode 100644 crates/pathfinder/renderer/src/gpu/renderer.rs delete mode 100644 crates/pathfinder/renderer/src/gpu/shaders.rs delete mode 100644 crates/pathfinder/renderer/src/gpu_data.rs delete mode 100644 crates/pathfinder/renderer/src/lib.rs delete mode 100644 crates/pathfinder/renderer/src/options.rs delete mode 100644 crates/pathfinder/renderer/src/paint.rs delete mode 100644 crates/pathfinder/renderer/src/scene.rs delete mode 100644 crates/pathfinder/renderer/src/tile_map.rs delete mode 100644 crates/pathfinder/renderer/src/tiles.rs delete mode 100644 crates/pathfinder/renderer/src/z_buffer.rs delete mode 100644 crates/pathfinder/resources/Cargo.toml delete mode 100644 crates/pathfinder/resources/MANIFEST delete mode 100644 crates/pathfinder/resources/build.rs delete mode 100644 crates/pathfinder/resources/debug-fonts/regular.json delete mode 100644 crates/pathfinder/resources/fonts/LICENSE-APACHE delete mode 100644 crates/pathfinder/resources/fonts/LICENSE-SIL delete mode 100644 crates/pathfinder/resources/fonts/NotoEmoji-Regular.ttf delete mode 100644 crates/pathfinder/resources/fonts/Overpass-Regular.otf delete mode 100644 crates/pathfinder/resources/fonts/Roboto-Bold.ttf delete mode 100644 crates/pathfinder/resources/fonts/Roboto-Regular.ttf delete mode 100644 crates/pathfinder/resources/shaders/README.md delete mode 100644 crates/pathfinder/resources/shaders/gl3/blit.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/blit.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/clear.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/clear.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/debug_solid.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/debug_solid.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/debug_texture.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/debug_texture.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/demo_ground.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/demo_ground.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/fill.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/fill.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/reproject.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/reproject.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/stencil.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/stencil.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/tile.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/tile.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/tile_clip.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/tile_clip.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/tile_copy.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl3/tile_copy.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/blit.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/blit.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/clear.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/clear.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/debug_solid.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/debug_solid.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/debug_texture.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/debug_texture.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/demo_ground.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/demo_ground.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/fill.cs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/fill.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/fill.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/reproject.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/reproject.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/stencil.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/stencil.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_clip.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_clip.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_copy.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_copy.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_fill.cs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_fill.fs.glsl delete mode 100644 crates/pathfinder/resources/shaders/gl4/tile_fill.vs.glsl delete mode 100644 crates/pathfinder/resources/shaders/metal/blit.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/blit.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/clear.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/clear.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/debug_solid.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/debug_solid.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/debug_texture.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/debug_texture.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/demo_ground.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/demo_ground.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/fill.cs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/fill.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/fill.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/reproject.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/reproject.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/stencil.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/stencil.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/tile.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/tile.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/tile_clip.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/tile_clip.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/tile_copy.fs.metal delete mode 100644 crates/pathfinder/resources/shaders/metal/tile_copy.vs.metal delete mode 100644 crates/pathfinder/resources/shaders/vulkan/blit.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/blit.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/clear.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/clear.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/debug_solid.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/debug_solid.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/debug_texture.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/debug_texture.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/demo_ground.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/demo_ground.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/fill.cs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/fill.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/fill.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/reproject.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/reproject.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/stencil.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/stencil.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/tile.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/tile.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/tile_clip.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/tile_clip.vs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/tile_copy.fs.spv delete mode 100644 crates/pathfinder/resources/shaders/vulkan/tile_copy.vs.spv delete mode 100644 crates/pathfinder/resources/src/embedded.rs delete mode 100644 crates/pathfinder/resources/src/fs.rs delete mode 100644 crates/pathfinder/resources/src/lib.rs delete mode 100644 crates/pathfinder/resources/svg/Ghostscript_Tiger.svg delete mode 100644 crates/pathfinder/resources/svg/julius-caesar-with-bg.svg delete mode 100644 crates/pathfinder/resources/svg/julius-caesar.svg delete mode 100644 crates/pathfinder/resources/svg/magicleap-quickstart-p03.svg delete mode 100644 crates/pathfinder/resources/svg/material_design_icons.svg delete mode 100644 crates/pathfinder/resources/svg/nba-notext.svg delete mode 100644 crates/pathfinder/resources/svg/paper.svg delete mode 100644 crates/pathfinder/resources/svg/pathfinder-magicleap-demo.svg delete mode 100644 crates/pathfinder/resources/svg/pathfinder_logo.svg delete mode 100644 crates/pathfinder/resources/swf/tiger-flat.swf delete mode 100644 crates/pathfinder/resources/swf/tiger.swf delete mode 100644 crates/pathfinder/resources/textures/area-lut.png delete mode 100644 crates/pathfinder/resources/textures/debug-corner-fill.png delete mode 100644 crates/pathfinder/resources/textures/debug-corner-outline.png delete mode 100644 crates/pathfinder/resources/textures/debug-font.png delete mode 100644 crates/pathfinder/resources/textures/demo-background.png delete mode 100644 crates/pathfinder/resources/textures/demo-effects.png delete mode 100644 crates/pathfinder/resources/textures/demo-open.png delete mode 100644 crates/pathfinder/resources/textures/demo-rotate.png delete mode 100644 crates/pathfinder/resources/textures/demo-screenshot.png delete mode 100644 crates/pathfinder/resources/textures/demo-zoom-actual-size.png delete mode 100644 crates/pathfinder/resources/textures/demo-zoom-in.png delete mode 100644 crates/pathfinder/resources/textures/demo-zoom-out.png delete mode 100644 crates/pathfinder/resources/textures/example-nanovg.png delete mode 100644 crates/pathfinder/resources/textures/gamma-lut.png delete mode 100644 crates/pathfinder/resources/textures/pathfinder-logo.png delete mode 100644 crates/pathfinder/shaders/Makefile delete mode 100644 crates/pathfinder/shaders/blit.fs.glsl delete mode 100644 crates/pathfinder/shaders/blit.vs.glsl delete mode 100644 crates/pathfinder/shaders/build/metal/blit.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/blit.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/clear.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/clear.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/debug_solid.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/debug_solid.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/debug_texture.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/debug_texture.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/demo_ground.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/demo_ground.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/fill.cs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/fill.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/fill.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/reproject.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/reproject.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/stencil.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/stencil.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/tile.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/tile.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/tile_clip.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/tile_clip.vs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/tile_copy.fs.spv delete mode 100644 crates/pathfinder/shaders/build/metal/tile_copy.vs.spv delete mode 100644 crates/pathfinder/shaders/clear.fs.glsl delete mode 100644 crates/pathfinder/shaders/clear.vs.glsl delete mode 100644 crates/pathfinder/shaders/debug_solid.fs.glsl delete mode 100644 crates/pathfinder/shaders/debug_solid.vs.glsl delete mode 100644 crates/pathfinder/shaders/debug_texture.fs.glsl delete mode 100644 crates/pathfinder/shaders/debug_texture.vs.glsl delete mode 100644 crates/pathfinder/shaders/demo_ground.fs.glsl delete mode 100644 crates/pathfinder/shaders/demo_ground.vs.glsl delete mode 100644 crates/pathfinder/shaders/fill.cs.glsl delete mode 100644 crates/pathfinder/shaders/fill.fs.glsl delete mode 100644 crates/pathfinder/shaders/fill.inc.glsl delete mode 100644 crates/pathfinder/shaders/fill.vs.glsl delete mode 100644 crates/pathfinder/shaders/reproject.fs.glsl delete mode 100644 crates/pathfinder/shaders/reproject.vs.glsl delete mode 100644 crates/pathfinder/shaders/stencil.fs.glsl delete mode 100644 crates/pathfinder/shaders/stencil.vs.glsl delete mode 100644 crates/pathfinder/shaders/tile.fs.glsl delete mode 100644 crates/pathfinder/shaders/tile.vs.glsl delete mode 100644 crates/pathfinder/shaders/tile_clip.fs.glsl delete mode 100644 crates/pathfinder/shaders/tile_clip.vs.glsl delete mode 100644 crates/pathfinder/shaders/tile_copy.fs.glsl delete mode 100644 crates/pathfinder/shaders/tile_copy.vs.glsl delete mode 100644 crates/pathfinder/simd/Cargo.toml delete mode 100644 crates/pathfinder/simd/build.rs delete mode 100644 crates/pathfinder/simd/src/arm/mod.rs delete mode 100644 crates/pathfinder/simd/src/arm/swizzle_f32x4.rs delete mode 100644 crates/pathfinder/simd/src/arm/swizzle_i32x4.rs delete mode 100644 crates/pathfinder/simd/src/extras.rs delete mode 100644 crates/pathfinder/simd/src/lib.rs delete mode 100644 crates/pathfinder/simd/src/scalar/mod.rs delete mode 100644 crates/pathfinder/simd/src/scalar/swizzle_f32x4.rs delete mode 100644 crates/pathfinder/simd/src/scalar/swizzle_i32x4.rs delete mode 100644 crates/pathfinder/simd/src/test.rs delete mode 100644 crates/pathfinder/simd/src/x86/mod.rs delete mode 100644 crates/pathfinder/simd/src/x86/swizzle_f32x4.rs delete mode 100644 crates/pathfinder/simd/src/x86/swizzle_i32x4.rs delete mode 100644 crates/pathfinder/site/assets/fonts/D-DIN-Bold.woff2 delete mode 100644 crates/pathfinder/site/assets/fonts/D-DIN-Italic.woff2 delete mode 100644 crates/pathfinder/site/assets/fonts/D-DIN.woff2 delete mode 100644 crates/pathfinder/site/assets/fonts/SIL Open Font License.txt delete mode 100644 crates/pathfinder/site/assets/svg/pathfinder_logo_bw.svg delete mode 100644 crates/pathfinder/site/index.html delete mode 100644 crates/pathfinder/site/package.json delete mode 100644 crates/pathfinder/svg/Cargo.toml delete mode 100644 crates/pathfinder/svg/src/lib.rs delete mode 100644 crates/pathfinder/swf/Cargo.toml delete mode 100644 crates/pathfinder/swf/src/lib.rs delete mode 100644 crates/pathfinder/swf/src/shapes.rs delete mode 100644 crates/pathfinder/swf/src/timeline.rs delete mode 100644 crates/pathfinder/text/Cargo.toml delete mode 100644 crates/pathfinder/text/src/lib.rs delete mode 100644 crates/pathfinder/ui/Cargo.toml delete mode 100644 crates/pathfinder/ui/src/lib.rs delete mode 100644 crates/pathfinder/utils/area-lut/Cargo.toml delete mode 100644 crates/pathfinder/utils/area-lut/src/main.rs delete mode 100644 crates/pathfinder/utils/convert/Cargo.toml delete mode 100644 crates/pathfinder/utils/convert/src/main.rs delete mode 100644 crates/pathfinder/utils/gamma-lut/Cargo.toml delete mode 100644 crates/pathfinder/utils/gamma-lut/src/gamma_lut.rs delete mode 100644 crates/pathfinder/utils/gamma-lut/src/main.rs delete mode 100644 crates/pathfinder/utils/svg-to-skia/Cargo.toml delete mode 100644 crates/pathfinder/utils/svg-to-skia/src/main.rs delete mode 100644 examples/2d/pathfinder.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index ecf8958b14..4ba6f16f5e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -819,25 +819,6 @@ "args": [], "cwd": "${workspaceFolder}" }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in library 'bevy_pathfinder'", - "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=bevy_pathfinder" - ], - "filter": { - "name": "bevy_pathfinder", - "kind": "lib" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, { "type": "lldb", "request": "launch", @@ -989,43 +970,6 @@ "args": [], "cwd": "${workspaceFolder}" }, - { - "type": "lldb", - "request": "launch", - "name": "Debug example 'pathfinder'", - "cargo": { - "args": [ - "build", - "--example=pathfinder", - "--package=bevy" - ], - "filter": { - "name": "pathfinder", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in example 'pathfinder'", - "cargo": { - "args": [ - "test", - "--no-run", - "--example=pathfinder", - "--package=bevy" - ], - "filter": { - "name": "pathfinder", - "kind": "example" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, { "type": "lldb", "request": "launch", diff --git a/Cargo.toml b/Cargo.toml index caaba26720..ec42aca9e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,13 @@ edition = "2018" [features] default = ["headless", "wgpu", "winit"] -headless = ["asset", "core", "derive", "diagnostic", "gltf", "input", "pathfinder", "pbr", "render", "serialization", "transform", "ui", "window"] +headless = ["asset", "core", "derive", "diagnostic", "gltf", "input", "pbr", "render", "serialization", "transform", "ui", "window"] asset = ["bevy_asset"] core = ["bevy_core"] derive = ["bevy_derive"] diagnostic = ["bevy_diagnostic"] gltf = ["bevy_gltf"] input = ["bevy_input"] -pathfinder = ["bevy_pathfinder"] pbr = ["bevy_pbr"] render = ["bevy_render"] serialization = ["bevy_serialization"] @@ -29,10 +28,6 @@ members = [ "crates/*", "examples/app/dynamic_plugin_loading/example_plugin" ] -exclude = [ - "crates/pathfinder" -] - [dependencies] # bevy @@ -43,7 +38,6 @@ bevy_derive = { path = "crates/bevy_derive", optional = true } bevy_diagnostic = { path = "crates/bevy_diagnostic", optional = true } bevy_gltf = { path = "crates/bevy_gltf", optional = true } bevy_input = { path = "crates/bevy_input", optional = true } -bevy_pathfinder = { path = "crates/bevy_pathfinder", optional = true } bevy_pbr = { path = "crates/bevy_pbr", optional = true } bevy_render = { path = "crates/bevy_render", optional = true } bevy_serialization = { path = "crates/bevy_serialization", optional = true } @@ -71,10 +65,6 @@ opt-level = 3 name = "hello_world" path = "examples/hello_world.rs" -[[example]] -name = "pathfinder" -path = "examples/2d/pathfinder.rs" - [[example]] name = "sprite" path = "examples/2d/sprite.rs" diff --git a/crates/bevy_pathfinder/Cargo.toml b/crates/bevy_pathfinder/Cargo.toml deleted file mode 100644 index 97bee67c6a..0000000000 --- a/crates/bevy_pathfinder/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "bevy_pathfinder" -version = "0.1.0" -authors = ["Carter Anderson "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bevy_app = { path = "../bevy_app" } -bevy_asset = { path = "../bevy_asset" } -bevy_render = { path = "../bevy_render" } -legion = { path = "../bevy_legion" } - -pathfinder_geometry = { path = "../pathfinder/geometry", features = ["shader_alignment_32_bits"] } -pathfinder_gpu = { path = "../pathfinder/gpu", features = ["shader_alignment_32_bits"] } -pathfinder_renderer = { path = "../pathfinder/renderer" } -pathfinder_simd = { path = "../pathfinder/simd" } -pathfinder_resources = { path = "../pathfinder/resources" } -pathfinder_color = { path = "../pathfinder/color" } -pathfinder_canvas = { path = "../pathfinder/canvas" } - -zerocopy = "0.3.0" -byteorder = "1.3" diff --git a/crates/bevy_pathfinder/src/device.rs b/crates/bevy_pathfinder/src/device.rs deleted file mode 100644 index 4dc7750c5d..0000000000 --- a/crates/bevy_pathfinder/src/device.rs +++ /dev/null @@ -1,1274 +0,0 @@ -use bevy_asset::{AssetStorage, Handle}; -use bevy_render::{ - pass::{ - LoadOp, PassDescriptor, RenderPass, RenderPassColorAttachmentDescriptor, - RenderPassDepthStencilAttachmentDescriptor, StoreOp, TextureAttachment, - }, - pipeline::{ - state_descriptors::{ - BlendDescriptor, BlendFactor, BlendOperation, ColorStateDescriptor, ColorWrite, - CompareFunction, DepthStencilStateDescriptor, IndexFormat, PrimitiveTopology, - RasterizationStateDescriptor, StencilOperation, StencilStateFaceDescriptor, - }, - BindType, InputStepMode, PipelineDescriptor, VertexAttributeDescriptor, - VertexBufferDescriptor, VertexFormat, - }, - render_resource::{ - BufferInfo, BufferUsage, RenderResource, RenderResourceAssignment, - RenderResourceAssignments, ResourceInfo, - }, - renderer::RenderContext, - shader::{Shader, ShaderSource, ShaderStage, ShaderStages}, - texture::{ - AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDescriptor, TextureDimension, - TextureFormat, TextureUsage, - }, - Color, -}; -use byteorder::{NativeEndian, WriteBytesExt}; -use pathfinder_canvas::vec2i; -use pathfinder_geometry::{rect::RectI, vector::Vector2I}; -use pathfinder_gpu::{ - BufferData, BufferTarget, BufferUploadMode, Device, FeatureLevel, ProgramKind, RenderState, - RenderTarget, ShaderKind, TextureData, TextureDataRef, TextureSamplingFlags, UniformData, - VertexAttrClass, VertexAttrDescriptor, VertexAttrType, -}; -use pathfinder_resources::ResourceLoader; -use std::{borrow::Cow, cell::RefCell, collections::HashMap, mem, rc::Rc}; - -pub struct BevyPathfinderDevice<'a> { - render_context: RefCell<&'a mut dyn RenderContext>, - shaders: RefCell<&'a mut AssetStorage>, - samplers: RefCell>, - main_color_texture: RenderResource, - main_depth_stencil_texture: RenderResource, - default_sampler: RenderResource, - default_uniform_texture: RenderResource, - default_buffer: RenderResource, -} - -impl<'a> BevyPathfinderDevice<'a> { - pub fn new( - render_context: &'a mut dyn RenderContext, - shaders: &'a mut AssetStorage, - main_color_texture: RenderResource, - main_depth_stencil_texture: RenderResource, - ) -> Self { - let default_sampler = render_context - .resources() - .create_sampler(&SamplerDescriptor::default()); - let default_texture = render_context - .resources() - .create_texture(TextureDescriptor { - dimension: TextureDimension::D2, - format: TextureFormat::Bgra8UnormSrgb, - mip_level_count: 1, - sample_count: 1, - size: Extent3d { - width: 32, - height: 32, - depth: 1, - }, - usage: TextureUsage::COPY_DST | TextureUsage::SAMPLED, - }); - let default_uniform_buffer = render_context.resources().create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM, - ..Default::default() - }, - &[0; 16], - ); - BevyPathfinderDevice { - render_context: RefCell::new(render_context), - shaders: RefCell::new(shaders), - samplers: RefCell::new(HashMap::new()), - main_color_texture, - main_depth_stencil_texture, - default_sampler, - default_uniform_texture: default_texture, - default_buffer: default_uniform_buffer, - } - } - - pub fn setup_uniforms( - &self, - render_state: &RenderState, - render_resource_assignments: &mut RenderResourceAssignments, - ) { - let mut uniform_buffer_data = Vec::new(); - let mut uniform_buffer_ranges = Vec::new(); - for (bevy_uniform, uniform_data) in render_state.uniforms.iter() { - let start_index = uniform_buffer_data.len(); - match *uniform_data { - UniformData::Float(value) => uniform_buffer_data - .write_f32::(value) - .unwrap(), - UniformData::IVec2(vector) => { - uniform_buffer_data - .write_i32::(vector.x()) - .unwrap(); - uniform_buffer_data - .write_i32::(vector.y()) - .unwrap(); - } - UniformData::IVec3(values) => { - uniform_buffer_data - .write_i32::(values[0]) - .unwrap(); - uniform_buffer_data - .write_i32::(values[1]) - .unwrap(); - uniform_buffer_data - .write_i32::(values[2]) - .unwrap(); - } - UniformData::Int(value) => uniform_buffer_data - .write_i32::(value) - .unwrap(), - UniformData::Mat2(matrix) => { - uniform_buffer_data - .write_f32::(matrix.x()) - .unwrap(); - uniform_buffer_data - .write_f32::(matrix.y()) - .unwrap(); - uniform_buffer_data - .write_f32::(matrix.z()) - .unwrap(); - uniform_buffer_data - .write_f32::(matrix.w()) - .unwrap(); - } - UniformData::Mat4(matrix) => { - for column in &matrix { - uniform_buffer_data - .write_f32::(column.x()) - .unwrap(); - uniform_buffer_data - .write_f32::(column.y()) - .unwrap(); - uniform_buffer_data - .write_f32::(column.z()) - .unwrap(); - uniform_buffer_data - .write_f32::(column.w()) - .unwrap(); - } - } - UniformData::Vec2(vector) => { - uniform_buffer_data - .write_f32::(vector.x()) - .unwrap(); - uniform_buffer_data - .write_f32::(vector.y()) - .unwrap(); - } - UniformData::Vec3(array) => { - uniform_buffer_data - .write_f32::(array[0]) - .unwrap(); - uniform_buffer_data - .write_f32::(array[1]) - .unwrap(); - uniform_buffer_data - .write_f32::(array[2]) - .unwrap(); - } - UniformData::Vec4(vector) => { - uniform_buffer_data - .write_f32::(vector.x()) - .unwrap(); - uniform_buffer_data - .write_f32::(vector.y()) - .unwrap(); - uniform_buffer_data - .write_f32::(vector.z()) - .unwrap(); - uniform_buffer_data - .write_f32::(vector.w()) - .unwrap(); - } - UniformData::TextureUnit(index) => { - let texture = &render_state.textures[index as usize]; - render_resource_assignments.set( - &bevy_uniform.name, - RenderResourceAssignment::Texture(texture.handle), - ); - let sampler_resource = - if let Some(sampler_resource) = *texture.sampler_resource.borrow() { - // NOTE: this assumes theres is only one sampler - // TODO: see if we need more than one sampler - sampler_resource - } else { - self.default_sampler - }; - - render_resource_assignments.set( - "uSampler", - RenderResourceAssignment::Sampler(sampler_resource), - ); - - uniform_buffer_ranges.push(None); - - continue; - } - UniformData::ImageUnit(_) => panic!("image unit not currently supported"), - } - let end_index = uniform_buffer_data.len(); - while uniform_buffer_data.len() % 256 != 0 { - uniform_buffer_data.push(0); - } - uniform_buffer_ranges.push(Some(start_index..end_index)); - } - - let buffer_resource = self - .render_context - .borrow() - .resources() - .create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM, - ..Default::default() - }, - &uniform_buffer_data, - ); - - for ((bevy_uniform, _data), range) in render_state - .uniforms - .iter() - .zip(uniform_buffer_ranges.iter()) - { - if let Some(range) = range { - render_resource_assignments.set( - &bevy_uniform.name, - RenderResourceAssignment::Buffer { - dynamic_index: None, - range: range.start as u64..range.end as u64, - resource: buffer_resource, - }, - ) - } - } - } - - pub fn fill_in_missing_uniforms( - &self, - render_resource_assignments: &mut RenderResourceAssignments, - pipeline_descriptor: &PipelineDescriptor, - ) { - for bind_group in pipeline_descriptor.get_layout().unwrap().bind_groups.iter() { - for binding in bind_group.bindings.iter() { - if render_resource_assignments.get(&binding.name).is_none() { - match binding.bind_type { - BindType::SampledTexture { .. } => { - render_resource_assignments.set( - &binding.name, - RenderResourceAssignment::Texture(self.default_uniform_texture), - ); - } - BindType::Buffer { .. } => { - render_resource_assignments.set( - &binding.name, - RenderResourceAssignment::Buffer { - resource: self.default_buffer, - range: 0..binding.bind_type.get_uniform_size().unwrap(), - dynamic_index: None, - }, - ); - } - BindType::Uniform { .. } => { - render_resource_assignments.set( - &binding.name, - RenderResourceAssignment::Buffer { - resource: self.default_buffer, - range: 0..binding.bind_type.get_uniform_size().unwrap(), - dynamic_index: None, - }, - ); - } - BindType::Sampler { .. } => { - render_resource_assignments.set( - &binding.name, - RenderResourceAssignment::Sampler(self.default_sampler), - ); - } - _ => panic!( - "no defaults available for bind type {:?}", - binding.bind_type - ), - } - } - } - } - } - - pub fn setup_vertex_buffers( - &self, - render_state: &RenderState, - render_resource_assignments: &mut RenderResourceAssignments, - ) { - for (i, vertex_buffer) in render_state - .vertex_array - .vertex_buffers - .borrow() - .iter() - .enumerate() - { - let resource = vertex_buffer.handle.borrow().unwrap(); - let mut indices_resource = None; - if i == 0 { - if let Some(ref index_buffer) = *render_state.vertex_array.index_buffer.borrow() { - indices_resource = Some(index_buffer.handle.borrow().unwrap()); - } - } - render_resource_assignments.set_vertex_buffer( - get_vertex_buffer_name(i), - resource, - indices_resource, - ); - } - } - - pub fn setup_bind_groups( - &self, - pipeline_descriptor: &PipelineDescriptor, - render_resource_assignments: &mut RenderResourceAssignments, - ) { - let bind_groups = &pipeline_descriptor.get_layout().unwrap().bind_groups; - let render_context = self.render_context.borrow(); - let render_resources = render_context.resources(); - for bind_group in bind_groups.iter() { - render_resource_assignments.update_render_resource_set_id(bind_group); - } - render_resources.setup_bind_groups(pipeline_descriptor, &render_resource_assignments); - } - - pub fn draw(&self, render_state: &RenderState, draw_func: F) - where - F: Fn(&mut dyn RenderPass), - { - // TODO: maybe sync textures here? - let pass_descriptor = self.create_pass_descriptor(render_state); - self.setup_pipline_descriptor( - render_state, - &pass_descriptor, - &render_state.vertex_array.requested_descriptors.borrow(), - ); - let mut render_resource_assignments = RenderResourceAssignments::default(); - self.setup_uniforms(render_state, &mut render_resource_assignments); - self.fill_in_missing_uniforms( - &mut render_resource_assignments, - &render_state.program.pipeline_descriptor.borrow(), - ); - self.setup_bind_groups( - &render_state.program.pipeline_descriptor.borrow(), - &mut render_resource_assignments, - ); - self.setup_vertex_buffers(render_state, &mut render_resource_assignments); - self.render_context.borrow_mut().begin_pass( - &pass_descriptor, - &render_resource_assignments, - &mut |pass| { - let viewport = render_state.viewport; - pass.set_viewport( - viewport.origin().x() as f32, - viewport.origin().y() as f32, - viewport.size().x() as f32, - viewport.size().y() as f32, - 0.0, - 1.0, - ); - pass.set_pipeline(render_state.program.pipeline_handle); - - if let Some(stencil_state) = render_state.options.stencil { - pass.set_stencil_reference(stencil_state.reference); - } - - let pipeline_descriptor = render_state.program.pipeline_descriptor.borrow(); - pass.set_render_resources(&pipeline_descriptor, &render_resource_assignments); - draw_func(pass); - }, - ) - } - - fn get_texture_format(&self, render_resource: RenderResource) -> Option { - // TODO: add swap chain resource info so this isnt necessary - let mut texture_format = Some(TextureFormat::Bgra8UnormSrgb); - self.render_context - .borrow() - .resources() - .get_resource_info(render_resource, &mut |info| { - if let Some(ResourceInfo::Texture(descriptor)) = info { - texture_format = Some(descriptor.format) - } - }); - texture_format - } - - // v - pub fn setup_pipline_descriptor( - &self, - render_state: &RenderState, - pass_descriptor: &PassDescriptor, - requested_vertex_descriptors: &HashMap, - ) { - // TODO: only create pipelines once - // if self - // .render_context - // .borrow() - // .resources() - // .get_asset_resource(render_state.program.pipeline_handle, 0) - // .is_some() - // { - // return; - // } - - let mut pipeline_descriptor = render_state.program.pipeline_descriptor.borrow_mut(); - pipeline_descriptor.primitive_topology = match render_state.primitive { - pathfinder_gpu::Primitive::Triangles => PrimitiveTopology::TriangleList, - pathfinder_gpu::Primitive::Lines => PrimitiveTopology::LineList, - }; - pipeline_descriptor.rasterization_state = Some(RasterizationStateDescriptor::default()); - { - let mut layout = pipeline_descriptor.get_layout_mut().unwrap(); - let mut i = 0; - let mut descriptors = Vec::with_capacity(requested_vertex_descriptors.len()); - loop { - if let Some(descriptor) = requested_vertex_descriptors.get(&i) { - let mut descriptor = descriptor.clone(); - descriptor.attributes.sort_by_key(|a| a.shader_location); - descriptors.push(descriptor); - i += 1; - } else { - break; - } - } - layout.vertex_buffer_descriptors = descriptors; - } - - let color_texture_format = if let TextureAttachment::RenderResource(texture_resource) = - pass_descriptor - .color_attachments - .first() - .unwrap() - .attachment - { - self.get_texture_format(texture_resource) - .expect("expected color attachment RenderResource to have a texture format") - } else { - panic!("expected a RenderResource color attachment"); - }; - - // TODO: make sure colors render correctly - let mut color_state = ColorStateDescriptor { - format: color_texture_format, - color_blend: BlendDescriptor { - src_factor: BlendFactor::SrcAlpha, - dst_factor: BlendFactor::OneMinusSrcAlpha, - operation: BlendOperation::Add, - }, - alpha_blend: BlendDescriptor { - src_factor: BlendFactor::One, - dst_factor: BlendFactor::One, - operation: BlendOperation::Add, - }, - write_mask: if render_state.options.color_mask { - ColorWrite::all() - } else { - ColorWrite::empty() - }, - }; - - if let Some(blend_state) = render_state.options.blend { - let blend_op = blend_state.op.to_bevy_blend_op(); - color_state.color_blend.src_factor = blend_state.src_rgb_factor.to_bevy_blend_factor(); - color_state.color_blend.dst_factor = blend_state.dest_rgb_factor.to_bevy_blend_factor(); - color_state.color_blend.operation = blend_op; - - color_state.alpha_blend.src_factor = - blend_state.src_alpha_factor.to_bevy_blend_factor(); - color_state.alpha_blend.dst_factor = - blend_state.dest_alpha_factor.to_bevy_blend_factor(); - color_state.color_blend.operation = blend_op; - } - - pipeline_descriptor.color_states.push(color_state); - - if let Some(ref pass_depth_stencil_descriptor) = pass_descriptor.depth_stencil_attachment { - // TODO: maybe we need a stencil-type depth format? TextureFormat::Depth24PlusStencil8 - let format = self - .get_texture_format( - pass_depth_stencil_descriptor - .attachment - .get_resource() - .expect("Expected attachment to be a resource"), - ) - .expect("expected a texture format"); - let mut descriptor = DepthStencilStateDescriptor { - format, - depth_write_enabled: false, - depth_compare: CompareFunction::Less, - stencil_front: StencilStateFaceDescriptor::IGNORE, - stencil_back: StencilStateFaceDescriptor::IGNORE, - stencil_read_mask: 0, - stencil_write_mask: 0, - }; - - if let Some(depth_state) = render_state.options.depth { - let compare_function = depth_state.func.to_bevy_compare_function(); - descriptor.depth_compare = compare_function; - descriptor.depth_write_enabled = true; - } - - if let Some(stencil_state) = render_state.options.stencil { - let compare = stencil_state.func.to_bevy_compare_function(); - let (pass_op, write_mask) = if stencil_state.write { - (StencilOperation::Replace, stencil_state.mask) - } else { - (StencilOperation::Keep, 0) - }; - - let stencil_descriptor = StencilStateFaceDescriptor { - compare, - pass_op, - depth_fail_op: StencilOperation::Keep, - fail_op: StencilOperation::Keep, - }; - - descriptor.stencil_write_mask = write_mask; - descriptor.stencil_front = stencil_descriptor.clone(); - descriptor.stencil_back = stencil_descriptor; - } - pipeline_descriptor.depth_stencil_state = Some(descriptor); - } - - self.render_context - .borrow() - .resources() - .create_render_pipeline( - render_state.program.pipeline_handle, - &pipeline_descriptor, - &self.shaders.borrow(), - ); - } - - pub fn create_pass_descriptor( - &self, - render_state: &RenderState, - ) -> PassDescriptor { - let mut depth_texture = None; - let color_texture = match render_state.target { - RenderTarget::Default { .. } => { - depth_texture = Some(self.main_depth_stencil_texture); - self.main_color_texture - } - RenderTarget::Framebuffer(framebuffer) => framebuffer.handle, - }; - - let mut color_attachment = RenderPassColorAttachmentDescriptor { - attachment: TextureAttachment::RenderResource(color_texture), - clear_color: Color::WHITE, - load_op: LoadOp::Load, - store_op: StoreOp::Store, - resolve_target: None, - }; - - if let Some(color) = render_state.options.clear_ops.color { - color_attachment.clear_color = Color::rgba(color.r(), color.g(), color.b(), color.a()); - color_attachment.load_op = LoadOp::Clear; - } - - let depth_stencil_attachment = if let Some(depth_texture) = depth_texture { - let mut descriptor = RenderPassDepthStencilAttachmentDescriptor { - attachment: TextureAttachment::RenderResource(depth_texture), - depth_load_op: LoadOp::Load, - depth_store_op: StoreOp::Store, - stencil_load_op: LoadOp::Load, - stencil_store_op: StoreOp::Store, - clear_depth: 1.0, - clear_stencil: 0, - }; - - if let Some(depth) = render_state.options.clear_ops.depth { - descriptor.clear_depth = depth; - descriptor.depth_load_op = LoadOp::Clear; - } - - if let Some(value) = render_state.options.clear_ops.stencil { - descriptor.clear_stencil = value as u32; - descriptor.stencil_load_op = LoadOp::Clear; - } - - Some(descriptor) - } else { - None - }; - - PassDescriptor { - color_attachments: vec![color_attachment], - depth_stencil_attachment, - sample_count: 1, - } - } -} - -pub struct BevyTimerQuery {} -pub struct BevyTextureDataReceiver {} -pub struct BevyUniform { - pub name: String, -} - -pub struct BevyVertexAttr { - attr: RefCell, -} - -#[derive(Debug)] -pub struct BevyVertexArray { - requested_descriptors: RefCell>, - vertex_buffers: RefCell>, - index_buffer: RefCell>, -} - -pub struct BevyProgram { - pipeline_descriptor: RefCell, - pipeline_handle: Handle, -} - -pub struct BevyTexture { - handle: RenderResource, - texture_descriptor: TextureDescriptor, - sampler_resource: RefCell>, -} - -#[derive(Debug, Clone)] -pub struct BevyBuffer { - handle: Rc>>, - mode: BufferUploadMode, -} - -impl<'a> Device for BevyPathfinderDevice<'a> { - type Buffer = BevyBuffer; - type Fence = (); - type Framebuffer = BevyTexture; - type Program = BevyProgram; - type Shader = Handle; - type StorageBuffer = (); - type Texture = BevyTexture; - type TextureDataReceiver = BevyTextureDataReceiver; - type TimerQuery = BevyTimerQuery; - type Uniform = BevyUniform; - type VertexArray = BevyVertexArray; - type VertexAttr = BevyVertexAttr; - - fn create_texture( - &self, - format: pathfinder_gpu::TextureFormat, - size: Vector2I, - ) -> Self::Texture { - let descriptor = TextureDescriptor { - size: Extent3d { - depth: 1, - width: size.x() as u32, - height: size.y() as u32, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: match format { - pathfinder_gpu::TextureFormat::R8 => TextureFormat::R8Unorm, - pathfinder_gpu::TextureFormat::R16F => TextureFormat::R16Float, - pathfinder_gpu::TextureFormat::RGBA8 => TextureFormat::Rgba8Unorm, - pathfinder_gpu::TextureFormat::RGBA16F => TextureFormat::Rgba16Float, - pathfinder_gpu::TextureFormat::RGBA32F => TextureFormat::Rgba32Float, - }, - usage: TextureUsage::WRITE_ALL | TextureUsage::SAMPLED, // TODO: this might be overly safe - }; - BevyTexture { - handle: self - .render_context - .borrow() - .resources() - .create_texture(descriptor), - texture_descriptor: descriptor, - sampler_resource: RefCell::new(None), - } - } - fn create_texture_from_data( - &self, - format: pathfinder_gpu::TextureFormat, - size: Vector2I, - data: TextureDataRef, - ) -> Self::Texture { - let texture = self.create_texture(format, size); - self.upload_to_texture(&texture, RectI::new(Vector2I::default(), size), data); - texture - } - fn create_shader( - &self, - resources: &dyn ResourceLoader, - name: &str, - kind: ShaderKind, - ) -> Self::Shader { - let suffix = match kind { - ShaderKind::Vertex => 'v', - ShaderKind::Fragment => 'f', - ShaderKind::Compute => 'c', - }; - let path = format!("shaders/vulkan/{}.{}s.spv", name, suffix); - let bytes = resources.slurp(&path).unwrap(); - - self.create_shader_from_source(name, &bytes, kind) - } - fn create_shader_from_source( - &self, - _name: &str, - source: &[u8], - kind: ShaderKind, - ) -> Self::Shader { - let stage = match kind { - ShaderKind::Fragment => ShaderStage::Fragment, - ShaderKind::Vertex => ShaderStage::Vertex, - ShaderKind::Compute => panic!("bevy does not currently support compute shaders"), - }; - let shader = Shader::new(stage, ShaderSource::spirv_from_bytes(source)); - let mut shaders = self.shaders.borrow_mut(); - let handle = shaders.add(shader); - self.render_context - .borrow() - .resources() - .create_shader_module(handle, &mut shaders); - handle - } - - fn create_vertex_array(&self) -> Self::VertexArray { - BevyVertexArray { - requested_descriptors: RefCell::new(HashMap::new()), - index_buffer: RefCell::new(None), - vertex_buffers: RefCell::new(Vec::new()), - } - } - fn create_program_from_shaders( - &self, - _resources: &dyn ResourceLoader, - _name: &str, - shaders: ProgramKind>, - ) -> BevyProgram { - match shaders { - ProgramKind::Compute(_) => panic!("bevy does not currently support compute shaders"), - ProgramKind::Raster { vertex, fragment } => { - let mut descriptor = PipelineDescriptor::new(ShaderStages { - vertex, - fragment: Some(fragment), - }); - descriptor.index_format = IndexFormat::Uint32; - descriptor.reflect_layout(&self.shaders.borrow(), false, None, None); - BevyProgram { - pipeline_descriptor: RefCell::new(descriptor), - pipeline_handle: Handle::new(), - } - } - } - } - fn get_vertex_attr(&self, program: &BevyProgram, name: &str) -> Option { - // TODO: this probably isn't actually used for anything. try to optimize - let pipeline_descriptor = program.pipeline_descriptor.borrow(); - let layout = pipeline_descriptor.get_layout().unwrap(); - let attribute_name = format!("a{}", name); - for buffer_descriptor in layout.vertex_buffer_descriptors.iter() { - let attribute = buffer_descriptor - .attributes - .iter() - .find(|a| a.name == attribute_name) - .cloned(); - if attribute.is_some() { - return attribute.map(|a| BevyVertexAttr { - attr: RefCell::new(a), - }); - } - } - - None - } - fn get_uniform(&self, _program: &BevyProgram, name: &str) -> Self::Uniform { - BevyUniform { - name: format!("u{}", name), - } - } - fn bind_buffer( - &self, - vertex_array: &BevyVertexArray, - buffer: &BevyBuffer, - target: BufferTarget, - ) { - match target { - BufferTarget::Vertex => vertex_array - .vertex_buffers - .borrow_mut() - .push(buffer.clone()), - BufferTarget::Index => *vertex_array.index_buffer.borrow_mut() = Some(buffer.clone()), - _ => panic!("Buffers bound to vertex arrays must be vertex or index buffers!"), - } - } - fn configure_vertex_attr( - &self, - vertex_array: &BevyVertexArray, - bevy_attr: &BevyVertexAttr, - descriptor: &VertexAttrDescriptor, - ) { - // println!("configure: {:?} {:?}") - let format = match (descriptor.class, descriptor.attr_type, descriptor.size) { - (VertexAttrClass::Int, VertexAttrType::I8, 2) => VertexFormat::Char2, - // (VertexAttrClass::Int, VertexAttrType::I8, 3) => VertexFormat::Char3, - (VertexAttrClass::Int, VertexAttrType::I8, 4) => VertexFormat::Char4, - (VertexAttrClass::Int, VertexAttrType::U8, 2) => VertexFormat::Uchar2, - // (VertexAttrClass::Int, VertexAttrType::U8, 3) => VertexFormat::Uchar3, - (VertexAttrClass::Int, VertexAttrType::U8, 4) => VertexFormat::Uchar4, - (VertexAttrClass::FloatNorm, VertexAttrType::U8, 2) => VertexFormat::Uchar2Norm, - // (VertexAttrClass::FloatNorm, VertexAttrType::U8, 3) => { - // VertexFormat::UChar3Normalized - // } - (VertexAttrClass::FloatNorm, VertexAttrType::U8, 4) => VertexFormat::Uchar4Norm, - (VertexAttrClass::FloatNorm, VertexAttrType::I8, 2) => VertexFormat::Char2Norm, - // (VertexAttrClass::FloatNorm, VertexAttrType::I8, 3) => { - // VertexFormat::Char3Norm - // } - (VertexAttrClass::FloatNorm, VertexAttrType::I8, 4) => VertexFormat::Char4Norm, - (VertexAttrClass::Int, VertexAttrType::I16, 2) => VertexFormat::Short2, - // (VertexAttrClass::Int, VertexAttrType::I16, 3) => VertexFormat::Short3, - (VertexAttrClass::Int, VertexAttrType::I16, 4) => VertexFormat::Short4, - (VertexAttrClass::Int, VertexAttrType::U16, 2) => VertexFormat::Ushort2, - // (VertexAttrClass::Int, VertexAttrType::U16, 3) => VertexFormat::UShort3, - (VertexAttrClass::Int, VertexAttrType::U16, 4) => VertexFormat::Ushort4, - (VertexAttrClass::FloatNorm, VertexAttrType::U16, 2) => VertexFormat::Ushort2Norm, - // (VertexAttrClass::FloatNorm, VertexAttrType::U16, 3) => { - // VertexFormat::UShort3Normalized - // } - (VertexAttrClass::FloatNorm, VertexAttrType::U16, 4) => VertexFormat::Ushort4Norm, - (VertexAttrClass::FloatNorm, VertexAttrType::I16, 2) => VertexFormat::Short2Norm, - // (VertexAttrClass::FloatNorm, VertexAttrType::I16, 3) => { - // VertexFormat::Short3Normalized - // } - (VertexAttrClass::FloatNorm, VertexAttrType::I16, 4) => VertexFormat::Short4Norm, - (VertexAttrClass::Int, VertexAttrType::I32, 1) => VertexFormat::Int, - (VertexAttrClass::Int, VertexAttrType::I32, 2) => VertexFormat::Int2, - (VertexAttrClass::Int, VertexAttrType::I32, 3) => VertexFormat::Int3, - (VertexAttrClass::Int, VertexAttrType::I32, 4) => VertexFormat::Int4, - (VertexAttrClass::Int, VertexAttrType::U32, 1) => VertexFormat::Uint, - (VertexAttrClass::Int, VertexAttrType::U32, 2) => VertexFormat::Uint2, - (VertexAttrClass::Int, VertexAttrType::U32, 3) => VertexFormat::Uint3, - (VertexAttrClass::Int, VertexAttrType::U32, 4) => VertexFormat::Uint4, - (VertexAttrClass::Float, VertexAttrType::F32, 1) => VertexFormat::Float, - (VertexAttrClass::Float, VertexAttrType::F32, 2) => VertexFormat::Float2, - (VertexAttrClass::Float, VertexAttrType::F32, 3) => VertexFormat::Float3, - (VertexAttrClass::Float, VertexAttrType::F32, 4) => VertexFormat::Float4, - // (VertexAttrClass::Int, VertexAttrType::I8, 1) => VertexFormat::Char, - // (VertexAttrClass::Int, VertexAttrType::U8, 1) => VertexFormat::UChar, - // (VertexAttrClass::FloatNorm, VertexAttrType::I8, 1) => VertexFormat::CharNormalized, - // (VertexAttrClass::FloatNorm, VertexAttrType::U8, 1) => { - // VertexFormat::UCharNormalized - // } - // (VertexAttrClass::Int, VertexAttrType::I16, 1) => VertexFormat::Short, - // (VertexAttrClass::Int, VertexAttrType::U16, 1) => VertexFormat::UShort, - // (VertexAttrClass::FloatNorm, VertexAttrType::U16, 1) => { - // VertexFormat::UShortNormalized - // } - // (VertexAttrClass::FloatNorm, VertexAttrType::I16, 1) => { - // VertexFormat::ShortNormalized - // } - (attr_class, attr_type, attr_size) => panic!( - "Unsupported vertex class/type/size combination: {:?}/{:?}/{}!", - attr_class, attr_type, attr_size - ), - }; - - let mut requested_descriptors = vertex_array.requested_descriptors.borrow_mut(); - let buffer_index = descriptor.buffer_index; - let step_mode = if descriptor.divisor == 0 { - InputStepMode::Vertex - } else { - InputStepMode::Instance - }; - - assert!( - descriptor.divisor <= 1, - "instanced step size greater than 1 not supported" - ); - - let vertex_buffer_descriptor = - requested_descriptors - .entry(buffer_index) - .or_insert_with(|| VertexBufferDescriptor { - name: Cow::Borrowed(get_vertex_buffer_name(buffer_index as usize)), - attributes: Vec::new(), - step_mode, - stride: descriptor.stride as u64, - }); - - let mut attr = bevy_attr.attr.borrow_mut(); - attr.format = format; - attr.offset = descriptor.offset as u64; - - vertex_buffer_descriptor.attributes.push(attr.clone()); - } - - fn create_framebuffer(&self, texture: BevyTexture) -> BevyTexture { - texture - } - - fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer { - BevyBuffer { - handle: Rc::new(RefCell::new(None)), - mode, - } - } - - fn allocate_buffer(&self, buffer: &BevyBuffer, data: BufferData, target: BufferTarget) { - let usage = match target { - BufferTarget::Vertex => BufferUsage::VERTEX, - BufferTarget::Index => BufferUsage::INDEX, - BufferTarget::Storage => BufferUsage::empty(), - }; - - let buffer_usage = match buffer.mode { - BufferUploadMode::Dynamic => BufferUsage::WRITE_ALL | usage, - BufferUploadMode::Static => BufferUsage::COPY_DST | usage, - }; - match data { - BufferData::Uninitialized(size) => { - let size = size * mem::size_of::(); - let new_buffer = - self.render_context - .borrow() - .resources() - .create_buffer(BufferInfo { - size, - buffer_usage, - ..Default::default() - }); - *buffer.handle.borrow_mut() = Some(new_buffer); - } - BufferData::Memory(slice) => { - let new_buffer = self - .render_context - .borrow() - .resources() - .create_buffer_with_data( - BufferInfo { - buffer_usage, - ..Default::default() - }, - slice_to_u8(slice), - ); - *buffer.handle.borrow_mut() = Some(new_buffer); - } - } - } - - fn framebuffer_texture<'f>(&self, framebuffer: &'f BevyTexture) -> &'f BevyTexture { - framebuffer - } - - fn destroy_framebuffer(&self, framebuffer: BevyTexture) -> BevyTexture { - // TODO: should this deallocate the bevy texture? - framebuffer - } - - fn texture_format(&self, texture: &BevyTexture) -> pathfinder_gpu::TextureFormat { - match texture.texture_descriptor.format { - TextureFormat::R8Unorm => pathfinder_gpu::TextureFormat::R8, - TextureFormat::R16Float => pathfinder_gpu::TextureFormat::R16F, - TextureFormat::Rgba8Unorm => pathfinder_gpu::TextureFormat::RGBA8, - TextureFormat::Rgba16Float => pathfinder_gpu::TextureFormat::RGBA16F, - TextureFormat::Rgba32Float => pathfinder_gpu::TextureFormat::RGBA32F, - _ => panic!( - "unexpected texture format {:?}", - texture.texture_descriptor.format - ), - } - } - - fn texture_size(&self, texture: &BevyTexture) -> Vector2I { - vec2i( - texture.texture_descriptor.size.width as i32, - texture.texture_descriptor.size.height as i32, - ) - } - - fn set_texture_sampling_mode(&self, texture: &BevyTexture, flags: TextureSamplingFlags) { - let mut samplers = self.samplers.borrow_mut(); - let resource = samplers.entry(flags.bits()).or_insert_with(|| { - let descriptor = SamplerDescriptor { - address_mode_u: if flags.contains(TextureSamplingFlags::REPEAT_U) { - AddressMode::Repeat - } else { - AddressMode::ClampToEdge - }, - address_mode_v: if flags.contains(TextureSamplingFlags::REPEAT_V) { - AddressMode::Repeat - } else { - AddressMode::ClampToEdge - }, - address_mode_w: AddressMode::ClampToEdge, - mag_filter: if flags.contains(TextureSamplingFlags::NEAREST_MAG) { - FilterMode::Nearest - } else { - FilterMode::Linear - }, - min_filter: if flags.contains(TextureSamplingFlags::NEAREST_MIN) { - FilterMode::Nearest - } else { - FilterMode::Linear - }, - mipmap_filter: FilterMode::Nearest, - lod_min_clamp: -100.0, - lod_max_clamp: 100.0, - compare_function: CompareFunction::Always, - }; - self.render_context - .borrow_mut() - .resources() - .create_sampler(&descriptor) - }); - *texture.sampler_resource.borrow_mut() = Some(*resource); - } - - fn upload_to_texture(&self, texture: &BevyTexture, rect: RectI, data: TextureDataRef) { - let texture_size = self.texture_size(texture); - assert!(rect.size().x() >= 0); - assert!(rect.size().y() >= 0); - assert!(rect.max_x() <= texture_size.x()); - assert!(rect.max_y() <= texture_size.y()); - - let format = self.texture_format(&texture); - let width = rect.size().x() as u32; - let height = rect.size().y() as u32; - let origin = [rect.origin().x() as u32, rect.origin().y() as u32, 0]; - let bytes_per_pixel = format.bytes_per_pixel() as u32; - - let staging_buffer = self - .render_context - .borrow() - .resources() - .create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::COPY_SRC, - ..Default::default() - }, - get_texture_bytes(&data), - ); - - let stride = bytes_per_pixel * width; - - self.render_context.borrow_mut().copy_buffer_to_texture( - staging_buffer, - 0, - stride, - texture.handle, - origin, - 0, - 0, - Extent3d { - width, - height, - depth: 1, - }, - ) - } - fn read_pixels( - &self, - _target: &RenderTarget, - _viewport: RectI, - ) -> Self::TextureDataReceiver { - // TODO: this might actually be optional, which is great because otherwise this requires a command buffer sync - todo!() - } - fn begin_commands(&self) { - // NOTE: the Bevy Render Graph handles command buffer creation - } - fn end_commands(&self) { - // NOTE: the Bevy Render Graph handles command buffer submission - } - fn draw_arrays(&self, index_count: u32, render_state: &RenderState) { - self.draw(render_state, |pass| { - pass.draw(0..index_count, 0..1); - }); - } - fn draw_elements(&self, index_count: u32, render_state: &RenderState) { - self.draw(render_state, |pass| { - pass.draw_indexed(0..index_count, 0, 0..1); - }); - } - fn draw_elements_instanced( - &self, - index_count: u32, - instance_count: u32, - render_state: &RenderState, - ) { - self.draw(render_state, |pass| { - pass.draw_indexed(0..index_count, 0, 0..instance_count); - }); - } - fn create_timer_query(&self) -> Self::TimerQuery { - // TODO: maybe not needed - BevyTimerQuery {} - } - fn begin_timer_query(&self, _query: &Self::TimerQuery) {} - fn end_timer_query(&self, _query: &Self::TimerQuery) {} - fn try_recv_timer_query(&self, _query: &Self::TimerQuery) -> Option { - todo!() - } - fn recv_timer_query(&self, _query: &Self::TimerQuery) -> std::time::Duration { - todo!() - } - fn try_recv_texture_data(&self, _receiver: &Self::TextureDataReceiver) -> Option { - todo!() - } - fn recv_texture_data(&self, _receiver: &Self::TextureDataReceiver) -> TextureData { - todo!() - } - fn feature_level(&self) -> pathfinder_gpu::FeatureLevel { - // TODO: change to 11 when compute is added - FeatureLevel::D3D10 - } - fn set_compute_program_local_size( - &self, - _program: &mut Self::Program, - _local_size: pathfinder_gpu::ComputeDimensions, - ) { - todo!() - } - fn get_storage_buffer( - &self, - _program: &Self::Program, - _name: &str, - _binding: u32, - ) -> Self::StorageBuffer { - panic!("Compute shader is unsupported in Bevy!"); - } - fn upload_to_buffer( - &self, - buffer: &BevyBuffer, - position: usize, - data: &[T], - _target: BufferTarget, - ) { - let temp_buffer = self - .render_context - .borrow() - .resources() - .create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::COPY_SRC | BufferUsage::COPY_DST, - ..Default::default() - }, - slice_to_u8(data), - ); - let buffer_handle = buffer.handle.borrow().unwrap(); - self.render_context.borrow_mut().copy_buffer_to_buffer( - temp_buffer, - 0, - buffer_handle, - (position * mem::size_of::()) as u64, - (data.len() * mem::size_of::()) as u64, - ) - } - fn dispatch_compute( - &self, - _dimensions: pathfinder_gpu::ComputeDimensions, - _state: &pathfinder_gpu::ComputeState, - ) { - panic!("Compute shader is unsupported in Bevy!"); - } - fn add_fence(&self) -> Self::Fence {} - fn wait_for_fence(&self, _fence: &Self::Fence) {} -} - -fn get_texture_bytes<'a>(data_ref: &'a TextureDataRef) -> &'a [u8] { - match data_ref { - TextureDataRef::U8(data) => slice_to_u8(data), - TextureDataRef::F16(data) => slice_to_u8(data), - TextureDataRef::F32(data) => slice_to_u8(data), - } -} - -fn slice_to_u8(slice: &[T]) -> &[u8] { - unsafe { - std::slice::from_raw_parts( - slice.as_ptr() as *const u8, - slice.len() * mem::size_of::(), - ) - } -} - -trait ToBevyBlendOp { - fn to_bevy_blend_op(self) -> BlendOperation; -} - -impl ToBevyBlendOp for pathfinder_gpu::BlendOp { - #[inline] - fn to_bevy_blend_op(self) -> BlendOperation { - match self { - pathfinder_gpu::BlendOp::Add => BlendOperation::Add, - pathfinder_gpu::BlendOp::Subtract => BlendOperation::Subtract, - pathfinder_gpu::BlendOp::ReverseSubtract => BlendOperation::ReverseSubtract, - pathfinder_gpu::BlendOp::Min => BlendOperation::Min, - pathfinder_gpu::BlendOp::Max => BlendOperation::Max, - } - } -} - -trait ToBevyBlendFactor { - fn to_bevy_blend_factor(self) -> BlendFactor; -} - -impl ToBevyBlendFactor for pathfinder_gpu::BlendFactor { - #[inline] - fn to_bevy_blend_factor(self) -> BlendFactor { - match self { - pathfinder_gpu::BlendFactor::Zero => BlendFactor::Zero, - pathfinder_gpu::BlendFactor::One => BlendFactor::One, - pathfinder_gpu::BlendFactor::SrcAlpha => BlendFactor::SrcAlpha, - pathfinder_gpu::BlendFactor::OneMinusSrcAlpha => BlendFactor::OneMinusSrcAlpha, - pathfinder_gpu::BlendFactor::DestAlpha => BlendFactor::DstAlpha, - pathfinder_gpu::BlendFactor::OneMinusDestAlpha => BlendFactor::OneMinusDstAlpha, - pathfinder_gpu::BlendFactor::DestColor => BlendFactor::DstColor, - } - } -} - -trait ToBevyCompareFunction { - fn to_bevy_compare_function(self) -> CompareFunction; -} - -impl ToBevyCompareFunction for pathfinder_gpu::DepthFunc { - fn to_bevy_compare_function(self) -> CompareFunction { - match self { - pathfinder_gpu::DepthFunc::Always => CompareFunction::Always, - pathfinder_gpu::DepthFunc::Less => CompareFunction::Less, - } - } -} - -impl ToBevyCompareFunction for pathfinder_gpu::StencilFunc { - fn to_bevy_compare_function(self) -> CompareFunction { - match self { - pathfinder_gpu::StencilFunc::Always => CompareFunction::Always, - pathfinder_gpu::StencilFunc::Equal => CompareFunction::Equal, - } - } -} - -pub const PATHFINDER_VERTEX_BUFFER_0: &'static str = "P0"; -pub const PATHFINDER_VERTEX_BUFFER_1: &'static str = "P1"; -pub const PATHFINDER_VERTEX_BUFFER_2: &'static str = "P2"; -pub const PATHFINDER_VERTEX_BUFFER_3: &'static str = "P3"; - -pub fn get_vertex_buffer_name(index: usize) -> &'static str { - match index { - 0 => PATHFINDER_VERTEX_BUFFER_0, - 1 => PATHFINDER_VERTEX_BUFFER_1, - 2 => PATHFINDER_VERTEX_BUFFER_2, - 3 => PATHFINDER_VERTEX_BUFFER_3, - _ => panic!("encountered unknown vertex buffer index"), - } -} diff --git a/crates/bevy_pathfinder/src/lib.rs b/crates/bevy_pathfinder/src/lib.rs deleted file mode 100644 index 38054039d5..0000000000 --- a/crates/bevy_pathfinder/src/lib.rs +++ /dev/null @@ -1,39 +0,0 @@ -mod device; -mod pathfinder_node; -use bevy_app::{AppBuilder, AppPlugin}; -pub use device::*; - -use bevy_render::{ - base_render_graph, - render_graph::{ - nodes::{WindowSwapChainNode, WindowTextureNode}, - RenderGraph, - }, -}; -use pathfinder_node::PathfinderNode; - -#[derive(Default)] -pub struct PathfinderPlugin; - -impl AppPlugin for PathfinderPlugin { - fn build(&self, app: &mut AppBuilder) { - let mut render_graph = app.resources().get_mut::().unwrap(); - render_graph.add_node_named("pathfinder", PathfinderNode::default()); - render_graph - .add_slot_edge( - base_render_graph::node::PRIMARY_SWAP_CHAIN, - WindowSwapChainNode::OUT_TEXTURE, - "pathfinder", - PathfinderNode::IN_COLOR_TEXTURE, - ) - .unwrap(); - render_graph - .add_slot_edge( - base_render_graph::node::MAIN_DEPTH_TEXTURE, - WindowTextureNode::OUT_TEXTURE, - "pathfinder", - PathfinderNode::IN_DEPTH_STENCIL_TEXTURE, - ) - .unwrap(); - } -} diff --git a/crates/bevy_pathfinder/src/pathfinder_node.rs b/crates/bevy_pathfinder/src/pathfinder_node.rs deleted file mode 100644 index a6390b5144..0000000000 --- a/crates/bevy_pathfinder/src/pathfinder_node.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::BevyPathfinderDevice; -use bevy_asset::AssetStorage; -use bevy_render::{ - render_graph::{Node, ResourceSlotInfo, ResourceSlots}, - render_resource::ResourceInfo, - renderer::RenderContext, - shader::{FieldBindType, Shader}, -}; -use legion::prelude::{Resources, World}; -use pathfinder_canvas::{vec2f, Canvas, CanvasFontContext, ColorF, Path2D, RectF, Vector2I}; -use pathfinder_renderer::{ - concurrent::{rayon::RayonExecutor, scene_proxy::SceneProxy}, - gpu::{ - options::{DestFramebuffer, RendererOptions}, - renderer::Renderer, - }, - options::BuildOptions, -}; -use pathfinder_resources::embedded::EmbeddedResourceLoader; -use std::borrow::Cow; - -#[derive(Default)] -pub struct PathfinderNode; - -impl PathfinderNode { - pub const IN_COLOR_TEXTURE: &'static str = "color"; - pub const IN_DEPTH_STENCIL_TEXTURE: &'static str = "depth_stencil"; -} - -impl Node for PathfinderNode { - fn input(&self) -> &[ResourceSlotInfo] { - static INPUT: &[ResourceSlotInfo] = &[ - ResourceSlotInfo { - name: Cow::Borrowed(PathfinderNode::IN_COLOR_TEXTURE), - resource_type: FieldBindType::Texture, - }, - ResourceSlotInfo { - name: Cow::Borrowed(PathfinderNode::IN_DEPTH_STENCIL_TEXTURE), - resource_type: FieldBindType::Texture, - }, - ]; - INPUT - } - fn update( - &mut self, - _world: &World, - resources: &Resources, - render_context: &mut dyn RenderContext, - input: &ResourceSlots, - _output: &mut ResourceSlots, - ) { - let mut shaders = resources.get_mut::>().unwrap(); - let color_texture = input.get(PathfinderNode::IN_COLOR_TEXTURE).unwrap(); - let depth_stencil_texture = input.get(PathfinderNode::IN_DEPTH_STENCIL_TEXTURE).unwrap(); - let device = BevyPathfinderDevice::new( - render_context, - &mut shaders, - color_texture, - depth_stencil_texture, - ); - let window_size = Vector2I::new(1280 as i32, 720 as i32); - let mut renderer = Renderer::new( - device, - &EmbeddedResourceLoader::new(), - DestFramebuffer::full_window(window_size), - RendererOptions { - background_color: Some(ColorF::white()), - ..Default::default() - }, - ); - - // Make a canvas. We're going to draw a house. - let mut canvas = Canvas::new(window_size.to_f32()) - .get_context_2d(CanvasFontContext::from_system_source()); - - // Set line width. - canvas.set_line_width(10.0); - - // Draw walls. - canvas.stroke_rect(RectF::new(vec2f(75.0, 140.0), vec2f(150.0, 110.0))); - - // Draw door. - canvas.fill_rect(RectF::new(vec2f(130.0, 190.0), vec2f(40.0, 60.0))); - - // Draw roof. - let mut path = Path2D::new(); - path.move_to(vec2f(50.0, 140.0)); - path.line_to(vec2f(150.0, 60.0)); - path.line_to(vec2f(250.0, 140.0)); - path.close_path(); - canvas.stroke_path(path); - - // Render the canvas to screen. - let scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(), RayonExecutor); - scene.build_and_render(&mut renderer, BuildOptions::default()); - // TODO: submit command buffers? - } -} diff --git a/crates/bevy_render/src/render_graph/graph.rs b/crates/bevy_render/src/render_graph/graph.rs index 5d95d60004..72032f20ad 100644 --- a/crates/bevy_render/src/render_graph/graph.rs +++ b/crates/bevy_render/src/render_graph/graph.rs @@ -314,7 +314,6 @@ mod tests { use super::RenderGraph; use crate::{ render_graph::{Edge, Node, NodeId, RenderGraphError, ResourceSlotInfo, ResourceSlots}, - render_resource::ResourceInfo, renderer::RenderContext, shader::FieldBindType, }; use legion::prelude::{Resources, World}; diff --git a/crates/bevy_render/src/render_graph/node_slot.rs b/crates/bevy_render/src/render_graph/node_slot.rs index 383ec9cf26..bd4bc68e86 100644 --- a/crates/bevy_render/src/render_graph/node_slot.rs +++ b/crates/bevy_render/src/render_graph/node_slot.rs @@ -1,5 +1,5 @@ use super::RenderGraphError; -use crate::{shader::FieldBindType, render_resource::{RenderResource, ResourceInfo}}; +use crate::{render_resource::RenderResource, shader::FieldBindType}; use std::borrow::Cow; #[derive(Debug, Clone)] diff --git a/crates/bevy_render/src/render_graph/nodes/pass_node.rs b/crates/bevy_render/src/render_graph/nodes/pass_node.rs index db1744a0dd..fb30856d28 100644 --- a/crates/bevy_render/src/render_graph/nodes/pass_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/pass_node.rs @@ -3,7 +3,7 @@ use crate::{ pass::{PassDescriptor, TextureAttachment}, pipeline::{PipelineCompiler, PipelineDescriptor}, render_graph::{Node, ResourceSlotInfo, ResourceSlots}, - render_resource::{RenderResourceAssignments, ResourceInfo}, + render_resource::RenderResourceAssignments, renderer::RenderContext, shader::{FieldBindType, Shader}, }; diff --git a/crates/bevy_render/src/render_graph/nodes/window_swapchain_node.rs b/crates/bevy_render/src/render_graph/nodes/window_swapchain_node.rs index f67f44a458..ed7d8c0cc1 100644 --- a/crates/bevy_render/src/render_graph/nodes/window_swapchain_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/window_swapchain_node.rs @@ -1,6 +1,5 @@ use crate::{ render_graph::{Node, ResourceSlotInfo, ResourceSlots}, - render_resource::ResourceInfo, renderer::RenderContext, shader::FieldBindType, }; use bevy_app::{EventReader, Events}; diff --git a/crates/bevy_render/src/render_graph/nodes/window_texture_node.rs b/crates/bevy_render/src/render_graph/nodes/window_texture_node.rs index 9924c63e9a..48dd797796 100644 --- a/crates/bevy_render/src/render_graph/nodes/window_texture_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/window_texture_node.rs @@ -1,6 +1,5 @@ use crate::{ render_graph::{Node, ResourceSlotInfo, ResourceSlots}, - render_resource::ResourceInfo, renderer::RenderContext, texture::TextureDescriptor, shader::FieldBindType, }; diff --git a/crates/bevy_render/src/render_graph/schedule.rs b/crates/bevy_render/src/render_graph/schedule.rs index c30e4e8bcb..d996b93863 100644 --- a/crates/bevy_render/src/render_graph/schedule.rs +++ b/crates/bevy_render/src/render_graph/schedule.rs @@ -264,7 +264,6 @@ mod tests { use super::{DependentNodeStager, OrderedJob, RenderGraphStager, Stage}; use crate::{ render_graph::{Node, NodeId, RenderGraph, ResourceSlotInfo, ResourceSlots}, - render_resource::ResourceInfo, renderer::RenderContext, shader::FieldBindType, }; use legion::prelude::{Resources, World}; diff --git a/crates/pathfinder/Cargo.toml b/crates/pathfinder/Cargo.toml deleted file mode 100644 index db37ef4dcc..0000000000 --- a/crates/pathfinder/Cargo.toml +++ /dev/null @@ -1,70 +0,0 @@ -[workspace] -members = [ - "c", - "canvas", - "color", - "content", - "demo/android/rust", - "demo/common", - "demo/magicleap", - "demo/native", - "examples/canvas_glutin_minimal", - "examples/canvas_metal_minimal", - "examples/canvas_minimal", - "examples/canvas_moire", - "examples/canvas_nanovg", - "examples/canvas_text", - "examples/lottie_basic", - "examples/swf_basic", - "geometry", - "gl", - "gpu", - "lottie", - "export", - "metal", - "renderer", - "resources", - "simd", - "svg", - "swf", - "text", - "ui", - "utils/area-lut", - "utils/gamma-lut", - "utils/svg-to-skia", - "utils/convert", - "webgl", -] - -default-members = [ - "c", - "canvas", - "content", - "demo/common", - "demo/native", - "examples/canvas_glutin_minimal", - "examples/canvas_minimal", - "examples/canvas_moire", - "examples/canvas_text", - "examples/lottie_basic", - "examples/swf_basic", - "geometry", - "gl", - "gpu", - "lottie", - "export", - "renderer", - "simd", - "svg", - "swf", - "text", - "ui", - "utils/area-lut", - "utils/gamma-lut", - "utils/svg-to-skia", - "utils/convert", -] - -[patch.crates-io] -pathfinder_geometry = { path = "geometry" } -pathfinder_simd = { path = "simd" } diff --git a/crates/pathfinder/LICENSE-APACHE b/crates/pathfinder/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/crates/pathfinder/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/crates/pathfinder/LICENSE-MIT b/crates/pathfinder/LICENSE-MIT deleted file mode 100644 index 25597d5838..0000000000 --- a/crates/pathfinder/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2010 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crates/pathfinder/README.md b/crates/pathfinder/README.md deleted file mode 100644 index fc5b25e8da..0000000000 --- a/crates/pathfinder/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# Pathfinder 3 - -![Logo](https://github.com/servo/pathfinder/raw/master/resources/textures/pathfinder-logo.png) - -Pathfinder 3 is a fast, practical, GPU-based rasterizer for fonts and vector graphics using OpenGL -3.0+, OpenGL ES 3.0+, WebGL 2, and Metal. - -Please note that Pathfinder is under heavy development and is incomplete in various areas. - -## Quick start - -Pathfinder contains a library that implements a subset of the -[HTML canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API). You can quickly add -vector rendering to any Rust app with it. The library is available on `crates.io`. See -`examples/canvas_minimal` for a small example of usage. - -### Demos - -Demo app sources are available in [demo/](https://github.com/servo/pathfinder/tree/master/demo). A prebuilt package for Magic Leap can be found in [releases](https://github.com/servo/pathfinder/releases). - -## Features - -The project features: - -* High quality antialiasing. Pathfinder can compute exact fractional trapezoidal area coverage on a - per-pixel basis for the highest-quality antialiasing possible (effectively 256xAA). - -* Fast CPU setup, making full use of parallelism. Pathfinder 3 uses the Rayon library to quickly - perform a CPU tiling prepass to prepare vector scenes for the GPU. This prepass can be pipelined - with the GPU to hide its latency. - -* Fast GPU rendering, even at small pixel sizes. Even on lower-end GPUs, Pathfinder typically - matches or exceeds the performance of the best CPU rasterizers. The difference is particularly - pronounced at large sizes, where Pathfinder regularly achieves multi-factor speedups. - -* GPU compute-based rendering, where available. Pathfinder can optionally use compute shaders to - achieve better performance than what the built-in GPU rasterization hardware can provide. Compute - shader capability is not required, and all features are available without it. - -* Advanced font rendering. Pathfinder can render fonts with slight hinting and can perform subpixel - antialiasing on LCD screens. It can do stem darkening/font dilation like macOS and FreeType in - order to make text easier to read at small sizes. The library also has support for gamma - correction. - -* Support for SVG. Pathfinder 3 is designed to efficiently handle workloads that consist of many - overlapping vector paths, such as those commonly found in SVG and PDF files. It can perform - occlusion culling, which often results in dramatic performance wins over typical software - renderers that use the painter's algorithm. A simple loader that leverages the `resvg` library - to render a subset of SVG is included, so it's easy to get started. - -* 3D capability. Pathfinder can render fonts and vector paths in 3D environments without any loss - in quality. This is intended to be useful for vector-graphics-based user interfaces in VR, for - example. - -* Lightweight. Unlike large vector graphics packages that mix and match many different algorithms, - Pathfinder 3 uses a single, simple technique. It consists of a set of modular crates, so - applications can pick and choose only the components that are necessary to minimize dependencies. - -* Portability to most GPUs manufactured in the last decade, including integrated and mobile GPUs. - Geometry, tessellation, and compute shader functionality is not required. - -## Building - -Pathfinder 3 is a set of modular packages, allowing you to choose which parts of the library you -need. An SVG rendering demo, written in Rust, is included, so you can try Pathfinder out right -away. It also provides an example of how to use the library. (Note that, like the rest of -Pathfinder, the demo is under heavy development and has known bugs.) - -Running the demo is as simple as: - - $ cd demo/native - $ cargo run --release - -Running examples (e.g. `canvas_nanovg`) can be done with: - - $ cd examples/canvas_nanovg - $ cargo run --release - -Pathfinder libraries are available on `crates.io` with the `pathfinder_` prefix (e.g. -`pathfinder_canvas`), but you may wish to use the `master` branch for the latest features and bug -fixes. - -## Community - -There's a Matrix chat room available at -[`#pathfinder:mozilla.org`](https://matrix.to/#/!XiDASQfNTTMrJbXHTw:mozilla.org?via=mozilla.org). -If you're on the Mozilla Matrix server, you can search for Pathfinder to find it. For more -information on connecting to the Matrix network, see -[this `wiki.mozilla.org` page](https://wiki.mozilla.org/Matrix). - -The entire Pathfinder community, including the chat room and GitHub project, is expected to abide -by the same Code of Conduct that the Rust project itself follows. - -## Build status - -[![Build Status](https://travis-ci.org/servo/pathfinder.svg?branch=master)](https://travis-ci.org/servo/pathfinder) - -## Authors - -The primary author is Patrick Walton (@pcwalton), with contributions from the Servo development -community. - -The logo was designed by Jay Vining. - -## License - -Pathfinder is licensed under the same terms as Rust itself. See `LICENSE-APACHE` and `LICENSE-MIT`. - -Material Design icons are copyright Google Inc. and licensed under the Apache 2.0 license. diff --git a/crates/pathfinder/canvas/Cargo.toml b/crates/pathfinder/canvas/Cargo.toml deleted file mode 100644 index b247fcd6b5..0000000000 --- a/crates/pathfinder/canvas/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "pathfinder_canvas" -version = "0.5.0" -edition = "2018" -authors = ["Patrick Walton "] -description = "A GPU-accelerated vector graphics renderer that works like HTML canvas" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" -keywords = ["pathfinder", "canvas", "vector", "graphics", "gpu"] - -[lib] -crate-type = ["rlib", "staticlib"] - -[dependencies] -font-kit = { version = "0.6", optional = true } - -[dependencies.pathfinder_color] -path = "../color" -version = "0.5" - -[dependencies.pathfinder_content] -path = "../content" -version = "0.5" - -[dependencies.pathfinder_geometry] -path = "../geometry" -version = "0.5" - -[dependencies.pathfinder_renderer] -path = "../renderer" -version = "0.5" - -[dependencies.pathfinder_text] -path = "../text" -version = "0.5" -optional = true - -[dependencies.skribo] -version = "0.1" -optional = true - -[features] -pf-text = ["pathfinder_text", "skribo", "font-kit"] diff --git a/crates/pathfinder/canvas/src/lib.rs b/crates/pathfinder/canvas/src/lib.rs deleted file mode 100644 index 4de6a02899..0000000000 --- a/crates/pathfinder/canvas/src/lib.rs +++ /dev/null @@ -1,959 +0,0 @@ -// pathfinder/canvas/src/lib.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A simple API for Pathfinder that mirrors a subset of HTML canvas. - -pub use pathfinder_color::{ColorF, ColorU, rgbaf, rgbau, rgbf, rgbu}; -pub use pathfinder_color::{color_slice_to_u8_slice, u8_slice_to_color_slice, u8_vec_to_color_vec}; -pub use pathfinder_content::fill::FillRule; -pub use pathfinder_content::stroke::LineCap; -pub use pathfinder_content::outline::ArcDirection; -pub use pathfinder_geometry::rect::{RectF, RectI}; -pub use pathfinder_geometry::transform2d::Transform2F; -pub use pathfinder_geometry::vector::{IntoVector2F, Vector2F, Vector2I, vec2f, vec2i}; - -use pathfinder_content::dash::OutlineDash; -use pathfinder_content::effects::{BlendMode, BlurDirection, PatternFilter}; -use pathfinder_content::gradient::Gradient; -use pathfinder_content::outline::{Contour, Outline}; -use pathfinder_content::pattern::Pattern; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_content::stroke::{LineJoin as StrokeLineJoin}; -use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_renderer::paint::{Paint, PaintCompositeOp}; -use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene}; -use std::borrow::Cow; -use std::default::Default; -use std::f32::consts::PI; -use std::f32; -use std::fmt::{Debug, Error as FmtError, Formatter}; -use std::mem; -use std::sync::Arc; - -pub use text::CanvasFontContext; - -#[cfg(feature = "pf-text")] -use skribo::FontCollection; -#[cfg(not(feature = "pf-text"))] -use crate::text::FontCollection; - -#[cfg(feature = "pf-text")] -pub use text::TextMetrics; - -const HAIRLINE_STROKE_WIDTH: f32 = 0.0333; -const DEFAULT_FONT_SIZE: f32 = 10.0; - -#[cfg(feature = "pf-text")] -mod text; - -// For users who don't want text capability, include a tiny convenience stub. -#[cfg(not(feature = "pf-text"))] -mod text { - #[derive(Clone)] - pub struct CanvasFontContext; - - impl CanvasFontContext { - pub fn from_system_source() -> Self { - CanvasFontContext - } - } - - pub struct FontCollection; -} - -#[cfg(test)] -mod tests; - -pub struct Canvas { - scene: Scene, -} - -impl Canvas { - #[inline] - pub fn new(size: Vector2F) -> Canvas { - let mut scene = Scene::new(); - scene.set_view_box(RectF::new(Vector2F::zero(), size)); - Canvas::from_scene(scene) - } - - #[inline] - pub fn from_scene(scene: Scene) -> Canvas { - Canvas { scene } - } - - #[inline] - pub fn into_scene(self) -> Scene { - self.scene - } - - pub fn get_context_2d(self, canvas_font_context: CanvasFontContext) - -> CanvasRenderingContext2D { - #[cfg(feature = "pf-text")] - let default_font_collection = - canvas_font_context.0.borrow().default_font_collection.clone(); - #[cfg(not(feature = "pf-text"))] - let default_font_collection = Arc::new(FontCollection); - CanvasRenderingContext2D { - canvas: self, - current_state: State::default(default_font_collection), - saved_states: vec![], - canvas_font_context, - } - } - - #[inline] - pub fn size(&self) -> Vector2I { - self.scene.view_box().size().ceil().to_i32() - } -} - -pub struct CanvasRenderingContext2D { - canvas: Canvas, - current_state: State, - saved_states: Vec, - #[allow(dead_code)] - canvas_font_context: CanvasFontContext, -} - -impl CanvasRenderingContext2D { - // Canvas accessors - - #[inline] - pub fn canvas(&self) -> &Canvas { - &self.canvas - } - - #[inline] - pub fn into_canvas(self) -> Canvas { - self.canvas - } - - // Drawing rectangles - - #[inline] - pub fn fill_rect(&mut self, rect: RectF) { - let mut path = Path2D::new(); - path.rect(rect); - self.fill_path(path, FillRule::Winding); - } - - #[inline] - pub fn stroke_rect(&mut self, rect: RectF) { - let mut path = Path2D::new(); - path.rect(rect); - self.stroke_path(path); - } - - pub fn clear_rect(&mut self, rect: RectF) { - let mut path = Path2D::new(); - path.rect(rect); - - let paint = Paint::transparent_black(); - let paint = self.current_state.resolve_paint(&paint); - let paint_id = self.canvas.scene.push_paint(&paint); - - let mut outline = path.into_outline(); - outline.transform(&self.current_state.transform); - - let mut path = DrawPath::new(outline, paint_id); - path.set_blend_mode(BlendMode::Clear); - self.canvas.scene.push_path(path); - } - - // Line styles - - #[inline] - pub fn set_line_width(&mut self, new_line_width: f32) { - self.current_state.line_width = new_line_width - } - - #[inline] - pub fn set_line_cap(&mut self, new_line_cap: LineCap) { - self.current_state.line_cap = new_line_cap - } - - #[inline] - pub fn set_line_join(&mut self, new_line_join: LineJoin) { - self.current_state.line_join = new_line_join - } - - #[inline] - pub fn set_miter_limit(&mut self, new_miter_limit: f32) { - self.current_state.miter_limit = new_miter_limit - } - - #[inline] - pub fn set_line_dash(&mut self, mut new_line_dash: Vec) { - // Duplicate and concatenate if an odd number of dashes are present. - if new_line_dash.len() % 2 == 1 { - let mut real_line_dash = new_line_dash.clone(); - real_line_dash.extend(new_line_dash.into_iter()); - new_line_dash = real_line_dash; - } - - self.current_state.line_dash = new_line_dash - } - - #[inline] - pub fn set_line_dash_offset(&mut self, new_line_dash_offset: f32) { - self.current_state.line_dash_offset = new_line_dash_offset - } - - // Fill and stroke styles - - #[inline] - pub fn set_fill_style(&mut self, new_fill_style: FS) where FS: Into { - self.current_state.fill_paint = new_fill_style.into().into_paint(); - } - - #[inline] - pub fn set_stroke_style(&mut self, new_stroke_style: FS) where FS: Into { - self.current_state.stroke_paint = new_stroke_style.into().into_paint(); - } - - // Shadows - - #[inline] - pub fn shadow_blur(&self) -> f32 { - self.current_state.shadow_blur - } - - #[inline] - pub fn set_shadow_blur(&mut self, new_shadow_blur: f32) { - self.current_state.shadow_blur = new_shadow_blur; - } - - #[inline] - pub fn shadow_color(&self) -> ColorU { - self.current_state.shadow_color - } - - #[inline] - pub fn set_shadow_color(&mut self, new_shadow_color: ColorU) { - self.current_state.shadow_color = new_shadow_color; - } - - #[inline] - pub fn shadow_offset(&self) -> Vector2F { - self.current_state.shadow_offset - } - - #[inline] - pub fn set_shadow_offset(&mut self, new_shadow_offset: Vector2F) { - self.current_state.shadow_offset = new_shadow_offset; - } - - // Drawing paths - - #[inline] - pub fn fill_path(&mut self, path: Path2D, fill_rule: FillRule) { - self.push_path(path.into_outline(), PathOp::Fill, fill_rule); - } - - #[inline] - pub fn stroke_path(&mut self, path: Path2D) { - let mut stroke_style = self.current_state.resolve_stroke_style(); - - // The smaller scale is relevant here, as we multiply by it and want to ensure it is always - // bigger than `HAIRLINE_STROKE_WIDTH`. - let transform_scales = self.current_state.transform.extract_scale(); - let transform_scale = f32::min(transform_scales.x(), transform_scales.y()); - - // Avoid the division in the normal case of sufficient thickness. - if stroke_style.line_width * transform_scale < HAIRLINE_STROKE_WIDTH { - stroke_style.line_width = HAIRLINE_STROKE_WIDTH / transform_scale; - } - - let mut outline = path.into_outline(); - if !self.current_state.line_dash.is_empty() { - let mut dash = OutlineDash::new(&outline, - &self.current_state.line_dash, - self.current_state.line_dash_offset); - dash.dash(); - outline = dash.into_outline(); - } - - let mut stroke_to_fill = OutlineStrokeToFill::new(&outline, stroke_style); - stroke_to_fill.offset(); - outline = stroke_to_fill.into_outline(); - - self.push_path(outline, PathOp::Stroke, FillRule::Winding); - } - - pub fn clip_path(&mut self, path: Path2D, fill_rule: FillRule) { - let mut outline = path.into_outline(); - outline.transform(&self.current_state.transform); - - let mut clip_path = ClipPath::new(outline); - clip_path.set_fill_rule(fill_rule); - let clip_path_id = self.canvas.scene.push_clip_path(clip_path); - - self.current_state.clip_path = Some(clip_path_id); - } - - fn push_path(&mut self, mut outline: Outline, path_op: PathOp, fill_rule: FillRule) { - let paint = self.current_state.resolve_paint(match path_op { - PathOp::Fill => &self.current_state.fill_paint, - PathOp::Stroke => &self.current_state.stroke_paint, - }); - let paint_id = self.canvas.scene.push_paint(&paint); - - let transform = self.current_state.transform; - let clip_path = self.current_state.clip_path; - let blend_mode = self.current_state.global_composite_operation.to_blend_mode(); - - outline.transform(&transform); - - if !self.current_state.shadow_color.is_fully_transparent() { - let mut outline = outline.clone(); - outline.transform(&Transform2F::from_translation(self.current_state.shadow_offset)); - - let shadow_blur_info = - push_shadow_blur_render_targets_if_needed(&mut self.canvas.scene, - &self.current_state, - outline.bounds()); - - if let Some(ref shadow_blur_info) = shadow_blur_info { - outline.transform(&Transform2F::from_translation(-shadow_blur_info.bounds - .origin() - .to_f32())); - } - - // Per spec the shadow must respect the alpha of the shadowed path, but otherwise have - // the color of the shadow paint. - let mut shadow_paint = (*paint).clone(); - let shadow_base_alpha = shadow_paint.base_color().a; - let mut shadow_color = self.current_state.shadow_color.to_f32(); - shadow_color.set_a(shadow_color.a() * shadow_base_alpha as f32 / 255.0); - shadow_paint.set_base_color(shadow_color.to_u8()); - if let &mut Some(ref mut shadow_paint_overlay) = shadow_paint.overlay_mut() { - shadow_paint_overlay.set_composite_op(PaintCompositeOp::DestIn); - } - let shadow_paint_id = self.canvas.scene.push_paint(&shadow_paint); - - let mut path = DrawPath::new(outline, shadow_paint_id); - if shadow_blur_info.is_none() { - path.set_clip_path(clip_path); - } - path.set_fill_rule(fill_rule); - path.set_blend_mode(blend_mode); - self.canvas.scene.push_path(path); - - composite_shadow_blur_render_targets_if_needed(&mut self.canvas.scene, - shadow_blur_info, - clip_path); - } - - let mut path = DrawPath::new(outline, paint_id); - path.set_clip_path(clip_path); - path.set_fill_rule(fill_rule); - path.set_blend_mode(blend_mode); - self.canvas.scene.push_path(path); - - fn push_shadow_blur_render_targets_if_needed(scene: &mut Scene, - current_state: &State, - outline_bounds: RectF) - -> Option { - if current_state.shadow_blur == 0.0 { - return None; - } - - let sigma = current_state.shadow_blur * 0.5; - let bounds = outline_bounds.dilate(sigma * 3.0).round_out().to_i32(); - - let render_target_y = RenderTarget::new(bounds.size(), String::new()); - let render_target_id_y = scene.push_render_target(render_target_y); - let render_target_x = RenderTarget::new(bounds.size(), String::new()); - let render_target_id_x = scene.push_render_target(render_target_x); - - Some(ShadowBlurRenderTargetInfo { - id_x: render_target_id_x, - id_y: render_target_id_y, - bounds, - sigma, - }) - } - - fn composite_shadow_blur_render_targets_if_needed(scene: &mut Scene, - info: Option, - clip_path: Option) { - let info = match info { - None => return, - Some(info) => info, - }; - - let mut paint_x = Pattern::from_render_target(info.id_x, info.bounds.size()); - let mut paint_y = Pattern::from_render_target(info.id_y, info.bounds.size()); - paint_y.apply_transform(Transform2F::from_translation(info.bounds.origin().to_f32())); - - let sigma = info.sigma; - paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma })); - paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma })); - - let paint_id_x = scene.push_paint(&Paint::from_pattern(paint_x)); - let paint_id_y = scene.push_paint(&Paint::from_pattern(paint_y)); - - // TODO(pcwalton): Apply clip as necessary. - let outline_x = Outline::from_rect(RectF::new(vec2f(0.0, 0.0), - info.bounds.size().to_f32())); - let path_x = DrawPath::new(outline_x, paint_id_x); - let outline_y = Outline::from_rect(info.bounds.to_f32()); - let mut path_y = DrawPath::new(outline_y, paint_id_y); - path_y.set_clip_path(clip_path); - - scene.pop_render_target(); - scene.push_path(path_x); - scene.pop_render_target(); - scene.push_path(path_y); - } - - } - - // Transformations - - #[inline] - pub fn rotate(&mut self, angle: f32) { - self.current_state.transform *= Transform2F::from_rotation(angle) - } - - #[inline] - pub fn scale(&mut self, scale: S) where S: IntoVector2F { - self.current_state.transform *= Transform2F::from_scale(scale) - } - - #[inline] - pub fn translate(&mut self, offset: Vector2F) { - self.current_state.transform *= Transform2F::from_translation(offset) - } - - #[inline] - pub fn transform(&self) -> Transform2F { - self.current_state.transform - } - - #[inline] - pub fn set_transform(&mut self, new_transform: &Transform2F) { - self.current_state.transform = *new_transform; - } - - #[inline] - pub fn reset_transform(&mut self) { - self.current_state.transform = Transform2F::default(); - } - - // Compositing - - #[inline] - pub fn global_alpha(&self) -> f32 { - self.current_state.global_alpha - } - - #[inline] - pub fn set_global_alpha(&mut self, new_global_alpha: f32) { - self.current_state.global_alpha = new_global_alpha; - } - - #[inline] - pub fn global_composite_operation(&self) -> CompositeOperation { - self.current_state.global_composite_operation - } - - #[inline] - pub fn set_global_composite_operation(&mut self, new_composite_operation: CompositeOperation) { - self.current_state.global_composite_operation = new_composite_operation; - } - - // Drawing images - - #[inline] - pub fn draw_image(&mut self, image: I, dest_location: L) - where I: CanvasImageSource, L: CanvasImageDestLocation { - let pattern = image.to_pattern(self, Transform2F::default()); - let src_rect = RectF::new(vec2f(0.0, 0.0), pattern.size().to_f32()); - self.draw_subimage(pattern, src_rect, dest_location) - } - - pub fn draw_subimage(&mut self, image: I, src_location: RectF, dest_location: L) - where I: CanvasImageSource, L: CanvasImageDestLocation { - let dest_size = dest_location.size().unwrap_or(src_location.size()); - let scale = dest_size / src_location.size(); - let offset = dest_location.origin() - src_location.origin(); - let transform = Transform2F::from_scale(scale).translate(offset); - - let pattern = image.to_pattern(self, transform); - let old_fill_paint = self.current_state.fill_paint.clone(); - self.set_fill_style(pattern); - self.fill_rect(RectF::new(dest_location.origin(), dest_size)); - self.current_state.fill_paint = old_fill_paint; - } - - // Image smoothing - - #[inline] - pub fn image_smoothing_enabled(&self) -> bool { - self.current_state.image_smoothing_enabled - } - - #[inline] - pub fn set_image_smoothing_enabled(&mut self, enabled: bool) { - self.current_state.image_smoothing_enabled = enabled - } - - #[inline] - pub fn image_smoothing_quality(&self) -> ImageSmoothingQuality { - self.current_state.image_smoothing_quality - } - - #[inline] - pub fn set_image_smoothing_quality(&mut self, new_quality: ImageSmoothingQuality) { - self.current_state.image_smoothing_quality = new_quality - } - - // The canvas state - - #[inline] - pub fn save(&mut self) { - self.saved_states.push(self.current_state.clone()); - } - - #[inline] - pub fn restore(&mut self) { - if let Some(state) = self.saved_states.pop() { - self.current_state = state; - } - } - - // Extensions - - pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F) - -> Pattern { - let subscene_size = canvas.size(); - let subscene = canvas.into_scene(); - let render_target = RenderTarget::new(subscene_size, String::new()); - let render_target_id = self.canvas.scene.push_render_target(render_target); - self.canvas.scene.append_scene(subscene); - self.canvas.scene.pop_render_target(); - - let mut pattern = Pattern::from_render_target(render_target_id, subscene_size); - pattern.apply_transform(transform); - pattern - } -} - -#[derive(Clone)] -struct State { - transform: Transform2F, - font_collection: Arc, - font_size: f32, - line_width: f32, - line_cap: LineCap, - line_join: LineJoin, - miter_limit: f32, - line_dash: Vec, - line_dash_offset: f32, - fill_paint: Paint, - stroke_paint: Paint, - shadow_color: ColorU, - shadow_blur: f32, - shadow_offset: Vector2F, - text_align: TextAlign, - text_baseline: TextBaseline, - image_smoothing_enabled: bool, - image_smoothing_quality: ImageSmoothingQuality, - global_alpha: f32, - global_composite_operation: CompositeOperation, - clip_path: Option, -} - -impl State { - fn default(default_font_collection: Arc) -> State { - State { - transform: Transform2F::default(), - font_collection: default_font_collection, - font_size: DEFAULT_FONT_SIZE, - line_width: 1.0, - line_cap: LineCap::Butt, - line_join: LineJoin::Miter, - miter_limit: 10.0, - line_dash: vec![], - line_dash_offset: 0.0, - fill_paint: Paint::black(), - stroke_paint: Paint::black(), - shadow_color: ColorU::transparent_black(), - shadow_blur: 0.0, - shadow_offset: Vector2F::zero(), - text_align: TextAlign::Left, - text_baseline: TextBaseline::Alphabetic, - image_smoothing_enabled: true, - image_smoothing_quality: ImageSmoothingQuality::Low, - global_alpha: 1.0, - global_composite_operation: CompositeOperation::SourceOver, - clip_path: None, - } - } - - fn resolve_paint<'a>(&self, paint: &'a Paint) -> Cow<'a, Paint> { - let mut must_copy = !self.transform.is_identity() || self.global_alpha < 1.0; - if !must_copy { - if let Some(ref pattern) = paint.pattern() { - must_copy = self.image_smoothing_enabled != pattern.smoothing_enabled() - } - } - - if !must_copy { - return Cow::Borrowed(paint); - } - - let mut paint = (*paint).clone(); - paint.apply_transform(&self.transform); - - let mut base_color = paint.base_color().to_f32(); - base_color.set_a(base_color.a() * self.global_alpha); - paint.set_base_color(base_color.to_u8()); - - if let Some(ref mut pattern) = paint.pattern_mut() { - pattern.set_smoothing_enabled(self.image_smoothing_enabled); - } - Cow::Owned(paint) - } - - fn resolve_stroke_style(&self) -> StrokeStyle { - StrokeStyle { - line_width: self.line_width, - line_cap: self.line_cap, - line_join: match self.line_join { - LineJoin::Miter => StrokeLineJoin::Miter(self.miter_limit), - LineJoin::Bevel => StrokeLineJoin::Bevel, - LineJoin::Round => StrokeLineJoin::Round, - }, - } - } -} - -#[derive(Clone)] -pub struct Path2D { - outline: Outline, - current_contour: Contour, -} - -impl Path2D { - #[inline] - pub fn new() -> Path2D { - Path2D { outline: Outline::new(), current_contour: Contour::new() } - } - - #[inline] - pub fn close_path(&mut self) { - self.current_contour.close(); - } - - #[inline] - pub fn move_to(&mut self, to: Vector2F) { - // TODO(pcwalton): Cull degenerate contours. - self.flush_current_contour(); - self.current_contour.push_endpoint(to); - } - - #[inline] - pub fn line_to(&mut self, to: Vector2F) { - self.current_contour.push_endpoint(to); - } - - #[inline] - pub fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) { - self.current_contour.push_quadratic(ctrl, to); - } - - #[inline] - pub fn bezier_curve_to(&mut self, ctrl0: Vector2F, ctrl1: Vector2F, to: Vector2F) { - self.current_contour.push_cubic(ctrl0, ctrl1, to); - } - - #[inline] - pub fn arc(&mut self, - center: Vector2F, - radius: f32, - start_angle: f32, - end_angle: f32, - direction: ArcDirection) { - let transform = Transform2F::from_scale(radius).translate(center); - self.current_contour.push_arc(&transform, start_angle, end_angle, direction); - } - - #[inline] - pub fn arc_to(&mut self, ctrl: Vector2F, to: Vector2F, radius: f32) { - // FIXME(pcwalton): What should we do if there's no initial point? - let from = self.current_contour.last_position().unwrap_or_default(); - let (v0, v1) = (from - ctrl, to - ctrl); - let (vu0, vu1) = (v0.normalize(), v1.normalize()); - let hypot = radius / f32::sqrt(0.5 * (1.0 - vu0.dot(vu1))); - let bisector = vu0 + vu1; - let center = ctrl + bisector * (hypot / bisector.length()); - - let transform = Transform2F::from_scale(radius).translate(center); - let chord = LineSegment2F::new(vu0.yx() * vec2f(-1.0, 1.0), vu1.yx() * vec2f( 1.0, -1.0)); - - // FIXME(pcwalton): Is clockwise direction correct? - self.current_contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); - } - - pub fn rect(&mut self, rect: RectF) { - self.flush_current_contour(); - self.current_contour.push_endpoint(rect.origin()); - self.current_contour.push_endpoint(rect.upper_right()); - self.current_contour.push_endpoint(rect.lower_right()); - self.current_contour.push_endpoint(rect.lower_left()); - self.current_contour.close(); - } - - pub fn ellipse(&mut self, - center: Vector2F, - axes: A, - rotation: f32, - start_angle: f32, - end_angle: f32) - where A: IntoVector2F { - self.flush_current_contour(); - - let transform = Transform2F::from_scale(axes).rotate(rotation).translate(center); - self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW); - - if end_angle - start_angle >= 2.0 * PI { - self.current_contour.close(); - } - } - - // https://html.spec.whatwg.org/multipage/canvas.html#dom-path2d-addpath - pub fn add_path(&mut self, mut path: Path2D, transform: &Transform2F) { - self.flush_current_contour(); - path.flush_current_contour(); - path.outline.transform(transform); - let last_contour = path.outline.pop_contour(); - for contour in path.outline.into_contours() { - self.outline.push_contour(contour); - } - self.current_contour = last_contour.unwrap_or_else(Contour::new); - } - - pub fn into_outline(mut self) -> Outline { - self.flush_current_contour(); - self.outline - } - - fn flush_current_contour(&mut self) { - if !self.current_contour.is_empty() { - self.outline.push_contour(mem::replace(&mut self.current_contour, Contour::new())); - } - } -} - -#[derive(Clone)] -pub enum FillStyle { - Color(ColorU), - Gradient(Gradient), - Pattern(Pattern), -} - -impl FillStyle { - fn into_paint(self) -> Paint { - match self { - FillStyle::Color(color) => Paint::from_color(color), - FillStyle::Gradient(gradient) => Paint::from_gradient(gradient), - FillStyle::Pattern(pattern) => Paint::from_pattern(pattern), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum TextAlign { - Left, - Right, - Center, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum TextBaseline { - Alphabetic, - Top, - Hanging, - Middle, - Ideographic, - Bottom, -} - -// We duplicate `pathfinder_content::stroke::LineJoin` here because the HTML canvas API treats the -// miter limit as part of the canvas state, while the native Pathfinder API treats the miter limit -// as part of the line join. Pathfinder's choice is more logical, because the miter limit is -// specific to miter joins. In this API, however, for compatibility we go with the HTML canvas -// semantics. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LineJoin { - Miter, - Bevel, - Round, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CompositeOperation { - SourceOver, - SourceIn, - SourceOut, - SourceAtop, - DestinationOver, - DestinationIn, - DestinationOut, - DestinationAtop, - Lighter, - Copy, - Xor, - Multiply, - Screen, - Overlay, - Darken, - Lighten, - ColorDodge, - ColorBurn, - HardLight, - SoftLight, - Difference, - Exclusion, - Hue, - Saturation, - Color, - Luminosity, -} - -impl CompositeOperation { - fn to_blend_mode(self) -> BlendMode { - match self { - CompositeOperation::Copy => BlendMode::Copy, - CompositeOperation::SourceAtop => BlendMode::SrcAtop, - CompositeOperation::DestinationOver => BlendMode::DestOver, - CompositeOperation::DestinationOut => BlendMode::DestOut, - CompositeOperation::Xor => BlendMode::Xor, - CompositeOperation::Lighter => BlendMode::Lighter, - CompositeOperation::Multiply => BlendMode::Multiply, - CompositeOperation::Screen => BlendMode::Screen, - CompositeOperation::Overlay => BlendMode::Overlay, - CompositeOperation::Darken => BlendMode::Darken, - CompositeOperation::Lighten => BlendMode::Lighten, - CompositeOperation::ColorDodge => BlendMode::ColorDodge, - CompositeOperation::ColorBurn => BlendMode::ColorBurn, - CompositeOperation::HardLight => BlendMode::HardLight, - CompositeOperation::SoftLight => BlendMode::SoftLight, - CompositeOperation::Difference => BlendMode::Difference, - CompositeOperation::Exclusion => BlendMode::Exclusion, - CompositeOperation::Hue => BlendMode::Hue, - CompositeOperation::Saturation => BlendMode::Saturation, - CompositeOperation::Color => BlendMode::Color, - CompositeOperation::Luminosity => BlendMode::Luminosity, - CompositeOperation::SourceOver => BlendMode::SrcOver, - CompositeOperation::SourceIn => BlendMode::SrcIn, - CompositeOperation::SourceOut => BlendMode::SrcOut, - CompositeOperation::DestinationIn => BlendMode::DestIn, - CompositeOperation::DestinationAtop => BlendMode::DestAtop, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ImageSmoothingQuality { - Low, - Medium, - High, -} - -pub trait CanvasImageSource { - fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F) - -> Pattern; -} - -pub trait CanvasImageDestLocation { - fn origin(&self) -> Vector2F; - fn size(&self) -> Option; -} - -impl CanvasImageSource for Pattern { - #[inline] - fn to_pattern(mut self, _: &mut CanvasRenderingContext2D, transform: Transform2F) -> Pattern { - self.apply_transform(transform); - self - } -} - -impl CanvasImageSource for Canvas { - #[inline] - fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F) - -> Pattern { - dest_context.create_pattern_from_canvas(self, transform) - } -} - -impl CanvasImageDestLocation for RectF { - #[inline] - fn origin(&self) -> Vector2F { - RectF::origin(*self) - } - #[inline] - fn size(&self) -> Option { - Some(RectF::size(*self)) - } -} - -impl CanvasImageDestLocation for Vector2F { - #[inline] - fn origin(&self) -> Vector2F { - *self - } - #[inline] - fn size(&self) -> Option { - None - } -} - -impl Debug for Path2D { - fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> { - self.clone().into_outline().fmt(formatter) - } -} - -impl From for FillStyle { - #[inline] - fn from(color: ColorU) -> FillStyle { - FillStyle::Color(color) - } -} - -impl From for FillStyle { - #[inline] - fn from(gradient: Gradient) -> FillStyle { - FillStyle::Gradient(gradient) - } -} - -impl From for FillStyle { - #[inline] - fn from(pattern: Pattern) -> FillStyle { - FillStyle::Pattern(pattern) - } -} - -struct ShadowBlurRenderTargetInfo { - id_x: RenderTargetId, - id_y: RenderTargetId, - bounds: RectI, - sigma: f32, -} - -enum PathOp { - Fill, - Stroke, -} diff --git a/crates/pathfinder/canvas/src/tests.rs b/crates/pathfinder/canvas/src/tests.rs deleted file mode 100644 index 6aad48183a..0000000000 --- a/crates/pathfinder/canvas/src/tests.rs +++ /dev/null @@ -1,19 +0,0 @@ -// pathfinder/canvas/src/tests.rs -// -// For this file only, any copyright is dedicated to the Public Domain. -// https://creativecommons.org/publicdomain/zero/1.0/ - -use pathfinder_geometry::vector::{Vector2F, vec2f}; -use super::Path2D; - -#[test] -pub fn test_path2d_formatting() { - let mut path = Path2D::new(); - path.move_to(vec2f(0.0, 1.0)); - path.line_to(vec2f(2.0, 3.0)); - assert_eq!(format!("{:?}", path), "M 0 1 L 2 3"); - path.line_to(vec2f(4.0, 5.0)); - assert_eq!(format!("{:?}", path), "M 0 1 L 2 3 L 4 5"); - path.close_path(); - assert_eq!(format!("{:?}", path), "M 0 1 L 2 3 L 4 5 z"); -} diff --git a/crates/pathfinder/canvas/src/text.rs b/crates/pathfinder/canvas/src/text.rs deleted file mode 100644 index 44af8a19d2..0000000000 --- a/crates/pathfinder/canvas/src/text.rs +++ /dev/null @@ -1,507 +0,0 @@ -// pathfinder/canvas/src/text.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::{CanvasRenderingContext2D, State, TextAlign, TextBaseline}; -use font_kit::canvas::RasterizationOptions; -use font_kit::family_name::FamilyName; -use font_kit::handle::Handle; -use font_kit::hinting::HintingOptions; -use font_kit::loaders::default::Font; -use font_kit::properties::Properties; -use font_kit::source::{Source, SystemSource}; -use font_kit::sources::mem::MemSource; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::util; -use pathfinder_geometry::vector::{Vector2F, vec2f}; -use pathfinder_renderer::paint::PaintId; -use pathfinder_text::{FontContext, FontRenderOptions, TextRenderMode}; -use skribo::{FontCollection, FontFamily, FontRef, Layout, TextStyle}; -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::Arc; - -impl CanvasRenderingContext2D { - pub fn fill_text(&mut self, string: &str, position: Vector2F) { - let paint = self.current_state.resolve_paint(&self.current_state.fill_paint); - let paint_id = self.canvas.scene.push_paint(&paint); - self.fill_or_stroke_text(string, position, paint_id, TextRenderMode::Fill); - } - - pub fn stroke_text(&mut self, string: &str, position: Vector2F) { - let paint = self.current_state.resolve_paint(&self.current_state.stroke_paint); - let paint_id = self.canvas.scene.push_paint(&paint); - let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style()); - self.fill_or_stroke_text(string, position, paint_id, render_mode); - } - - pub fn measure_text(&self, string: &str) -> TextMetrics { - let mut metrics = self.layout_text(string).metrics(); - metrics.make_origin_relative(&self.current_state); - metrics - } - - pub fn fill_layout(&mut self, layout: &Layout, transform: Transform2F) { - let paint_id = self.canvas.scene.push_paint(&self.current_state.fill_paint); - - let clip_path = self.current_state.clip_path; - let blend_mode = self.current_state.global_composite_operation.to_blend_mode(); - - // TODO(pcwalton): Report errors. - drop(self.canvas_font_context - .0 - .borrow_mut() - .font_context - .push_layout(&mut self.canvas.scene, - &layout, - &TextStyle { size: self.current_state.font_size }, - &FontRenderOptions { - transform: transform * self.current_state.transform, - render_mode: TextRenderMode::Fill, - hinting_options: HintingOptions::None, - clip_path, - blend_mode, - paint_id, - })); - } - - fn fill_or_stroke_text(&mut self, - string: &str, - mut position: Vector2F, - paint_id: PaintId, - render_mode: TextRenderMode) { - let layout = self.layout_text(string); - - let clip_path = self.current_state.clip_path; - let blend_mode = self.current_state.global_composite_operation.to_blend_mode(); - - position += layout.metrics().text_origin(&self.current_state); - let transform = self.current_state.transform * Transform2F::from_translation(position); - - // TODO(pcwalton): Report errors. - drop(self.canvas_font_context - .0 - .borrow_mut() - .font_context - .push_layout(&mut self.canvas.scene, - &layout, - &TextStyle { size: self.current_state.font_size }, - &FontRenderOptions { - transform, - render_mode, - hinting_options: HintingOptions::None, - clip_path, - blend_mode, - paint_id, - })); - } - - fn layout_text(&self, string: &str) -> Layout { - skribo::layout(&TextStyle { size: self.current_state.font_size }, - &self.current_state.font_collection, - string) - } - - // Text styles - - #[inline] - pub fn font(&self) -> Arc { - self.current_state.font_collection.clone() - } - - #[inline] - pub fn set_font(&mut self, font_collection: FC) where FC: IntoFontCollection { - let font_collection = font_collection.into_font_collection(&self.canvas_font_context); - self.current_state.font_collection = font_collection; - } - - #[inline] - pub fn font_size(&self) -> f32 { - self.current_state.font_size - } - - #[inline] - pub fn set_font_size(&mut self, new_font_size: f32) { - self.current_state.font_size = new_font_size; - } - - #[inline] - pub fn text_align(&self) -> TextAlign { - self.current_state.text_align - } - - #[inline] - pub fn set_text_align(&mut self, new_text_align: TextAlign) { - self.current_state.text_align = new_text_align; - } - - #[inline] - pub fn text_baseline(&self) -> TextBaseline { - self.current_state.text_baseline - } - - #[inline] - pub fn set_text_baseline(&mut self, new_text_baseline: TextBaseline) { - self.current_state.text_baseline = new_text_baseline; - } -} - -/// Represents the dimensions of a piece of text in the canvas. -#[derive(Clone, Copy, Debug)] -pub struct TextMetrics { - /// The calculated width of a segment of inline text in pixels. - pub width: f32, - /// The distance from the alignment point given by the `text_align` state to the left side of - /// the bounding rectangle of the given text, in pixels. The distance is measured parallel to - /// the baseline. - pub actual_bounding_box_left: f32, - /// The distance from the alignment point given by the `text_align` state to the right side of - /// the bounding rectangle of the given text, in pixels. The distance is measured parallel to - /// the baseline. - pub actual_bounding_box_right: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the top of - /// the highest bounding rectangle of all the fonts used to render the text, in pixels. - pub font_bounding_box_ascent: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the bottom - /// of the highest bounding rectangle of all the fonts used to render the text, in pixels. - pub font_bounding_box_descent: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the top of - /// the bounding rectangle used to render the text, in pixels. - pub actual_bounding_box_ascent: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the bottom - /// of the bounding rectangle used to render the text, in pixels. - pub actual_bounding_box_descent: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the top of - /// the em square in the line box, in pixels. - pub em_height_ascent: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the bottom - /// of the em square in the line box, in pixels. - pub em_height_descent: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the hanging - /// baseline of the line box, in pixels. - pub hanging_baseline: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the - /// alphabetic baseline of the line box, in pixels. - pub alphabetic_baseline: f32, - /// The distance from the horizontal line indicated by the `text_baseline` state to the - /// ideographic baseline of the line box, in pixels. - pub ideographic_baseline: f32, -} - -#[cfg(feature = "pf-text")] -#[derive(Clone)] -pub struct CanvasFontContext(pub(crate) Rc>); - -pub(super) struct CanvasFontContextData { - pub(super) font_context: FontContext, - #[allow(dead_code)] - pub(super) font_source: Arc, - #[allow(dead_code)] - pub(super) default_font_collection: Arc, -} - -impl CanvasFontContext { - pub fn new(font_source: Arc) -> CanvasFontContext { - let mut default_font_collection = FontCollection::new(); - if let Ok(default_font) = font_source.select_best_match(&[FamilyName::SansSerif], - &Properties::new()) { - if let Ok(default_font) = default_font.load() { - default_font_collection.add_family(FontFamily::new_from_font(default_font)); - } - } - - CanvasFontContext(Rc::new(RefCell::new(CanvasFontContextData { - font_source, - default_font_collection: Arc::new(default_font_collection), - font_context: FontContext::new(), - }))) - } - - /// A convenience method to create a font context with the system source. - /// This allows usage of fonts installed on the system. - pub fn from_system_source() -> CanvasFontContext { - CanvasFontContext::new(Arc::new(SystemSource::new())) - } - - /// A convenience method to create a font context with a set of in-memory fonts. - pub fn from_fonts(fonts: I) -> CanvasFontContext where I: Iterator { - CanvasFontContext::new(Arc::new(MemSource::from_fonts(fonts).unwrap())) - } - - fn get_font_by_postscript_name(&self, postscript_name: &str) -> Font { - let this = self.0.borrow(); - if let Some(cached_font) = this.font_context.get_cached_font(postscript_name) { - return (*cached_font).clone(); - } - this.font_source - .select_by_postscript_name(postscript_name) - .expect("Couldn't find a font with that PostScript name!") - .load() - .expect("Failed to load the font!") - } -} - -// Text layout utilities - -impl TextMetrics { - fn text_origin(&self, state: &State) -> Vector2F { - let x = match state.text_align { - TextAlign::Left => 0.0, - TextAlign::Right => -self.width, - TextAlign::Center => -0.5 * self.width, - }; - - let y = match state.text_baseline { - TextBaseline::Alphabetic => 0.0, - TextBaseline::Top => self.em_height_ascent, - TextBaseline::Middle => util::lerp(self.em_height_ascent, self.em_height_descent, 0.5), - TextBaseline::Bottom => self.em_height_descent, - TextBaseline::Ideographic => self.ideographic_baseline, - TextBaseline::Hanging => self.hanging_baseline, - }; - - vec2f(x, y) - } - - fn make_origin_relative(&mut self, state: &State) { - let text_origin = self.text_origin(state); - self.actual_bounding_box_left += text_origin.x(); - self.actual_bounding_box_right += text_origin.x(); - self.font_bounding_box_ascent -= text_origin.y(); - self.font_bounding_box_descent -= text_origin.y(); - self.actual_bounding_box_ascent -= text_origin.y(); - self.actual_bounding_box_descent -= text_origin.y(); - self.em_height_ascent -= text_origin.y(); - self.em_height_descent -= text_origin.y(); - self.hanging_baseline -= text_origin.y(); - self.alphabetic_baseline -= text_origin.y(); - self.ideographic_baseline -= text_origin.y(); - } -} - -pub trait LayoutExt { - fn metrics(&self) -> TextMetrics; - fn width(&self) -> f32; - fn actual_bounding_box_left(&self) -> f32; - fn actual_bounding_box_right(&self) -> f32; - fn hanging_baseline(&self) -> f32; - fn ideographic_baseline(&self) -> f32; -} - -impl LayoutExt for Layout { - // NB: This does not return origin-relative values. To get those, call `make_origin_relative()` - // afterward. - fn metrics(&self) -> TextMetrics { - let (mut em_height_ascent, mut em_height_descent) = (0.0, 0.0); - let (mut font_bounding_box_ascent, mut font_bounding_box_descent) = (0.0, 0.0); - let (mut actual_bounding_box_ascent, mut actual_bounding_box_descent) = (0.0, 0.0); - - let mut last_font: Option> = None; - for glyph in &self.glyphs { - match last_font { - Some(ref last_font) if Arc::ptr_eq(&last_font, &glyph.font.font) => {} - _ => { - let font = glyph.font.font.clone(); - - let font_metrics = font.metrics(); - let scale_factor = self.size / font_metrics.units_per_em as f32; - em_height_ascent = (font_metrics.ascent * scale_factor).max(em_height_ascent); - em_height_descent = - (font_metrics.descent * scale_factor).min(em_height_descent); - font_bounding_box_ascent = (font_metrics.bounding_box.max_y() * - scale_factor).max(font_bounding_box_ascent); - font_bounding_box_descent = (font_metrics.bounding_box.min_y() * - scale_factor).min(font_bounding_box_descent); - - last_font = Some(font); - } - } - - let font = last_font.as_ref().unwrap(); - let glyph_rect = font.raster_bounds(glyph.glyph_id, - self.size, - Transform2F::default(), - HintingOptions::None, - RasterizationOptions::GrayscaleAa).unwrap(); - actual_bounding_box_ascent = - (glyph_rect.max_y() as f32).max(actual_bounding_box_ascent); - actual_bounding_box_descent = - (glyph_rect.min_y() as f32).min(actual_bounding_box_descent); - } - - TextMetrics { - width: self.width(), - actual_bounding_box_left: self.actual_bounding_box_left(), - actual_bounding_box_right: self.actual_bounding_box_right(), - font_bounding_box_ascent, - font_bounding_box_descent, - actual_bounding_box_ascent, - actual_bounding_box_descent, - em_height_ascent, - em_height_descent, - alphabetic_baseline: 0.0, - hanging_baseline: self.hanging_baseline(), - ideographic_baseline: self.ideographic_baseline(), - } - } - - fn width(&self) -> f32 { - let last_glyph = match self.glyphs.last() { - None => return 0.0, - Some(last_glyph) => last_glyph, - }; - - let glyph_id = last_glyph.glyph_id; - let font_metrics = last_glyph.font.font.metrics(); - let scale_factor = self.size / font_metrics.units_per_em as f32; - let glyph_rect = last_glyph.font.font.typographic_bounds(glyph_id).unwrap(); - last_glyph.offset.x() + glyph_rect.max_x() * scale_factor - } - - fn actual_bounding_box_left(&self) -> f32 { - let first_glyph = match self.glyphs.get(0) { - None => return 0.0, - Some(first_glyph) => first_glyph, - }; - - let glyph_id = first_glyph.glyph_id; - let font_metrics = first_glyph.font.font.metrics(); - let scale_factor = self.size / font_metrics.units_per_em as f32; - let glyph_rect = first_glyph.font - .font - .raster_bounds(glyph_id, - font_metrics.units_per_em as f32, - Transform2F::default(), - HintingOptions::None, - RasterizationOptions::GrayscaleAa).unwrap(); - first_glyph.offset.x() + glyph_rect.min_x() as f32 * scale_factor - } - - fn actual_bounding_box_right(&self) -> f32 { - let last_glyph = match self.glyphs.last() { - None => return 0.0, - Some(last_glyph) => last_glyph, - }; - - let glyph_id = last_glyph.glyph_id; - let font_metrics = last_glyph.font.font.metrics(); - let scale_factor = self.size / font_metrics.units_per_em as f32; - let glyph_rect = last_glyph.font - .font - .raster_bounds(glyph_id, - font_metrics.units_per_em as f32, - Transform2F::default(), - HintingOptions::None, - RasterizationOptions::GrayscaleAa).unwrap(); - last_glyph.offset.x() + glyph_rect.max_x() as f32 * scale_factor - } - - fn hanging_baseline(&self) -> f32 { - // TODO(pcwalton) - 0.0 - } - - fn ideographic_baseline(&self) -> f32 { - // TODO(pcwalton) - 0.0 - } -} - -/// Various things that can be conveniently converted into font collections for use with -/// `CanvasRenderingContext2D::set_font()`. -pub trait IntoFontCollection { - fn into_font_collection(self, font_context: &CanvasFontContext) -> Arc; -} - -impl IntoFontCollection for Arc { - #[inline] - fn into_font_collection(self, _: &CanvasFontContext) -> Arc { - self - } -} - -impl IntoFontCollection for FontFamily { - #[inline] - fn into_font_collection(self, _: &CanvasFontContext) -> Arc { - let mut font_collection = FontCollection::new(); - font_collection.add_family(self); - Arc::new(font_collection) - } -} - -impl IntoFontCollection for Vec { - #[inline] - fn into_font_collection(self, _: &CanvasFontContext) -> Arc { - let mut font_collection = FontCollection::new(); - for family in self { - font_collection.add_family(family); - } - Arc::new(font_collection) - } -} - -/* -impl IntoFontCollection for Handle { - #[inline] - fn into_font_collection(self, context: &CanvasFontContext) -> Arc { - self.load().expect("Failed to load the font!").into_font_collection(context) - } -} - -impl<'a> IntoFontCollection for &'a [Handle] { - #[inline] - fn into_font_collection(self, context: &CanvasFontContext) -> Arc { - let mut font_collection = FontCollection::new(); - for handle in self { - let postscript_name = handle.postscript_name(); - - let font = handle.load().expect("Failed to load the font!"); - font_collection.add_family(FontFamily::new_from_font(font)); - } - Arc::new(font_collection) - } -} -*/ - -impl IntoFontCollection for Font { - #[inline] - fn into_font_collection(self, context: &CanvasFontContext) -> Arc { - FontFamily::new_from_font(self).into_font_collection(context) - } -} - -impl<'a> IntoFontCollection for &'a [Font] { - #[inline] - fn into_font_collection(self, context: &CanvasFontContext) -> Arc { - let mut family = FontFamily::new(); - for font in self { - family.add_font(FontRef::new((*font).clone())) - } - family.into_font_collection(context) - } -} - -impl<'a> IntoFontCollection for &'a str { - #[inline] - fn into_font_collection(self, context: &CanvasFontContext) -> Arc { - context.get_font_by_postscript_name(self).into_font_collection(context) - } -} - -impl<'a, 'b> IntoFontCollection for &'a [&'b str] { - #[inline] - fn into_font_collection(self, context: &CanvasFontContext) -> Arc { - let mut font_collection = FontCollection::new(); - for postscript_name in self { - let font = context.get_font_by_postscript_name(postscript_name); - font_collection.add_family(FontFamily::new_from_font(font)); - } - Arc::new(font_collection) - } -} diff --git a/crates/pathfinder/color/Cargo.toml b/crates/pathfinder/color/Cargo.toml deleted file mode 100644 index bbae0b6b9c..0000000000 --- a/crates/pathfinder/color/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "pathfinder_color" -version = "0.5.0" -edition = "2018" -authors = ["Patrick Walton "] -description = "A minimal SIMD-accelerated color handling library" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" - -[dependencies] - -[dependencies.pathfinder_simd] -path = "../simd" -version = "0.5" diff --git a/crates/pathfinder/color/src/lib.rs b/crates/pathfinder/color/src/lib.rs deleted file mode 100644 index 2a2486b949..0000000000 --- a/crates/pathfinder/color/src/lib.rs +++ /dev/null @@ -1,272 +0,0 @@ -// pathfinder/color/src/lib.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use pathfinder_simd::default::F32x4; -use std::f32::consts::PI; -use std::fmt::{self, Debug, Formatter}; -use std::slice; - -// TODO(pcwalton): Maybe this should be a u32? Need to be aware of endianness issues if we do that. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -#[repr(C)] -pub struct ColorU { - pub r: u8, - pub g: u8, - pub b: u8, - pub a: u8, -} - -impl ColorU { - #[inline] - pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU { - ColorU { r, g, b, a } - } - - #[inline] - pub fn transparent_black() -> ColorU { - ColorU::from_u32(0) - } - - #[inline] - pub fn from_u32(rgba: u32) -> ColorU { - ColorU { - r: (rgba >> 24) as u8, - g: ((rgba >> 16) & 0xff) as u8, - b: ((rgba >> 8) & 0xff) as u8, - a: (rgba & 0xff) as u8, - } - } - - #[inline] - pub fn black() -> ColorU { - ColorU { - r: 0, - g: 0, - b: 0, - a: 255, - } - } - - #[inline] - pub fn white() -> ColorU { - ColorU { - r: 255, - g: 255, - b: 255, - a: 255, - } - } - - #[inline] - pub fn to_f32(&self) -> ColorF { - let color = F32x4::new(self.r as f32, self.g as f32, self.b as f32, self.a as f32); - ColorF(color * F32x4::splat(1.0 / 255.0)) - } - - #[inline] - pub fn is_opaque(&self) -> bool { - self.a == !0 - } - - #[inline] - pub fn is_fully_transparent(&self) -> bool { - self.a == 0 - } -} - -impl Debug for ColorU { - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - if self.a == 255 { - write!(formatter, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b) - } else { - write!( - formatter, - "rgba({}, {}, {}, {})", - self.r, - self.g, - self.b, - self.a as f32 / 255.0 - ) - } - } -} - -#[derive(Clone, Copy, PartialEq, Default)] -pub struct ColorF(pub F32x4); - -impl ColorF { - // Constructors - - #[inline] - pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF { - ColorF(F32x4::new(r, g, b, a)) - } - - #[inline] - pub fn from_hsla(mut h: f32, s: f32, l: f32, a: f32) -> ColorF { - // https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB - - // Make sure hue is always positive. - h %= 2.0 * PI; - if h < 0.0 { - h += 2.0 * PI; - } - - h *= 3.0 / PI; - - // Calculate chroma. - let c = (1.0 - f32::abs(2.0 * l - 1.0)) * s; - let xc = F32x4::new(c * (1.0 - f32::abs(h % 2.0 - 1.0)), c, 0.0, a); - let rgba = match f32::ceil(h) as i32 { - 1 => xc.yxzw(), - 2 => xc.xyzw(), - 3 => xc.zyxw(), - 4 => xc.zxyw(), - 5 => xc.xzyw(), - 0 | 6 => xc.yzxw(), - _ => xc.zzzw(), - }; - let m = l - 0.5 * c; - ColorF(rgba + F32x4::new(m, m, m, 0.0)) - } - - #[inline] - pub fn from_hsl(h: f32, s: f32, l: f32) -> ColorF { - ColorF::from_hsla(h, s, l, 1.0) - } - - #[inline] - pub fn transparent_black() -> ColorF { - ColorF::default() - } - - #[inline] - pub fn black() -> ColorF { - ColorF(F32x4::new(0.0, 0.0, 0.0, 1.0)) - } - - #[inline] - pub fn white() -> ColorF { - ColorF(F32x4::splat(1.0)) - } - - #[inline] - pub fn to_u8(&self) -> ColorU { - let color = (self.0 * F32x4::splat(255.0)).to_i32x4(); - ColorU { r: color[0] as u8, g: color[1] as u8, b: color[2] as u8, a: color[3] as u8 } - } - - #[inline] - pub fn lerp(&self, other: ColorF, t: f32) -> ColorF { - ColorF(self.0 + (other.0 - self.0) * F32x4::splat(t)) - } - - #[inline] - pub fn r(&self) -> f32 { - self.0[0] - } - - #[inline] - pub fn g(&self) -> f32 { - self.0[1] - } - - #[inline] - pub fn b(&self) -> f32 { - self.0[2] - } - - #[inline] - pub fn a(&self) -> f32 { - self.0[3] - } - - #[inline] - pub fn set_r(&mut self, r: f32) { - self.0[0] = r; - } - - #[inline] - pub fn set_g(&mut self, g: f32) { - self.0[1] = g; - } - - #[inline] - pub fn set_b(&mut self, b: f32) { - self.0[2] = b; - } - - #[inline] - pub fn set_a(&mut self, a: f32) { - self.0[3] = a; - } -} - -impl Debug for ColorF { - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - write!( - formatter, - "rgba({}, {}, {}, {})", - self.r() * 255.0, - self.g() * 255.0, - self.b() * 255.0, - self.a() - ) - } -} - -#[inline] -pub fn color_slice_to_u8_slice(slice: &[ColorU]) -> &[u8] { - unsafe { - slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 4) - } -} - -#[inline] -pub fn u8_slice_to_color_slice(slice: &[u8]) -> &[ColorU] { - unsafe { - assert_eq!(slice.len() % 4, 0); - slice::from_raw_parts(slice.as_ptr() as *const ColorU, slice.len() / 4) - } -} - -// TODO(pcwalton): Do this without a copy? -#[inline] -pub fn u8_vec_to_color_vec(buffer: Vec) -> Vec { - u8_slice_to_color_slice(&buffer).to_vec() -} - -/// A convenience method to construct a `ColorU` from an RGB triple. -/// -/// Alpha is set to 255. -#[inline] -pub fn rgbu(r: u8, g: u8, b: u8) -> ColorU { - ColorU::new(r, g, b, 255) -} - -/// A convenience method to construct a `ColorU` from an RGBA triple. -#[inline] -pub fn rgbau(r: u8, g: u8, b: u8, a: u8) -> ColorU { - ColorU::new(r, g, b, a) -} - -/// A convenience method to construct a `ColorF` from an RGB triple. -/// -/// Alpha is set to 1.0. -#[inline] -pub fn rgbf(r: f32, g: f32, b: f32) -> ColorF { - ColorF::new(r, g, b, 1.0) -} - -/// A convenience method to construct a `ColorF` from an RGBA triple. -#[inline] -pub fn rgbaf(r: f32, g: f32, b: f32, a: f32) -> ColorF { - ColorF::new(r, g, b, a) -} diff --git a/crates/pathfinder/content/Cargo.toml b/crates/pathfinder/content/Cargo.toml deleted file mode 100644 index c2b769e0d3..0000000000 --- a/crates/pathfinder/content/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "pathfinder_content" -version = "0.5.0" -edition = "2018" -authors = ["Patrick Walton "] -description = "Vector path utilities for the Pathfinder rendering library" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" - -[dependencies] -arrayvec = "0.5" -bitflags = "1.0" -log = "0.4" -smallvec = "1.2" - -[dependencies.image] -version = "0.23" -default-features = false -features = [] -optional = true - -[features] -default = ["pf-image"] -pf-image = ["image"] - -[dependencies.pathfinder_color] -path = "../color" -version = "0.5" - -[dependencies.pathfinder_geometry] -path = "../geometry" -version = "0.5" - -[dependencies.pathfinder_simd] -path = "../simd" -version = "0.5" - -[dev-dependencies] -quickcheck = "0.9" diff --git a/crates/pathfinder/content/src/clip.rs b/crates/pathfinder/content/src/clip.rs deleted file mode 100644 index c5d4aa70cf..0000000000 --- a/crates/pathfinder/content/src/clip.rs +++ /dev/null @@ -1,555 +0,0 @@ -// pathfinder/content/src/clip.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::outline::{Contour, ContourIterFlags, PointFlags, PushSegmentFlags}; -use crate::segment::{CubicSegment, Segment}; -use arrayvec::ArrayVec; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::util::lerp; -use pathfinder_geometry::vector::{Vector2F, Vector4F}; -use smallvec::SmallVec; -use std::fmt::Debug; -use std::mem; - -#[derive(Clone, Copy, Debug)] -struct Edge(LineSegment2F); - -impl TEdge for Edge { - #[inline] - fn point_is_inside(&self, point: Vector2F) -> bool { - let area = (self.0.to() - self.0.from()).det(point - self.0.from()); - debug!("point_is_inside({:?}, {:?}), area={}", self, point, area); - area >= 0.0 - } - - fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]> { - let mut results = ArrayVec::new(); - if let Some(t) = segment.intersection_t(self.0) { - if t >= 0.0 && t <= 1.0 { - results.push(t); - } - } - results - } -} - -#[derive(Clone, Copy, Debug)] -enum AxisAlignedEdge { - Left(f32), - Top(f32), - Right(f32), - Bottom(f32), -} - -impl TEdge for AxisAlignedEdge { - #[inline] - fn point_is_inside(&self, point: Vector2F) -> bool { - match *self { - AxisAlignedEdge::Left(x) => point.x() >= x, - AxisAlignedEdge::Top(y) => point.y() >= y, - AxisAlignedEdge::Right(x) => point.x() <= x, - AxisAlignedEdge::Bottom(y) => point.y() <= y, - } - } - - fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]> { - let mut results = ArrayVec::new(); - let t = match *self { - AxisAlignedEdge::Left(x) | AxisAlignedEdge::Right(x) => segment.solve_t_for_x(x), - AxisAlignedEdge::Top(y) | AxisAlignedEdge::Bottom(y) => segment.solve_t_for_y(y), - }; - if t >= 0.0 && t <= 1.0 { - results.push(t); - } - results - } -} - -trait TEdge: Debug { - fn point_is_inside(&self, point: Vector2F) -> bool; - fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]>; - - fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation { - let from_inside = self.point_is_inside(segment.baseline.from()); - debug!( - "point {:?} inside {:?}: {:?}", - segment.baseline.from(), - self, - from_inside - ); - if from_inside != self.point_is_inside(segment.baseline.to()) { - return EdgeRelativeLocation::Intersecting; - } - if !segment.is_line() { - if from_inside != self.point_is_inside(segment.ctrl.from()) { - return EdgeRelativeLocation::Intersecting; - } - if !segment.is_quadratic() { - if from_inside != self.point_is_inside(segment.ctrl.to()) { - return EdgeRelativeLocation::Intersecting; - } - } - } - if from_inside { - EdgeRelativeLocation::Inside - } else { - EdgeRelativeLocation::Outside - } - } - - fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> { - if segment.is_line() { - return self.intersect_line_segment(segment.baseline); - } - - let mut segment = *segment; - if segment.is_quadratic() { - segment = segment.to_cubic(); - } - - let mut results = ArrayVec::new(); - let mut prev_t = 0.0; - while !results.is_full() { - if prev_t >= 1.0 { - break; - } - let next_t = match self.intersect_cubic_segment(&segment, prev_t, 1.0) { - None => break, - Some(next_t) => next_t, - }; - results.push(next_t); - prev_t = next_t + EPSILON; - } - return results; - - const EPSILON: f32 = 0.0001; - } - - fn intersect_cubic_segment( - &self, - segment: &Segment, - mut t_min: f32, - mut t_max: f32, - ) -> Option { - debug!( - "... intersect_cubic_segment({:?}, {:?}, t=({}, {}))", - self, segment, t_min, t_max - ); - - let mut segment = segment.as_cubic_segment().split_after(t_min); - segment = segment - .as_cubic_segment() - .split_before(t_max / (1.0 - t_min)); - - if !self.intersects_cubic_segment_hull(segment.as_cubic_segment()) { - return None; - } - - loop { - let t_mid = lerp(t_min, t_max, 0.5); - if t_max - t_min < 0.00001 { - return Some(t_mid); - } - - let (prev_segment, next_segment) = segment.as_cubic_segment().split(0.5); - if self.intersects_cubic_segment_hull(prev_segment.as_cubic_segment()) { - t_max = t_mid; - segment = prev_segment; - } else if self.intersects_cubic_segment_hull(next_segment.as_cubic_segment()) { - t_min = t_mid; - segment = next_segment; - } else { - return None; - } - } - } - - fn intersects_cubic_segment_hull(&self, cubic_segment: CubicSegment) -> bool { - let inside = self.point_is_inside(cubic_segment.0.baseline.from()); - inside != self.point_is_inside(cubic_segment.0.ctrl.from()) - || inside != self.point_is_inside(cubic_segment.0.ctrl.to()) - || inside != self.point_is_inside(cubic_segment.0.baseline.to()) - } -} - -trait ContourClipper -where - Self::Edge: TEdge + Debug, -{ - type Edge; - - fn contour_mut(&mut self) -> &mut Contour; - - fn clip_against(&mut self, edge: Self::Edge) { - // Fast path to avoid allocation in the no-clip case. - match self.check_for_fast_clip(&edge) { - FastClipResult::SlowPath => {} - FastClipResult::AllInside => return, - FastClipResult::AllOutside => { - *self.contour_mut() = Contour::new(); - return; - } - } - - let input = self.contour_mut().take(); - for segment in input.iter(ContourIterFlags::empty()) { - self.clip_segment_against(segment, &edge); - } - if input.is_closed() { - self.contour_mut().close(); - } - } - - fn clip_segment_against(&mut self, mut segment: Segment, edge: &Self::Edge) { - // Easy cases. - match edge.trivially_test_segment(&segment) { - EdgeRelativeLocation::Outside => return, - EdgeRelativeLocation::Inside => { - debug!("trivial test inside, pushing segment"); - self.push_segment(&segment); - return; - } - EdgeRelativeLocation::Intersecting => {} - } - - // We have a potential intersection. - debug!("potential intersection: {:?} edge: {:?}", segment, edge); - let mut starts_inside = edge.point_is_inside(segment.baseline.from()); - let intersection_ts = edge.intersect_segment(&segment); - let mut last_t = 0.0; - debug!("... intersections: {:?}", intersection_ts); - for t in intersection_ts { - let (before_split, after_split) = segment.split((t - last_t) / (1.0 - last_t)); - - // Push the split segment if appropriate. - debug!( - "... ... edge={:?} before_split={:?} t={:?} starts_inside={:?}", - edge, before_split, t, starts_inside - ); - if starts_inside { - debug!("... split segment case, pushing segment"); - self.push_segment(&before_split); - } - - // We've now transitioned from inside to outside or vice versa. - starts_inside = !starts_inside; - last_t = t; - segment = after_split; - } - - // No more intersections. Push the last segment if applicable. - if starts_inside { - debug!("... last segment case, pushing segment"); - self.push_segment(&segment); - } - } - - fn push_segment(&mut self, segment: &Segment) { - let contour = self.contour_mut(); - if let Some(last_position) = contour.last_position() { - if last_position != segment.baseline.from() { - // Add a line to join up segments. - contour.push_point(segment.baseline.from(), PointFlags::empty(), true); - } - } - - contour.push_segment(segment, PushSegmentFlags::UPDATE_BOUNDS); - } - - fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult { - let mut result = None; - for segment in self.contour_mut().iter(ContourIterFlags::empty()) { - let location = edge.trivially_test_segment(&segment); - match (result, location) { - (None, EdgeRelativeLocation::Outside) => { - result = Some(FastClipResult::AllOutside); - } - (None, EdgeRelativeLocation::Inside) => { - result = Some(FastClipResult::AllInside); - } - (Some(FastClipResult::AllInside), EdgeRelativeLocation::Inside) - | (Some(FastClipResult::AllOutside), EdgeRelativeLocation::Outside) => {} - (_, _) => return FastClipResult::SlowPath, - } - } - result.unwrap_or(FastClipResult::AllOutside) - } -} - -#[derive(Clone, Copy)] -enum FastClipResult { - SlowPath, - AllInside, - AllOutside, -} - -// General convex polygon clipping in 2D - -pub(crate) struct ContourPolygonClipper { - clip_polygon: SmallVec<[Vector2F; 4]>, - contour: Contour, -} - -impl ContourClipper for ContourPolygonClipper { - type Edge = Edge; - - #[inline] - fn contour_mut(&mut self) -> &mut Contour { - &mut self.contour - } -} - -impl ContourPolygonClipper { - #[inline] - pub(crate) fn new(clip_polygon: &[Vector2F], contour: Contour) -> ContourPolygonClipper { - ContourPolygonClipper { - clip_polygon: SmallVec::from_slice(clip_polygon), - contour, - } - } - - pub(crate) fn clip(mut self) -> Contour { - // TODO(pcwalton): Maybe have a coarse circumscribed rect and use that for clipping? - - let clip_polygon = mem::replace(&mut self.clip_polygon, SmallVec::default()); - let mut prev = match clip_polygon.last() { - None => return Contour::new(), - Some(prev) => *prev, - }; - for &next in &clip_polygon { - self.clip_against(Edge(LineSegment2F::new(prev, next))); - prev = next; - } - - self.contour - } -} - -#[derive(PartialEq)] -enum EdgeRelativeLocation { - Intersecting, - Inside, - Outside, -} - -// Fast axis-aligned box 2D clipping - -pub(crate) struct ContourRectClipper { - clip_rect: RectF, - contour: Contour, -} - -impl ContourClipper for ContourRectClipper { - type Edge = AxisAlignedEdge; - - #[inline] - fn contour_mut(&mut self) -> &mut Contour { - &mut self.contour - } -} - -impl ContourRectClipper { - #[inline] - pub(crate) fn new(clip_rect: RectF, contour: Contour) -> ContourRectClipper { - ContourRectClipper { clip_rect, contour } - } - - pub(crate) fn clip(mut self) -> Contour { - if self.clip_rect.contains_rect(self.contour.bounds()) { - return self.contour; - } - - self.clip_against(AxisAlignedEdge::Left(self.clip_rect.min_x())); - self.clip_against(AxisAlignedEdge::Top(self.clip_rect.min_y())); - self.clip_against(AxisAlignedEdge::Right(self.clip_rect.max_x())); - self.clip_against(AxisAlignedEdge::Bottom(self.clip_rect.max_y())); - - self.contour - } -} - -// 3D quad clipping - -pub struct PolygonClipper3D { - subject: Vec, -} - -impl PolygonClipper3D { - #[inline] - pub fn new(subject: Vec) -> PolygonClipper3D { - PolygonClipper3D { subject } - } - - pub fn clip(mut self) -> Vec { - // TODO(pcwalton): Fast path for completely contained polygon? - - debug!("before clipping against bottom: {:?}", self.subject); - self.clip_against(Edge3D::Bottom); - debug!("before clipping against top: {:?}", self.subject); - self.clip_against(Edge3D::Top); - debug!("before clipping against left: {:?}", self.subject); - self.clip_against(Edge3D::Left); - debug!("before clipping against right: {:?}", self.subject); - self.clip_against(Edge3D::Right); - debug!("before clipping against far: {:?}", self.subject); - self.clip_against(Edge3D::Far); - debug!("before clipping against near: {:?}", self.subject); - self.clip_against(Edge3D::Near); - debug!("after clipping: {:?}", self.subject); - - self.subject - } - - fn clip_against(&mut self, edge: Edge3D) { - let input = mem::replace(&mut self.subject, vec![]); - let mut prev = match input.last() { - None => return, - Some(point) => *point, - }; - for next in input { - if edge.point_is_inside(next) { - if !edge.point_is_inside(prev) { - self.subject.push(edge.line_intersection(prev, next)); - } - self.subject.push(next); - } else if edge.point_is_inside(prev) { - self.subject.push(edge.line_intersection(prev, next)); - } - prev = next; - } - } -} - -#[derive(Clone, Copy, Debug)] -enum Edge3D { - Left, - Right, - Bottom, - Top, - Near, - Far, -} - -impl Edge3D { - #[inline] - fn point_is_inside(self, point: Vector4F) -> bool { - let w = point.w(); - match self { - Edge3D::Left => point.x() >= -w, - Edge3D::Right => point.x() <= w, - Edge3D::Bottom => point.y() >= -w, - Edge3D::Top => point.y() <= w, - Edge3D::Near => point.z() >= -w, - Edge3D::Far => point.z() <= w, - } - } - - // Blinn & Newell, "Clipping using homogeneous coordinates", SIGGRAPH 1978. - fn line_intersection(self, prev: Vector4F, next: Vector4F) -> Vector4F { - let (x0, x1) = match self { - Edge3D::Left | Edge3D::Right => (prev.x(), next.x()), - Edge3D::Bottom | Edge3D::Top => (prev.y(), next.y()), - Edge3D::Near | Edge3D::Far => (prev.z(), next.z()), - }; - let (w0, w1) = (prev.w(), next.w()); - let sign = match self { - Edge3D::Left | Edge3D::Bottom | Edge3D::Near => -1.0, - Edge3D::Right | Edge3D::Top | Edge3D::Far => 1.0, - }; - let alpha = ((x0 - sign * w0) as f64) / ((sign * (w1 - w0) - (x1 - x0)) as f64); - prev.lerp(next, alpha as f32) - } -} - -/// Coarse collision detection - -// Separating axis theorem. Requires that the polygon be convex. -pub(crate) fn rect_is_outside_polygon(rect: RectF, polygon_points: &[Vector2F]) -> bool { - let mut outcode = Outcode::all(); - for point in polygon_points { - if point.x() > rect.min_x() { - outcode.remove(Outcode::LEFT); - } - if point.x() < rect.max_x() { - outcode.remove(Outcode::RIGHT); - } - if point.y() > rect.min_y() { - outcode.remove(Outcode::TOP); - } - if point.y() < rect.max_y() { - outcode.remove(Outcode::BOTTOM); - } - } - if !outcode.is_empty() { - return true; - } - - // FIXME(pcwalton): Check winding! - let rect_points = [ - rect.origin(), - rect.upper_right(), - rect.lower_left(), - rect.lower_right(), - ]; - for (next_point_index, &next) in polygon_points.iter().enumerate() { - let prev_point_index = if next_point_index == 0 { - polygon_points.len() - 1 - } else { - next_point_index - 1 - }; - let prev = polygon_points[prev_point_index]; - let polygon_edge_vector = next - prev; - if rect_points - .iter() - .all(|&rect_point| polygon_edge_vector.det(rect_point - prev) < 0.0) - { - return true; - } - } - - false -} - -// Edge equation method. Requires that the polygon be convex. -pub(crate) fn rect_is_inside_polygon(rect: RectF, polygon_points: &[Vector2F]) -> bool { - // FIXME(pcwalton): Check winding! - let rect_points = [ - rect.origin(), - rect.upper_right(), - rect.lower_left(), - rect.lower_right(), - ]; - for (next_point_index, &next) in polygon_points.iter().enumerate() { - let prev_point_index = if next_point_index == 0 { - polygon_points.len() - 1 - } else { - next_point_index - 1 - }; - let prev = polygon_points[prev_point_index]; - let polygon_edge_vector = next - prev; - for &rect_point in &rect_points { - if polygon_edge_vector.det(rect_point - prev) < 0.0 { - return false; - } - } - } - - true -} - -bitflags! { - struct Outcode: u8 { - const LEFT = 0x01; - const RIGHT = 0x02; - const TOP = 0x04; - const BOTTOM = 0x08; - } -} diff --git a/crates/pathfinder/content/src/dash.rs b/crates/pathfinder/content/src/dash.rs deleted file mode 100644 index 9c820a9740..0000000000 --- a/crates/pathfinder/content/src/dash.rs +++ /dev/null @@ -1,134 +0,0 @@ -// pathfinder/content/src/dash.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Line dashing support. - -use crate::outline::{Contour, ContourIterFlags, Outline, PushSegmentFlags}; -use std::mem; - -const EPSILON: f32 = 0.0001; - -pub struct OutlineDash<'a> { - input: &'a Outline, - output: Outline, - state: DashState<'a>, -} - -impl<'a> OutlineDash<'a> { - #[inline] - pub fn new(input: &'a Outline, dashes: &'a [f32], offset: f32) -> OutlineDash<'a> { - OutlineDash { input, output: Outline::new(), state: DashState::new(dashes, offset) } - } - - pub fn dash(&mut self) { - for contour in &self.input.contours { - ContourDash::new(contour, &mut self.output, &mut self.state).dash() - } - } - - pub fn into_outline(mut self) -> Outline { - if self.state.is_on() { - self.output.push_contour(self.state.output); - } - self.output - } -} - -struct ContourDash<'a, 'b, 'c> { - input: &'a Contour, - output: &'b mut Outline, - state: &'c mut DashState<'a>, -} - -impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> { - fn new(input: &'a Contour, output: &'b mut Outline, state: &'c mut DashState<'a>) - -> ContourDash<'a, 'b, 'c> { - ContourDash { input, output, state } - } - - fn dash(&mut self) { - let mut iterator = self.input.iter(ContourIterFlags::empty()); - let mut queued_segment = None; - loop { - if queued_segment.is_none() { - match iterator.next() { - None => break, - Some(segment) => queued_segment = Some(segment), - } - } - - let mut current_segment = queued_segment.take().unwrap(); - let mut distance = self.state.distance_left; - - let t = current_segment.time_for_distance(distance); - if t < 1.0 { - let (prev_segment, next_segment) = current_segment.split(t); - current_segment = prev_segment; - queued_segment = Some(next_segment); - } else { - distance = current_segment.arc_length(); - } - - if self.state.is_on() { - self.state.output.push_segment(¤t_segment, PushSegmentFlags::empty()); - } - - self.state.distance_left -= distance; - if self.state.distance_left < EPSILON { - if self.state.is_on() { - self.output.push_contour(mem::replace(&mut self.state.output, Contour::new())); - } - - self.state.current_dash_index += 1; - if self.state.current_dash_index == self.state.dashes.len() { - self.state.current_dash_index = 0; - } - - self.state.distance_left = self.state.dashes[self.state.current_dash_index]; - } - } - } -} - -struct DashState<'a> { - output: Contour, - dashes: &'a [f32], - current_dash_index: usize, - distance_left: f32, -} - -impl<'a> DashState<'a> { - fn new(dashes: &'a [f32], mut offset: f32) -> DashState<'a> { - let total: f32 = dashes.iter().cloned().sum(); - offset %= total; - - let mut current_dash_index = 0; - while current_dash_index < dashes.len() { - let dash = dashes[current_dash_index]; - if offset < dash { - break; - } - offset -= dash; - current_dash_index += 1; - } - - DashState { - output: Contour::new(), - dashes, - current_dash_index, - distance_left: offset, - } - } - - #[inline] - fn is_on(&self) -> bool { - self.current_dash_index % 2 == 0 - } -} diff --git a/crates/pathfinder/content/src/dilation.rs b/crates/pathfinder/content/src/dilation.rs deleted file mode 100644 index 9e39f699a5..0000000000 --- a/crates/pathfinder/content/src/dilation.rs +++ /dev/null @@ -1,125 +0,0 @@ -// pathfinder/content/src/dilation.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::orientation::Orientation; -use crate::outline::Contour; -use pathfinder_geometry::vector::{Vector2F, vec2f}; - -pub struct ContourDilator<'a> { - contour: &'a mut Contour, - amount: Vector2F, - orientation: Orientation, -} - -impl<'a> ContourDilator<'a> { - pub fn new( - contour: &'a mut Contour, - amount: Vector2F, - orientation: Orientation, - ) -> ContourDilator<'a> { - ContourDilator { - contour, - amount, - orientation, - } - } - - pub fn dilate(&mut self) { - // Determine orientation. - let scale = self.amount * (match self.orientation { - Orientation::Ccw => vec2f( 1.0, -1.0), - Orientation::Cw => vec2f(-1.0, 1.0), - }); - - // Find the starting and previous positions. - let first_position = self.contour.position_of(0); - let mut prev_point_index = 0; - let mut prev_position; - - loop { - prev_point_index = self.contour.prev_point_index_of(prev_point_index); - prev_position = self.contour.position_of(prev_point_index); - if prev_point_index == 0 || prev_position != first_position { - break; - } - } - - // Initialize our loop. - let first_point_index = self.contour.next_point_index_of(prev_point_index); - let mut current_point_index = first_point_index; - let mut position = first_position; - - let mut prev_vector = (position - prev_position).normalize(); - - loop { - // Find the next non-degenerate position. - let mut next_point_index = current_point_index; - let mut next_position; - loop { - next_point_index = self.contour.next_point_index_of(next_point_index); - if next_point_index == first_point_index { - next_position = first_position; - break; - } - next_position = self.contour.position_of(next_point_index); - if next_point_index == current_point_index || next_position != position { - break; - } - } - let next_vector = (next_position - position).normalize(); - - debug!( - "prev={} cur={} next={}", - prev_point_index, current_point_index, next_point_index - ); - - // Calculate new position by moving the point by the bisector. - let bisector = prev_vector.yx() + next_vector.yx(); - let bisector_length = bisector.length(); - let scaled_bisector = if bisector_length == 0.0 { - Vector2F::zero() - } else { - bisector * scale * (1.0 / bisector_length) - }; - let new_position = position - scaled_bisector; - - debug!( - "dilate(): prev={}({:?}) cur={}({:?}) next={}({:?}) bisector={:?}({:?}, {:?})", - prev_point_index, - prev_position, - current_point_index, - position, - next_point_index, - next_position, - bisector, - bisector_length, - scaled_bisector - ); - - // Update all points. - let mut point_index = current_point_index; - while point_index != next_point_index { - self.contour.points[point_index as usize] = new_position; - debug!("... updating {:?}", point_index); - point_index = self.contour.next_point_index_of(point_index); - } - - // Check to see if we're done. - if next_point_index == first_point_index { - break; - } - - // Continue. - prev_vector = next_vector; - position = next_position; - current_point_index = next_point_index; - } - } -} diff --git a/crates/pathfinder/content/src/effects.rs b/crates/pathfinder/content/src/effects.rs deleted file mode 100644 index e5db8f43a8..0000000000 --- a/crates/pathfinder/content/src/effects.rs +++ /dev/null @@ -1,205 +0,0 @@ -// pathfinder/content/src/effects.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Special effects that can be applied to layers. - -use pathfinder_color::ColorF; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::vector::Vector2F; -use pathfinder_simd::default::F32x2; - -/// This intentionally does not precisely match what Core Graphics does (a -/// Lanczos function), because we don't want any ringing artefacts. -pub static DEFRINGING_KERNEL_CORE_GRAPHICS: DefringingKernel = - DefringingKernel([0.033165660, 0.102074051, 0.221434336, 0.286651906]); -pub static DEFRINGING_KERNEL_FREETYPE: DefringingKernel = - DefringingKernel([0.0, 0.031372549, 0.301960784, 0.337254902]); - -/// Should match macOS 10.13 High Sierra. -pub static STEM_DARKENING_FACTORS: [f32; 2] = [0.0121, 0.0121 * 1.25]; - -/// Should match macOS 10.13 High Sierra. -pub const MAX_STEM_DARKENING_AMOUNT: [f32; 2] = [0.3, 0.3]; - -/// This value is a subjective cutoff. Above this ppem value, no stem darkening is performed. -pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0; - -/// The shader that should be used when compositing this layer onto its destination. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum Filter { - /// No special filter. - None, - - /// Converts a linear gradient to a radial one. - RadialGradient { - /// The line that the circles lie along. - line: LineSegment2F, - /// The radii of the circles at the two endpoints. - radii: F32x2, - /// The origin of the linearized gradient in the texture. - uv_origin: Vector2F, - }, - - PatternFilter(PatternFilter), -} - -/// Shaders applicable to patterns. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum PatternFilter { - /// Performs postprocessing operations useful for monochrome text. - Text { - /// The foreground color of the text. - fg_color: ColorF, - /// The background color of the text. - bg_color: ColorF, - /// The kernel used for defringing, if subpixel AA is enabled. - defringing_kernel: Option, - /// Whether gamma correction is used when compositing. - /// - /// If this is enabled, stem darkening is advised. - gamma_correction: bool, - }, - - /// A blur operation in one direction, either horizontal or vertical. - /// - /// To produce a full Gaussian blur, perform two successive blur operations, one in each - /// direction. - Blur { - direction: BlurDirection, - sigma: f32, - }, -} - -/// Blend modes that can be applied to individual paths. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum BlendMode { - // Porter-Duff, supported by GPU blender - Clear, - Copy, - SrcIn, - SrcOut, - SrcOver, - SrcAtop, - DestIn, - DestOut, - DestOver, - DestAtop, - Xor, - Lighter, - - // Others, unsupported by GPU blender - Darken, - Lighten, - Multiply, - Screen, - HardLight, - Overlay, - ColorDodge, - ColorBurn, - SoftLight, - Difference, - Exclusion, - Hue, - Saturation, - Color, - Luminosity, -} - -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct DefringingKernel(pub [f32; 4]); - -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum BlurDirection { - X, - Y, -} - -impl Default for BlendMode { - #[inline] - fn default() -> BlendMode { - BlendMode::SrcOver - } -} - -impl Default for Filter { - #[inline] - fn default() -> Filter { - Filter::None - } -} - -impl BlendMode { - /// Whether the backdrop is irrelevant when applying this blend mode (i.e. destination blend - /// factor is zero when source alpha is one). - #[inline] - pub fn occludes_backdrop(self) -> bool { - match self { - BlendMode::SrcOver | BlendMode::Clear => true, - BlendMode::DestOver | - BlendMode::DestOut | - BlendMode::SrcAtop | - BlendMode::Xor | - BlendMode::Lighter | - BlendMode::Lighten | - BlendMode::Darken | - BlendMode::Copy | - BlendMode::SrcIn | - BlendMode::DestIn | - BlendMode::SrcOut | - BlendMode::DestAtop | - BlendMode::Multiply | - BlendMode::Screen | - BlendMode::HardLight | - BlendMode::Overlay | - BlendMode::ColorDodge | - BlendMode::ColorBurn | - BlendMode::SoftLight | - BlendMode::Difference | - BlendMode::Exclusion | - BlendMode::Hue | - BlendMode::Saturation | - BlendMode::Color | - BlendMode::Luminosity => false, - } - } - - /// True if this blend mode does not preserve destination areas outside the source. - pub fn is_destructive(self) -> bool { - match self { - BlendMode::Clear | - BlendMode::Copy | - BlendMode::SrcIn | - BlendMode::DestIn | - BlendMode::SrcOut | - BlendMode::DestAtop => true, - BlendMode::SrcOver | - BlendMode::DestOver | - BlendMode::DestOut | - BlendMode::SrcAtop | - BlendMode::Xor | - BlendMode::Lighter | - BlendMode::Lighten | - BlendMode::Darken | - BlendMode::Multiply | - BlendMode::Screen | - BlendMode::HardLight | - BlendMode::Overlay | - BlendMode::ColorDodge | - BlendMode::ColorBurn | - BlendMode::SoftLight | - BlendMode::Difference | - BlendMode::Exclusion | - BlendMode::Hue | - BlendMode::Saturation | - BlendMode::Color | - BlendMode::Luminosity => false, - } - } -} diff --git a/crates/pathfinder/content/src/fill.rs b/crates/pathfinder/content/src/fill.rs deleted file mode 100644 index e2941f17d9..0000000000 --- a/crates/pathfinder/content/src/fill.rs +++ /dev/null @@ -1,17 +0,0 @@ -// pathfinder/content/src/fill.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Fill rules. - -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum FillRule { - Winding, - EvenOdd, -} diff --git a/crates/pathfinder/content/src/gradient.rs b/crates/pathfinder/content/src/gradient.rs deleted file mode 100644 index 111559703f..0000000000 --- a/crates/pathfinder/content/src/gradient.rs +++ /dev/null @@ -1,205 +0,0 @@ -// pathfinder/content/src/gradient.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::sorted_vector::SortedVector; -use crate::util; -use pathfinder_color::ColorU; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::Vector2F; -use pathfinder_geometry::util as geometry_util; -use pathfinder_simd::default::F32x2; -use std::cmp::{Ordering, PartialOrd}; -use std::convert; -use std::hash::{Hash, Hasher}; -use std::mem; - -#[derive(Clone, PartialEq, Debug)] -pub struct Gradient { - pub geometry: GradientGeometry, - stops: SortedVector, -} - -#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] -pub struct ColorStop { - pub offset: f32, - pub color: ColorU, -} - -#[derive(Clone, PartialEq, Debug)] -pub enum GradientGeometry { - Linear(LineSegment2F), - Radial { - /// The line that connects the two circles. It may have zero length for simple radial - /// gradients. - line: LineSegment2F, - /// The radii of the two circles. The first value may be zero. - radii: F32x2, - /// Transform from radial gradient space into screen space. - /// - /// Like `gradientTransform` in SVG. Note that this is the inverse of Cairo's gradient - /// transform. - transform: Transform2F, - } -} - -impl Eq for Gradient {} - -impl Hash for Gradient { - fn hash(&self, state: &mut H) where H: Hasher { - match self.geometry { - GradientGeometry::Linear(line) => { - (0).hash(state); - util::hash_line_segment(line, state); - } - GradientGeometry::Radial { line, radii, transform } => { - (1).hash(state); - util::hash_line_segment(line, state); - util::hash_f32(radii.x(), state); - util::hash_f32(radii.y(), state); - util::hash_f32(transform.m11(), state); - util::hash_f32(transform.m12(), state); - util::hash_f32(transform.m13(), state); - util::hash_f32(transform.m21(), state); - util::hash_f32(transform.m22(), state); - util::hash_f32(transform.m23(), state); - } - } - self.stops.hash(state); - } -} - -impl Eq for ColorStop {} - -impl Hash for ColorStop { - fn hash(&self, state: &mut H) where H: Hasher { - unsafe { - self.color.hash(state); - let offset = mem::transmute::(self.offset); - offset.hash(state); - } - } -} - -impl Gradient { - #[inline] - pub fn linear(line: LineSegment2F) -> Gradient { - Gradient { geometry: GradientGeometry::Linear(line), stops: SortedVector::new() } - } - - #[inline] - pub fn linear_from_points(from: Vector2F, to: Vector2F) -> Gradient { - Gradient::linear(LineSegment2F::new(from, to)) - } - - #[inline] - pub fn radial(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine { - let transform = Transform2F::default(); - Gradient { - geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform }, - stops: SortedVector::new(), - } - } - - #[inline] - pub fn add(&mut self, stop: ColorStop) { - self.stops.push(stop); - } - - /// A convenience method to add a color stop. - #[inline] - pub fn add_color_stop(&mut self, color: ColorU, offset: f32) { - self.add(ColorStop::new(color, offset)) - } - - #[inline] - pub fn stops(&self) -> &[ColorStop] { - &self.stops.array - } - - #[inline] - pub fn stops_mut(&mut self) -> &mut [ColorStop] { - &mut self.stops.array - } - - pub fn sample(&self, mut t: f32) -> ColorU { - if self.stops.is_empty() { - return ColorU::transparent_black(); - } - - t = geometry_util::clamp(t, 0.0, 1.0); - let last_index = self.stops.len() - 1; - let upper_index = self.stops.binary_search_by(|stop| { - stop.offset.partial_cmp(&t).unwrap_or(Ordering::Less) - }).unwrap_or_else(convert::identity).min(last_index); - let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index }; - - let lower_stop = &self.stops.array[lower_index]; - let upper_stop = &self.stops.array[upper_index]; - - let denom = upper_stop.offset - lower_stop.offset; - if denom == 0.0 { - return lower_stop.color; - } - - lower_stop.color - .to_f32() - .lerp(upper_stop.color.to_f32(), (t - lower_stop.offset) / denom) - .to_u8() - } - - #[inline] - pub fn is_opaque(&self) -> bool { - self.stops.array.iter().all(|stop| stop.color.is_opaque()) - } - - #[inline] - pub fn is_fully_transparent(&self) -> bool { - self.stops.array.iter().all(|stop| stop.color.is_fully_transparent()) - } - - pub fn apply_transform(&mut self, new_transform: Transform2F) { - if new_transform.is_identity() { - return; - } - - match self.geometry { - GradientGeometry::Linear(ref mut line) => *line = new_transform * *line, - GradientGeometry::Radial { ref mut transform, .. } => { - *transform = new_transform * *transform - } - } - } -} - -impl ColorStop { - #[inline] - pub fn new(color: ColorU, offset: f32) -> ColorStop { - ColorStop { color, offset } - } -} - -pub trait RadialGradientLine { - fn to_line(self) -> LineSegment2F; -} - -impl RadialGradientLine for LineSegment2F { - #[inline] - fn to_line(self) -> LineSegment2F { - self - } -} - -impl RadialGradientLine for Vector2F { - #[inline] - fn to_line(self) -> LineSegment2F { - LineSegment2F::new(self, self) - } -} diff --git a/crates/pathfinder/content/src/lib.rs b/crates/pathfinder/content/src/lib.rs deleted file mode 100644 index 8674760237..0000000000 --- a/crates/pathfinder/content/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -// pathfinder/content/src/lib.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Pathfinder's representation of a vector scene. -//! -//! This module also contains various path utilities. - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate log; - -pub mod clip; -pub mod dash; -pub mod effects; -pub mod fill; -pub mod gradient; -pub mod orientation; -pub mod outline; -pub mod pattern; -pub mod render_target; -pub mod segment; -pub mod sorted_vector; -pub mod stroke; -pub mod transform; - -mod dilation; -mod util; diff --git a/crates/pathfinder/content/src/orientation.rs b/crates/pathfinder/content/src/orientation.rs deleted file mode 100644 index 9e815ebb0d..0000000000 --- a/crates/pathfinder/content/src/orientation.rs +++ /dev/null @@ -1,43 +0,0 @@ -// pathfinder/geometry/src/orientation.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::outline::Outline; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Orientation { - Ccw = -1, - Cw = 1, -} - -impl Orientation { - /// This follows the FreeType algorithm. - pub fn from_outline(outline: &Outline) -> Orientation { - let mut area = 0.0; - for contour in &outline.contours { - let mut prev_position = match contour.last_position() { - None => continue, - Some(position) => position, - }; - for &next_position in &contour.points { - area += prev_position.det(next_position); - prev_position = next_position; - } - } - Orientation::from_area(area) - } - - fn from_area(area: f32) -> Orientation { - if area <= 0.0 { - Orientation::Ccw - } else { - Orientation::Cw - } - } -} diff --git a/crates/pathfinder/content/src/outline.rs b/crates/pathfinder/content/src/outline.rs deleted file mode 100644 index 7a062ea061..0000000000 --- a/crates/pathfinder/content/src/outline.rs +++ /dev/null @@ -1,927 +0,0 @@ -// pathfinder/content/src/outline.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A compressed in-memory representation of paths. - -use crate::clip::{self, ContourPolygonClipper, ContourRectClipper}; -use crate::dilation::ContourDilator; -use crate::orientation::Orientation; -use crate::segment::{Segment, SegmentFlags, SegmentKind}; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::transform3d::Perspective; -use pathfinder_geometry::unit_vector::UnitVector; -use pathfinder_geometry::vector::{Vector2F, vec2f}; -use std::f32::consts::PI; -use std::fmt::{self, Debug, Formatter}; -use std::mem; - -#[derive(Clone)] -pub struct Outline { - pub(crate) contours: Vec, - pub(crate) bounds: RectF, -} - -#[derive(Clone)] -pub struct Contour { - pub(crate) points: Vec, - pub(crate) flags: Vec, - pub(crate) bounds: RectF, - pub(crate) closed: bool, -} - -bitflags! { - pub struct PointFlags: u8 { - const CONTROL_POINT_0 = 0x01; - const CONTROL_POINT_1 = 0x02; - } -} - -bitflags! { - pub struct PushSegmentFlags: u8 { - const UPDATE_BOUNDS = 0x01; - const INCLUDE_FROM_POINT = 0x02; - } -} - -impl Outline { - #[inline] - pub fn new() -> Outline { - Outline { - contours: vec![], - bounds: RectF::default(), - } - } - - #[inline] - pub fn from_segments(segments: I) -> Outline - where - I: Iterator, - { - let mut outline = Outline::new(); - let mut current_contour = Contour::new(); - - for segment in segments { - if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) { - if !current_contour.is_empty() { - outline - .contours - .push(mem::replace(&mut current_contour, Contour::new())); - } - current_contour.push_point(segment.baseline.from(), PointFlags::empty(), true); - } - - if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) { - if !current_contour.is_empty() { - current_contour.close(); - let contour = mem::replace(&mut current_contour, Contour::new()); - outline.push_contour(contour); - } - continue; - } - - if segment.is_none() { - continue; - } - - if !segment.is_line() { - current_contour.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0, true); - if !segment.is_quadratic() { - current_contour.push_point( - segment.ctrl.to(), - PointFlags::CONTROL_POINT_1, - true, - ); - } - } - - current_contour.push_point(segment.baseline.to(), PointFlags::empty(), true); - } - - outline.push_contour(current_contour); - outline - } - - #[inline] - pub fn from_rect(rect: RectF) -> Outline { - let mut outline = Outline::new(); - outline.push_contour(Contour::from_rect(rect)); - outline - } - - #[inline] - pub fn bounds(&self) -> RectF { - self.bounds - } - - #[inline] - pub fn contours(&self) -> &[Contour] { - &self.contours - } - - #[inline] - pub fn into_contours(self) -> Vec { - self.contours - } - - /// Removes all contours from this outline. - #[inline] - pub fn clear(&mut self) { - self.contours.clear(); - self.bounds = RectF::default(); - } - - pub fn push_contour(&mut self, contour: Contour) { - if contour.is_empty() { - return; - } - - if self.contours.is_empty() { - self.bounds = contour.bounds; - } else { - self.bounds = self.bounds.union_rect(contour.bounds); - } - - self.contours.push(contour); - } - - pub fn pop_contour(&mut self) -> Option { - let last_contour = self.contours.pop(); - - let mut new_bounds = None; - for contour in &mut self.contours { - contour.update_bounds(&mut new_bounds); - } - self.bounds = new_bounds.unwrap_or_else(|| RectF::default()); - - last_contour - } - - pub fn transform(&mut self, transform: &Transform2F) { - if transform.is_identity() { - return; - } - - let mut new_bounds = None; - for contour in &mut self.contours { - contour.transform(transform); - contour.update_bounds(&mut new_bounds); - } - self.bounds = new_bounds.unwrap_or_else(|| RectF::default()); - } - - pub fn apply_perspective(&mut self, perspective: &Perspective) { - let mut new_bounds = None; - for contour in &mut self.contours { - contour.apply_perspective(perspective); - contour.update_bounds(&mut new_bounds); - } - self.bounds = new_bounds.unwrap_or_else(|| RectF::default()); - } - - pub fn dilate(&mut self, amount: Vector2F) { - let orientation = Orientation::from_outline(self); - self.contours - .iter_mut() - .for_each(|contour| contour.dilate(amount, orientation)); - self.bounds = self.bounds.dilate(amount); - } - - pub fn prepare_for_tiling(&mut self, view_box: RectF) { - self.contours - .iter_mut() - .for_each(|contour| contour.prepare_for_tiling(view_box)); - self.bounds = self - .bounds - .intersection(view_box) - .unwrap_or_else(|| RectF::default()); - } - - pub fn is_outside_polygon(&self, clip_polygon: &[Vector2F]) -> bool { - clip::rect_is_outside_polygon(self.bounds, clip_polygon) - } - - fn is_inside_polygon(&self, clip_polygon: &[Vector2F]) -> bool { - clip::rect_is_inside_polygon(self.bounds, clip_polygon) - } - - pub fn clip_against_polygon(&mut self, clip_polygon: &[Vector2F]) { - // Quick check. - if self.is_inside_polygon(clip_polygon) { - return; - } - - for contour in mem::replace(&mut self.contours, vec![]) { - self.push_contour(ContourPolygonClipper::new(clip_polygon, contour).clip()); - } - } - - pub fn clip_against_rect(&mut self, clip_rect: RectF) { - if clip_rect.contains_rect(self.bounds) { - return; - } - - for contour in mem::replace(&mut self.contours, vec![]) { - self.push_contour(ContourRectClipper::new(clip_rect, contour).clip()); - } - } - - #[inline] - pub fn close_all_contours(&mut self) { - self.contours.iter_mut().for_each(|contour| contour.close()); - } -} - -impl Debug for Outline { - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - for (contour_index, contour) in self.contours.iter().enumerate() { - if contour_index > 0 { - write!(formatter, " ")?; - } - contour.fmt(formatter)?; - } - Ok(()) - } -} - -impl Contour { - #[inline] - pub fn new() -> Contour { - Contour { - points: vec![], - flags: vec![], - bounds: RectF::default(), - closed: false, - } - } - - #[inline] - pub fn with_capacity(length: usize) -> Contour { - Contour { - points: Vec::with_capacity(length), - flags: Vec::with_capacity(length), - bounds: RectF::default(), - closed: false, - } - } - - #[inline] - pub fn from_rect(rect: RectF) -> Contour { - let mut contour = Contour::new(); - contour.push_point(rect.origin(), PointFlags::empty(), false); - contour.push_point(rect.upper_right(), PointFlags::empty(), false); - contour.push_point(rect.lower_right(), PointFlags::empty(), false); - contour.push_point(rect.lower_left(), PointFlags::empty(), false); - contour.close(); - contour.bounds = rect; - contour - } - - // Replaces this contour with a new one, with arrays preallocated to match `self`. - #[inline] - pub(crate) fn take(&mut self) -> Contour { - let length = self.len() as usize; - mem::replace( - self, - Contour { - points: Vec::with_capacity(length), - flags: Vec::with_capacity(length), - bounds: RectF::default(), - closed: false, - }, - ) - } - - /// restore self to the state of Contour::new(), but keep the points buffer allocated - #[inline] - pub fn clear(&mut self) { - self.points.clear(); - self.flags.clear(); - self.bounds = RectF::default(); - self.closed = false; - } - - #[inline] - pub fn iter(&self, flags: ContourIterFlags) -> ContourIter { - ContourIter { - contour: self, - index: 1, - flags, - } - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.points.is_empty() - } - - #[inline] - pub fn len(&self) -> u32 { - self.points.len() as u32 - } - - #[inline] - pub fn bounds(&self) -> RectF { - self.bounds - } - - #[inline] - pub fn is_closed(&self) -> bool { - self.closed - } - - #[inline] - pub fn position_of(&self, index: u32) -> Vector2F { - self.points[index as usize] - } - - #[inline] - pub fn last_position(&self) -> Option { - self.points.last().cloned() - } - - #[inline] - pub(crate) fn position_of_last(&self, index: u32) -> Vector2F { - self.points[self.points.len() - index as usize] - } - - #[inline] - pub fn push_endpoint(&mut self, point: Vector2F) { - self.push_point(point, PointFlags::empty(), true); - } - - #[inline] - pub fn push_quadratic(&mut self, ctrl: Vector2F, point: Vector2F) { - self.push_point(ctrl, PointFlags::CONTROL_POINT_0, true); - self.push_point(point, PointFlags::empty(), true); - } - - #[inline] - pub fn push_cubic(&mut self, ctrl0: Vector2F, ctrl1: Vector2F, point: Vector2F) { - self.push_point(ctrl0, PointFlags::CONTROL_POINT_0, true); - self.push_point(ctrl1, PointFlags::CONTROL_POINT_1, true); - self.push_point(point, PointFlags::empty(), true); - } - - #[inline] - pub fn close(&mut self) { - self.closed = true; - } - - #[inline] - pub(crate) fn push_point(&mut self, - point: Vector2F, - flags: PointFlags, - update_bounds: bool) { - debug_assert!(!point.x().is_nan() && !point.y().is_nan()); - - if update_bounds { - let first = self.is_empty(); - union_rect(&mut self.bounds, point, first); - } - - self.points.push(point); - self.flags.push(flags); - } - - #[inline] - pub(crate) fn push_segment(&mut self, segment: &Segment, flags: PushSegmentFlags) { - if segment.is_none() { - return; - } - - let update_bounds = flags.contains(PushSegmentFlags::UPDATE_BOUNDS); - self.push_point(segment.baseline.from(), PointFlags::empty(), update_bounds); - - if !segment.is_line() { - self.push_point( - segment.ctrl.from(), - PointFlags::CONTROL_POINT_0, - update_bounds, - ); - if !segment.is_quadratic() { - self.push_point( - segment.ctrl.to(), - PointFlags::CONTROL_POINT_1, - update_bounds, - ); - } - } - - self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds); - } - - pub fn push_arc(&mut self, - transform: &Transform2F, - start_angle: f32, - end_angle: f32, - direction: ArcDirection) { - if end_angle - start_angle >= PI * 2.0 { - self.push_ellipse(transform); - } else { - let start = vec2f(start_angle.cos(), start_angle.sin()); - let end = vec2f(end_angle.cos(), end_angle.sin()); - self.push_arc_from_unit_chord(transform, LineSegment2F::new(start, end), direction); - } - } - - pub fn push_arc_from_unit_chord(&mut self, - transform: &Transform2F, - mut chord: LineSegment2F, - direction: ArcDirection) { - let mut direction_transform = Transform2F::default(); - if direction == ArcDirection::CCW { - chord *= vec2f(1.0, -1.0); - direction_transform = Transform2F::from_scale(vec2f(1.0, -1.0)); - } - - let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to())); - for segment_index in 0..4 { - debug!("push_arc_from_unit_chord(): loop segment index {}", segment_index); - - let mut sweep_vector = end_vector.rev_rotate_by(vector); - let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON; - debug!("... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}", - end_vector, - vector, - sweep_vector, - last); - - let mut segment; - if !last { - sweep_vector = UnitVector(vec2f(0.0, 1.0)); - segment = Segment::quarter_circle_arc(); - } else { - segment = Segment::arc_from_cos(sweep_vector.0.x()); - } - - let half_sweep_vector = sweep_vector.halve_angle(); - let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector)); - segment = segment.transform(&(*transform * direction_transform * rotation)); - - let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS; - if segment_index == 0 { - push_segment_flags.insert(PushSegmentFlags::INCLUDE_FROM_POINT); - } - self.push_segment(&segment, push_segment_flags); - - if last { - break; - } - - vector = vector.rotate_by(sweep_vector); - } - - const EPSILON: f32 = 0.001; - } - - pub fn push_ellipse(&mut self, transform: &Transform2F) { - let segment = Segment::quarter_circle_arc(); - let mut rotation; - self.push_segment(&segment.transform(transform), - PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT); - rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, 1.0))); - self.push_segment(&segment.transform(&(*transform * rotation)), - PushSegmentFlags::UPDATE_BOUNDS); - rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0, 0.0))); - self.push_segment(&segment.transform(&(*transform * rotation)), - PushSegmentFlags::UPDATE_BOUNDS); - rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, -1.0))); - self.push_segment(&segment.transform(&(*transform * rotation)), - PushSegmentFlags::UPDATE_BOUNDS); - } - - #[inline] - pub fn segment_after(&self, point_index: u32) -> Segment { - debug_assert!(self.point_is_endpoint(point_index)); - - let mut segment = Segment::none(); - segment.baseline.set_from(self.position_of(point_index)); - - let point1_index = self.add_to_point_index(point_index, 1); - if self.point_is_endpoint(point1_index) { - segment.baseline.set_to(self.position_of(point1_index)); - segment.kind = SegmentKind::Line; - } else { - segment.ctrl.set_from(self.position_of(point1_index)); - - let point2_index = self.add_to_point_index(point_index, 2); - if self.point_is_endpoint(point2_index) { - segment.baseline.set_to(self.position_of(point2_index)); - segment.kind = SegmentKind::Quadratic; - } else { - segment.ctrl.set_to(self.position_of(point2_index)); - segment.kind = SegmentKind::Cubic; - - let point3_index = self.add_to_point_index(point_index, 3); - segment.baseline.set_to(self.position_of(point3_index)); - } - } - - segment - } - - #[inline] - pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegment2F { - let next_point_index = self.next_point_index_of(prev_point_index); - LineSegment2F::new( - self.points[prev_point_index as usize], - self.points[next_point_index as usize], - ) - } - - #[inline] - pub fn point_is_endpoint(&self, point_index: u32) -> bool { - !self.flags[point_index as usize] - .intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1) - } - - #[inline] - pub fn add_to_point_index(&self, point_index: u32, addend: u32) -> u32 { - let (index, limit) = (point_index + addend, self.len()); - if index >= limit { - index - limit - } else { - index - } - } - - #[inline] - pub fn point_is_logically_above(&self, a: u32, b: u32) -> bool { - let (a_y, b_y) = (self.points[a as usize].y(), self.points[b as usize].y()); - a_y < b_y || (a_y == b_y && a < b) - } - - #[inline] - pub fn prev_endpoint_index_of(&self, mut point_index: u32) -> u32 { - loop { - point_index = self.prev_point_index_of(point_index); - if self.point_is_endpoint(point_index) { - return point_index; - } - } - } - - #[inline] - pub fn next_endpoint_index_of(&self, mut point_index: u32) -> u32 { - loop { - point_index = self.next_point_index_of(point_index); - if self.point_is_endpoint(point_index) { - return point_index; - } - } - } - - #[inline] - pub fn prev_point_index_of(&self, point_index: u32) -> u32 { - if point_index == 0 { - self.len() - 1 - } else { - point_index - 1 - } - } - - #[inline] - pub fn next_point_index_of(&self, point_index: u32) -> u32 { - if point_index == self.len() - 1 { - 0 - } else { - point_index + 1 - } - } - - pub fn transform(&mut self, transform: &Transform2F) { - if transform.is_identity() { - return; - } - - for (point_index, point) in self.points.iter_mut().enumerate() { - *point = *transform * *point; - union_rect(&mut self.bounds, *point, point_index == 0); - } - } - - pub fn apply_perspective(&mut self, perspective: &Perspective) { - for (point_index, point) in self.points.iter_mut().enumerate() { - *point = *perspective * *point; - union_rect(&mut self.bounds, *point, point_index == 0); - } - } - - pub fn dilate(&mut self, amount: Vector2F, orientation: Orientation) { - ContourDilator::new(self, amount, orientation).dilate(); - self.bounds = self.bounds.dilate(amount); - } - - fn prepare_for_tiling(&mut self, view_box: RectF) { - // Snap points to the view box bounds. This mops up floating point error from the clipping - // process. - let (mut last_endpoint_index, mut contour_is_monotonic) = (None, true); - for point_index in 0..(self.points.len() as u32) { - if contour_is_monotonic { - if self.point_is_endpoint(point_index) { - if let Some(last_endpoint_index) = last_endpoint_index { - if !self.curve_with_endpoints_is_monotonic(last_endpoint_index, - point_index) { - contour_is_monotonic = false; - } - } - last_endpoint_index = Some(point_index); - } - } - } - - // Convert to monotonic, if necessary. - if !contour_is_monotonic { - self.make_monotonic(); - } - - // Update bounds. - self.bounds = self - .bounds - .intersection(view_box) - .unwrap_or_else(|| RectF::default()); - } - - fn make_monotonic(&mut self) { - debug!("--- make_monotonic() ---"); - - let contour = self.take(); - self.bounds = contour.bounds; - - let mut last_endpoint_index = None; - let input_point_count = contour.points.len() as u32; - for point_index in 0..(input_point_count + 1) { - if point_index < input_point_count && !contour.point_is_endpoint(point_index) { - continue; - } - - if let Some(last_endpoint_index) = last_endpoint_index { - let position_index = if point_index == input_point_count { - 0 - } else { - point_index - }; - let baseline = LineSegment2F::new( - contour.points[last_endpoint_index as usize], - contour.points[position_index as usize], - ); - let point_count = point_index - last_endpoint_index + 1; - if point_count == 3 { - let ctrl_point_index = last_endpoint_index as usize + 1; - let ctrl_position = &contour.points[ctrl_point_index]; - handle_cubic( - self, - &Segment::quadratic(baseline, *ctrl_position).to_cubic(), - ); - } else if point_count == 4 { - let first_ctrl_point_index = last_endpoint_index as usize + 1; - let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0]; - let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1]; - let ctrl = LineSegment2F::new(*ctrl_position_0, *ctrl_position_1); - handle_cubic(self, &Segment::cubic(baseline, ctrl)); - } - - self.push_point( - contour.points[position_index as usize], - PointFlags::empty(), - false, - ); - } - - last_endpoint_index = Some(point_index); - } - - fn handle_cubic(contour: &mut Contour, segment: &Segment) { - debug!("handle_cubic({:?})", segment); - - match segment.as_cubic_segment().y_extrema() { - (Some(t0), Some(t1)) => { - let (segments_01, segment_2) = segment.as_cubic_segment().split(t1); - let (segment_0, segment_1) = segments_01.as_cubic_segment().split(t0 / t1); - contour.push_segment(&segment_0, PushSegmentFlags::empty()); - contour.push_segment(&segment_1, PushSegmentFlags::empty()); - contour.push_segment(&segment_2, PushSegmentFlags::empty()); - } - (Some(t0), None) | (None, Some(t0)) => { - let (segment_0, segment_1) = segment.as_cubic_segment().split(t0); - contour.push_segment(&segment_0, PushSegmentFlags::empty()); - contour.push_segment(&segment_1, PushSegmentFlags::empty()); - } - (None, None) => contour.push_segment(segment, PushSegmentFlags::empty()), - } - } - } - - fn curve_with_endpoints_is_monotonic( - &self, - start_endpoint_index: u32, - end_endpoint_index: u32, - ) -> bool { - let start_position = self.points[start_endpoint_index as usize]; - let end_position = self.points[end_endpoint_index as usize]; - - if start_position.x() <= end_position.x() { - for point_index in start_endpoint_index..end_endpoint_index { - if self.points[point_index as usize].x() > self.points[point_index as usize + 1].x() - { - return false; - } - } - } else { - for point_index in start_endpoint_index..end_endpoint_index { - if self.points[point_index as usize].x() < self.points[point_index as usize + 1].x() - { - return false; - } - } - } - - if start_position.y() <= end_position.y() { - for point_index in start_endpoint_index..end_endpoint_index { - if self.points[point_index as usize].y() > self.points[point_index as usize + 1].y() - { - return false; - } - } - } else { - for point_index in start_endpoint_index..end_endpoint_index { - if self.points[point_index as usize].y() < self.points[point_index as usize + 1].y() - { - return false; - } - } - } - - true - } - - // Use this function to keep bounds up to date when mutating paths. See `Outline::transform()` - // for an example of use. - pub(crate) fn update_bounds(&self, bounds: &mut Option) { - *bounds = Some(match *bounds { - None => self.bounds, - Some(bounds) => bounds.union_rect(self.bounds), - }) - } -} - -impl Debug for Contour { - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT) - .enumerate() { - if segment_index == 0 { - write!( - formatter, - "M {} {}", - segment.baseline.from_x(), - segment.baseline.from_y() - )?; - } - - match segment.kind { - SegmentKind::None => {} - SegmentKind::Line => { - write!( - formatter, - " L {} {}", - segment.baseline.to_x(), - segment.baseline.to_y() - )?; - } - SegmentKind::Quadratic => { - write!( - formatter, - " Q {} {} {} {}", - segment.ctrl.from_x(), - segment.ctrl.from_y(), - segment.baseline.to_x(), - segment.baseline.to_y() - )?; - } - SegmentKind::Cubic => { - write!( - formatter, - " C {} {} {} {} {} {}", - segment.ctrl.from_x(), - segment.ctrl.from_y(), - segment.ctrl.to_x(), - segment.ctrl.to_y(), - segment.baseline.to_x(), - segment.baseline.to_y() - )?; - } - } - } - - if self.closed { - write!(formatter, " z")?; - } - - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] -pub struct PointIndex(u32); - -impl PointIndex { - #[inline] - pub fn new(contour: u32, point: u32) -> PointIndex { - debug_assert!(contour <= 0xfff); - debug_assert!(point <= 0x000f_ffff); - PointIndex((contour << 20) | point) - } - - #[inline] - pub fn contour(self) -> u32 { - self.0 >> 20 - } - - #[inline] - pub fn point(self) -> u32 { - self.0 & 0x000f_ffff - } -} - -pub struct ContourIter<'a> { - contour: &'a Contour, - index: u32, - flags: ContourIterFlags, -} - -impl<'a> Iterator for ContourIter<'a> { - type Item = Segment; - - #[inline] - fn next(&mut self) -> Option { - let contour = self.contour; - - let include_close_segment = self.contour.closed && - !self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT); - if (self.index == contour.len() && !include_close_segment) || - self.index == contour.len() + 1 { - return None; - } - - let point0_index = self.index - 1; - let point0 = contour.position_of(point0_index); - if self.index == contour.len() { - let point1 = contour.position_of(0); - self.index += 1; - return Some(Segment::line(LineSegment2F::new(point0, point1))); - } - - let point1_index = self.index; - self.index += 1; - let point1 = contour.position_of(point1_index); - if contour.point_is_endpoint(point1_index) { - return Some(Segment::line(LineSegment2F::new(point0, point1))); - } - - let point2_index = self.index; - let point2 = contour.position_of(point2_index); - self.index += 1; - if contour.point_is_endpoint(point2_index) { - return Some(Segment::quadratic(LineSegment2F::new(point0, point2), point1)); - } - - let point3_index = self.index; - let point3 = contour.position_of(point3_index); - self.index += 1; - debug_assert!(contour.point_is_endpoint(point3_index)); - return Some(Segment::cubic( - LineSegment2F::new(point0, point3), - LineSegment2F::new(point1, point2), - )); - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ArcDirection { - CW, - CCW, -} - -bitflags! { - pub struct ContourIterFlags: u8 { - const IGNORE_CLOSE_SEGMENT = 1; - } -} - -#[inline] -pub(crate) fn union_rect(bounds: &mut RectF, new_point: Vector2F, first: bool) { - if first { - *bounds = RectF::from_points(new_point, new_point); - } else { - *bounds = bounds.union_point(new_point) - } -} diff --git a/crates/pathfinder/content/src/pattern.rs b/crates/pathfinder/content/src/pattern.rs deleted file mode 100644 index cdd34205cf..0000000000 --- a/crates/pathfinder/content/src/pattern.rs +++ /dev/null @@ -1,226 +0,0 @@ -// pathfinder/content/src/pattern.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Raster image patterns. - -use crate::effects::PatternFilter; -use crate::render_target::RenderTargetId; -use crate::util; -use pathfinder_color::{self as color, ColorU}; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::{Vector2I, vec2i}; -use std::collections::hash_map::DefaultHasher; -use std::fmt::{self, Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::sync::Arc; - -#[cfg(feature = "pf-image")] -use image::RgbaImage; - -/// A raster image pattern. -#[derive(Clone, PartialEq, Debug)] -pub struct Pattern { - source: PatternSource, - transform: Transform2F, - filter: Option, - flags: PatternFlags, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub enum PatternSource { - Image(Image), - RenderTarget { - id: RenderTargetId, - size: Vector2I, - } -} - -/// RGBA, non-premultiplied. -// FIXME(pcwalton): Hash the pixel contents so that we don't have to compare every pixel! -// TODO(pcwalton): Should the pixels be premultiplied? -#[derive(Clone, PartialEq, Eq)] -pub struct Image { - size: Vector2I, - pixels: Arc>, - pixels_hash: u64, - is_opaque: bool, -} - -bitflags! { - pub struct PatternFlags: u8 { - const REPEAT_X = 0x01; - const REPEAT_Y = 0x02; - const NO_SMOOTHING = 0x04; - } -} - -impl Pattern { - #[inline] - fn from_source(source: PatternSource) -> Pattern { - Pattern { - source, - transform: Transform2F::default(), - filter: None, - flags: PatternFlags::empty(), - } - } - - #[inline] - pub fn from_image(image: Image) -> Pattern { - Pattern::from_source(PatternSource::Image(image)) - } - - #[inline] - pub fn from_render_target(id: RenderTargetId, size: Vector2I) -> Pattern { - Pattern::from_source(PatternSource::RenderTarget { id, size }) - } - - #[inline] - pub fn transform(&self) -> Transform2F { - self.transform - } - - #[inline] - pub fn apply_transform(&mut self, transform: Transform2F) { - self.transform = transform * self.transform; - } - - #[inline] - pub fn size(&self) -> Vector2I { - match self.source { - PatternSource::Image(ref image) => image.size(), - PatternSource::RenderTarget { size, .. } => size, - } - } - - #[inline] - pub fn filter(&self) -> Option { - self.filter - } - - #[inline] - pub fn set_filter(&mut self, filter: Option) { - self.filter = filter; - } - - #[inline] - pub fn repeat_x(&self) -> bool { - self.flags.contains(PatternFlags::REPEAT_X) - } - - #[inline] - pub fn set_repeat_x(&mut self, repeat_x: bool) { - self.flags.set(PatternFlags::REPEAT_X, repeat_x); - } - - #[inline] - pub fn repeat_y(&self) -> bool { - self.flags.contains(PatternFlags::REPEAT_Y) - } - - #[inline] - pub fn set_repeat_y(&mut self, repeat_y: bool) { - self.flags.set(PatternFlags::REPEAT_Y, repeat_y); - } - - #[inline] - pub fn smoothing_enabled(&self) -> bool { - !self.flags.contains(PatternFlags::NO_SMOOTHING) - } - - #[inline] - pub fn set_smoothing_enabled(&mut self, enable: bool) { - self.flags.set(PatternFlags::NO_SMOOTHING, !enable); - } - - #[inline] - pub fn is_opaque(&self) -> bool { - self.source.is_opaque() - } - - #[inline] - pub fn source(&self) -> &PatternSource { - &self.source - } -} - -impl Image { - #[inline] - pub fn new(size: Vector2I, pixels: Arc>) -> Image { - assert_eq!(size.x() as usize * size.y() as usize, pixels.len()); - let is_opaque = pixels.iter().all(|pixel| pixel.is_opaque()); - - let mut pixels_hasher = DefaultHasher::new(); - pixels.hash(&mut pixels_hasher); - let pixels_hash = pixels_hasher.finish(); - - Image { size, pixels, pixels_hash, is_opaque } - } - - #[cfg(feature = "pf-image")] - pub fn from_image_buffer(image_buffer: RgbaImage) -> Image { - let (width, height) = image_buffer.dimensions(); - let pixels = color::u8_vec_to_color_vec(image_buffer.into_raw()); - Image::new(vec2i(width as i32, height as i32), Arc::new(pixels)) - } - - #[inline] - pub fn size(&self) -> Vector2I { - self.size - } - - #[inline] - pub fn pixels(&self) -> &Arc> { - &self.pixels - } - - #[inline] - pub fn is_opaque(&self) -> bool { - self.is_opaque - } -} - -impl PatternSource { - #[inline] - pub fn is_opaque(&self) -> bool { - match *self { - PatternSource::Image(ref image) => image.is_opaque(), - PatternSource::RenderTarget { .. } => { - // TODO(pcwalton): Maybe do something smarter here? - false - } - } - } -} - -impl Debug for Image { - #[inline] - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - write!(formatter, "(image {}×{} px)", self.size.x(), self.size.y()) - } -} - -impl Hash for Image { - fn hash(&self, hasher: &mut H) where H: Hasher { - self.size.hash(hasher); - self.pixels_hash.hash(hasher); - self.is_opaque.hash(hasher); - } -} - -impl Eq for Pattern {} - -impl Hash for Pattern { - fn hash(&self, state: &mut H) where H: Hasher { - self.source.hash(state); - util::hash_transform2f(self.transform, state); - self.flags.hash(state); - } -} diff --git a/crates/pathfinder/content/src/render_target.rs b/crates/pathfinder/content/src/render_target.rs deleted file mode 100644 index ffd1b88f65..0000000000 --- a/crates/pathfinder/content/src/render_target.rs +++ /dev/null @@ -1,17 +0,0 @@ -// pathfinder/content/src/render_target.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Render targets. - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct RenderTargetId { - pub scene: u32, - pub render_target: u32, -} diff --git a/crates/pathfinder/content/src/segment.rs b/crates/pathfinder/content/src/segment.rs deleted file mode 100644 index 3c607f7990..0000000000 --- a/crates/pathfinder/content/src/segment.rs +++ /dev/null @@ -1,426 +0,0 @@ -// pathfinder/content/src/segment.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Line or curve segments, optimized with SIMD. - -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::util::{self, EPSILON}; -use pathfinder_geometry::vector::{Vector2F, vec2f}; -use pathfinder_simd::default::F32x4; -use std::f32::consts::SQRT_2; - -const MAX_NEWTON_ITERATIONS: u32 = 32; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Segment { - pub baseline: LineSegment2F, - pub ctrl: LineSegment2F, - pub kind: SegmentKind, - pub flags: SegmentFlags, -} - -impl Segment { - #[inline] - pub fn none() -> Segment { - Segment { - baseline: LineSegment2F::default(), - ctrl: LineSegment2F::default(), - kind: SegmentKind::None, - flags: SegmentFlags::empty(), - } - } - - #[inline] - pub fn line(line: LineSegment2F) -> Segment { - Segment { - baseline: line, - ctrl: LineSegment2F::default(), - kind: SegmentKind::Line, - flags: SegmentFlags::empty(), - } - } - - #[inline] - pub fn quadratic(baseline: LineSegment2F, ctrl: Vector2F) -> Segment { - Segment { - baseline, - ctrl: LineSegment2F::new(ctrl, Vector2F::zero()), - kind: SegmentKind::Quadratic, - flags: SegmentFlags::empty(), - } - } - - #[inline] - pub fn cubic(baseline: LineSegment2F, ctrl: LineSegment2F) -> Segment { - Segment { - baseline, - ctrl, - kind: SegmentKind::Cubic, - flags: SegmentFlags::empty(), - } - } - - /// Approximates an unit-length arc with a cubic Bézier curve. - /// - /// The maximum supported sweep angle is π/2 (i.e. 90°). - pub fn arc(sweep_angle: f32) -> Segment { - Segment::arc_from_cos(f32::cos(sweep_angle)) - } - - /// Approximates an unit-length arc with a cubic Bézier curve, given the cosine of the sweep - /// angle. - /// - /// The maximum supported sweep angle is π/2 (i.e. 90°). - pub fn arc_from_cos(cos_sweep_angle: f32) -> Segment { - // Richard A. DeVeneza, "How to determine the control points of a Bézier curve that - // approximates a small arc", 2004. - // - // https://www.tinaja.com/glib/bezcirc2.pdf - if cos_sweep_angle >= 1.0 - EPSILON { - return Segment::line(LineSegment2F::new(vec2f(1.0, 0.0), vec2f(1.0, 0.0))); - } - - let term = F32x4::new(cos_sweep_angle, -cos_sweep_angle, - cos_sweep_angle, -cos_sweep_angle); - let signs = F32x4::new(1.0, -1.0, 1.0, 1.0); - let p3p0 = ((F32x4::splat(1.0) + term) * F32x4::splat(0.5)).sqrt() * signs; - let (p0x, p0y) = (p3p0.z(), p3p0.w()); - let (p1x, p1y) = (4.0 - p0x, (1.0 - p0x) * (3.0 - p0x) / p0y); - let p2p1 = F32x4::new(p1x, -p1y, p1x, p1y) * F32x4::splat(1.0 / 3.0); - return Segment::cubic(LineSegment2F(p3p0), LineSegment2F(p2p1)); - } - - #[inline] - pub fn quarter_circle_arc() -> Segment { - let p0 = Vector2F::splat(SQRT_2 * 0.5); - let p1 = vec2f(-SQRT_2 / 6.0 + 4.0 / 3.0, 7.0 * SQRT_2 / 6.0 - 4.0 / 3.0); - let flip = vec2f(1.0, -1.0); - let (p2, p3) = (p1 * flip, p0 * flip); - Segment::cubic(LineSegment2F::new(p3, p0), LineSegment2F::new(p2, p1)) - } - - #[inline] - pub fn as_line_segment(&self) -> LineSegment2F { - debug_assert!(self.is_line()); - self.baseline - } - - #[inline] - pub fn is_none(&self) -> bool { - self.kind == SegmentKind::None - } - - #[inline] - pub fn is_line(&self) -> bool { - self.kind == SegmentKind::Line - } - - #[inline] - pub fn is_quadratic(&self) -> bool { - self.kind == SegmentKind::Quadratic - } - - #[inline] - pub fn is_cubic(&self) -> bool { - self.kind == SegmentKind::Cubic - } - - #[inline] - pub fn as_cubic_segment(&self) -> CubicSegment { - debug_assert!(self.is_cubic()); - CubicSegment(self) - } - - // FIXME(pcwalton): We should basically never use this function. - // FIXME(pcwalton): Handle lines! - #[inline] - pub fn to_cubic(&self) -> Segment { - if self.is_cubic() { - return *self; - } - - let mut new_segment = *self; - let p1_2 = self.ctrl.from() + self.ctrl.from(); - new_segment.ctrl = LineSegment2F::new(self.baseline.from() + p1_2, - p1_2 + self.baseline.to()) * (1.0 / 3.0); - new_segment.kind = SegmentKind::Cubic; - new_segment - } - - #[inline] - pub fn is_monotonic(&self) -> bool { - // FIXME(pcwalton): Don't degree elevate! - match self.kind { - SegmentKind::None | SegmentKind::Line => true, - SegmentKind::Quadratic => self.to_cubic().as_cubic_segment().is_monotonic(), - SegmentKind::Cubic => self.as_cubic_segment().is_monotonic(), - } - } - - #[inline] - pub fn reversed(&self) -> Segment { - Segment { - baseline: self.baseline.reversed(), - ctrl: if self.is_quadratic() { - self.ctrl - } else { - self.ctrl.reversed() - }, - kind: self.kind, - flags: self.flags, - } - } - - // Reverses if necessary so that the from point is above the to point. Calling this method - // again will undo the transformation. - #[inline] - pub fn orient(&self, y_winding: i32) -> Segment { - if y_winding >= 0 { - *self - } else { - self.reversed() - } - } - - #[inline] - pub fn is_tiny(&self) -> bool { - const EPSILON: f32 = 0.0001; - self.baseline.square_length() < EPSILON - } - - #[inline] - pub fn split(&self, t: f32) -> (Segment, Segment) { - // FIXME(pcwalton): Don't degree elevate! - if self.is_line() { - let (before, after) = self.as_line_segment().split(t); - (Segment::line(before), Segment::line(after)) - } else { - self.to_cubic().as_cubic_segment().split(t) - } - } - - #[inline] - pub fn sample(self, t: f32) -> Vector2F { - // FIXME(pcwalton): Don't degree elevate! - if self.is_line() { - self.as_line_segment().sample(t) - } else { - self.to_cubic().as_cubic_segment().sample(t) - } - } - - #[inline] - pub fn transform(self, transform: &Transform2F) -> Segment { - Segment { - baseline: *transform * self.baseline, - ctrl: *transform * self.ctrl, - kind: self.kind, - flags: self.flags, - } - } - - pub fn arc_length(&self) -> f32 { - // FIXME(pcwalton) - self.baseline.vector().length() - } - - pub fn time_for_distance(&self, distance: f32) -> f32 { - // FIXME(pcwalton) - distance / self.arc_length() - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -#[repr(u8)] -pub enum SegmentKind { - None, - Line, - Quadratic, - Cubic, -} - -bitflags! { - pub struct SegmentFlags: u8 { - const FIRST_IN_SUBPATH = 0x01; - const CLOSES_SUBPATH = 0x02; - } -} - -#[derive(Clone, Copy, Debug)] -pub struct CubicSegment<'s>(pub &'s Segment); - -impl<'s> CubicSegment<'s> { - // See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000. - #[inline] - pub fn is_flat(self, tolerance: f32) -> bool { - let mut uv = F32x4::splat(3.0) * self.0.ctrl.0 - - self.0.baseline.0 - - self.0.baseline.0 - - self.0.baseline.reversed().0; - uv = uv * uv; - uv = uv.max(uv.zwxy()); - uv[0] + uv[1] <= 16.0 * tolerance * tolerance - } - - #[inline] - pub fn split(self, t: f32) -> (Segment, Segment) { - let (baseline0, ctrl0, baseline1, ctrl1); - if t <= 0.0 { - let from = &self.0.baseline.from(); - baseline0 = LineSegment2F::new(*from, *from); - ctrl0 = LineSegment2F::new(*from, *from); - baseline1 = self.0.baseline; - ctrl1 = self.0.ctrl; - } else if t >= 1.0 { - let to = &self.0.baseline.to(); - baseline0 = self.0.baseline; - ctrl0 = self.0.ctrl; - baseline1 = LineSegment2F::new(*to, *to); - ctrl1 = LineSegment2F::new(*to, *to); - } else { - let tttt = F32x4::splat(t); - - let (p0p3, p1p2) = (self.0.baseline.0, self.0.ctrl.0); - let p0p1 = p0p3.concat_xy_xy(p1p2); - - // p01 = lerp(p0, p1, t), p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t) - let p01p12 = p0p1 + tttt * (p1p2 - p0p1); - let pxxp23 = p1p2 + tttt * (p0p3 - p1p2); - let p12p23 = p01p12.concat_zw_zw(pxxp23); - - // p012 = lerp(p01, p12, t), p123 = lerp(p12, p23, t) - let p012p123 = p01p12 + tttt * (p12p23 - p01p12); - let p123 = p012p123.zwzw(); - - // p0123 = lerp(p012, p123, t) - let p0123 = p012p123 + tttt * (p123 - p012p123); - - baseline0 = LineSegment2F(p0p3.concat_xy_xy(p0123)); - ctrl0 = LineSegment2F(p01p12.concat_xy_xy(p012p123)); - baseline1 = LineSegment2F(p0123.concat_xy_zw(p0p3)); - ctrl1 = LineSegment2F(p012p123.concat_zw_zw(p12p23)); - } - - ( - Segment { - baseline: baseline0, - ctrl: ctrl0, - kind: SegmentKind::Cubic, - flags: self.0.flags & SegmentFlags::FIRST_IN_SUBPATH, - }, - Segment { - baseline: baseline1, - ctrl: ctrl1, - kind: SegmentKind::Cubic, - flags: self.0.flags & SegmentFlags::CLOSES_SUBPATH, - }, - ) - } - - #[inline] - pub fn split_before(self, t: f32) -> Segment { - self.split(t).0 - } - - #[inline] - pub fn split_after(self, t: f32) -> Segment { - self.split(t).1 - } - - // FIXME(pcwalton): Use Horner's method! - #[inline] - pub fn sample(self, t: f32) -> Vector2F { - self.split(t).0.baseline.to() - } - - #[inline] - pub fn is_monotonic(self) -> bool { - // TODO(pcwalton): Optimize this. - let (p0, p3) = (self.0.baseline.from_y(), self.0.baseline.to_y()); - let (p1, p2) = (self.0.ctrl.from_y(), self.0.ctrl.to_y()); - (p0 <= p1 && p1 <= p2 && p2 <= p3) || (p0 >= p1 && p1 >= p2 && p2 >= p3) - } - - #[inline] - pub fn y_extrema(self) -> (Option, Option) { - if self.is_monotonic() { - return (None, None); - } - - let p0p1p2p3 = F32x4::new( - self.0.baseline.from_y(), - self.0.ctrl.from_y(), - self.0.ctrl.to_y(), - self.0.baseline.to_y(), - ); - - let pxp0p1p2 = p0p1p2p3.wxyz(); - let pxv0v1v2 = p0p1p2p3 - pxp0p1p2; - let (v0, v1, v2) = (pxv0v1v2[1], pxv0v1v2[2], pxv0v1v2[3]); - - let (t0, t1); - let (v0_to_v1, v2_to_v1) = (v0 - v1, v2 - v1); - let denom = v0_to_v1 + v2_to_v1; - - if util::approx_eq(denom, 0.0) { - // Let's not divide by zero (issue #146). Fall back to Newton's method. - // FIXME(pcwalton): Can we have two roots here? - let mut t = 0.5; - for _ in 0..MAX_NEWTON_ITERATIONS { - let dydt = 3.0 * ((denom * t - v0_to_v1 - v0_to_v1) * t + v0); - if f32::abs(dydt) <= EPSILON { - break - } - let d2ydt2 = 6.0 * (denom * t - v0_to_v1); - t -= dydt / d2ydt2; - } - t0 = t; - t1 = 0.0; - debug!("... t=(newton) {}", t); - } else { - // Algebraically compute the values for t. - let discrim = f32::sqrt(v1 * v1 - v0 * v2); - let denom_recip = 1.0 / denom; - - t0 = (v0_to_v1 + discrim) * denom_recip; - t1 = (v0_to_v1 - discrim) * denom_recip; - - debug!("... t=({} +/- {})/{} t0={} t1={}", v0_to_v1, discrim, denom, t0, t1); - } - - return match ( - t0 > EPSILON && t0 < 1.0 - EPSILON, - t1 > EPSILON && t1 < 1.0 - EPSILON, - ) { - (false, false) => (None, None), - (true, false) => (Some(t0), None), - (false, true) => (Some(t1), None), - (true, true) => (Some(f32::min(t0, t1)), Some(f32::max(t0, t1))), - }; - } - - #[inline] - pub fn min_x(&self) -> f32 { - f32::min(self.0.baseline.min_x(), self.0.ctrl.min_x()) - } - #[inline] - pub fn min_y(&self) -> f32 { - f32::min(self.0.baseline.min_y(), self.0.ctrl.min_y()) - } - #[inline] - pub fn max_x(&self) -> f32 { - f32::max(self.0.baseline.max_x(), self.0.ctrl.max_x()) - } - #[inline] - pub fn max_y(&self) -> f32 { - f32::max(self.0.baseline.max_y(), self.0.ctrl.max_y()) - } -} diff --git a/crates/pathfinder/content/src/sorted_vector.rs b/crates/pathfinder/content/src/sorted_vector.rs deleted file mode 100644 index 21c584f515..0000000000 --- a/crates/pathfinder/content/src/sorted_vector.rs +++ /dev/null @@ -1,100 +0,0 @@ -// pathfinder/content/src/sorted_vector.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A vector that maintains sorted order with insertion sort. - -use std::cmp::Ordering; -use std::convert; - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct SortedVector -where - T: PartialOrd, -{ - pub array: Vec, -} - -impl SortedVector -where - T: PartialOrd, -{ - #[inline] - pub fn new() -> SortedVector { - SortedVector { array: vec![] } - } - - #[inline] - pub fn push(&mut self, value: T) { - let index = self.binary_search_by(|other| { - other.partial_cmp(&value).unwrap_or(Ordering::Less) - }).unwrap_or_else(convert::identity); - self.array.insert(index, value); - } - - #[inline] - pub fn peek(&self) -> Option<&T> { - self.array.last() - } - - #[inline] - pub fn pop(&mut self) -> Option { - self.array.pop() - } - - #[inline] - pub fn clear(&mut self) { - self.array.clear() - } - - #[allow(dead_code)] - #[inline] - pub fn is_empty(&self) -> bool { - self.array.is_empty() - } - - #[inline] - pub fn len(&self) -> usize { - self.array.len() - } - - #[inline] - pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result - where F: FnMut(&'a T) -> Ordering { - self.array.binary_search_by(f) - } -} - -#[cfg(test)] -mod test { - use crate::sorted_vector::SortedVector; - use quickcheck; - - #[test] - fn test_sorted_vec() { - quickcheck::quickcheck(prop_sorted_vec as fn(Vec) -> bool); - - fn prop_sorted_vec(mut values: Vec) -> bool { - let mut sorted_vec = SortedVector::new(); - for &value in &values { - sorted_vec.push(value) - } - - values.sort(); - let mut results = Vec::with_capacity(values.len()); - while !sorted_vec.is_empty() { - results.push(sorted_vec.pop().unwrap()); - } - results.reverse(); - assert_eq!(&values, &results); - - true - } - } -} diff --git a/crates/pathfinder/content/src/stroke.rs b/crates/pathfinder/content/src/stroke.rs deleted file mode 100644 index 385fbecf3d..0000000000 --- a/crates/pathfinder/content/src/stroke.rs +++ /dev/null @@ -1,428 +0,0 @@ -// pathfinder/content/src/stroke.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Utilities for converting path strokes to fills. - -use crate::outline::{ArcDirection, Contour, ContourIterFlags, Outline, PushSegmentFlags}; -use crate::segment::Segment; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::util::EPSILON; -use pathfinder_geometry::vector::{Vector2F, vec2f}; -use std::f32; - -const TOLERANCE: f32 = 0.01; - -pub struct OutlineStrokeToFill<'a> { - input: &'a Outline, - output: Outline, - style: StrokeStyle, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct StrokeStyle { - pub line_width: f32, - pub line_cap: LineCap, - pub line_join: LineJoin, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LineCap { - Butt, - Square, - Round, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LineJoin { - Miter(f32), - Bevel, - Round, -} - -impl<'a> OutlineStrokeToFill<'a> { - #[inline] - pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill { - OutlineStrokeToFill { input, output: Outline::new(), style } - } - - pub fn offset(&mut self) { - let mut new_contours = vec![]; - for input in &self.input.contours { - let closed = input.closed; - let mut stroker = ContourStrokeToFill::new(input, - Contour::new(), - self.style.line_width * 0.5, - self.style.line_join); - - stroker.offset_forward(); - if closed { - self.push_stroked_contour(&mut new_contours, stroker, true); - stroker = ContourStrokeToFill::new(input, - Contour::new(), - self.style.line_width * 0.5, - self.style.line_join); - } else { - self.add_cap(&mut stroker.output); - } - - stroker.offset_backward(); - if !closed { - self.add_cap(&mut stroker.output); - } - - self.push_stroked_contour(&mut new_contours, stroker, closed); - } - - let mut new_bounds = None; - new_contours.iter().for_each(|contour| contour.update_bounds(&mut new_bounds)); - - self.output.contours = new_contours; - self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default()); - } - - #[inline] - pub fn into_outline(self) -> Outline { - self.output - } - - fn push_stroked_contour(&mut self, - new_contours: &mut Vec, - mut stroker: ContourStrokeToFill, - closed: bool) { - // Add join if necessary. - if closed && stroker.output.might_need_join(self.style.line_join) { - let (p1, p0) = (stroker.output.position_of(1), stroker.output.position_of(0)); - let final_segment = LineSegment2F::new(p1, p0); - stroker.output.add_join(self.style.line_width * 0.5, - self.style.line_join, - stroker.input.position_of(0), - final_segment); - } - - stroker.output.closed = true; - new_contours.push(stroker.output); - } - - fn add_cap(&mut self, contour: &mut Contour) { - if self.style.line_cap == LineCap::Butt || contour.len() < 2 { - return - } - - let width = self.style.line_width; - let p1 = contour.position_of_last(1); - - // Determine the ending gradient. - let mut p0; - let mut p0_index = contour.len() - 2; - loop { - p0 = contour.position_of(p0_index); - if (p1 - p0).square_length() > EPSILON { - break; - } - if p0_index == 0 { - return; - } - p0_index -= 1; - } - let gradient = (p1 - p0).normalize(); - - match self.style.line_cap { - LineCap::Butt => unreachable!(), - - LineCap::Square => { - let offset = gradient * (width * 0.5); - - let p2 = p1 + offset; - let p3 = p2 + gradient.yx() * vec2f(-width, width); - let p4 = p3 - offset; - - contour.push_endpoint(p2); - contour.push_endpoint(p3); - contour.push_endpoint(p4); - } - - LineCap::Round => { - let scale = width * 0.5; - let offset = gradient.yx() * vec2f(-1.0, 1.0); - let translation = p1 + offset * (width * 0.5); - let transform = Transform2F::from_scale(scale).translate(translation); - let chord = LineSegment2F::new(-offset, offset); - contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); - } - } - } -} - -struct ContourStrokeToFill<'a> { - input: &'a Contour, - output: Contour, - radius: f32, - join: LineJoin, -} - -impl<'a> ContourStrokeToFill<'a> { - #[inline] - fn new(input: &Contour, output: Contour, radius: f32, join: LineJoin) -> ContourStrokeToFill { - ContourStrokeToFill { input, output, radius, join } - } - - fn offset_forward(&mut self) { - for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() { - // FIXME(pcwalton): We negate the radius here so that round end caps can be drawn - // clockwise. Of course, we should just implement anticlockwise arcs to begin with... - let join = if segment_index == 0 { LineJoin::Bevel } else { self.join }; - segment.offset(-self.radius, join, &mut self.output); - } - } - - fn offset_backward(&mut self) { - let mut segments: Vec<_> = self - .input - .iter(ContourIterFlags::empty()) - .map(|segment| segment.reversed()) - .collect(); - segments.reverse(); - for (segment_index, segment) in segments.iter().enumerate() { - // FIXME(pcwalton): We negate the radius here so that round end caps can be drawn - // clockwise. Of course, we should just implement anticlockwise arcs to begin with... - let join = if segment_index == 0 { LineJoin::Bevel } else { self.join }; - segment.offset(-self.radius, join, &mut self.output); - } - } -} - -trait Offset { - fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour); - fn add_to_contour(&self, - distance: f32, - join: LineJoin, - join_point: Vector2F, - contour: &mut Contour); - fn offset_once(&self, distance: f32) -> Self; - fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool; -} - -impl Offset for Segment { - fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour) { - let join_point = self.baseline.from(); - if self.baseline.square_length() < TOLERANCE * TOLERANCE { - self.add_to_contour(distance, join, join_point, contour); - return; - } - - let candidate = self.offset_once(distance); - if self.error_is_within_tolerance(&candidate, distance) { - candidate.add_to_contour(distance, join, join_point, contour); - return; - } - - debug!("--- SPLITTING ---"); - debug!("... PRE-SPLIT: {:?}", self); - let (before, after) = self.split(0.5); - debug!("... AFTER-SPLIT: {:?} {:?}", before, after); - before.offset(distance, join, contour); - after.offset(distance, join, contour); - } - - fn add_to_contour(&self, - distance: f32, - join: LineJoin, - join_point: Vector2F, - contour: &mut Contour) { - // Add join if necessary. - if contour.might_need_join(join) { - let p3 = self.baseline.from(); - let p4 = if self.is_line() { - self.baseline.to() - } else { - // NB: If you change the representation of quadratic curves, you will need to - // change this. - self.ctrl.from() - }; - - contour.add_join(distance, join, join_point, LineSegment2F::new(p4, p3)); - } - - // Push segment. - let flags = PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT; - contour.push_segment(self, flags); - } - - fn offset_once(&self, distance: f32) -> Segment { - if self.is_line() { - return Segment::line(self.baseline.offset(distance)); - } - - if self.is_quadratic() { - let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from()); - let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to()); - segment_0 = segment_0.offset(distance); - segment_1 = segment_1.offset(distance); - let ctrl = match segment_0.intersection_t(segment_1) { - Some(t) => segment_0.sample(t), - None => segment_0.to().lerp(segment_1.from(), 0.5), - }; - let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); - return Segment::quadratic(baseline, ctrl); - } - - debug_assert!(self.is_cubic()); - - if self.baseline.from() == self.ctrl.from() { - let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.to()); - let mut segment_1 = LineSegment2F::new(self.ctrl.to(), self.baseline.to()); - segment_0 = segment_0.offset(distance); - segment_1 = segment_1.offset(distance); - let ctrl = match segment_0.intersection_t(segment_1) { - Some(t) => segment_0.sample(t), - None => segment_0.to().lerp(segment_1.from(), 0.5), - }; - let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); - let ctrl = LineSegment2F::new(segment_0.from(), ctrl); - return Segment::cubic(baseline, ctrl); - } - - if self.ctrl.to() == self.baseline.to() { - let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from()); - let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to()); - segment_0 = segment_0.offset(distance); - segment_1 = segment_1.offset(distance); - let ctrl = match segment_0.intersection_t(segment_1) { - Some(t) => segment_0.sample(t), - None => segment_0.to().lerp(segment_1.from(), 0.5), - }; - let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); - let ctrl = LineSegment2F::new(ctrl, segment_1.to()); - return Segment::cubic(baseline, ctrl); - } - - let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from()); - let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.ctrl.to()); - let mut segment_2 = LineSegment2F::new(self.ctrl.to(), self.baseline.to()); - segment_0 = segment_0.offset(distance); - segment_1 = segment_1.offset(distance); - segment_2 = segment_2.offset(distance); - let (ctrl_0, ctrl_1) = match ( - segment_0.intersection_t(segment_1), - segment_1.intersection_t(segment_2), - ) { - (Some(t0), Some(t1)) => (segment_0.sample(t0), segment_1.sample(t1)), - _ => ( - segment_0.to().lerp(segment_1.from(), 0.5), - segment_1.to().lerp(segment_2.from(), 0.5), - ), - }; - let baseline = LineSegment2F::new(segment_0.from(), segment_2.to()); - let ctrl = LineSegment2F::new(ctrl_0, ctrl_1); - Segment::cubic(baseline, ctrl) - } - - fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool { - let (mut min, mut max) = ( - f32::abs(distance) - TOLERANCE, - f32::abs(distance) + TOLERANCE, - ); - min = if min <= 0.0 { 0.0 } else { min * min }; - max = if max <= 0.0 { 0.0 } else { max * max }; - - for t_num in 0..(SAMPLE_COUNT + 1) { - let t = t_num as f32 / SAMPLE_COUNT as f32; - // FIXME(pcwalton): Use signed distance! - let (this_p, other_p) = (self.sample(t), other.sample(t)); - let vector = this_p - other_p; - let square_distance = vector.square_length(); - debug!( - "this_p={:?} other_p={:?} vector={:?} sqdist={:?} min={:?} max={:?}", - this_p, other_p, vector, square_distance, min, max - ); - if square_distance < min || square_distance > max { - return false; - } - } - - return true; - - const SAMPLE_COUNT: u32 = 16; - } -} - -impl Contour { - fn might_need_join(&self, join: LineJoin) -> bool { - if self.len() < 2 { - false - } else { - match join { - LineJoin::Miter(_) | LineJoin::Round => true, - LineJoin::Bevel => false, - } - } - } - - fn add_join(&mut self, - distance: f32, - join: LineJoin, - join_point: Vector2F, - next_tangent: LineSegment2F) { - let (p0, p1) = (self.position_of_last(2), self.position_of_last(1)); - let prev_tangent = LineSegment2F::new(p0, p1); - - if prev_tangent.square_length() < EPSILON || next_tangent.square_length() < EPSILON { - return; - } - - match join { - LineJoin::Bevel => {} - LineJoin::Miter(miter_limit) => { - if let Some(prev_tangent_t) = prev_tangent.intersection_t(next_tangent) { - if prev_tangent_t < -EPSILON { - return; - } - let miter_endpoint = prev_tangent.sample(prev_tangent_t); - let threshold = miter_limit * distance; - if (miter_endpoint - join_point).square_length() > threshold * threshold { - return; - } - self.push_endpoint(miter_endpoint); - } - } - LineJoin::Round => { - let scale = distance.abs(); - let transform = Transform2F::from_scale(scale).translate(join_point); - let chord_from = (prev_tangent.to() - join_point).normalize(); - let chord_to = (next_tangent.to() - join_point).normalize(); - let chord = LineSegment2F::new(chord_from, chord_to); - self.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); - } - } - } -} - -impl Default for StrokeStyle { - #[inline] - fn default() -> StrokeStyle { - StrokeStyle { - line_width: 1.0, - line_cap: LineCap::default(), - line_join: LineJoin::default(), - } - } -} - -impl Default for LineCap { - #[inline] - fn default() -> LineCap { LineCap::Butt } -} - -impl Default for LineJoin { - #[inline] - fn default() -> LineJoin { LineJoin::Miter(10.0) } -} diff --git a/crates/pathfinder/content/src/transform.rs b/crates/pathfinder/content/src/transform.rs deleted file mode 100644 index 4f2cde2b2b..0000000000 --- a/crates/pathfinder/content/src/transform.rs +++ /dev/null @@ -1,106 +0,0 @@ -// pathfinder/content/src/transform.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Utilities for transforming paths. - -use crate::segment::Segment; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::transform3d::Perspective; - -/// Transforms a path with a SIMD 2D transform. -pub struct Transform2FPathIter -where - I: Iterator, -{ - iter: I, - transform: Transform2F, -} - -impl Iterator for Transform2FPathIter -where - I: Iterator, -{ - type Item = Segment; - - #[inline] - fn next(&mut self) -> Option { - // TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD? - let mut segment = self.iter.next()?; - if !segment.is_none() { - segment.baseline.set_from(self.transform * segment.baseline.from()); - segment.baseline.set_to(self.transform * segment.baseline.to()); - if !segment.is_line() { - segment.ctrl.set_from(self.transform * segment.ctrl.from()); - if !segment.is_quadratic() { - segment.ctrl.set_to(self.transform * segment.ctrl.to()); - } - } - } - Some(segment) - } -} - -impl Transform2FPathIter -where - I: Iterator, -{ - #[inline] - pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter { - Transform2FPathIter { - iter, - transform: *transform, - } - } -} - -/// Transforms a path with a perspective projection. -pub struct PerspectivePathIter -where - I: Iterator, -{ - iter: I, - perspective: Perspective, -} - -impl Iterator for PerspectivePathIter -where - I: Iterator, -{ - type Item = Segment; - - #[inline] - fn next(&mut self) -> Option { - let mut segment = self.iter.next()?; - if !segment.is_none() { - segment.baseline.set_from(self.perspective * segment.baseline.from()); - segment.baseline.set_to(self.perspective * segment.baseline.to()); - if !segment.is_line() { - segment.ctrl.set_from(self.perspective * segment.ctrl.from()); - if !segment.is_quadratic() { - segment.ctrl.set_to(self.perspective * segment.ctrl.to()); - } - } - } - Some(segment) - } -} - -impl PerspectivePathIter -where - I: Iterator, -{ - #[inline] - pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter { - PerspectivePathIter { - iter, - perspective: *perspective, - } - } -} diff --git a/crates/pathfinder/content/src/util.rs b/crates/pathfinder/content/src/util.rs deleted file mode 100644 index 8c0c186b4d..0000000000 --- a/crates/pathfinder/content/src/util.rs +++ /dev/null @@ -1,47 +0,0 @@ -// pathfinder/content/src/util.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Miscellaneous utilities. - -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_simd::default::{F32x2, F32x4}; -use std::hash::{Hash, Hasher}; -use std::mem; - -pub(crate) fn hash_line_segment(line_segment: LineSegment2F, state: &mut H) where H: Hasher { - hash_f32x4(line_segment.0, state); -} - -pub(crate) fn hash_transform2f(transform: Transform2F, state: &mut H) where H: Hasher { - hash_f32x4(transform.matrix.0, state); - hash_f32x2(transform.vector.0, state); -} - -pub(crate) fn hash_f32(value: f32, state: &mut H) where H: Hasher { - unsafe { - let data: u32 = mem::transmute::(value); - data.hash(state); - } -} - -pub(crate) fn hash_f32x2(vector: F32x2, state: &mut H) where H: Hasher { - unsafe { - let data: [u32; 2] = mem::transmute::(vector); - data.hash(state); - } -} - -pub(crate) fn hash_f32x4(vector: F32x4, state: &mut H) where H: Hasher { - unsafe { - let data: [u32; 4] = mem::transmute::(vector); - data.hash(state); - } -} diff --git a/crates/pathfinder/export/Cargo.toml b/crates/pathfinder/export/Cargo.toml deleted file mode 100644 index 49db4a001d..0000000000 --- a/crates/pathfinder/export/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "pathfinder_export" -version = "0.1.0" -authors = ["Sebastian Köln "] -edition = "2018" - -[dependencies] -pathfinder_color = { path = "../color" } -pathfinder_content = { path = "../content" } -pathfinder_geometry = { path = "../geometry" } -pathfinder_renderer = { path = "../renderer" } -deflate = "*" diff --git a/crates/pathfinder/export/src/lib.rs b/crates/pathfinder/export/src/lib.rs deleted file mode 100644 index 4a4ab5b54b..0000000000 --- a/crates/pathfinder/export/src/lib.rs +++ /dev/null @@ -1,194 +0,0 @@ -// pathfinder/export/src/lib.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use pathfinder_content::outline::ContourIterFlags; -use pathfinder_content::segment::SegmentKind; -use pathfinder_renderer::scene::Scene; -use pathfinder_geometry::vector::{Vector2F, vec2f}; -use std::fmt; -use std::io::{self, Write}; - -mod pdf; -use pdf::Pdf; - -pub enum FileFormat { - /// Scalable Vector Graphics - SVG, - - /// Portable Document Format - PDF, - - /// PostScript - PS, -} - -pub trait Export { - fn export(&self, writer: &mut W, format: FileFormat) -> io::Result<()>; -} - -impl Export for Scene { - fn export(&self, writer: &mut W, format: FileFormat) -> io::Result<()> { - match format { - FileFormat::SVG => export_svg(self, writer), - FileFormat::PDF => export_pdf(self, writer), - FileFormat::PS => export_ps(self, writer) - } - } -} - -fn export_svg(scene: &Scene, writer: &mut W) -> io::Result<()> { - let view_box = scene.view_box(); - writeln!( - writer, - "", - view_box.origin().x(), - view_box.origin().y(), - view_box.size().x(), - view_box.size().y() - )?; - for (paint, outline, name) in scene.paths() { - write!(writer, " ", paint, outline)?; - } - writeln!(writer, "")?; - Ok(()) -} - -fn export_pdf(scene: &Scene, writer: &mut W) -> io::Result<()> { - let mut pdf = Pdf::new(); - let view_box = scene.view_box(); - pdf.add_page(view_box.size()); - - let height = view_box.size().y(); - let tr = |v: Vector2F| -> Vector2F { - let r = v - view_box.origin(); - vec2f(r.x(), height - r.y()) - }; - - for (paint, outline, _) in scene.paths() { - // TODO(pcwalton): Gradients and patterns. - if paint.is_color() { - pdf.set_fill_color(paint.base_color()); - } - - for contour in outline.contours() { - for (segment_index, segment) in contour.iter(ContourIterFlags::empty()).enumerate() { - if segment_index == 0 { - pdf.move_to(tr(segment.baseline.from())); - } - - match segment.kind { - SegmentKind::None => {} - SegmentKind::Line => pdf.line_to(tr(segment.baseline.to())), - SegmentKind::Quadratic => { - let current = segment.baseline.from(); - let c = segment.ctrl.from(); - let p = segment.baseline.to(); - let c1 = c * (2.0 / 3.0) + current * (1.0 / 3.0); - let c2 = c * (2.0 / 3.0) + p * (1.0 / 3.0); - pdf.cubic_to(c1, c2, p); - } - SegmentKind::Cubic => { - pdf.cubic_to(tr(segment.ctrl.from()), - tr(segment.ctrl.to()), - tr(segment.baseline.to())) - } - } - } - - if contour.is_closed() { - pdf.close(); - } - } - - // closes implicitly - pdf.fill(); - } - pdf.write_to(writer) -} - -fn export_ps(scene: &Scene, writer: &mut W) -> io::Result<()> { - struct P(Vector2F); - impl fmt::Display for P { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.0.x(), self.0.y()) - } - } - - let view_box = scene.view_box(); - writeln!(writer, "%!PS-Adobe-3.0 EPSF-3.0")?; - writeln!(writer, "%%BoundingBox: {:.0} {:.0}", - P(view_box.origin()), - P(view_box.size()), - )?; - writeln!(writer, "%%HiResBoundingBox: {} {}", - P(view_box.origin()), - P(view_box.size()), - )?; - writeln!(writer, "0 {} translate", view_box.size().y())?; - writeln!(writer, "1 -1 scale")?; - - for (paint, outline, name) in scene.paths() { - if !name.is_empty() { - writeln!(writer, "newpath % {}", name)?; - } else { - writeln!(writer, "newpath")?; - } - - for contour in outline.contours() { - for (segment_index, segment) in contour.iter(ContourIterFlags::empty()).enumerate() { - if segment_index == 0 { - writeln!(writer, "{} moveto", P(segment.baseline.from()))?; - } - - match segment.kind { - SegmentKind::None => {} - SegmentKind::Line => { - writeln!(writer, "{} lineto", P(segment.baseline.to()))?; - } - SegmentKind::Quadratic => { - let current = segment.baseline.from(); - let c = segment.ctrl.from(); - let p = segment.baseline.to(); - let c1 = c * (2.0 / 3.0) + current * (1.0 / 3.0); - let c2 = c * (2.0 / 3.0) + p * (1.0 / 3.0); - writeln!(writer, "{} {} {} curveto", P(c1), P(c2), P(p))?; - } - SegmentKind::Cubic => { - writeln!(writer, "{} {} {} curveto", - P(segment.ctrl.from()), - P(segment.ctrl.to()), - P(segment.baseline.to()) - )?; - } - } - } - - if contour.is_closed() { - writeln!(writer, "closepath")?; - } - } - - // TODO(pcwalton): Gradients and patterns. - if paint.is_color() { - let color = paint.base_color(); - writeln!(writer, "{} {} {} setrgbcolor", color.r, color.g, color.b)?; - } - - writeln!(writer, "fill")?; - } - writeln!(writer, "showpage")?; - Ok(()) -} - - diff --git a/crates/pathfinder/export/src/pdf.rs b/crates/pathfinder/export/src/pdf.rs deleted file mode 100644 index bb85344b50..0000000000 --- a/crates/pathfinder/export/src/pdf.rs +++ /dev/null @@ -1,260 +0,0 @@ -// pathfinder/export/src/pdf.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! This is a heavily modified version of the pdfpdf crate by Benjamin Kimock -//! (aka. saethlin) - -use deflate::Compression; -use pathfinder_color::ColorU; -use pathfinder_geometry::vector::Vector2F; -use std::io::{self, Write}; - -struct Counter { - inner: T, - count: u64 -} -impl Counter { - pub fn new(inner: T) -> Counter { - Counter { - inner, - count: 0 - } - } - pub fn pos(&self) -> u64 { - self.count - } -} -impl Write for Counter { - fn write(&mut self, buf: &[u8]) -> io::Result { - match self.inner.write(buf) { - Ok(n) => { - self.count += n as u64; - Ok(n) - }, - Err(e) => Err(e) - } - } - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.inner.write_all(buf)?; - self.count += buf.len() as u64; - Ok(()) - } -} - -/// Represents a PDF internal object -struct PdfObject { - contents: Vec, - is_page: bool, - is_xobject: bool, - offset: Option, -} - -/// The top-level struct that represents a (partially) in-memory PDF file -pub struct Pdf { - page_buffer: Vec, - objects: Vec, - page_size: Option, - compression: Option, -} - -impl Default for Pdf { - fn default() -> Self { - Self::new() - } -} - -impl Pdf { - /// Create a new blank PDF document - #[inline] - pub fn new() -> Self { - Self { - page_buffer: Vec::new(), - objects: vec![ - PdfObject { - contents: Vec::new(), - is_page: false, - is_xobject: false, - offset: None, - }, - PdfObject { - contents: Vec::new(), - is_page: false, - is_xobject: false, - offset: None, - }, - ], - page_size: None, - compression: Some(Compression::Fast) - } - } - - fn add_object(&mut self, data: Vec, is_page: bool, is_xobject: bool) -> usize { - self.objects.push(PdfObject { - contents: data, - is_page, - is_xobject, - offset: None, - }); - self.objects.len() - } - - /// Set the color for all subsequent drawing operations - #[inline] - pub fn set_fill_color(&mut self, color: ColorU) { - let norm = |color| f32::from(color) / 255.0; - writeln!(self.page_buffer, "{} {} {} rg", - norm(color.r), - norm(color.g), - norm(color.b) - ).unwrap(); - } - - /// Move to a new page in the PDF document - #[inline] - pub fn add_page(&mut self, size: Vector2F) { - // Compress and write out the previous page if it exists - if !self.page_buffer.is_empty() { - self.end_page(); - self.page_buffer.clear(); - } - - self.page_buffer - .extend("/DeviceRGB cs /DeviceRGB CS\n1 j 1 J\n".bytes()); - self.page_size = Some(size); - } - - pub fn move_to(&mut self, p: Vector2F) { - writeln!(self.page_buffer, "{} {} m", p.x(), p.y()).unwrap(); - } - - pub fn line_to(&mut self, p: Vector2F) { - writeln!(self.page_buffer, "{} {} l", p.x(), p.y()).unwrap(); - } - - pub fn cubic_to(&mut self, c1: Vector2F, c2: Vector2F, p: Vector2F) { - writeln!(self.page_buffer, "{} {} {} {} {} {} c", c1.x(), c1.y(), c2.x(), c2.y(), p.x(), p.y()).unwrap(); - } - pub fn fill(&mut self) { - writeln!(self.page_buffer, "f").unwrap(); - } - - pub fn close(&mut self) { - writeln!(self.page_buffer, "h").unwrap(); - } - /// Dump a page out to disk - fn end_page(&mut self) { - let size = match self.page_size.take() { - Some(size) => size, - None => return // no page started - }; - let page_stream = if let Some(level) = self.compression { - let compressed = deflate::deflate_bytes_zlib_conf(&self.page_buffer, level); - let mut page = format!( - "<< /Length {} /Filter [/FlateDecode] >>\nstream\n", - compressed.len() - ) - .into_bytes(); - page.extend_from_slice(&compressed); - page.extend(b"endstream\n"); - page - } else { - let mut page = Vec::new(); - page.extend(format!("<< /Length {} >>\nstream\n", self.page_buffer.len()).bytes()); - page.extend(&self.page_buffer); - page.extend(b"endstream\n"); - page - }; - - // Create the stream object for this page - let stream_object_id = self.add_object(page_stream, false, false); - - // Create the page object, which describes settings for the whole page - let mut page_object = b"<< /Type /Page\n \ - /Parent 2 0 R\n \ - /Resources <<\n" - .to_vec(); - - for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) { - write!(page_object, "/XObject {} 0 R ", idx+1).unwrap(); - } - - write!(page_object, - " >>\n \ - /MediaBox [0 0 {} {}]\n \ - /Contents {} 0 R\n\ - >>\n", - size.x(), size.y(), stream_object_id - ).unwrap(); - self.add_object(page_object, true, false); - } - - /// Write the in-memory PDF representation to disk - pub fn write_to(&mut self, writer: W) -> io::Result<()> where W: Write { - let mut out = Counter::new(writer); - out.write_all(b"%PDF-1.7\n%\xB5\xED\xAE\xFB\n")?; - - if !self.page_buffer.is_empty() { - self.end_page(); - } - - // Write out each object - for (idx, obj) in self.objects.iter_mut().enumerate().skip(2) { - obj.offset = Some(out.pos()); - write!(out, "{} 0 obj\n", idx+1)?; - out.write_all(&obj.contents)?; - out.write_all(b"endobj\n")?; - } - - // Write out the page tree object - self.objects[1].offset = Some(out.pos()); - out.write_all(b"2 0 obj\n")?; - out.write_all(b"<< /Type /Pages\n")?; - write!(out, - "/Count {}\n", - self.objects.iter().filter(|o| o.is_page).count() - )?; - out.write_all(b"/Kids [")?; - for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) { - write!(out, "{} 0 R ", idx + 1)?; - } - out.write_all(b"] >>\nendobj\n")?; - - // Write out the catalog dictionary object - self.objects[0].offset = Some(out.pos()); - out.write_all(b"1 0 obj\n<< /Type /Catalog\n/Pages 2 0 R >>\nendobj\n")?; - - // Write the cross-reference table - let startxref = out.pos() + 1; // NOTE: apparently there's some 1-based indexing?? - out.write_all(b"xref\n")?; - write!(out, "0 {}\n", self.objects.len() + 1)?; - out.write_all(b"0000000000 65535 f \n")?; - - for obj in &self.objects { - write!(out, "{:010} 00000 f \n", obj.offset.unwrap())?; - } - - // Write the document trailer - out.write_all(b"trailer\n")?; - write!(out, "<< /Size {}\n", self.objects.len())?; - out.write_all(b"/Root 1 0 R >>\n")?; - - // Write the offset to the xref table - write!(out, "startxref\n{}\n", startxref)?; - - // Write the PDF EOF - out.write_all(b"%%EOF")?; - - Ok(()) - } -} diff --git a/crates/pathfinder/geometry/Cargo.toml b/crates/pathfinder/geometry/Cargo.toml deleted file mode 100644 index b05e929977..0000000000 --- a/crates/pathfinder/geometry/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "pathfinder_geometry" -version = "0.5.1" -edition = "2018" -authors = ["Patrick Walton "] -description = "Basic SIMD-accelerated geometry/linear algebra" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" - -[features] -shader_alignment_32_bits = [] - -[dependencies] - -[dependencies.log] -version = "0.4" - -[dependencies.pathfinder_simd] -path = "../simd" -version = "0.5" diff --git a/crates/pathfinder/geometry/src/alignment.rs b/crates/pathfinder/geometry/src/alignment.rs deleted file mode 100644 index 44db8652ba..0000000000 --- a/crates/pathfinder/geometry/src/alignment.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[cfg(feature = "shader_alignment_32_bits")] -pub type AlignedU8 = u32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub type AlignedU8 = u8; - -#[cfg(feature = "shader_alignment_32_bits")] -pub type AlignedU16 = u32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub type AlignedU16 = u16; - -#[cfg(feature = "shader_alignment_32_bits")] -pub type AlignedI8 = i32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub type AlignedI8 = i8; - -#[cfg(feature = "shader_alignment_32_bits")] -pub type AlignedI16 = i32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub type AlignedI16 = i16; \ No newline at end of file diff --git a/crates/pathfinder/geometry/src/angle.rs b/crates/pathfinder/geometry/src/angle.rs deleted file mode 100644 index 1fa9e09eea..0000000000 --- a/crates/pathfinder/geometry/src/angle.rs +++ /dev/null @@ -1,19 +0,0 @@ -// pathfinder/geometry/src/angle.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Angle utilities. - -use std::f32::consts::PI; - -#[inline] -pub fn angle_from_degrees(degrees: f32) -> f32 { - const SCALE: f32 = 2.0 * PI / 360.0; - degrees * SCALE -} diff --git a/crates/pathfinder/geometry/src/lib.rs b/crates/pathfinder/geometry/src/lib.rs deleted file mode 100644 index 44d88cc2a0..0000000000 --- a/crates/pathfinder/geometry/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -// pathfinder/geometry/src/lib.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Basic geometry and linear algebra primitives, optimized with SIMD. - -pub mod angle; -pub mod line_segment; -pub mod rect; -pub mod transform2d; -pub mod transform3d; -pub mod unit_vector; -pub mod util; -pub mod vector; -pub mod alignment; diff --git a/crates/pathfinder/geometry/src/line_segment.rs b/crates/pathfinder/geometry/src/line_segment.rs deleted file mode 100644 index 1fb0a7cd65..0000000000 --- a/crates/pathfinder/geometry/src/line_segment.rs +++ /dev/null @@ -1,309 +0,0 @@ -// pathfinder/geometry/src/basic/line_segment.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Line segment types, optimized with SIMD. - -use crate::transform2d::Matrix2x2F; -use crate::alignment::AlignedU8; -use crate::util; -use crate::vector::{vec2f, Vector2F}; -use pathfinder_simd::default::F32x4; -use std::ops::{Add, Mul, MulAssign, Sub}; - -#[derive(Clone, Copy, Debug, PartialEq, Default)] -pub struct LineSegment2F(pub F32x4); - -impl LineSegment2F { - #[inline] - pub fn new(from: Vector2F, to: Vector2F) -> LineSegment2F { - LineSegment2F(from.0.concat_xy_xy(to.0)) - } - - #[inline] - pub fn from(self) -> Vector2F { - Vector2F(self.0.xy()) - } - - #[inline] - pub fn to(self) -> Vector2F { - Vector2F(self.0.zw()) - } - - #[inline] - pub fn set_from(&mut self, point: Vector2F) { - self.0 = point.0.to_f32x4().concat_xy_zw(self.0) - } - - #[inline] - pub fn set_to(&mut self, point: Vector2F) { - self.0 = self.0.concat_xy_xy(point.0.to_f32x4()) - } - - #[allow(clippy::wrong_self_convention)] - #[inline] - pub fn from_x(self) -> f32 { - self.0[0] - } - - #[allow(clippy::wrong_self_convention)] - #[inline] - pub fn from_y(self) -> f32 { - self.0[1] - } - - #[inline] - pub fn to_x(self) -> f32 { - self.0[2] - } - - #[inline] - pub fn to_y(self) -> f32 { - self.0[3] - } - - #[inline] - pub fn set_from_x(&mut self, x: f32) { - self.0[0] = x - } - - #[inline] - pub fn set_from_y(&mut self, y: f32) { - self.0[1] = y - } - - #[inline] - pub fn set_to_x(&mut self, x: f32) { - self.0[2] = x - } - - #[inline] - pub fn set_to_y(&mut self, y: f32) { - self.0[3] = y - } - - #[inline] - pub fn split(self, t: f32) -> (LineSegment2F, LineSegment2F) { - debug_assert!(t >= 0.0 && t <= 1.0); - let (from_from, to_to) = (self.0.xyxy(), self.0.zwzw()); - let d_d = to_to - from_from; - let mid_mid = from_from + d_d * F32x4::splat(t); - ( - LineSegment2F(from_from.concat_xy_xy(mid_mid)), - LineSegment2F(mid_mid.concat_xy_xy(to_to)), - ) - } - - // Returns the left segment first, followed by the right segment. - #[inline] - pub fn split_at_x(self, x: f32) -> (LineSegment2F, LineSegment2F) { - let (min_part, max_part) = self.split(self.solve_t_for_x(x)); - if min_part.from_x() < max_part.from_x() { - (min_part, max_part) - } else { - (max_part, min_part) - } - } - - // Returns the upper segment first, followed by the lower segment. - #[inline] - pub fn split_at_y(self, y: f32) -> (LineSegment2F, LineSegment2F) { - let (min_part, max_part) = self.split(self.solve_t_for_y(y)); - - // Make sure we compare `from_y` and `to_y` to properly handle the case in which one of the - // two segments is zero-length. - if min_part.from_y() < max_part.to_y() { - (min_part, max_part) - } else { - (max_part, min_part) - } - } - - #[inline] - pub fn solve_t_for_x(self, x: f32) -> f32 { - (x - self.from_x()) / (self.to_x() - self.from_x()) - } - - #[inline] - pub fn solve_t_for_y(self, y: f32) -> f32 { - (y - self.from_y()) / (self.to_y() - self.from_y()) - } - - #[inline] - pub fn solve_x_for_y(self, y: f32) -> f32 { - util::lerp(self.from_x(), self.to_x(), self.solve_t_for_y(y)) - } - - #[inline] - pub fn solve_y_for_x(self, x: f32) -> f32 { - util::lerp(self.from_y(), self.to_y(), self.solve_t_for_x(x)) - } - - #[inline] - pub fn reversed(self) -> LineSegment2F { - LineSegment2F(self.0.zwxy()) - } - - #[inline] - pub fn upper_point(self) -> Vector2F { - if self.from_y() < self.to_y() { - self.from() - } else { - self.to() - } - } - - #[inline] - pub fn min_x(self) -> f32 { - f32::min(self.from_x(), self.to_x()) - } - - #[inline] - pub fn max_x(self) -> f32 { - f32::max(self.from_x(), self.to_x()) - } - - #[inline] - pub fn min_y(self) -> f32 { - f32::min(self.from_y(), self.to_y()) - } - - #[inline] - pub fn max_y(self) -> f32 { - f32::max(self.from_y(), self.to_y()) - } - - #[inline] - pub fn y_winding(self) -> i32 { - if self.from_y() < self.to_y() { - 1 - } else { - -1 - } - } - - // Reverses if necessary so that the from point is above the to point. Calling this method - // again will undo the transformation. - #[inline] - pub fn orient(self, y_winding: i32) -> LineSegment2F { - if y_winding >= 0 { - self - } else { - self.reversed() - } - } - - // TODO(pcwalton): Optimize with SIMD. - #[inline] - pub fn square_length(self) -> f32 { - let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y()); - dx * dx + dy * dy - } - - #[inline] - pub fn length(self) -> f32 { - self.square_length().sqrt() - } - - #[inline] - pub fn vector(self) -> Vector2F { - self.to() - self.from() - } - - // http://www.cs.swan.ac.uk/~cssimon/line_intersection.html - pub fn intersection_t(self, other: LineSegment2F) -> Option { - let p0p1 = self.vector(); - let matrix = Matrix2x2F(other.vector().0.concat_xy_xy((-p0p1).0)); - if f32::abs(matrix.det()) < EPSILON { - return None; - } - return Some((matrix.inverse() * (self.from() - other.from())).y()); - - const EPSILON: f32 = 0.0001; - } - - #[inline] - pub fn sample(self, t: f32) -> Vector2F { - self.from() + self.vector() * t - } - - #[inline] - pub fn midpoint(self) -> Vector2F { - self.sample(0.5) - } - - #[inline] - pub fn offset(self, distance: f32) -> LineSegment2F { - if self.is_zero_length() { - self - } else { - self + self.vector().yx().normalize() * vec2f(-distance, distance) - } - } - - #[inline] - pub fn is_zero_length(self) -> bool { - self.vector().is_zero() - } -} - -impl Add for LineSegment2F { - type Output = LineSegment2F; - #[inline] - fn add(self, point: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 + point.0.to_f32x4().xyxy()) - } -} - -impl Sub for LineSegment2F { - type Output = LineSegment2F; - #[inline] - fn sub(self, point: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 - point.0.to_f32x4().xyxy()) - } -} - -impl Mul for LineSegment2F { - type Output = LineSegment2F; - #[inline] - fn mul(self, factors: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 * factors.0.to_f32x4().xyxy()) - } -} - -impl Mul for LineSegment2F { - type Output = LineSegment2F; - #[inline] - fn mul(self, factor: f32) -> LineSegment2F { - LineSegment2F(self.0 * F32x4::splat(factor)) - } -} - -impl MulAssign for LineSegment2F { - #[inline] - fn mul_assign(&mut self, factors: Vector2F) { - *self = *self * factors - } -} - -#[derive(Clone, Copy, Debug, Default)] -#[repr(C)] -pub struct LineSegmentU4 { - pub from: AlignedU8, - pub to: AlignedU8, -} - -#[derive(Clone, Copy, Debug, Default)] -#[repr(C)] -pub struct LineSegmentU8 { - pub from_x: u8, - pub from_y: u8, - pub to_x: u8, - pub to_y: u8, -} diff --git a/crates/pathfinder/geometry/src/rect.rs b/crates/pathfinder/geometry/src/rect.rs deleted file mode 100644 index 858c27123f..0000000000 --- a/crates/pathfinder/geometry/src/rect.rs +++ /dev/null @@ -1,371 +0,0 @@ -// pathfinder/geometry/src/basic/rect.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! 2D axis-aligned rectangles, optimized with SIMD. - -use crate::vector::{IntoVector2F, Vector2F, Vector2I}; -use pathfinder_simd::default::{F32x4, I32x4}; -use std::ops::{Add, Mul, Sub}; - -#[derive(Clone, Copy, Debug, PartialEq, Default)] -pub struct RectF(pub F32x4); - -impl RectF { - #[inline] - pub fn new(origin: Vector2F, size: Vector2F) -> RectF { - RectF(origin.0.concat_xy_xy(origin.0 + size.0)) - } - - #[inline] - pub fn from_points(origin: Vector2F, lower_right: Vector2F) -> RectF { - RectF(origin.0.concat_xy_xy(lower_right.0)) - } - - // Accessors - - #[inline] - pub fn origin(self) -> Vector2F { - Vector2F(self.0.xy()) - } - - #[inline] - pub fn size(self) -> Vector2F { - Vector2F(self.0.zw() - self.0.xy()) - } - - #[inline] - pub fn origin_x(self) -> f32 { - self.0.x() - } - - #[inline] - pub fn origin_y(self) -> f32 { - self.0.y() - } - - #[inline] - pub fn width(self) -> f32 { - self.0.z() - self.0.x() - } - - #[inline] - pub fn height(self) -> f32 { - self.0.w() - self.0.y() - } - - #[inline] - pub fn upper_right(self) -> Vector2F { - Vector2F(self.0.zy()) - } - - #[inline] - pub fn lower_left(self) -> Vector2F { - Vector2F(self.0.xw()) - } - - #[inline] - pub fn lower_right(self) -> Vector2F { - Vector2F(self.0.zw()) - } - - // Mutators - - #[inline] - pub fn set_origin_x(&mut self, x: f32) { - self.0.set_x(x) - } - - #[inline] - pub fn set_origin_y(&mut self, y: f32) { - self.0.set_y(y) - } - - #[inline] - pub fn contains_point(self, point: Vector2F) -> bool { - // self.origin <= point && point <= self.lower_right - let point = point.0.to_f32x4(); - self.0.concat_xy_xy(point).packed_le(point.concat_xy_zw(self.0)).all_true() - } - - #[inline] - pub fn contains_rect(self, other: RectF) -> bool { - // self.origin <= other.origin && other.lower_right <= self.lower_right - self.0.concat_xy_zw(other.0).packed_le(other.0.concat_xy_zw(self.0)).all_true() - } - - #[inline] - pub fn is_empty(self) -> bool { - self.origin() == self.lower_right() - } - - #[inline] - pub fn union_point(self, point: Vector2F) -> RectF { - RectF::from_points(self.origin().min(point), self.lower_right().max(point)) - } - - #[inline] - pub fn union_rect(self, other: RectF) -> RectF { - RectF::from_points( - self.origin().min(other.origin()), - self.lower_right().max(other.lower_right()), - ) - } - - #[inline] - pub fn intersects(self, other: RectF) -> bool { - // self.origin < other.lower_right && other.origin < self.lower_right - self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true() - } - - #[inline] - pub fn intersection(self, other: RectF) -> Option { - if !self.intersects(other) { - None - } else { - Some(RectF::from_points( - self.origin().max(other.origin()), - self.lower_right().min(other.lower_right()), - )) - } - } - - #[inline] - pub fn min_x(self) -> f32 { - self.0[0] - } - - #[inline] - pub fn min_y(self) -> f32 { - self.0[1] - } - - #[inline] - pub fn max_x(self) -> f32 { - self.0[2] - } - - #[inline] - pub fn max_y(self) -> f32 { - self.0[3] - } - - #[inline] - pub fn center(self) -> Vector2F { - self.origin() + self.size() * 0.5 - } - - /// Rounds all points to the nearest integer. - #[inline] - pub fn round(self) -> RectF { - RectF(self.0.to_i32x4().to_f32x4()) - } - - #[inline] - pub fn round_out(self) -> RectF { - RectF::from_points(self.origin().floor(), self.lower_right().ceil()) - } - - #[inline] - pub fn dilate(self, amount: A) -> RectF where A: IntoVector2F { - let amount = amount.into_vector_2f(); - RectF::from_points(self.origin() - amount, self.lower_right() + amount) - } - - #[inline] - pub fn contract(self, amount: A) -> RectF where A: IntoVector2F { - let amount = amount.into_vector_2f(); - RectF::from_points(self.origin() + amount, self.lower_right() - amount) - } - - #[inline] - pub fn to_i32(&self) -> RectI { - RectI(self.0.to_i32x4()) - } -} - -impl Add for RectF { - type Output = RectF; - #[inline] - fn add(self, other: Vector2F) -> RectF { - RectF::new(self.origin() + other, self.size()) - } -} - -impl Add for RectF { - type Output = RectF; - #[inline] - fn add(self, other: f32) -> RectF { - RectF::new(self.origin() + other, self.size()) - } -} - -impl Mul for RectF { - type Output = RectF; - #[inline] - fn mul(self, factors: Vector2F) -> RectF { - RectF(self.0 * factors.0.concat_xy_xy(factors.0)) - } -} - -impl Mul for RectF { - type Output = RectF; - #[inline] - fn mul(self, factor: f32) -> RectF { - RectF(self.0 * F32x4::splat(factor)) - } -} - -impl Sub for RectF { - type Output = RectF; - #[inline] - fn sub(self, other: Vector2F) -> RectF { - RectF::new(self.origin() - other, self.size()) - } -} - -impl Sub for RectF { - type Output = RectF; - #[inline] - fn sub(self, other: f32) -> RectF { - RectF::new(self.origin() - other, self.size()) - } -} - -/// NB: The origin is inclusive, while the lower right point is exclusive. -#[derive(Clone, Copy, Debug, PartialEq, Default)] -pub struct RectI(pub I32x4); - -impl RectI { - #[inline] - pub fn new(origin: Vector2I, size: Vector2I) -> RectI { - RectI(origin.0.concat_xy_xy(origin.0 + size.0)) - } - - #[inline] - pub fn from_points(origin: Vector2I, lower_right: Vector2I) -> RectI { - RectI(origin.0.concat_xy_xy(lower_right.0)) - } - - // Accessors - - #[inline] - pub fn origin(&self) -> Vector2I { - Vector2I(self.0.xy()) - } - - #[inline] - pub fn size(&self) -> Vector2I { - Vector2I(self.0.zw() - self.0.xy()) - } - - #[inline] - pub fn origin_x(self) -> i32 { - self.0.x() - } - - #[inline] - pub fn origin_y(self) -> i32 { - self.0.y() - } - - #[inline] - pub fn width(self) -> i32 { - self.0.z() - self.0.x() - } - - #[inline] - pub fn height(self) -> i32 { - self.0.w() - self.0.y() - } - - #[inline] - pub fn upper_right(&self) -> Vector2I { - Vector2I(self.0.zy()) - } - - #[inline] - pub fn lower_left(&self) -> Vector2I { - Vector2I(self.0.xw()) - } - - #[inline] - pub fn lower_right(&self) -> Vector2I { - Vector2I(self.0.zw()) - } - - #[inline] - pub fn scale(self, factor: i32) -> RectI { - RectI(self.0 * I32x4::splat(factor)) - } - - #[inline] - pub fn scale_xy(self, factors: Vector2I) -> RectI { - RectI(self.0 * factors.0.concat_xy_xy(factors.0)) - } - - #[inline] - pub fn min_x(self) -> i32 { - self.0[0] - } - - #[inline] - pub fn min_y(self) -> i32 { - self.0[1] - } - - #[inline] - pub fn max_x(self) -> i32 { - self.0[2] - } - - #[inline] - pub fn max_y(self) -> i32 { - self.0[3] - } - - #[inline] - pub fn intersects(self, other: RectI) -> bool { - // self.origin < other.lower_right && other.origin < self.lower_right - self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true() - } - - #[inline] - pub fn intersection(self, other: RectI) -> Option { - if !self.intersects(other) { - None - } else { - Some(RectI::from_points( - self.origin().max(other.origin()), - self.lower_right().min(other.lower_right()), - )) - } - } - - #[inline] - pub fn contains_point(&self, point: Vector2I) -> bool { - // self.origin <= point && point <= self.lower_right - 1 - let lower_right = self.lower_right() - 1; - self.origin() - .0 - .concat_xy_xy(point.0) - .packed_le(point.0.concat_xy_xy(lower_right.0)) - .all_true() - } - - #[inline] - pub fn contract(self, amount: Vector2I) -> RectI { - RectI::from_points(self.origin() + amount, self.lower_right() - amount) - } - - #[inline] - pub fn to_f32(&self) -> RectF { - RectF(self.0.to_f32x4()) - } -} diff --git a/crates/pathfinder/geometry/src/transform2d.rs b/crates/pathfinder/geometry/src/transform2d.rs deleted file mode 100644 index c29ae7b9c3..0000000000 --- a/crates/pathfinder/geometry/src/transform2d.rs +++ /dev/null @@ -1,345 +0,0 @@ -// pathfinder/geometry/src/basic/transform2d.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! 2D affine transforms. - -use crate::line_segment::LineSegment2F; -use crate::rect::RectF; -use crate::transform3d::Transform4F; -use crate::unit_vector::UnitVector; -use crate::vector::{IntoVector2F, Vector2F, vec2f}; -use pathfinder_simd::default::F32x4; -use std::ops::{Mul, MulAssign, Sub}; - -/// A 2x2 matrix, optimized with SIMD, in column-major order. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Matrix2x2F(pub F32x4); - -impl Default for Matrix2x2F { - #[inline] - fn default() -> Matrix2x2F { - Self::from_scale(1.0) - } -} - -impl Matrix2x2F { - #[inline] - pub fn from_scale(scale: S) -> Matrix2x2F where S: IntoVector2F { - let scale = scale.into_vector_2f(); - Matrix2x2F(F32x4::new(scale.x(), 0.0, 0.0, scale.y())) - } - - #[inline] - pub fn from_rotation(theta: f32) -> Matrix2x2F { - Matrix2x2F::from_rotation_vector(UnitVector::from_angle(theta)) - } - - #[inline] - pub fn from_rotation_vector(vector: UnitVector) -> Matrix2x2F { - Matrix2x2F((vector.0).0.to_f32x4().xyyx() * F32x4::new(1.0, 1.0, -1.0, 1.0)) - } - - #[inline] - pub fn row_major(m00: f32, m01: f32, m10: f32, m11: f32) -> Matrix2x2F { - Matrix2x2F(F32x4::new(m00, m10, m01, m11)) - } - - #[inline] - pub fn entrywise_mul(&self, other: &Matrix2x2F) -> Matrix2x2F { - Matrix2x2F(self.0 * other.0) - } - - #[inline] - pub fn adjugate(&self) -> Matrix2x2F { - Matrix2x2F(self.0.wyzx() * F32x4::new(1.0, -1.0, -1.0, 1.0)) - } - - #[inline] - pub fn det(&self) -> f32 { - self.0[0] * self.0[3] - self.0[2] * self.0[1] - } - - #[inline] - pub fn inverse(&self) -> Matrix2x2F { - Matrix2x2F(F32x4::splat(1.0 / self.det()) * self.adjugate().0) - } - - #[inline] - pub fn scale(&self, factor: f32) -> Matrix2x2F { - Matrix2x2F(self.0 * F32x4::splat(factor)) - } - - /// Extracts the scale from this matrix. - #[inline] - pub fn extract_scale(&self) -> Vector2F { - let squared = self.0 * self.0; - Vector2F((squared.xy() + squared.zw()).sqrt()) - } - - #[inline] - pub fn m11(&self) -> f32 { - self.0[0] - } - - #[inline] - pub fn m21(&self) -> f32 { - self.0[1] - } - - #[inline] - pub fn m12(&self) -> f32 { - self.0[2] - } - - #[inline] - pub fn m22(&self) -> f32 { - self.0[3] - } -} - -impl Sub for Matrix2x2F { - type Output = Matrix2x2F; - #[inline] - fn sub(self, other: Matrix2x2F) -> Matrix2x2F { - Matrix2x2F(self.0 - other.0) - } -} - -impl Mul for Matrix2x2F { - type Output = Matrix2x2F; - #[inline] - fn mul(self, other: Matrix2x2F) -> Matrix2x2F { - Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww()) - } -} - -impl Mul for Matrix2x2F { - type Output = Vector2F; - #[inline] - fn mul(self, vector: Vector2F) -> Vector2F { - let halves = self.0 * vector.0.to_f32x4().xxyy(); - Vector2F(halves.xy() + halves.zw()) - } -} - -/// An affine transform, optimized with SIMD. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Transform2F { - pub matrix: Matrix2x2F, - pub vector: Vector2F, -} - -impl Default for Transform2F { - #[inline] - fn default() -> Transform2F { - Self::from_scale(vec2f(1.0, 1.0)) - } -} - -impl Transform2F { - #[inline] - pub fn from_scale(scale: S) -> Transform2F where S: IntoVector2F { - let scale = scale.into_vector_2f(); - Transform2F { - matrix: Matrix2x2F::from_scale(scale), - vector: Vector2F::zero(), - } - } - - #[inline] - pub fn from_rotation(theta: f32) -> Transform2F { - Transform2F { - matrix: Matrix2x2F::from_rotation(theta), - vector: Vector2F::zero(), - } - } - - #[inline] - pub fn from_rotation_vector(vector: UnitVector) -> Transform2F { - Transform2F { - matrix: Matrix2x2F::from_rotation_vector(vector), - vector: Vector2F::zero(), - } - } - - #[inline] - pub fn from_translation(vector: Vector2F) -> Transform2F { - Transform2F { matrix: Matrix2x2F::default(), vector } - } - - #[inline] - pub fn from_scale_rotation_translation(scale: S, theta: f32, translation: Vector2F) - -> Transform2F where S: IntoVector2F { - let scale = scale.into_vector_2f(); - let rotation = Transform2F::from_rotation(theta); - let translation = Transform2F::from_translation(translation); - Transform2F::from_scale(scale) * rotation * translation - } - - #[inline] - pub fn row_major(m11: f32, m12: f32, m13: f32, m21: f32, m22: f32, m23: f32) -> Transform2F { - Transform2F { - matrix: Matrix2x2F::row_major(m11, m12, m21, m22), - vector: Vector2F::new(m13, m23), - } - } - - // TODO(pcwalton): Optimize better with SIMD. - #[inline] - pub fn to_3d(&self) -> Transform4F { - Transform4F::row_major( - self.matrix.0[0], - self.matrix.0[1], - 0.0, - self.vector.x(), - self.matrix.0[2], - self.matrix.0[3], - 0.0, - self.vector.y(), - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - ) - } - - #[inline] - pub fn is_identity(&self) -> bool { - *self == Transform2F::default() - } - - /// Extracts the scale from this matrix. - #[inline] - pub fn extract_scale(&self) -> Vector2F { - self.matrix.extract_scale() - } - - #[inline] - pub fn m11(&self) -> f32 { - self.matrix.m11() - } - #[inline] - pub fn m21(&self) -> f32 { - self.matrix.m21() - } - #[inline] - pub fn m12(&self) -> f32 { - self.matrix.m12() - } - #[inline] - pub fn m22(&self) -> f32 { - self.matrix.m22() - } - #[inline] - pub fn m13(&self) -> f32 { - self.vector.x() - } - #[inline] - pub fn m23(&self) -> f32 { - self.vector.y() - } - - #[inline] - pub fn translate(&self, vector: Vector2F) -> Transform2F { - Transform2F::from_translation(vector) * *self - } - - #[inline] - pub fn rotate(&self, theta: f32) -> Transform2F { - Transform2F::from_rotation(theta) * *self - } - - #[inline] - pub fn scale(&self, scale: S) -> Transform2F where S: IntoVector2F { - let scale = scale.into_vector_2f(); - Transform2F::from_scale(scale) * *self - } - - /// Returns the translation part of this matrix. - /// - /// This decomposition assumes that scale, rotation, and translation are applied in that order. - #[inline] - pub fn translation(&self) -> Vector2F { - self.vector - } - - /// Returns the rotation angle of this matrix. - /// - /// This decomposition assumes that scale, rotation, and translation are applied in that order. - #[inline] - pub fn rotation(&self) -> f32 { - f32::atan2(self.m21(), self.m11()) - } - - /// Returns the scale factor of this matrix. - /// - /// This decomposition assumes that scale, rotation, and translation are applied in that order. - #[inline] - pub fn scale_factor(&self) -> f32 { - Vector2F(self.matrix.0.zw()).length() - } - - #[inline] - pub fn inverse(&self) -> Transform2F { - let matrix_inv = self.matrix.inverse(); - let vector_inv = -(matrix_inv * self.vector); - Transform2F { matrix: matrix_inv, vector: vector_inv } - } -} - -impl Mul for Transform2F { - type Output = Transform2F; - #[inline] - fn mul(self, other: Transform2F) -> Transform2F { - Transform2F { - matrix: self.matrix * other.matrix, - vector: self * other.vector, - } - } -} - -impl Mul for Transform2F { - type Output = Vector2F; - #[inline] - fn mul(self, vector: Vector2F) -> Vector2F { - self.matrix * vector + self.vector - } -} - -impl Mul for Transform2F { - type Output = LineSegment2F; - #[inline] - fn mul(self, line_segment: LineSegment2F) -> LineSegment2F { - LineSegment2F::new(self * line_segment.from(), self * line_segment.to()) - } -} - -impl Mul for Transform2F { - type Output = RectF; - #[inline] - fn mul(self, rect: RectF) -> RectF { - let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right()); - let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right()); - let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); - let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); - RectF::from_points(min_point, max_point) - } -} - -impl MulAssign for Transform2F { - #[inline] - fn mul_assign(&mut self, other: Transform2F) { - *self = *self * other - } -} diff --git a/crates/pathfinder/geometry/src/transform3d.rs b/crates/pathfinder/geometry/src/transform3d.rs deleted file mode 100644 index 94d3648578..0000000000 --- a/crates/pathfinder/geometry/src/transform3d.rs +++ /dev/null @@ -1,504 +0,0 @@ -// pathfinder/geometry/src/basic/transform3d.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! 3D transforms that can be applied to paths. - -use crate::rect::RectF; -use crate::transform2d::Matrix2x2F; -use crate::vector::{Vector2F, Vector2I, Vector3F, Vector4F}; -use pathfinder_simd::default::F32x4; -use std::ops::{Add, Mul, MulAssign, Neg}; - -/// An transform, optimized with SIMD. -/// -/// In column-major order. -#[derive(Clone, Copy, Debug, PartialEq)] -#[repr(C)] -pub struct Transform4F { - pub c0: F32x4, - pub c1: F32x4, - pub c2: F32x4, - pub c3: F32x4, -} - -impl Default for Transform4F { - #[inline] - fn default() -> Transform4F { - Transform4F { - c0: F32x4::new(1.0, 0.0, 0.0, 0.0), - c1: F32x4::new(0.0, 1.0, 0.0, 0.0), - c2: F32x4::new(0.0, 0.0, 1.0, 0.0), - c3: F32x4::new(0.0, 0.0, 0.0, 1.0), - } - } -} - -impl Transform4F { - #[inline] - pub fn row_major( - m00: f32, - m01: f32, - m02: f32, - m03: f32, - m10: f32, - m11: f32, - m12: f32, - m13: f32, - m20: f32, - m21: f32, - m22: f32, - m23: f32, - m30: f32, - m31: f32, - m32: f32, - m33: f32, - ) -> Transform4F { - Transform4F { - c0: F32x4::new(m00, m10, m20, m30), - c1: F32x4::new(m01, m11, m21, m31), - c2: F32x4::new(m02, m12, m22, m32), - c3: F32x4::new(m03, m13, m23, m33), - } - } - - #[inline] - pub fn from_scale(scale: Vector4F) -> Transform4F { - Transform4F { - c0: F32x4::new(scale.x(), 0.0, 0.0, 0.0), - c1: F32x4::new(0.0, scale.y(), 0.0, 0.0), - c2: F32x4::new(0.0, 0.0, scale.z(), 0.0), - c3: F32x4::new(0.0, 0.0, 0.0, 1.0), - } - } - - #[inline] - pub fn from_uniform_scale(factor: f32) -> Transform4F { - Transform4F::from_scale(Vector4F::splat(factor)) - } - - #[inline] - pub fn from_translation(mut translation: Vector4F) -> Transform4F { - translation.set_w(1.0); - Transform4F { c3: translation.0, ..Transform4F::default() } - } - - // TODO(pcwalton): Optimize. - pub fn from_rotation(yaw: f32, pitch: f32, roll: f32) -> Transform4F { - let (cos_b, sin_b) = (yaw.cos(), yaw.sin()); - let (cos_c, sin_c) = (pitch.cos(), pitch.sin()); - let (cos_a, sin_a) = (roll.cos(), roll.sin()); - let m00 = cos_a * cos_b; - let m01 = cos_a * sin_b * sin_c - sin_a * cos_c; - let m02 = cos_a * sin_b * cos_c + sin_a * sin_c; - let m10 = sin_a * cos_b; - let m11 = sin_a * sin_b * sin_c + cos_a * cos_c; - let m12 = sin_a * sin_b * cos_c - cos_a * sin_c; - let m20 = -sin_b; - let m21 = cos_b * sin_c; - let m22 = cos_b * cos_c; - Transform4F::row_major( - m00, m01, m02, 0.0, m10, m11, m12, 0.0, m20, m21, m22, 0.0, 0.0, 0.0, 0.0, 1.0, - ) - } - - /// Creates a rotation matrix from the given quaternion. - /// - /// The quaternion is expected to be packed into a SIMD type (x, y, z, w) corresponding to - /// x + yi + zj + wk. - pub fn from_rotation_quaternion(q: F32x4) -> Transform4F { - // TODO(pcwalton): Optimize better with more shuffles. - let (mut sq, mut w, mut xy_xz_yz) = (q * q, q.wwww() * q, q.xxyy() * q.yzzy()); - sq += sq; - w += w; - xy_xz_yz += xy_xz_yz; - let diag = F32x4::splat(1.0) - (sq.yxxy() + sq.zzyy()); - let (wx2, wy2, wz2) = (w.x(), w.y(), w.z()); - let (xy2, xz2, yz2) = (xy_xz_yz.x(), xy_xz_yz.y(), xy_xz_yz.z()); - Transform4F::row_major( - diag.x(), - xy2 - wz2, - xz2 + wy2, - 0.0, - xy2 + wz2, - diag.y(), - yz2 - wx2, - 0.0, - xz2 - wy2, - yz2 + wx2, - diag.z(), - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - ) - } - - /// Just like `glOrtho()`. - #[inline] - pub fn from_ortho( - left: f32, - right: f32, - bottom: f32, - top: f32, - near_val: f32, - far_val: f32, - ) -> Transform4F { - let x_inv = 1.0 / (right - left); - let y_inv = 1.0 / (top - bottom); - let z_inv = 1.0 / (far_val - near_val); - let tx = -(right + left) * x_inv; - let ty = -(top + bottom) * y_inv; - let tz = -(far_val + near_val) * z_inv; - Transform4F::row_major( - 2.0 * x_inv, - 0.0, - 0.0, - tx, - 0.0, - 2.0 * y_inv, - 0.0, - ty, - 0.0, - 0.0, - -2.0 * z_inv, - tz, - 0.0, - 0.0, - 0.0, - 1.0, - ) - } - - /// Linearly interpolate between transforms - pub fn lerp(&self, weight: f32, other: &Transform4F) -> Transform4F { - let c0 = self.c0 * F32x4::splat(weight) + other.c0 * F32x4::splat(1.0 - weight); - let c1 = self.c1 * F32x4::splat(weight) + other.c1 * F32x4::splat(1.0 - weight); - let c2 = self.c2 * F32x4::splat(weight) + other.c2 * F32x4::splat(1.0 - weight); - let c3 = self.c3 * F32x4::splat(weight) + other.c3 * F32x4::splat(1.0 - weight); - Transform4F { c0, c1, c2, c3 } - } - - /// Just like `gluPerspective()`. - #[inline] - pub fn from_perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform4F { - let f = 1.0 / (fov_y * 0.5).tan(); - let z_denom = 1.0 / (z_near - z_far); - let m00 = f / aspect; - let m11 = f; - let m22 = (z_far + z_near) * z_denom; - let m23 = 2.0 * z_far * z_near * z_denom; - let m32 = -1.0; - Transform4F::row_major( - m00, 0.0, 0.0, 0.0, 0.0, m11, 0.0, 0.0, 0.0, 0.0, m22, m23, 0.0, 0.0, m32, 0.0, - ) - } - - /// Just like `gluLookAt()`. - #[inline] - pub fn looking_at(eye: Vector3F, center: Vector3F, mut up: Vector3F) -> Transform4F { - let f = (center - eye).normalize(); - up = up.normalize(); - let s = f.cross(up); - let u = s.normalize().cross(f); - let minus_f = -f; - - // TODO(pcwalton): Use SIMD. This needs a matrix transpose: - // https://fgiesen.wordpress.com/2013/07/09/simd-transposes-1/ - let transform = Transform4F::row_major(s.x(), s.y(), s.z(), 0.0, - u.x(), u.y(), u.z(), 0.0, - minus_f.x(), minus_f.y(), minus_f.z(), 0.0, - 0.0, 0.0, 0.0, 1.0) * - Transform4F::from_translation((-eye).to_4d()); - transform - } - - // +- -+ - // | A B | - // | C D | - // +- -+ - #[inline] - pub fn from_submatrices( - a: Matrix2x2F, - b: Matrix2x2F, - c: Matrix2x2F, - d: Matrix2x2F, - ) -> Transform4F { - Transform4F { - c0: a.0.concat_xy_xy(c.0), - c1: a.0.concat_zw_zw(c.0), - c2: b.0.concat_xy_xy(d.0), - c3: b.0.concat_zw_zw(d.0), - } - } - - #[inline] - pub fn rotate(&self, yaw: f32, pitch: f32, roll: f32) -> Transform4F { - Transform4F::from_rotation(yaw, pitch, roll) * *self - } - - #[inline] - pub fn scale(&self, scale: Vector4F) -> Transform4F { - Transform4F::from_scale(scale) * *self - } - - #[inline] - pub fn uniform_scale(&self, scale: f32) -> Transform4F { - Transform4F::from_uniform_scale(scale) * *self - } - - #[inline] - pub fn translate(&self, translation: Vector4F) -> Transform4F { - Transform4F::from_translation(translation) * *self - } - - #[inline] - pub fn upper_left(&self) -> Matrix2x2F { - Matrix2x2F(self.c0.concat_xy_xy(self.c1)) - } - - #[inline] - pub fn upper_right(&self) -> Matrix2x2F { - Matrix2x2F(self.c2.concat_xy_xy(self.c3)) - } - - #[inline] - pub fn lower_left(&self) -> Matrix2x2F { - Matrix2x2F(self.c0.concat_zw_zw(self.c1)) - } - - #[inline] - pub fn lower_right(&self) -> Matrix2x2F { - Matrix2x2F(self.c2.concat_zw_zw(self.c3)) - } - - // https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion - // - // If A is the upper left submatrix of this matrix, this method assumes that A and the Schur - // complement of A are invertible. - pub fn inverse(&self) -> Transform4F { - // Extract submatrices. - let (a, b) = (self.upper_left(), self.upper_right()); - let (c, d) = (self.lower_left(), self.lower_right()); - - // Compute temporary matrices. - let a_inv = a.inverse(); - let x = c * a_inv; - let y = (d - x * b).inverse(); - let z = a_inv * b; - - // Compute new submatrices. - let (a_new, b_new) = (a_inv + z * y * x, -z * y); - let (c_new, d_new) = (-y * x, y); - - // Construct inverse. - Transform4F::from_submatrices(a_new, b_new, c_new, d_new) - } - - pub fn approx_eq(&self, other: &Transform4F, epsilon: f32) -> bool { - self.c0.approx_eq(other.c0, epsilon) - && self.c1.approx_eq(other.c1, epsilon) - && self.c2.approx_eq(other.c2, epsilon) - && self.c3.approx_eq(other.c3, epsilon) - } - - #[inline] - pub fn as_ptr(&self) -> *const f32 { - (&self.c0) as *const F32x4 as *const f32 - } - - #[inline] - pub fn to_columns(&self) -> [F32x4; 4] { - [self.c0, self.c1, self.c2, self.c3] - } -} - -impl Mul for Transform4F { - type Output = Transform4F; - - // https://stackoverflow.com/a/18508113 - #[inline] - fn mul(self, other: Transform4F) -> Transform4F { - return Transform4F { - c0: mul_col(&self, other.c0), - c1: mul_col(&self, other.c1), - c2: mul_col(&self, other.c2), - c3: mul_col(&self, other.c3), - }; - - #[inline] - fn mul_col(a: &Transform4F, b_col: F32x4) -> F32x4 { - a.c0 * b_col.xxxx() + a.c1 * b_col.yyyy() + a.c2 * b_col.zzzz() + a.c3 * b_col.wwww() - } - } -} - -impl Mul for Transform4F { - type Output = Vector4F; - - #[inline] - fn mul(self, vector: Vector4F) -> Vector4F { - let term0 = self.c0 * F32x4::splat(vector.x()); - let term1 = self.c1 * F32x4::splat(vector.y()); - let term2 = self.c2 * F32x4::splat(vector.z()); - let term3 = self.c3 * F32x4::splat(vector.w()); - Vector4F(term0 + term1 + term2 + term3) - } -} - -impl MulAssign for Transform4F { - fn mul_assign(&mut self, other: Transform4F) { - *self = *self * other - } -} - -impl Add for Matrix2x2F { - type Output = Matrix2x2F; - #[inline] - fn add(self, other: Matrix2x2F) -> Matrix2x2F { - Matrix2x2F(self.0 + other.0) - } -} - -impl Neg for Matrix2x2F { - type Output = Matrix2x2F; - #[inline] - fn neg(self) -> Matrix2x2F { - Matrix2x2F(-self.0) - } -} - -#[derive(Clone, Copy, Debug)] -pub struct Perspective { - pub transform: Transform4F, - pub window_size: Vector2I, -} - -impl Perspective { - #[inline] - pub fn new(transform: &Transform4F, window_size: Vector2I) -> Perspective { - Perspective { - transform: *transform, - window_size, - } - } -} - -impl Mul for Perspective { - type Output = Perspective; - #[inline] - fn mul(self, other: Transform4F) -> Perspective { - Perspective { - transform: self.transform * other, - window_size: self.window_size, - } - } -} - -impl Mul for Perspective { - type Output = Vector2F; - #[inline] - fn mul(self, vector: Vector2F) -> Vector2F { - let point = (self.transform * vector.to_4d()).to_2d() * Vector2F::new(1.0, -1.0); - (point + 1.0) * self.window_size.to_f32() * 0.5 - } -} - -impl Mul for Perspective { - type Output = RectF; - #[inline] - fn mul(self, rect: RectF) -> RectF { - let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right()); - let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right()); - let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); - let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); - RectF::from_points(min_point, max_point) - } -} - -#[cfg(test)] -mod test { - use crate::vector::Vector4F; - use crate::transform3d::Transform4F; - - #[test] - fn test_post_mul() { - let a = Transform4F::row_major( - 3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0, - ); - let b = Transform4F::row_major( - 3.0, 8.0, 4.0, 6.0, 2.0, 6.0, 4.0, 3.0, 3.0, 8.0, 3.0, 2.0, 7.0, 9.0, 5.0, 0.0, - ); - let c = Transform4F::row_major( - 58.0, 107.0, 53.0, 29.0, 84.0, 177.0, 87.0, 72.0, 106.0, 199.0, 101.0, 49.0, 62.0, - 152.0, 83.0, 75.0, - ); - assert_eq!(a * b, c); - } - - #[test] - fn test_pre_mul() { - let a = Transform4F::row_major( - 3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0, - ); - let b = Transform4F::row_major( - 3.0, 8.0, 4.0, 6.0, 2.0, 6.0, 4.0, 3.0, 3.0, 8.0, 3.0, 2.0, 7.0, 9.0, 5.0, 0.0, - ); - let c = Transform4F::row_major( - 135.0, 93.0, 110.0, 103.0, 93.0, 61.0, 85.0, 82.0, 104.0, 52.0, 90.0, 86.0, 117.0, - 50.0, 122.0, 125.0, - ); - assert_eq!(b * a, c); - } - - #[test] - fn test_transform_point() { - let a = Transform4F::row_major( - 3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0, - ); - let p = Vector4F::new(3.0, 8.0, 4.0, 6.0); - let q = Vector4F::new(63.0, 97.0, 135.0, 117.0); - assert_eq!(a * p, q); - } - - #[test] - fn test_inverse() { - // Random matrix. - let m = Transform4F::row_major( - 0.86277982, 0.15986552, 0.90739898, 0.60066808, 0.17386167, 0.016353, 0.8535783, - 0.12969608, 0.0946466, 0.43248631, 0.63480505, 0.08154603, 0.50305436, 0.48359687, - 0.51057162, 0.24812012, - ); - let p0 = Vector4F::new(0.95536648, 0.80633691, 0.16357357, 0.5477598); - let p1 = m * p0; - let m_inv = m.inverse(); - let m_inv_exp = Transform4F::row_major( - -2.47290136, - 3.48865688, - -6.12298336, - 6.17536696, - 0.00124033357, - -1.72561993, - 2.16876606, - 0.186227748, - -0.375021729, - 1.53883017, - -0.0558194403, - 0.121857058, - 5.78300323, - -6.87635769, - 8.30196620, - -9.10374060, - ); - assert!(m_inv.approx_eq(&m_inv_exp, 0.0001)); - let p2 = m_inv * p1; - assert!(p0.approx_eq(p2, 0.0001)); - } -} diff --git a/crates/pathfinder/geometry/src/unit_vector.rs b/crates/pathfinder/geometry/src/unit_vector.rs deleted file mode 100644 index 5a36d1d1f3..0000000000 --- a/crates/pathfinder/geometry/src/unit_vector.rs +++ /dev/null @@ -1,47 +0,0 @@ -// pathfinder/geometry/src/unit_vector.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A utility module that allows unit vectors to be treated like angles. - -use crate::vector::Vector2F; -use pathfinder_simd::default::F32x2; - -#[derive(Clone, Copy, Debug)] -pub struct UnitVector(pub Vector2F); - -impl UnitVector { - #[inline] - pub fn from_angle(theta: f32) -> UnitVector { - UnitVector(Vector2F::new(theta.cos(), theta.sin())) - } - - /// Angle addition formula. - #[inline] - pub fn rotate_by(&self, other: UnitVector) -> UnitVector { - let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy(); - UnitVector(Vector2F::new(products[0] - products[1], products[2] + products[3])) - } - - /// Angle subtraction formula. - #[inline] - pub fn rev_rotate_by(&self, other: UnitVector) -> UnitVector { - let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy(); - UnitVector(Vector2F::new(products[0] + products[1], products[2] - products[3])) - } - - /// Half angle formula. - #[inline] - pub fn halve_angle(&self) -> UnitVector { - let x = self.0.x(); - let term = F32x2::new(x, -x); - UnitVector(Vector2F((F32x2::splat(0.5) * (F32x2::splat(1.0) + term)).max(F32x2::default()) - .sqrt())) - } -} diff --git a/crates/pathfinder/geometry/src/util.rs b/crates/pathfinder/geometry/src/util.rs deleted file mode 100644 index 1add973be9..0000000000 --- a/crates/pathfinder/geometry/src/util.rs +++ /dev/null @@ -1,39 +0,0 @@ -// pathfinder/geometry/src/util.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Various utilities. - -use std::f32; - -pub const EPSILON: f32 = 0.001; - -/// Approximate equality. -#[inline] -pub fn approx_eq(a: f32, b: f32) -> bool { - f32::abs(a - b) <= EPSILON -} - -/// Linear interpolation. -#[inline] -pub fn lerp(a: f32, b: f32, t: f32) -> f32 { - a + (b - a) * t -} - -/// Clamping. -#[inline] -pub fn clamp(x: f32, min_val: f32, max_val: f32) -> f32 { - f32::min(max_val, f32::max(min_val, x)) -} - -/// Divides `a` by `b`, rounding up. -#[inline] -pub fn alignup_i32(a: i32, b: i32) -> i32 { - (a + b - 1) / b -} diff --git a/crates/pathfinder/geometry/src/vector.rs b/crates/pathfinder/geometry/src/vector.rs deleted file mode 100644 index cd2c28cf08..0000000000 --- a/crates/pathfinder/geometry/src/vector.rs +++ /dev/null @@ -1,687 +0,0 @@ -// pathfinder/geometry/src/basic/point.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A SIMD-optimized point type. - -use pathfinder_simd::default::{F32x2, F32x4, I32x2}; -use std::hash::{Hash, Hasher}; -use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; - -/// 2D points with 32-bit floating point coordinates. -#[derive(Clone, Copy, Debug, Default)] -pub struct Vector2F(pub F32x2); - -impl Vector2F { - #[inline] - pub fn new(x: f32, y: f32) -> Vector2F { - Vector2F(F32x2::new(x, y)) - } - - #[inline] - pub fn splat(value: f32) -> Vector2F { - Vector2F(F32x2::splat(value)) - } - - #[inline] - pub fn zero() -> Vector2F { - Vector2F::default() - } - - #[inline] - pub fn to_3d(self) -> Vector3F { - Vector3F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0))) - } - - #[inline] - pub fn to_4d(self) -> Vector4F { - Vector4F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0))) - } - - #[inline] - pub fn x(self) -> f32 { - self.0[0] - } - - #[inline] - pub fn y(self) -> f32 { - self.0[1] - } - - #[inline] - pub fn set_x(&mut self, x: f32) { - self.0[0] = x; - } - - #[inline] - pub fn set_y(&mut self, y: f32) { - self.0[1] = y; - } - - #[inline] - pub fn min(self, other: Vector2F) -> Vector2F { - Vector2F(self.0.min(other.0)) - } - - #[inline] - pub fn max(self, other: Vector2F) -> Vector2F { - Vector2F(self.0.max(other.0)) - } - - #[inline] - pub fn clamp(self, min_val: Vector2F, max_val: Vector2F) -> Vector2F { - self.max(min_val).min(max_val) - } - - #[inline] - pub fn det(self, other: Vector2F) -> f32 { - self.x() * other.y() - self.y() * other.x() - } - - #[inline] - pub fn dot(self, other: Vector2F) -> f32 { - let xy = self.0 * other.0; - xy.x() + xy.y() - } - - #[inline] - pub fn floor(self) -> Vector2F { - Vector2F(self.0.floor()) - } - - #[inline] - pub fn ceil(self) -> Vector2F { - Vector2F(self.0.ceil()) - } - - /// Rounds both coordinates to the nearest integer. - #[inline] - pub fn round(self) -> Vector2F { - Vector2F(self.0.to_i32x2().to_f32x2()) - } - - /// Treats this point as a vector and calculates its squared length. - #[inline] - pub fn square_length(self) -> f32 { - let squared = self.0 * self.0; - squared[0] + squared[1] - } - - /// Treats this point as a vector and calculates its length. - #[inline] - pub fn length(self) -> f32 { - f32::sqrt(self.square_length()) - } - - /// Treats this point as a vector and normalizes it. - #[inline] - pub fn normalize(self) -> Vector2F { - self * (1.0 / self.length()) - } - - /// Swaps y and x. - #[inline] - pub fn yx(self) -> Vector2F { - Vector2F(self.0.yx()) - } - - /// Returns the coefficient when the given vector `a` is projected onto this one. - /// - /// That is, if this vector is `v` and this function returns `c`, then `proj_v a = cv`. In - /// other words, this function computes `(a⋅v) / (v⋅v)`. - #[inline] - pub fn projection_coefficient(self, a: Vector2F) -> f32 { - a.dot(self) / self.square_length() - } - - #[inline] - pub fn is_zero(self) -> bool { - self == Vector2F::zero() - } - - #[inline] - pub fn lerp(self, other: Vector2F, t: f32) -> Vector2F { - self + (other - self) * t - } - - #[inline] - pub fn to_i32(self) -> Vector2I { - Vector2I(self.0.to_i32x2()) - } -} - -/// A convenience alias for `Vector2F::new()`. -#[inline] -pub fn vec2f(x: f32, y: f32) -> Vector2F { - Vector2F::new(x, y) -} - -impl PartialEq for Vector2F { - #[inline] - fn eq(&self, other: &Vector2F) -> bool { - self.0.packed_eq(other.0).all_true() - } -} - -impl Add for Vector2F { - type Output = Vector2F; - #[inline] - fn add(self, other: Vector2F) -> Vector2F { - Vector2F(self.0 + other.0) - } -} - -impl Add for Vector2F { - type Output = Vector2F; - #[inline] - fn add(self, other: f32) -> Vector2F { - self + Vector2F::splat(other) - } -} - -impl AddAssign for Vector2F { - #[inline] - fn add_assign(&mut self, other: Vector2F) { - *self = *self + other - } -} - -impl Sub for Vector2F { - type Output = Vector2F; - #[inline] - fn sub(self, other: Vector2F) -> Vector2F { - Vector2F(self.0 - other.0) - } -} - -impl Sub for Vector2F { - type Output = Vector2F; - #[inline] - fn sub(self, other: f32) -> Vector2F { - self - Vector2F::splat(other) - } -} - -impl SubAssign for Vector2F { - #[inline] - fn sub_assign(&mut self, other: Vector2F) { - *self = *self - other - } -} - -impl Mul for Vector2F { - type Output = Vector2F; - #[inline] - fn mul(self, other: Vector2F) -> Vector2F { - Vector2F(self.0 * other.0) - } -} - -impl Mul for Vector2F { - type Output = Vector2F; - #[inline] - fn mul(self, other: f32) -> Vector2F { - self * Vector2F::splat(other) - } -} - -impl MulAssign for Vector2F { - #[inline] - fn mul_assign(&mut self, other: Vector2F) { - *self = *self * other - } -} - -impl MulAssign for Vector2F { - #[inline] - fn mul_assign(&mut self, other: f32) { - *self = *self * other - } -} - -impl Div for Vector2F { - type Output = Vector2F; - #[inline] - fn div(self, other: Vector2F) -> Vector2F { - Vector2F(self.0 / other.0) - } -} - -impl Div for Vector2F { - type Output = Vector2F; - #[inline] - fn div(self, other: f32) -> Vector2F { - self / Vector2F::splat(other) - } -} - -impl Neg for Vector2F { - type Output = Vector2F; - #[inline] - fn neg(self) -> Vector2F { - Vector2F(-self.0) - } -} - -/// Either a scalar or a `Vector2F`. -/// -/// Scalars will be automatically splatted (i.e. `x` becomes `vec2f(x, x)`). -/// -/// Be judicious with the use of this trait. Only use it if it will aid readability without the -/// potential to introduce bugs. -pub trait IntoVector2F { - fn into_vector_2f(self) -> Vector2F; -} - -impl IntoVector2F for Vector2F { - #[inline] - fn into_vector_2f(self) -> Vector2F { - self - } -} - -impl IntoVector2F for f32 { - #[inline] - fn into_vector_2f(self) -> Vector2F { - Vector2F::splat(self) - } -} - -/// 2D points with 32-bit signed integer coordinates. -#[derive(Clone, Copy, Debug, Default)] -pub struct Vector2I(pub I32x2); - -impl Vector2I { - #[inline] - pub fn new(x: i32, y: i32) -> Vector2I { - Vector2I(I32x2::new(x, y)) - } - - #[inline] - pub fn splat(value: i32) -> Vector2I { - Vector2I(I32x2::splat(value)) - } - - #[inline] - pub fn zero() -> Vector2I { - Vector2I::default() - } - - #[inline] - pub fn x(self) -> i32 { - self.0[0] - } - - #[inline] - pub fn y(self) -> i32 { - self.0[1] - } - - #[inline] - pub fn set_x(&mut self, x: i32) { - self.0[0] = x; - } - - #[inline] - pub fn set_y(&mut self, y: i32) { - self.0[1] = y; - } - - #[inline] - pub fn min(self, other: Vector2I) -> Vector2I { - Vector2I(self.0.min(other.0)) - } - - #[inline] - pub fn max(self, other: Vector2I) -> Vector2I { - Vector2I(self.0.max(other.0)) - } - - #[inline] - pub fn to_f32(self) -> Vector2F { - Vector2F(self.0.to_f32x2()) - } -} - -/// A convenience alias for `Vector2I::new()`. -#[inline] -pub fn vec2i(x: i32, y: i32) -> Vector2I { - Vector2I::new(x, y) -} - -impl Add for Vector2I { - type Output = Vector2I; - #[inline] - fn add(self, other: Vector2I) -> Vector2I { - Vector2I(self.0 + other.0) - } -} - -impl Add for Vector2I { - type Output = Vector2I; - #[inline] - fn add(self, other: i32) -> Vector2I { - self + Vector2I::splat(other) - } -} - -impl AddAssign for Vector2I { - #[inline] - fn add_assign(&mut self, other: Vector2I) { - self.0 += other.0 - } -} - -impl Neg for Vector2I { - type Output = Vector2I; - #[inline] - fn neg(self) -> Vector2I { - Vector2I(-self.0) - } -} - -impl Sub for Vector2I { - type Output = Vector2I; - #[inline] - fn sub(self, other: Vector2I) -> Vector2I { - Vector2I(self.0 - other.0) - } -} - -impl Sub for Vector2I { - type Output = Vector2I; - #[inline] - fn sub(self, other: i32) -> Vector2I { - self - Vector2I::splat(other) - } -} - -impl Mul for Vector2I { - type Output = Vector2I; - #[inline] - fn mul(self, other: Vector2I) -> Vector2I { - Vector2I(self.0 * other.0) - } -} - -impl Mul for Vector2I { - type Output = Vector2I; - #[inline] - fn mul(self, other: i32) -> Vector2I { - self * Vector2I::splat(other) - } -} - -impl PartialEq for Vector2I { - #[inline] - fn eq(&self, other: &Vector2I) -> bool { - self.0.packed_eq(other.0).all_true() - } -} - -impl Eq for Vector2I {} - -impl Hash for Vector2I { - #[inline] - fn hash(&self, state: &mut H) where H: Hasher { - self.x().hash(state); - self.y().hash(state); - } -} - -/// 3D points. -/// -/// The w value in the SIMD vector is always 0.0. -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Vector3F(pub F32x4); - -impl Vector3F { - #[inline] - pub fn new(x: f32, y: f32, z: f32) -> Vector3F { - Vector3F(F32x4::new(x, y, z, 0.0)) - } - - #[inline] - pub fn splat(x: f32) -> Vector3F { - let mut vector = F32x4::splat(x); - vector.set_w(0.0); - Vector3F(vector) - } - - /// Truncates this vector to 2D. - #[inline] - pub fn to_2d(self) -> Vector2F { - Vector2F(self.0.xy()) - } - - /// Converts this vector to an equivalent 3D homogeneous one with a w component of 1.0. - #[inline] - pub fn to_4d(self) -> Vector4F { - let mut vector = self.0; - vector.set_w(1.0); - Vector4F(vector) - } - - #[inline] - pub fn cross(self, other: Vector3F) -> Vector3F { - Vector3F(self.0.yzxw() * other.0.zxyw() - self.0.zxyw() * other.0.yzxw()) - } - - #[inline] - pub fn square_length(self) -> f32 { - let squared = self.0 * self.0; - squared[0] + squared[1] + squared[2] - } - - #[inline] - pub fn length(self) -> f32 { - f32::sqrt(self.square_length()) - } - - #[inline] - pub fn normalize(self) -> Vector3F { - Vector3F(self.0 * F32x4::splat(1.0 / self.length())) - } - - #[inline] - pub fn x(self) -> f32 { - self.0[0] - } - - #[inline] - pub fn y(self) -> f32 { - self.0[1] - } - - #[inline] - pub fn z(self) -> f32 { - self.0[2] - } - - #[inline] - pub fn scale(self, factor: f32) -> Vector3F { - Vector3F(self.0 * F32x4::splat(factor)) - } -} - -impl Add for Vector3F { - type Output = Vector3F; - #[inline] - fn add(self, other: Vector3F) -> Vector3F { - Vector3F(self.0 + other.0) - } -} - -impl AddAssign for Vector3F { - #[inline] - fn add_assign(&mut self, other: Vector3F) { - self.0 += other.0 - } -} - -impl Neg for Vector3F { - type Output = Vector3F; - #[inline] - fn neg(self) -> Vector3F { - Vector3F(self.0 * F32x4::new(-1.0, -1.0, -1.0, 0.0)) - } -} - -impl Sub for Vector3F { - type Output = Vector3F; - #[inline] - fn sub(self, other: Vector3F) -> Vector3F { - Vector3F(self.0 - other.0) - } -} - -/// 3D homogeneous points. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Vector4F(pub F32x4); - -impl Vector4F { - #[inline] - pub fn new(x: f32, y: f32, z: f32, w: f32) -> Vector4F { - Vector4F(F32x4::new(x, y, z, w)) - } - - #[inline] - pub fn splat(value: f32) -> Vector4F { - Vector4F(F32x4::splat(value)) - } - - #[inline] - pub fn to_2d(self) -> Vector2F { - self.to_3d().to_2d() - } - - /// Performs perspective division to convert this vector to 3D. - #[inline] - pub fn to_3d(self) -> Vector3F { - let mut vector = self.0 * F32x4::splat(1.0 / self.w()); - vector.set_w(0.0); - Vector3F(vector) - } - - #[inline] - pub fn x(self) -> f32 { - self.0[0] - } - - #[inline] - pub fn y(self) -> f32 { - self.0[1] - } - - #[inline] - pub fn z(self) -> f32 { - self.0[2] - } - - #[inline] - pub fn w(self) -> f32 { - self.0[3] - } - - #[inline] - pub fn scale(self, x: f32) -> Vector4F { - let mut factors = F32x4::splat(x); - factors[3] = 1.0; - Vector4F(self.0 * factors) - } - - #[inline] - pub fn set_x(&mut self, x: f32) { - self.0[0] = x - } - - #[inline] - pub fn set_y(&mut self, y: f32) { - self.0[1] = y - } - - #[inline] - pub fn set_z(&mut self, z: f32) { - self.0[2] = z - } - - #[inline] - pub fn set_w(&mut self, w: f32) { - self.0[3] = w - } - - #[inline] - pub fn approx_eq(self, other: Vector4F, epsilon: f32) -> bool { - self.0.approx_eq(other.0, epsilon) - } - - /// Checks to see whether this *homogeneous* coordinate equals zero. - /// - /// Note that since this treats the coordinate as a homogeneous coordinate, the `w` is ignored. - // TODO(pcwalton): Optimize with SIMD. - #[inline] - pub fn is_zero(self) -> bool { - self.x() == 0.0 && self.y() == 0.0 && self.z() == 0.0 - } - - #[inline] - pub fn lerp(self, other: Vector4F, t: f32) -> Vector4F { - Vector4F(self.0 + (other.0 - self.0) * F32x4::splat(t)) - } -} - -impl Add for Vector4F { - type Output = Vector4F; - #[inline] - fn add(self, other: Vector4F) -> Vector4F { - Vector4F(self.0 + other.0) - } -} - -impl AddAssign for Vector4F { - #[inline] - fn add_assign(&mut self, other: Vector4F) { - self.0 += other.0 - } -} - -impl Mul for Vector4F { - type Output = Vector4F; - #[inline] - fn mul(self, other: Vector4F) -> Vector4F { - Vector4F(self.0 * other.0) - } -} - -impl Neg for Vector4F { - type Output = Vector4F; - /// NB: This does not negate w, because that is rarely what you what for homogeneous - /// coordinates. - #[inline] - fn neg(self) -> Vector4F { - Vector4F(self.0 * F32x4::new(-1.0, -1.0, -1.0, 1.0)) - } -} - -impl Sub for Vector4F { - type Output = Vector4F; - #[inline] - fn sub(self, other: Vector4F) -> Vector4F { - Vector4F(self.0 - other.0) - } -} - -impl Default for Vector4F { - #[inline] - fn default() -> Vector4F { - let mut point = F32x4::default(); - point.set_w(1.0); - Vector4F(point) - } -} diff --git a/crates/pathfinder/gpu/Cargo.toml b/crates/pathfinder/gpu/Cargo.toml deleted file mode 100644 index 80a178692b..0000000000 --- a/crates/pathfinder/gpu/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "pathfinder_gpu" -version = "0.5.0" -edition = "2018" -authors = ["Patrick Walton "] -description = "A simple cross-platform GPU abstraction library" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" - -[features] -shader_alignment_32_bits = ["pathfinder_geometry/shader_alignment_32_bits"] - -[dependencies] -bitflags = "1.0" -half = "1.5" - -[dependencies.image] -version = "0.23" -default-features = false -features = ["png"] - -[dependencies.pathfinder_color] -path = "../color" -version = "0.5" - -[dependencies.pathfinder_geometry] -path = "../geometry" -version = "0.5" - -[dependencies.pathfinder_resources] -path = "../resources" -version = "0.5" - -[dependencies.pathfinder_simd] -path = "../simd" -version = "0.5" diff --git a/crates/pathfinder/gpu/src/lib.rs b/crates/pathfinder/gpu/src/lib.rs deleted file mode 100644 index 77a1b5f55a..0000000000 --- a/crates/pathfinder/gpu/src/lib.rs +++ /dev/null @@ -1,605 +0,0 @@ -// pathfinder/gpu/src/lib.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Minimal abstractions over GPU device capabilities. - -#[macro_use] -extern crate bitflags; - -use half::f16; -use image::ImageFormat; -use pathfinder_color::ColorF; -use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform3d::Transform4F; -use pathfinder_geometry::vector::{Vector2I, vec2i}; -use pathfinder_resources::ResourceLoader; -use pathfinder_simd::default::{F32x2, F32x4, I32x2}; -use std::os::raw::c_void; -use std::time::Duration; - -pub trait Device: Sized { - type Buffer; - type Fence; - type Framebuffer; - type Program; - type Shader; - type StorageBuffer; - type Texture; - type TextureDataReceiver; - type TimerQuery; - type Uniform; - type VertexArray; - type VertexAttr; - - fn feature_level(&self) -> FeatureLevel; - fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture; - fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef) - -> Self::Texture; - fn create_shader(&self, resources: &dyn ResourceLoader, name: &str, kind: ShaderKind) - -> Self::Shader; - fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind) - -> Self::Shader; - fn create_vertex_array(&self) -> Self::VertexArray; - fn create_program_from_shaders(&self, - resources: &dyn ResourceLoader, - name: &str, - shaders: ProgramKind) - -> Self::Program; - fn set_compute_program_local_size(&self, - program: &mut Self::Program, - local_size: ComputeDimensions); - fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option; - fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform; - fn get_storage_buffer(&self, program: &Self::Program, name: &str, binding: u32) - -> Self::StorageBuffer; - fn bind_buffer(&self, - vertex_array: &Self::VertexArray, - buffer: &Self::Buffer, - target: BufferTarget); - fn configure_vertex_attr(&self, - vertex_array: &Self::VertexArray, - attr: &Self::VertexAttr, - descriptor: &VertexAttrDescriptor); - fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer; - fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer; - fn allocate_buffer(&self, - buffer: &Self::Buffer, - data: BufferData, - target: BufferTarget); - fn upload_to_buffer(&self, - buffer: &Self::Buffer, - position: usize, - data: &[T], - target: BufferTarget); - fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture; - fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture; - fn texture_format(&self, texture: &Self::Texture) -> TextureFormat; - fn texture_size(&self, texture: &Self::Texture) -> Vector2I; - fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags); - fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef); - fn read_pixels(&self, target: &RenderTarget, viewport: RectI) - -> Self::TextureDataReceiver; - fn begin_commands(&self); - fn end_commands(&self); - fn draw_arrays(&self, index_count: u32, render_state: &RenderState); - fn draw_elements(&self, index_count: u32, render_state: &RenderState); - fn draw_elements_instanced(&self, - index_count: u32, - instance_count: u32, - render_state: &RenderState); - fn dispatch_compute(&self, dimensions: ComputeDimensions, state: &ComputeState); - fn add_fence(&self) -> Self::Fence; - fn wait_for_fence(&self, fence: &Self::Fence); - fn create_timer_query(&self) -> Self::TimerQuery; - fn begin_timer_query(&self, query: &Self::TimerQuery); - fn end_timer_query(&self, query: &Self::TimerQuery); - fn try_recv_timer_query(&self, query: &Self::TimerQuery) -> Option; - fn recv_timer_query(&self, query: &Self::TimerQuery) -> Duration; - fn try_recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> Option; - fn recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> TextureData; - - fn create_texture_from_png(&self, - resources: &dyn ResourceLoader, - name: &str, - format: TextureFormat) - -> Self::Texture { - let data = resources.slurp(&format!("textures/{}.png", name)).unwrap(); - let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap(); - match format { - TextureFormat::R8 => { - let image = image.to_luma(); - let size = vec2i(image.width() as i32, image.height() as i32); - self.create_texture_from_data(format, size, TextureDataRef::U8(&image)) - } - TextureFormat::RGBA8 => { - let image = image.to_rgba(); - let size = vec2i(image.width() as i32, image.height() as i32); - self.create_texture_from_data(format, size, TextureDataRef::U8(&image)) - } - _ => unimplemented!(), - } - } - - fn create_program_from_shader_names( - &self, - resources: &dyn ResourceLoader, - program_name: &str, - shader_names: ProgramKind<&str>, - ) -> Self::Program { - let shaders = match shader_names { - ProgramKind::Raster { vertex, fragment } => { - ProgramKind::Raster { - vertex: self.create_shader(resources, vertex, ShaderKind::Vertex), - fragment: self.create_shader(resources, fragment, ShaderKind::Fragment), - } - } - ProgramKind::Compute(compute) => { - ProgramKind::Compute(self.create_shader(resources, compute, ShaderKind::Compute)) - } - }; - self.create_program_from_shaders(resources, program_name, shaders) - } - - fn create_raster_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program { - let shaders = ProgramKind::Raster { vertex: name, fragment: name }; - self.create_program_from_shader_names(resources, name, shaders) - } - - fn create_compute_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program { - let shaders = ProgramKind::Compute(name); - self.create_program_from_shader_names(resources, name, shaders) - } -} - -/// These are rough analogues to D3D versions; don't expect them to represent exactly the feature -/// set of the versions. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum FeatureLevel { - D3D10, - D3D11, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum TextureFormat { - R8, - R16F, - RGBA8, - RGBA16F, - RGBA32F, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum VertexAttrType { - F32, - I32, - I16, - I8, - U32, - U16, - U8, -} - -impl VertexAttrType { - pub fn get_size(&self) -> usize { - match *self { - VertexAttrType::F32 => 4, - VertexAttrType::I32 => 4, - VertexAttrType::I16 => 2, - VertexAttrType::I8 => 1, - VertexAttrType::U32 => 4, - VertexAttrType::U16 => 2, - VertexAttrType::U8 => 1, - } - } -} - -#[cfg(feature = "shader_alignment_32_bits")] -pub const ALIGNED_U8_ATTR: VertexAttrType = VertexAttrType::U32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub const ALIGNED_U8_ATTR: VertexAttrType = VertexAttrType::U8; - -#[cfg(feature = "shader_alignment_32_bits")] -pub const ALIGNED_U16_ATTR: VertexAttrType = VertexAttrType::U32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub const ALIGNED_U16_ATTR: VertexAttrType = VertexAttrType::U16; - -#[cfg(feature = "shader_alignment_32_bits")] -pub const ALIGNED_I8_ATTR: VertexAttrType = VertexAttrType::I32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub const ALIGNED_I8_ATTR: VertexAttrType = VertexAttrType::I8; - -#[cfg(feature = "shader_alignment_32_bits")] -pub const ALIGNED_I16_ATTR: VertexAttrType = VertexAttrType::I32; -#[cfg(not(feature = "shader_alignment_32_bits"))] -pub const ALIGNED_I16_ATTR: VertexAttrType = VertexAttrType::I16; - -#[derive(Clone, Copy, Debug)] -pub enum BufferData<'a, T> { - Uninitialized(usize), - Memory(&'a [T]), -} - -#[derive(Clone, Copy, Debug)] -pub enum BufferTarget { - Vertex, - Index, - Storage, -} - -#[derive(Clone, Copy, Debug)] -pub enum BufferUploadMode { - Static, - Dynamic, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ShaderKind { - Vertex, - Fragment, - Compute, -} - -#[derive(Clone, Copy, Debug)] -pub enum ProgramKind { - Raster { - vertex: T, - fragment: T, - }, - Compute(T), -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct ComputeDimensions { - pub x: u32, - pub y: u32, - pub z: u32, -} - -#[derive(Clone, Copy)] -pub enum UniformData { - Float(f32), - IVec2(I32x2), - IVec3([i32; 3]), - Int(i32), - Mat2(F32x4), - Mat4([F32x4; 4]), - Vec2(F32x2), - Vec3([f32; 3]), - Vec4(F32x4), - TextureUnit(u32), - ImageUnit(u32), -} - -#[derive(Clone, Copy)] -pub enum Primitive { - Triangles, - Lines, -} - -#[derive(Clone)] -pub struct RenderState<'a, D> where D: Device { - pub target: &'a RenderTarget<'a, D>, - pub program: &'a D::Program, - pub vertex_array: &'a D::VertexArray, - pub primitive: Primitive, - pub uniforms: &'a [(&'a D::Uniform, UniformData)], - pub textures: &'a [&'a D::Texture], - pub images: &'a [ImageBinding<'a, D>], - pub viewport: RectI, - pub options: RenderOptions, -} - -#[derive(Clone)] -pub struct ComputeState<'a, D> where D: Device { - pub program: &'a D::Program, - pub uniforms: &'a [(&'a D::Uniform, UniformData)], - pub textures: &'a [&'a D::Texture], - pub images: &'a [ImageBinding<'a, D>], - pub storage_buffers: &'a [(&'a D::StorageBuffer, &'a D::Buffer)], -} - -#[derive(Clone, Debug)] -pub struct ImageBinding<'a, D> where D: Device { - pub texture: &'a D::Texture, - pub access: ImageAccess, -} - -#[derive(Clone, Debug)] -pub struct RenderOptions { - pub blend: Option, - pub depth: Option, - pub stencil: Option, - pub clear_ops: ClearOps, - pub color_mask: bool, -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct ClearOps { - pub color: Option, - pub depth: Option, - pub stencil: Option, -} - -#[derive(Clone, Copy, Debug)] -pub enum RenderTarget<'a, D> where D: Device { - Default, - Framebuffer(&'a D::Framebuffer), -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct BlendState { - pub dest_rgb_factor: BlendFactor, - pub dest_alpha_factor: BlendFactor, - pub src_rgb_factor: BlendFactor, - pub src_alpha_factor: BlendFactor, - pub op: BlendOp, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum BlendFactor { - Zero, - One, - SrcAlpha, - OneMinusSrcAlpha, - DestAlpha, - OneMinusDestAlpha, - DestColor, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum BlendOp { - Add, - Subtract, - ReverseSubtract, - Min, - Max, -} - -#[derive(Clone, Copy, Default, Debug)] -pub struct DepthState { - pub func: DepthFunc, - pub write: bool, -} - -#[derive(Clone, Copy, Debug)] -pub enum DepthFunc { - Less, - Always, -} - -#[derive(Clone, Copy, Debug)] -pub struct StencilState { - pub func: StencilFunc, - pub reference: u32, - pub mask: u32, - pub write: bool, -} - -#[derive(Clone, Copy, Debug)] -pub enum StencilFunc { - Always, - Equal, -} - -impl Default for RenderOptions { - #[inline] - fn default() -> RenderOptions { - RenderOptions { - blend: None, - depth: None, - stencil: None, - clear_ops: ClearOps::default(), - color_mask: true, - } - } -} - -impl Default for BlendOp { - #[inline] - fn default() -> BlendOp { - BlendOp::Add - } -} - -impl Default for StencilState { - #[inline] - fn default() -> StencilState { - StencilState { - func: StencilFunc::default(), - reference: 0, - mask: !0, - write: false, - } - } -} - -impl Default for DepthFunc { - #[inline] - fn default() -> DepthFunc { - DepthFunc::Less - } -} - -impl Default for StencilFunc { - #[inline] - fn default() -> StencilFunc { - StencilFunc::Always - } -} - -#[derive(Clone, Debug)] -pub enum TextureData { - U8(Vec), - U16(Vec), - F16(Vec), - F32(Vec), -} - -#[derive(Clone, Copy, Debug)] -pub enum TextureDataRef<'a> { - U8(&'a [u8]), - F16(&'a [f16]), - F32(&'a [f32]), -} - -impl UniformData { - #[inline] - pub fn from_transform_3d(transform: &Transform4F) -> UniformData { - UniformData::Mat4([transform.c0, transform.c1, transform.c2, transform.c3]) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct VertexAttrDescriptor { - pub size: usize, - pub class: VertexAttrClass, - pub attr_type: VertexAttrType, - pub stride: usize, - pub offset: usize, - pub divisor: u32, - pub buffer_index: u32, -} - -impl VertexAttrDescriptor { - pub const fn datatype_only(class: VertexAttrClass, attr_type: VertexAttrType, size: usize) -> Self { - VertexAttrDescriptor { - size, - class, - attr_type, - divisor: 0, - buffer_index: 0, - stride: 0, - offset: 0, - } - } -} - -pub struct VertexBufferDescriptor { - pub index: u32, - pub divisor: u32, - pub vertex_attrs: Vec, -} - -impl VertexBufferDescriptor { - pub fn update_attrs(&mut self) { - let mut offset = 0; - for attr in self.vertex_attrs.iter_mut() { - attr.buffer_index = self.index; - attr.divisor = self.divisor; - attr.offset = offset; - offset += attr.size * attr.attr_type.get_size(); - } - - for attr in self.vertex_attrs.iter_mut() { - attr.stride = offset; - } - } - - pub fn configure_vertex_attrs(&self, device: &D, vertex_array: &D::VertexArray, attrs: &[D::VertexAttr]) { - for (attr, descriptor) in attrs.iter().zip(self.vertex_attrs.iter()) { - device.configure_vertex_attr(vertex_array, attr, &descriptor); - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum VertexAttrClass { - Float, - FloatNorm, - Int, -} - -impl TextureFormat { - #[inline] - pub fn channels(self) -> usize { - match self { - TextureFormat::R8 | TextureFormat::R16F => 1, - TextureFormat::RGBA8 | TextureFormat::RGBA16F | TextureFormat::RGBA32F => 4, - } - } - - #[inline] - pub fn bytes_per_pixel(self) -> usize { - match self { - TextureFormat::R8 => 1, - TextureFormat::R16F => 2, - TextureFormat::RGBA8 => 4, - TextureFormat::RGBA16F => 8, - TextureFormat::RGBA32F => 16, - } - } -} - -impl ClearOps { - #[inline] - pub fn has_ops(&self) -> bool { - self.color.is_some() || self.depth.is_some() || self.stencil.is_some() - } -} - -impl Default for BlendState { - #[inline] - fn default() -> BlendState { - BlendState { - src_rgb_factor: BlendFactor::One, - dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, - src_alpha_factor: BlendFactor::One, - dest_alpha_factor: BlendFactor::One, - op: BlendOp::Add, - } - } -} - -bitflags! { - pub struct TextureSamplingFlags: u8 { - const REPEAT_U = 0x01; - const REPEAT_V = 0x02; - const NEAREST_MIN = 0x04; - const NEAREST_MAG = 0x08; - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ImageAccess { - Read, - Write, - ReadWrite, -} - -impl<'a> TextureDataRef<'a> { - #[doc(hidden)] - pub fn check_and_extract_data_ptr(self, minimum_size: Vector2I, format: TextureFormat) - -> *const c_void { - let channels = match (format, self) { - (TextureFormat::R8, TextureDataRef::U8(_)) => 1, - (TextureFormat::RGBA8, TextureDataRef::U8(_)) => 4, - (TextureFormat::RGBA16F, TextureDataRef::F16(_)) => 4, - (TextureFormat::RGBA32F, TextureDataRef::F32(_)) => 4, - _ => panic!("Unimplemented texture format!"), - }; - - let area = minimum_size.x() as usize * minimum_size.y() as usize; - - match self { - TextureDataRef::U8(data) => { - assert!(data.len() >= area * channels); - data.as_ptr() as *const c_void - } - TextureDataRef::F16(data) => { - assert!(data.len() >= area * channels); - data.as_ptr() as *const c_void - } - TextureDataRef::F32(data) => { - assert!(data.len() >= area * channels); - data.as_ptr() as *const c_void - } - } - } -} diff --git a/crates/pathfinder/lottie/Cargo.toml b/crates/pathfinder/lottie/Cargo.toml deleted file mode 100644 index a7b6b26271..0000000000 --- a/crates/pathfinder/lottie/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "pathfinder_lottie" -version = "0.1.0" -authors = ["Patrick Walton "] -edition = "2018" - -[dependencies] -serde_json = "1.0" - -[dependencies.serde] -version = "1.0" -features = ["derive"] diff --git a/crates/pathfinder/lottie/src/lib.rs b/crates/pathfinder/lottie/src/lib.rs deleted file mode 100644 index 4b062cdb23..0000000000 --- a/crates/pathfinder/lottie/src/lib.rs +++ /dev/null @@ -1,313 +0,0 @@ -// pathfinder/lottie/src/lib.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Experimental support for Lottie. This is very incomplete. - -use serde::{Deserialize, Serialize}; -use serde_json::Error as JSONError; -use std::io::Read; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Lottie { - #[serde(rename = "v")] - pub version: String, - #[serde(rename = "fr")] - pub frame_rate: i64, - #[serde(rename = "ip")] - pub in_point: i64, - #[serde(rename = "op")] - pub out_point: i64, - #[serde(rename = "w")] - pub width: f64, - #[serde(rename = "h")] - pub height: f64, - #[serde(rename = "ddd")] - pub three_d: i64, - pub assets: Vec, - pub layers: Vec, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Asset {} - -// FIXME(pcwalton): Using an untagged enum is a botch here. There actually is a tag: it's just an -// integer, which `serde_json` doesn't support natively. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum Layer { - Shape { - #[serde(rename = "ddd")] - three_d: i64, - #[serde(rename = "ind")] - index: i64, - #[serde(rename = "nm")] - name: String, - #[serde(rename = "ks")] - transform: Transform, - #[serde(rename = "ao")] - auto_orient: i64, - #[serde(rename = "ip")] - in_point: i64, - #[serde(rename = "op")] - out_point: i64, - #[serde(rename = "st")] - start_time: i64, - #[serde(rename = "bm")] - blend_mode: i64, - #[serde(rename = "sr")] - stretch: i64, - #[serde(rename = "ln")] - #[serde(default)] - layer_id: Option, - shapes: Vec, - }, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Transform { - #[serde(rename = "p")] - pub position: MultidimensionalPropertyValue, - #[serde(rename = "a")] - pub anchor_point: MultidimensionalPropertyValue, - #[serde(rename = "s")] - pub scale: MultidimensionalPropertyValue, - #[serde(rename = "r")] - pub rotation: PropertyValue, - #[serde(rename = "o")] - #[serde(default)] - pub opacity: Option, - #[serde(rename = "px")] - #[serde(default)] - pub position_x: Option, - #[serde(rename = "py")] - #[serde(default)] - pub position_y: Option, - #[serde(rename = "pz")] - #[serde(default)] - pub position_z: Option, - #[serde(rename = "sk")] - #[serde(default)] - pub skew: Option, - #[serde(rename = "sa")] - #[serde(default)] - pub skew_axis: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum PropertyValue { - Value { - #[serde(rename = "k")] - value: f32, - #[serde(rename = "x")] - #[serde(default)] - expression: Option, - #[serde(rename = "ix")] - #[serde(default)] - index: Option, - }, - KeyframedValue { - #[serde(rename = "k")] - keyframes: Vec, - #[serde(rename = "x")] - #[serde(default)] - expression: Option, - #[serde(rename = "ix")] - #[serde(default)] - index: Option, - }, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyframeValue { - #[serde(rename = "s")] - #[serde(default)] - pub start: Option>, - #[serde(rename = "t")] - pub time: i64, - #[serde(rename = "i")] - #[serde(default)] - pub interpolation: Option, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct Interpolation { - pub x: f32, - pub y: f32, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct OffsetInterpolation { - pub x: Vec, - pub y: Vec, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct OffsetKeyframe { - #[serde(rename = "s")] - #[serde(default)] - pub start: Option>, - #[serde(rename = "t")] - pub time: i64, - #[serde(rename = "i")] - #[serde(default)] - pub in_value: Option, - #[serde(rename = "o")] - #[serde(default)] - pub out_value: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum MultidimensionalPropertyValue { - Value { - #[serde(rename = "k")] - value: Vec, - #[serde(rename = "x")] - #[serde(default)] - expression: Option, - #[serde(rename = "ix")] - #[serde(default)] - index: Option, - }, - KeyframedValue { - #[serde(rename = "k")] - keyframes: Vec, - #[serde(rename = "x")] - #[serde(default)] - expression: Option, - #[serde(rename = "ix")] - #[serde(default)] - index: Option, - #[serde(rename = "ti")] - #[serde(default)] - in_tangent: Option, - #[serde(rename = "to")] - #[serde(default)] - out_tangent: Option, - }, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "ty")] -pub enum Shape { - #[serde(rename = "gr")] - Group { - #[serde(rename = "it")] - items: Vec, - #[serde(rename = "nm")] - name: String, - }, - #[serde(rename = "sh")] - Shape { - #[serde(rename = "ks")] - vertices: ShapeVertices, - #[serde(rename = "d")] - #[serde(default)] - direction: Option, - }, - #[serde(rename = "fl")] - Fill { - #[serde(rename = "nm")] - #[serde(default)] - name: Option, - #[serde(rename = "o")] - #[serde(default)] - opacity: Option, - #[serde(rename = "c")] - color: MultidimensionalPropertyValue, - }, - #[serde(rename = "tr")] - Transform { - #[serde(rename = "r")] - rotation: PropertyValue, - #[serde(rename = "sk")] - skew: PropertyValue, - #[serde(rename = "sa")] - skew_axis: PropertyValue, - #[serde(rename = "p")] - position: MultidimensionalPropertyValue, - #[serde(rename = "a")] - anchor_point: MultidimensionalPropertyValue, - #[serde(rename = "s")] - scale: MultidimensionalPropertyValue, - }, - #[serde(other)] - Unimplemented, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum ShapeVertices { - Shape { - #[serde(rename = "k")] - value: ShapeProperty, - #[serde(rename = "x")] - #[serde(default)] - expression: Option, - #[serde(rename = "ix")] - #[serde(default)] - index: Option, - #[serde(rename = "a")] - animated: i64, - }, - ShapeKeyframed { - #[serde(rename = "k")] - value: Vec, - #[serde(rename = "x")] - #[serde(default)] - expression: Option, - #[serde(rename = "ix")] - #[serde(default)] - index: Option, - #[serde(rename = "a")] - animated: i64, - #[serde(rename = "ti")] - #[serde(default)] - in_tangent: Vec, - #[serde(rename = "to")] - #[serde(default)] - out_tangent: Vec, - }, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShapeProperty { - #[serde(rename = "c")] - pub closed: bool, - #[serde(rename = "i")] - pub in_points: Vec<[f32; 2]>, - #[serde(rename = "o")] - pub out_points: Vec<[f32; 2]>, - #[serde(rename = "v")] - pub vertices: Vec<[f32; 2]>, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShapeKeyframeProperty { - #[serde(rename = "s")] - #[serde(default)] - pub start: Vec>, - #[serde(rename = "t")] - pub time: i64, - #[serde(rename = "i")] - #[serde(default)] - pub in_value: Option, - #[serde(rename = "o")] - #[serde(default)] - pub out_value: Option, -} - -impl Lottie { - #[inline] - pub fn from_reader(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} diff --git a/crates/pathfinder/renderer/Cargo.toml b/crates/pathfinder/renderer/Cargo.toml deleted file mode 100644 index 282794e57e..0000000000 --- a/crates/pathfinder/renderer/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "pathfinder_renderer" -version = "0.5.0" -edition = "2018" -authors = ["Patrick Walton "] -description = "A GPU-accelerated vector graphics and font renderer" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" - -[dependencies] -bitflags = "1.0" -byteorder = "1.2" -crossbeam-channel = "0.4" -once_cell = "1.3.1" -fxhash = "0.2" -half = "1.5" -hashbrown = "0.7" -rayon = "1.0" -serde = "1.0" -serde_json = "1.0" -smallvec = "1.2" -vec_map = "0.8" -instant = { version = "0.1.2", features = ["wasm-bindgen"] } - -[dependencies.log] -version = "0.4" - -[dependencies.pathfinder_color] -path = "../color" -version = "0.5" - -[dependencies.pathfinder_content] -path = "../content" -version = "0.5" - -[dependencies.pathfinder_geometry] -path = "../geometry" -version = "0.5" - -[dependencies.pathfinder_gpu] -path = "../gpu" -version = "0.5" - -[dependencies.pathfinder_resources] -path = "../resources" -version = "0.5" - -[dependencies.pathfinder_simd] -path = "../simd" -version = "0.5" - -[dependencies.pathfinder_ui] -path = "../ui" -version = "0.5" - -[dev-dependencies] -quickcheck = "0.9" diff --git a/crates/pathfinder/renderer/src/allocator.rs b/crates/pathfinder/renderer/src/allocator.rs deleted file mode 100644 index 929ef25dde..0000000000 --- a/crates/pathfinder/renderer/src/allocator.rs +++ /dev/null @@ -1,327 +0,0 @@ -// pathfinder/renderer/src/allocator.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A simple quadtree-based texture allocator. - -use crate::gpu_data::{TextureLocation, TexturePageId}; -use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; - -const ATLAS_TEXTURE_LENGTH: u32 = 1024; - -#[derive(Clone, Debug)] -pub struct TextureAllocator { - pages: Vec, -} - -#[derive(Clone, Debug)] -pub struct TexturePage { - allocator: TexturePageAllocator, - is_new: bool, -} - -#[derive(Clone, Debug)] -pub enum TexturePageAllocator { - // An atlas allocated with our quadtree allocator. - Atlas(TextureAtlasAllocator), - // A single image. - Image { size: Vector2I }, -} - -#[derive(Clone, Debug)] -pub struct TextureAtlasAllocator { - root: TreeNode, - size: u32, -} - -#[derive(Clone, Debug)] -enum TreeNode { - EmptyLeaf, - FullLeaf, - // Top left, top right, bottom left, and bottom right, in that order. - Parent([Box; 4]), -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[allow(dead_code)] -pub enum AllocationMode { - Atlas, - OwnPage, -} - -impl TextureAllocator { - #[inline] - pub fn new() -> TextureAllocator { - TextureAllocator { pages: vec![] } - } - - pub fn allocate(&mut self, requested_size: Vector2I, mode: AllocationMode) -> TextureLocation { - // If requested, or if the image is too big, use a separate page. - if mode == AllocationMode::OwnPage || - requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 || - requested_size.y() > ATLAS_TEXTURE_LENGTH as i32 { - return self.allocate_image(requested_size); - } - - // Try to add to each atlas. - for (page_index, page) in self.pages.iter_mut().enumerate() { - match page.allocator { - TexturePageAllocator::Image { .. } => {} - TexturePageAllocator::Atlas(ref mut allocator) => { - if let Some(rect) = allocator.allocate(requested_size) { - return TextureLocation { page: TexturePageId(page_index as u32), rect }; - } - } - } - } - - // Add a new atlas. - let page = TexturePageId(self.pages.len() as u32); - let mut allocator = TextureAtlasAllocator::new(); - let rect = allocator.allocate(requested_size).expect("Allocation failed!"); - self.pages.push(TexturePage { - is_new: true, - allocator: TexturePageAllocator::Atlas(allocator), - }); - TextureLocation { page, rect } - } - - pub fn allocate_image(&mut self, requested_size: Vector2I) -> TextureLocation { - let page = TexturePageId(self.pages.len() as u32); - let rect = RectI::new(Vector2I::default(), requested_size); - self.pages.push(TexturePage { - is_new: true, - allocator: TexturePageAllocator::Image { size: rect.size() }, - }); - TextureLocation { page, rect } - } - - pub fn page_size(&self, page_id: TexturePageId) -> Vector2I { - match self.pages[page_id.0 as usize].allocator { - TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32), - TexturePageAllocator::Image { size, .. } => size, - } - } - - pub fn page_scale(&self, page_id: TexturePageId) -> Vector2F { - vec2f(1.0, 1.0) / self.page_size(page_id).to_f32() - } - - pub fn page_is_new(&self, page_id: TexturePageId) -> bool { - self.pages[page_id.0 as usize].is_new - } - - pub fn mark_page_as_allocated(&mut self, page_id: TexturePageId) { - self.pages[page_id.0 as usize].is_new = false; - } - - #[inline] - pub fn page_count(&self) -> u32 { - self.pages.len() as u32 - } -} - -impl TextureAtlasAllocator { - #[inline] - fn new() -> TextureAtlasAllocator { - TextureAtlasAllocator::with_length(ATLAS_TEXTURE_LENGTH) - } - - #[inline] - fn with_length(length: u32) -> TextureAtlasAllocator { - TextureAtlasAllocator { root: TreeNode::EmptyLeaf, size: length } - } - - #[inline] - fn allocate(&mut self, requested_size: Vector2I) -> Option { - let requested_length = - (requested_size.x().max(requested_size.y()) as u32).next_power_of_two(); - self.root.allocate(Vector2I::default(), self.size, requested_length) - } - - #[inline] - #[allow(dead_code)] - fn free(&mut self, rect: RectI) { - let requested_length = rect.width() as u32; - self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length) - } - - #[inline] - #[allow(dead_code)] - fn is_empty(&self) -> bool { - match self.root { - TreeNode::EmptyLeaf => true, - _ => false, - } - } -} - -impl TreeNode { - // Invariant: `requested_size` must be a power of two. - fn allocate(&mut self, this_origin: Vector2I, this_size: u32, requested_size: u32) - -> Option { - if let TreeNode::FullLeaf = *self { - // No room here. - return None; - } - if this_size < requested_size { - // Doesn't fit. - return None; - } - - // Allocate here or split, as necessary. - if let TreeNode::EmptyLeaf = *self { - // Do we have a perfect fit? - if this_size == requested_size { - *self = TreeNode::FullLeaf; - return Some(RectI::new(this_origin, Vector2I::splat(this_size as i32))); - } - - // Split. - *self = TreeNode::Parent([ - Box::new(TreeNode::EmptyLeaf), - Box::new(TreeNode::EmptyLeaf), - Box::new(TreeNode::EmptyLeaf), - Box::new(TreeNode::EmptyLeaf), - ]); - } - - // Recurse into children. - match *self { - TreeNode::Parent(ref mut kids) => { - let kid_size = this_size / 2; - if let Some(origin) = kids[0].allocate(this_origin, kid_size, requested_size) { - return Some(origin); - } - if let Some(origin) = kids[1].allocate(this_origin + vec2i(kid_size as i32, 0), - kid_size, - requested_size) { - return Some(origin); - } - if let Some(origin) = kids[2].allocate(this_origin + vec2i(0, kid_size as i32), - kid_size, - requested_size) { - return Some(origin); - } - if let Some(origin) = kids[3].allocate(this_origin + kid_size as i32, - kid_size, - requested_size) { - return Some(origin); - } - - self.merge_if_necessary(); - return None; - } - TreeNode::EmptyLeaf | TreeNode::FullLeaf => unreachable!(), - } - } - - #[allow(dead_code)] - fn free(&mut self, - this_origin: Vector2I, - this_size: u32, - requested_origin: Vector2I, - requested_size: u32) { - if this_size <= requested_size { - if this_size == requested_size && this_origin == requested_origin { - *self = TreeNode::EmptyLeaf; - } - return; - } - - let child_size = this_size / 2; - let this_center = this_origin + child_size as i32; - - let child_index; - let mut child_origin = this_origin; - - if requested_origin.y() < this_center.y() { - if requested_origin.x() < this_center.x() { - child_index = 0; - } else { - child_index = 1; - child_origin += vec2i(child_size as i32, 0); - } - } else { - if requested_origin.x() < this_center.x() { - child_index = 2; - child_origin += vec2i(0, child_size as i32); - } else { - child_index = 3; - child_origin = this_center; - } - } - - match *self { - TreeNode::Parent(ref mut kids) => { - kids[child_index].free(child_origin, child_size, requested_origin, requested_size); - self.merge_if_necessary(); - } - TreeNode::EmptyLeaf | TreeNode::FullLeaf => unreachable!(), - } - } - - fn merge_if_necessary(&mut self) { - match *self { - TreeNode::Parent(ref mut kids) => { - if kids.iter().all(|kid| { - match **kid { - TreeNode::EmptyLeaf => true, - _ => false, - } - }) { - *self = TreeNode::EmptyLeaf; - } - } - _ => {} - } - } -} - -#[cfg(test)] -mod test { - use pathfinder_geometry::vector::vec2i; - use quickcheck; - use std::u32; - - use super::TextureAtlasAllocator; - - #[test] - fn test_allocation_and_freeing() { - quickcheck::quickcheck(prop_allocation_and_freeing_work as - fn(u32, Vec<(u32, u32)>) -> bool); - - fn prop_allocation_and_freeing_work(mut length: u32, mut sizes: Vec<(u32, u32)>) -> bool { - length = u32::next_power_of_two(length).max(1); - - for &mut (ref mut width, ref mut height) in &mut sizes { - *width = (*width).min(length).max(1); - *height = (*height).min(length).max(1); - } - - let mut allocator = TextureAtlasAllocator::with_length(length); - let mut locations = vec![]; - for &(width, height) in &sizes { - let size = vec2i(width as i32, height as i32); - if let Some(location) = allocator.allocate(size) { - locations.push(location); - } - } - - for location in locations { - allocator.free(location); - } - - assert!(allocator.is_empty()); - - true - } - } -} diff --git a/crates/pathfinder/renderer/src/builder.rs b/crates/pathfinder/renderer/src/builder.rs deleted file mode 100644 index 61e98d6454..0000000000 --- a/crates/pathfinder/renderer/src/builder.rs +++ /dev/null @@ -1,884 +0,0 @@ -// pathfinder/renderer/src/builder.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Packs data onto the GPU. - -use crate::concurrent::executor::Executor; -use crate::gpu::renderer::{BlendModeExt, MASK_TILES_ACROSS, MASK_TILES_DOWN}; -use crate::gpu_data::{AlphaTileId, Clip, ClipBatch, ClipBatchKey, ClipBatchKind, Fill}; -use crate::gpu_data::{FillBatchEntry, RenderCommand, TILE_CTRL_MASK_0_SHIFT}; -use crate::gpu_data::{TILE_CTRL_MASK_EVEN_ODD, TILE_CTRL_MASK_WINDING, Tile, TileBatch}; -use crate::gpu_data::{TileBatchTexture, TileObjectPrimitive}; -use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener}; -use crate::paint::{PaintInfo, PaintMetadata}; -use crate::scene::{DisplayItem, Scene}; -use crate::tile_map::DenseTileMap; -use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH}; -use crate::tiles::{Tiler, TilingPathInfo}; -use crate::z_buffer::{DepthMetadata, ZBuffer}; -use pathfinder_content::effects::{BlendMode, Filter}; -use pathfinder_content::fill::FillRule; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_geometry::alignment::{AlignedI16, AlignedU8, AlignedU16, AlignedI8}; -use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8}; -use pathfinder_geometry::rect::{RectF, RectI}; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; -use pathfinder_gpu::TextureSamplingFlags; -use pathfinder_simd::default::{F32x4, I32x4}; -use std::sync::atomic::AtomicUsize; -use instant::Instant; -use std::u32; - -pub(crate) const ALPHA_TILE_LEVEL_COUNT: usize = 2; -pub(crate) const ALPHA_TILES_PER_LEVEL: usize = 1 << (32 - ALPHA_TILE_LEVEL_COUNT + 1); - -pub(crate) struct SceneBuilder<'a, 'b> { - scene: &'a mut Scene, - built_options: &'b PreparedBuildOptions, - next_alpha_tile_indices: [AtomicUsize; ALPHA_TILE_LEVEL_COUNT], - pub(crate) listener: Box, -} - -#[derive(Debug)] -pub(crate) struct ObjectBuilder { - pub built_path: BuiltPath, - pub fills: Vec, - pub bounds: RectF, -} - -#[derive(Debug)] -struct BuiltDrawPath { - path: BuiltPath, - blend_mode: BlendMode, - filter: Filter, - color_texture: Option, - sampling_flags_1: TextureSamplingFlags, - mask_0_fill_rule: FillRule, -} - -#[derive(Debug)] -pub(crate) struct BuiltPath { - pub solid_tiles: SolidTiles, - pub empty_tiles: Vec, - pub single_mask_tiles: Vec, - pub clip_tiles: Vec, - pub tiles: DenseTileMap, - pub fill_rule: FillRule, -} - -#[derive(Clone, Debug)] -pub struct BuiltTile { - pub page: u16, - pub tile: Tile, -} - -#[derive(Clone, Copy, Debug)] -pub struct BuiltClip { - pub clip: Clip, - pub key: ClipBatchKey, -} - -#[derive(Clone, Debug)] -pub(crate) enum SolidTiles { - Occluders(Vec), - Regular(Vec), -} - -#[derive(Clone, Copy, Debug)] -pub(crate) struct Occluder { - pub(crate) coords: Vector2I, -} - -impl<'a, 'b> SceneBuilder<'a, 'b> { - pub(crate) fn new( - scene: &'a mut Scene, - built_options: &'b PreparedBuildOptions, - listener: Box, - ) -> SceneBuilder<'a, 'b> { - SceneBuilder { - scene, - built_options, - next_alpha_tile_indices: [AtomicUsize::new(0), AtomicUsize::new(0)], - listener, - } - } - - pub fn build(&mut self, executor: &E) where E: Executor { - let start_time = Instant::now(); - - // Send the start rendering command. - let bounding_quad = self.built_options.bounding_quad(); - - let clip_path_count = self.scene.clip_paths.len(); - let draw_path_count = self.scene.paths.len(); - let total_path_count = clip_path_count + draw_path_count; - - let needs_readable_framebuffer = self.needs_readable_framebuffer(); - - self.listener.send(RenderCommand::Start { - bounding_quad, - path_count: total_path_count, - needs_readable_framebuffer, - }); - - let render_transform = match self.built_options.transform { - PreparedRenderTransform::Transform2D(transform) => transform.inverse(), - _ => Transform2F::default() - }; - - // Build paint data. - let PaintInfo { - render_commands, - paint_metadata, - render_target_metadata: _, - } = self.scene.build_paint_info(render_transform); - for render_command in render_commands { - self.listener.send(render_command); - } - - let effective_view_box = self.scene.effective_view_box(self.built_options); - - let built_clip_paths = executor.build_vector(clip_path_count, |path_index| { - self.build_clip_path(PathBuildParams { - path_index, - view_box: effective_view_box, - built_options: &self.built_options, - scene: &self.scene, - }) - }); - - let built_draw_paths = executor.build_vector(draw_path_count, |path_index| { - self.build_draw_path(DrawPathBuildParams { - path_build_params: PathBuildParams { - path_index, - view_box: effective_view_box, - built_options: &self.built_options, - scene: &self.scene, - }, - paint_metadata: &paint_metadata, - built_clip_paths: &built_clip_paths, - }) - }); - - self.finish_building(&paint_metadata, built_draw_paths); - - let cpu_build_time = Instant::now() - start_time; - self.listener.send(RenderCommand::Finish { cpu_build_time }); - } - - fn build_clip_path(&self, params: PathBuildParams) -> BuiltPath { - let PathBuildParams { path_index, view_box, built_options, scene } = params; - let path_object = &scene.clip_paths[path_index]; - let outline = scene.apply_render_options(path_object.outline(), built_options); - - let mut tiler = Tiler::new(self, - &outline, - path_object.fill_rule(), - view_box, - TilingPathInfo::Clip); - - tiler.generate_tiles(); - self.send_fills(tiler.object_builder.fills); - tiler.object_builder.built_path - } - - fn build_draw_path(&self, params: DrawPathBuildParams) -> BuiltDrawPath { - let DrawPathBuildParams { - path_build_params: PathBuildParams { path_index, view_box, built_options, scene }, - paint_metadata, - built_clip_paths, - } = params; - - let path_object = &scene.paths[path_index]; - let outline = scene.apply_render_options(path_object.outline(), built_options); - - let paint_id = path_object.paint(); - let paint_metadata = &paint_metadata[paint_id.0 as usize]; - let built_clip_path = path_object.clip_path().map(|clip_path_id| { - &built_clip_paths[clip_path_id.0 as usize] - }); - - let mut tiler = Tiler::new(self, - &outline, - path_object.fill_rule(), - view_box, - TilingPathInfo::Draw(DrawTilingPathInfo { - paint_id, - paint_metadata, - blend_mode: path_object.blend_mode(), - built_clip_path, - fill_rule: path_object.fill_rule(), - })); - - tiler.generate_tiles(); - self.send_fills(tiler.object_builder.fills); - BuiltDrawPath { - path: tiler.object_builder.built_path, - blend_mode: path_object.blend_mode(), - filter: paint_metadata.filter(), - color_texture: paint_metadata.tile_batch_texture(), - sampling_flags_1: TextureSamplingFlags::empty(), - mask_0_fill_rule: path_object.fill_rule(), - } - } - - fn send_fills(&self, fills: Vec) { - if !fills.is_empty() { - self.listener.send(RenderCommand::AddFills(fills)); - } - } - - fn build_clips(&self, built_draw_paths: &[BuiltDrawPath]) { - let mut built_clip_tiles = vec![]; - for built_draw_path in built_draw_paths { - for built_clip_tile in &built_draw_path.path.clip_tiles { - built_clip_tiles.push(*built_clip_tile); - } - } - - built_clip_tiles.sort_by_key(|built_clip_tile| built_clip_tile.key); - - let mut batches: Vec = vec![]; - for built_clip_tile in built_clip_tiles { - if batches.is_empty() || batches.last_mut().unwrap().key != built_clip_tile.key { - batches.push(ClipBatch { key: built_clip_tile.key, clips: vec![] }); - } - batches.last_mut().unwrap().clips.push(built_clip_tile.clip); - } - - if !batches.is_empty() { - self.listener.send(RenderCommand::ClipTiles(batches)); - } - } - - fn cull_tiles(&self, paint_metadata: &[PaintMetadata], built_draw_paths: Vec) - -> CulledTiles { - let mut culled_tiles = CulledTiles { display_list: vec![] }; - - let mut remaining_layer_z_buffers = self.build_solid_tiles(&built_draw_paths); - remaining_layer_z_buffers.reverse(); - - // Process first Z-buffer. - let first_z_buffer = remaining_layer_z_buffers.pop().unwrap(); - let first_solid_tiles = first_z_buffer.build_solid_tiles(paint_metadata); - for batch in first_solid_tiles.batches { - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch)); - } - - let mut layer_z_buffers_stack = vec![first_z_buffer]; - let mut current_depth = 1; - - for display_item in &self.scene.display_list { - match *display_item { - DisplayItem::PushRenderTarget(render_target_id) => { - culled_tiles.display_list - .push(CulledDisplayItem::PushRenderTarget(render_target_id)); - - let z_buffer = remaining_layer_z_buffers.pop().unwrap(); - let solid_tiles = z_buffer.build_solid_tiles(paint_metadata); - for batch in solid_tiles.batches { - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch)); - } - layer_z_buffers_stack.push(z_buffer); - } - - DisplayItem::PopRenderTarget => { - culled_tiles.display_list.push(CulledDisplayItem::PopRenderTarget); - layer_z_buffers_stack.pop(); - } - - DisplayItem::DrawPaths { - start_index: start_draw_path_index, - end_index: end_draw_path_index, - } => { - for draw_path_index in start_draw_path_index..end_draw_path_index { - let built_draw_path = &built_draw_paths[draw_path_index as usize]; - let layer_z_buffer = layer_z_buffers_stack.last().unwrap(); - let color_texture = built_draw_path.color_texture; - - debug_assert!(built_draw_path.path.empty_tiles.is_empty() || - built_draw_path.blend_mode.is_destructive()); - self.add_alpha_tiles(&mut culled_tiles, - layer_z_buffer, - &built_draw_path.path.empty_tiles, - current_depth, - None, - built_draw_path.blend_mode, - built_draw_path.filter); - - self.add_alpha_tiles(&mut culled_tiles, - layer_z_buffer, - &built_draw_path.path.single_mask_tiles, - current_depth, - color_texture, - built_draw_path.blend_mode, - built_draw_path.filter); - - match built_draw_path.path.solid_tiles { - SolidTiles::Regular(ref tiles) => { - self.add_alpha_tiles(&mut culled_tiles, - layer_z_buffer, - tiles, - current_depth, - color_texture, - built_draw_path.blend_mode, - built_draw_path.filter); - } - SolidTiles::Occluders(_) => {} - } - - current_depth += 1; - } - } - } - } - - culled_tiles - } - - fn build_solid_tiles(&self, built_draw_paths: &[BuiltDrawPath]) -> Vec { - let effective_view_box = self.scene.effective_view_box(self.built_options); - let mut z_buffers = vec![ZBuffer::new(effective_view_box)]; - let mut z_buffer_index_stack = vec![0]; - let mut current_depth = 1; - - // Create Z-buffers. - for display_item in &self.scene.display_list { - match *display_item { - DisplayItem::PushRenderTarget { .. } => { - z_buffer_index_stack.push(z_buffers.len()); - z_buffers.push(ZBuffer::new(effective_view_box)); - } - DisplayItem::PopRenderTarget => { - z_buffer_index_stack.pop(); - } - DisplayItem::DrawPaths { start_index, end_index } => { - let (start_index, end_index) = (start_index as usize, end_index as usize); - let z_buffer = &mut z_buffers[*z_buffer_index_stack.last().unwrap()]; - for (path_subindex, built_draw_path) in - built_draw_paths[start_index..end_index].iter().enumerate() { - let path_index = (path_subindex + start_index) as u32; - let path = &self.scene.paths[path_index as usize]; - let metadata = DepthMetadata { paint_id: path.paint() }; - match built_draw_path.path.solid_tiles { - SolidTiles::Regular(_) => { - z_buffer.update(&[], current_depth, metadata); - } - SolidTiles::Occluders(ref occluders) => { - z_buffer.update(occluders, current_depth, metadata); - } - } - current_depth += 1; - } - } - } - } - debug_assert_eq!(z_buffer_index_stack.len(), 1); - - z_buffers - } - - fn add_alpha_tiles(&self, - culled_tiles: &mut CulledTiles, - layer_z_buffer: &ZBuffer, - built_alpha_tiles: &[BuiltTile], - current_depth: u32, - color_texture: Option, - blend_mode: BlendMode, - filter: Filter) { - let mut batch_indices: Vec = vec![]; - for built_alpha_tile in built_alpha_tiles { - // Early cull if possible. - let alpha_tile_coords = built_alpha_tile.tile.tile_position(); - if !layer_z_buffer.test(alpha_tile_coords, current_depth) { - continue; - } - - // Find an appropriate batch if we can. - let mut dest_batch_index = batch_indices.iter().filter(|&batch_index| { - batch_index.tile_page == built_alpha_tile.page - }).next().cloned(); - - // If no batch was found, try to reuse the last batch in the display list. - // - // TODO(pcwalton): We could try harder to find a batch by taking tile positions into - // account... - if dest_batch_index.is_none() { - match culled_tiles.display_list.last() { - Some(&CulledDisplayItem::DrawTiles(TileBatch { - tiles: _, - color_texture: ref batch_color_texture, - blend_mode: batch_blend_mode, - filter: batch_filter, - tile_page: batch_tile_page - })) if *batch_color_texture == color_texture && - batch_blend_mode == blend_mode && - batch_filter == filter && - !batch_blend_mode.needs_readable_framebuffer() && - batch_tile_page == built_alpha_tile.page => { - dest_batch_index = Some(BatchIndex { - display_item_index: culled_tiles.display_list.len() - 1, - tile_page: batch_tile_page, - }); - batch_indices.push(dest_batch_index.unwrap()); - } - _ => {} - } - } - - // If it's still the case that no suitable batch was found, then make a new one. - if dest_batch_index.is_none() { - dest_batch_index = Some(BatchIndex { - display_item_index: culled_tiles.display_list.len(), - tile_page: built_alpha_tile.page, - }); - batch_indices.push(dest_batch_index.unwrap()); - culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(TileBatch { - tiles: vec![], - color_texture, - blend_mode, - filter, - tile_page: built_alpha_tile.page, - })); - } - - // Add to the appropriate batch. - match culled_tiles.display_list[dest_batch_index.unwrap().display_item_index] { - CulledDisplayItem::DrawTiles(ref mut tiles) => { - tiles.tiles.push(built_alpha_tile.tile); - } - _ => unreachable!(), - } - } - - #[derive(Clone, Copy)] - struct BatchIndex { - display_item_index: usize, - tile_page: u16, - } - } - - fn pack_tiles(&mut self, culled_tiles: CulledTiles) { - self.listener.send(RenderCommand::BeginTileDrawing); - for display_item in culled_tiles.display_list { - match display_item { - CulledDisplayItem::DrawTiles(batch) => { - self.listener.send(RenderCommand::DrawTiles(batch)) - } - CulledDisplayItem::PushRenderTarget(render_target_id) => { - self.listener.send(RenderCommand::PushRenderTarget(render_target_id)) - } - CulledDisplayItem::PopRenderTarget => { - self.listener.send(RenderCommand::PopRenderTarget) - } - } - } - } - - fn finish_building(&mut self, - paint_metadata: &[PaintMetadata], - built_draw_paths: Vec) { - self.listener.send(RenderCommand::FlushFills); - self.build_clips(&built_draw_paths); - let culled_tiles = self.cull_tiles(paint_metadata, built_draw_paths); - self.pack_tiles(culled_tiles); - } - - fn needs_readable_framebuffer(&self) -> bool { - let mut framebuffer_nesting = 0; - for display_item in &self.scene.display_list { - match *display_item { - DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1, - DisplayItem::PopRenderTarget => framebuffer_nesting -= 1, - DisplayItem::DrawPaths { start_index, end_index } => { - if framebuffer_nesting > 0 { - continue; - } - for path_index in start_index..end_index { - let blend_mode = self.scene.paths[path_index as usize].blend_mode(); - if blend_mode.needs_readable_framebuffer() { - return true; - } - } - } - } - } - false - } -} - -struct PathBuildParams<'a> { - path_index: usize, - view_box: RectF, - built_options: &'a PreparedBuildOptions, - scene: &'a Scene, -} - -struct DrawPathBuildParams<'a> { - path_build_params: PathBuildParams<'a>, - paint_metadata: &'a [PaintMetadata], - built_clip_paths: &'a [BuiltPath], -} - -impl BuiltPath { - fn new(path_bounds: RectF, - view_box_bounds: RectF, - fill_rule: FillRule, - tiling_path_info: &TilingPathInfo) - -> BuiltPath { - let occludes = match *tiling_path_info { - TilingPathInfo::Draw(ref draw_tiling_path_info) => { - draw_tiling_path_info.paint_metadata.is_opaque && - draw_tiling_path_info.blend_mode.occludes_backdrop() - } - TilingPathInfo::Clip => true, - }; - - let tile_map_bounds = if tiling_path_info.has_destructive_blend_mode() { - view_box_bounds - } else { - path_bounds - }; - - BuiltPath { - empty_tiles: vec![], - single_mask_tiles: vec![], - clip_tiles: vec![], - solid_tiles: if occludes { - SolidTiles::Occluders(vec![]) - } else { - SolidTiles::Regular(vec![]) - }, - tiles: DenseTileMap::new(tiles::round_rect_out_to_tile_bounds(tile_map_bounds)), - fill_rule, - } - } -} - -impl Occluder { - #[inline] - pub(crate) fn new(coords: Vector2I) -> Occluder { - Occluder { coords } - } -} - -struct CulledTiles { - display_list: Vec, -} - -enum CulledDisplayItem { - DrawTiles(TileBatch), - PushRenderTarget(RenderTargetId), - PopRenderTarget, -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct TileStats { - pub solid_tile_count: u32, - pub alpha_tile_count: u32, -} - -// Utilities for built objects - -impl ObjectBuilder { - pub(crate) fn new(path_bounds: RectF, - view_box_bounds: RectF, - fill_rule: FillRule, - tiling_path_info: &TilingPathInfo) - -> ObjectBuilder { - ObjectBuilder { - built_path: BuiltPath::new(path_bounds, view_box_bounds, fill_rule, tiling_path_info), - bounds: path_bounds, - fills: vec![], - } - } - - #[inline] - pub(crate) fn tile_rect(&self) -> RectI { - self.built_path.tiles.rect - } - - fn add_fill( - &mut self, - scene_builder: &SceneBuilder, - segment: LineSegment2F, - tile_coords: Vector2I, - ) { - debug!("add_fill({:?} ({:?}))", segment, tile_coords); - - // Ensure this fill is in bounds. If not, cull it. - if self.tile_coords_to_local_index(tile_coords).is_none() { - return; - } - - debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT); - - // Compute the upper left corner of the tile. - let tile_size = F32x4::splat(TILE_WIDTH as f32); - let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size; - - // Convert to 4.8 fixed point. - let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0); - let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32)); - let segment = segment.clamp(min, max).to_i32x4(); - let (from_x, from_y, to_x, to_y) = (segment[0], segment[1], segment[2], segment[3]); - - // Cull degenerate fills. - if from_x == to_x { - debug!("... culling!"); - return; - } - - // Allocate a global tile if necessary. - let alpha_tile_id = self.get_or_allocate_alpha_tile_index(scene_builder, tile_coords); - - // Pack whole pixels. - let px = (segment & I32x4::splat(0xf00)).to_u32x4(); - let px = (px >> 8).to_i32x4() | (px >> 4).to_i32x4().yxwz(); - - // Pack instance data. - debug!("... OK, pushing"); - self.fills.push(FillBatchEntry { - page: alpha_tile_id.page(), - fill: Fill { - px: LineSegmentU4 { from: px[0] as AlignedU8, to: px[2] as AlignedU8 }, - subpx: LineSegmentU8 { - from_x: from_x as u8, - from_y: from_y as u8, - to_x: to_x as u8, - to_y: to_y as u8, - }, - alpha_tile_index: alpha_tile_id.tile() as AlignedU16, - }, - }); - } - - fn get_or_allocate_alpha_tile_index( - &mut self, - scene_builder: &SceneBuilder, - tile_coords: Vector2I, - ) -> AlphaTileId { - let local_tile_index = self.built_path.tiles.coords_to_index_unchecked(tile_coords); - let alpha_tile_id = self.built_path.tiles.data[local_tile_index].alpha_tile_id; - if alpha_tile_id.is_valid() { - return alpha_tile_id; - } - - let alpha_tile_id = AlphaTileId::new(&scene_builder.next_alpha_tile_indices, 0); - self.built_path.tiles.data[local_tile_index].alpha_tile_id = alpha_tile_id; - alpha_tile_id - } - - pub(crate) fn add_active_fill( - &mut self, - scene_builder: &SceneBuilder, - left: f32, - right: f32, - mut winding: i32, - tile_coords: Vector2I, - ) { - let tile_origin_y = (tile_coords.y() * TILE_HEIGHT as i32) as f32; - let left = vec2f(left, tile_origin_y); - let right = vec2f(right, tile_origin_y); - - let segment = if winding < 0 { - LineSegment2F::new(left, right) - } else { - LineSegment2F::new(right, left) - }; - - debug!( - "... emitting active fill {} -> {} winding {} @ tile {:?}", - left.x(), - right.x(), - winding, - tile_coords - ); - - while winding != 0 { - self.add_fill(scene_builder, segment, tile_coords); - if winding < 0 { - winding += 1 - } else { - winding -= 1 - } - } - } - - pub(crate) fn generate_fill_primitives_for_line( - &mut self, - scene_builder: &SceneBuilder, - mut segment: LineSegment2F, - tile_y: i32, - ) { - debug!( - "... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})", - segment, - tile_y, - tile_y as f32 * TILE_HEIGHT as f32, - (tile_y + 1) as f32 * TILE_HEIGHT as f32 - ); - - let winding = segment.from_x() > segment.to_x(); - let (segment_left, segment_right) = if !winding { - (segment.from_x(), segment.to_x()) - } else { - (segment.to_x(), segment.from_x()) - }; - - let mut subsegment_x = (segment_left as i32 & !(TILE_WIDTH as i32 - 1)) as f32; - while subsegment_x < segment_right { - let (mut fill_from, mut fill_to) = (segment.from(), segment.to()); - let subsegment_x_next = subsegment_x + TILE_WIDTH as f32; - if subsegment_x_next < segment_right { - let x = subsegment_x_next; - let point = Vector2F::new(x, segment.solve_y_for_x(x)); - if !winding { - fill_to = point; - segment = LineSegment2F::new(point, segment.to()); - } else { - fill_from = point; - segment = LineSegment2F::new(segment.from(), point); - } - } - - let fill_segment = LineSegment2F::new(fill_from, fill_to); - let fill_tile_coords = vec2i(subsegment_x as i32 / TILE_WIDTH as i32, tile_y); - self.add_fill(scene_builder, fill_segment, fill_tile_coords); - - subsegment_x = subsegment_x_next; - } - } - - #[inline] - pub(crate) fn tile_coords_to_local_index(&self, coords: Vector2I) -> Option { - self.built_path.tiles.coords_to_index(coords).map(|index| index as u32) - } - - #[inline] - pub(crate) fn local_tile_index_to_coords(&self, tile_index: u32) -> Vector2I { - self.built_path.tiles.index_to_coords(tile_index as usize) - } -} - -impl<'a> PackedTile<'a> { - pub(crate) fn add_to(&self, - tiles: &mut Vec, - clips: &mut Vec, - draw_tiling_path_info: &DrawTilingPathInfo, - scene_builder: &SceneBuilder) { - let draw_tile_page = self.draw_tile.alpha_tile_id.page() as u16; - let draw_tile_index = self.draw_tile.alpha_tile_id.tile() as u16; - let draw_tile_backdrop = self.draw_tile.backdrop as i8; - - match self.clip_tile { - None => { - tiles.push(BuiltTile { - page: draw_tile_page, - tile: Tile::new_alpha(self.tile_coords, - draw_tile_index, - draw_tile_backdrop, - draw_tiling_path_info), - }); - } - Some(clip_tile) => { - let clip_tile_page = clip_tile.alpha_tile_id.page() as u16; - let clip_tile_index = clip_tile.alpha_tile_id.tile() as u16; - let clip_tile_backdrop = clip_tile.backdrop; - - let dest_tile_id = AlphaTileId::new(&scene_builder.next_alpha_tile_indices, 1); - let dest_tile_page = dest_tile_id.page() as u16; - let dest_tile_index = dest_tile_id.tile() as u16; - - clips.push(BuiltClip { - clip: Clip::new(dest_tile_index, draw_tile_index, draw_tile_backdrop), - key: ClipBatchKey { - src_page: draw_tile_page, - dest_page: dest_tile_page, - kind: ClipBatchKind::Draw, - }, - }); - clips.push(BuiltClip { - clip: Clip::new(dest_tile_index, clip_tile_index, clip_tile_backdrop), - key: ClipBatchKey { - src_page: clip_tile_page, - dest_page: dest_tile_page, - kind: ClipBatchKind::Clip, - }, - }); - tiles.push(BuiltTile { - page: dest_tile_page, - tile: Tile::new_alpha(self.tile_coords, - dest_tile_index, - 0, - draw_tiling_path_info), - }); - } - } - } -} - -impl Tile { - #[inline] - fn new_alpha(tile_origin: Vector2I, - draw_tile_index: u16, - draw_tile_backdrop: i8, - draw_tiling_path_info: &DrawTilingPathInfo) - -> Tile { - let mask_0_uv = calculate_mask_uv(draw_tile_index); - - let mut ctrl = 0; - match draw_tiling_path_info.fill_rule { - FillRule::EvenOdd => ctrl |= TILE_CTRL_MASK_EVEN_ODD << TILE_CTRL_MASK_0_SHIFT, - FillRule::Winding => ctrl |= TILE_CTRL_MASK_WINDING << TILE_CTRL_MASK_0_SHIFT, - } - - Tile { - tile_x: tile_origin.x() as AlignedI16, - tile_y: tile_origin.y() as AlignedI16, - mask_0_u: mask_0_uv.x() as AlignedU8, - mask_0_v: mask_0_uv.y() as AlignedU8, - mask_0_backdrop: draw_tile_backdrop as AlignedI8, - ctrl: ctrl as AlignedU16, - pad: 0, - color: draw_tiling_path_info.paint_id.0 as AlignedU16, - } - } - - #[inline] - pub fn tile_position(&self) -> Vector2I { - vec2i(self.tile_x as i32, self.tile_y as i32) - } -} - -impl Clip { - #[inline] - fn new(dest_tile_index: u16, src_tile_index: u16, src_backdrop: i8) -> Clip { - let dest_uv = calculate_mask_uv(dest_tile_index); - let src_uv = calculate_mask_uv(src_tile_index); - Clip { - dest_u: dest_uv.x() as AlignedU8, - dest_v: dest_uv.y() as AlignedU8, - src_u: src_uv.x() as AlignedU8, - src_v: src_uv.y() as AlignedU8, - backdrop: src_backdrop as AlignedI8, - pad_0: 0, - pad_1: 0, - } - } -} - -fn calculate_mask_uv(tile_index: u16) -> Vector2I { - debug_assert_eq!(MASK_TILES_ACROSS, MASK_TILES_DOWN); - let mask_u = tile_index as i32 % MASK_TILES_ACROSS as i32; - let mask_v = tile_index as i32 / MASK_TILES_ACROSS as i32; - vec2i(mask_u, mask_v) -} diff --git a/crates/pathfinder/renderer/src/concurrent/executor.rs b/crates/pathfinder/renderer/src/concurrent/executor.rs deleted file mode 100644 index 150f4d32d2..0000000000 --- a/crates/pathfinder/renderer/src/concurrent/executor.rs +++ /dev/null @@ -1,31 +0,0 @@ -// pathfinder/renderer/src/concurrent/executor.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An abstraction over threading and parallelism systems such as Rayon. - -/// An abstraction over threading and parallelism systems such as Rayon. -pub trait Executor { - /// Like the Rayon snippet: - /// - /// ```norun - /// (0..length).into_par_iter().map(builder).collect() - /// ``` - fn build_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> T + Send + Sync; -} - -pub struct SequentialExecutor; - -impl Executor for SequentialExecutor { - fn build_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> T + Send + Sync { - (0..length).into_iter().map(builder).collect() - } -} diff --git a/crates/pathfinder/renderer/src/concurrent/mod.rs b/crates/pathfinder/renderer/src/concurrent/mod.rs deleted file mode 100644 index 01ceffe393..0000000000 --- a/crates/pathfinder/renderer/src/concurrent/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// pathfinder/renderer/src/concurrent/mod.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Threading and concurrency support. - -pub mod executor; -pub mod rayon; -pub mod scene_proxy; diff --git a/crates/pathfinder/renderer/src/concurrent/rayon.rs b/crates/pathfinder/renderer/src/concurrent/rayon.rs deleted file mode 100644 index e11d28b1d2..0000000000 --- a/crates/pathfinder/renderer/src/concurrent/rayon.rs +++ /dev/null @@ -1,23 +0,0 @@ -// pathfinder/renderer/src/concurrent/rayon.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An implementation of the executor using the Rayon library. - -use crate::concurrent::executor::Executor; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; - -pub struct RayonExecutor; - -impl Executor for RayonExecutor { - fn build_vector(&self, length: usize, builder: F) -> Vec - where T: Send, F: Fn(usize) -> T + Send + Sync { - (0..length).into_par_iter().map(builder).collect() - } -} diff --git a/crates/pathfinder/renderer/src/concurrent/scene_proxy.rs b/crates/pathfinder/renderer/src/concurrent/scene_proxy.rs deleted file mode 100644 index 69cdf131a8..0000000000 --- a/crates/pathfinder/renderer/src/concurrent/scene_proxy.rs +++ /dev/null @@ -1,151 +0,0 @@ -// pathfinder/renderer/src/concurrent/scene_proxy.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A version of `Scene` that proxies all method calls out to a separate -//! thread. -//! -//! This is useful for: -//! -//! * Avoiding GPU driver stalls on synchronous APIs such as OpenGL. -//! -//! * Avoiding UI latency by building scenes off the main thread. -//! -//! You don't need to use this API to use Pathfinder; it's only a convenience. - -use crate::concurrent::executor::Executor; -use crate::gpu::renderer::Renderer; -use crate::gpu_data::RenderCommand; -use crate::options::{BuildOptions, RenderCommandListener}; -use crate::scene::Scene; -use crossbeam_channel::{self, Receiver, Sender}; -use pathfinder_geometry::rect::RectF; -use pathfinder_gpu::Device; -use std::thread; - -const MAX_MESSAGES_IN_FLIGHT: usize = 1024; - -pub struct SceneProxy { - sender: Sender, -} - -impl SceneProxy { - pub fn new(executor: E) -> SceneProxy where E: Executor + Send + 'static { - SceneProxy::from_scene(Scene::new(), executor) - } - - pub fn from_scene(scene: Scene, executor: E) -> SceneProxy - where E: Executor + Send + 'static { - let (main_to_worker_sender, main_to_worker_receiver) = - crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); - thread::spawn(move || scene_thread(scene, executor, main_to_worker_receiver)); - SceneProxy { sender: main_to_worker_sender } - } - - #[inline] - pub fn replace_scene(&self, new_scene: Scene) { - self.sender.send(MainToWorkerMsg::ReplaceScene(new_scene)).unwrap(); - } - - #[inline] - pub fn set_view_box(&self, new_view_box: RectF) { - self.sender.send(MainToWorkerMsg::SetViewBox(new_view_box)).unwrap(); - } - - #[inline] - pub fn build_with_listener(&self, - options: BuildOptions, - listener: Box) { - self.sender.send(MainToWorkerMsg::Build(options, listener)).unwrap(); - } - - #[inline] - pub fn build_with_stream(&self, options: BuildOptions) -> RenderCommandStream { - let (sender, receiver) = crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); - let listener = Box::new(move |command| drop(sender.send(command))); - self.build_with_listener(options, listener); - RenderCommandStream::new(receiver) - } - - /// A convenience method to build a scene and send the resulting commands - /// to the given renderer. - /// - /// Exactly equivalent to: - /// - /// ```norun - /// for command in scene_proxy.build_with_stream(options) { - /// renderer.render_command(&command) - /// } - /// ``` - #[inline] - pub fn build_and_render(&self, renderer: &mut Renderer, build_options: BuildOptions) - where D: Device { - renderer.begin_scene(); - for command in self.build_with_stream(build_options) { - renderer.render_command(&command); - } - renderer.end_scene(); - } - - #[inline] - pub fn copy_scene(&self) -> Scene { - let (sender, receiver) = crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); - self.sender.send(MainToWorkerMsg::CopyScene(sender)).unwrap(); - receiver.recv().unwrap() - } -} - -fn scene_thread(mut scene: Scene, - executor: E, - main_to_worker_receiver: Receiver) - where E: Executor { - while let Ok(msg) = main_to_worker_receiver.recv() { - match msg { - MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene, - MainToWorkerMsg::CopyScene(sender) => sender.send(scene.clone()).unwrap(), - MainToWorkerMsg::SetViewBox(new_view_box) => scene.set_view_box(new_view_box), - MainToWorkerMsg::Build(options, listener) => scene.build(options, listener, &executor) - } - } -} - -enum MainToWorkerMsg { - ReplaceScene(Scene), - CopyScene(Sender), - SetViewBox(RectF), - Build(BuildOptions, Box), -} - -pub struct RenderCommandStream { - receiver: Receiver, - done: bool, -} - -impl RenderCommandStream { - fn new(receiver: Receiver) -> RenderCommandStream { - RenderCommandStream { receiver, done: false } - } -} - -impl Iterator for RenderCommandStream { - type Item = RenderCommand; - - #[inline] - fn next(&mut self) -> Option { - if self.done { - None - } else { - let command = self.receiver.recv().unwrap(); - if let RenderCommand::Finish { .. } = command { - self.done = true; - } - Some(command) - } - } -} diff --git a/crates/pathfinder/renderer/src/gpu/debug.rs b/crates/pathfinder/renderer/src/gpu/debug.rs deleted file mode 100644 index 1f85035a61..0000000000 --- a/crates/pathfinder/renderer/src/gpu/debug.rs +++ /dev/null @@ -1,217 +0,0 @@ -// pathfinder/renderer/src/gpu/debug.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A debug overlay. -//! -//! We don't render the demo UI text using Pathfinder itself so that we can use the debug UI to -//! debug Pathfinder if it's totally busted. -//! -//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/ - -use crate::gpu::renderer::{RenderStats, RenderTime}; -use pathfinder_geometry::vector::{Vector2I, vec2i}; -use pathfinder_geometry::rect::RectI; -use pathfinder_gpu::Device; -use pathfinder_resources::ResourceLoader; -use pathfinder_ui::{FONT_ASCENT, LINE_HEIGHT, PADDING, UIPresenter, WINDOW_COLOR}; -use std::collections::VecDeque; -use std::ops::{Add, Div}; -use std::time::Duration; - -const SAMPLE_BUFFER_SIZE: usize = 60; - -const STATS_WINDOW_WIDTH: i32 = 325; -const STATS_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 4 + PADDING + 2; - -const PERFORMANCE_WINDOW_WIDTH: i32 = 400; -const PERFORMANCE_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 4 + PADDING + 2; - -pub struct DebugUIPresenter -where - D: Device, -{ - pub ui_presenter: UIPresenter, - cpu_samples: SampleBuffer, - gpu_samples: SampleBuffer, -} - -impl DebugUIPresenter -where - D: Device, -{ - pub fn new( - device: &D, - resources: &dyn ResourceLoader, - framebuffer_size: Vector2I, - ) -> DebugUIPresenter { - let ui_presenter = UIPresenter::new(device, resources, framebuffer_size); - DebugUIPresenter { - ui_presenter, - cpu_samples: SampleBuffer::new(), - gpu_samples: SampleBuffer::new(), - } - } - - pub fn add_sample(&mut self, stats: RenderStats, rendering_time: RenderTime) { - self.cpu_samples.push(stats); - self.gpu_samples.push(rendering_time); - } - - pub fn draw(&self, device: &D) { - self.draw_stats_window(device); - self.draw_performance_window(device); - } - - fn draw_stats_window(&self, device: &D) { - let framebuffer_size = self.ui_presenter.framebuffer_size(); - let bottom = framebuffer_size.y() - PADDING; - let window_rect = RectI::new( - vec2i(framebuffer_size.x() - PADDING - STATS_WINDOW_WIDTH, - bottom - PERFORMANCE_WINDOW_HEIGHT - PADDING - STATS_WINDOW_HEIGHT), - vec2i(STATS_WINDOW_WIDTH, STATS_WINDOW_HEIGHT), - ); - - self.ui_presenter.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR); - - let mean_cpu_sample = self.cpu_samples.mean(); - let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT); - self.ui_presenter.draw_text( - device, - &format!("Paths: {}", mean_cpu_sample.path_count), - origin, - false, - ); - self.ui_presenter.draw_text( - device, - &format!("Solid Tiles: {}", mean_cpu_sample.solid_tile_count), - origin + vec2i(0, LINE_HEIGHT * 1), - false, - ); - self.ui_presenter.draw_text( - device, - &format!("Alpha Tiles: {}", mean_cpu_sample.alpha_tile_count), - origin + vec2i(0, LINE_HEIGHT * 2), - false, - ); - self.ui_presenter.draw_text( - device, - &format!("Fills: {}", mean_cpu_sample.fill_count), - origin + vec2i(0, LINE_HEIGHT * 3), - false, - ); - } - - fn draw_performance_window(&self, device: &D) { - let framebuffer_size = self.ui_presenter.framebuffer_size(); - let bottom = framebuffer_size.y() - PADDING; - let window_rect = RectI::new( - vec2i(framebuffer_size.x() - PADDING - PERFORMANCE_WINDOW_WIDTH, - bottom - PERFORMANCE_WINDOW_HEIGHT), - vec2i(PERFORMANCE_WINDOW_WIDTH, PERFORMANCE_WINDOW_HEIGHT), - ); - - self.ui_presenter.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR); - - let mean_cpu_sample = self.cpu_samples.mean(); - let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT); - self.ui_presenter.draw_text( - device, - &format!("CPU: {:.3} ms", duration_to_ms(mean_cpu_sample.cpu_build_time)), - origin, - false, - ); - - let mean_gpu_sample = self.gpu_samples.mean(); - self.ui_presenter.draw_text( - device, - &format!("GPU: {:.3} ms", duration_to_ms(mean_gpu_sample.gpu_time)), - origin + vec2i(0, LINE_HEIGHT * 1), - false, - ); - - let wallclock_time = f64::max(duration_to_ms(mean_gpu_sample.gpu_time), - duration_to_ms(mean_cpu_sample.cpu_build_time)); - self.ui_presenter.draw_text( - device, - &format!("Wallclock: {:.3} ms", wallclock_time), - origin + vec2i(0, LINE_HEIGHT * 3), - false, - ); - } - -} - -struct SampleBuffer -where - S: Add + Div + Clone + Default, -{ - samples: VecDeque, -} - -impl SampleBuffer -where - S: Add + Div + Clone + Default, -{ - fn new() -> SampleBuffer { - SampleBuffer { - samples: VecDeque::with_capacity(SAMPLE_BUFFER_SIZE), - } - } - - fn push(&mut self, time: S) { - self.samples.push_back(time); - while self.samples.len() > SAMPLE_BUFFER_SIZE { - self.samples.pop_front(); - } - } - - fn mean(&self) -> S { - let mut mean = Default::default(); - if self.samples.is_empty() { - return mean; - } - - for time in &self.samples { - mean = mean + (*time).clone(); - } - - mean / self.samples.len() - } -} - -#[derive(Clone, Default)] -struct CPUSample { - elapsed: Duration, - stats: RenderStats, -} - -impl Add for CPUSample { - type Output = CPUSample; - fn add(self, other: CPUSample) -> CPUSample { - CPUSample { - elapsed: self.elapsed + other.elapsed, - stats: self.stats + other.stats, - } - } -} - -impl Div for CPUSample { - type Output = CPUSample; - fn div(self, divisor: usize) -> CPUSample { - CPUSample { - elapsed: self.elapsed / (divisor as u32), - stats: self.stats / divisor, - } - } -} - -fn duration_to_ms(time: Duration) -> f64 { - time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0 -} diff --git a/crates/pathfinder/renderer/src/gpu/mod.rs b/crates/pathfinder/renderer/src/gpu/mod.rs deleted file mode 100644 index 9d005dec1f..0000000000 --- a/crates/pathfinder/renderer/src/gpu/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// pathfinder/renderer/src/gpu/mod.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The GPU renderer for Pathfinder 3. - -pub mod debug; -pub mod options; -pub mod renderer; - -pub(crate) mod shaders; diff --git a/crates/pathfinder/renderer/src/gpu/options.rs b/crates/pathfinder/renderer/src/gpu/options.rs deleted file mode 100644 index 435a631774..0000000000 --- a/crates/pathfinder/renderer/src/gpu/options.rs +++ /dev/null @@ -1,58 +0,0 @@ -// pathfinder/renderer/src/gpu/options.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use pathfinder_color::ColorF; -use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::vector::Vector2I; -use pathfinder_gpu::Device; - -/// Options that influence rendering. -#[derive(Default)] -pub struct RendererOptions { - pub background_color: Option, - pub no_compute: bool, -} - -#[derive(Clone)] -pub enum DestFramebuffer where D: Device { - Default { - viewport: RectI, - window_size: Vector2I, - }, - Other(D::Framebuffer), -} - -impl Default for DestFramebuffer where D: Device { - #[inline] - fn default() -> DestFramebuffer { - DestFramebuffer::Default { viewport: RectI::default(), window_size: Vector2I::default() } - } -} - -impl DestFramebuffer -where - D: Device, -{ - #[inline] - pub fn full_window(window_size: Vector2I) -> DestFramebuffer { - let viewport = RectI::new(Vector2I::default(), window_size); - DestFramebuffer::Default { viewport, window_size } - } - - #[inline] - pub fn window_size(&self, device: &D) -> Vector2I { - match *self { - DestFramebuffer::Default { window_size, .. } => window_size, - DestFramebuffer::Other(ref framebuffer) => { - device.texture_size(device.framebuffer_texture(framebuffer)) - } - } - } -} diff --git a/crates/pathfinder/renderer/src/gpu/renderer.rs b/crates/pathfinder/renderer/src/gpu/renderer.rs deleted file mode 100644 index 8fd5b27f3b..0000000000 --- a/crates/pathfinder/renderer/src/gpu/renderer.rs +++ /dev/null @@ -1,2040 +0,0 @@ -// pathfinder/renderer/src/gpu/renderer.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::gpu::debug::DebugUIPresenter; -use crate::gpu::options::{DestFramebuffer, RendererOptions}; -use crate::gpu::shaders::{BlitProgram, BlitVertexArray, ClearProgram, ClearVertexArray, ClipTileProgram, ClipTileVertexArray}; -use crate::gpu::shaders::{CopyTileProgram, CopyTileVertexArray, FillProgram, FillVertexArray}; -use crate::gpu::shaders::{MAX_FILLS_PER_BATCH, MAX_TILES_PER_BATCH, ReprojectionProgram}; -use crate::gpu::shaders::{ReprojectionVertexArray, StencilProgram, StencilVertexArray}; -use crate::gpu::shaders::{TileProgram, TileVertexArray}; -use crate::gpu_data::{ClipBatch, ClipBatchKey, ClipBatchKind, Fill, FillBatchEntry, RenderCommand}; -use crate::gpu_data::{TextureLocation, TextureMetadataEntry, TexturePageDescriptor, TexturePageId}; -use crate::gpu_data::{Tile, TileBatchTexture}; -use crate::options::BoundingQuad; -use crate::paint::PaintCompositeOp; -use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; -use fxhash::FxHashMap; -use half::f16; -use pathfinder_color::{self as color, ColorF, ColorU}; -use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel}; -use pathfinder_content::effects::{Filter, PatternFilter}; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform3d::Transform4F; -use pathfinder_geometry::util; -use pathfinder_geometry::{alignment::AlignedU16, vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i}}; -use pathfinder_gpu::{BlendFactor, BlendOp, BlendState, BufferData, BufferTarget, BufferUploadMode}; -use pathfinder_gpu::{ClearOps, ComputeDimensions, ComputeState, DepthFunc, DepthState, Device}; -use pathfinder_gpu::{ImageAccess, ImageBinding, Primitive, RenderOptions, RenderState}; -use pathfinder_gpu::{RenderTarget, StencilFunc, StencilState, TextureDataRef}; -use pathfinder_gpu::{TextureFormat, UniformData}; -use pathfinder_resources::ResourceLoader; -use pathfinder_simd::default::{F32x2, F32x4, I32x2}; -use std::collections::VecDeque; -use std::f32; -use std::marker::PhantomData; -use std::mem; -use std::ops::{Add, Div}; -use std::time::Duration; -use std::u32; - -static QUAD_VERTEX_POSITIONS: [AlignedU16; 8] = [0, 0, 1, 0, 1, 1, 0, 1]; -static QUAD_VERTEX_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3]; - -pub(crate) const MASK_TILES_ACROSS: u32 = 256; -pub(crate) const MASK_TILES_DOWN: u32 = 256; - -// 1.0 / sqrt(2*pi) -const SQRT_2_PI_INV: f32 = 0.3989422804014327; - -const TEXTURE_CACHE_SIZE: usize = 8; - -const MIN_FILL_STORAGE_CLASS: usize = 14; // 0x4000 entries, 128kB -const MIN_TILE_STORAGE_CLASS: usize = 10; // 1024 entries, 12kB - -const TEXTURE_METADATA_ENTRIES_PER_ROW: i32 = 128; -const TEXTURE_METADATA_TEXTURE_WIDTH: i32 = TEXTURE_METADATA_ENTRIES_PER_ROW * 4; -const TEXTURE_METADATA_TEXTURE_HEIGHT: i32 = 65536 / TEXTURE_METADATA_ENTRIES_PER_ROW; - -// FIXME(pcwalton): Shrink this again! -const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32; -const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 / 4 * MASK_TILES_DOWN as i32; - -const COMBINER_CTRL_COLOR_COMBINE_SRC_IN: i32 = 0x1; -const COMBINER_CTRL_COLOR_COMBINE_DEST_IN: i32 = 0x2; - -const COMBINER_CTRL_FILTER_RADIAL_GRADIENT: i32 = 0x1; -const COMBINER_CTRL_FILTER_TEXT: i32 = 0x2; -const COMBINER_CTRL_FILTER_BLUR: i32 = 0x3; - -const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0; -const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1; -const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2; -const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3; -const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4; -const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5; -const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6; -const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7; -const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8; -const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9; -const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa; -const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb; -const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc; -const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd; -const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe; -const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf; - -const COMBINER_CTRL_COLOR_FILTER_SHIFT: i32 = 4; -const COMBINER_CTRL_COLOR_COMBINE_SHIFT: i32 = 6; -const COMBINER_CTRL_COMPOSITE_SHIFT: i32 = 8; - -pub struct Renderer where D: Device { - // Device - pub device: D, - - // Core data - dest_framebuffer: DestFramebuffer, - options: RendererOptions, - blit_program: BlitProgram, - clear_program: ClearProgram, - fill_program: FillProgram, - tile_program: TileProgram, - tile_copy_program: CopyTileProgram, - tile_clip_program: ClipTileProgram, - stencil_program: StencilProgram, - reprojection_program: ReprojectionProgram, - quad_vertex_positions_buffer: D::Buffer, - quad_vertex_indices_buffer: D::Buffer, - next_fills: Vec, - fill_tile_map: Vec, - texture_pages: Vec>>, - render_targets: Vec, - render_target_stack: Vec, - area_lut_texture: D::Texture, - gamma_lut_texture: D::Texture, - - // Frames - front_frame: Frame, - back_frame: Frame, - front_frame_fence: Option, - - // Rendering state - texture_cache: TextureCache, - - // Debug - pub stats: RenderStats, - current_cpu_build_time: Option, - current_timer: Option>, - pending_timers: VecDeque>, - timer_query_cache: TimerQueryCache, - pub debug_ui_presenter: DebugUIPresenter, - - // Extra info - flags: RendererFlags, -} - -struct Frame where D: Device { - framebuffer_flags: FramebufferFlags, - blit_vertex_array: BlitVertexArray, - clear_vertex_array: ClearVertexArray, - fill_vertex_storage_allocator: StorageAllocator>, - tile_vertex_storage_allocator: StorageAllocator>, - quads_vertex_indices_buffer: D::Buffer, - quads_vertex_indices_length: usize, - alpha_tile_pages: FxHashMap>, - tile_clip_vertex_array: ClipTileVertexArray, - stencil_vertex_array: StencilVertexArray, - reprojection_vertex_array: ReprojectionVertexArray, - dest_blend_framebuffer: D::Framebuffer, - intermediate_dest_framebuffer: D::Framebuffer, - texture_metadata_texture: D::Texture, -} - -impl Renderer where D: Device { - pub fn new(device: D, - resources: &dyn ResourceLoader, - dest_framebuffer: DestFramebuffer, - options: RendererOptions) - -> Renderer { - let blit_program = BlitProgram::new(&device, resources); - let clear_program = ClearProgram::new(&device, resources); - let fill_program = FillProgram::new(&device, resources, &options); - let tile_program = TileProgram::new(&device, resources); - let tile_copy_program = CopyTileProgram::new(&device, resources); - let tile_clip_program = ClipTileProgram::new(&device, resources); - let stencil_program = StencilProgram::new(&device, resources); - let reprojection_program = ReprojectionProgram::new(&device, resources); - - let area_lut_texture = - device.create_texture_from_png(resources, "area-lut", TextureFormat::RGBA8); - let gamma_lut_texture = - device.create_texture_from_png(resources, "gamma-lut", TextureFormat::R8); - - let quad_vertex_positions_buffer = device.create_buffer(BufferUploadMode::Static); - device.allocate_buffer(&quad_vertex_positions_buffer, - BufferData::Memory(&QUAD_VERTEX_POSITIONS), - BufferTarget::Vertex); - let quad_vertex_indices_buffer = device.create_buffer(BufferUploadMode::Static); - device.allocate_buffer(&quad_vertex_indices_buffer, - BufferData::Memory(&QUAD_VERTEX_INDICES), - BufferTarget::Index); - - let window_size = dest_framebuffer.window_size(&device); - - let timer_query_cache = TimerQueryCache::new(&device); - let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size); - - let front_frame = Frame::new(&device, - &blit_program, - &clear_program, - &tile_clip_program, - &reprojection_program, - &stencil_program, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer, - window_size); - let back_frame = Frame::new(&device, - &blit_program, - &clear_program, - &tile_clip_program, - &reprojection_program, - &stencil_program, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer, - window_size); - - Renderer { - device, - - dest_framebuffer, - options, - blit_program, - clear_program, - fill_program, - tile_program, - tile_copy_program, - tile_clip_program, - quad_vertex_positions_buffer, - quad_vertex_indices_buffer, - next_fills: vec![], - fill_tile_map: vec![-1; 256 * 256], - texture_pages: vec![], - render_targets: vec![], - render_target_stack: vec![], - - front_frame, - back_frame, - front_frame_fence: None, - - area_lut_texture, - gamma_lut_texture, - - stencil_program, - - reprojection_program, - - stats: RenderStats::default(), - current_cpu_build_time: None, - current_timer: None, - pending_timers: VecDeque::new(), - timer_query_cache, - debug_ui_presenter, - - texture_cache: TextureCache::new(), - - flags: RendererFlags::empty(), - } - } - - pub fn begin_scene(&mut self) { - self.back_frame.framebuffer_flags = FramebufferFlags::empty(); - for alpha_tile_page in self.back_frame.alpha_tile_pages.values_mut() { - alpha_tile_page.framebuffer_is_dirty = false; - } - - self.device.begin_commands(); - self.current_timer = Some(PendingTimer::new()); - self.stats = RenderStats::default(); - } - - pub fn render_command(&mut self, command: &RenderCommand) { - debug!("render command: {:?}", command); - match *command { - RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => { - self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer); - } - RenderCommand::AllocateTexturePage { page_id, ref descriptor } => { - self.allocate_texture_page(page_id, descriptor) - } - RenderCommand::UploadTexelData { ref texels, location } => { - self.upload_texel_data(texels, location) - } - RenderCommand::DeclareRenderTarget { id, location } => { - self.declare_render_target(id, location) - } - RenderCommand::UploadTextureMetadata(ref metadata) => { - self.upload_texture_metadata(metadata) - } - RenderCommand::AddFills(ref fills) => self.add_fills(fills), - RenderCommand::FlushFills => { - let page_indices: Vec<_> = - self.back_frame.alpha_tile_pages.keys().cloned().collect(); - for page_index in page_indices { - self.draw_buffered_fills(page_index) - } - } - RenderCommand::ClipTiles(ref batches) => { - batches.iter().for_each(|batch| self.draw_clip_batch(batch)) - } - RenderCommand::BeginTileDrawing => {} - RenderCommand::PushRenderTarget(render_target_id) => { - self.push_render_target(render_target_id) - } - RenderCommand::PopRenderTarget => self.pop_render_target(), - RenderCommand::DrawTiles(ref batch) => { - let count = batch.tiles.len(); - self.stats.alpha_tile_count += count; - let storage_id = self.upload_tiles(&batch.tiles); - self.draw_tiles(batch.tile_page, - count as u32, - storage_id, - batch.color_texture, - batch.blend_mode, - batch.filter) - } - RenderCommand::Finish { cpu_build_time } => { - self.stats.cpu_build_time = cpu_build_time; - } - } - } - - pub fn end_scene(&mut self) { - self.clear_dest_framebuffer_if_necessary(); - self.blit_intermediate_dest_framebuffer_if_necessary(); - - let old_front_frame_fence = self.front_frame_fence.take(); - self.front_frame_fence = Some(self.device.add_fence()); - self.device.end_commands(); - - self.back_frame.fill_vertex_storage_allocator.end_frame(); - self.back_frame.tile_vertex_storage_allocator.end_frame(); - - if let Some(timer) = self.current_timer.take() { - self.pending_timers.push_back(timer); - } - self.current_cpu_build_time = None; - - if let Some(old_front_frame_fence) = old_front_frame_fence { - self.device.wait_for_fence(&old_front_frame_fence); - } - - mem::swap(&mut self.front_frame, &mut self.back_frame); - } - - fn start_rendering(&mut self, - bounding_quad: BoundingQuad, - path_count: usize, - mut needs_readable_framebuffer: bool) { - if let DestFramebuffer::Other(_) = self.dest_framebuffer { - needs_readable_framebuffer = false; - } - - if self.flags.contains(RendererFlags::USE_DEPTH) { - self.draw_stencil(&bounding_quad); - } - self.stats.path_count = path_count; - - self.flags.set(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED, - needs_readable_framebuffer); - - self.render_targets.clear(); - } - - pub fn draw_debug_ui(&self) { - self.debug_ui_presenter.draw(&self.device); - } - - pub fn shift_rendering_time(&mut self) -> Option { - if let Some(mut pending_timer) = self.pending_timers.pop_front() { - for old_query in pending_timer.poll(&self.device) { - self.timer_query_cache.free(old_query); - } - if let Some(gpu_time) = pending_timer.total_time() { - return Some(RenderTime { gpu_time }) - } - self.pending_timers.push_front(pending_timer); - } - None - } - - #[inline] - pub fn dest_framebuffer(&self) -> &DestFramebuffer { - &self.dest_framebuffer - } - - #[inline] - pub fn replace_dest_framebuffer( - &mut self, - new_dest_framebuffer: DestFramebuffer, - ) -> DestFramebuffer { - mem::replace(&mut self.dest_framebuffer, new_dest_framebuffer) - } - - #[inline] - pub fn set_options(&mut self, new_options: RendererOptions) { - self.options = new_options - } - - #[inline] - pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: Vector2I) { - self.debug_ui_presenter.ui_presenter.set_framebuffer_size(new_framebuffer_size); - } - - #[inline] - pub fn disable_depth(&mut self) { - self.flags.remove(RendererFlags::USE_DEPTH); - } - - #[inline] - pub fn enable_depth(&mut self) { - self.flags.insert(RendererFlags::USE_DEPTH); - } - - #[inline] - pub fn quad_vertex_positions_buffer(&self) -> &D::Buffer { - &self.quad_vertex_positions_buffer - } - - #[inline] - pub fn quad_vertex_indices_buffer(&self) -> &D::Buffer { - &self.quad_vertex_indices_buffer - } - - fn allocate_texture_page(&mut self, - page_id: TexturePageId, - descriptor: &TexturePageDescriptor) { - // Fill in IDs up to the requested page ID. - let page_index = page_id.0 as usize; - while self.texture_pages.len() < page_index + 1 { - self.texture_pages.push(None); - } - - // Clear out any existing texture. - if let Some(old_texture_page) = self.texture_pages[page_index].take() { - let old_texture = self.device.destroy_framebuffer(old_texture_page.framebuffer); - self.texture_cache.release_texture(old_texture); - } - - // Allocate texture. - let texture_size = descriptor.size; - let texture = self.texture_cache.create_texture(&mut self.device, - TextureFormat::RGBA8, - texture_size); - let framebuffer = self.device.create_framebuffer(texture); - self.texture_pages[page_index] = Some(TexturePage { - framebuffer, - must_preserve_contents: false, - }); - } - - fn upload_texel_data(&mut self, texels: &[ColorU], location: TextureLocation) { - let texture_page = self.texture_pages[location.page.0 as usize] - .as_mut() - .expect("Texture page not allocated yet!"); - let texture = self.device.framebuffer_texture(&texture_page.framebuffer); - let texels = color::color_slice_to_u8_slice(texels); - self.device.upload_to_texture(texture, location.rect, TextureDataRef::U8(texels)); - texture_page.must_preserve_contents = true; - } - - fn declare_render_target(&mut self, - render_target_id: RenderTargetId, - location: TextureLocation) { - while self.render_targets.len() < render_target_id.render_target as usize + 1 { - self.render_targets.push(RenderTargetInfo { - location: TextureLocation { page: TexturePageId(!0), rect: RectI::default() }, - }); - } - let mut render_target = &mut self.render_targets[render_target_id.render_target as usize]; - debug_assert_eq!(render_target.location.page, TexturePageId(!0)); - render_target.location = location; - } - - fn upload_texture_metadata(&mut self, metadata: &[TextureMetadataEntry]) { - let padded_texel_size = - (util::alignup_i32(metadata.len() as i32, TEXTURE_METADATA_ENTRIES_PER_ROW) * - TEXTURE_METADATA_TEXTURE_WIDTH * 4) as usize; - let mut texels = Vec::with_capacity(padded_texel_size); - for entry in metadata { - let base_color = entry.base_color.to_f32(); - texels.extend_from_slice(&[ - f16::from_f32(entry.color_0_transform.m11()), - f16::from_f32(entry.color_0_transform.m21()), - f16::from_f32(entry.color_0_transform.m12()), - f16::from_f32(entry.color_0_transform.m22()), - f16::from_f32(entry.color_0_transform.m13()), - f16::from_f32(entry.color_0_transform.m23()), - f16::default(), - f16::default(), - f16::from_f32(base_color.r()), - f16::from_f32(base_color.g()), - f16::from_f32(base_color.b()), - f16::from_f32(base_color.a()), - f16::default(), - f16::default(), - f16::default(), - f16::default(), - ]); - } - while texels.len() < padded_texel_size { - texels.push(f16::default()) - } - - let texture = &mut self.back_frame.texture_metadata_texture; - let width = TEXTURE_METADATA_TEXTURE_WIDTH; - let height = texels.len() as i32 / (4 * TEXTURE_METADATA_TEXTURE_WIDTH); - let rect = RectI::new(Vector2I::zero(), Vector2I::new(width, height)); - self.device.upload_to_texture(texture, rect, TextureDataRef::F16(&texels)); - } - - fn upload_tiles(&mut self, tiles: &[Tile]) -> StorageID { - debug_assert!(tiles.len() <= MAX_TILES_PER_BATCH); - - let tile_program = &self.tile_program; - let tile_copy_program = &self.tile_copy_program; - let quad_vertex_positions_buffer = &self.quad_vertex_positions_buffer; - let quad_vertex_indices_buffer = &self.quad_vertex_indices_buffer; - let storage_id = self.back_frame.tile_vertex_storage_allocator.allocate(&self.device, - tiles.len() as u64, - |device, size| { - TileVertexStorage::new(size, - device, - tile_program, - tile_copy_program, - quad_vertex_positions_buffer, - quad_vertex_indices_buffer) - }); - - let vertex_buffer = &self.back_frame - .tile_vertex_storage_allocator - .get(storage_id) - .vertex_buffer; - self.device.upload_to_buffer(vertex_buffer, 0, tiles, BufferTarget::Vertex); - - self.ensure_index_buffer(tiles.len()); - - storage_id - } - - fn ensure_index_buffer(&mut self, mut length: usize) { - length = length.next_power_of_two(); - if self.back_frame.quads_vertex_indices_length >= length { - return; - } - - // TODO(pcwalton): Generate these with SIMD. - let mut indices: Vec = Vec::with_capacity(length * 6); - for index in 0..(length as u32) { - indices.extend_from_slice(&[ - index * 4 + 0, index * 4 + 1, index * 4 + 2, - index * 4 + 1, index * 4 + 3, index * 4 + 2, - ]); - } - - self.device.allocate_buffer(&self.back_frame.quads_vertex_indices_buffer, - BufferData::Memory(&indices), - BufferTarget::Index); - - self.back_frame.quads_vertex_indices_length = length; - } - - fn add_fills(&mut self, fill_batch: &[FillBatchEntry]) { - if fill_batch.is_empty() { - return; - } - - self.stats.fill_count += fill_batch.len(); - - // We have to make sure we don't split batches across draw calls, or else the compute - // shader path, which expects to see all the fills belonging to one tile in the same - // batch, will break. - - let mut pages_touched = vec![]; - for fill_batch_entry in fill_batch { - let page_index = fill_batch_entry.page; - if !self.back_frame.alpha_tile_pages.contains_key(&page_index) { - let alpha_tile_page = AlphaTilePage::new(&mut self.device); - self.back_frame.alpha_tile_pages.insert(page_index, alpha_tile_page); - } - - let page = self.back_frame.alpha_tile_pages.get_mut(&page_index).unwrap(); - if page.pending_fills.is_empty() { - pages_touched.push(page_index); - } - page.pending_fills.push(fill_batch_entry.fill); - } - - for page_index in pages_touched { - if self.back_frame.alpha_tile_pages[&page_index].buffered_fills.len() + - self.back_frame.alpha_tile_pages[&page_index].pending_fills.len() > - MAX_FILLS_PER_BATCH { - self.draw_buffered_fills(page_index); - } - - let page = self.back_frame.alpha_tile_pages.get_mut(&page_index).unwrap(); - for fill in &page.pending_fills { - page.buffered_fills.push(*fill); - } - page.pending_fills.clear(); - } - } - - fn draw_buffered_fills(&mut self, page: u16) { - match self.fill_program { - FillProgram::Raster(_) => self.draw_buffered_fills_via_raster(page), - FillProgram::Compute(_) => self.draw_buffered_fills_via_compute(page), - } - } - - fn draw_buffered_fills_via_raster(&mut self, page: u16) { - let fill_raster_program = match self.fill_program { - FillProgram::Raster(ref fill_raster_program) => fill_raster_program, - _ => unreachable!(), - }; - - let mask_viewport = self.mask_viewport(); - - let alpha_tile_page = self.back_frame - .alpha_tile_pages - .get_mut(&page) - .expect("Where's the alpha tile page?"); - let buffered_fills = &mut alpha_tile_page.buffered_fills; - if buffered_fills.is_empty() { - return; - } - - let storage_id = { - let fill_program = &self.fill_program; - let quad_vertex_positions_buffer = &self.quad_vertex_positions_buffer; - let quad_vertex_indices_buffer = &self.quad_vertex_indices_buffer; - self.back_frame - .fill_vertex_storage_allocator - .allocate(&self.device, MAX_FILLS_PER_BATCH as u64, |device, size| { - FillVertexStorage::new(size, - device, - fill_program, - quad_vertex_positions_buffer, - quad_vertex_indices_buffer) - }) - }; - let fill_vertex_storage = self.back_frame.fill_vertex_storage_allocator.get(storage_id); - - let fill_vertex_array = match fill_vertex_storage.auxiliary { - FillVertexStorageAuxiliary::Raster { ref vertex_array } => vertex_array, - _ => unreachable!(), - }; - - self.device.upload_to_buffer(&fill_vertex_storage.vertex_buffer, - 0, - &buffered_fills, - BufferTarget::Vertex); - - let mut clear_color = None; - if !alpha_tile_page.framebuffer_is_dirty { - clear_color = Some(ColorF::default()); - }; - - let timer_query = self.timer_query_cache.alloc(&self.device); - self.device.begin_timer_query(&timer_query); - - debug_assert!(buffered_fills.len() <= u32::MAX as usize); - self.device.draw_elements_instanced(6, buffered_fills.len() as u32, &RenderState { - target: &RenderTarget::Framebuffer(&alpha_tile_page.framebuffer), - program: &fill_raster_program.program, - vertex_array: &fill_vertex_array.vertex_array, - primitive: Primitive::Triangles, - textures: &[&self.area_lut_texture], - uniforms: &[ - (&fill_raster_program.framebuffer_size_uniform, - UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32, - MASK_FRAMEBUFFER_HEIGHT as f32))), - (&fill_raster_program.tile_size_uniform, - UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), - (&fill_raster_program.area_lut_uniform, UniformData::TextureUnit(0)), - ], - images: &[], - viewport: mask_viewport, - options: RenderOptions { - blend: Some(BlendState { - src_rgb_factor: BlendFactor::One, - src_alpha_factor: BlendFactor::One, - dest_rgb_factor: BlendFactor::One, - dest_alpha_factor: BlendFactor::One, - ..BlendState::default() - }), - clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, - ..RenderOptions::default() - }, - }); - - self.device.end_timer_query(&timer_query); - self.current_timer.as_mut().unwrap().fill_times.push(TimerFuture::new(timer_query)); - - alpha_tile_page.framebuffer_is_dirty = true; - buffered_fills.clear(); - } - - fn draw_buffered_fills_via_compute(&mut self, page: u16) { - let fill_compute_program = match self.fill_program { - FillProgram::Compute(ref fill_compute_program) => fill_compute_program, - _ => unreachable!(), - }; - - let alpha_tile_page = self.back_frame - .alpha_tile_pages - .get_mut(&page) - .expect("Where's the alpha tile page?"); - let buffered_fills = &mut alpha_tile_page.buffered_fills; - if buffered_fills.is_empty() { - return; - } - - let storage_id = { - let fill_program = &self.fill_program; - let quad_vertex_positions_buffer = &self.quad_vertex_positions_buffer; - let quad_vertex_indices_buffer = &self.quad_vertex_indices_buffer; - self.back_frame.fill_vertex_storage_allocator.allocate(&self.device, - MAX_FILLS_PER_BATCH as u64, - |device, size| { - FillVertexStorage::new(size, - device, - fill_program, - quad_vertex_positions_buffer, - quad_vertex_indices_buffer) - }) - }; - let fill_vertex_storage = self.back_frame.fill_vertex_storage_allocator.get(storage_id); - - let (tile_map_buffer, next_fills_buffer) = match fill_vertex_storage.auxiliary { - FillVertexStorageAuxiliary::Compute { ref tile_map_buffer, ref next_fills_buffer } => { - (tile_map_buffer, next_fills_buffer) - } - _ => unreachable!(), - }; - - // Initialize the tile map and fill linked list buffers. - self.fill_tile_map.iter_mut().for_each(|entry| *entry = -1); - while self.next_fills.len() < buffered_fills.len() { - self.next_fills.push(-1); - } - - // Create a linked list running through all our fills. - let (mut first_fill_tile, mut last_fill_tile) = (256 * 256, 0); - for (fill_index, fill) in buffered_fills.iter().enumerate() { - let fill_tile_index = fill.alpha_tile_index as usize; - self.next_fills[fill_index as usize] = self.fill_tile_map[fill_tile_index]; - self.fill_tile_map[fill_tile_index] = fill_index as i32; - first_fill_tile = first_fill_tile.min(fill_tile_index as u32); - last_fill_tile = last_fill_tile.max(fill_tile_index as u32); - } - let fill_tile_count = last_fill_tile - first_fill_tile + 1; - - self.device.upload_to_buffer(&fill_vertex_storage.vertex_buffer, - 0, - &buffered_fills, - BufferTarget::Storage); - self.device.upload_to_buffer(next_fills_buffer, - 0, - &self.next_fills, - BufferTarget::Storage); - self.device.upload_to_buffer(tile_map_buffer, - 0, - &self.fill_tile_map, - BufferTarget::Storage); - - let image_binding = ImageBinding { - texture: self.device.framebuffer_texture(&alpha_tile_page.framebuffer), - access: ImageAccess::Write, - }; - - let timer_query = self.timer_query_cache.alloc(&self.device); - self.device.begin_timer_query(&timer_query); - - debug_assert!(buffered_fills.len() <= u32::MAX as usize); - let dimensions = ComputeDimensions { x: 1, y: 1, z: fill_tile_count as u32 }; - self.device.dispatch_compute(dimensions, &ComputeState { - program: &fill_compute_program.program, - textures: &[&self.area_lut_texture], - images: &[image_binding], - uniforms: &[ - (&fill_compute_program.area_lut_uniform, UniformData::TextureUnit(0)), - (&fill_compute_program.dest_uniform, UniformData::ImageUnit(0)), - (&fill_compute_program.first_tile_index_uniform, - UniformData::Int(first_fill_tile as i32)), - ], - storage_buffers: &[ - (&fill_compute_program.fills_storage_buffer, &fill_vertex_storage.vertex_buffer), - (&fill_compute_program.next_fills_storage_buffer, next_fills_buffer), - (&fill_compute_program.fill_tile_map_storage_buffer, tile_map_buffer), - ], - }); - - self.device.end_timer_query(&timer_query); - self.current_timer.as_mut().unwrap().fill_times.push(TimerFuture::new(timer_query)); - - alpha_tile_page.framebuffer_is_dirty = true; - buffered_fills.clear(); - } - - fn draw_clip_batch(&mut self, batch: &ClipBatch) { - if batch.clips.is_empty() { - return; - } - - let ClipBatchKey { dest_page, src_page, kind } = batch.key; - - self.device.allocate_buffer(&self.back_frame.tile_clip_vertex_array.vertex_buffer, - BufferData::Memory(&batch.clips), - BufferTarget::Vertex); - - if !self.back_frame.alpha_tile_pages.contains_key(&dest_page) { - let alpha_tile_page = AlphaTilePage::new(&mut self.device); - self.back_frame.alpha_tile_pages.insert(dest_page, alpha_tile_page); - } - - let mut clear_color = None; - if !self.back_frame.alpha_tile_pages[&dest_page].framebuffer_is_dirty { - clear_color = Some(ColorF::default()); - }; - - let blend = match kind { - ClipBatchKind::Draw => None, - ClipBatchKind::Clip => { - Some(BlendState { - src_rgb_factor: BlendFactor::One, - src_alpha_factor: BlendFactor::One, - dest_rgb_factor: BlendFactor::One, - dest_alpha_factor: BlendFactor::One, - op: BlendOp::Min, - }) - } - }; - - let mask_viewport = self.mask_viewport(); - - let timer_query = self.timer_query_cache.alloc(&self.device); - self.device.begin_timer_query(&timer_query); - - { - let dest_framebuffer = &self.back_frame.alpha_tile_pages[&dest_page].framebuffer; - let src_framebuffer = &self.back_frame.alpha_tile_pages[&src_page].framebuffer; - let src_texture = self.device.framebuffer_texture(&src_framebuffer); - - debug_assert!(batch.clips.len() <= u32::MAX as usize); - self.device.draw_elements_instanced(6, batch.clips.len() as u32, &RenderState { - target: &RenderTarget::Framebuffer(dest_framebuffer), - program: &self.tile_clip_program.program, - vertex_array: &self.back_frame.tile_clip_vertex_array.vertex_array, - primitive: Primitive::Triangles, - textures: &[src_texture], - images: &[], - uniforms: &[(&self.tile_clip_program.src_uniform, UniformData::TextureUnit(0))], - viewport: mask_viewport, - options: RenderOptions { - blend, - clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, - ..RenderOptions::default() - }, - }); - - self.device.end_timer_query(&timer_query); - self.current_timer.as_mut().unwrap().fill_times.push(TimerFuture::new(timer_query)); - } - - self.back_frame - .alpha_tile_pages - .get_mut(&dest_page) - .unwrap() - .framebuffer_is_dirty = true; - } - - fn tile_transform(&self) -> Transform4F { - let draw_viewport = self.draw_viewport().size().to_f32(); - let scale = Vector4F::new(2.0 / draw_viewport.x(), -2.0 / draw_viewport.y(), 1.0, 1.0); - Transform4F::from_scale(scale).translate(Vector4F::new(-1.0, 1.0, 0.0, 1.0)) - } - - fn draw_tiles(&mut self, - tile_page: u16, - tile_count: u32, - storage_id: StorageID, - color_texture_0: Option, - blend_mode: BlendMode, - filter: Filter) { - // TODO(pcwalton): Disable blend for solid tiles. - - let needs_readable_framebuffer = blend_mode.needs_readable_framebuffer(); - if needs_readable_framebuffer { - self.copy_alpha_tiles_to_dest_blend_texture(tile_count, storage_id); - } - - let clear_color = self.clear_color_for_draw_operation(); - let draw_viewport = self.draw_viewport(); - - let timer_query = self.timer_query_cache.alloc(&self.device); - self.device.begin_timer_query(&timer_query); - - let mut textures = vec![&self.back_frame.texture_metadata_texture]; - let mut uniforms = vec![ - (&self.tile_program.transform_uniform, - UniformData::Mat4(self.tile_transform().to_columns())), - (&self.tile_program.tile_size_uniform, - UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), - (&self.tile_program.framebuffer_size_uniform, - UniformData::Vec2(draw_viewport.size().to_f32().0)), - (&self.tile_program.texture_metadata_uniform, UniformData::TextureUnit(0)), - (&self.tile_program.texture_metadata_size_uniform, - UniformData::IVec2(I32x2::new(TEXTURE_METADATA_TEXTURE_WIDTH, - TEXTURE_METADATA_TEXTURE_HEIGHT))), - ]; - - if needs_readable_framebuffer { - uniforms.push((&self.tile_program.dest_texture_uniform, - UniformData::TextureUnit(textures.len() as u32))); - textures.push(self.device - .framebuffer_texture(&self.back_frame.dest_blend_framebuffer)); - } - - if let Some(alpha_tile_page) = self.back_frame.alpha_tile_pages.get(&tile_page) { - uniforms.push((&self.tile_program.mask_texture_0_uniform, - UniformData::TextureUnit(textures.len() as u32))); - uniforms.push((&self.tile_program.mask_texture_size_0_uniform, - UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32, - MASK_FRAMEBUFFER_HEIGHT as f32)))); - textures.push(self.device.framebuffer_texture(&alpha_tile_page.framebuffer)); - } - - // TODO(pcwalton): Refactor. - let mut ctrl = 0; - match color_texture_0 { - Some(color_texture) => { - let color_texture_page = self.texture_page(color_texture.page); - let color_texture_size = self.device.texture_size(color_texture_page).to_f32(); - self.device.set_texture_sampling_mode(color_texture_page, - color_texture.sampling_flags); - uniforms.push((&self.tile_program.color_texture_0_uniform, - UniformData::TextureUnit(textures.len() as u32))); - uniforms.push((&self.tile_program.color_texture_size_0_uniform, - UniformData::Vec2(color_texture_size.0))); - textures.push(color_texture_page); - - ctrl |= color_texture.composite_op.to_combine_mode() << - COMBINER_CTRL_COLOR_COMBINE_SHIFT; - } - None => { - uniforms.push((&self.tile_program.color_texture_size_0_uniform, - UniformData::Vec2(F32x2::default()))); - } - } - - ctrl |= blend_mode.to_composite_ctrl() << COMBINER_CTRL_COMPOSITE_SHIFT; - - match filter { - Filter::None => self.set_uniforms_for_no_filter(&mut uniforms), - Filter::RadialGradient { line, radii, uv_origin } => { - ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_FILTER_SHIFT; - self.set_uniforms_for_radial_gradient_filter(&mut uniforms, line, radii, uv_origin) - } - Filter::PatternFilter(PatternFilter::Text { - fg_color, - bg_color, - defringing_kernel, - gamma_correction, - }) => { - ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_FILTER_SHIFT; - self.set_uniforms_for_text_filter(&mut textures, - &mut uniforms, - fg_color, - bg_color, - defringing_kernel, - gamma_correction); - } - Filter::PatternFilter(PatternFilter::Blur { direction, sigma }) => { - ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_FILTER_SHIFT; - self.set_uniforms_for_blur_filter(&mut uniforms, direction, sigma); - } - } - - uniforms.push((&self.tile_program.ctrl_uniform, UniformData::Int(ctrl))); - - let vertex_array = &self.back_frame - .tile_vertex_storage_allocator - .get(storage_id) - .tile_vertex_array - .vertex_array; - - self.device.draw_elements_instanced(6, tile_count, &RenderState { - target: &self.draw_render_target(), - program: &self.tile_program.program, - vertex_array, - primitive: Primitive::Triangles, - textures: &textures, - images: &[], - uniforms: &uniforms, - viewport: draw_viewport, - options: RenderOptions { - blend: blend_mode.to_blend_state(), - stencil: self.stencil_state(), - clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, - ..RenderOptions::default() - }, - }); - - self.device.end_timer_query(&timer_query); - self.current_timer.as_mut().unwrap().tile_times.push(TimerFuture::new(timer_query)); - - self.preserve_draw_framebuffer(); - } - - fn copy_alpha_tiles_to_dest_blend_texture(&mut self, tile_count: u32, storage_id: StorageID) { - let draw_viewport = self.draw_viewport(); - - let mut textures = vec![]; - let mut uniforms = vec![ - (&self.tile_copy_program.transform_uniform, - UniformData::Mat4(self.tile_transform().to_columns())), - (&self.tile_copy_program.tile_size_uniform, - UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), - ]; - - let draw_framebuffer = match self.draw_render_target() { - RenderTarget::Framebuffer(framebuffer) => framebuffer, - RenderTarget::Default => panic!("Can't copy alpha tiles from default framebuffer!"), - }; - let draw_texture = self.device.framebuffer_texture(&draw_framebuffer); - - uniforms.push((&self.tile_copy_program.src_uniform, - UniformData::TextureUnit(textures.len() as u32))); - textures.push(draw_texture); - uniforms.push((&self.tile_copy_program.framebuffer_size_uniform, - UniformData::Vec2(draw_viewport.size().to_f32().0))); - - let vertex_array = &self.back_frame - .tile_vertex_storage_allocator - .get(storage_id) - .tile_copy_vertex_array - .vertex_array; - - self.device.draw_elements(tile_count * 6, &RenderState { - target: &RenderTarget::Framebuffer(&self.back_frame.dest_blend_framebuffer), - program: &self.tile_copy_program.program, - vertex_array, - primitive: Primitive::Triangles, - textures: &textures, - images: &[], - uniforms: &uniforms, - viewport: draw_viewport, - options: RenderOptions { - clear_ops: ClearOps { - color: Some(ColorF::new(1.0, 0.0, 0.0, 1.0)), - ..ClearOps::default() - }, - ..RenderOptions::default() - }, - }); - } - - fn draw_stencil(&mut self, quad_positions: &[Vector4F]) { - self.device.allocate_buffer(&self.back_frame.stencil_vertex_array.vertex_buffer, - BufferData::Memory(quad_positions), - BufferTarget::Vertex); - - // Create indices for a triangle fan. (This is OK because the clipped quad should always be - // convex.) - let mut indices: Vec = vec![]; - for index in 1..(quad_positions.len() as u32 - 1) { - indices.extend_from_slice(&[0, index as u32, index + 1]); - } - self.device.allocate_buffer(&self.back_frame.stencil_vertex_array.index_buffer, - BufferData::Memory(&indices), - BufferTarget::Index); - - self.device.draw_elements(indices.len() as u32, &RenderState { - target: &self.draw_render_target(), - program: &self.stencil_program.program, - vertex_array: &self.back_frame.stencil_vertex_array.vertex_array, - primitive: Primitive::Triangles, - textures: &[], - images: &[], - uniforms: &[], - viewport: self.draw_viewport(), - options: RenderOptions { - // FIXME(pcwalton): Should we really write to the depth buffer? - depth: Some(DepthState { func: DepthFunc::Less, write: true }), - stencil: Some(StencilState { - func: StencilFunc::Always, - reference: 1, - mask: 1, - write: true, - }), - color_mask: false, - clear_ops: ClearOps { stencil: Some(0), ..ClearOps::default() }, - ..RenderOptions::default() - }, - }); - } - - pub fn reproject_texture( - &mut self, - texture: &D::Texture, - old_transform: &Transform4F, - new_transform: &Transform4F, - ) { - let clear_color = self.clear_color_for_draw_operation(); - - self.device.draw_elements(6, &RenderState { - target: &self.draw_render_target(), - program: &self.reprojection_program.program, - vertex_array: &self.back_frame.reprojection_vertex_array.vertex_array, - primitive: Primitive::Triangles, - textures: &[texture], - images: &[], - uniforms: &[ - (&self.reprojection_program.old_transform_uniform, - UniformData::from_transform_3d(old_transform)), - (&self.reprojection_program.new_transform_uniform, - UniformData::from_transform_3d(new_transform)), - (&self.reprojection_program.texture_uniform, UniformData::TextureUnit(0)), - ], - viewport: self.draw_viewport(), - options: RenderOptions { - blend: BlendMode::SrcOver.to_blend_state(), - depth: Some(DepthState { func: DepthFunc::Less, write: false, }), - clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, - ..RenderOptions::default() - }, - }); - - self.preserve_draw_framebuffer(); - } - - pub fn draw_render_target(&self) -> RenderTarget { - match self.render_target_stack.last() { - Some(&render_target_id) => { - let texture_page_id = self.render_target_location(render_target_id).page; - let framebuffer = self.texture_page_framebuffer(texture_page_id); - RenderTarget::Framebuffer(framebuffer) - } - None => { - if self.flags.contains(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED) { - RenderTarget::Framebuffer(&self.back_frame.intermediate_dest_framebuffer) - } else { - match self.dest_framebuffer { - DestFramebuffer::Default { .. } => RenderTarget::Default, - DestFramebuffer::Other(ref framebuffer) => { - RenderTarget::Framebuffer(framebuffer) - } - } - } - } - } - } - - fn push_render_target(&mut self, render_target_id: RenderTargetId) { - self.render_target_stack.push(render_target_id); - } - - fn pop_render_target(&mut self) { - self.render_target_stack.pop().expect("Render target stack underflow!"); - } - - fn set_uniforms_for_no_filter<'a>(&'a self, - uniforms: &mut Vec<(&'a D::Uniform, UniformData)>) { - uniforms.extend_from_slice(&[ - (&self.tile_program.filter_params_0_uniform, UniformData::Vec4(F32x4::default())), - (&self.tile_program.filter_params_1_uniform, UniformData::Vec4(F32x4::default())), - (&self.tile_program.filter_params_2_uniform, UniformData::Vec4(F32x4::default())), - ]); - } - - fn set_uniforms_for_radial_gradient_filter<'a>( - &'a self, - uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, - line: LineSegment2F, - radii: F32x2, - uv_origin: Vector2F) { - uniforms.extend_from_slice(&[ - (&self.tile_program.filter_params_0_uniform, - UniformData::Vec4(line.from().0.concat_xy_xy(line.vector().0))), - (&self.tile_program.filter_params_1_uniform, - UniformData::Vec4(radii.concat_xy_xy(uv_origin.0))), - (&self.tile_program.filter_params_2_uniform, UniformData::Vec4(F32x4::default())), - ]); - } - - fn set_uniforms_for_text_filter<'a>(&'a self, - textures: &mut Vec<&'a D::Texture>, - uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, - fg_color: ColorF, - bg_color: ColorF, - defringing_kernel: Option, - gamma_correction: bool) { - let gamma_lut_texture_unit = textures.len() as u32; - textures.push(&self.gamma_lut_texture); - - match defringing_kernel { - Some(ref kernel) => { - uniforms.push((&self.tile_program.filter_params_0_uniform, - UniformData::Vec4(F32x4::from_slice(&kernel.0)))); - } - None => { - uniforms.push((&self.tile_program.filter_params_0_uniform, - UniformData::Vec4(F32x4::default()))); - } - } - - let mut params_2 = fg_color.0; - params_2.set_w(gamma_correction as i32 as f32); - - uniforms.extend_from_slice(&[ - (&self.tile_program.gamma_lut_uniform, - UniformData::TextureUnit(gamma_lut_texture_unit)), - (&self.tile_program.filter_params_1_uniform, UniformData::Vec4(bg_color.0)), - (&self.tile_program.filter_params_2_uniform, UniformData::Vec4(params_2)), - ]); - - } - - fn set_uniforms_for_blur_filter<'a>(&'a self, - uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, - direction: BlurDirection, - sigma: f32) { - let sigma_inv = 1.0 / sigma; - let gauss_coeff_x = SQRT_2_PI_INV * sigma_inv; - let gauss_coeff_y = f32::exp(-0.5 * sigma_inv * sigma_inv); - let gauss_coeff_z = gauss_coeff_y * gauss_coeff_y; - - let src_offset = match direction { - BlurDirection::X => vec2f(1.0, 0.0), - BlurDirection::Y => vec2f(0.0, 1.0), - }; - - let support = f32::ceil(1.5 * sigma) * 2.0; - - uniforms.extend_from_slice(&[ - (&self.tile_program.filter_params_0_uniform, - UniformData::Vec4(src_offset.0.concat_xy_xy(F32x2::new(support, 0.0)))), - (&self.tile_program.filter_params_1_uniform, - UniformData::Vec4(F32x4::new(gauss_coeff_x, gauss_coeff_y, gauss_coeff_z, 0.0))), - (&self.tile_program.filter_params_2_uniform, UniformData::Vec4(F32x4::default())), - ]); - } - - fn clear_dest_framebuffer_if_necessary(&mut self) { - let background_color = match self.options.background_color { - None => return, - Some(background_color) => background_color, - }; - - if self.back_frame - .framebuffer_flags - .contains(FramebufferFlags::DEST_FRAMEBUFFER_IS_DIRTY) { - return; - } - - let main_viewport = self.main_viewport(); - let uniforms = [ - (&self.clear_program.rect_uniform, UniformData::Vec4(main_viewport.to_f32().0)), - (&self.clear_program.framebuffer_size_uniform, - UniformData::Vec2(main_viewport.size().to_f32().0)), - (&self.clear_program.color_uniform, UniformData::Vec4(background_color.0)), - ]; - - self.device.draw_elements(6, &RenderState { - target: &RenderTarget::Default, - program: &self.clear_program.program, - vertex_array: &self.back_frame.clear_vertex_array.vertex_array, - primitive: Primitive::Triangles, - textures: &[], - images: &[], - uniforms: &uniforms[..], - viewport: main_viewport, - options: RenderOptions::default(), - }); - } - - fn blit_intermediate_dest_framebuffer_if_necessary(&mut self) { - if !self.flags.contains(RendererFlags::INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED) { - return; - } - - let main_viewport = self.main_viewport(); - - let uniforms = [(&self.blit_program.src_uniform, UniformData::TextureUnit(0))]; - let textures = [ - (self.device.framebuffer_texture(&self.back_frame.intermediate_dest_framebuffer)) - ]; - - self.device.draw_elements(6, &RenderState { - target: &RenderTarget::Default, - program: &self.blit_program.program, - vertex_array: &self.back_frame.blit_vertex_array.vertex_array, - primitive: Primitive::Triangles, - textures: &textures[..], - images: &[], - uniforms: &uniforms[..], - viewport: main_viewport, - options: RenderOptions { - clear_ops: ClearOps { - color: Some(ColorF::new(0.0, 0.0, 0.0, 1.0)), - ..ClearOps::default() - }, - ..RenderOptions::default() - }, - }); - } - - fn stencil_state(&self) -> Option { - if !self.flags.contains(RendererFlags::USE_DEPTH) { - return None; - } - - Some(StencilState { - func: StencilFunc::Equal, - reference: 1, - mask: 1, - write: false, - }) - } - - fn clear_color_for_draw_operation(&self) -> Option { - let must_preserve_contents = match self.render_target_stack.last() { - Some(&render_target_id) => { - let texture_page = self.render_target_location(render_target_id).page; - self.texture_pages[texture_page.0 as usize] - .as_ref() - .expect("Draw target texture page not allocated!") - .must_preserve_contents - } - None => { - self.back_frame - .framebuffer_flags - .contains(FramebufferFlags::DEST_FRAMEBUFFER_IS_DIRTY) - } - }; - - if must_preserve_contents { - None - } else if self.render_target_stack.is_empty() { - self.options.background_color - } else { - Some(ColorF::default()) - } - } - - fn preserve_draw_framebuffer(&mut self) { - match self.render_target_stack.last() { - Some(&render_target_id) => { - let texture_page = self.render_target_location(render_target_id).page; - self.texture_pages[texture_page.0 as usize] - .as_mut() - .expect("Draw target texture page not allocated!") - .must_preserve_contents = true; - } - None => { - self.back_frame - .framebuffer_flags - .insert(FramebufferFlags::DEST_FRAMEBUFFER_IS_DIRTY); - } - } - } - - pub fn draw_viewport(&self) -> RectI { - match self.render_target_stack.last() { - Some(&render_target_id) => self.render_target_location(render_target_id).rect, - None => self.main_viewport(), - } - } - - fn main_viewport(&self) -> RectI { - match self.dest_framebuffer { - DestFramebuffer::Default { viewport, .. } => viewport, - DestFramebuffer::Other(ref framebuffer) => { - let size = self - .device - .texture_size(self.device.framebuffer_texture(framebuffer)); - RectI::new(Vector2I::default(), size) - } - } - } - - fn mask_viewport(&self) -> RectI { - RectI::new(Vector2I::zero(), vec2i(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT)) - } - - fn render_target_location(&self, render_target_id: RenderTargetId) -> TextureLocation { - self.render_targets[render_target_id.render_target as usize].location - } - - fn texture_page_framebuffer(&self, id: TexturePageId) -> &D::Framebuffer { - &self.texture_pages[id.0 as usize] - .as_ref() - .expect("Texture page not allocated!") - .framebuffer - } - - fn texture_page(&self, id: TexturePageId) -> &D::Texture { - self.device.framebuffer_texture(&self.texture_page_framebuffer(id)) - } -} - -impl Frame where D: Device { - // FIXME(pcwalton): This signature shouldn't be so big. Make a struct. - fn new(device: &D, - blit_program: &BlitProgram, - clear_program: &ClearProgram, - tile_clip_program: &ClipTileProgram, - reprojection_program: &ReprojectionProgram, - stencil_program: &StencilProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer, - window_size: Vector2I) - -> Frame { - let quads_vertex_indices_buffer = device.create_buffer(BufferUploadMode::Dynamic); - - let blit_vertex_array = BlitVertexArray::new(device, - &blit_program, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer); - let clear_vertex_array = ClearVertexArray::new(device, - &clear_program, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer); - let tile_clip_vertex_array = ClipTileVertexArray::new(device, - &tile_clip_program, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer); - let reprojection_vertex_array = ReprojectionVertexArray::new(device, - &reprojection_program, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer); - let stencil_vertex_array = StencilVertexArray::new(device, &stencil_program); - - let fill_vertex_storage_allocator = StorageAllocator::new(MIN_FILL_STORAGE_CLASS); - let tile_vertex_storage_allocator = StorageAllocator::new(MIN_TILE_STORAGE_CLASS); - - let texture_metadata_texture_size = vec2i(TEXTURE_METADATA_TEXTURE_WIDTH, - TEXTURE_METADATA_TEXTURE_HEIGHT); - let texture_metadata_texture = device.create_texture(TextureFormat::RGBA16F, - texture_metadata_texture_size); - - let intermediate_dest_texture = device.create_texture(TextureFormat::RGBA8, window_size); - let intermediate_dest_framebuffer = device.create_framebuffer(intermediate_dest_texture); - - let dest_blend_texture = device.create_texture(TextureFormat::RGBA8, window_size); - let dest_blend_framebuffer = device.create_framebuffer(dest_blend_texture); - - Frame { - blit_vertex_array, - clear_vertex_array, - tile_vertex_storage_allocator, - fill_vertex_storage_allocator, - tile_clip_vertex_array, - reprojection_vertex_array, - stencil_vertex_array, - quads_vertex_indices_buffer, - quads_vertex_indices_length: 0, - alpha_tile_pages: FxHashMap::default(), - texture_metadata_texture, - intermediate_dest_framebuffer, - dest_blend_framebuffer, - framebuffer_flags: FramebufferFlags::empty(), - } - } -} - -// Buffer management - -struct StorageAllocator where D: Device { - buckets: Vec>, - min_size_class: usize, - phantom: PhantomData, -} - -struct StorageAllocatorBucket { - free: Vec, - in_use: Vec, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -struct StorageID { - bucket: usize, - index: usize, -} - -impl StorageAllocator where D: Device { - fn new(min_size_class: usize) -> StorageAllocator { - StorageAllocator { buckets: vec![], min_size_class, phantom: PhantomData } - } - - fn allocate(&mut self, device: &D, size: u64, allocator: F) -> StorageID - where D: Device, F: FnOnce(&D, u64) -> S { - let size_class = (64 - (size.leading_zeros() as usize)).max(self.min_size_class); - let bucket_index = size_class - self.min_size_class; - while self.buckets.len() < bucket_index + 1 { - self.buckets.push(StorageAllocatorBucket { free: vec![], in_use: vec![] }); - } - - let bucket = &mut self.buckets[bucket_index]; - match bucket.free.pop() { - Some(storage) => bucket.in_use.push(storage), - None => bucket.in_use.push(allocator(device, 1 << size_class as u64)), - } - StorageID { bucket: bucket_index, index: bucket.in_use.len() - 1 } - } - - fn get(&self, storage_id: StorageID) -> &S { - &self.buckets[storage_id.bucket].in_use[storage_id.index] - } - - fn end_frame(&mut self) { - for bucket in &mut self.buckets { - bucket.free.extend(mem::replace(&mut bucket.in_use, vec![]).into_iter()) - } - } -} - -struct FillVertexStorage where D: Device { - vertex_buffer: D::Buffer, - auxiliary: FillVertexStorageAuxiliary, -} - -enum FillVertexStorageAuxiliary where D: Device { - Raster { vertex_array: FillVertexArray }, - Compute { - next_fills_buffer: D::Buffer, - tile_map_buffer: D::Buffer, - }, -} - -struct TileVertexStorage where D: Device { - tile_vertex_array: TileVertexArray, - tile_copy_vertex_array: CopyTileVertexArray, - vertex_buffer: D::Buffer, -} - -impl FillVertexStorage where D: Device { - fn new(size: u64, - device: &D, - fill_program: &FillProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer) - -> FillVertexStorage { - let vertex_buffer = device.create_buffer(BufferUploadMode::Dynamic); - let vertex_buffer_data: BufferData = BufferData::Uninitialized(size as usize); - device.allocate_buffer(&vertex_buffer, vertex_buffer_data, BufferTarget::Vertex); - - let auxiliary = match *fill_program { - FillProgram::Raster(ref fill_raster_program) => { - FillVertexStorageAuxiliary::Raster { - vertex_array: FillVertexArray::new(device, - fill_raster_program, - &vertex_buffer, - quad_vertex_positions_buffer, - quad_vertex_indices_buffer), - } - } - FillProgram::Compute(_) => { - let next_fills_buffer = device.create_buffer(BufferUploadMode::Dynamic); - let tile_map_buffer = device.create_buffer(BufferUploadMode::Dynamic); - let next_fills_buffer_data: BufferData = - BufferData::Uninitialized(size as usize); - let tile_map_buffer_data: BufferData = - BufferData::Uninitialized(256 * 256); - device.allocate_buffer(&next_fills_buffer, - next_fills_buffer_data, - BufferTarget::Storage); - device.allocate_buffer(&tile_map_buffer, - tile_map_buffer_data, - BufferTarget::Storage); - FillVertexStorageAuxiliary::Compute { next_fills_buffer, tile_map_buffer } - } - }; - - FillVertexStorage { vertex_buffer, auxiliary } - } -} - -impl TileVertexStorage where D: Device { - fn new(size: u64, - device: &D, - tile_program: &TileProgram, - tile_copy_program: &CopyTileProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer) - -> TileVertexStorage { - let vertex_buffer = device.create_buffer(BufferUploadMode::Dynamic); - device.allocate_buffer::(&vertex_buffer, - BufferData::Uninitialized(size as usize), - BufferTarget::Vertex); - let tile_vertex_array = TileVertexArray::new(device, - &tile_program, - &vertex_buffer, - &quad_vertex_positions_buffer, - &quad_vertex_indices_buffer); - let tile_copy_vertex_array = CopyTileVertexArray::new(device, - &tile_copy_program, - &vertex_buffer, - &quad_vertex_indices_buffer); - TileVertexStorage { vertex_buffer, tile_vertex_array, tile_copy_vertex_array } - } -} - -// Render stats - -#[derive(Clone, Copy, Debug, Default)] -pub struct RenderStats { - pub path_count: usize, - pub fill_count: usize, - pub alpha_tile_count: usize, - pub solid_tile_count: usize, - pub cpu_build_time: Duration, -} - -impl Add for RenderStats { - type Output = RenderStats; - fn add(self, other: RenderStats) -> RenderStats { - RenderStats { - path_count: self.path_count + other.path_count, - solid_tile_count: self.solid_tile_count + other.solid_tile_count, - alpha_tile_count: self.alpha_tile_count + other.alpha_tile_count, - fill_count: self.fill_count + other.fill_count, - cpu_build_time: self.cpu_build_time + other.cpu_build_time, - } - } -} - -impl Div for RenderStats { - type Output = RenderStats; - fn div(self, divisor: usize) -> RenderStats { - RenderStats { - path_count: self.path_count / divisor, - solid_tile_count: self.solid_tile_count / divisor, - alpha_tile_count: self.alpha_tile_count / divisor, - fill_count: self.fill_count / divisor, - cpu_build_time: self.cpu_build_time / divisor as u32, - } - } -} - -struct TimerQueryCache where D: Device { - free_queries: Vec, -} - -struct PendingTimer where D: Device { - fill_times: Vec>, - tile_times: Vec>, -} - -enum TimerFuture where D: Device { - Pending(D::TimerQuery), - Resolved(Duration), -} - -impl TimerQueryCache where D: Device { - fn new(_: &D) -> TimerQueryCache { - TimerQueryCache { free_queries: vec![] } - } - - fn alloc(&mut self, device: &D) -> D::TimerQuery { - self.free_queries.pop().unwrap_or_else(|| device.create_timer_query()) - } - - fn free(&mut self, old_query: D::TimerQuery) { - self.free_queries.push(old_query); - } -} - -impl PendingTimer where D: Device { - fn new() -> PendingTimer { - PendingTimer { fill_times: vec![], tile_times: vec![] } - } - - fn poll(&mut self, device: &D) -> Vec { - let mut old_queries = vec![]; - for future in self.fill_times.iter_mut().chain(self.tile_times.iter_mut()) { - if let Some(old_query) = future.poll(device) { - old_queries.push(old_query) - } - } - old_queries - } - - fn total_time(&self) -> Option { - let mut total = Duration::default(); - for future in self.fill_times.iter().chain(self.tile_times.iter()) { - match *future { - TimerFuture::Pending(_) => return None, - TimerFuture::Resolved(time) => total += time, - } - } - Some(total) - } -} - -impl TimerFuture where D: Device { - fn new(query: D::TimerQuery) -> TimerFuture { - TimerFuture::Pending(query) - } - - fn poll(&mut self, device: &D) -> Option { - let duration = match *self { - TimerFuture::Pending(ref query) => device.try_recv_timer_query(query), - TimerFuture::Resolved(_) => None, - }; - match duration { - None => None, - Some(duration) => { - match mem::replace(self, TimerFuture::Resolved(duration)) { - TimerFuture::Resolved(_) => unreachable!(), - TimerFuture::Pending(old_query) => Some(old_query), - } - } - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct RenderTime { - pub gpu_time: Duration, -} - -impl Default for RenderTime { - #[inline] - fn default() -> RenderTime { - RenderTime { gpu_time: Duration::new(0, 0) } - } -} - -impl Add for RenderTime { - type Output = RenderTime; - - #[inline] - fn add(self, other: RenderTime) -> RenderTime { - RenderTime { gpu_time: self.gpu_time + other.gpu_time } - } -} - -impl Div for RenderTime { - type Output = RenderTime; - - #[inline] - fn div(self, divisor: usize) -> RenderTime { - RenderTime { gpu_time: self.gpu_time / divisor as u32 } - } -} - -bitflags! { - struct FramebufferFlags: u8 { - const MASK_FRAMEBUFFER_IS_DIRTY = 0x01; - const DEST_FRAMEBUFFER_IS_DIRTY = 0x02; - } -} - -struct TextureCache where D: Device { - textures: Vec, -} - -impl TextureCache where D: Device { - fn new() -> TextureCache { - TextureCache { textures: vec![] } - } - - fn create_texture(&mut self, device: &mut D, format: TextureFormat, size: Vector2I) - -> D::Texture { - for index in 0..self.textures.len() { - if device.texture_size(&self.textures[index]) == size && - device.texture_format(&self.textures[index]) == format { - return self.textures.remove(index); - } - } - - device.create_texture(format, size) - } - - fn release_texture(&mut self, texture: D::Texture) { - if self.textures.len() == TEXTURE_CACHE_SIZE { - self.textures.pop(); - } - self.textures.insert(0, texture); - } -} - -struct TexturePage where D: Device { - framebuffer: D::Framebuffer, - must_preserve_contents: bool, -} - -struct RenderTargetInfo { - location: TextureLocation, -} - -trait ToBlendState { - fn to_blend_state(self) -> Option; -} - -impl ToBlendState for BlendMode { - fn to_blend_state(self) -> Option { - match self { - BlendMode::Clear => { - Some(BlendState { - src_rgb_factor: BlendFactor::Zero, - dest_rgb_factor: BlendFactor::Zero, - src_alpha_factor: BlendFactor::Zero, - dest_alpha_factor: BlendFactor::Zero, - ..BlendState::default() - }) - } - BlendMode::SrcOver => { - Some(BlendState { - src_rgb_factor: BlendFactor::One, - dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, - src_alpha_factor: BlendFactor::One, - dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, - ..BlendState::default() - }) - } - BlendMode::DestOver => { - Some(BlendState { - src_rgb_factor: BlendFactor::OneMinusDestAlpha, - dest_rgb_factor: BlendFactor::One, - src_alpha_factor: BlendFactor::OneMinusDestAlpha, - dest_alpha_factor: BlendFactor::One, - ..BlendState::default() - }) - } - BlendMode::SrcIn => { - Some(BlendState { - src_rgb_factor: BlendFactor::DestAlpha, - dest_rgb_factor: BlendFactor::Zero, - src_alpha_factor: BlendFactor::DestAlpha, - dest_alpha_factor: BlendFactor::Zero, - ..BlendState::default() - }) - } - BlendMode::DestIn => { - Some(BlendState { - src_rgb_factor: BlendFactor::Zero, - dest_rgb_factor: BlendFactor::SrcAlpha, - src_alpha_factor: BlendFactor::Zero, - dest_alpha_factor: BlendFactor::SrcAlpha, - ..BlendState::default() - }) - } - BlendMode::SrcOut => { - Some(BlendState { - src_rgb_factor: BlendFactor::OneMinusDestAlpha, - dest_rgb_factor: BlendFactor::Zero, - src_alpha_factor: BlendFactor::OneMinusDestAlpha, - dest_alpha_factor: BlendFactor::Zero, - ..BlendState::default() - }) - } - BlendMode::DestOut => { - Some(BlendState { - src_rgb_factor: BlendFactor::Zero, - dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, - src_alpha_factor: BlendFactor::Zero, - dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, - ..BlendState::default() - }) - } - BlendMode::SrcAtop => { - Some(BlendState { - src_rgb_factor: BlendFactor::DestAlpha, - dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, - src_alpha_factor: BlendFactor::DestAlpha, - dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, - ..BlendState::default() - }) - } - BlendMode::DestAtop => { - Some(BlendState { - src_rgb_factor: BlendFactor::OneMinusDestAlpha, - dest_rgb_factor: BlendFactor::SrcAlpha, - src_alpha_factor: BlendFactor::OneMinusDestAlpha, - dest_alpha_factor: BlendFactor::SrcAlpha, - ..BlendState::default() - }) - } - BlendMode::Xor => { - Some(BlendState { - src_rgb_factor: BlendFactor::OneMinusDestAlpha, - dest_rgb_factor: BlendFactor::OneMinusSrcAlpha, - src_alpha_factor: BlendFactor::OneMinusDestAlpha, - dest_alpha_factor: BlendFactor::OneMinusSrcAlpha, - ..BlendState::default() - }) - } - BlendMode::Lighter => { - Some(BlendState { - src_rgb_factor: BlendFactor::One, - dest_rgb_factor: BlendFactor::One, - src_alpha_factor: BlendFactor::One, - dest_alpha_factor: BlendFactor::One, - ..BlendState::default() - }) - } - BlendMode::Copy | - BlendMode::Darken | - BlendMode::Lighten | - BlendMode::Multiply | - BlendMode::Screen | - BlendMode::HardLight | - BlendMode::Overlay | - BlendMode::ColorDodge | - BlendMode::ColorBurn | - BlendMode::SoftLight | - BlendMode::Difference | - BlendMode::Exclusion | - BlendMode::Hue | - BlendMode::Saturation | - BlendMode::Color | - BlendMode::Luminosity => { - // Blending is done manually in the shader. - None - } - } - } -} - -pub trait BlendModeExt { - fn needs_readable_framebuffer(self) -> bool; -} - -impl BlendModeExt for BlendMode { - fn needs_readable_framebuffer(self) -> bool { - match self { - BlendMode::Clear | - BlendMode::SrcOver | - BlendMode::DestOver | - BlendMode::SrcIn | - BlendMode::DestIn | - BlendMode::SrcOut | - BlendMode::DestOut | - BlendMode::SrcAtop | - BlendMode::DestAtop | - BlendMode::Xor | - BlendMode::Lighter | - BlendMode::Copy => false, - BlendMode::Lighten | - BlendMode::Darken | - BlendMode::Multiply | - BlendMode::Screen | - BlendMode::HardLight | - BlendMode::Overlay | - BlendMode::ColorDodge | - BlendMode::ColorBurn | - BlendMode::SoftLight | - BlendMode::Difference | - BlendMode::Exclusion | - BlendMode::Hue | - BlendMode::Saturation | - BlendMode::Color | - BlendMode::Luminosity => true, - } - } -} - -struct AlphaTilePage where D: Device { - buffered_fills: Vec, - pending_fills: Vec, - framebuffer: D::Framebuffer, - framebuffer_is_dirty: bool, -} - -impl AlphaTilePage where D: Device { - fn new(device: &mut D) -> AlphaTilePage { - let framebuffer_size = vec2i(MASK_FRAMEBUFFER_WIDTH, MASK_FRAMEBUFFER_HEIGHT); - let framebuffer_texture = device.create_texture(TextureFormat::RGBA16F, framebuffer_size); - let framebuffer = device.create_framebuffer(framebuffer_texture); - AlphaTilePage { - buffered_fills: vec![], - pending_fills: vec![], - framebuffer, - framebuffer_is_dirty: false, - } - } -} - -bitflags! { - struct RendererFlags: u8 { - // Whether we need a depth buffer. - const USE_DEPTH = 0x01; - // Whether an intermediate destination framebuffer is needed. - // - // This will be true if any exotic blend modes are used at the top level (not inside a - // render target), *and* the output framebuffer is the default framebuffer. - const INTERMEDIATE_DEST_FRAMEBUFFER_NEEDED = 0x02; - } -} - -trait ToCompositeCtrl { - fn to_composite_ctrl(&self) -> i32; -} - -impl ToCompositeCtrl for BlendMode { - fn to_composite_ctrl(&self) -> i32 { - match *self { - BlendMode::SrcOver | - BlendMode::SrcAtop | - BlendMode::DestOver | - BlendMode::DestOut | - BlendMode::Xor | - BlendMode::Lighter | - BlendMode::Clear | - BlendMode::Copy | - BlendMode::SrcIn | - BlendMode::SrcOut | - BlendMode::DestIn | - BlendMode::DestAtop => COMBINER_CTRL_COMPOSITE_NORMAL, - BlendMode::Multiply => COMBINER_CTRL_COMPOSITE_MULTIPLY, - BlendMode::Darken => COMBINER_CTRL_COMPOSITE_DARKEN, - BlendMode::Lighten => COMBINER_CTRL_COMPOSITE_LIGHTEN, - BlendMode::Screen => COMBINER_CTRL_COMPOSITE_SCREEN, - BlendMode::Overlay => COMBINER_CTRL_COMPOSITE_OVERLAY, - BlendMode::ColorDodge => COMBINER_CTRL_COMPOSITE_COLOR_DODGE, - BlendMode::ColorBurn => COMBINER_CTRL_COMPOSITE_COLOR_BURN, - BlendMode::HardLight => COMBINER_CTRL_COMPOSITE_HARD_LIGHT, - BlendMode::SoftLight => COMBINER_CTRL_COMPOSITE_SOFT_LIGHT, - BlendMode::Difference => COMBINER_CTRL_COMPOSITE_DIFFERENCE, - BlendMode::Exclusion => COMBINER_CTRL_COMPOSITE_EXCLUSION, - BlendMode::Hue => COMBINER_CTRL_COMPOSITE_HUE, - BlendMode::Saturation => COMBINER_CTRL_COMPOSITE_SATURATION, - BlendMode::Color => COMBINER_CTRL_COMPOSITE_COLOR, - BlendMode::Luminosity => COMBINER_CTRL_COMPOSITE_LUMINOSITY, - } - } -} - -trait ToCombineMode { - fn to_combine_mode(self) -> i32; -} - -impl ToCombineMode for PaintCompositeOp { - fn to_combine_mode(self) -> i32 { - match self { - PaintCompositeOp::DestIn => COMBINER_CTRL_COLOR_COMBINE_DEST_IN, - PaintCompositeOp::SrcIn => COMBINER_CTRL_COLOR_COMBINE_SRC_IN, - } - } -} diff --git a/crates/pathfinder/renderer/src/gpu/shaders.rs b/crates/pathfinder/renderer/src/gpu/shaders.rs deleted file mode 100644 index 827276af51..0000000000 --- a/crates/pathfinder/renderer/src/gpu/shaders.rs +++ /dev/null @@ -1,648 +0,0 @@ -// pathfinder/renderer/src/gpu/shaders.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::gpu::options::RendererOptions; -use crate::gpu::renderer::{MASK_TILES_ACROSS, MASK_TILES_DOWN}; -use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; -use pathfinder_gpu::{BufferTarget, BufferUploadMode, ComputeDimensions, Device, FeatureLevel, VertexAttrClass}; -use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType, VertexBufferDescriptor}; -use pathfinder_gpu::{ALIGNED_I16_ATTR, ALIGNED_I8_ATTR, ALIGNED_U8_ATTR, ALIGNED_U16_ATTR}; -use pathfinder_resources::ResourceLoader; -use once_cell::sync::Lazy; - -pub const MAX_FILLS_PER_BATCH: usize = 0x10000; -pub const MAX_TILES_PER_BATCH: usize = MASK_TILES_ACROSS as usize * MASK_TILES_DOWN as usize; - -pub struct BlitVertexArray where D: Device { - pub vertex_array: D::VertexArray, -} - -impl BlitVertexArray where D: Device { - pub fn new(device: &D, - blit_program: &BlitProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer) - -> BlitVertexArray { - let vertex_array = device.create_vertex_array(); - let position_attrs= &[ - device.get_vertex_attr(&blit_program.program, "Position").unwrap(), - ]; - - static POSITION_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 2), - ] - }; - descriptor.update_attrs(); - descriptor - }); - - device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); - POSITION_BUFFER.configure_vertex_attrs(device, &vertex_array, position_attrs); - device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); - - BlitVertexArray { vertex_array } - } -} - -pub struct ClearVertexArray where D: Device { - pub vertex_array: D::VertexArray, -} - -impl ClearVertexArray where D: Device { - pub fn new(device: &D, - clear_program: &ClearProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer) - -> ClearVertexArray { - let vertex_array = device.create_vertex_array(); - let position_attrs= &[ - device.get_vertex_attr(&clear_program.program, "Position").unwrap(), - ]; - - static POSITION_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 2), - ] - }; - descriptor.update_attrs(); - descriptor - }); - - device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); - POSITION_BUFFER.configure_vertex_attrs(device, &vertex_array, position_attrs); - device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); - - ClearVertexArray { vertex_array } - } -} - -pub struct FillVertexArray where D: Device { - pub vertex_array: D::VertexArray, -} - -impl FillVertexArray -where - D: Device, -{ - pub fn new( - device: &D, - fill_program: &FillRasterProgram, - vertex_buffer: &D::Buffer, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer, - ) -> FillVertexArray { - let vertex_array = device.create_vertex_array(); - - let tess_coord_attrs= &[ - device.get_vertex_attr(&fill_program.program, "TessCoord").unwrap() - ]; - - static TESS_COORD_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U16_ATTR, 2), - ] - }; - descriptor.update_attrs(); - descriptor - }); - - let fill_attrs= &[ - device.get_vertex_attr(&fill_program.program, "FromSubpx").unwrap(), - device.get_vertex_attr(&fill_program.program, "ToSubpx").unwrap(), - device.get_vertex_attr(&fill_program.program, "FromPx").unwrap(), - device.get_vertex_attr(&fill_program.program, "ToPx").unwrap(), - device.get_vertex_attr(&fill_program.program, "TileIndex").unwrap(), - ]; - - static FILL_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 1, - divisor: 1, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::FloatNorm, VertexAttrType::U8, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::FloatNorm, VertexAttrType::U8, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U8_ATTR, 1), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U8_ATTR, 1), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U16_ATTR, 1), - ] - }; - descriptor.update_attrs(); - descriptor - }); - - device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); - TESS_COORD_BUFFER.configure_vertex_attrs(device, &vertex_array, tess_coord_attrs); - device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); - FILL_BUFFER.configure_vertex_attrs(device, &vertex_array, fill_attrs); - device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); - - FillVertexArray { vertex_array } - } -} - -pub struct TileVertexArray where D: Device { - pub vertex_array: D::VertexArray, -} - -impl TileVertexArray where D: Device { - pub fn new(device: &D, - tile_program: &TileProgram, - tile_vertex_buffer: &D::Buffer, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer) - -> TileVertexArray { - let vertex_array = device.create_vertex_array(); - - let tile_offset_attrs = &[ - device.get_vertex_attr(&tile_program.program, "TileOffset").unwrap(), - ]; - - static TILE_OFFSET_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U16_ATTR, 2), - ] - }; - descriptor.update_attrs(); - descriptor - }); - - let tile_buffer_attrs = &[ - device.get_vertex_attr(&tile_program.program, "TileOrigin").unwrap(), - device.get_vertex_attr(&tile_program.program, "MaskTexCoord0").unwrap(), - device.get_vertex_attr(&tile_program.program, "MaskBackdrop").unwrap(), - device.get_vertex_attr(&tile_program.program, "Color").unwrap(), - device.get_vertex_attr(&tile_program.program, "TileCtrl").unwrap(), - ]; - - static TILE_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 1, - divisor: 1, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U8_ATTR, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I8_ATTR, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 1), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 1), - ] - }; - descriptor.update_attrs(); - descriptor - - }); - - device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); - TILE_OFFSET_BUFFER.configure_vertex_attrs(device, &vertex_array, tile_offset_attrs); - device.bind_buffer(&vertex_array, tile_vertex_buffer, BufferTarget::Vertex); - TILE_BUFFER.configure_vertex_attrs(device, &vertex_array, tile_buffer_attrs); - device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); - - TileVertexArray { vertex_array } - } -} - -pub struct CopyTileVertexArray where D: Device { - pub vertex_array: D::VertexArray, -} - -impl CopyTileVertexArray where D: Device { - pub fn new( - device: &D, - copy_tile_program: &CopyTileProgram, - copy_tile_vertex_buffer: &D::Buffer, - quads_vertex_indices_buffer: &D::Buffer, - ) -> CopyTileVertexArray { - let vertex_array = device.create_vertex_array(); - - let copy_tile_attrs = &[ - device.get_vertex_attr(©_tile_program.program, "TilePosition").unwrap(), - ]; - - static COPY_TILE_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor { - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 2), - ], - }; - descriptor.update_attrs(); - descriptor - }); - - device.bind_buffer(&vertex_array, copy_tile_vertex_buffer, BufferTarget::Vertex); - COPY_TILE_BUFFER.configure_vertex_attrs(device, &vertex_array, copy_tile_attrs); - device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index); - - CopyTileVertexArray { vertex_array } - } -} - -pub struct ClipTileVertexArray where D: Device { - pub vertex_array: D::VertexArray, - pub vertex_buffer: D::Buffer, -} - -impl ClipTileVertexArray where D: Device { - pub fn new(device: &D, - clip_tile_program: &ClipTileProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer) - -> ClipTileVertexArray { - let vertex_array = device.create_vertex_array(); - let vertex_buffer = device.create_buffer(BufferUploadMode::Dynamic); - let tile_offset_attrs = &[ - device.get_vertex_attr(&clip_tile_program.program, "TileOffset").unwrap(), - ]; - - static TILE_OFFSET_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 2), - ], - }; - descriptor.update_attrs(); - descriptor - }); - - let clip_tile_attrs = &[ - device.get_vertex_attr(&clip_tile_program.program, "DestTileOrigin").unwrap(), - device.get_vertex_attr(&clip_tile_program.program, "SrcTileOrigin").unwrap(), - device.get_vertex_attr(&clip_tile_program.program, "SrcBackdrop").unwrap(), - ]; - - static CLIP_TILE_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 1, - divisor: 1, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U8_ATTR, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_U8_ATTR, 2), - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I8_ATTR, 1), - ], - }; - descriptor.update_attrs(); - descriptor - }); - - device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); - TILE_OFFSET_BUFFER.configure_vertex_attrs(device, &vertex_array, tile_offset_attrs); - device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); - CLIP_TILE_BUFFER.configure_vertex_attrs(device, &vertex_array, clip_tile_attrs); - device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); - - ClipTileVertexArray { vertex_array, vertex_buffer } - } -} - - -pub struct BlitProgram where D: Device { - pub program: D::Program, - pub src_uniform: D::Uniform, -} - -impl BlitProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> BlitProgram { - let program = device.create_raster_program(resources, "blit"); - let src_uniform = device.get_uniform(&program, "Src"); - BlitProgram { program, src_uniform } - } -} - -pub struct ClearProgram where D: Device { - pub program: D::Program, - pub rect_uniform: D::Uniform, - pub framebuffer_size_uniform: D::Uniform, - pub color_uniform: D::Uniform, -} - -impl ClearProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> ClearProgram { - let program = device.create_raster_program(resources, "clear"); - let rect_uniform = device.get_uniform(&program, "Rect"); - let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); - let color_uniform = device.get_uniform(&program, "Color"); - ClearProgram { program, rect_uniform, framebuffer_size_uniform, color_uniform } - } -} - -pub enum FillProgram where D: Device { - Raster(FillRasterProgram), - Compute(FillComputeProgram), -} - -impl FillProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader, options: &RendererOptions) - -> FillProgram { - match (options.no_compute, device.feature_level()) { - (false, FeatureLevel::D3D11) => { - FillProgram::Compute(FillComputeProgram::new(device, resources)) - } - (_, FeatureLevel::D3D10) | (true, _) => { - FillProgram::Raster(FillRasterProgram::new(device, resources)) - } - } - } -} - -pub struct FillRasterProgram where D: Device { - pub program: D::Program, - pub framebuffer_size_uniform: D::Uniform, - pub tile_size_uniform: D::Uniform, - pub area_lut_uniform: D::Uniform, -} - -impl FillRasterProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> FillRasterProgram { - let program = device.create_raster_program(resources, "fill"); - let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); - let tile_size_uniform = device.get_uniform(&program, "TileSize"); - let area_lut_uniform = device.get_uniform(&program, "AreaLUT"); - FillRasterProgram { - program, - framebuffer_size_uniform, - tile_size_uniform, - area_lut_uniform, - } - } -} - -pub struct FillComputeProgram where D: Device { - pub program: D::Program, - pub dest_uniform: D::Uniform, - pub area_lut_uniform: D::Uniform, - pub first_tile_index_uniform: D::Uniform, - pub fills_storage_buffer: D::StorageBuffer, - pub next_fills_storage_buffer: D::StorageBuffer, - pub fill_tile_map_storage_buffer: D::StorageBuffer, -} - -impl FillComputeProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> FillComputeProgram { - let mut program = device.create_compute_program(resources, "fill"); - let local_size = ComputeDimensions { x: TILE_WIDTH, y: TILE_HEIGHT / 4, z: 1 }; - device.set_compute_program_local_size(&mut program, local_size); - - let dest_uniform = device.get_uniform(&program, "Dest"); - let area_lut_uniform = device.get_uniform(&program, "AreaLUT"); - let first_tile_index_uniform = device.get_uniform(&program, "FirstTileIndex"); - let fills_storage_buffer = device.get_storage_buffer(&program, "Fills", 0); - let next_fills_storage_buffer = device.get_storage_buffer(&program, "NextFills", 1); - let fill_tile_map_storage_buffer = device.get_storage_buffer(&program, "FillTileMap", 2); - - FillComputeProgram { - program, - dest_uniform, - area_lut_uniform, - first_tile_index_uniform, - fills_storage_buffer, - next_fills_storage_buffer, - fill_tile_map_storage_buffer, - } - } -} - -pub struct TileProgram where D: Device { - pub program: D::Program, - pub transform_uniform: D::Uniform, - pub tile_size_uniform: D::Uniform, - pub texture_metadata_uniform: D::Uniform, - pub texture_metadata_size_uniform: D::Uniform, - pub dest_texture_uniform: D::Uniform, - pub color_texture_0_uniform: D::Uniform, - pub color_texture_size_0_uniform: D::Uniform, - pub color_texture_1_uniform: D::Uniform, - pub mask_texture_0_uniform: D::Uniform, - pub mask_texture_size_0_uniform: D::Uniform, - pub gamma_lut_uniform: D::Uniform, - pub filter_params_0_uniform: D::Uniform, - pub filter_params_1_uniform: D::Uniform, - pub filter_params_2_uniform: D::Uniform, - pub framebuffer_size_uniform: D::Uniform, - pub ctrl_uniform: D::Uniform, -} - -impl TileProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgram { - let program = device.create_raster_program(resources, "tile"); - let transform_uniform = device.get_uniform(&program, "Transform"); - let tile_size_uniform = device.get_uniform(&program, "TileSize"); - let texture_metadata_uniform = device.get_uniform(&program, "TextureMetadata"); - let texture_metadata_size_uniform = device.get_uniform(&program, "TextureMetadataSize"); - let dest_texture_uniform = device.get_uniform(&program, "DestTexture"); - let color_texture_0_uniform = device.get_uniform(&program, "ColorTexture0"); - let color_texture_size_0_uniform = device.get_uniform(&program, "ColorTextureSize0"); - let color_texture_1_uniform = device.get_uniform(&program, "ColorTexture1"); - let mask_texture_0_uniform = device.get_uniform(&program, "MaskTexture0"); - let mask_texture_size_0_uniform = device.get_uniform(&program, "MaskTextureSize0"); - let gamma_lut_uniform = device.get_uniform(&program, "GammaLUT"); - let filter_params_0_uniform = device.get_uniform(&program, "FilterParams0"); - let filter_params_1_uniform = device.get_uniform(&program, "FilterParams1"); - let filter_params_2_uniform = device.get_uniform(&program, "FilterParams2"); - let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); - let ctrl_uniform = device.get_uniform(&program, "Ctrl"); - TileProgram { - program, - transform_uniform, - tile_size_uniform, - texture_metadata_uniform, - texture_metadata_size_uniform, - dest_texture_uniform, - color_texture_0_uniform, - color_texture_size_0_uniform, - color_texture_1_uniform, - mask_texture_0_uniform, - mask_texture_size_0_uniform, - gamma_lut_uniform, - filter_params_0_uniform, - filter_params_1_uniform, - filter_params_2_uniform, - framebuffer_size_uniform, - ctrl_uniform, - } - } -} - -pub struct CopyTileProgram where D: Device { - pub program: D::Program, - pub transform_uniform: D::Uniform, - pub tile_size_uniform: D::Uniform, - pub framebuffer_size_uniform: D::Uniform, - pub src_uniform: D::Uniform, -} - -impl CopyTileProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> CopyTileProgram { - let program = device.create_raster_program(resources, "tile_copy"); - let transform_uniform = device.get_uniform(&program, "Transform"); - let tile_size_uniform = device.get_uniform(&program, "TileSize"); - let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); - let src_uniform = device.get_uniform(&program, "Src"); - CopyTileProgram { - program, - transform_uniform, - tile_size_uniform, - framebuffer_size_uniform, - src_uniform, - } - } -} - -pub struct ClipTileProgram where D: Device { - pub program: D::Program, - pub src_uniform: D::Uniform, -} - -impl ClipTileProgram where D: Device { - pub fn new(device: &D, resources: &dyn ResourceLoader) -> ClipTileProgram { - let program = device.create_raster_program(resources, "tile_clip"); - let src_uniform = device.get_uniform(&program, "Src"); - ClipTileProgram { program, src_uniform } - } -} - -pub struct StencilProgram -where - D: Device, -{ - pub program: D::Program, -} - -impl StencilProgram -where - D: Device, -{ - pub fn new(device: &D, resources: &dyn ResourceLoader) -> StencilProgram { - let program = device.create_raster_program(resources, "stencil"); - StencilProgram { program } - } -} - -pub struct StencilVertexArray -where - D: Device, -{ - pub vertex_array: D::VertexArray, - pub vertex_buffer: D::Buffer, - pub index_buffer: D::Buffer, -} - -impl StencilVertexArray -where - D: Device, -{ - pub fn new(device: &D, stencil_program: &StencilProgram) -> StencilVertexArray { - let vertex_array = device.create_vertex_array(); - let vertex_buffer = device.create_buffer(BufferUploadMode::Static); - let index_buffer = device.create_buffer(BufferUploadMode::Static); - - let position_attr = device.get_vertex_attr(&stencil_program.program, "Position").unwrap(); - - device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex); - device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor { - size: 3, - class: VertexAttrClass::Float, - attr_type: VertexAttrType::F32, - stride: 4 * 4, - offset: 0, - divisor: 0, - buffer_index: 0, - }); - device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index); - - StencilVertexArray { vertex_array, vertex_buffer, index_buffer } - } -} - -pub struct ReprojectionProgram -where - D: Device, -{ - pub program: D::Program, - pub old_transform_uniform: D::Uniform, - pub new_transform_uniform: D::Uniform, - pub texture_uniform: D::Uniform, -} - -impl ReprojectionProgram -where - D: Device, -{ - pub fn new(device: &D, resources: &dyn ResourceLoader) -> ReprojectionProgram { - let program = device.create_raster_program(resources, "reproject"); - let old_transform_uniform = device.get_uniform(&program, "OldTransform"); - let new_transform_uniform = device.get_uniform(&program, "NewTransform"); - let texture_uniform = device.get_uniform(&program, "Texture"); - - ReprojectionProgram { - program, - old_transform_uniform, - new_transform_uniform, - texture_uniform, - } - } -} - -pub struct ReprojectionVertexArray -where - D: Device, -{ - pub vertex_array: D::VertexArray, -} - -impl ReprojectionVertexArray -where - D: Device, -{ - pub fn new( - device: &D, - reprojection_program: &ReprojectionProgram, - quad_vertex_positions_buffer: &D::Buffer, - quad_vertex_indices_buffer: &D::Buffer, - ) -> ReprojectionVertexArray { - let vertex_array = device.create_vertex_array(); - let position_attrs = &[ - device.get_vertex_attr(&reprojection_program.program, "Position") - .unwrap(), - ]; - static POSITION_BUFFER: Lazy = Lazy::new(|| { - let mut descriptor = VertexBufferDescriptor{ - index: 0, - divisor: 0, - vertex_attrs: vec![ - VertexAttrDescriptor::datatype_only(VertexAttrClass::Int, ALIGNED_I16_ATTR, 2), - ] - }; - descriptor.update_attrs(); - descriptor - }); - - device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex); - POSITION_BUFFER.configure_vertex_attrs(device, &vertex_array, position_attrs); - device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index); - - ReprojectionVertexArray { vertex_array } - } -} diff --git a/crates/pathfinder/renderer/src/gpu_data.rs b/crates/pathfinder/renderer/src/gpu_data.rs deleted file mode 100644 index fe5ae10d70..0000000000 --- a/crates/pathfinder/renderer/src/gpu_data.rs +++ /dev/null @@ -1,277 +0,0 @@ -// pathfinder/renderer/src/gpu_data.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Packed data ready to be sent to the GPU. - -use crate::builder::{ALPHA_TILES_PER_LEVEL, ALPHA_TILE_LEVEL_COUNT}; -use crate::options::BoundingQuad; -use crate::paint::PaintCompositeOp; -use pathfinder_color::ColorU; -use pathfinder_content::effects::{BlendMode, Filter}; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_geometry::alignment::{AlignedI8, AlignedU8, AlignedI16, AlignedU16}; -use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; -use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::Vector2I; -use pathfinder_gpu::TextureSamplingFlags; -use std::fmt::{Debug, Formatter, Result as DebugResult}; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::Duration; -use std::u32; - -pub const TILE_CTRL_MASK_MASK: i32 = 0x3; -pub const TILE_CTRL_MASK_WINDING: i32 = 0x1; -pub const TILE_CTRL_MASK_EVEN_ODD: i32 = 0x2; - -pub const TILE_CTRL_MASK_0_SHIFT: i32 = 0; - -pub enum RenderCommand { - // Starts rendering a frame. - Start { - /// The number of paths that will be rendered. - path_count: usize, - - /// A bounding quad for the scene. - bounding_quad: BoundingQuad, - - /// Whether the framebuffer we're rendering to must be readable. - /// - /// This is needed if a path that renders directly to the output framebuffer (i.e. not to a - /// render target) uses one of the more exotic blend modes. - needs_readable_framebuffer: bool, - }, - - // Allocates a texture page. - AllocateTexturePage { page_id: TexturePageId, descriptor: TexturePageDescriptor }, - - // Uploads data to a texture page. - UploadTexelData { texels: Arc>, location: TextureLocation }, - - // Associates a render target with a texture page. - // - // TODO(pcwalton): Add a rect to this so we can render to subrects of a page. - DeclareRenderTarget { id: RenderTargetId, location: TextureLocation }, - - // Upload texture metadata. - UploadTextureMetadata(Vec), - - // Adds fills to the queue. - AddFills(Vec), - - // Flushes the queue of fills. - FlushFills, - - // Renders clips to the mask tile. - ClipTiles(Vec), - - // Pushes a render target onto the stack. Draw commands go to the render target on top of the - // stack. - PushRenderTarget(RenderTargetId), - - // Pops a render target from the stack. - PopRenderTarget, - - // Marks that tile compositing is about to begin. - BeginTileDrawing, - - // Draws a batch of tiles to the render target on top of the stack. - DrawTiles(TileBatch), - - // Presents a rendered frame. - Finish { cpu_build_time: Duration }, -} - -#[derive(Clone, Copy, PartialEq, Debug, Default)] -pub struct TexturePageId(pub u32); - -#[derive(Clone, Copy, Debug)] -pub struct TexturePageDescriptor { - pub size: Vector2I, -} - -#[derive(Clone, Copy, PartialEq, Debug, Default)] -pub struct TextureLocation { - pub page: TexturePageId, - pub rect: RectI, -} - -#[derive(Clone, Debug)] -pub struct TileBatch { - pub tiles: Vec, - pub color_texture: Option, - pub filter: Filter, - pub blend_mode: BlendMode, - pub tile_page: u16, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct TileBatchTexture { - pub page: TexturePageId, - pub sampling_flags: TextureSamplingFlags, - pub composite_op: PaintCompositeOp, -} - -#[derive(Clone, Copy, Debug)] -pub struct FillObjectPrimitive { - pub px: LineSegmentU4, - pub subpx: LineSegmentU8, - pub tile_x: i16, - pub tile_y: i16, -} - -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct TileObjectPrimitive { - pub alpha_tile_id: AlphaTileId, - pub backdrop: i8, -} - -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct TextureMetadataEntry { - pub color_0_transform: Transform2F, - pub base_color: ColorU, -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct FillBatchEntry { - pub fill: Fill, - pub page: u16, -} - -#[derive(Clone, Copy, Debug, Default)] -#[repr(C)] -pub struct Fill { - pub subpx: LineSegmentU8, - pub px: LineSegmentU4, - pub alpha_tile_index: AlignedU16, -} - -#[derive(Clone, Debug)] -pub struct ClipBatch { - pub clips: Vec, - pub key: ClipBatchKey, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct ClipBatchKey { - pub dest_page: u16, - pub src_page: u16, - pub kind: ClipBatchKind, -} - -// Order is significant here. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum ClipBatchKind { - Draw, - Clip, -} - -#[derive(Clone, Copy, Debug, Default)] -#[repr(C)] -pub struct Clip { - pub dest_u: AlignedU8, - pub dest_v: AlignedU8, - pub src_u: AlignedU8, - pub src_v: AlignedU8, - pub backdrop: AlignedI8, - pub pad_0: AlignedU8, - pub pad_1: AlignedU16, -} - -#[derive(Clone, Copy, Debug, Default)] -#[repr(C)] -pub struct Tile { - pub tile_x: AlignedI16, - pub tile_y: AlignedI16, - pub mask_0_u: AlignedU8, - pub mask_0_v: AlignedU8, - pub mask_0_backdrop: AlignedI8, - pub pad: AlignedU8, - pub color: AlignedU16, - pub ctrl: AlignedU16, -} - -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct AlphaTileId(pub u32); - -impl AlphaTileId { - #[inline] - pub fn new(next_alpha_tile_index: &[AtomicUsize; ALPHA_TILE_LEVEL_COUNT], level: usize) - -> AlphaTileId { - let alpha_tile_index = next_alpha_tile_index[level].fetch_add(1, Ordering::Relaxed); - debug_assert!(alpha_tile_index < ALPHA_TILES_PER_LEVEL); - AlphaTileId((level * ALPHA_TILES_PER_LEVEL + alpha_tile_index) as u32) - } - - #[inline] - pub fn invalid() -> AlphaTileId { - AlphaTileId(!0) - } - - #[inline] - pub fn page(self) -> u16 { - (self.0 >> 16) as u16 - } - - #[inline] - pub fn tile(self) -> u16 { - (self.0 & 0xffff) as u16 - } - - #[inline] - pub fn is_valid(self) -> bool { - self.0 < !0 - } -} - -impl Debug for RenderCommand { - fn fmt(&self, formatter: &mut Formatter) -> DebugResult { - match *self { - RenderCommand::Start { .. } => write!(formatter, "Start"), - RenderCommand::AllocateTexturePage { page_id, descriptor: _ } => { - write!(formatter, "AllocateTexturePage({})", page_id.0) - } - RenderCommand::UploadTexelData { ref texels, location } => { - write!(formatter, "UploadTexelData(x{:?}, {:?})", texels.len(), location) - } - RenderCommand::DeclareRenderTarget { id, location } => { - write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location) - } - RenderCommand::UploadTextureMetadata(ref metadata) => { - write!(formatter, "UploadTextureMetadata(x{})", metadata.len()) - } - RenderCommand::AddFills(ref fills) => { - write!(formatter, "AddFills(x{})", fills.len()) - } - RenderCommand::FlushFills => write!(formatter, "FlushFills"), - RenderCommand::ClipTiles(ref batches) => { - write!(formatter, "ClipTiles(x{})", batches.len()) - } - RenderCommand::PushRenderTarget(render_target_id) => { - write!(formatter, "PushRenderTarget({:?})", render_target_id) - } - RenderCommand::PopRenderTarget => write!(formatter, "PopRenderTarget"), - RenderCommand::BeginTileDrawing => write!(formatter, "BeginTileDrawing"), - RenderCommand::DrawTiles(ref batch) => { - write!(formatter, - "DrawTiles(x{}, C0 {:?}, {:?})", - batch.tiles.len(), - batch.color_texture, - batch.blend_mode) - } - RenderCommand::Finish { cpu_build_time } => { - write!(formatter, "Finish({} ms)", cpu_build_time.as_secs_f64() * 1000.0) - } - } - } -} diff --git a/crates/pathfinder/renderer/src/lib.rs b/crates/pathfinder/renderer/src/lib.rs deleted file mode 100644 index b12d8fbcce..0000000000 --- a/crates/pathfinder/renderer/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -// pathfinder/renderer/src/lib.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The CPU portion of Pathfinder's renderer. - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate log; - -pub mod concurrent; -pub mod gpu; -pub mod gpu_data; -pub mod options; -pub mod paint; -pub mod scene; - -mod allocator; -mod builder; -mod tile_map; -mod tiles; -mod z_buffer; diff --git a/crates/pathfinder/renderer/src/options.rs b/crates/pathfinder/renderer/src/options.rs deleted file mode 100644 index 86ba541e64..0000000000 --- a/crates/pathfinder/renderer/src/options.rs +++ /dev/null @@ -1,153 +0,0 @@ -// pathfinder/renderer/src/options.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Options that control how rendering is to be performed. - -use crate::gpu_data::RenderCommand; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::transform3d::Perspective; -use pathfinder_geometry::vector::{Vector2F, Vector4F}; -use pathfinder_content::clip::PolygonClipper3D; - -pub trait RenderCommandListener: Send + Sync { - fn send(&self, command: RenderCommand); -} - -impl RenderCommandListener for F -where - F: Fn(RenderCommand) + Send + Sync, -{ - #[inline] - fn send(&self, command: RenderCommand) { - (*self)(command) - } -} - -/// Options that influence scene building. -#[derive(Clone, Default)] -pub struct BuildOptions { - pub transform: RenderTransform, - pub dilation: Vector2F, - pub subpixel_aa_enabled: bool, -} - -impl BuildOptions { - pub(crate) fn prepare(self, bounds: RectF) -> PreparedBuildOptions { - PreparedBuildOptions { - transform: self.transform.prepare(bounds), - dilation: self.dilation, - subpixel_aa_enabled: self.subpixel_aa_enabled, - } - } -} - -#[derive(Clone)] -pub enum RenderTransform { - Transform2D(Transform2F), - Perspective(Perspective), -} - -impl Default for RenderTransform { - #[inline] - fn default() -> RenderTransform { - RenderTransform::Transform2D(Transform2F::default()) - } -} - -impl RenderTransform { - fn prepare(&self, bounds: RectF) -> PreparedRenderTransform { - let perspective = match self { - RenderTransform::Transform2D(ref transform) => { - if transform.is_identity() { - return PreparedRenderTransform::None; - } - return PreparedRenderTransform::Transform2D(*transform); - } - RenderTransform::Perspective(ref perspective) => *perspective, - }; - - let mut points = vec![ - bounds.origin().to_4d(), - bounds.upper_right().to_4d(), - bounds.lower_right().to_4d(), - bounds.lower_left().to_4d(), - ]; - debug!("-----"); - debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points); - for point in &mut points { - *point = perspective.transform * *point; - } - debug!("... PERSPECTIVE quad={:?}", points); - - // Compute depth. - let quad = [ - points[0].to_3d().to_4d(), - points[1].to_3d().to_4d(), - points[2].to_3d().to_4d(), - points[3].to_3d().to_4d(), - ]; - debug!("... PERSPECTIVE-DIVIDED points = {:?}", quad); - - points = PolygonClipper3D::new(points).clip(); - debug!("... CLIPPED quad={:?}", points); - for point in &mut points { - *point = point.to_3d().to_4d() - } - - let inverse_transform = perspective.transform.inverse(); - let clip_polygon = points.into_iter() - .map(|point| (inverse_transform * point).to_2d()) - .collect(); - return PreparedRenderTransform::Perspective { - perspective, - clip_polygon, - quad, - }; - } -} - -pub(crate) struct PreparedBuildOptions { - pub(crate) transform: PreparedRenderTransform, - pub(crate) dilation: Vector2F, - pub(crate) subpixel_aa_enabled: bool, -} - -impl PreparedBuildOptions { - #[inline] - pub(crate) fn bounding_quad(&self) -> BoundingQuad { - match self.transform { - PreparedRenderTransform::Perspective { quad, .. } => quad, - _ => [Vector4F::default(); 4], - } - } -} - -pub(crate) type BoundingQuad = [Vector4F; 4]; - -pub(crate) enum PreparedRenderTransform { - None, - Transform2D(Transform2F), - Perspective { - perspective: Perspective, - clip_polygon: Vec, - quad: [Vector4F; 4], - }, -} - -impl PreparedRenderTransform { - #[inline] - pub(crate) fn is_2d(&self) -> bool { - match *self { - PreparedRenderTransform::Transform2D(_) => true, - _ => false, - } - } -} diff --git a/crates/pathfinder/renderer/src/paint.rs b/crates/pathfinder/renderer/src/paint.rs deleted file mode 100644 index 8d876f3c54..0000000000 --- a/crates/pathfinder/renderer/src/paint.rs +++ /dev/null @@ -1,702 +0,0 @@ -// pathfinder/renderer/src/paint.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::allocator::{AllocationMode, TextureAllocator}; -use crate::gpu_data::{RenderCommand, TextureLocation, TextureMetadataEntry, TexturePageDescriptor}; -use crate::gpu_data::{TexturePageId, TileBatchTexture}; -use crate::scene::{RenderTarget, SceneId}; -use hashbrown::HashMap; -use pathfinder_color::ColorU; -use pathfinder_content::effects::{Filter, PatternFilter}; -use pathfinder_content::gradient::{Gradient, GradientGeometry}; -use pathfinder_content::pattern::{Pattern, PatternSource}; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::{RectF, RectI}; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; -use pathfinder_gpu::TextureSamplingFlags; -use pathfinder_simd::default::{F32x2, F32x4}; -use std::f32; -use std::fmt::{self, Debug, Formatter}; -use std::sync::Arc; - -// The size of a gradient tile. -// -// TODO(pcwalton): Choose this size dynamically! -const GRADIENT_TILE_LENGTH: u32 = 256; - -#[derive(Clone)] -pub struct Palette { - pub paints: Vec, - render_targets: Vec, - cache: HashMap, - allocator: TextureAllocator, - scene_id: SceneId, -} - -#[derive(Clone)] -struct RenderTargetData { - render_target: RenderTarget, - metadata: RenderTargetMetadata, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct Paint { - base_color: ColorU, - overlay: Option, -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct PaintOverlay { - composite_op: PaintCompositeOp, - contents: PaintContents, -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum PaintContents { - Gradient(Gradient), - Pattern(Pattern), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct PaintId(pub u16); - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct GradientId(pub u32); - -/// How a paint is to be composited over a base color, or vice versa. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub enum PaintCompositeOp { - SrcIn, - DestIn, -} - -impl Debug for PaintContents { - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - match *self { - PaintContents::Gradient(ref gradient) => gradient.fmt(formatter), - PaintContents::Pattern(ref pattern) => pattern.fmt(formatter), - } - } -} - -impl Palette { - #[inline] - pub fn new(scene_id: SceneId) -> Palette { - Palette { - paints: vec![], - render_targets: vec![], - cache: HashMap::new(), - allocator: TextureAllocator::new(), - scene_id, - } - } -} - -impl Paint { - #[inline] - pub fn from_color(color: ColorU) -> Paint { - Paint { base_color: color, overlay: None } - } - - #[inline] - pub fn from_gradient(gradient: Gradient) -> Paint { - Paint { - base_color: ColorU::white(), - overlay: Some(PaintOverlay { - composite_op: PaintCompositeOp::SrcIn, - contents: PaintContents::Gradient(gradient), - }), - } - } - - #[inline] - pub fn from_pattern(pattern: Pattern) -> Paint { - Paint { - base_color: ColorU::white(), - overlay: Some(PaintOverlay { - composite_op: PaintCompositeOp::SrcIn, - contents: PaintContents::Pattern(pattern), - }), - } - } - - #[inline] - pub fn black() -> Paint { - Paint::from_color(ColorU::black()) - } - - #[inline] - pub fn transparent_black() -> Paint { - Paint::from_color(ColorU::transparent_black()) - } - - pub fn is_opaque(&self) -> bool { - if !self.base_color.is_opaque() { - return false; - } - - match self.overlay { - None => true, - Some(ref overlay) => { - match overlay.contents { - PaintContents::Gradient(ref gradient) => gradient.is_opaque(), - PaintContents::Pattern(ref pattern) => pattern.is_opaque(), - } - } - } - } - - pub fn is_fully_transparent(&self) -> bool { - if !self.base_color.is_fully_transparent() { - return false; - } - - match self.overlay { - None => true, - Some(ref overlay) => { - match overlay.contents { - PaintContents::Gradient(ref gradient) => gradient.is_fully_transparent(), - PaintContents::Pattern(_) => false, - } - } - } - } - - #[inline] - pub fn is_color(&self) -> bool { - self.overlay.is_none() - } - - pub fn apply_transform(&mut self, transform: &Transform2F) { - if transform.is_identity() { - return; - } - - if let Some(ref mut overlay) = self.overlay { - match overlay.contents { - PaintContents::Gradient(ref mut gradient) => gradient.apply_transform(*transform), - PaintContents::Pattern(ref mut pattern) => pattern.apply_transform(*transform), - } - } - } - - #[inline] - pub fn base_color(&self) -> ColorU { - self.base_color - } - - #[inline] - pub fn set_base_color(&mut self, new_base_color: ColorU) { - self.base_color = new_base_color; - } - - #[inline] - pub fn overlay(&self) -> &Option { - &self.overlay - } - - #[inline] - pub fn overlay_mut(&mut self) -> &mut Option { - &mut self.overlay - } - - #[inline] - pub fn pattern(&self) -> Option<&Pattern> { - match self.overlay { - None => None, - Some(ref overlay) => { - match overlay.contents { - PaintContents::Pattern(ref pattern) => Some(pattern), - _ => None, - } - } - } - } - - #[inline] - pub fn pattern_mut(&mut self) -> Option<&mut Pattern> { - match self.overlay { - None => None, - Some(ref mut overlay) => { - match overlay.contents { - PaintContents::Pattern(ref mut pattern) => Some(pattern), - _ => None, - } - } - } - } - - #[inline] - pub fn gradient(&self) -> Option<&Gradient> { - match self.overlay { - None => None, - Some(ref overlay) => { - match overlay.contents { - PaintContents::Gradient(ref gradient) => Some(gradient), - _ => None, - } - } - } - } -} - -impl PaintOverlay { - #[inline] - pub fn contents(&self) -> &PaintContents { - &self.contents - } - - #[inline] - pub fn composite_op(&self) -> PaintCompositeOp { - self.composite_op - } - - #[inline] - pub fn set_composite_op(&mut self, new_composite_op: PaintCompositeOp) { - self.composite_op = new_composite_op - } -} - -pub struct PaintInfo { - /// The render commands needed to prepare the textures. - pub render_commands: Vec, - /// The metadata for each paint. - /// - /// The indices of this vector are paint IDs. - pub paint_metadata: Vec, - /// The metadata for each render target. - /// - /// The indices of this vector are render target IDs. - pub render_target_metadata: Vec, -} - -#[derive(Debug)] -pub struct PaintMetadata { - /// Metadata associated with the color texture, if applicable. - pub color_texture_metadata: Option, - /// The base color that the color texture gets mixed into. - pub base_color: ColorU, - /// True if this paint is fully opaque. - pub is_opaque: bool, -} - -#[derive(Debug)] -pub struct PaintColorTextureMetadata { - /// The location of the paint. - pub location: TextureLocation, - /// The scale for the page this paint is on. - pub page_scale: Vector2F, - /// The transform to apply to screen coordinates to translate them into UVs. - pub transform: Transform2F, - /// The sampling mode for the texture. - pub sampling_flags: TextureSamplingFlags, - /// The filter to be applied to this paint. - pub filter: PaintFilter, - /// How the color texture is to be composited over the base color. - pub composite_op: PaintCompositeOp, -} - -#[derive(Clone, Copy, Debug)] -pub struct RadialGradientMetadata { - /// The line segment that connects the two circles. - pub line: LineSegment2F, - /// The radii of the two circles. - pub radii: F32x2, -} - -#[derive(Clone, Copy, Debug)] -pub struct RenderTargetMetadata { - /// The location of the render target. - pub location: TextureLocation, -} - -#[derive(Debug)] -pub enum PaintFilter { - None, - RadialGradient { - /// The line segment that connects the two circles. - line: LineSegment2F, - /// The radii of the two circles. - radii: F32x2, - }, - PatternFilter(PatternFilter), -} - -impl Palette { - #[allow(clippy::trivially_copy_pass_by_ref)] - pub fn push_paint(&mut self, paint: &Paint) -> PaintId { - if let Some(paint_id) = self.cache.get(paint) { - return *paint_id; - } - - let paint_id = PaintId(self.paints.len() as u16); - self.cache.insert((*paint).clone(), paint_id); - self.paints.push((*paint).clone()); - paint_id - } - - pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { - let id = self.render_targets.len() as u32; - - let metadata = RenderTargetMetadata { - location: self.allocator.allocate_image(render_target.size()), - }; - - self.render_targets.push(RenderTargetData { render_target, metadata }); - RenderTargetId { scene: self.scene_id.0, render_target: id } - } - - pub fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo { - let mut paint_metadata = vec![]; - - // Assign paint locations. - let mut gradient_tile_builder = GradientTileBuilder::new(); - let mut image_texel_info = vec![]; - for paint in &self.paints { - let allocator = &mut self.allocator; - let render_targets = &self.render_targets; - let color_texture_metadata = paint.overlay.as_ref().map(|overlay| { - match overlay.contents { - PaintContents::Gradient(ref gradient) => { - // FIXME(pcwalton): The gradient size might not be big enough. Detect this. - let location = gradient_tile_builder.allocate(allocator, gradient); - PaintColorTextureMetadata { - location, - page_scale: allocator.page_scale(location.page), - sampling_flags: TextureSamplingFlags::empty(), - filter: match gradient.geometry { - GradientGeometry::Linear(_) => PaintFilter::None, - GradientGeometry::Radial { line, radii, .. } => { - PaintFilter::RadialGradient { line, radii } - } - }, - transform: Transform2F::default(), - composite_op: overlay.composite_op(), - } - } - PaintContents::Pattern(ref pattern) => { - let location; - match *pattern.source() { - PatternSource::RenderTarget { id: render_target_id, .. } => { - let index = render_target_id.render_target as usize; - location = render_targets[index].metadata.location; - } - PatternSource::Image(ref image) => { - // TODO(pcwalton): We should be able to use tile cleverness to - // repeat inside the atlas in some cases. - let allocation_mode = AllocationMode::OwnPage; - location = allocator.allocate(image.size(), allocation_mode); - image_texel_info.push(ImageTexelInfo { - location, - texels: (*image.pixels()).clone(), - }); - } - } - - let mut sampling_flags = TextureSamplingFlags::empty(); - if pattern.repeat_x() { - sampling_flags.insert(TextureSamplingFlags::REPEAT_U); - } - if pattern.repeat_y() { - sampling_flags.insert(TextureSamplingFlags::REPEAT_V); - } - if !pattern.smoothing_enabled() { - sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | - TextureSamplingFlags::NEAREST_MAG); - } - - let filter = match pattern.filter() { - None => PaintFilter::None, - Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter), - }; - - PaintColorTextureMetadata { - location, - page_scale: allocator.page_scale(location.page), - sampling_flags, - filter, - transform: Transform2F::default(), - composite_op: overlay.composite_op(), - } - } - } - }); - - paint_metadata.push(PaintMetadata { - color_texture_metadata, - is_opaque: paint.is_opaque(), - base_color: paint.base_color(), - }); - } - - // Calculate texture transforms. - for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) { - let mut color_texture_metadata = match metadata.color_texture_metadata { - None => continue, - Some(ref mut color_texture_metadata) => color_texture_metadata, - }; - - let texture_scale = self.allocator.page_scale(color_texture_metadata.location.page); - let texture_rect = color_texture_metadata.location.rect; - color_texture_metadata.transform = match paint.overlay - .as_ref() - .expect("Why do we have color texture \ - metadata but no overlay?") - .contents { - PaintContents::Gradient(Gradient { - geometry: GradientGeometry::Linear(gradient_line), - .. - }) => { - // Project gradient line onto (0.0-1.0, v0). - let v0 = texture_rect.to_f32().center().y() * texture_scale.y(); - let dp = gradient_line.vector(); - let m0 = dp.0.concat_xy_xy(dp.0) / F32x4::splat(gradient_line.square_length()); - let m13 = m0.zw() * -gradient_line.from().0; - Transform2F::row_major(m0.x(), m0.y(), m13.x() + m13.y(), 0.0, 0.0, v0) - } - PaintContents::Gradient(Gradient { - geometry: GradientGeometry::Radial { ref transform, .. }, - .. - }) => transform.inverse(), - PaintContents::Pattern(ref pattern) => { - match pattern.source() { - PatternSource::Image(_) => { - let texture_origin_uv = - rect_to_uv(texture_rect, texture_scale).origin(); - Transform2F::from_scale(texture_scale).translate(texture_origin_uv) * - pattern.transform().inverse() - } - PatternSource::RenderTarget { .. } => { - // FIXME(pcwalton): Only do this in GL, not Metal! - let texture_origin_uv = - rect_to_uv(texture_rect, texture_scale).lower_left(); - Transform2F::from_translation(texture_origin_uv) * - Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) * - pattern.transform().inverse() - } - } - } - }; - color_texture_metadata.transform *= render_transform; - } - - // Create texture metadata. - let texture_metadata = paint_metadata.iter().map(|paint_metadata| { - TextureMetadataEntry { - color_0_transform: match paint_metadata.color_texture_metadata { - None => Transform2F::default(), - Some(ref color_texture_metadata) => color_texture_metadata.transform, - }, - base_color: paint_metadata.base_color, - } - }).collect(); - let mut render_commands = vec![RenderCommand::UploadTextureMetadata(texture_metadata)]; - - // Allocate textures. - let mut texture_page_descriptors = vec![]; - for page_index in 0..self.allocator.page_count() { - let page_id = TexturePageId(page_index); - let page_size = self.allocator.page_size(page_id); - let descriptor = TexturePageDescriptor { size: page_size }; - texture_page_descriptors.push(descriptor); - - if self.allocator.page_is_new(page_id) { - render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor }); - self.allocator.mark_page_as_allocated(page_id); - } - } - - // Gather up render target metadata. - let render_target_metadata: Vec<_> = self.render_targets.iter().map(|render_target_data| { - render_target_data.metadata - }).collect(); - - // Create render commands. - for (index, metadata) in render_target_metadata.iter().enumerate() { - let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 }; - render_commands.push(RenderCommand::DeclareRenderTarget { - id, - location: metadata.location, - }); - } - gradient_tile_builder.create_render_commands(&mut render_commands); - for image_texel_info in image_texel_info { - render_commands.push(RenderCommand::UploadTexelData { - texels: image_texel_info.texels, - location: image_texel_info.location, - }); - } - - PaintInfo { render_commands, paint_metadata, render_target_metadata } - } - - pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo { - // Merge render targets. - let mut render_target_mapping = HashMap::new(); - for (old_render_target_index, render_target) in palette.render_targets - .into_iter() - .enumerate() { - let old_render_target_id = RenderTargetId { - scene: palette.scene_id.0, - render_target: old_render_target_index as u32, - }; - let new_render_target_id = self.push_render_target(render_target.render_target); - render_target_mapping.insert(old_render_target_id, new_render_target_id); - } - - // Merge paints. - let mut paint_mapping = HashMap::new(); - for (old_paint_index, old_paint) in palette.paints.iter().enumerate() { - let old_paint_id = PaintId(old_paint_index as u16); - let new_paint_id = match *old_paint.overlay() { - None => self.push_paint(old_paint), - Some(ref overlay) => { - match *overlay.contents() { - PaintContents::Pattern(ref pattern) => { - match pattern.source() { - PatternSource::RenderTarget { id: old_render_target_id, size } => { - let mut new_pattern = - Pattern::from_render_target(*old_render_target_id, *size); - new_pattern.set_filter(pattern.filter()); - new_pattern.apply_transform(pattern.transform()); - new_pattern.set_repeat_x(pattern.repeat_x()); - new_pattern.set_repeat_y(pattern.repeat_y()); - new_pattern.set_smoothing_enabled(pattern.smoothing_enabled()); - self.push_paint(&Paint::from_pattern(new_pattern)) - } - _ => self.push_paint(old_paint), - } - } - _ => self.push_paint(old_paint), - } - } - }; - paint_mapping.insert(old_paint_id, new_paint_id); - } - - MergedPaletteInfo { render_target_mapping, paint_mapping } - } -} - -pub(crate) struct MergedPaletteInfo { - pub(crate) render_target_mapping: HashMap, - pub(crate) paint_mapping: HashMap, -} - -impl PaintMetadata { - pub(crate) fn filter(&self) -> Filter { - match self.color_texture_metadata { - None => Filter::None, - Some(ref color_metadata) => { - match color_metadata.filter { - PaintFilter::None => Filter::None, - PaintFilter::RadialGradient { line, radii } => { - let uv_rect = rect_to_uv(color_metadata.location.rect, - color_metadata.page_scale).contract( - vec2f(0.0, color_metadata.page_scale.y() * 0.5)); - Filter::RadialGradient { line, radii, uv_origin: uv_rect.origin() } - } - PaintFilter::PatternFilter(pattern_filter) => { - Filter::PatternFilter(pattern_filter) - } - } - } - } - } - - pub(crate) fn tile_batch_texture(&self) -> Option { - self.color_texture_metadata.as_ref().map(PaintColorTextureMetadata::as_tile_batch_texture) - } -} - -fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF { - rect.to_f32() * texture_scale -} - -// Gradient allocation - -struct GradientTileBuilder { - tiles: Vec, -} - -struct GradientTile { - texels: Vec, - page: TexturePageId, - next_index: u32, -} - -impl GradientTileBuilder { - fn new() -> GradientTileBuilder { - GradientTileBuilder { tiles: vec![] } - } - - fn allocate(&mut self, allocator: &mut TextureAllocator, gradient: &Gradient) - -> TextureLocation { - if self.tiles.is_empty() || - self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH { - let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32); - let area = size.x() as usize * size.y() as usize; - self.tiles.push(GradientTile { - texels: vec![ColorU::black(); area], - page: allocator.allocate(size, AllocationMode::OwnPage).page, - next_index: 0, - }) - } - - let mut data = self.tiles.last_mut().unwrap(); - let location = TextureLocation { - page: data.page, - rect: RectI::new(vec2i(0, data.next_index as i32), - vec2i(GRADIENT_TILE_LENGTH as i32, 1)), - }; - data.next_index += 1; - - // FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec. - // TODO(pcwalton): Optimize this: - // 1. Calculate ∇t up front and use differencing in the inner loop. - // 2. Go four pixels at a time with SIMD. - let first_address = location.rect.origin_y() as usize * GRADIENT_TILE_LENGTH as usize; - for x in 0..(GRADIENT_TILE_LENGTH as i32) { - let t = (x as f32 + 0.5) / GRADIENT_TILE_LENGTH as f32; - data.texels[first_address + x as usize] = gradient.sample(t); - } - - location - } - - fn create_render_commands(self, render_commands: &mut Vec) { - for tile in self.tiles { - render_commands.push(RenderCommand::UploadTexelData { - texels: Arc::new(tile.texels), - location: TextureLocation { - rect: RectI::new(vec2i(0, 0), Vector2I::splat(GRADIENT_TILE_LENGTH as i32)), - page: tile.page, - }, - }); - } - } -} - -struct ImageTexelInfo { - location: TextureLocation, - texels: Arc>, -} - -impl PaintColorTextureMetadata { - pub(crate) fn as_tile_batch_texture(&self) -> TileBatchTexture { - TileBatchTexture { - page: self.location.page, - sampling_flags: self.sampling_flags, - composite_op: self.composite_op, - } - } -} diff --git a/crates/pathfinder/renderer/src/scene.rs b/crates/pathfinder/renderer/src/scene.rs deleted file mode 100644 index 8a30e914bd..0000000000 --- a/crates/pathfinder/renderer/src/scene.rs +++ /dev/null @@ -1,419 +0,0 @@ -// pathfinder/renderer/src/scene.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A set of paths to be rendered. - -use crate::builder::SceneBuilder; -use crate::concurrent::executor::Executor; -use crate::options::{BuildOptions, PreparedBuildOptions}; -use crate::options::{PreparedRenderTransform, RenderCommandListener}; -use crate::paint::{MergedPaletteInfo, Paint, PaintId, PaintInfo, Palette}; -use pathfinder_content::effects::BlendMode; -use pathfinder_content::fill::FillRule; -use pathfinder_content::outline::Outline; -use pathfinder_content::render_target::RenderTargetId; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2F; -use pathfinder_geometry::vector::{Vector2I, vec2f}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -static NEXT_SCENE_ID: AtomicUsize = AtomicUsize::new(0); - -#[derive(Clone)] -pub struct Scene { - pub(crate) display_list: Vec, - pub(crate) paths: Vec, - pub(crate) clip_paths: Vec, - palette: Palette, - bounds: RectF, - view_box: RectF, - id: SceneId, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct SceneId(pub u32); - -impl Scene { - #[inline] - pub fn new() -> Scene { - let scene_id = SceneId(NEXT_SCENE_ID.fetch_add(1, Ordering::Relaxed) as u32); - Scene { - display_list: vec![], - paths: vec![], - clip_paths: vec![], - palette: Palette::new(scene_id), - bounds: RectF::default(), - view_box: RectF::default(), - id: scene_id, - } - } - - pub fn push_path(&mut self, path: DrawPath) { - let path_index = self.paths.len() as u32; - self.paths.push(path); - self.push_path_with_index(path_index); - } - - fn push_path_with_index(&mut self, path_index: u32) { - self.bounds = self.bounds.union_rect(self.paths[path_index as usize].outline.bounds()); - - if let Some(DisplayItem::DrawPaths { - start_index: _, - ref mut end_index - }) = self.display_list.last_mut() { - *end_index = path_index + 1; - } else { - self.display_list.push(DisplayItem::DrawPaths { - start_index: path_index, - end_index: path_index + 1, - }); - } - } - - pub fn push_clip_path(&mut self, clip_path: ClipPath) -> ClipPathId { - self.bounds = self.bounds.union_rect(clip_path.outline.bounds()); - let clip_path_id = ClipPathId(self.clip_paths.len() as u32); - self.clip_paths.push(clip_path); - clip_path_id - } - - pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId { - let render_target_id = self.palette.push_render_target(render_target); - self.display_list.push(DisplayItem::PushRenderTarget(render_target_id)); - render_target_id - } - - pub fn pop_render_target(&mut self) { - self.display_list.push(DisplayItem::PopRenderTarget); - } - - pub fn append_scene(&mut self, scene: Scene) { - let MergedPaletteInfo { - render_target_mapping, - paint_mapping, - } = self.palette.append_palette(scene.palette); - - // Merge clip paths. - let mut clip_path_mapping = Vec::with_capacity(scene.clip_paths.len()); - for clip_path in scene.clip_paths { - clip_path_mapping.push(self.clip_paths.len()); - self.clip_paths.push(clip_path); - } - - // Merge draw paths. - let mut draw_path_mapping = Vec::with_capacity(scene.paths.len()); - for draw_path in scene.paths { - draw_path_mapping.push(self.paths.len() as u32); - self.paths.push(DrawPath { - outline: draw_path.outline, - paint: paint_mapping[&draw_path.paint], - clip_path: draw_path.clip_path.map(|clip_path_id| { - ClipPathId(clip_path_mapping[clip_path_id.0 as usize] as u32) - }), - fill_rule: draw_path.fill_rule, - blend_mode: draw_path.blend_mode, - name: draw_path.name, - }); - } - - // Merge display items. - for display_item in scene.display_list { - match display_item { - DisplayItem::PushRenderTarget(old_render_target_id) => { - let new_render_target_id = render_target_mapping[&old_render_target_id]; - self.display_list.push(DisplayItem::PushRenderTarget(new_render_target_id)); - } - DisplayItem::PopRenderTarget => { - self.display_list.push(DisplayItem::PopRenderTarget); - } - DisplayItem::DrawPaths { - start_index: old_start_path_index, - end_index: old_end_path_index, - } => { - for old_path_index in old_start_path_index..old_end_path_index { - self.push_path_with_index(draw_path_mapping[old_path_index as usize]) - } - } - } - } - } - - #[inline] - pub fn build_paint_info(&mut self, render_transform: Transform2F) -> PaintInfo { - self.palette.build_paint_info(render_transform) - } - - #[allow(clippy::trivially_copy_pass_by_ref)] - pub fn push_paint(&mut self, paint: &Paint) -> PaintId { - self.palette.push_paint(paint) - } - - #[inline] - pub fn path_count(&self) -> usize { - self.paths.len() - } - - #[inline] - pub fn bounds(&self) -> RectF { - self.bounds - } - - #[inline] - pub fn set_bounds(&mut self, new_bounds: RectF) { - self.bounds = new_bounds; - } - - #[inline] - pub fn view_box(&self) -> RectF { - self.view_box - } - - #[inline] - pub fn set_view_box(&mut self, new_view_box: RectF) { - self.view_box = new_view_box; - } - - pub(crate) fn apply_render_options( - &self, - original_outline: &Outline, - options: &PreparedBuildOptions, - ) -> Outline { - let effective_view_box = self.effective_view_box(options); - - let mut outline; - match options.transform { - PreparedRenderTransform::Perspective { - ref perspective, - ref clip_polygon, - .. - } => { - if original_outline.is_outside_polygon(clip_polygon) { - outline = Outline::new(); - } else { - outline = (*original_outline).clone(); - outline.close_all_contours(); - outline.clip_against_polygon(clip_polygon); - outline.apply_perspective(perspective); - - // TODO(pcwalton): Support subpixel AA in 3D. - } - } - _ => { - // TODO(pcwalton): Short circuit. - outline = (*original_outline).clone(); - outline.close_all_contours(); - if options.transform.is_2d() || options.subpixel_aa_enabled { - let mut transform = match options.transform { - PreparedRenderTransform::Transform2D(transform) => transform, - PreparedRenderTransform::None => Transform2F::default(), - PreparedRenderTransform::Perspective { .. } => unreachable!(), - }; - if options.subpixel_aa_enabled { - transform *= Transform2F::from_scale(vec2f(3.0, 1.0)) - } - outline.transform(&transform); - } - outline.clip_against_rect(effective_view_box); - } - } - - if !options.dilation.is_zero() { - outline.dilate(options.dilation); - } - - // TODO(pcwalton): Fold this into previous passes to avoid unnecessary clones during - // monotonic conversion. - outline.prepare_for_tiling(self.effective_view_box(options)); - outline - } - - #[inline] - pub(crate) fn effective_view_box(&self, render_options: &PreparedBuildOptions) -> RectF { - if render_options.subpixel_aa_enabled { - self.view_box * vec2f(3.0, 1.0) - } else { - self.view_box - } - } - - #[inline] - pub fn build(&mut self, - options: BuildOptions, - listener: Box, - executor: &E) - where E: Executor { - let prepared_options = options.prepare(self.bounds); - SceneBuilder::new(self, &prepared_options, listener).build(executor) - } - - pub fn paths<'a>(&'a self) -> PathIter { - PathIter { - scene: self, - pos: 0 - } - } -} - -pub struct PathIter<'a> { - scene: &'a Scene, - pos: usize -} - -impl<'a> Iterator for PathIter<'a> { - type Item = (&'a Paint, &'a Outline, &'a str); - fn next(&mut self) -> Option { - let item = self.scene.paths.get(self.pos).map(|path_object| { - ( - self.scene.palette.paints.get(path_object.paint.0 as usize).unwrap(), - &path_object.outline, - &*path_object.name - ) - }); - self.pos += 1; - item - } -} - -#[derive(Clone, Debug)] -pub struct DrawPath { - outline: Outline, - paint: PaintId, - clip_path: Option, - fill_rule: FillRule, - blend_mode: BlendMode, - name: String, -} - -#[derive(Clone, Debug)] -pub struct ClipPath { - outline: Outline, - fill_rule: FillRule, - name: String, -} - -#[derive(Clone, Copy, Debug)] -pub struct ClipPathId(pub u32); - -#[derive(Clone, Debug)] -pub struct RenderTarget { - size: Vector2I, - name: String, -} - -/// Drawing commands. -#[derive(Clone, Debug)] -pub enum DisplayItem { - /// Draws paths to the render target on top of the stack. - DrawPaths { start_index: u32, end_index: u32 }, - - /// Pushes a render target onto the top of the stack. - PushRenderTarget(RenderTargetId), - - /// Pops a render target from the stack. - PopRenderTarget, -} - -impl DrawPath { - #[inline] - pub fn new(outline: Outline, paint: PaintId) -> DrawPath { - DrawPath { - outline, - paint, - clip_path: None, - fill_rule: FillRule::Winding, - blend_mode: BlendMode::SrcOver, - name: String::new(), - } - } - - #[inline] - pub fn outline(&self) -> &Outline { - &self.outline - } - - #[inline] - pub(crate) fn clip_path(&self) -> Option { - self.clip_path - } - - #[inline] - pub fn set_clip_path(&mut self, new_clip_path: Option) { - self.clip_path = new_clip_path - } - - #[inline] - pub(crate) fn paint(&self) -> PaintId { - self.paint - } - - #[inline] - pub(crate) fn fill_rule(&self) -> FillRule { - self.fill_rule - } - - #[inline] - pub fn set_fill_rule(&mut self, new_fill_rule: FillRule) { - self.fill_rule = new_fill_rule - } - - #[inline] - pub(crate) fn blend_mode(&self) -> BlendMode { - self.blend_mode - } - - #[inline] - pub fn set_blend_mode(&mut self, new_blend_mode: BlendMode) { - self.blend_mode = new_blend_mode - } - - #[inline] - pub fn set_name(&mut self, new_name: String) { - self.name = new_name - } -} - -impl ClipPath { - #[inline] - pub fn new(outline: Outline) -> ClipPath { - ClipPath { outline, fill_rule: FillRule::Winding, name: String::new() } - } - - #[inline] - pub fn outline(&self) -> &Outline { - &self.outline - } - - #[inline] - pub(crate) fn fill_rule(&self) -> FillRule { - self.fill_rule - } - - #[inline] - pub fn set_fill_rule(&mut self, new_fill_rule: FillRule) { - self.fill_rule = new_fill_rule - } - - #[inline] - pub fn set_name(&mut self, new_name: String) { - self.name = new_name - } -} - -impl RenderTarget { - #[inline] - pub fn new(size: Vector2I, name: String) -> RenderTarget { - RenderTarget { size, name } - } - - #[inline] - pub fn size(&self) -> Vector2I { - self.size - } -} diff --git a/crates/pathfinder/renderer/src/tile_map.rs b/crates/pathfinder/renderer/src/tile_map.rs deleted file mode 100644 index 8b86a8589d..0000000000 --- a/crates/pathfinder/renderer/src/tile_map.rs +++ /dev/null @@ -1,70 +0,0 @@ -// pathfinder/renderer/src/tile_map.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::vector::{Vector2I, vec2i}; - -#[derive(Debug)] -pub struct DenseTileMap { - pub data: Vec, - pub rect: RectI, -} - -impl DenseTileMap { - #[inline] - pub fn new(rect: RectI) -> DenseTileMap - where - T: Copy + Clone + Default, - { - let length = rect.size().x() as usize * rect.size().y() as usize; - DenseTileMap { - data: vec![T::default(); length], - rect, - } - } - - #[inline] - pub fn from_builder(build: F, rect: RectI) -> DenseTileMap - where - F: FnMut(usize) -> T, - { - let length = rect.size().x() as usize * rect.size().y() as usize; - DenseTileMap { - data: (0..length).map(build).collect(), - rect, - } - } - - #[inline] - pub fn get(&self, coords: Vector2I) -> Option<&T> { - self.coords_to_index(coords).and_then(|index| self.data.get(index)) - } - - #[inline] - pub fn coords_to_index(&self, coords: Vector2I) -> Option { - if self.rect.contains_point(coords) { - Some(self.coords_to_index_unchecked(coords)) - } else { - None - } - } - - #[inline] - pub fn coords_to_index_unchecked(&self, coords: Vector2I) -> usize { - (coords.y() - self.rect.min_y()) as usize * self.rect.size().x() as usize - + (coords.x() - self.rect.min_x()) as usize - } - - #[inline] - pub fn index_to_coords(&self, index: usize) -> Vector2I { - let (width, index) = (self.rect.size().x(), index as i32); - self.rect.origin() + vec2i(index % width, index / width) - } -} diff --git a/crates/pathfinder/renderer/src/tiles.rs b/crates/pathfinder/renderer/src/tiles.rs deleted file mode 100644 index 678e60191f..0000000000 --- a/crates/pathfinder/renderer/src/tiles.rs +++ /dev/null @@ -1,701 +0,0 @@ -// pathfinder/renderer/src/tiles.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::builder::{BuiltPath, ObjectBuilder, Occluder, SceneBuilder, SolidTiles}; -use crate::gpu_data::{AlphaTileId, TileObjectPrimitive}; -use crate::paint::{PaintId, PaintMetadata}; -use pathfinder_content::effects::BlendMode; -use pathfinder_content::fill::FillRule; -use pathfinder_content::outline::{Contour, Outline, PointIndex}; -use pathfinder_content::segment::Segment; -use pathfinder_content::sorted_vector::SortedVector; -use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::rect::{RectF, RectI}; -use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i}; -use std::cmp::Ordering; -use std::mem; - -// TODO(pcwalton): Make this configurable. -const FLATTENING_TOLERANCE: f32 = 0.1; - -pub const TILE_WIDTH: u32 = 16; -pub const TILE_HEIGHT: u32 = 16; - -pub(crate) struct Tiler<'a, 'b> { - scene_builder: &'a SceneBuilder<'b, 'a>, - pub(crate) object_builder: ObjectBuilder, - outline: &'a Outline, - path_info: TilingPathInfo<'a>, - - point_queue: SortedVector, - active_edges: SortedVector, - old_active_edges: Vec, -} - -#[derive(Clone, Copy)] -pub(crate) enum TilingPathInfo<'a> { - Clip, - Draw(DrawTilingPathInfo<'a>), -} - -#[derive(Clone, Copy)] -pub(crate) struct DrawTilingPathInfo<'a> { - pub(crate) paint_id: PaintId, - pub(crate) paint_metadata: &'a PaintMetadata, - pub(crate) blend_mode: BlendMode, - pub(crate) built_clip_path: Option<&'a BuiltPath>, - pub(crate) fill_rule: FillRule, -} - -impl<'a, 'b> Tiler<'a, 'b> { - #[allow(clippy::or_fun_call)] - pub(crate) fn new( - scene_builder: &'a SceneBuilder<'b, 'a>, - outline: &'a Outline, - fill_rule: FillRule, - view_box: RectF, - path_info: TilingPathInfo<'a>, - ) -> Tiler<'a, 'b> { - let bounds = outline - .bounds() - .intersection(view_box) - .unwrap_or(RectF::default()); - let object_builder = ObjectBuilder::new(bounds, view_box, fill_rule, &path_info); - - Tiler { - scene_builder, - object_builder, - outline, - path_info, - - point_queue: SortedVector::new(), - active_edges: SortedVector::new(), - old_active_edges: vec![], - } - } - - pub(crate) fn generate_tiles(&mut self) { - // Initialize the point queue. - self.init_point_queue(); - - // Reset active edges. - self.active_edges.clear(); - self.old_active_edges.clear(); - - // Generate strips. - let tile_rect = self.object_builder.tile_rect(); - for strip_origin_y in tile_rect.min_y()..tile_rect.max_y() { - self.generate_strip(strip_origin_y); - } - - // Pack and cull. - self.pack_and_cull(); - - // Done! - debug!("{:#?}", self.object_builder.built_path); - } - - fn generate_strip(&mut self, strip_origin_y: i32) { - // Process old active edges. - self.process_old_active_edges(strip_origin_y); - - // Add new active edges. - let strip_max_y = ((i32::from(strip_origin_y) + 1) * TILE_HEIGHT as i32) as f32; - while let Some(queued_endpoint) = self.point_queue.peek() { - // We're done when we see an endpoint that belongs to the next tile strip. - // - // Note that this test must be `>`, not `>=`, in order to make sure we don't miss - // active edges that lie precisely on the tile strip boundary. - if queued_endpoint.y > strip_max_y { - break; - } - - self.add_new_active_edge(strip_origin_y); - } - } - - fn pack_and_cull(&mut self) { - let draw_tiling_path_info = match self.path_info { - TilingPathInfo::Clip => return, - TilingPathInfo::Draw(draw_tiling_path_info) => draw_tiling_path_info, - }; - - let blend_mode_is_destructive = draw_tiling_path_info.blend_mode.is_destructive(); - - for (draw_tile_index, draw_tile) in self.object_builder - .built_path - .tiles - .data - .iter() - .enumerate() { - let packed_tile = PackedTile::new(draw_tile_index as u32, - draw_tile, - &draw_tiling_path_info, - &self.object_builder); - - match packed_tile.tile_type { - TileType::Solid => { - match self.object_builder.built_path.solid_tiles { - SolidTiles::Occluders(ref mut occluders) => { - occluders.push(Occluder::new(packed_tile.tile_coords)); - } - SolidTiles::Regular(ref mut solid_tiles) => { - packed_tile.add_to(solid_tiles, - &mut self.object_builder.built_path.clip_tiles, - &draw_tiling_path_info, - &self.scene_builder); - } - } - } - TileType::SingleMask => { - debug_assert_ne!(packed_tile.draw_tile.alpha_tile_id.page(), !0); - packed_tile.add_to(&mut self.object_builder.built_path.single_mask_tiles, - &mut self.object_builder.built_path.clip_tiles, - &draw_tiling_path_info, - &self.scene_builder); - } - TileType::Empty if blend_mode_is_destructive => { - packed_tile.add_to(&mut self.object_builder.built_path.empty_tiles, - &mut self.object_builder.built_path.clip_tiles, - &draw_tiling_path_info, - &self.scene_builder); - } - TileType::Empty => { - // Just cull. - } - } - } - } - - fn process_old_active_edges(&mut self, tile_y: i32) { - let mut current_tile_x = self.object_builder.tile_rect().min_x(); - let mut current_subtile_x = 0.0; - let mut current_winding = 0; - - debug_assert!(self.old_active_edges.is_empty()); - mem::swap(&mut self.old_active_edges, &mut self.active_edges.array); - - // FIXME(pcwalton): Yuck. - let mut last_segment_x = -9999.0; - - let tile_top = (i32::from(tile_y) * TILE_HEIGHT as i32) as f32; - - debug!("---------- tile y {}({}) ----------", tile_y, tile_top); - debug!("old active edges: {:#?}", self.old_active_edges); - - for mut active_edge in self.old_active_edges.drain(..) { - // Determine x-intercept and winding. - let segment_x = active_edge.crossing.x(); - let edge_winding = - if active_edge.segment.baseline.from_y() < active_edge.segment.baseline.to_y() { - 1 - } else { - -1 - }; - - debug!( - "tile Y {}({}): segment_x={} edge_winding={} current_tile_x={} \ - current_subtile_x={} current_winding={}", - tile_y, - tile_top, - segment_x, - edge_winding, - current_tile_x, - current_subtile_x, - current_winding - ); - debug!( - "... segment={:#?} crossing={:?}", - active_edge.segment, active_edge.crossing - ); - - // FIXME(pcwalton): Remove this debug code! - debug_assert!(segment_x >= last_segment_x); - last_segment_x = segment_x; - - // Do initial subtile fill, if necessary. - let segment_tile_x = f32::floor(segment_x) as i32 / TILE_WIDTH as i32; - if current_tile_x < segment_tile_x && current_subtile_x > 0.0 { - let current_x = - (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x; - let tile_right_x = ((i32::from(current_tile_x) + 1) * TILE_WIDTH as i32) as f32; - let current_tile_coords = vec2i(current_tile_x, tile_y); - self.object_builder.add_active_fill( - self.scene_builder, - current_x, - tile_right_x, - current_winding, - current_tile_coords, - ); - current_tile_x += 1; - current_subtile_x = 0.0; - } - - // Move over to the correct tile, filling in as we go. - while current_tile_x < segment_tile_x { - debug!( - "... emitting backdrop {} @ tile {}", - current_winding, current_tile_x - ); - let current_tile_coords = vec2i(current_tile_x, tile_y); - if let Some(tile_index) = self.object_builder - .tile_coords_to_local_index(current_tile_coords) { - // FIXME(pcwalton): Handle winding overflow. - self.object_builder.built_path.tiles.data[tile_index as usize].backdrop = - current_winding as i8; - } - - current_tile_x += 1; - current_subtile_x = 0.0; - } - - // Do final subtile fill, if necessary. - debug_assert_eq!(current_tile_x, segment_tile_x); - let segment_subtile_x = - segment_x - (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32; - if segment_subtile_x > current_subtile_x { - let current_x = - (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x; - let current_tile_coords = vec2i(current_tile_x, tile_y); - self.object_builder.add_active_fill( - self.scene_builder, - current_x, - segment_x, - current_winding, - current_tile_coords, - ); - current_subtile_x = segment_subtile_x; - } - - // Update winding. - current_winding += edge_winding; - - // Process the edge. - debug!("about to process existing active edge {:#?}", active_edge); - debug_assert!(f32::abs(active_edge.crossing.y() - tile_top) < 0.1); - active_edge.process(self.scene_builder, &mut self.object_builder, tile_y); - if !active_edge.segment.is_none() { - self.active_edges.push(active_edge); - } - } - } - - fn add_new_active_edge(&mut self, tile_y: i32) { - let outline = &self.outline; - let point_index = self.point_queue.pop().unwrap().point_index; - - let contour = &outline.contours()[point_index.contour() as usize]; - - // TODO(pcwalton): Could use a bitset of processed edges… - let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point()); - let next_endpoint_index = contour.next_endpoint_index_of(point_index.point()); - - debug!( - "adding new active edge, tile_y={} point_index={} prev={} next={} pos={:?} \ - prevpos={:?} nextpos={:?}", - tile_y, - point_index.point(), - prev_endpoint_index, - next_endpoint_index, - contour.position_of(point_index.point()), - contour.position_of(prev_endpoint_index), - contour.position_of(next_endpoint_index) - ); - - if contour.point_is_logically_above(point_index.point(), prev_endpoint_index) { - debug!("... adding prev endpoint"); - - process_active_segment( - contour, - prev_endpoint_index, - &mut self.active_edges, - self.scene_builder, - &mut self.object_builder, - tile_y, - ); - - self.point_queue.push(QueuedEndpoint { - point_index: PointIndex::new(point_index.contour(), prev_endpoint_index), - y: contour.position_of(prev_endpoint_index).y(), - }); - - debug!("... done adding prev endpoint"); - } - - if contour.point_is_logically_above(point_index.point(), next_endpoint_index) { - debug!( - "... adding next endpoint {} -> {}", - point_index.point(), - next_endpoint_index - ); - - process_active_segment( - contour, - point_index.point(), - &mut self.active_edges, - self.scene_builder, - &mut self.object_builder, - tile_y, - ); - - self.point_queue.push(QueuedEndpoint { - point_index: PointIndex::new(point_index.contour(), next_endpoint_index), - y: contour.position_of(next_endpoint_index).y(), - }); - - debug!("... done adding next endpoint"); - } - } - - fn init_point_queue(&mut self) { - // Find MIN points. - self.point_queue.clear(); - for (contour_index, contour) in self.outline.contours().iter().enumerate() { - let contour_index = contour_index as u32; - let mut cur_endpoint_index = 0; - let mut prev_endpoint_index = contour.prev_endpoint_index_of(cur_endpoint_index); - let mut next_endpoint_index = contour.next_endpoint_index_of(cur_endpoint_index); - loop { - if contour.point_is_logically_above(cur_endpoint_index, prev_endpoint_index) - && contour.point_is_logically_above(cur_endpoint_index, next_endpoint_index) - { - self.point_queue.push(QueuedEndpoint { - point_index: PointIndex::new(contour_index, cur_endpoint_index), - y: contour.position_of(cur_endpoint_index).y(), - }); - } - - if cur_endpoint_index >= next_endpoint_index { - break; - } - - prev_endpoint_index = cur_endpoint_index; - cur_endpoint_index = next_endpoint_index; - next_endpoint_index = contour.next_endpoint_index_of(cur_endpoint_index); - } - } - } -} - -impl<'a> TilingPathInfo<'a> { - pub(crate) fn has_destructive_blend_mode(&self) -> bool { - match *self { - TilingPathInfo::Draw(ref draw_tiling_path_info) => { - draw_tiling_path_info.blend_mode.is_destructive() - } - TilingPathInfo::Clip => false, - } - } -} - -pub(crate) struct PackedTile<'a> { - pub(crate) tile_type: TileType, - pub(crate) tile_coords: Vector2I, - pub(crate) draw_tile: &'a TileObjectPrimitive, - pub(crate) clip_tile: Option<&'a TileObjectPrimitive>, -} - -#[derive(Clone, Copy, PartialEq)] -pub(crate) enum TileType { - Solid, - Empty, - SingleMask, -} - -impl<'a> PackedTile<'a> { - fn new(draw_tile_index: u32, - draw_tile: &'a TileObjectPrimitive, - draw_tiling_path_info: &DrawTilingPathInfo<'a>, - object_builder: &ObjectBuilder) - -> PackedTile<'a> { - let tile_coords = object_builder.local_tile_index_to_coords(draw_tile_index as u32); - - // First, if the draw tile is empty, cull it regardless of clip. - if draw_tile.is_solid() { - match (object_builder.built_path.fill_rule, draw_tile.backdrop) { - (FillRule::Winding, 0) => { - return PackedTile { - tile_type: TileType::Empty, - tile_coords, - draw_tile, - clip_tile: None, - }; - } - (FillRule::Winding, _) => {} - (FillRule::EvenOdd, backdrop) if backdrop % 2 == 0 => { - return PackedTile { - tile_type: TileType::Empty, - tile_coords, - draw_tile, - clip_tile: None, - }; - } - (FillRule::EvenOdd, _) => {} - } - } - - // Figure out what clip tile we need, if any. - let clip_tile = match draw_tiling_path_info.built_clip_path { - None => None, - Some(built_clip_path) => { - match built_clip_path.tiles.get(tile_coords) { - None => { - // This tile is outside of the bounds of the clip path entirely. We can - // cull it. - return PackedTile { - tile_type: TileType::Empty, - tile_coords, - draw_tile, - clip_tile: None, - }; - } - Some(clip_tile) if clip_tile.is_solid() => { - if clip_tile.backdrop != 0 { - // The clip tile is fully opaque, so this tile isn't clipped at - // all. - None - } else { - // This tile is completely clipped out. Cull it. - return PackedTile { - tile_type: TileType::Empty, - tile_coords, - draw_tile, - clip_tile: None, - }; - } - } - Some(clip_tile) => Some(clip_tile), - } - } - }; - - // Choose a tile type. - match clip_tile { - None if draw_tile.is_solid() => { - // This is a solid tile that completely occludes the background. - PackedTile { tile_type: TileType::Solid, tile_coords, draw_tile, clip_tile } - } - None => { - // We have a draw tile and no clip tile. - PackedTile { - tile_type: TileType::SingleMask, - tile_coords, - draw_tile, - clip_tile: None, - } - } - Some(clip_tile) if draw_tile.is_solid() => { - // We have a solid draw tile and a clip tile. This is effectively the same as - // having a draw tile and no clip tile. - // - // FIXME(pcwalton): This doesn't preserve the fill rule of the clip path! - PackedTile { - tile_type: TileType::SingleMask, - tile_coords, - draw_tile: clip_tile, - clip_tile: None, - } - } - Some(clip_tile) => { - // We have both a draw and clip mask. Composite them together. - PackedTile { - tile_type: TileType::SingleMask, - tile_coords, - draw_tile, - clip_tile: Some(clip_tile), - } - } - } - } -} - -pub fn round_rect_out_to_tile_bounds(rect: RectF) -> RectI { - (rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32)).round_out().to_i32() -} - -fn process_active_segment( - contour: &Contour, - from_endpoint_index: u32, - active_edges: &mut SortedVector, - builder: &SceneBuilder, - object_builder: &mut ObjectBuilder, - tile_y: i32, -) { - let mut active_edge = ActiveEdge::from_segment(&contour.segment_after(from_endpoint_index)); - debug!("... process_active_segment({:#?})", active_edge); - active_edge.process(builder, object_builder, tile_y); - if !active_edge.segment.is_none() { - debug!("... ... pushing resulting active edge: {:#?}", active_edge); - active_edges.push(active_edge); - } -} - -// Queued endpoints - -#[derive(PartialEq)] -struct QueuedEndpoint { - point_index: PointIndex, - y: f32, -} - -impl Eq for QueuedEndpoint {} - -impl PartialOrd for QueuedEndpoint { - fn partial_cmp(&self, other: &QueuedEndpoint) -> Option { - // NB: Reversed! - (other.y, other.point_index).partial_cmp(&(self.y, self.point_index)) - } -} - -// Active edges - -#[derive(Clone, PartialEq, Debug)] -struct ActiveEdge { - segment: Segment, - // TODO(pcwalton): Shrink `crossing` down to just one f32? - crossing: Vector2F, -} - -impl ActiveEdge { - fn from_segment(segment: &Segment) -> ActiveEdge { - let crossing = if segment.baseline.from_y() < segment.baseline.to_y() { - segment.baseline.from() - } else { - segment.baseline.to() - }; - ActiveEdge::from_segment_and_crossing(segment, crossing) - } - - fn from_segment_and_crossing(segment: &Segment, crossing: Vector2F) -> ActiveEdge { - ActiveEdge { segment: *segment, crossing } - } - - fn process(&mut self, - builder: &SceneBuilder, - object_builder: &mut ObjectBuilder, - tile_y: i32) { - let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32; - debug!( - "process_active_edge({:#?}, tile_y={}({}))", - self, tile_y, tile_bottom - ); - - let mut segment = self.segment; - let winding = segment.baseline.y_winding(); - - if segment.is_line() { - let line_segment = segment.as_line_segment(); - self.segment = - match self.process_line_segment(line_segment, builder, object_builder, tile_y) { - Some(lower_part) => Segment::line(lower_part), - None => Segment::none(), - }; - return; - } - - // TODO(pcwalton): Don't degree elevate! - if !segment.is_cubic() { - segment = segment.to_cubic(); - } - - // If necessary, draw initial line. - if self.crossing.y() < segment.baseline.min_y() { - let first_line_segment = - LineSegment2F::new(self.crossing, segment.baseline.upper_point()).orient(winding); - if self.process_line_segment(first_line_segment, builder, object_builder, tile_y) - .is_some() { - return; - } - } - - let mut oriented_segment = segment.orient(winding); - loop { - let mut split_t = 1.0; - let mut before_segment = oriented_segment; - let mut after_segment = None; - - while !before_segment - .as_cubic_segment() - .is_flat(FLATTENING_TOLERANCE) - { - let next_t = 0.5 * split_t; - let (before, after) = oriented_segment.as_cubic_segment().split(next_t); - before_segment = before; - after_segment = Some(after); - split_t = next_t; - } - - debug!( - "... tile_y={} winding={} segment={:?} t={} before_segment={:?} - after_segment={:?}", - tile_y, winding, segment, split_t, before_segment, after_segment - ); - - let line = before_segment.baseline.orient(winding); - match self.process_line_segment(line, builder, object_builder, tile_y) { - Some(lower_part) if split_t == 1.0 => { - self.segment = Segment::line(lower_part); - return; - } - None if split_t == 1.0 => { - self.segment = Segment::none(); - return; - } - Some(_) => { - self.segment = after_segment.unwrap().orient(winding); - return; - } - None => oriented_segment = after_segment.unwrap(), - } - } - } - - fn process_line_segment( - &mut self, - line_segment: LineSegment2F, - builder: &SceneBuilder, - object_builder: &mut ObjectBuilder, - tile_y: i32, - ) -> Option { - let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32; - debug!( - "process_line_segment({:?}, tile_y={}) tile_bottom={}", - line_segment, tile_y, tile_bottom - ); - - if line_segment.max_y() <= tile_bottom { - object_builder.generate_fill_primitives_for_line(builder, line_segment, tile_y); - return None; - } - - let (upper_part, lower_part) = line_segment.split_at_y(tile_bottom); - object_builder.generate_fill_primitives_for_line(builder, upper_part, tile_y); - self.crossing = lower_part.upper_point(); - Some(lower_part) - } -} - -impl PartialOrd for ActiveEdge { - fn partial_cmp(&self, other: &ActiveEdge) -> Option { - self.crossing.x().partial_cmp(&other.crossing.x()) - } -} - -impl Default for TileObjectPrimitive { - #[inline] - fn default() -> TileObjectPrimitive { - TileObjectPrimitive { backdrop: 0, alpha_tile_id: AlphaTileId::invalid() } - } -} - -impl TileObjectPrimitive { - #[inline] - pub fn is_solid(&self) -> bool { !self.alpha_tile_id.is_valid() } -} diff --git a/crates/pathfinder/renderer/src/z_buffer.rs b/crates/pathfinder/renderer/src/z_buffer.rs deleted file mode 100644 index 291ffafc35..0000000000 --- a/crates/pathfinder/renderer/src/z_buffer.rs +++ /dev/null @@ -1,123 +0,0 @@ -// pathfinder/renderer/src/z_buffer.rs -// -// Copyright © 2019 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Software occlusion culling. - -use crate::builder::Occluder; -use crate::gpu_data::{Tile, TileBatch}; -use crate::paint::{PaintId, PaintMetadata}; -use crate::tile_map::DenseTileMap; -use crate::tiles; -use pathfinder_content::effects::BlendMode; -use pathfinder_geometry::alignment::{AlignedU16, AlignedI16}; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::vector::Vector2I; -use vec_map::VecMap; - -pub(crate) struct ZBuffer { - buffer: DenseTileMap, - depth_metadata: VecMap, -} - -pub(crate) struct SolidTiles { - pub(crate) batches: Vec, -} - -#[derive(Clone, Copy)] -pub(crate) struct DepthMetadata { - pub(crate) paint_id: PaintId, -} -impl ZBuffer { - pub(crate) fn new(view_box: RectF) -> ZBuffer { - let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box); - ZBuffer { - buffer: DenseTileMap::from_builder(|_| 0, tile_rect), - depth_metadata: VecMap::new(), - } - } - - pub(crate) fn test(&self, coords: Vector2I, depth: u32) -> bool { - let tile_index = self.buffer.coords_to_index_unchecked(coords); - self.buffer.data[tile_index as usize] < depth - } - - pub(crate) fn update(&mut self, - solid_tiles: &[Occluder], - depth: u32, - metadata: DepthMetadata) { - self.depth_metadata.insert(depth as usize, metadata); - for solid_tile in solid_tiles { - let tile_index = self.buffer.coords_to_index_unchecked(solid_tile.coords); - let z_dest = &mut self.buffer.data[tile_index as usize]; - *z_dest = u32::max(*z_dest, depth); - } - } - - pub(crate) fn build_solid_tiles(&self, paint_metadata: &[PaintMetadata]) -> SolidTiles { - let mut solid_tiles = SolidTiles { batches: vec![] }; - - for tile_index in 0..self.buffer.data.len() { - let depth = self.buffer.data[tile_index]; - if depth == 0 { - continue; - } - - let tile_coords = self.buffer.index_to_coords(tile_index); - - let depth_metadata = self.depth_metadata[depth as usize]; - let paint_id = depth_metadata.paint_id; - let paint_metadata = &paint_metadata[paint_id.0 as usize]; - - let tile_position = tile_coords + self.buffer.rect.origin(); - - // Create a batch if necessary. - let paint_tile_batch_texture = paint_metadata.tile_batch_texture(); - let paint_filter = paint_metadata.filter(); - match solid_tiles.batches.last() { - Some(TileBatch { color_texture: tile_batch_texture, filter: tile_filter, .. }) if - *tile_batch_texture == paint_tile_batch_texture && - *tile_filter == paint_filter => {} - _ => { - // Batch break. - // - // TODO(pcwalton): We could be more aggressive with batching here, since we - // know there are no overlaps. - solid_tiles.batches.push(TileBatch { - color_texture: paint_tile_batch_texture, - tiles: vec![], - filter: paint_filter, - blend_mode: BlendMode::default(), - tile_page: !0, - }); - } - } - - let batch = solid_tiles.batches.last_mut().unwrap(); - batch.tiles.push(Tile::new_solid_from_paint_id(tile_position, paint_id)); - } - - solid_tiles - } -} - -impl Tile { - pub(crate) fn new_solid_from_paint_id(tile_origin: Vector2I, paint_id: PaintId) -> Tile { - Tile { - tile_x: tile_origin.x() as AlignedI16, - tile_y: tile_origin.y() as AlignedI16, - mask_0_backdrop: 0, - mask_0_u: 0, - mask_0_v: 0, - ctrl: 0, - pad: 0, - color: paint_id.0 as AlignedU16, - } - } -} diff --git a/crates/pathfinder/resources/Cargo.toml b/crates/pathfinder/resources/Cargo.toml deleted file mode 100644 index 6068f0619d..0000000000 --- a/crates/pathfinder/resources/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "pathfinder_resources" -version = "0.5.0" -edition = "2018" -authors = ["Patrick Walton "] -description = "Shaders, textures, etc. for the Pathfinder vector graphics library" -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/pathfinder" -homepage = "https://github.com/servo/pathfinder" - -[dependencies] diff --git a/crates/pathfinder/resources/MANIFEST b/crates/pathfinder/resources/MANIFEST deleted file mode 100644 index fbe946fdb7..0000000000 --- a/crates/pathfinder/resources/MANIFEST +++ /dev/null @@ -1,106 +0,0 @@ -# This file must contain the paths of all resources that are used by the Pathfinder library. -# -# When you add a new resource, make sure to add it to this file. - -debug-fonts/regular.json -shaders/gl3/blit.fs.glsl -shaders/gl3/blit.vs.glsl -shaders/gl3/clear.fs.glsl -shaders/gl3/clear.vs.glsl -shaders/gl3/debug_solid.fs.glsl -shaders/gl3/debug_solid.vs.glsl -shaders/gl3/debug_texture.fs.glsl -shaders/gl3/debug_texture.vs.glsl -shaders/gl3/demo_ground.fs.glsl -shaders/gl3/demo_ground.vs.glsl -shaders/gl3/fill.fs.glsl -shaders/gl3/fill.vs.glsl -shaders/gl3/reproject.fs.glsl -shaders/gl3/reproject.vs.glsl -shaders/gl3/stencil.fs.glsl -shaders/gl3/stencil.vs.glsl -shaders/gl3/tile.fs.glsl -shaders/gl3/tile.vs.glsl -shaders/gl3/tile_clip.fs.glsl -shaders/gl3/tile_clip.vs.glsl -shaders/gl3/tile_copy.fs.glsl -shaders/gl3/tile_copy.vs.glsl -shaders/gl4/blit.fs.glsl -shaders/gl4/blit.vs.glsl -shaders/gl4/clear.fs.glsl -shaders/gl4/clear.vs.glsl -shaders/gl4/debug_solid.fs.glsl -shaders/gl4/debug_solid.vs.glsl -shaders/gl4/debug_texture.fs.glsl -shaders/gl4/debug_texture.vs.glsl -shaders/gl4/demo_ground.fs.glsl -shaders/gl4/demo_ground.vs.glsl -shaders/gl4/fill.fs.glsl -shaders/gl4/fill.vs.glsl -shaders/gl4/reproject.fs.glsl -shaders/gl4/reproject.vs.glsl -shaders/gl4/stencil.fs.glsl -shaders/gl4/stencil.vs.glsl -shaders/gl4/tile.fs.glsl -shaders/gl4/tile.vs.glsl -shaders/gl4/tile_clip.fs.glsl -shaders/gl4/tile_clip.vs.glsl -shaders/gl4/tile_copy.fs.glsl -shaders/gl4/tile_copy.vs.glsl -shaders/metal/blit.fs.metal -shaders/metal/blit.vs.metal -shaders/metal/clear.fs.metal -shaders/metal/clear.vs.metal -shaders/metal/debug_solid.fs.metal -shaders/metal/debug_solid.vs.metal -shaders/metal/debug_texture.fs.metal -shaders/metal/debug_texture.vs.metal -shaders/metal/demo_ground.fs.metal -shaders/metal/demo_ground.vs.metal -shaders/metal/fill.fs.metal -shaders/metal/fill.vs.metal -shaders/metal/reproject.fs.metal -shaders/metal/reproject.vs.metal -shaders/metal/stencil.fs.metal -shaders/metal/stencil.vs.metal -shaders/metal/tile.fs.metal -shaders/metal/tile.vs.metal -shaders/metal/tile_clip.fs.metal -shaders/metal/tile_clip.vs.metal -shaders/metal/tile_copy.fs.metal -shaders/metal/tile_copy.vs.metal -shaders/vulkan/blit.fs.spv -shaders/vulkan/blit.vs.spv -shaders/vulkan/clear.fs.spv -shaders/vulkan/clear.vs.spv -shaders/vulkan/debug_solid.fs.spv -shaders/vulkan/debug_solid.vs.spv -shaders/vulkan/debug_texture.fs.spv -shaders/vulkan/debug_texture.vs.spv -shaders/vulkan/demo_ground.fs.spv -shaders/vulkan/demo_ground.vs.spv -shaders/vulkan/fill.fs.spv -shaders/vulkan/fill.vs.spv -shaders/vulkan/reproject.fs.spv -shaders/vulkan/reproject.vs.spv -shaders/vulkan/stencil.fs.spv -shaders/vulkan/stencil.vs.spv -shaders/vulkan/tile.fs.spv -shaders/vulkan/tile.vs.spv -shaders/vulkan/tile_clip.fs.spv -shaders/vulkan/tile_clip.vs.spv -shaders/vulkan/tile_copy.fs.spv -shaders/vulkan/tile_copy.vs.spv -textures/area-lut.png -textures/debug-corner-fill.png -textures/debug-corner-outline.png -textures/debug-font.png -textures/demo-background.png -textures/demo-effects.png -textures/demo-open.png -textures/demo-rotate.png -textures/demo-screenshot.png -textures/demo-zoom-actual-size.png -textures/demo-zoom-in.png -textures/demo-zoom-out.png -textures/gamma-lut.png diff --git a/crates/pathfinder/resources/build.rs b/crates/pathfinder/resources/build.rs deleted file mode 100644 index 0912c1adce..0000000000 --- a/crates/pathfinder/resources/build.rs +++ /dev/null @@ -1,51 +0,0 @@ -// pathfinder/resources/build.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::env; -use std::fs::File; -use std::io::{BufRead, BufReader, Write}; -use std::path::Path; - -fn main() { - let out_dir = env::var_os("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("manifest.rs"); - let mut dest = File::create(dest_path).unwrap(); - let cwd = env::current_dir().unwrap(); - - writeln!(&mut dest, "// Generated by `pathfinder/resources/build.rs`. Do not edit!\n").unwrap(); - writeln!(&mut dest, - "pub static RESOURCES: &'static [(&'static str, &'static [u8])] = &[").unwrap(); - - let src = BufReader::new(File::open("MANIFEST").unwrap()); - for line in src.lines() { - let line = line.unwrap(); - let line = line.trim_start().trim_end(); - if line.is_empty() || line.starts_with("#") { - continue; - } - - let escaped_path = line.escape_default().to_string(); - let mut full_path = cwd.clone(); - full_path.push(line); - let escaped_full_path = full_path.to_str().unwrap().escape_default().to_string(); - - writeln!(&mut dest, - " (\"{}\", include_bytes!(\"{}\")),", - escaped_path, - escaped_full_path).unwrap(); - - println!("cargo:rerun-if-changed={}", line); - } - - writeln!(&mut dest, "];").unwrap(); - - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=MANIFEST"); -} diff --git a/crates/pathfinder/resources/debug-fonts/regular.json b/crates/pathfinder/resources/debug-fonts/regular.json deleted file mode 100644 index e2da3c18af..0000000000 --- a/crates/pathfinder/resources/debug-fonts/regular.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "name": "D-DIN", - "size": 28, - "bold": false, - "italic": false, - "width": 512, - "height": 128, - "characters": { - "0":{"x":338,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18}, - "1":{"x":30,"y":64,"width":11,"height":29,"originX":0,"originY":28,"advance":12}, - "2":{"x":490,"y":35,"width":17,"height":29,"originX":0,"originY":28,"advance":17}, - "3":{"x":356,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18}, - "4":{"x":398,"y":35,"width":19,"height":29,"originX":0,"originY":28,"advance":19}, - "5":{"x":374,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18}, - "6":{"x":392,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18}, - "7":{"x":436,"y":35,"width":18,"height":29,"originX":1,"originY":28,"advance":16}, - "8":{"x":319,"y":0,"width":19,"height":30,"originX":0,"originY":28,"advance":19}, - "9":{"x":454,"y":35,"width":18,"height":29,"originX":0,"originY":28,"advance":18}, - " ":{"x":147,"y":93,"width":3,"height":3,"originX":1,"originY":1,"advance":8}, - "!":{"x":62,"y":64,"width":7,"height":29,"originX":-1,"originY":28,"advance":9}, - "\"":{"x":52,"y":93,"width":12,"height":12,"originX":0,"originY":28,"advance":11}, - "#":{"x":233,"y":35,"width":21,"height":29,"originX":1,"originY":28,"advance":19}, - "$":{"x":138,"y":0,"width":18,"height":34,"originX":1,"originY":30,"advance":17}, - "%":{"x":156,"y":0,"width":31,"height":30,"originX":0,"originY":28,"advance":31}, - "&":{"x":187,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":22}, - "'":{"x":64,"y":93,"width":6,"height":12,"originX":0,"originY":28,"advance":6}, - "(":{"x":59,"y":0,"width":10,"height":35,"originX":0,"originY":30,"advance":11}, - ")":{"x":48,"y":0,"width":11,"height":35,"originX":0,"originY":30,"advance":11}, - "*":{"x":19,"y":93,"width":14,"height":14,"originX":0,"originY":28,"advance":14}, - "+":{"x":432,"y":64,"width":19,"height":19,"originX":0,"originY":20,"advance":19}, - ",":{"x":70,"y":93,"width":6,"height":11,"originX":0,"originY":5,"advance":6}, - "-":{"x":105,"y":93,"width":14,"height":6,"originX":0,"originY":16,"advance":15}, - ".":{"x":119,"y":93,"width":7,"height":6,"originX":0,"originY":5,"advance":7}, - "/":{"x":446,"y":0,"width":14,"height":30,"originX":0,"originY":28,"advance":15}, - ":":{"x":470,"y":64,"width":7,"height":19,"originX":0,"originY":18,"advance":7}, - ";":{"x":171,"y":64,"width":7,"height":24,"originX":0,"originY":18,"advance":7}, - "<":{"x":477,"y":64,"width":19,"height":17,"originX":0,"originY":19,"advance":19}, - "=":{"x":33,"y":93,"width":19,"height":13,"originX":0,"originY":17,"advance":19}, - ">":{"x":0,"y":93,"width":19,"height":17,"originX":0,"originY":19,"advance":19}, - "?":{"x":0,"y":64,"width":17,"height":29,"originX":1,"originY":28,"advance":16}, - "@":{"x":106,"y":0,"width":32,"height":34,"originX":0,"originY":28,"advance":32}, - "A":{"x":26,"y":35,"width":25,"height":29,"originX":2,"originY":28,"advance":22}, - "B":{"x":254,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":23}, - "C":{"x":209,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23}, - "D":{"x":145,"y":35,"width":22,"height":29,"originX":-1,"originY":28,"advance":23}, - "E":{"x":338,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20}, - "F":{"x":358,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20}, - "G":{"x":231,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23}, - "H":{"x":275,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":23}, - "I":{"x":69,"y":64,"width":7,"height":29,"originX":-1,"originY":28,"advance":9}, - "J":{"x":41,"y":64,"width":11,"height":29,"originX":1,"originY":28,"advance":12}, - "K":{"x":76,"y":35,"width":23,"height":29,"originX":-1,"originY":28,"advance":22}, - "L":{"x":378,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20}, - "M":{"x":0,"y":35,"width":26,"height":29,"originX":-1,"originY":28,"advance":28}, - "N":{"x":99,"y":35,"width":23,"height":29,"originX":-1,"originY":28,"advance":25}, - "O":{"x":253,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23}, - "P":{"x":296,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":22}, - "Q":{"x":0,"y":0,"width":22,"height":35,"originX":0,"originY":28,"advance":23}, - "R":{"x":167,"y":35,"width":22,"height":29,"originX":-1,"originY":28,"advance":22}, - "S":{"x":275,"y":0,"width":22,"height":30,"originX":1,"originY":28,"advance":20}, - "T":{"x":189,"y":35,"width":22,"height":29,"originX":2,"originY":28,"advance":18}, - "U":{"x":297,"y":0,"width":22,"height":30,"originX":-1,"originY":28,"advance":24}, - "V":{"x":122,"y":35,"width":23,"height":29,"originX":2,"originY":28,"advance":19}, - "W":{"x":474,"y":0,"width":33,"height":29,"originX":2,"originY":28,"advance":30}, - "X":{"x":51,"y":35,"width":25,"height":29,"originX":2,"originY":28,"advance":21}, - "Y":{"x":211,"y":35,"width":22,"height":29,"originX":2,"originY":28,"advance":19}, - "Z":{"x":317,"y":35,"width":21,"height":29,"originX":1,"originY":28,"advance":19}, - "[":{"x":69,"y":0,"width":10,"height":35,"originX":-1,"originY":30,"advance":11}, - "\\":{"x":460,"y":0,"width":14,"height":30,"originX":0,"originY":28,"advance":15}, - "]":{"x":79,"y":0,"width":10,"height":35,"originX":0,"originY":30,"advance":11}, - "^":{"x":451,"y":64,"width":19,"height":19,"originX":0,"originY":28,"advance":19}, - "_":{"x":126,"y":93,"width":21,"height":5,"originX":2,"originY":0,"advance":17}, - "`":{"x":95,"y":93,"width":10,"height":8,"originX":-4,"originY":28,"advance":19}, - "a":{"x":235,"y":64,"width":18,"height":23,"originX":0,"originY":21,"advance":19}, - "b":{"x":410,"y":0,"width":18,"height":30,"originX":-1,"originY":28,"advance":19}, - "c":{"x":178,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19}, - "d":{"x":428,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":19}, - "e":{"x":197,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19}, - "f":{"x":17,"y":64,"width":13,"height":29,"originX":0,"originY":28,"advance":12}, - "g":{"x":96,"y":64,"width":18,"height":28,"originX":0,"originY":21,"advance":19}, - "h":{"x":472,"y":35,"width":18,"height":29,"originX":-1,"originY":28,"advance":19}, - "i":{"x":150,"y":64,"width":7,"height":28,"originX":-1,"originY":27,"advance":9}, - "j":{"x":89,"y":0,"width":10,"height":35,"originX":3,"originY":28,"advance":8}, - "k":{"x":417,"y":35,"width":19,"height":29,"originX":-1,"originY":28,"advance":18}, - "l":{"x":52,"y":64,"width":10,"height":29,"originX":-1,"originY":28,"advance":10}, - "m":{"x":289,"y":64,"width":29,"height":22,"originX":-1,"originY":21,"advance":31}, - "n":{"x":366,"y":64,"width":18,"height":22,"originX":-1,"originY":21,"advance":19}, - "o":{"x":216,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19}, - "p":{"x":114,"y":64,"width":18,"height":28,"originX":-1,"originY":21,"advance":19}, - "q":{"x":132,"y":64,"width":18,"height":28,"originX":0,"originY":21,"advance":19}, - "r":{"x":419,"y":64,"width":13,"height":22,"originX":-1,"originY":21,"advance":13}, - "s":{"x":253,"y":64,"width":18,"height":23,"originX":0,"originY":21,"advance":17}, - "t":{"x":157,"y":64,"width":14,"height":27,"originX":1,"originY":26,"advance":13}, - "u":{"x":271,"y":64,"width":18,"height":23,"originX":-1,"originY":21,"advance":19}, - "v":{"x":347,"y":64,"width":19,"height":22,"originX":1,"originY":21,"advance":16}, - "w":{"x":318,"y":64,"width":29,"height":22,"originX":1,"originY":21,"advance":27}, - "x":{"x":384,"y":64,"width":18,"height":22,"originX":1,"originY":21,"advance":16}, - "y":{"x":76,"y":64,"width":20,"height":28,"originX":2,"originY":21,"advance":16}, - "z":{"x":402,"y":64,"width":17,"height":22,"originX":0,"originY":21,"advance":17}, - "{":{"x":22,"y":0,"width":13,"height":35,"originX":0,"originY":30,"advance":13}, - "|":{"x":99,"y":0,"width":7,"height":35,"originX":-2,"originY":30,"advance":11}, - "}":{"x":35,"y":0,"width":13,"height":35,"originX":0,"originY":30,"advance":13}, - "~":{"x":76,"y":93,"width":19,"height":9,"originX":0,"originY":15,"advance":19} - } -} diff --git a/crates/pathfinder/resources/fonts/LICENSE-APACHE b/crates/pathfinder/resources/fonts/LICENSE-APACHE deleted file mode 100644 index 75b52484ea..0000000000 --- a/crates/pathfinder/resources/fonts/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/crates/pathfinder/resources/fonts/LICENSE-SIL b/crates/pathfinder/resources/fonts/LICENSE-SIL deleted file mode 100644 index ec2cdcae2a..0000000000 --- a/crates/pathfinder/resources/fonts/LICENSE-SIL +++ /dev/null @@ -1,49 +0,0 @@ -Copyright 2011 Red Hat, Inc., -with Reserved Font Name OVERPASS. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. - -The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the copyright statement(s). - -"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. - -"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. - -5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/crates/pathfinder/resources/fonts/NotoEmoji-Regular.ttf b/crates/pathfinder/resources/fonts/NotoEmoji-Regular.ttf deleted file mode 100644 index 19b7badf4afe64dae28cd64fc506b95291bb8b03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418804 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�p8T)6I% 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
3=AUYLi1zm#9`5f{OeSj(h#op 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 z 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 diff --git a/crates/pathfinder/resources/fonts/Roboto-Regular.ttf b/crates/pathfinder/resources/fonts/Roboto-Regular.ttf deleted file mode 100644 index 2b6392ffe8712b9c5450733320cd220d6c0f4bce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171272 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 diff --git a/crates/pathfinder/resources/shaders/README.md b/crates/pathfinder/resources/shaders/README.md deleted file mode 100644 index 24bff80f7d..0000000000 --- a/crates/pathfinder/resources/shaders/README.md +++ /dev/null @@ -1,7 +0,0 @@ -This directory contains postprocessed versions of the shaders in the top-level -`shaders/` directory, for convenience. Don't modify the shaders here; instead -modify the corresponding shaders in `shaders/` and rerun `make` in that -directory. - -You will need `glslangValidator` and `spirv-cross` installed to execute the -Makefile. On macOS, you can get these with `brew install glslang spirv-cross`. diff --git a/crates/pathfinder/resources/shaders/gl3/blit.fs.glsl b/crates/pathfinder/resources/shaders/gl3/blit.fs.glsl deleted file mode 100644 index 806d714951..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/blit.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform sampler2D SPIRV_Cross_CombineduSrcuSampler; - -in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec4 color = texture(SPIRV_Cross_CombineduSrcuSampler, vTexCoord); - oFragColor = vec4(color.xyz * color.w, color.w); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/blit.vs.glsl b/crates/pathfinder/resources/shaders/gl3/blit.vs.glsl deleted file mode 100644 index 7efaa8cdc4..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/blit.vs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 0) in ivec2 aPosition; -out vec2 vTexCoord; - -void main() -{ - vec2 texCoord = vec2(aPosition); - vTexCoord = texCoord; - gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), vec2(aPosition)), 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/clear.fs.glsl b/crates/pathfinder/resources/shaders/gl3/clear.fs.glsl deleted file mode 100644 index bfc45445dc..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/clear.fs.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uColor[1]; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - oFragColor = vec4(uColor[0].xyz, 1.0) * uColor[0].w; -} - diff --git a/crates/pathfinder/resources/shaders/gl3/clear.vs.glsl b/crates/pathfinder/resources/shaders/gl3/clear.vs.glsl deleted file mode 100644 index 76b85239b9..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/clear.vs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uRect[1]; -uniform vec4 uFramebufferSize[1]; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vec2 position = ((mix(uRect[0].xy, uRect[0].zw, vec2(aPosition)) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/debug_solid.fs.glsl b/crates/pathfinder/resources/shaders/gl3/debug_solid.fs.glsl deleted file mode 100644 index bfc45445dc..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/debug_solid.fs.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uColor[1]; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - oFragColor = vec4(uColor[0].xyz, 1.0) * uColor[0].w; -} - diff --git a/crates/pathfinder/resources/shaders/gl3/debug_solid.vs.glsl b/crates/pathfinder/resources/shaders/gl3/debug_solid.vs.glsl deleted file mode 100644 index 705fbc5efb..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/debug_solid.vs.glsl +++ /dev/null @@ -1,13 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uFramebufferSize[1]; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vec2 position = ((vec2(aPosition) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/debug_texture.fs.glsl b/crates/pathfinder/resources/shaders/gl3/debug_texture.fs.glsl deleted file mode 100644 index 7a23aec868..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/debug_texture.fs.glsl +++ /dev/null @@ -1,16 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uColor[1]; -uniform sampler2D SPIRV_Cross_CombineduTextureuSampler; - -in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - float alpha = texture(SPIRV_Cross_CombineduTextureuSampler, vTexCoord).x * uColor[0].w; - oFragColor = vec4(uColor[0].xyz, 1.0) * alpha; -} - diff --git a/crates/pathfinder/resources/shaders/gl3/debug_texture.vs.glsl b/crates/pathfinder/resources/shaders/gl3/debug_texture.vs.glsl deleted file mode 100644 index e8965d26f7..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/debug_texture.vs.glsl +++ /dev/null @@ -1,17 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTextureSize[1]; -uniform vec4 uFramebufferSize[1]; -out vec2 vTexCoord; -layout(location = 1) in ivec2 aTexCoord; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vTexCoord = vec2(aTexCoord) / uTextureSize[0].xy; - vec2 position = ((vec2(aPosition) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/demo_ground.fs.glsl b/crates/pathfinder/resources/shaders/gl3/demo_ground.fs.glsl deleted file mode 100644 index f33c5b98b1..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/demo_ground.fs.glsl +++ /dev/null @@ -1,24 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uGridlineColor[1]; -uniform vec4 uGroundColor[1]; -in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec2 texCoordPx = fract(vTexCoord) / fwidth(vTexCoord); - vec4 _28; - if (any(lessThanEqual(texCoordPx, vec2(1.0)))) - { - _28 = uGridlineColor[0]; - } - else - { - _28 = uGroundColor[0]; - } - oFragColor = _28; -} - diff --git a/crates/pathfinder/resources/shaders/gl3/demo_ground.vs.glsl b/crates/pathfinder/resources/shaders/gl3/demo_ground.vs.glsl deleted file mode 100644 index c87472beac..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/demo_ground.vs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform ivec4 uGridlineCount[1]; -uniform vec4 uTransform[4]; -out vec2 vTexCoord; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vTexCoord = vec2(aPosition * ivec2(uGridlineCount[0].x)); - gl_Position = mat4(uTransform[0], uTransform[1], uTransform[2], uTransform[3]) * vec4(ivec4(aPosition.x, 0, aPosition.y, 1)); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/fill.fs.glsl b/crates/pathfinder/resources/shaders/gl3/fill.fs.glsl deleted file mode 100644 index b7c0dee338..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/fill.fs.glsl +++ /dev/null @@ -1,32 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform sampler2D SPIRV_Cross_CombineduAreaLUTuSampler; - -layout(location = 0) out vec4 oFragColor; -in vec2 vFrom; -in vec2 vTo; - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D SPIRV_Cross_CombinedareaLUTareaLUTSampler) -{ - bvec2 _34 = bvec2(from.x < to.x); - vec2 left = vec2(_34.x ? from.x : to.x, _34.y ? from.y : to.y); - bvec2 _44 = bvec2(from.x < to.x); - vec2 right = vec2(_44.x ? to.x : from.x, _44.y ? to.y : from.y); - vec2 window = clamp(vec2(from.x, to.x), vec2(-0.5), vec2(0.5)); - float offset = mix(window.x, window.y, 0.5) - left.x; - float t = offset / (right.x - left.x); - float y = mix(left.y, right.y, t); - float d = (right.y - left.y) / (right.x - left.x); - float dX = window.x - window.y; - return texture(SPIRV_Cross_CombinedareaLUTareaLUTSampler, vec2(y + 8.0, abs(d * dX)) / vec2(16.0)) * dX; -} - -void main() -{ - vec2 param = vFrom; - vec2 param_1 = vTo; - oFragColor = computeCoverage(param, param_1, SPIRV_Cross_CombineduAreaLUTuSampler); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/fill.vs.glsl b/crates/pathfinder/resources/shaders/gl3/fill.vs.glsl deleted file mode 100644 index a112aeca2f..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/fill.vs.glsl +++ /dev/null @@ -1,54 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTileSize[1]; -uniform vec4 uFramebufferSize[1]; -layout(location = 5) in uint aTileIndex; -layout(location = 3) in uint aFromPx; -layout(location = 1) in vec2 aFromSubpx; -layout(location = 4) in uint aToPx; -layout(location = 2) in vec2 aToSubpx; -layout(location = 0) in uvec2 aTessCoord; -out vec2 vFrom; -out vec2 vTo; - -vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) -{ - uint tilesPerRow = uint(stencilTextureWidth / uTileSize[0].x); - uvec2 tileOffset = uvec2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); - return (vec2(tileOffset) * uTileSize[0].xy) * vec2(1.0, 0.25); -} - -void main() -{ - uint param = aTileIndex; - float param_1 = uFramebufferSize[0].x; - vec2 tileOrigin = computeTileOffset(param, param_1); - vec2 from = vec2(float(aFromPx & 15u), float(aFromPx >> 4u)) + aFromSubpx; - vec2 to = vec2(float(aToPx & 15u), float(aToPx >> 4u)) + aToSubpx; - vec2 position; - if (aTessCoord.x == 0u) - { - position.x = floor(min(from.x, to.x)); - } - else - { - position.x = ceil(max(from.x, to.x)); - } - if (aTessCoord.y == 0u) - { - position.y = floor(min(from.y, to.y)); - } - else - { - position.y = uTileSize[0].y; - } - position.y = floor(position.y * 0.25); - vec2 offset = vec2(0.0, 1.5) - (position * vec2(1.0, 4.0)); - vFrom = from + offset; - vTo = to + offset; - vec2 globalPosition = (((tileOrigin + position) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(globalPosition, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/reproject.fs.glsl b/crates/pathfinder/resources/shaders/gl3/reproject.fs.glsl deleted file mode 100644 index 5803ed2bc8..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/reproject.fs.glsl +++ /dev/null @@ -1,17 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uOldTransform[4]; -uniform sampler2D SPIRV_Cross_CombineduTextureuSampler; - -in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec4 normTexCoord = mat4(uOldTransform[0], uOldTransform[1], uOldTransform[2], uOldTransform[3]) * vec4(vTexCoord, 0.0, 1.0); - vec2 texCoord = ((normTexCoord.xy / vec2(normTexCoord.w)) + vec2(1.0)) * 0.5; - oFragColor = texture(SPIRV_Cross_CombineduTextureuSampler, texCoord); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/reproject.vs.glsl b/crates/pathfinder/resources/shaders/gl3/reproject.vs.glsl deleted file mode 100644 index 8cb538d474..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/reproject.vs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uNewTransform[4]; -layout(location = 0) in ivec2 aPosition; -out vec2 vTexCoord; - -void main() -{ - vec2 position = vec2(aPosition); - vTexCoord = position; - gl_Position = mat4(uNewTransform[0], uNewTransform[1], uNewTransform[2], uNewTransform[3]) * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/stencil.fs.glsl b/crates/pathfinder/resources/shaders/gl3/stencil.fs.glsl deleted file mode 100644 index c9a19890dc..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/stencil.fs.glsl +++ /dev/null @@ -1,11 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 0) out vec4 oFragColor; - -void main() -{ - oFragColor = vec4(1.0, 0.0, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/stencil.vs.glsl b/crates/pathfinder/resources/shaders/gl3/stencil.vs.glsl deleted file mode 100644 index f56fba14ef..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/stencil.vs.glsl +++ /dev/null @@ -1,11 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 0) in vec3 aPosition; - -void main() -{ - gl_Position = vec4(aPosition, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/tile.fs.glsl b/crates/pathfinder/resources/shaders/gl3/tile.fs.glsl deleted file mode 100644 index ad5011885f..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/tile.fs.glsl +++ /dev/null @@ -1,571 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uMaskTextureSize0[1]; -uniform vec4 uColorTextureSize0[1]; -uniform vec4 uFramebufferSize[1]; -uniform vec4 uFilterParams0[1]; -uniform vec4 uFilterParams1[1]; -uniform vec4 uFilterParams2[1]; -uniform ivec4 uCtrl[1]; -uniform sampler2D SPIRV_Cross_CombineduMaskTexture0uSampler; -uniform sampler2D SPIRV_Cross_CombineduColorTexture0uSampler; -uniform sampler2D SPIRV_Cross_CombineduGammaLUTuSampler; -uniform sampler2D SPIRV_Cross_CombineduDestTextureuSampler; - -in vec3 vMaskTexCoord0; -in vec4 vBaseColor; -in vec2 vColorTexCoord0; -layout(location = 0) out vec4 oFragColor; -in float vTileCtrl; - -float sampleMask(float maskAlpha, vec2 maskTextureSize, vec3 maskTexCoord, int maskCtrl, sampler2D SPIRV_Cross_CombinedmaskTextureuSampler) -{ - if (maskCtrl == 0) - { - return maskAlpha; - } - ivec2 maskTexCoordI = ivec2(floor(maskTexCoord.xy)); - vec4 texel = texture(SPIRV_Cross_CombinedmaskTextureuSampler, (vec2(maskTexCoordI / ivec2(1, 4)) + vec2(0.5)) / maskTextureSize); - float coverage = texel[maskTexCoordI.y % 4] + maskTexCoord.z; - if ((maskCtrl & 1) != 0) - { - coverage = abs(coverage); - } - else - { - coverage = 1.0 - abs(1.0 - mod(coverage, 2.0)); - } - return min(maskAlpha, coverage); -} - -vec4 filterRadialGradient(vec2 colorTexCoord, vec2 colorTextureSize, vec2 fragCoord, vec2 framebufferSize, vec4 filterParams0, vec4 filterParams1, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - vec2 lineFrom = filterParams0.xy; - vec2 lineVector = filterParams0.zw; - vec2 radii = filterParams1.xy; - vec2 uvOrigin = filterParams1.zw; - vec2 dP = colorTexCoord - lineFrom; - vec2 dC = lineVector; - float dR = radii.y - radii.x; - float a = dot(dC, dC) - (dR * dR); - float b = dot(dP, dC) + (radii.x * dR); - float c = dot(dP, dP) - (radii.x * radii.x); - float discrim = (b * b) - (a * c); - vec4 color = vec4(0.0); - if (abs(discrim) >= 9.9999997473787516355514526367188e-06) - { - vec2 ts = vec2((vec2(1.0, -1.0) * sqrt(discrim)) + vec2(b)) / vec2(a); - if (ts.x > ts.y) - { - ts = ts.yx; - } - float _566; - if (ts.x >= 0.0) - { - _566 = ts.x; - } - else - { - _566 = ts.y; - } - float t = _566; - color = texture(SPIRV_Cross_CombinedcolorTextureuSampler, uvOrigin + vec2(clamp(t, 0.0, 1.0), 0.0)); - } - return color; -} - -vec4 filterBlur(vec2 colorTexCoord, vec2 colorTextureSize, vec4 filterParams0, vec4 filterParams1, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - vec2 srcOffsetScale = filterParams0.xy / colorTextureSize; - int support = int(filterParams0.z); - vec3 gaussCoeff = filterParams1.xyz; - float gaussSum = gaussCoeff.x; - vec4 color = texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord) * gaussCoeff.x; - vec2 _615 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = vec3(_615.x, _615.y, gaussCoeff.z); - for (int i = 1; i <= support; i += 2) - { - float gaussPartialSum = gaussCoeff.x; - vec2 _635 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = vec3(_635.x, _635.y, gaussCoeff.z); - gaussPartialSum += gaussCoeff.x; - vec2 srcOffset = srcOffsetScale * (float(i) + (gaussCoeff.x / gaussPartialSum)); - color += ((texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord - srcOffset) + texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord + srcOffset)) * gaussPartialSum); - gaussSum += (2.0 * gaussPartialSum); - vec2 _679 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = vec3(_679.x, _679.y, gaussCoeff.z); - } - return color / vec4(gaussSum); -} - -float filterTextSample1Tap(float offset, vec2 colorTexCoord, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - return texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord + vec2(offset, 0.0)).x; -} - -void filterTextSample9Tap(out vec4 outAlphaLeft, out float outAlphaCenter, out vec4 outAlphaRight, vec2 colorTexCoord, vec4 kernel, float onePixel, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - bool wide = kernel.x > 0.0; - float _243; - if (wide) - { - float param = (-4.0) * onePixel; - vec2 param_1 = colorTexCoord; - _243 = filterTextSample1Tap(param, param_1, SPIRV_Cross_CombinedcolorTextureuSampler); - } - else - { - _243 = 0.0; - } - float param_2 = (-3.0) * onePixel; - vec2 param_3 = colorTexCoord; - float param_4 = (-2.0) * onePixel; - vec2 param_5 = colorTexCoord; - float param_6 = (-1.0) * onePixel; - vec2 param_7 = colorTexCoord; - outAlphaLeft = vec4(_243, filterTextSample1Tap(param_2, param_3, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_4, param_5, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_6, param_7, SPIRV_Cross_CombinedcolorTextureuSampler)); - float param_8 = 0.0; - vec2 param_9 = colorTexCoord; - outAlphaCenter = filterTextSample1Tap(param_8, param_9, SPIRV_Cross_CombinedcolorTextureuSampler); - float param_10 = 1.0 * onePixel; - vec2 param_11 = colorTexCoord; - float param_12 = 2.0 * onePixel; - vec2 param_13 = colorTexCoord; - float param_14 = 3.0 * onePixel; - vec2 param_15 = colorTexCoord; - float _303; - if (wide) - { - float param_16 = 4.0 * onePixel; - vec2 param_17 = colorTexCoord; - _303 = filterTextSample1Tap(param_16, param_17, SPIRV_Cross_CombinedcolorTextureuSampler); - } - else - { - _303 = 0.0; - } - outAlphaRight = vec4(filterTextSample1Tap(param_10, param_11, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_12, param_13, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_14, param_15, SPIRV_Cross_CombinedcolorTextureuSampler), _303); -} - -float filterTextConvolve7Tap(vec4 alpha0, vec3 alpha1, vec4 kernel) -{ - return dot(alpha0, kernel) + dot(alpha1, kernel.zyx); -} - -float filterTextGammaCorrectChannel(float bgColor, float fgColor, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - return texture(SPIRV_Cross_CombinedgammaLUTuSampler, vec2(fgColor, 1.0 - bgColor)).x; -} - -vec3 filterTextGammaCorrect(vec3 bgColor, vec3 fgColor, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - float param = bgColor.x; - float param_1 = fgColor.x; - float param_2 = bgColor.y; - float param_3 = fgColor.y; - float param_4 = bgColor.z; - float param_5 = fgColor.z; - return vec3(filterTextGammaCorrectChannel(param, param_1, SPIRV_Cross_CombinedgammaLUTuSampler), filterTextGammaCorrectChannel(param_2, param_3, SPIRV_Cross_CombinedgammaLUTuSampler), filterTextGammaCorrectChannel(param_4, param_5, SPIRV_Cross_CombinedgammaLUTuSampler)); -} - -vec4 filterText(vec2 colorTexCoord, vec2 colorTextureSize, vec4 filterParams0, vec4 filterParams1, vec4 filterParams2, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - vec4 kernel = filterParams0; - vec3 bgColor = filterParams1.xyz; - vec3 fgColor = filterParams2.xyz; - bool gammaCorrectionEnabled = filterParams2.w != 0.0; - vec3 alpha; - if (kernel.w == 0.0) - { - alpha = texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord).xxx; - } - else - { - vec2 param_3 = colorTexCoord; - vec4 param_4 = kernel; - float param_5 = 1.0 / colorTextureSize.x; - vec4 param; - float param_1; - vec4 param_2; - filterTextSample9Tap(param, param_1, param_2, param_3, param_4, param_5, SPIRV_Cross_CombinedcolorTextureuSampler); - vec4 alphaLeft = param; - float alphaCenter = param_1; - vec4 alphaRight = param_2; - vec4 param_6 = alphaLeft; - vec3 param_7 = vec3(alphaCenter, alphaRight.xy); - vec4 param_8 = kernel; - float r = filterTextConvolve7Tap(param_6, param_7, param_8); - vec4 param_9 = vec4(alphaLeft.yzw, alphaCenter); - vec3 param_10 = alphaRight.xyz; - vec4 param_11 = kernel; - float g = filterTextConvolve7Tap(param_9, param_10, param_11); - vec4 param_12 = vec4(alphaLeft.zw, alphaCenter, alphaRight.x); - vec3 param_13 = alphaRight.yzw; - vec4 param_14 = kernel; - float b = filterTextConvolve7Tap(param_12, param_13, param_14); - alpha = vec3(r, g, b); - } - if (gammaCorrectionEnabled) - { - vec3 param_15 = bgColor; - vec3 param_16 = alpha; - alpha = filterTextGammaCorrect(param_15, param_16, SPIRV_Cross_CombinedgammaLUTuSampler); - } - return vec4(mix(bgColor, fgColor, alpha), 1.0); -} - -vec4 sampleColor(vec2 colorTexCoord, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - return texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord); -} - -vec4 filterNone(vec2 colorTexCoord, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - vec2 param = colorTexCoord; - return sampleColor(param, SPIRV_Cross_CombinedcolorTextureuSampler); -} - -vec4 filterColor(vec2 colorTexCoord, vec2 colorTextureSize, vec2 fragCoord, vec2 framebufferSize, vec4 filterParams0, vec4 filterParams1, vec4 filterParams2, int colorFilter, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - switch (colorFilter) - { - case 1: - { - vec2 param = colorTexCoord; - vec2 param_1 = colorTextureSize; - vec2 param_2 = fragCoord; - vec2 param_3 = framebufferSize; - vec4 param_4 = filterParams0; - vec4 param_5 = filterParams1; - return filterRadialGradient(param, param_1, param_2, param_3, param_4, param_5, SPIRV_Cross_CombinedcolorTextureuSampler); - } - case 3: - { - vec2 param_6 = colorTexCoord; - vec2 param_7 = colorTextureSize; - vec4 param_8 = filterParams0; - vec4 param_9 = filterParams1; - return filterBlur(param_6, param_7, param_8, param_9, SPIRV_Cross_CombinedcolorTextureuSampler); - } - case 2: - { - vec2 param_10 = colorTexCoord; - vec2 param_11 = colorTextureSize; - vec4 param_12 = filterParams0; - vec4 param_13 = filterParams1; - vec4 param_14 = filterParams2; - return filterText(param_10, param_11, param_12, param_13, param_14, SPIRV_Cross_CombinedcolorTextureuSampler, SPIRV_Cross_CombinedgammaLUTuSampler); - } - } - vec2 param_15 = colorTexCoord; - return filterNone(param_15, SPIRV_Cross_CombinedcolorTextureuSampler); -} - -vec4 combineColor0(vec4 destColor, vec4 srcColor, int op) -{ - switch (op) - { - case 1: - { - return vec4(srcColor.xyz, srcColor.w * destColor.w); - } - case 2: - { - return vec4(destColor.xyz, srcColor.w * destColor.w); - } - } - return destColor; -} - -vec3 compositeScreen(vec3 destColor, vec3 srcColor) -{ - return (destColor + srcColor) - (destColor * srcColor); -} - -vec3 compositeSelect(bvec3 cond, vec3 ifTrue, vec3 ifFalse) -{ - float _745; - if (cond.x) - { - _745 = ifTrue.x; - } - else - { - _745 = ifFalse.x; - } - float _756; - if (cond.y) - { - _756 = ifTrue.y; - } - else - { - _756 = ifFalse.y; - } - float _767; - if (cond.z) - { - _767 = ifTrue.z; - } - else - { - _767 = ifFalse.z; - } - return vec3(_745, _756, _767); -} - -vec3 compositeHardLight(vec3 destColor, vec3 srcColor) -{ - vec3 param = destColor; - vec3 param_1 = (vec3(2.0) * srcColor) - vec3(1.0); - bvec3 param_2 = lessThanEqual(srcColor, vec3(0.5)); - vec3 param_3 = (destColor * vec3(2.0)) * srcColor; - vec3 param_4 = compositeScreen(param, param_1); - return compositeSelect(param_2, param_3, param_4); -} - -vec3 compositeColorDodge(vec3 destColor, vec3 srcColor) -{ - bvec3 destZero = equal(destColor, vec3(0.0)); - bvec3 srcOne = equal(srcColor, vec3(1.0)); - bvec3 param = srcOne; - vec3 param_1 = vec3(1.0); - vec3 param_2 = destColor / (vec3(1.0) - srcColor); - bvec3 param_3 = destZero; - vec3 param_4 = vec3(0.0); - vec3 param_5 = compositeSelect(param, param_1, param_2); - return compositeSelect(param_3, param_4, param_5); -} - -vec3 compositeSoftLight(vec3 destColor, vec3 srcColor) -{ - bvec3 param = lessThanEqual(destColor, vec3(0.25)); - vec3 param_1 = ((((vec3(16.0) * destColor) - vec3(12.0)) * destColor) + vec3(4.0)) * destColor; - vec3 param_2 = sqrt(destColor); - vec3 darkenedDestColor = compositeSelect(param, param_1, param_2); - bvec3 param_3 = lessThanEqual(srcColor, vec3(0.5)); - vec3 param_4 = destColor * (vec3(1.0) - destColor); - vec3 param_5 = darkenedDestColor - destColor; - vec3 factor = compositeSelect(param_3, param_4, param_5); - return destColor + (((srcColor * 2.0) - vec3(1.0)) * factor); -} - -float compositeDivide(float num, float denom) -{ - float _781; - if (denom != 0.0) - { - _781 = num / denom; - } - else - { - _781 = 0.0; - } - return _781; -} - -vec3 compositeRGBToHSL(vec3 rgb) -{ - float v = max(max(rgb.x, rgb.y), rgb.z); - float xMin = min(min(rgb.x, rgb.y), rgb.z); - float c = v - xMin; - float l = mix(xMin, v, 0.5); - vec3 _887; - if (rgb.x == v) - { - _887 = vec3(0.0, rgb.yz); - } - else - { - vec3 _900; - if (rgb.y == v) - { - _900 = vec3(2.0, rgb.zx); - } - else - { - _900 = vec3(4.0, rgb.xy); - } - _887 = _900; - } - vec3 terms = _887; - float param = ((terms.x * c) + terms.y) - terms.z; - float param_1 = c; - float h = 1.0471975803375244140625 * compositeDivide(param, param_1); - float param_2 = c; - float param_3 = v; - float s = compositeDivide(param_2, param_3); - return vec3(h, s, l); -} - -vec3 compositeHSL(vec3 destColor, vec3 srcColor, int op) -{ - switch (op) - { - case 12: - { - return vec3(srcColor.x, destColor.y, destColor.z); - } - case 13: - { - return vec3(destColor.x, srcColor.y, destColor.z); - } - case 14: - { - return vec3(srcColor.x, srcColor.y, destColor.z); - } - default: - { - return vec3(destColor.x, destColor.y, srcColor.z); - } - } -} - -vec3 compositeHSLToRGB(vec3 hsl) -{ - float a = hsl.y * min(hsl.z, 1.0 - hsl.z); - vec3 ks = mod(vec3(0.0, 8.0, 4.0) + vec3(hsl.x * 1.90985929965972900390625), vec3(12.0)); - return hsl.zzz - (clamp(min(ks - vec3(3.0), vec3(9.0) - ks), vec3(-1.0), vec3(1.0)) * a); -} - -vec3 compositeRGB(vec3 destColor, vec3 srcColor, int op) -{ - switch (op) - { - case 1: - { - return destColor * srcColor; - } - case 2: - { - vec3 param = destColor; - vec3 param_1 = srcColor; - return compositeScreen(param, param_1); - } - case 3: - { - vec3 param_2 = srcColor; - vec3 param_3 = destColor; - return compositeHardLight(param_2, param_3); - } - case 4: - { - return min(destColor, srcColor); - } - case 5: - { - return max(destColor, srcColor); - } - case 6: - { - vec3 param_4 = destColor; - vec3 param_5 = srcColor; - return compositeColorDodge(param_4, param_5); - } - case 7: - { - vec3 param_6 = vec3(1.0) - destColor; - vec3 param_7 = vec3(1.0) - srcColor; - return vec3(1.0) - compositeColorDodge(param_6, param_7); - } - case 8: - { - vec3 param_8 = destColor; - vec3 param_9 = srcColor; - return compositeHardLight(param_8, param_9); - } - case 9: - { - vec3 param_10 = destColor; - vec3 param_11 = srcColor; - return compositeSoftLight(param_10, param_11); - } - case 10: - { - return abs(destColor - srcColor); - } - case 11: - { - return (destColor + srcColor) - ((vec3(2.0) * destColor) * srcColor); - } - case 12: - case 13: - case 14: - case 15: - { - vec3 param_12 = destColor; - vec3 param_13 = srcColor; - vec3 param_14 = compositeRGBToHSL(param_12); - vec3 param_15 = compositeRGBToHSL(param_13); - int param_16 = op; - vec3 param_17 = compositeHSL(param_14, param_15, param_16); - return compositeHSLToRGB(param_17); - } - } - return srcColor; -} - -vec4 composite(vec4 srcColor, vec2 destTextureSize, vec2 fragCoord, int op, sampler2D SPIRV_Cross_CombineddestTextureuSampler) -{ - if (op == 0) - { - return srcColor; - } - vec2 destTexCoord = fragCoord / destTextureSize; - vec4 destColor = texture(SPIRV_Cross_CombineddestTextureuSampler, destTexCoord); - vec3 param = destColor.xyz; - vec3 param_1 = srcColor.xyz; - int param_2 = op; - vec3 blendedRGB = compositeRGB(param, param_1, param_2); - return vec4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0); -} - -void calculateColor(int tileCtrl, int ctrl) -{ - int maskCtrl0 = (tileCtrl >> 0) & 3; - float maskAlpha = 1.0; - float param = maskAlpha; - vec2 param_1 = uMaskTextureSize0[0].xy; - vec3 param_2 = vMaskTexCoord0; - int param_3 = maskCtrl0; - maskAlpha = sampleMask(param, param_1, param_2, param_3, SPIRV_Cross_CombineduMaskTexture0uSampler); - vec4 color = vBaseColor; - int color0Combine = (ctrl >> 6) & 3; - if (color0Combine != 0) - { - int color0Filter = (ctrl >> 4) & 3; - vec2 param_4 = vColorTexCoord0; - vec2 param_5 = uColorTextureSize0[0].xy; - vec2 param_6 = gl_FragCoord.xy; - vec2 param_7 = uFramebufferSize[0].xy; - vec4 param_8 = uFilterParams0[0]; - vec4 param_9 = uFilterParams1[0]; - vec4 param_10 = uFilterParams2[0]; - int param_11 = color0Filter; - vec4 color0 = filterColor(param_4, param_5, param_6, param_7, param_8, param_9, param_10, param_11, SPIRV_Cross_CombineduColorTexture0uSampler, SPIRV_Cross_CombineduGammaLUTuSampler); - vec4 param_12 = color; - vec4 param_13 = color0; - int param_14 = color0Combine; - color = combineColor0(param_12, param_13, param_14); - } - color.w *= maskAlpha; - int compositeOp = (ctrl >> 8) & 15; - vec4 param_15 = color; - vec2 param_16 = uFramebufferSize[0].xy; - vec2 param_17 = gl_FragCoord.xy; - int param_18 = compositeOp; - color = composite(param_15, param_16, param_17, param_18, SPIRV_Cross_CombineduDestTextureuSampler); - vec3 _1389 = color.xyz * color.w; - color = vec4(_1389.x, _1389.y, _1389.z, color.w); - oFragColor = color; -} - -void main() -{ - int param = int(vTileCtrl); - int param_1 = uCtrl[0].x; - calculateColor(param, param_1); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/tile.vs.glsl b/crates/pathfinder/resources/shaders/gl3/tile.vs.glsl deleted file mode 100644 index 39df49168b..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/tile.vs.glsl +++ /dev/null @@ -1,41 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTileSize[1]; -uniform ivec4 uTextureMetadataSize[1]; -uniform vec4 uTransform[4]; -uniform sampler2D SPIRV_Cross_CombineduTextureMetadatauSampler; - -layout(location = 1) in ivec2 aTileOrigin; -layout(location = 0) in ivec2 aTileOffset; -layout(location = 2) in uvec2 aMaskTexCoord0; -layout(location = 4) in int aColor; -out vec2 vColorTexCoord0; -out vec3 vMaskTexCoord0; -layout(location = 3) in ivec2 aMaskBackdrop; -out vec4 vBaseColor; -out float vTileCtrl; -layout(location = 5) in int aTileCtrl; - -void main() -{ - vec2 tileOrigin = vec2(aTileOrigin); - vec2 tileOffset = vec2(aTileOffset); - vec2 position = (tileOrigin + tileOffset) * uTileSize[0].xy; - vec2 maskTexCoord0 = (vec2(aMaskTexCoord0) + tileOffset) * uTileSize[0].xy; - vec2 textureMetadataScale = vec2(1.0) / vec2(uTextureMetadataSize[0].xy); - vec2 metadataEntryCoord = vec2(float((aColor % 128) * 4), float(aColor / 128)); - vec2 colorTexMatrix0Coord = (metadataEntryCoord + vec2(0.5)) * textureMetadataScale; - vec2 colorTexOffsetsCoord = (metadataEntryCoord + vec2(1.5, 0.5)) * textureMetadataScale; - vec2 baseColorCoord = (metadataEntryCoord + vec2(2.5, 0.5)) * textureMetadataScale; - vec4 colorTexMatrix0 = textureLod(SPIRV_Cross_CombineduTextureMetadatauSampler, colorTexMatrix0Coord, 0.0); - vec4 colorTexOffsets = textureLod(SPIRV_Cross_CombineduTextureMetadatauSampler, colorTexOffsetsCoord, 0.0); - vec4 baseColor = textureLod(SPIRV_Cross_CombineduTextureMetadatauSampler, baseColorCoord, 0.0); - vColorTexCoord0 = (mat2(vec2(colorTexMatrix0.xy), vec2(colorTexMatrix0.zw)) * position) + colorTexOffsets.xy; - vMaskTexCoord0 = vec3(maskTexCoord0, float(aMaskBackdrop.x)); - vBaseColor = baseColor; - vTileCtrl = float(aTileCtrl); - gl_Position = mat4(uTransform[0], uTransform[1], uTransform[2], uTransform[3]) * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/tile_clip.fs.glsl b/crates/pathfinder/resources/shaders/gl3/tile_clip.fs.glsl deleted file mode 100644 index 5bbb7a2593..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/tile_clip.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform sampler2D SPIRV_Cross_CombineduSrcuSampler; - -layout(location = 0) out vec4 oFragColor; -in vec2 vTexCoord; -in float vBackdrop; - -void main() -{ - oFragColor = clamp(abs(texture(SPIRV_Cross_CombineduSrcuSampler, vTexCoord) + vec4(vBackdrop)), vec4(0.0), vec4(1.0)); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/tile_clip.vs.glsl b/crates/pathfinder/resources/shaders/gl3/tile_clip.vs.glsl deleted file mode 100644 index 9cf2b9b012..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/tile_clip.vs.glsl +++ /dev/null @@ -1,20 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 1) in ivec2 aDestTileOrigin; -layout(location = 0) in ivec2 aTileOffset; -layout(location = 2) in ivec2 aSrcTileOrigin; -out vec2 vTexCoord; -out float vBackdrop; -layout(location = 3) in int aSrcBackdrop; - -void main() -{ - vec2 destPosition = vec2(aDestTileOrigin + aTileOffset) / vec2(256.0); - vec2 srcPosition = vec2(aSrcTileOrigin + aTileOffset) / vec2(256.0); - vTexCoord = srcPosition; - vBackdrop = float(aSrcBackdrop); - gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), destPosition), 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/tile_copy.fs.glsl b/crates/pathfinder/resources/shaders/gl3/tile_copy.fs.glsl deleted file mode 100644 index 015b6ec45f..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/tile_copy.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uFramebufferSize[1]; -uniform sampler2D SPIRV_Cross_CombineduSrcuSampler; - -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec2 texCoord = gl_FragCoord.xy / uFramebufferSize[0].xy; - oFragColor = texture(SPIRV_Cross_CombineduSrcuSampler, texCoord); -} - diff --git a/crates/pathfinder/resources/shaders/gl3/tile_copy.vs.glsl b/crates/pathfinder/resources/shaders/gl3/tile_copy.vs.glsl deleted file mode 100644 index d20f31e406..0000000000 --- a/crates/pathfinder/resources/shaders/gl3/tile_copy.vs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTileSize[1]; -uniform vec4 uTransform[4]; -layout(location = 0) in ivec2 aTilePosition; - -void main() -{ - vec2 position = vec2(aTilePosition) * uTileSize[0].xy; - gl_Position = mat4(uTransform[0], uTransform[1], uTransform[2], uTransform[3]) * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/blit.fs.glsl b/crates/pathfinder/resources/shaders/gl4/blit.fs.glsl deleted file mode 100644 index 1a9b2d9f13..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/blit.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(binding = 0) uniform sampler2D SPIRV_Cross_CombineduSrcuSampler; - -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec4 color = texture(SPIRV_Cross_CombineduSrcuSampler, vTexCoord); - oFragColor = vec4(color.xyz * color.w, color.w); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/blit.vs.glsl b/crates/pathfinder/resources/shaders/gl4/blit.vs.glsl deleted file mode 100644 index 0a3bf222e4..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/blit.vs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 0) in ivec2 aPosition; -layout(location = 0) out vec2 vTexCoord; - -void main() -{ - vec2 texCoord = vec2(aPosition); - vTexCoord = texCoord; - gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), vec2(aPosition)), 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/clear.fs.glsl b/crates/pathfinder/resources/shaders/gl4/clear.fs.glsl deleted file mode 100644 index bfc45445dc..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/clear.fs.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uColor[1]; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - oFragColor = vec4(uColor[0].xyz, 1.0) * uColor[0].w; -} - diff --git a/crates/pathfinder/resources/shaders/gl4/clear.vs.glsl b/crates/pathfinder/resources/shaders/gl4/clear.vs.glsl deleted file mode 100644 index 76b85239b9..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/clear.vs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uRect[1]; -uniform vec4 uFramebufferSize[1]; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vec2 position = ((mix(uRect[0].xy, uRect[0].zw, vec2(aPosition)) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/debug_solid.fs.glsl b/crates/pathfinder/resources/shaders/gl4/debug_solid.fs.glsl deleted file mode 100644 index bfc45445dc..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/debug_solid.fs.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uColor[1]; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - oFragColor = vec4(uColor[0].xyz, 1.0) * uColor[0].w; -} - diff --git a/crates/pathfinder/resources/shaders/gl4/debug_solid.vs.glsl b/crates/pathfinder/resources/shaders/gl4/debug_solid.vs.glsl deleted file mode 100644 index 705fbc5efb..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/debug_solid.vs.glsl +++ /dev/null @@ -1,13 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uFramebufferSize[1]; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vec2 position = ((vec2(aPosition) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/debug_texture.fs.glsl b/crates/pathfinder/resources/shaders/gl4/debug_texture.fs.glsl deleted file mode 100644 index c35786c4d7..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/debug_texture.fs.glsl +++ /dev/null @@ -1,16 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uColor[1]; -layout(binding = 2) uniform sampler2D SPIRV_Cross_CombineduTextureuSampler; - -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - float alpha = texture(SPIRV_Cross_CombineduTextureuSampler, vTexCoord).x * uColor[0].w; - oFragColor = vec4(uColor[0].xyz, 1.0) * alpha; -} - diff --git a/crates/pathfinder/resources/shaders/gl4/debug_texture.vs.glsl b/crates/pathfinder/resources/shaders/gl4/debug_texture.vs.glsl deleted file mode 100644 index 0097733723..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/debug_texture.vs.glsl +++ /dev/null @@ -1,17 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTextureSize[1]; -uniform vec4 uFramebufferSize[1]; -layout(location = 0) out vec2 vTexCoord; -layout(location = 1) in ivec2 aTexCoord; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vTexCoord = vec2(aTexCoord) / uTextureSize[0].xy; - vec2 position = ((vec2(aPosition) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(position.x, -position.y, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/demo_ground.fs.glsl b/crates/pathfinder/resources/shaders/gl4/demo_ground.fs.glsl deleted file mode 100644 index 2c4c3168e3..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/demo_ground.fs.glsl +++ /dev/null @@ -1,24 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uGridlineColor[1]; -uniform vec4 uGroundColor[1]; -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec2 texCoordPx = fract(vTexCoord) / fwidth(vTexCoord); - vec4 _28; - if (any(lessThanEqual(texCoordPx, vec2(1.0)))) - { - _28 = uGridlineColor[0]; - } - else - { - _28 = uGroundColor[0]; - } - oFragColor = _28; -} - diff --git a/crates/pathfinder/resources/shaders/gl4/demo_ground.vs.glsl b/crates/pathfinder/resources/shaders/gl4/demo_ground.vs.glsl deleted file mode 100644 index ab3fce9c6b..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/demo_ground.vs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform ivec4 uGridlineCount[1]; -uniform vec4 uTransform[4]; -layout(location = 0) out vec2 vTexCoord; -layout(location = 0) in ivec2 aPosition; - -void main() -{ - vTexCoord = vec2(aPosition * ivec2(uGridlineCount[0].x)); - gl_Position = mat4(uTransform[0], uTransform[1], uTransform[2], uTransform[3]) * vec4(ivec4(aPosition.x, 0, aPosition.y, 1)); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/fill.cs.glsl b/crates/pathfinder/resources/shaders/gl4/fill.cs.glsl deleted file mode 100644 index ebdbcb8cb6..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/fill.cs.glsl +++ /dev/null @@ -1,65 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - -layout(local_size_x = 16, local_size_y = 4, local_size_z = 1) in; - -uniform ivec4 uFirstTileIndex[1]; -layout(binding = 6, std430) restrict readonly buffer bFillTileMap -{ - int iFillTileMap[]; -} _159; - -layout(binding = 4, std430) restrict readonly buffer bFills -{ - uvec2 iFills[]; -} _180; - -layout(binding = 5, std430) restrict readonly buffer bNextFills -{ - int iNextFills[]; -} _264; - -layout(binding = 0) uniform writeonly image2D uDest; -layout(binding = 1) uniform sampler2D SPIRV_Cross_CombineduAreaLUTuSampler; - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D SPIRV_Cross_CombinedareaLUTareaLUTSampler) -{ - bvec2 _34 = bvec2(from.x < to.x); - vec2 left = vec2(_34.x ? from.x : to.x, _34.y ? from.y : to.y); - bvec2 _44 = bvec2(from.x < to.x); - vec2 right = vec2(_44.x ? to.x : from.x, _44.y ? to.y : from.y); - vec2 window = clamp(vec2(from.x, to.x), vec2(-0.5), vec2(0.5)); - float offset = mix(window.x, window.y, 0.5) - left.x; - float t = offset / (right.x - left.x); - float y = mix(left.y, right.y, t); - float d = (right.y - left.y) / (right.x - left.x); - float dX = window.x - window.y; - return textureLod(SPIRV_Cross_CombinedareaLUTareaLUTSampler, vec2(y + 8.0, abs(d * dX)) / vec2(16.0), 0.0) * dX; -} - -void main() -{ - ivec2 tileSubCoord = ivec2(gl_LocalInvocationID.xy) * ivec2(1, 4); - uint tileIndexOffset = gl_WorkGroupID.z; - uint tileIndex = tileIndexOffset + uint(uFirstTileIndex[0].x); - int fillIndex = _159.iFillTileMap[tileIndex]; - if (fillIndex < 0) - { - return; - } - vec4 coverages = vec4(0.0); - do - { - uvec2 fill = _180.iFills[fillIndex]; - vec2 from = vec2(float(fill.y & 15u), float((fill.y >> 4u) & 15u)) + (vec2(float(fill.x & 255u), float((fill.x >> 8u) & 255u)) / vec2(256.0)); - vec2 to = vec2(float((fill.y >> 8u) & 15u), float((fill.y >> 12u) & 15u)) + (vec2(float((fill.x >> 16u) & 255u), float((fill.x >> 24u) & 255u)) / vec2(256.0)); - vec2 param = from - (vec2(tileSubCoord) + vec2(0.5)); - vec2 param_1 = to - (vec2(tileSubCoord) + vec2(0.5)); - coverages += computeCoverage(param, param_1, SPIRV_Cross_CombineduAreaLUTuSampler); - fillIndex = _264.iNextFills[fillIndex]; - } while (fillIndex >= 0); - ivec2 tileOrigin = ivec2(int(tileIndex & 255u), int((tileIndex >> 8u) & 255u)) * ivec2(16, 4); - ivec2 destCoord = tileOrigin + ivec2(gl_LocalInvocationID.xy); - imageStore(uDest, destCoord, coverages); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/fill.fs.glsl b/crates/pathfinder/resources/shaders/gl4/fill.fs.glsl deleted file mode 100644 index 01c47c1651..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/fill.fs.glsl +++ /dev/null @@ -1,32 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(binding = 2) uniform sampler2D SPIRV_Cross_CombineduAreaLUTuSampler; - -layout(location = 0) out vec4 oFragColor; -layout(location = 0) in vec2 vFrom; -layout(location = 1) in vec2 vTo; - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D SPIRV_Cross_CombinedareaLUTareaLUTSampler) -{ - bvec2 _34 = bvec2(from.x < to.x); - vec2 left = vec2(_34.x ? from.x : to.x, _34.y ? from.y : to.y); - bvec2 _44 = bvec2(from.x < to.x); - vec2 right = vec2(_44.x ? to.x : from.x, _44.y ? to.y : from.y); - vec2 window = clamp(vec2(from.x, to.x), vec2(-0.5), vec2(0.5)); - float offset = mix(window.x, window.y, 0.5) - left.x; - float t = offset / (right.x - left.x); - float y = mix(left.y, right.y, t); - float d = (right.y - left.y) / (right.x - left.x); - float dX = window.x - window.y; - return texture(SPIRV_Cross_CombinedareaLUTareaLUTSampler, vec2(y + 8.0, abs(d * dX)) / vec2(16.0)) * dX; -} - -void main() -{ - vec2 param = vFrom; - vec2 param_1 = vTo; - oFragColor = computeCoverage(param, param_1, SPIRV_Cross_CombineduAreaLUTuSampler); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/fill.vs.glsl b/crates/pathfinder/resources/shaders/gl4/fill.vs.glsl deleted file mode 100644 index 73baecd0c5..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/fill.vs.glsl +++ /dev/null @@ -1,54 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTileSize[1]; -uniform vec4 uFramebufferSize[1]; -layout(location = 5) in uint aTileIndex; -layout(location = 3) in uint aFromPx; -layout(location = 1) in vec2 aFromSubpx; -layout(location = 4) in uint aToPx; -layout(location = 2) in vec2 aToSubpx; -layout(location = 0) in uvec2 aTessCoord; -layout(location = 0) out vec2 vFrom; -layout(location = 1) out vec2 vTo; - -vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) -{ - uint tilesPerRow = uint(stencilTextureWidth / uTileSize[0].x); - uvec2 tileOffset = uvec2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); - return (vec2(tileOffset) * uTileSize[0].xy) * vec2(1.0, 0.25); -} - -void main() -{ - uint param = aTileIndex; - float param_1 = uFramebufferSize[0].x; - vec2 tileOrigin = computeTileOffset(param, param_1); - vec2 from = vec2(float(aFromPx & 15u), float(aFromPx >> 4u)) + aFromSubpx; - vec2 to = vec2(float(aToPx & 15u), float(aToPx >> 4u)) + aToSubpx; - vec2 position; - if (aTessCoord.x == 0u) - { - position.x = floor(min(from.x, to.x)); - } - else - { - position.x = ceil(max(from.x, to.x)); - } - if (aTessCoord.y == 0u) - { - position.y = floor(min(from.y, to.y)); - } - else - { - position.y = uTileSize[0].y; - } - position.y = floor(position.y * 0.25); - vec2 offset = vec2(0.0, 1.5) - (position * vec2(1.0, 4.0)); - vFrom = from + offset; - vTo = to + offset; - vec2 globalPosition = (((tileOrigin + position) / uFramebufferSize[0].xy) * 2.0) - vec2(1.0); - gl_Position = vec4(globalPosition, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/reproject.fs.glsl b/crates/pathfinder/resources/shaders/gl4/reproject.fs.glsl deleted file mode 100644 index efcd612731..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/reproject.fs.glsl +++ /dev/null @@ -1,17 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uOldTransform[4]; -layout(binding = 2) uniform sampler2D SPIRV_Cross_CombineduTextureuSampler; - -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec4 normTexCoord = mat4(uOldTransform[0], uOldTransform[1], uOldTransform[2], uOldTransform[3]) * vec4(vTexCoord, 0.0, 1.0); - vec2 texCoord = ((normTexCoord.xy / vec2(normTexCoord.w)) + vec2(1.0)) * 0.5; - oFragColor = texture(SPIRV_Cross_CombineduTextureuSampler, texCoord); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/reproject.vs.glsl b/crates/pathfinder/resources/shaders/gl4/reproject.vs.glsl deleted file mode 100644 index d4d7378a8e..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/reproject.vs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uNewTransform[4]; -layout(location = 0) in ivec2 aPosition; -layout(location = 0) out vec2 vTexCoord; - -void main() -{ - vec2 position = vec2(aPosition); - vTexCoord = position; - gl_Position = mat4(uNewTransform[0], uNewTransform[1], uNewTransform[2], uNewTransform[3]) * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/stencil.fs.glsl b/crates/pathfinder/resources/shaders/gl4/stencil.fs.glsl deleted file mode 100644 index c9a19890dc..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/stencil.fs.glsl +++ /dev/null @@ -1,11 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 0) out vec4 oFragColor; - -void main() -{ - oFragColor = vec4(1.0, 0.0, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/stencil.vs.glsl b/crates/pathfinder/resources/shaders/gl4/stencil.vs.glsl deleted file mode 100644 index f56fba14ef..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/stencil.vs.glsl +++ /dev/null @@ -1,11 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 0) in vec3 aPosition; - -void main() -{ - gl_Position = vec4(aPosition, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile.fs.glsl b/crates/pathfinder/resources/shaders/gl4/tile.fs.glsl deleted file mode 100644 index 293cb6da15..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile.fs.glsl +++ /dev/null @@ -1,571 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uMaskTextureSize0[1]; -uniform vec4 uColorTextureSize0[1]; -uniform vec4 uFramebufferSize[1]; -uniform vec4 uFilterParams0[1]; -uniform vec4 uFilterParams1[1]; -uniform vec4 uFilterParams2[1]; -uniform ivec4 uCtrl[1]; -layout(binding = 2) uniform sampler2D SPIRV_Cross_CombineduMaskTexture0uSampler; -layout(binding = 1) uniform sampler2D SPIRV_Cross_CombineduColorTexture0uSampler; -layout(binding = 4) uniform sampler2D SPIRV_Cross_CombineduGammaLUTuSampler; -layout(binding = 3) uniform sampler2D SPIRV_Cross_CombineduDestTextureuSampler; - -layout(location = 0) in vec3 vMaskTexCoord0; -layout(location = 2) in vec4 vBaseColor; -layout(location = 1) in vec2 vColorTexCoord0; -layout(location = 0) out vec4 oFragColor; -layout(location = 3) in float vTileCtrl; - -float sampleMask(float maskAlpha, vec2 maskTextureSize, vec3 maskTexCoord, int maskCtrl, sampler2D SPIRV_Cross_CombinedmaskTextureuSampler) -{ - if (maskCtrl == 0) - { - return maskAlpha; - } - ivec2 maskTexCoordI = ivec2(floor(maskTexCoord.xy)); - vec4 texel = texture(SPIRV_Cross_CombinedmaskTextureuSampler, (vec2(maskTexCoordI / ivec2(1, 4)) + vec2(0.5)) / maskTextureSize); - float coverage = texel[maskTexCoordI.y % 4] + maskTexCoord.z; - if ((maskCtrl & 1) != 0) - { - coverage = abs(coverage); - } - else - { - coverage = 1.0 - abs(1.0 - mod(coverage, 2.0)); - } - return min(maskAlpha, coverage); -} - -vec4 filterRadialGradient(vec2 colorTexCoord, vec2 colorTextureSize, vec2 fragCoord, vec2 framebufferSize, vec4 filterParams0, vec4 filterParams1, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - vec2 lineFrom = filterParams0.xy; - vec2 lineVector = filterParams0.zw; - vec2 radii = filterParams1.xy; - vec2 uvOrigin = filterParams1.zw; - vec2 dP = colorTexCoord - lineFrom; - vec2 dC = lineVector; - float dR = radii.y - radii.x; - float a = dot(dC, dC) - (dR * dR); - float b = dot(dP, dC) + (radii.x * dR); - float c = dot(dP, dP) - (radii.x * radii.x); - float discrim = (b * b) - (a * c); - vec4 color = vec4(0.0); - if (abs(discrim) >= 9.9999997473787516355514526367188e-06) - { - vec2 ts = vec2((vec2(1.0, -1.0) * sqrt(discrim)) + vec2(b)) / vec2(a); - if (ts.x > ts.y) - { - ts = ts.yx; - } - float _566; - if (ts.x >= 0.0) - { - _566 = ts.x; - } - else - { - _566 = ts.y; - } - float t = _566; - color = texture(SPIRV_Cross_CombinedcolorTextureuSampler, uvOrigin + vec2(clamp(t, 0.0, 1.0), 0.0)); - } - return color; -} - -vec4 filterBlur(vec2 colorTexCoord, vec2 colorTextureSize, vec4 filterParams0, vec4 filterParams1, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - vec2 srcOffsetScale = filterParams0.xy / colorTextureSize; - int support = int(filterParams0.z); - vec3 gaussCoeff = filterParams1.xyz; - float gaussSum = gaussCoeff.x; - vec4 color = texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord) * gaussCoeff.x; - vec2 _615 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = vec3(_615.x, _615.y, gaussCoeff.z); - for (int i = 1; i <= support; i += 2) - { - float gaussPartialSum = gaussCoeff.x; - vec2 _635 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = vec3(_635.x, _635.y, gaussCoeff.z); - gaussPartialSum += gaussCoeff.x; - vec2 srcOffset = srcOffsetScale * (float(i) + (gaussCoeff.x / gaussPartialSum)); - color += ((texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord - srcOffset) + texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord + srcOffset)) * gaussPartialSum); - gaussSum += (2.0 * gaussPartialSum); - vec2 _679 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = vec3(_679.x, _679.y, gaussCoeff.z); - } - return color / vec4(gaussSum); -} - -float filterTextSample1Tap(float offset, vec2 colorTexCoord, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - return texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord + vec2(offset, 0.0)).x; -} - -void filterTextSample9Tap(out vec4 outAlphaLeft, out float outAlphaCenter, out vec4 outAlphaRight, vec2 colorTexCoord, vec4 kernel, float onePixel, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - bool wide = kernel.x > 0.0; - float _243; - if (wide) - { - float param = (-4.0) * onePixel; - vec2 param_1 = colorTexCoord; - _243 = filterTextSample1Tap(param, param_1, SPIRV_Cross_CombinedcolorTextureuSampler); - } - else - { - _243 = 0.0; - } - float param_2 = (-3.0) * onePixel; - vec2 param_3 = colorTexCoord; - float param_4 = (-2.0) * onePixel; - vec2 param_5 = colorTexCoord; - float param_6 = (-1.0) * onePixel; - vec2 param_7 = colorTexCoord; - outAlphaLeft = vec4(_243, filterTextSample1Tap(param_2, param_3, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_4, param_5, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_6, param_7, SPIRV_Cross_CombinedcolorTextureuSampler)); - float param_8 = 0.0; - vec2 param_9 = colorTexCoord; - outAlphaCenter = filterTextSample1Tap(param_8, param_9, SPIRV_Cross_CombinedcolorTextureuSampler); - float param_10 = 1.0 * onePixel; - vec2 param_11 = colorTexCoord; - float param_12 = 2.0 * onePixel; - vec2 param_13 = colorTexCoord; - float param_14 = 3.0 * onePixel; - vec2 param_15 = colorTexCoord; - float _303; - if (wide) - { - float param_16 = 4.0 * onePixel; - vec2 param_17 = colorTexCoord; - _303 = filterTextSample1Tap(param_16, param_17, SPIRV_Cross_CombinedcolorTextureuSampler); - } - else - { - _303 = 0.0; - } - outAlphaRight = vec4(filterTextSample1Tap(param_10, param_11, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_12, param_13, SPIRV_Cross_CombinedcolorTextureuSampler), filterTextSample1Tap(param_14, param_15, SPIRV_Cross_CombinedcolorTextureuSampler), _303); -} - -float filterTextConvolve7Tap(vec4 alpha0, vec3 alpha1, vec4 kernel) -{ - return dot(alpha0, kernel) + dot(alpha1, kernel.zyx); -} - -float filterTextGammaCorrectChannel(float bgColor, float fgColor, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - return texture(SPIRV_Cross_CombinedgammaLUTuSampler, vec2(fgColor, 1.0 - bgColor)).x; -} - -vec3 filterTextGammaCorrect(vec3 bgColor, vec3 fgColor, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - float param = bgColor.x; - float param_1 = fgColor.x; - float param_2 = bgColor.y; - float param_3 = fgColor.y; - float param_4 = bgColor.z; - float param_5 = fgColor.z; - return vec3(filterTextGammaCorrectChannel(param, param_1, SPIRV_Cross_CombinedgammaLUTuSampler), filterTextGammaCorrectChannel(param_2, param_3, SPIRV_Cross_CombinedgammaLUTuSampler), filterTextGammaCorrectChannel(param_4, param_5, SPIRV_Cross_CombinedgammaLUTuSampler)); -} - -vec4 filterText(vec2 colorTexCoord, vec2 colorTextureSize, vec4 filterParams0, vec4 filterParams1, vec4 filterParams2, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - vec4 kernel = filterParams0; - vec3 bgColor = filterParams1.xyz; - vec3 fgColor = filterParams2.xyz; - bool gammaCorrectionEnabled = filterParams2.w != 0.0; - vec3 alpha; - if (kernel.w == 0.0) - { - alpha = texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord).xxx; - } - else - { - vec2 param_3 = colorTexCoord; - vec4 param_4 = kernel; - float param_5 = 1.0 / colorTextureSize.x; - vec4 param; - float param_1; - vec4 param_2; - filterTextSample9Tap(param, param_1, param_2, param_3, param_4, param_5, SPIRV_Cross_CombinedcolorTextureuSampler); - vec4 alphaLeft = param; - float alphaCenter = param_1; - vec4 alphaRight = param_2; - vec4 param_6 = alphaLeft; - vec3 param_7 = vec3(alphaCenter, alphaRight.xy); - vec4 param_8 = kernel; - float r = filterTextConvolve7Tap(param_6, param_7, param_8); - vec4 param_9 = vec4(alphaLeft.yzw, alphaCenter); - vec3 param_10 = alphaRight.xyz; - vec4 param_11 = kernel; - float g = filterTextConvolve7Tap(param_9, param_10, param_11); - vec4 param_12 = vec4(alphaLeft.zw, alphaCenter, alphaRight.x); - vec3 param_13 = alphaRight.yzw; - vec4 param_14 = kernel; - float b = filterTextConvolve7Tap(param_12, param_13, param_14); - alpha = vec3(r, g, b); - } - if (gammaCorrectionEnabled) - { - vec3 param_15 = bgColor; - vec3 param_16 = alpha; - alpha = filterTextGammaCorrect(param_15, param_16, SPIRV_Cross_CombinedgammaLUTuSampler); - } - return vec4(mix(bgColor, fgColor, alpha), 1.0); -} - -vec4 sampleColor(vec2 colorTexCoord, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - return texture(SPIRV_Cross_CombinedcolorTextureuSampler, colorTexCoord); -} - -vec4 filterNone(vec2 colorTexCoord, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler) -{ - vec2 param = colorTexCoord; - return sampleColor(param, SPIRV_Cross_CombinedcolorTextureuSampler); -} - -vec4 filterColor(vec2 colorTexCoord, vec2 colorTextureSize, vec2 fragCoord, vec2 framebufferSize, vec4 filterParams0, vec4 filterParams1, vec4 filterParams2, int colorFilter, sampler2D SPIRV_Cross_CombinedcolorTextureuSampler, sampler2D SPIRV_Cross_CombinedgammaLUTuSampler) -{ - switch (colorFilter) - { - case 1: - { - vec2 param = colorTexCoord; - vec2 param_1 = colorTextureSize; - vec2 param_2 = fragCoord; - vec2 param_3 = framebufferSize; - vec4 param_4 = filterParams0; - vec4 param_5 = filterParams1; - return filterRadialGradient(param, param_1, param_2, param_3, param_4, param_5, SPIRV_Cross_CombinedcolorTextureuSampler); - } - case 3: - { - vec2 param_6 = colorTexCoord; - vec2 param_7 = colorTextureSize; - vec4 param_8 = filterParams0; - vec4 param_9 = filterParams1; - return filterBlur(param_6, param_7, param_8, param_9, SPIRV_Cross_CombinedcolorTextureuSampler); - } - case 2: - { - vec2 param_10 = colorTexCoord; - vec2 param_11 = colorTextureSize; - vec4 param_12 = filterParams0; - vec4 param_13 = filterParams1; - vec4 param_14 = filterParams2; - return filterText(param_10, param_11, param_12, param_13, param_14, SPIRV_Cross_CombinedcolorTextureuSampler, SPIRV_Cross_CombinedgammaLUTuSampler); - } - } - vec2 param_15 = colorTexCoord; - return filterNone(param_15, SPIRV_Cross_CombinedcolorTextureuSampler); -} - -vec4 combineColor0(vec4 destColor, vec4 srcColor, int op) -{ - switch (op) - { - case 1: - { - return vec4(srcColor.xyz, srcColor.w * destColor.w); - } - case 2: - { - return vec4(destColor.xyz, srcColor.w * destColor.w); - } - } - return destColor; -} - -vec3 compositeScreen(vec3 destColor, vec3 srcColor) -{ - return (destColor + srcColor) - (destColor * srcColor); -} - -vec3 compositeSelect(bvec3 cond, vec3 ifTrue, vec3 ifFalse) -{ - float _745; - if (cond.x) - { - _745 = ifTrue.x; - } - else - { - _745 = ifFalse.x; - } - float _756; - if (cond.y) - { - _756 = ifTrue.y; - } - else - { - _756 = ifFalse.y; - } - float _767; - if (cond.z) - { - _767 = ifTrue.z; - } - else - { - _767 = ifFalse.z; - } - return vec3(_745, _756, _767); -} - -vec3 compositeHardLight(vec3 destColor, vec3 srcColor) -{ - vec3 param = destColor; - vec3 param_1 = (vec3(2.0) * srcColor) - vec3(1.0); - bvec3 param_2 = lessThanEqual(srcColor, vec3(0.5)); - vec3 param_3 = (destColor * vec3(2.0)) * srcColor; - vec3 param_4 = compositeScreen(param, param_1); - return compositeSelect(param_2, param_3, param_4); -} - -vec3 compositeColorDodge(vec3 destColor, vec3 srcColor) -{ - bvec3 destZero = equal(destColor, vec3(0.0)); - bvec3 srcOne = equal(srcColor, vec3(1.0)); - bvec3 param = srcOne; - vec3 param_1 = vec3(1.0); - vec3 param_2 = destColor / (vec3(1.0) - srcColor); - bvec3 param_3 = destZero; - vec3 param_4 = vec3(0.0); - vec3 param_5 = compositeSelect(param, param_1, param_2); - return compositeSelect(param_3, param_4, param_5); -} - -vec3 compositeSoftLight(vec3 destColor, vec3 srcColor) -{ - bvec3 param = lessThanEqual(destColor, vec3(0.25)); - vec3 param_1 = ((((vec3(16.0) * destColor) - vec3(12.0)) * destColor) + vec3(4.0)) * destColor; - vec3 param_2 = sqrt(destColor); - vec3 darkenedDestColor = compositeSelect(param, param_1, param_2); - bvec3 param_3 = lessThanEqual(srcColor, vec3(0.5)); - vec3 param_4 = destColor * (vec3(1.0) - destColor); - vec3 param_5 = darkenedDestColor - destColor; - vec3 factor = compositeSelect(param_3, param_4, param_5); - return destColor + (((srcColor * 2.0) - vec3(1.0)) * factor); -} - -float compositeDivide(float num, float denom) -{ - float _781; - if (denom != 0.0) - { - _781 = num / denom; - } - else - { - _781 = 0.0; - } - return _781; -} - -vec3 compositeRGBToHSL(vec3 rgb) -{ - float v = max(max(rgb.x, rgb.y), rgb.z); - float xMin = min(min(rgb.x, rgb.y), rgb.z); - float c = v - xMin; - float l = mix(xMin, v, 0.5); - vec3 _887; - if (rgb.x == v) - { - _887 = vec3(0.0, rgb.yz); - } - else - { - vec3 _900; - if (rgb.y == v) - { - _900 = vec3(2.0, rgb.zx); - } - else - { - _900 = vec3(4.0, rgb.xy); - } - _887 = _900; - } - vec3 terms = _887; - float param = ((terms.x * c) + terms.y) - terms.z; - float param_1 = c; - float h = 1.0471975803375244140625 * compositeDivide(param, param_1); - float param_2 = c; - float param_3 = v; - float s = compositeDivide(param_2, param_3); - return vec3(h, s, l); -} - -vec3 compositeHSL(vec3 destColor, vec3 srcColor, int op) -{ - switch (op) - { - case 12: - { - return vec3(srcColor.x, destColor.y, destColor.z); - } - case 13: - { - return vec3(destColor.x, srcColor.y, destColor.z); - } - case 14: - { - return vec3(srcColor.x, srcColor.y, destColor.z); - } - default: - { - return vec3(destColor.x, destColor.y, srcColor.z); - } - } -} - -vec3 compositeHSLToRGB(vec3 hsl) -{ - float a = hsl.y * min(hsl.z, 1.0 - hsl.z); - vec3 ks = mod(vec3(0.0, 8.0, 4.0) + vec3(hsl.x * 1.90985929965972900390625), vec3(12.0)); - return hsl.zzz - (clamp(min(ks - vec3(3.0), vec3(9.0) - ks), vec3(-1.0), vec3(1.0)) * a); -} - -vec3 compositeRGB(vec3 destColor, vec3 srcColor, int op) -{ - switch (op) - { - case 1: - { - return destColor * srcColor; - } - case 2: - { - vec3 param = destColor; - vec3 param_1 = srcColor; - return compositeScreen(param, param_1); - } - case 3: - { - vec3 param_2 = srcColor; - vec3 param_3 = destColor; - return compositeHardLight(param_2, param_3); - } - case 4: - { - return min(destColor, srcColor); - } - case 5: - { - return max(destColor, srcColor); - } - case 6: - { - vec3 param_4 = destColor; - vec3 param_5 = srcColor; - return compositeColorDodge(param_4, param_5); - } - case 7: - { - vec3 param_6 = vec3(1.0) - destColor; - vec3 param_7 = vec3(1.0) - srcColor; - return vec3(1.0) - compositeColorDodge(param_6, param_7); - } - case 8: - { - vec3 param_8 = destColor; - vec3 param_9 = srcColor; - return compositeHardLight(param_8, param_9); - } - case 9: - { - vec3 param_10 = destColor; - vec3 param_11 = srcColor; - return compositeSoftLight(param_10, param_11); - } - case 10: - { - return abs(destColor - srcColor); - } - case 11: - { - return (destColor + srcColor) - ((vec3(2.0) * destColor) * srcColor); - } - case 12: - case 13: - case 14: - case 15: - { - vec3 param_12 = destColor; - vec3 param_13 = srcColor; - vec3 param_14 = compositeRGBToHSL(param_12); - vec3 param_15 = compositeRGBToHSL(param_13); - int param_16 = op; - vec3 param_17 = compositeHSL(param_14, param_15, param_16); - return compositeHSLToRGB(param_17); - } - } - return srcColor; -} - -vec4 composite(vec4 srcColor, vec2 destTextureSize, vec2 fragCoord, int op, sampler2D SPIRV_Cross_CombineddestTextureuSampler) -{ - if (op == 0) - { - return srcColor; - } - vec2 destTexCoord = fragCoord / destTextureSize; - vec4 destColor = texture(SPIRV_Cross_CombineddestTextureuSampler, destTexCoord); - vec3 param = destColor.xyz; - vec3 param_1 = srcColor.xyz; - int param_2 = op; - vec3 blendedRGB = compositeRGB(param, param_1, param_2); - return vec4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0); -} - -void calculateColor(int tileCtrl, int ctrl) -{ - int maskCtrl0 = (tileCtrl >> 0) & 3; - float maskAlpha = 1.0; - float param = maskAlpha; - vec2 param_1 = uMaskTextureSize0[0].xy; - vec3 param_2 = vMaskTexCoord0; - int param_3 = maskCtrl0; - maskAlpha = sampleMask(param, param_1, param_2, param_3, SPIRV_Cross_CombineduMaskTexture0uSampler); - vec4 color = vBaseColor; - int color0Combine = (ctrl >> 6) & 3; - if (color0Combine != 0) - { - int color0Filter = (ctrl >> 4) & 3; - vec2 param_4 = vColorTexCoord0; - vec2 param_5 = uColorTextureSize0[0].xy; - vec2 param_6 = gl_FragCoord.xy; - vec2 param_7 = uFramebufferSize[0].xy; - vec4 param_8 = uFilterParams0[0]; - vec4 param_9 = uFilterParams1[0]; - vec4 param_10 = uFilterParams2[0]; - int param_11 = color0Filter; - vec4 color0 = filterColor(param_4, param_5, param_6, param_7, param_8, param_9, param_10, param_11, SPIRV_Cross_CombineduColorTexture0uSampler, SPIRV_Cross_CombineduGammaLUTuSampler); - vec4 param_12 = color; - vec4 param_13 = color0; - int param_14 = color0Combine; - color = combineColor0(param_12, param_13, param_14); - } - color.w *= maskAlpha; - int compositeOp = (ctrl >> 8) & 15; - vec4 param_15 = color; - vec2 param_16 = uFramebufferSize[0].xy; - vec2 param_17 = gl_FragCoord.xy; - int param_18 = compositeOp; - color = composite(param_15, param_16, param_17, param_18, SPIRV_Cross_CombineduDestTextureuSampler); - vec3 _1389 = color.xyz * color.w; - color = vec4(_1389.x, _1389.y, _1389.z, color.w); - oFragColor = color; -} - -void main() -{ - int param = int(vTileCtrl); - int param_1 = uCtrl[0].x; - calculateColor(param, param_1); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile.vs.glsl b/crates/pathfinder/resources/shaders/gl4/tile.vs.glsl deleted file mode 100644 index 443710a2f9..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile.vs.glsl +++ /dev/null @@ -1,41 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTileSize[1]; -uniform ivec4 uTextureMetadataSize[1]; -uniform vec4 uTransform[4]; -layout(binding = 2) uniform sampler2D SPIRV_Cross_CombineduTextureMetadatauSampler; - -layout(location = 1) in ivec2 aTileOrigin; -layout(location = 0) in ivec2 aTileOffset; -layout(location = 2) in uvec2 aMaskTexCoord0; -layout(location = 4) in int aColor; -layout(location = 1) out vec2 vColorTexCoord0; -layout(location = 0) out vec3 vMaskTexCoord0; -layout(location = 3) in ivec2 aMaskBackdrop; -layout(location = 2) out vec4 vBaseColor; -layout(location = 3) out float vTileCtrl; -layout(location = 5) in int aTileCtrl; - -void main() -{ - vec2 tileOrigin = vec2(aTileOrigin); - vec2 tileOffset = vec2(aTileOffset); - vec2 position = (tileOrigin + tileOffset) * uTileSize[0].xy; - vec2 maskTexCoord0 = (vec2(aMaskTexCoord0) + tileOffset) * uTileSize[0].xy; - vec2 textureMetadataScale = vec2(1.0) / vec2(uTextureMetadataSize[0].xy); - vec2 metadataEntryCoord = vec2(float((aColor % 128) * 4), float(aColor / 128)); - vec2 colorTexMatrix0Coord = (metadataEntryCoord + vec2(0.5)) * textureMetadataScale; - vec2 colorTexOffsetsCoord = (metadataEntryCoord + vec2(1.5, 0.5)) * textureMetadataScale; - vec2 baseColorCoord = (metadataEntryCoord + vec2(2.5, 0.5)) * textureMetadataScale; - vec4 colorTexMatrix0 = textureLod(SPIRV_Cross_CombineduTextureMetadatauSampler, colorTexMatrix0Coord, 0.0); - vec4 colorTexOffsets = textureLod(SPIRV_Cross_CombineduTextureMetadatauSampler, colorTexOffsetsCoord, 0.0); - vec4 baseColor = textureLod(SPIRV_Cross_CombineduTextureMetadatauSampler, baseColorCoord, 0.0); - vColorTexCoord0 = (mat2(vec2(colorTexMatrix0.xy), vec2(colorTexMatrix0.zw)) * position) + colorTexOffsets.xy; - vMaskTexCoord0 = vec3(maskTexCoord0, float(aMaskBackdrop.x)); - vBaseColor = baseColor; - vTileCtrl = float(aTileCtrl); - gl_Position = mat4(uTransform[0], uTransform[1], uTransform[2], uTransform[3]) * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_clip.fs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_clip.fs.glsl deleted file mode 100644 index 871231fc0f..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_clip.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(binding = 0) uniform sampler2D SPIRV_Cross_CombineduSrcuSampler; - -layout(location = 0) out vec4 oFragColor; -layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in float vBackdrop; - -void main() -{ - oFragColor = clamp(abs(texture(SPIRV_Cross_CombineduSrcuSampler, vTexCoord) + vec4(vBackdrop)), vec4(0.0), vec4(1.0)); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_clip.vs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_clip.vs.glsl deleted file mode 100644 index bf6c55d9ea..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_clip.vs.glsl +++ /dev/null @@ -1,20 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -layout(location = 1) in ivec2 aDestTileOrigin; -layout(location = 0) in ivec2 aTileOffset; -layout(location = 2) in ivec2 aSrcTileOrigin; -layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out float vBackdrop; -layout(location = 3) in int aSrcBackdrop; - -void main() -{ - vec2 destPosition = vec2(aDestTileOrigin + aTileOffset) / vec2(256.0); - vec2 srcPosition = vec2(aSrcTileOrigin + aTileOffset) / vec2(256.0); - vTexCoord = srcPosition; - vBackdrop = float(aSrcBackdrop); - gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), destPosition), 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_copy.fs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_copy.fs.glsl deleted file mode 100644 index 48abe4391d..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_copy.fs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uFramebufferSize[1]; -layout(binding = 3) uniform sampler2D SPIRV_Cross_CombineduSrcuSampler; - -layout(location = 0) out vec4 oFragColor; - -void main() -{ - vec2 texCoord = gl_FragCoord.xy / uFramebufferSize[0].xy; - oFragColor = texture(SPIRV_Cross_CombineduSrcuSampler, texCoord); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_copy.vs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_copy.vs.glsl deleted file mode 100644 index d20f31e406..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_copy.vs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - -uniform vec4 uTileSize[1]; -uniform vec4 uTransform[4]; -layout(location = 0) in ivec2 aTilePosition; - -void main() -{ - vec2 position = vec2(aTilePosition) * uTileSize[0].xy; - gl_Position = mat4(uTransform[0], uTransform[1], uTransform[2], uTransform[3]) * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_fill.cs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_fill.cs.glsl deleted file mode 100644 index 025f2e2b92..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_fill.cs.glsl +++ /dev/null @@ -1,782 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - - - - - - - - - - - -#extension GL_GOOGLE_include_directive : enable - -precision highp float; -precision highp sampler2D; - -layout(local_size_x = 16, local_size_y = 4)in; - -layout(rgba8)uniform image2D uDestImage; -uniform sampler2D uTextureMetadata; -uniform ivec2 uTextureMetadataSize; -uniform sampler2D uColorTexture0; -uniform sampler2D uMaskTexture0; -uniform sampler2D uGammaLUT; -uniform vec2 uTileSize; -uniform vec4 uFilterParams0; -uniform vec4 uFilterParams1; -uniform vec4 uFilterParams2; -uniform vec2 uFramebufferSize; -uniform vec2 uColorTextureSize0; -uniform int uCtrl; -uniform sampler2D uAreaLUT; - -layout(std430, binding = 0)buffer bFills { - restrict readonly uvec2 iFills[]; -}; - -layout(std430, binding = 1)buffer bNextFills { - restrict readonly int iNextFills[]; -}; - -layout(std430, binding = 2)buffer bFillTileMap { - restrict readonly int iFillTileMap[]; -}; - -layout(std430, binding = 3)buffer bTiles { - restrict readonly uint iTiles[]; -}; - -layout(std430, binding = 4)buffer bNextTiles { - restrict readonly int iNextTiles[]; -}; - -layout(std430, binding = 5)buffer bFirstTiles { - restrict readonly int iFirstTiles[]; -}; - - - - - - - - - - - - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D areaLUT){ - - vec2 left = from . x < to . x ? from : to, right = from . x < to . x ? to : from; - - - vec2 window = clamp(vec2(from . x, to . x), - 0.5, 0.5); - float offset = mix(window . x, window . y, 0.5)- left . x; - float t = offset /(right . x - left . x); - - - float y = mix(left . y, right . y, t); - float d =(right . y - left . y)/(right . x - left . x); - - - float dX = window . x - window . y; - return texture(areaLUT, vec2(y + 8.0, abs(d * dX))/ 16.0)* dX; -} - - - - - - - - - - - - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D areaLUT); - -ivec2 calculateTileOrigin(uint tileIndex){ - return ivec2(tileIndex & 0xff,(tileIndex >> 8u)& 0xff)* 16; -} - -vec4 calculateFillAlpha(ivec2 tileSubCoord, uint tileIndex){ - int fillIndex = iFillTileMap[tileIndex]; - if(fillIndex < 0) - return vec4(0.0); - - vec4 coverages = vec4(0.0); - do { - uvec2 fill = iFills[fillIndex]; - vec2 from = vec2(fill . y & 0xf,(fill . y >> 4u)& 0xf)+ - vec2(fill . x & 0xff,(fill . x >> 8u)& 0xff)/ 256.0; - vec2 to = vec2((fill . y >> 8u)& 0xf,(fill . y >> 12u)& 0xf)+ - vec2((fill . x >> 16u)& 0xff,(fill . x >> 24u)& 0xff)/ 256.0; - - coverages += computeCoverage(from -(vec2(tileSubCoord)+ vec2(0.5)), - to -(vec2(tileSubCoord)+ vec2(0.5)), - uAreaLUT); - - fillIndex = iNextFills[fillIndex]; - } while(fillIndex >= 0); - - return coverages; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -vec4 sampleColor(sampler2D colorTexture, vec2 colorTexCoord){ - return texture(colorTexture, colorTexCoord); -} - - - -vec4 combineColor0(vec4 destColor, vec4 srcColor, int op){ - switch(op){ - case 0x1 : - return vec4(srcColor . rgb, srcColor . a * destColor . a); - case 0x2 : - return vec4(destColor . rgb, srcColor . a * destColor . a); - } - return destColor; -} - - - -float filterTextSample1Tap(float offset, sampler2D colorTexture, vec2 colorTexCoord){ - return texture(colorTexture, colorTexCoord + vec2(offset, 0.0)). r; -} - - -void filterTextSample9Tap(out vec4 outAlphaLeft, - out float outAlphaCenter, - out vec4 outAlphaRight, - sampler2D colorTexture, - vec2 colorTexCoord, - vec4 kernel, - float onePixel){ - bool wide = kernel . x > 0.0; - outAlphaLeft = - vec4(wide ? filterTextSample1Tap(- 4.0 * onePixel, colorTexture, colorTexCoord): 0.0, - filterTextSample1Tap(- 3.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(- 2.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(- 1.0 * onePixel, colorTexture, colorTexCoord)); - outAlphaCenter = filterTextSample1Tap(0.0, colorTexture, colorTexCoord); - outAlphaRight = - vec4(filterTextSample1Tap(1.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(2.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(3.0 * onePixel, colorTexture, colorTexCoord), - wide ? filterTextSample1Tap(4.0 * onePixel, colorTexture, colorTexCoord): 0.0); -} - -float filterTextConvolve7Tap(vec4 alpha0, vec3 alpha1, vec4 kernel){ - return dot(alpha0, kernel)+ dot(alpha1, kernel . zyx); -} - -float filterTextGammaCorrectChannel(float bgColor, float fgColor, sampler2D gammaLUT){ - return texture(gammaLUT, vec2(fgColor, 1.0 - bgColor)). r; -} - - -vec3 filterTextGammaCorrect(vec3 bgColor, vec3 fgColor, sampler2D gammaLUT){ - return vec3(filterTextGammaCorrectChannel(bgColor . r, fgColor . r, gammaLUT), - filterTextGammaCorrectChannel(bgColor . g, fgColor . g, gammaLUT), - filterTextGammaCorrectChannel(bgColor . b, fgColor . b, gammaLUT)); -} - - - - - - -vec4 filterText(vec2 colorTexCoord, - sampler2D colorTexture, - sampler2D gammaLUT, - vec2 colorTextureSize, - vec4 filterParams0, - vec4 filterParams1, - vec4 filterParams2){ - - vec4 kernel = filterParams0; - vec3 bgColor = filterParams1 . rgb; - vec3 fgColor = filterParams2 . rgb; - bool gammaCorrectionEnabled = filterParams2 . a != 0.0; - - - vec3 alpha; - if(kernel . w == 0.0){ - alpha = texture(colorTexture, colorTexCoord). rrr; - } else { - vec4 alphaLeft, alphaRight; - float alphaCenter; - filterTextSample9Tap(alphaLeft, - alphaCenter, - alphaRight, - colorTexture, - colorTexCoord, - kernel, - 1.0 / colorTextureSize . x); - - float r = filterTextConvolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight . xy), kernel); - float g = filterTextConvolve7Tap(vec4(alphaLeft . yzw, alphaCenter), alphaRight . xyz, kernel); - float b = filterTextConvolve7Tap(vec4(alphaLeft . zw, alphaCenter, alphaRight . x), - alphaRight . yzw, - kernel); - - alpha = vec3(r, g, b); - } - - - if(gammaCorrectionEnabled) - alpha = filterTextGammaCorrect(bgColor, alpha, gammaLUT); - - - return vec4(mix(bgColor, fgColor, alpha), 1.0); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -vec4 filterRadialGradient(vec2 colorTexCoord, - sampler2D colorTexture, - vec2 colorTextureSize, - vec2 fragCoord, - vec2 framebufferSize, - vec4 filterParams0, - vec4 filterParams1){ - vec2 lineFrom = filterParams0 . xy, lineVector = filterParams0 . zw; - vec2 radii = filterParams1 . xy, uvOrigin = filterParams1 . zw; - - vec2 dP = colorTexCoord - lineFrom, dC = lineVector; - float dR = radii . y - radii . x; - - float a = dot(dC, dC)- dR * dR; - float b = dot(dP, dC)+ radii . x * dR; - float c = dot(dP, dP)- radii . x * radii . x; - float discrim = b * b - a * c; - - vec4 color = vec4(0.0); - if(abs(discrim)>= 0.00001){ - vec2 ts = vec2(sqrt(discrim)* vec2(1.0, - 1.0)+ vec2(b))/ vec2(a); - if(ts . x > ts . y) - ts = ts . yx; - float t = ts . x >= 0.0 ? ts . x : ts . y; - color = texture(colorTexture, uvOrigin + vec2(clamp(t, 0.0, 1.0), 0.0)); - } - - return color; -} - - - - - - -vec4 filterBlur(vec2 colorTexCoord, - sampler2D colorTexture, - vec2 colorTextureSize, - vec4 filterParams0, - vec4 filterParams1){ - - vec2 srcOffsetScale = filterParams0 . xy / colorTextureSize; - int support = int(filterParams0 . z); - vec3 gaussCoeff = filterParams1 . xyz; - - - float gaussSum = gaussCoeff . x; - vec4 color = texture(colorTexture, colorTexCoord)* gaussCoeff . x; - gaussCoeff . xy *= gaussCoeff . yz; - - - - - - - - - - for(int i = 1;i <= support;i += 2){ - float gaussPartialSum = gaussCoeff . x; - gaussCoeff . xy *= gaussCoeff . yz; - gaussPartialSum += gaussCoeff . x; - - vec2 srcOffset = srcOffsetScale *(float(i)+ gaussCoeff . x / gaussPartialSum); - color +=(texture(colorTexture, colorTexCoord - srcOffset)+ - texture(colorTexture, colorTexCoord + srcOffset))* gaussPartialSum; - - gaussSum += 2.0 * gaussPartialSum; - gaussCoeff . xy *= gaussCoeff . yz; - } - - - return color / gaussSum; -} - -vec4 filterNone(vec2 colorTexCoord, sampler2D colorTexture){ - return sampleColor(colorTexture, colorTexCoord); -} - -vec4 filterColor(vec2 colorTexCoord, - sampler2D colorTexture, - sampler2D gammaLUT, - vec2 colorTextureSize, - vec2 fragCoord, - vec2 framebufferSize, - vec4 filterParams0, - vec4 filterParams1, - vec4 filterParams2, - int colorFilter){ - switch(colorFilter){ - case 0x1 : - return filterRadialGradient(colorTexCoord, - colorTexture, - colorTextureSize, - fragCoord, - framebufferSize, - filterParams0, - filterParams1); - case 0x3 : - return filterBlur(colorTexCoord, - colorTexture, - colorTextureSize, - filterParams0, - filterParams1); - case 0x2 : - return filterText(colorTexCoord, - colorTexture, - gammaLUT, - colorTextureSize, - filterParams0, - filterParams1, - filterParams2); - } - return filterNone(colorTexCoord, colorTexture); -} - - - -vec3 compositeSelect(bvec3 cond, vec3 ifTrue, vec3 ifFalse){ - return vec3(cond . x ? ifTrue . x : ifFalse . x, - cond . y ? ifTrue . y : ifFalse . y, - cond . z ? ifTrue . z : ifFalse . z); -} - -float compositeDivide(float num, float denom){ - return denom != 0.0 ? num / denom : 0.0; -} - -vec3 compositeColorDodge(vec3 destColor, vec3 srcColor){ - bvec3 destZero = equal(destColor, vec3(0.0)), srcOne = equal(srcColor, vec3(1.0)); - return compositeSelect(destZero, - vec3(0.0), - compositeSelect(srcOne, vec3(1.0), destColor /(vec3(1.0)- srcColor))); -} - - -vec3 compositeHSLToRGB(vec3 hsl){ - float a = hsl . y * min(hsl . z, 1.0 - hsl . z); - vec3 ks = mod(vec3(0.0, 8.0, 4.0)+ vec3(hsl . x * 1.9098593171027443), 12.0); - return hsl . zzz - clamp(min(ks - vec3(3.0), vec3(9.0)- ks), - 1.0, 1.0)* a; -} - - -vec3 compositeRGBToHSL(vec3 rgb){ - float v = max(max(rgb . r, rgb . g), rgb . b), xMin = min(min(rgb . r, rgb . g), rgb . b); - float c = v - xMin, l = mix(xMin, v, 0.5); - vec3 terms = rgb . r == v ? vec3(0.0, rgb . gb): - rgb . g == v ? vec3(2.0, rgb . br): - vec3(4.0, rgb . rg); - float h = 1.0471975511965976 * compositeDivide(terms . x * c + terms . y - terms . z, c); - float s = compositeDivide(c, v); - return vec3(h, s, l); -} - -vec3 compositeScreen(vec3 destColor, vec3 srcColor){ - return destColor + srcColor - destColor * srcColor; -} - -vec3 compositeHardLight(vec3 destColor, vec3 srcColor){ - return compositeSelect(lessThanEqual(srcColor, vec3(0.5)), - destColor * vec3(2.0)* srcColor, - compositeScreen(destColor, vec3(2.0)* srcColor - vec3(1.0))); -} - -vec3 compositeSoftLight(vec3 destColor, vec3 srcColor){ - vec3 darkenedDestColor = - compositeSelect(lessThanEqual(destColor, vec3(0.25)), - ((vec3(16.0)* destColor - 12.0)* destColor + 4.0)* destColor, - sqrt(destColor)); - vec3 factor = compositeSelect(lessThanEqual(srcColor, vec3(0.5)), - destColor *(vec3(1.0)- destColor), - darkenedDestColor - destColor); - return destColor +(srcColor * 2.0 - 1.0)* factor; -} - -vec3 compositeHSL(vec3 destColor, vec3 srcColor, int op){ - switch(op){ - case 0xc : - return vec3(srcColor . x, destColor . y, destColor . z); - case 0xd : - return vec3(destColor . x, srcColor . y, destColor . z); - case 0xe : - return vec3(srcColor . x, srcColor . y, destColor . z); - default : - return vec3(destColor . x, destColor . y, srcColor . z); - } -} - -vec3 compositeRGB(vec3 destColor, vec3 srcColor, int op){ - switch(op){ - case 0x1 : - return destColor * srcColor; - case 0x2 : - return compositeScreen(destColor, srcColor); - case 0x3 : - return compositeHardLight(srcColor, destColor); - case 0x4 : - return min(destColor, srcColor); - case 0x5 : - return max(destColor, srcColor); - case 0x6 : - return compositeColorDodge(destColor, srcColor); - case 0x7 : - return vec3(1.0)- compositeColorDodge(vec3(1.0)- destColor, vec3(1.0)- srcColor); - case 0x8 : - return compositeHardLight(destColor, srcColor); - case 0x9 : - return compositeSoftLight(destColor, srcColor); - case 0xa : - return abs(destColor - srcColor); - case 0xb : - return destColor + srcColor - vec3(2.0)* destColor * srcColor; - case 0xc : - case 0xd : - case 0xe : - case 0xf : - return compositeHSLToRGB(compositeHSL(compositeRGBToHSL(destColor), - compositeRGBToHSL(srcColor), - op)); - } - return srcColor; -} - -vec4 composite(vec4 srcColor, - sampler2D destTexture, - vec2 destTextureSize, - vec2 fragCoord, - int op){ - if(op == 0x0) - return srcColor; - - - vec2 destTexCoord = fragCoord / destTextureSize; - vec4 destColor = texture(destTexture, destTexCoord); - vec3 blendedRGB = compositeRGB(destColor . rgb, srcColor . rgb, op); - return vec4(srcColor . a *(1.0 - destColor . a)* srcColor . rgb + - srcColor . a * destColor . a * blendedRGB + - (1.0 - srcColor . a)* destColor . rgb, - 1.0); -} - - - -float sampleMask(float maskAlpha, - sampler2D maskTexture, - vec2 maskTextureSize, - vec3 maskTexCoord, - int maskCtrl){ - if(maskCtrl == 0) - return maskAlpha; - - ivec2 maskTexCoordI = ivec2(floor(maskTexCoord . xy)); - vec4 texel = texture(maskTexture,(vec2(maskTexCoordI / ivec2(1, 4))+ 0.5)/ maskTextureSize); - float coverage = texel[maskTexCoordI . y % 4]+ maskTexCoord . z; - - if((maskCtrl & 0x1)!= 0) - coverage = abs(coverage); - else - coverage = 1.0 - abs(1.0 - mod(coverage, 2.0)); - return min(maskAlpha, coverage); -} - - - -vec4 calculateColorWithMaskAlpha(float maskAlpha, - vec4 baseColor, - vec2 colorTexCoord0, - vec2 fragCoord, - int ctrl){ - - vec4 color = baseColor; - int color0Combine =(ctrl >> 6)& - 0x3; - if(color0Combine != 0){ - int color0Filter =(ctrl >> 4)& 0x3; - vec4 color0 = filterColor(colorTexCoord0, - uColorTexture0, - uGammaLUT, - uColorTextureSize0, - fragCoord, - uFramebufferSize, - uFilterParams0, - uFilterParams1, - uFilterParams2, - color0Filter); - color = combineColor0(color, color0, color0Combine); - } - - - color . a *= maskAlpha; - - - - - - - - - - color . rgb *= color . a; - return color; -} - - - - - - - - - - - - -void lookupTextureMetadata(int color, - out mat2 outColorTexMatrix0, - out vec4 outColorTexOffsets, - out vec4 outBaseColor){ - vec2 textureMetadataScale = vec2(1.0)/ vec2(uTextureMetadataSize); - vec2 metadataEntryCoord = vec2(color % 128 * 4, color / 128); - vec2 colorTexMatrix0Coord =(metadataEntryCoord + vec2(0.5, 0.5))* textureMetadataScale; - vec2 colorTexOffsetsCoord =(metadataEntryCoord + vec2(1.5, 0.5))* textureMetadataScale; - vec2 baseColorCoord =(metadataEntryCoord + vec2(2.5, 0.5))* textureMetadataScale; - outColorTexMatrix0 = mat2(texture(uTextureMetadata, colorTexMatrix0Coord)); - outColorTexOffsets = texture(uTextureMetadata, colorTexOffsetsCoord); - outBaseColor = texture(uTextureMetadata, baseColorCoord); -} - - - - -void main(){ - int maskCtrl0 =(uCtrl >> 0)& 0x1; - - vec4 colors[4]= { vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)}; - ivec2 tileSubCoord = ivec2(gl_LocalInvocationID . xy)* ivec2(1, 4); - ivec2 tileOrigin = ivec2(0); - - int tileIndex = iFirstTiles[gl_WorkGroupID . z]; - int overlapCount = 0; - - while(tileIndex >= 0){ - overlapCount ++; - - uint tileCoord = iTiles[tileIndex * 3 + 0]; - uint maskTexCoord = iTiles[tileIndex * 3 + 1]; - uint colorCtrl = iTiles[tileIndex * 3 + 2]; - - tileOrigin = ivec2(int(tileCoord & 0xffff), int(tileCoord >> 16)); - - int ctrl = int(uCtrl); - int tileColor = int(colorCtrl & 0xffff); - int tileCtrl = int(colorCtrl >> 16); - - mat2 colorTexMatrix0; - vec4 colorTexOffsets; - vec4 baseColor; - lookupTextureMetadata(tileColor, colorTexMatrix0, colorTexOffsets, baseColor); - - int maskTileCtrl0 =(tileCtrl >> 0)& 0x3; - - vec4 maskAlphas = vec4(1.0); - if(maskCtrl0 != 0 && maskTileCtrl0 != 0){ - uint maskTileIndex0 = maskTexCoord & 0xffff; - int maskTileBackdrop0 = int(maskTexCoord << 8)>> 24; - maskAlphas = clamp(abs(calculateFillAlpha(tileSubCoord, maskTileIndex0)+ - float(maskTileBackdrop0)), 0.0, 1.0); - } - - for(int yOffset = 0;yOffset < 4;yOffset ++){ - - ivec2 fragCoordI = tileOrigin * ivec2(uTileSize)+ tileSubCoord + ivec2(0, yOffset); - vec2 fragCoord = vec2(fragCoordI)+ vec2(0.5); - vec2 colorTexCoord0 = colorTexMatrix0 * fragCoord + colorTexOffsets . xy; - vec4 color = calculateColorWithMaskAlpha(maskAlphas[yOffset], - baseColor, - colorTexCoord0, - fragCoord, - ctrl); - colors[yOffset]= colors[yOffset]*(1.0 - color . a)+ color; - } - - tileIndex = iNextTiles[tileIndex]; - } - - for(int yOffset = 0;yOffset < 4;yOffset ++){ - ivec2 fragCoord = tileOrigin * ivec2(uTileSize)+ tileSubCoord + ivec2(0, yOffset); - - - vec4 color = colors[yOffset]; - if(color . a < 1.0) - color = imageLoad(uDestImage, fragCoord)*(1.0 - color . a)+ color; - imageStore(uDestImage, fragCoord, color); - } -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_fill.fs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_fill.fs.glsl deleted file mode 100644 index 88b566ff7a..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_fill.fs.glsl +++ /dev/null @@ -1,708 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - - - - - - - - - - - -#extension GL_GOOGLE_include_directive : enable - -precision highp float; -precision highp sampler2D; - -uniform sampler2D uColorTexture0; -uniform sampler2D uMaskTexture0; -uniform sampler2D uDestTexture; -uniform sampler2D uGammaLUT; -uniform vec4 uFilterParams0; -uniform vec4 uFilterParams1; -uniform vec4 uFilterParams2; -uniform vec2 uFramebufferSize; -uniform vec2 uColorTextureSize0; -uniform int uCtrl; -uniform sampler2D uAreaLUT; - -layout(std430, binding = 0)buffer bFills { - restrict readonly uvec2 iFills[]; -}; - -layout(std430, binding = 1)buffer bNextFills { - restrict readonly int iNextFills[]; -}; - -layout(std430, binding = 2)buffer bFillTileMap { - restrict readonly int iFillTileMap[]; -}; - -in vec2 vTileSubCoord; -flat in uint vMaskTileIndex0; -flat in int vMaskTileBackdrop0; -in vec2 vColorTexCoord0; -in vec4 vBaseColor; -in float vTileCtrl; - -out vec4 oFragColor; - - - - - - - - - - - - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D areaLUT){ - - vec2 left = from . x < to . x ? from : to, right = from . x < to . x ? to : from; - - - vec2 window = clamp(vec2(from . x, to . x), - 0.5, 0.5); - float offset = mix(window . x, window . y, 0.5)- left . x; - float t = offset /(right . x - left . x); - - - float y = mix(left . y, right . y, t); - float d =(right . y - left . y)/(right . x - left . x); - - - float dX = window . x - window . y; - return texture(areaLUT, vec2(y + 8.0, abs(d * dX))/ 16.0)* dX; -} - - - - - - - - - - - - -vec4 computeCoverage(vec2 from, vec2 to, sampler2D areaLUT); - -ivec2 calculateTileOrigin(uint tileIndex){ - return ivec2(tileIndex & 0xff,(tileIndex >> 8u)& 0xff)* 16; -} - -vec4 calculateFillAlpha(ivec2 tileSubCoord, uint tileIndex){ - int fillIndex = iFillTileMap[tileIndex]; - if(fillIndex < 0) - return vec4(0.0); - - vec4 coverages = vec4(0.0); - do { - uvec2 fill = iFills[fillIndex]; - vec2 from = vec2(fill . y & 0xf,(fill . y >> 4u)& 0xf)+ - vec2(fill . x & 0xff,(fill . x >> 8u)& 0xff)/ 256.0; - vec2 to = vec2((fill . y >> 8u)& 0xf,(fill . y >> 12u)& 0xf)+ - vec2((fill . x >> 16u)& 0xff,(fill . x >> 24u)& 0xff)/ 256.0; - - coverages += computeCoverage(from -(vec2(tileSubCoord)+ vec2(0.5)), - to -(vec2(tileSubCoord)+ vec2(0.5)), - uAreaLUT); - - fillIndex = iNextFills[fillIndex]; - } while(fillIndex >= 0); - - return coverages; -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -vec4 sampleColor(sampler2D colorTexture, vec2 colorTexCoord){ - return texture(colorTexture, colorTexCoord); -} - - - -vec4 combineColor0(vec4 destColor, vec4 srcColor, int op){ - switch(op){ - case 0x1 : - return vec4(srcColor . rgb, srcColor . a * destColor . a); - case 0x2 : - return vec4(destColor . rgb, srcColor . a * destColor . a); - } - return destColor; -} - - - -float filterTextSample1Tap(float offset, sampler2D colorTexture, vec2 colorTexCoord){ - return texture(colorTexture, colorTexCoord + vec2(offset, 0.0)). r; -} - - -void filterTextSample9Tap(out vec4 outAlphaLeft, - out float outAlphaCenter, - out vec4 outAlphaRight, - sampler2D colorTexture, - vec2 colorTexCoord, - vec4 kernel, - float onePixel){ - bool wide = kernel . x > 0.0; - outAlphaLeft = - vec4(wide ? filterTextSample1Tap(- 4.0 * onePixel, colorTexture, colorTexCoord): 0.0, - filterTextSample1Tap(- 3.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(- 2.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(- 1.0 * onePixel, colorTexture, colorTexCoord)); - outAlphaCenter = filterTextSample1Tap(0.0, colorTexture, colorTexCoord); - outAlphaRight = - vec4(filterTextSample1Tap(1.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(2.0 * onePixel, colorTexture, colorTexCoord), - filterTextSample1Tap(3.0 * onePixel, colorTexture, colorTexCoord), - wide ? filterTextSample1Tap(4.0 * onePixel, colorTexture, colorTexCoord): 0.0); -} - -float filterTextConvolve7Tap(vec4 alpha0, vec3 alpha1, vec4 kernel){ - return dot(alpha0, kernel)+ dot(alpha1, kernel . zyx); -} - -float filterTextGammaCorrectChannel(float bgColor, float fgColor, sampler2D gammaLUT){ - return texture(gammaLUT, vec2(fgColor, 1.0 - bgColor)). r; -} - - -vec3 filterTextGammaCorrect(vec3 bgColor, vec3 fgColor, sampler2D gammaLUT){ - return vec3(filterTextGammaCorrectChannel(bgColor . r, fgColor . r, gammaLUT), - filterTextGammaCorrectChannel(bgColor . g, fgColor . g, gammaLUT), - filterTextGammaCorrectChannel(bgColor . b, fgColor . b, gammaLUT)); -} - - - - - - -vec4 filterText(vec2 colorTexCoord, - sampler2D colorTexture, - sampler2D gammaLUT, - vec2 colorTextureSize, - vec4 filterParams0, - vec4 filterParams1, - vec4 filterParams2){ - - vec4 kernel = filterParams0; - vec3 bgColor = filterParams1 . rgb; - vec3 fgColor = filterParams2 . rgb; - bool gammaCorrectionEnabled = filterParams2 . a != 0.0; - - - vec3 alpha; - if(kernel . w == 0.0){ - alpha = texture(colorTexture, colorTexCoord). rrr; - } else { - vec4 alphaLeft, alphaRight; - float alphaCenter; - filterTextSample9Tap(alphaLeft, - alphaCenter, - alphaRight, - colorTexture, - colorTexCoord, - kernel, - 1.0 / colorTextureSize . x); - - float r = filterTextConvolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight . xy), kernel); - float g = filterTextConvolve7Tap(vec4(alphaLeft . yzw, alphaCenter), alphaRight . xyz, kernel); - float b = filterTextConvolve7Tap(vec4(alphaLeft . zw, alphaCenter, alphaRight . x), - alphaRight . yzw, - kernel); - - alpha = vec3(r, g, b); - } - - - if(gammaCorrectionEnabled) - alpha = filterTextGammaCorrect(bgColor, alpha, gammaLUT); - - - return vec4(mix(bgColor, fgColor, alpha), 1.0); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -vec4 filterRadialGradient(vec2 colorTexCoord, - sampler2D colorTexture, - vec2 colorTextureSize, - vec2 fragCoord, - vec2 framebufferSize, - vec4 filterParams0, - vec4 filterParams1){ - vec2 lineFrom = filterParams0 . xy, lineVector = filterParams0 . zw; - vec2 radii = filterParams1 . xy, uvOrigin = filterParams1 . zw; - - vec2 dP = colorTexCoord - lineFrom, dC = lineVector; - float dR = radii . y - radii . x; - - float a = dot(dC, dC)- dR * dR; - float b = dot(dP, dC)+ radii . x * dR; - float c = dot(dP, dP)- radii . x * radii . x; - float discrim = b * b - a * c; - - vec4 color = vec4(0.0); - if(abs(discrim)>= 0.00001){ - vec2 ts = vec2(sqrt(discrim)* vec2(1.0, - 1.0)+ vec2(b))/ vec2(a); - if(ts . x > ts . y) - ts = ts . yx; - float t = ts . x >= 0.0 ? ts . x : ts . y; - color = texture(colorTexture, uvOrigin + vec2(clamp(t, 0.0, 1.0), 0.0)); - } - - return color; -} - - - - - - -vec4 filterBlur(vec2 colorTexCoord, - sampler2D colorTexture, - vec2 colorTextureSize, - vec4 filterParams0, - vec4 filterParams1){ - - vec2 srcOffsetScale = filterParams0 . xy / colorTextureSize; - int support = int(filterParams0 . z); - vec3 gaussCoeff = filterParams1 . xyz; - - - float gaussSum = gaussCoeff . x; - vec4 color = texture(colorTexture, colorTexCoord)* gaussCoeff . x; - gaussCoeff . xy *= gaussCoeff . yz; - - - - - - - - - - for(int i = 1;i <= support;i += 2){ - float gaussPartialSum = gaussCoeff . x; - gaussCoeff . xy *= gaussCoeff . yz; - gaussPartialSum += gaussCoeff . x; - - vec2 srcOffset = srcOffsetScale *(float(i)+ gaussCoeff . x / gaussPartialSum); - color +=(texture(colorTexture, colorTexCoord - srcOffset)+ - texture(colorTexture, colorTexCoord + srcOffset))* gaussPartialSum; - - gaussSum += 2.0 * gaussPartialSum; - gaussCoeff . xy *= gaussCoeff . yz; - } - - - return color / gaussSum; -} - -vec4 filterNone(vec2 colorTexCoord, sampler2D colorTexture){ - return sampleColor(colorTexture, colorTexCoord); -} - -vec4 filterColor(vec2 colorTexCoord, - sampler2D colorTexture, - sampler2D gammaLUT, - vec2 colorTextureSize, - vec2 fragCoord, - vec2 framebufferSize, - vec4 filterParams0, - vec4 filterParams1, - vec4 filterParams2, - int colorFilter){ - switch(colorFilter){ - case 0x1 : - return filterRadialGradient(colorTexCoord, - colorTexture, - colorTextureSize, - fragCoord, - framebufferSize, - filterParams0, - filterParams1); - case 0x3 : - return filterBlur(colorTexCoord, - colorTexture, - colorTextureSize, - filterParams0, - filterParams1); - case 0x2 : - return filterText(colorTexCoord, - colorTexture, - gammaLUT, - colorTextureSize, - filterParams0, - filterParams1, - filterParams2); - } - return filterNone(colorTexCoord, colorTexture); -} - - - -vec3 compositeSelect(bvec3 cond, vec3 ifTrue, vec3 ifFalse){ - return vec3(cond . x ? ifTrue . x : ifFalse . x, - cond . y ? ifTrue . y : ifFalse . y, - cond . z ? ifTrue . z : ifFalse . z); -} - -float compositeDivide(float num, float denom){ - return denom != 0.0 ? num / denom : 0.0; -} - -vec3 compositeColorDodge(vec3 destColor, vec3 srcColor){ - bvec3 destZero = equal(destColor, vec3(0.0)), srcOne = equal(srcColor, vec3(1.0)); - return compositeSelect(destZero, - vec3(0.0), - compositeSelect(srcOne, vec3(1.0), destColor /(vec3(1.0)- srcColor))); -} - - -vec3 compositeHSLToRGB(vec3 hsl){ - float a = hsl . y * min(hsl . z, 1.0 - hsl . z); - vec3 ks = mod(vec3(0.0, 8.0, 4.0)+ vec3(hsl . x * 1.9098593171027443), 12.0); - return hsl . zzz - clamp(min(ks - vec3(3.0), vec3(9.0)- ks), - 1.0, 1.0)* a; -} - - -vec3 compositeRGBToHSL(vec3 rgb){ - float v = max(max(rgb . r, rgb . g), rgb . b), xMin = min(min(rgb . r, rgb . g), rgb . b); - float c = v - xMin, l = mix(xMin, v, 0.5); - vec3 terms = rgb . r == v ? vec3(0.0, rgb . gb): - rgb . g == v ? vec3(2.0, rgb . br): - vec3(4.0, rgb . rg); - float h = 1.0471975511965976 * compositeDivide(terms . x * c + terms . y - terms . z, c); - float s = compositeDivide(c, v); - return vec3(h, s, l); -} - -vec3 compositeScreen(vec3 destColor, vec3 srcColor){ - return destColor + srcColor - destColor * srcColor; -} - -vec3 compositeHardLight(vec3 destColor, vec3 srcColor){ - return compositeSelect(lessThanEqual(srcColor, vec3(0.5)), - destColor * vec3(2.0)* srcColor, - compositeScreen(destColor, vec3(2.0)* srcColor - vec3(1.0))); -} - -vec3 compositeSoftLight(vec3 destColor, vec3 srcColor){ - vec3 darkenedDestColor = - compositeSelect(lessThanEqual(destColor, vec3(0.25)), - ((vec3(16.0)* destColor - 12.0)* destColor + 4.0)* destColor, - sqrt(destColor)); - vec3 factor = compositeSelect(lessThanEqual(srcColor, vec3(0.5)), - destColor *(vec3(1.0)- destColor), - darkenedDestColor - destColor); - return destColor +(srcColor * 2.0 - 1.0)* factor; -} - -vec3 compositeHSL(vec3 destColor, vec3 srcColor, int op){ - switch(op){ - case 0xc : - return vec3(srcColor . x, destColor . y, destColor . z); - case 0xd : - return vec3(destColor . x, srcColor . y, destColor . z); - case 0xe : - return vec3(srcColor . x, srcColor . y, destColor . z); - default : - return vec3(destColor . x, destColor . y, srcColor . z); - } -} - -vec3 compositeRGB(vec3 destColor, vec3 srcColor, int op){ - switch(op){ - case 0x1 : - return destColor * srcColor; - case 0x2 : - return compositeScreen(destColor, srcColor); - case 0x3 : - return compositeHardLight(srcColor, destColor); - case 0x4 : - return min(destColor, srcColor); - case 0x5 : - return max(destColor, srcColor); - case 0x6 : - return compositeColorDodge(destColor, srcColor); - case 0x7 : - return vec3(1.0)- compositeColorDodge(vec3(1.0)- destColor, vec3(1.0)- srcColor); - case 0x8 : - return compositeHardLight(destColor, srcColor); - case 0x9 : - return compositeSoftLight(destColor, srcColor); - case 0xa : - return abs(destColor - srcColor); - case 0xb : - return destColor + srcColor - vec3(2.0)* destColor * srcColor; - case 0xc : - case 0xd : - case 0xe : - case 0xf : - return compositeHSLToRGB(compositeHSL(compositeRGBToHSL(destColor), - compositeRGBToHSL(srcColor), - op)); - } - return srcColor; -} - -vec4 composite(vec4 srcColor, - sampler2D destTexture, - vec2 destTextureSize, - vec2 fragCoord, - int op){ - if(op == 0x0) - return srcColor; - - - vec2 destTexCoord = fragCoord / destTextureSize; - vec4 destColor = texture(destTexture, destTexCoord); - vec3 blendedRGB = compositeRGB(destColor . rgb, srcColor . rgb, op); - return vec4(srcColor . a *(1.0 - destColor . a)* srcColor . rgb + - srcColor . a * destColor . a * blendedRGB + - (1.0 - srcColor . a)* destColor . rgb, - 1.0); -} - - - -float sampleMask(float maskAlpha, - sampler2D maskTexture, - vec2 maskTextureSize, - vec3 maskTexCoord, - int maskCtrl){ - if(maskCtrl == 0) - return maskAlpha; - - ivec2 maskTexCoordI = ivec2(floor(maskTexCoord . xy)); - vec4 texel = texture(maskTexture,(vec2(maskTexCoordI / ivec2(1, 4))+ 0.5)/ maskTextureSize); - float coverage = texel[maskTexCoordI . y % 4]+ maskTexCoord . z; - - if((maskCtrl & 0x1)!= 0) - coverage = abs(coverage); - else - coverage = 1.0 - abs(1.0 - mod(coverage, 2.0)); - return min(maskAlpha, coverage); -} - - - -vec4 calculateColorWithMaskAlpha(float maskAlpha, - vec4 baseColor, - vec2 colorTexCoord0, - vec2 fragCoord, - int ctrl){ - - vec4 color = baseColor; - int color0Combine =(ctrl >> 6)& - 0x3; - if(color0Combine != 0){ - int color0Filter =(ctrl >> 4)& 0x3; - vec4 color0 = filterColor(colorTexCoord0, - uColorTexture0, - uGammaLUT, - uColorTextureSize0, - fragCoord, - uFramebufferSize, - uFilterParams0, - uFilterParams1, - uFilterParams2, - color0Filter); - color = combineColor0(color, color0, color0Combine); - } - - - color . a *= maskAlpha; - - - - - - - - - - color . rgb *= color . a; - return color; -} - - -vec4 calculateColor(int tileCtrl, int ctrl){ - float maskAlpha = 1.0; - int maskCtrl0 =(ctrl >> 0)& 0x1; - int maskTileCtrl0 =(tileCtrl >> 0)& 0x3; - uint maskTileIndex0 = vMaskTileIndex0; - if(maskCtrl0 != 0 && maskTileCtrl0 != 0){ - ivec2 tileSubCoord = ivec2(floor(vTileSubCoord)); - vec4 alphas = calculateFillAlpha(tileSubCoord, maskTileIndex0)+ float(vMaskTileBackdrop0); - maskAlpha = alphas . x; - } - return calculateColorWithMaskAlpha(maskAlpha, - vBaseColor, - vColorTexCoord0, - gl_FragCoord . xy, - ctrl); -} - - - - - -void main(){ - oFragColor = calculateColor(int(vTileCtrl), uCtrl); - -} - diff --git a/crates/pathfinder/resources/shaders/gl4/tile_fill.vs.glsl b/crates/pathfinder/resources/shaders/gl4/tile_fill.vs.glsl deleted file mode 100644 index 1c089c0905..0000000000 --- a/crates/pathfinder/resources/shaders/gl4/tile_fill.vs.glsl +++ /dev/null @@ -1,58 +0,0 @@ -#version {{version}} -// Automatically generated from files in pathfinder/shaders/. Do not edit! - - - - - - - - - - - - -precision highp float; -precision highp sampler2D; - -uniform mat4 uTransform; -uniform vec2 uTileSize; -uniform sampler2D uTextureMetadata; -uniform ivec2 uTextureMetadataSize; - -in ivec2 aTileOffset; -in ivec2 aTileOrigin; -in uint aMaskTileIndex0; -in ivec2 aMaskBackdrop; -in int aColor; -in int aTileCtrl; - -out vec2 vTileSubCoord; -flat out uint vMaskTileIndex0; -flat out int vMaskTileBackdrop0; -out vec2 vColorTexCoord0; -out vec4 vBaseColor; -out float vTileCtrl; - -void main(){ - vec2 tileOrigin = vec2(aTileOrigin), tileOffset = vec2(aTileOffset); - vec2 position =(tileOrigin + tileOffset)* uTileSize; - - vec2 textureMetadataScale = vec2(1.0)/ vec2(uTextureMetadataSize); - vec2 metadataEntryCoord = vec2(aColor % 128 * 4, aColor / 128); - vec2 colorTexMatrix0Coord =(metadataEntryCoord + vec2(0.5, 0.5))* textureMetadataScale; - vec2 colorTexOffsetsCoord =(metadataEntryCoord + vec2(1.5, 0.5))* textureMetadataScale; - vec2 baseColorCoord =(metadataEntryCoord + vec2(2.5, 0.5))* textureMetadataScale; - vec4 colorTexMatrix0 = texture(uTextureMetadata, colorTexMatrix0Coord); - vec4 colorTexOffsets = texture(uTextureMetadata, colorTexOffsetsCoord); - vec4 baseColor = texture(uTextureMetadata, baseColorCoord); - - vTileSubCoord = tileOffset * vec2(16.0); - vColorTexCoord0 = mat2(colorTexMatrix0)* position + colorTexOffsets . xy; - vMaskTileIndex0 = aMaskTileIndex0; - vMaskTileBackdrop0 = aMaskBackdrop . x; - vBaseColor = baseColor; - vTileCtrl = float(aTileCtrl); - gl_Position = uTransform * vec4(position, 0.0, 1.0); -} - diff --git a/crates/pathfinder/resources/shaders/metal/blit.fs.metal b/crates/pathfinder/resources/shaders/metal/blit.fs.metal deleted file mode 100644 index 7ccae45247..0000000000 --- a/crates/pathfinder/resources/shaders/metal/blit.fs.metal +++ /dev/null @@ -1,24 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 vTexCoord [[user(locn0)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], texture2d uSrc [[texture(0)]], sampler uSampler [[sampler(0)]]) -{ - main0_out out = {}; - float4 color = uSrc.sample(uSampler, in.vTexCoord); - out.oFragColor = float4(color.xyz * color.w, color.w); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/blit.vs.metal b/crates/pathfinder/resources/shaders/metal/blit.vs.metal deleted file mode 100644 index 701d97f0f1..0000000000 --- a/crates/pathfinder/resources/shaders/metal/blit.vs.metal +++ /dev/null @@ -1,27 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct main0_out -{ - float2 vTexCoord [[user(locn0)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aPosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]]) -{ - main0_out out = {}; - float2 texCoord = float2(in.aPosition); - texCoord.y = 1.0 - texCoord.y; - out.vTexCoord = texCoord; - out.gl_Position = float4(mix(float2(-1.0), float2(1.0), float2(in.aPosition)), 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/clear.fs.metal b/crates/pathfinder/resources/shaders/metal/clear.fs.metal deleted file mode 100644 index f4d373c281..0000000000 --- a/crates/pathfinder/resources/shaders/metal/clear.fs.metal +++ /dev/null @@ -1,23 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uColor -{ - float4 color; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -fragment main0_out main0(constant uColor& _12 [[buffer(0)]]) -{ - main0_out out = {}; - out.oFragColor = float4(_12.color.xyz, 1.0) * _12.color.w; - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/clear.vs.metal b/crates/pathfinder/resources/shaders/metal/clear.vs.metal deleted file mode 100644 index 7f4126fe01..0000000000 --- a/crates/pathfinder/resources/shaders/metal/clear.vs.metal +++ /dev/null @@ -1,34 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uRect -{ - float4 rect; -}; - -struct uFramebufferSize -{ - float2 framebufferSize; -}; - -struct main0_out -{ - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aPosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uRect& _13 [[buffer(0)]], constant uFramebufferSize& _31 [[buffer(1)]]) -{ - main0_out out = {}; - float2 position = ((mix(_13.rect.xy, _13.rect.zw, float2(in.aPosition)) / _31.framebufferSize) * 2.0) - float2(1.0); - out.gl_Position = float4(position.x, -position.y, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/debug_solid.fs.metal b/crates/pathfinder/resources/shaders/metal/debug_solid.fs.metal deleted file mode 100644 index f4d373c281..0000000000 --- a/crates/pathfinder/resources/shaders/metal/debug_solid.fs.metal +++ /dev/null @@ -1,23 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uColor -{ - float4 color; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -fragment main0_out main0(constant uColor& _12 [[buffer(0)]]) -{ - main0_out out = {}; - out.oFragColor = float4(_12.color.xyz, 1.0) * _12.color.w; - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/debug_solid.vs.metal b/crates/pathfinder/resources/shaders/metal/debug_solid.vs.metal deleted file mode 100644 index 7c3d808683..0000000000 --- a/crates/pathfinder/resources/shaders/metal/debug_solid.vs.metal +++ /dev/null @@ -1,29 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uFramebufferSize -{ - float2 framebufferSize; -}; - -struct main0_out -{ - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aPosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uFramebufferSize& _18 [[buffer(0)]]) -{ - main0_out out = {}; - float2 position = ((float2(in.aPosition) / _18.framebufferSize) * 2.0) - float2(1.0); - out.gl_Position = float4(position.x, -position.y, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/debug_texture.fs.metal b/crates/pathfinder/resources/shaders/metal/debug_texture.fs.metal deleted file mode 100644 index 7d3443a2f2..0000000000 --- a/crates/pathfinder/resources/shaders/metal/debug_texture.fs.metal +++ /dev/null @@ -1,29 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uColor -{ - float4 color; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 vTexCoord [[user(locn0)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], constant uColor& _30 [[buffer(0)]], texture2d uTexture [[texture(0)]], sampler uSampler [[sampler(0)]]) -{ - main0_out out = {}; - float alpha = uTexture.sample(uSampler, in.vTexCoord).x * _30.color.w; - out.oFragColor = float4(_30.color.xyz, 1.0) * alpha; - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/debug_texture.vs.metal b/crates/pathfinder/resources/shaders/metal/debug_texture.vs.metal deleted file mode 100644 index 85e44c4c58..0000000000 --- a/crates/pathfinder/resources/shaders/metal/debug_texture.vs.metal +++ /dev/null @@ -1,37 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uTextureSize -{ - float2 _textureSize; -}; - -struct uFramebufferSize -{ - float2 framebufferSize; -}; - -struct main0_out -{ - float2 vTexCoord [[user(locn0)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aPosition [[attribute(0)]]; - int2 aTexCoord [[attribute(1)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uTextureSize& _18 [[buffer(0)]], constant uFramebufferSize& _31 [[buffer(1)]]) -{ - main0_out out = {}; - out.vTexCoord = float2(in.aTexCoord) / _18._textureSize; - float2 position = ((float2(in.aPosition) / _31.framebufferSize) * 2.0) - float2(1.0); - out.gl_Position = float4(position.x, -position.y, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/demo_ground.fs.metal b/crates/pathfinder/resources/shaders/metal/demo_ground.fs.metal deleted file mode 100644 index 3cc7970440..0000000000 --- a/crates/pathfinder/resources/shaders/metal/demo_ground.fs.metal +++ /dev/null @@ -1,43 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uGridlineColor -{ - float4 gridlineColor; -}; - -struct uGroundColor -{ - float4 groundColor; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 vTexCoord [[user(locn0)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], constant uGridlineColor& _33 [[buffer(0)]], constant uGroundColor& _42 [[buffer(1)]]) -{ - main0_out out = {}; - float2 texCoordPx = fract(in.vTexCoord) / fwidth(in.vTexCoord); - float4 _28; - if (any(texCoordPx <= float2(1.0))) - { - _28 = _33.gridlineColor; - } - else - { - _28 = _42.groundColor; - } - out.oFragColor = _28; - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/demo_ground.vs.metal b/crates/pathfinder/resources/shaders/metal/demo_ground.vs.metal deleted file mode 100644 index 24dc8e6fa9..0000000000 --- a/crates/pathfinder/resources/shaders/metal/demo_ground.vs.metal +++ /dev/null @@ -1,35 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uGridlineCount -{ - int gridlineCount; -}; - -struct uTransform -{ - float4x4 transform; -}; - -struct main0_out -{ - float2 vTexCoord [[user(locn0)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aPosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uGridlineCount& _17 [[buffer(0)]], constant uTransform& _35 [[buffer(1)]]) -{ - main0_out out = {}; - out.vTexCoord = float2(in.aPosition * int2(_17.gridlineCount)); - out.gl_Position = _35.transform * float4(int4(in.aPosition.x, 0, in.aPosition.y, 1)); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/fill.cs.metal b/crates/pathfinder/resources/shaders/metal/fill.cs.metal deleted file mode 100644 index a6e16e550d..0000000000 --- a/crates/pathfinder/resources/shaders/metal/fill.cs.metal +++ /dev/null @@ -1,70 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#pragma clang diagnostic ignored "-Wmissing-prototypes" - -#include -#include - -using namespace metal; - -struct uFirstTileIndex -{ - int firstTileIndex; -}; - -struct bFillTileMap -{ - int iFillTileMap[1]; -}; - -struct bFills -{ - uint2 iFills[1]; -}; - -struct bNextFills -{ - int iNextFills[1]; -}; - -constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(16u, 4u, 1u); - -static inline __attribute__((always_inline)) -float4 computeCoverage(thread const float2& from, thread const float2& to, thread const texture2d areaLUT, thread const sampler areaLUTSampler) -{ - float2 left = select(to, from, bool2(from.x < to.x)); - float2 right = select(from, to, bool2(from.x < to.x)); - float2 window = fast::clamp(float2(from.x, to.x), float2(-0.5), float2(0.5)); - float offset = mix(window.x, window.y, 0.5) - left.x; - float t = offset / (right.x - left.x); - float y = mix(left.y, right.y, t); - float d = (right.y - left.y) / (right.x - left.x); - float dX = window.x - window.y; - return areaLUT.sample(areaLUTSampler, (float2(y + 8.0, abs(d * dX)) / float2(16.0)), level(0.0)) * dX; -} - -kernel void main0(constant uFirstTileIndex& _147 [[buffer(0)]], const device bFillTileMap& _159 [[buffer(1)]], const device bFills& _180 [[buffer(2)]], const device bNextFills& _264 [[buffer(3)]], texture2d uAreaLUT [[texture(0)]], texture2d uDest [[texture(1)]], sampler uSampler [[sampler(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) -{ - int2 tileSubCoord = int2(gl_LocalInvocationID.xy) * int2(1, 4); - uint tileIndexOffset = gl_WorkGroupID.z; - uint tileIndex = tileIndexOffset + uint(_147.firstTileIndex); - int fillIndex = _159.iFillTileMap[tileIndex]; - if (fillIndex < 0) - { - return; - } - float4 coverages = float4(0.0); - do - { - uint2 fill = _180.iFills[fillIndex]; - float2 from = float2(float(fill.y & 15u), float((fill.y >> 4u) & 15u)) + (float2(float(fill.x & 255u), float((fill.x >> 8u) & 255u)) / float2(256.0)); - float2 to = float2(float((fill.y >> 8u) & 15u), float((fill.y >> 12u) & 15u)) + (float2(float((fill.x >> 16u) & 255u), float((fill.x >> 24u) & 255u)) / float2(256.0)); - float2 param = from - (float2(tileSubCoord) + float2(0.5)); - float2 param_1 = to - (float2(tileSubCoord) + float2(0.5)); - coverages += computeCoverage(param, param_1, uAreaLUT, uSampler); - fillIndex = _264.iNextFills[fillIndex]; - } while (fillIndex >= 0); - int2 tileOrigin = int2(int(tileIndex & 255u), int((tileIndex >> 8u) & 255u)) * int2(16, 4); - int2 destCoord = tileOrigin + int2(gl_LocalInvocationID.xy); - uDest.write(coverages, uint2(destCoord)); -} - diff --git a/crates/pathfinder/resources/shaders/metal/fill.fs.metal b/crates/pathfinder/resources/shaders/metal/fill.fs.metal deleted file mode 100644 index 707127376f..0000000000 --- a/crates/pathfinder/resources/shaders/metal/fill.fs.metal +++ /dev/null @@ -1,42 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#pragma clang diagnostic ignored "-Wmissing-prototypes" - -#include -#include - -using namespace metal; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 vFrom [[user(locn0)]]; - float2 vTo [[user(locn1)]]; -}; - -static inline __attribute__((always_inline)) -float4 computeCoverage(thread const float2& from, thread const float2& to, thread const texture2d areaLUT, thread const sampler areaLUTSampler) -{ - float2 left = select(to, from, bool2(from.x < to.x)); - float2 right = select(from, to, bool2(from.x < to.x)); - float2 window = fast::clamp(float2(from.x, to.x), float2(-0.5), float2(0.5)); - float offset = mix(window.x, window.y, 0.5) - left.x; - float t = offset / (right.x - left.x); - float y = mix(left.y, right.y, t); - float d = (right.y - left.y) / (right.x - left.x); - float dX = window.x - window.y; - return areaLUT.sample(areaLUTSampler, (float2(y + 8.0, abs(d * dX)) / float2(16.0))) * dX; -} - -fragment main0_out main0(main0_in in [[stage_in]], texture2d uAreaLUT [[texture(0)]], sampler uSampler [[sampler(0)]]) -{ - main0_out out = {}; - float2 param = in.vFrom; - float2 param_1 = in.vTo; - out.oFragColor = computeCoverage(param, param_1, uAreaLUT, uSampler); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/fill.vs.metal b/crates/pathfinder/resources/shaders/metal/fill.vs.metal deleted file mode 100644 index 46404c296f..0000000000 --- a/crates/pathfinder/resources/shaders/metal/fill.vs.metal +++ /dev/null @@ -1,78 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#pragma clang diagnostic ignored "-Wmissing-prototypes" - -#include -#include - -using namespace metal; - -struct uTileSize -{ - float2 tileSize; -}; - -struct uFramebufferSize -{ - float2 framebufferSize; -}; - -struct main0_out -{ - float2 vFrom [[user(locn0)]]; - float2 vTo [[user(locn1)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - uint2 aTessCoord [[attribute(0)]]; - float2 aFromSubpx [[attribute(1)]]; - float2 aToSubpx [[attribute(2)]]; - uint aFromPx [[attribute(3)]]; - uint aToPx [[attribute(4)]]; - uint aTileIndex [[attribute(5)]]; -}; - -static inline __attribute__((always_inline)) -float2 computeTileOffset(thread const uint& tileIndex, thread const float& stencilTextureWidth, constant uTileSize& v_20) -{ - uint tilesPerRow = uint(stencilTextureWidth / v_20.tileSize.x); - uint2 tileOffset = uint2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); - return (float2(tileOffset) * v_20.tileSize) * float2(1.0, 0.25); -} - -vertex main0_out main0(main0_in in [[stage_in]], constant uTileSize& v_20 [[buffer(0)]], constant uFramebufferSize& _57 [[buffer(1)]]) -{ - main0_out out = {}; - uint param = in.aTileIndex; - float param_1 = _57.framebufferSize.x; - float2 tileOrigin = computeTileOffset(param, param_1, v_20); - float2 from = float2(float(in.aFromPx & 15u), float(in.aFromPx >> 4u)) + in.aFromSubpx; - float2 to = float2(float(in.aToPx & 15u), float(in.aToPx >> 4u)) + in.aToSubpx; - float2 position; - if (in.aTessCoord.x == 0u) - { - position.x = floor(fast::min(from.x, to.x)); - } - else - { - position.x = ceil(fast::max(from.x, to.x)); - } - if (in.aTessCoord.y == 0u) - { - position.y = floor(fast::min(from.y, to.y)); - } - else - { - position.y = v_20.tileSize.y; - } - position.y = floor(position.y * 0.25); - float2 offset = float2(0.0, 1.5) - (position * float2(1.0, 4.0)); - out.vFrom = from + offset; - out.vTo = to + offset; - float2 globalPosition = (((tileOrigin + position) / _57.framebufferSize) * 2.0) - float2(1.0); - globalPosition.y = -globalPosition.y; - out.gl_Position = float4(globalPosition, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/reproject.fs.metal b/crates/pathfinder/resources/shaders/metal/reproject.fs.metal deleted file mode 100644 index e39a88ecf5..0000000000 --- a/crates/pathfinder/resources/shaders/metal/reproject.fs.metal +++ /dev/null @@ -1,30 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uOldTransform -{ - float4x4 oldTransform; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 vTexCoord [[user(locn0)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], constant uOldTransform& _13 [[buffer(0)]], texture2d uTexture [[texture(0)]], sampler uSampler [[sampler(0)]]) -{ - main0_out out = {}; - float4 normTexCoord = _13.oldTransform * float4(in.vTexCoord, 0.0, 1.0); - float2 texCoord = ((normTexCoord.xy / float2(normTexCoord.w)) + float2(1.0)) * 0.5; - out.oFragColor = uTexture.sample(uSampler, texCoord); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/reproject.vs.metal b/crates/pathfinder/resources/shaders/metal/reproject.vs.metal deleted file mode 100644 index a7cf025e0d..0000000000 --- a/crates/pathfinder/resources/shaders/metal/reproject.vs.metal +++ /dev/null @@ -1,32 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uNewTransform -{ - float4x4 newTransform; -}; - -struct main0_out -{ - float2 vTexCoord [[user(locn0)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aPosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uNewTransform& _36 [[buffer(0)]]) -{ - main0_out out = {}; - float2 position = float2(in.aPosition); - out.vTexCoord = position; - position.y = 1.0 - position.y; - out.gl_Position = _36.newTransform * float4(position, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/stencil.fs.metal b/crates/pathfinder/resources/shaders/metal/stencil.fs.metal deleted file mode 100644 index 999cc7e160..0000000000 --- a/crates/pathfinder/resources/shaders/metal/stencil.fs.metal +++ /dev/null @@ -1,18 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -fragment main0_out main0() -{ - main0_out out = {}; - out.oFragColor = float4(1.0, 0.0, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/stencil.vs.metal b/crates/pathfinder/resources/shaders/metal/stencil.vs.metal deleted file mode 100644 index 6b6182d4b2..0000000000 --- a/crates/pathfinder/resources/shaders/metal/stencil.vs.metal +++ /dev/null @@ -1,23 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct main0_out -{ - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - float3 aPosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]]) -{ - main0_out out = {}; - out.gl_Position = float4(in.aPosition, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/tile.fs.metal b/crates/pathfinder/resources/shaders/metal/tile.fs.metal deleted file mode 100644 index e2ac151910..0000000000 --- a/crates/pathfinder/resources/shaders/metal/tile.fs.metal +++ /dev/null @@ -1,639 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#pragma clang diagnostic ignored "-Wmissing-prototypes" - -#include -#include - -using namespace metal; - -struct uMaskTextureSize0 -{ - float2 iMaskTextureSize0; -}; - -struct uColorTextureSize0 -{ - float2 iColorTextureSize0; -}; - -struct uFramebufferSize -{ - float2 iFramebufferSize; -}; - -struct uFilterParams0 -{ - float4 iFilterParams0; -}; - -struct uFilterParams1 -{ - float4 iFilterParams1; -}; - -struct uFilterParams2 -{ - float4 iFilterParams2; -}; - -struct uCtrl -{ - int iCtrl; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float3 vMaskTexCoord0 [[user(locn0)]]; - float2 vColorTexCoord0 [[user(locn1)]]; - float4 vBaseColor [[user(locn2)]]; - float vTileCtrl [[user(locn3)]]; -}; - -// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() -template -inline Tx mod(Tx x, Ty y) -{ - return x - y * floor(x / y); -} - -static inline __attribute__((always_inline)) -float sampleMask(thread const float& maskAlpha, thread const texture2d maskTexture, thread const float2& maskTextureSize, thread const float3& maskTexCoord, thread const int& maskCtrl, thread sampler uSampler) -{ - if (maskCtrl == 0) - { - return maskAlpha; - } - int2 maskTexCoordI = int2(floor(maskTexCoord.xy)); - float4 texel = maskTexture.sample(uSampler, ((float2(maskTexCoordI / int2(1, 4)) + float2(0.5)) / maskTextureSize)); - float coverage = texel[maskTexCoordI.y % 4] + maskTexCoord.z; - if ((maskCtrl & 1) != 0) - { - coverage = abs(coverage); - } - else - { - coverage = 1.0 - abs(1.0 - mod(coverage, 2.0)); - } - return fast::min(maskAlpha, coverage); -} - -static inline __attribute__((always_inline)) -float4 filterRadialGradient(thread const float2& colorTexCoord, thread const texture2d colorTexture, thread const float2& colorTextureSize, thread const float2& fragCoord, thread const float2& framebufferSize, thread const float4& filterParams0, thread const float4& filterParams1, thread sampler uSampler) -{ - float2 lineFrom = filterParams0.xy; - float2 lineVector = filterParams0.zw; - float2 radii = filterParams1.xy; - float2 uvOrigin = filterParams1.zw; - float2 dP = colorTexCoord - lineFrom; - float2 dC = lineVector; - float dR = radii.y - radii.x; - float a = dot(dC, dC) - (dR * dR); - float b = dot(dP, dC) + (radii.x * dR); - float c = dot(dP, dP) - (radii.x * radii.x); - float discrim = (b * b) - (a * c); - float4 color = float4(0.0); - if (abs(discrim) >= 9.9999997473787516355514526367188e-06) - { - float2 ts = float2((float2(1.0, -1.0) * sqrt(discrim)) + float2(b)) / float2(a); - if (ts.x > ts.y) - { - ts = ts.yx; - } - float _566; - if (ts.x >= 0.0) - { - _566 = ts.x; - } - else - { - _566 = ts.y; - } - float t = _566; - color = colorTexture.sample(uSampler, (uvOrigin + float2(fast::clamp(t, 0.0, 1.0), 0.0))); - } - return color; -} - -static inline __attribute__((always_inline)) -float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d colorTexture, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1, thread sampler uSampler) -{ - float2 srcOffsetScale = filterParams0.xy / colorTextureSize; - int support = int(filterParams0.z); - float3 gaussCoeff = filterParams1.xyz; - float gaussSum = gaussCoeff.x; - float4 color = colorTexture.sample(uSampler, colorTexCoord) * gaussCoeff.x; - float2 _615 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = float3(_615.x, _615.y, gaussCoeff.z); - for (int i = 1; i <= support; i += 2) - { - float gaussPartialSum = gaussCoeff.x; - float2 _635 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = float3(_635.x, _635.y, gaussCoeff.z); - gaussPartialSum += gaussCoeff.x; - float2 srcOffset = srcOffsetScale * (float(i) + (gaussCoeff.x / gaussPartialSum)); - color += ((colorTexture.sample(uSampler, (colorTexCoord - srcOffset)) + colorTexture.sample(uSampler, (colorTexCoord + srcOffset))) * gaussPartialSum); - gaussSum += (2.0 * gaussPartialSum); - float2 _679 = gaussCoeff.xy * gaussCoeff.yz; - gaussCoeff = float3(_679.x, _679.y, gaussCoeff.z); - } - return color / float4(gaussSum); -} - -static inline __attribute__((always_inline)) -float filterTextSample1Tap(thread const float& offset, thread const texture2d colorTexture, thread const float2& colorTexCoord, thread sampler uSampler) -{ - return colorTexture.sample(uSampler, (colorTexCoord + float2(offset, 0.0))).x; -} - -static inline __attribute__((always_inline)) -void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCenter, thread float4& outAlphaRight, thread const texture2d colorTexture, thread const float2& colorTexCoord, thread const float4& kernel0, thread const float& onePixel, thread sampler uSampler) -{ - bool wide = kernel0.x > 0.0; - float _243; - if (wide) - { - float param = (-4.0) * onePixel; - float2 param_1 = colorTexCoord; - _243 = filterTextSample1Tap(param, colorTexture, param_1, uSampler); - } - else - { - _243 = 0.0; - } - float param_2 = (-3.0) * onePixel; - float2 param_3 = colorTexCoord; - float param_4 = (-2.0) * onePixel; - float2 param_5 = colorTexCoord; - float param_6 = (-1.0) * onePixel; - float2 param_7 = colorTexCoord; - outAlphaLeft = float4(_243, filterTextSample1Tap(param_2, colorTexture, param_3, uSampler), filterTextSample1Tap(param_4, colorTexture, param_5, uSampler), filterTextSample1Tap(param_6, colorTexture, param_7, uSampler)); - float param_8 = 0.0; - float2 param_9 = colorTexCoord; - outAlphaCenter = filterTextSample1Tap(param_8, colorTexture, param_9, uSampler); - float param_10 = 1.0 * onePixel; - float2 param_11 = colorTexCoord; - float param_12 = 2.0 * onePixel; - float2 param_13 = colorTexCoord; - float param_14 = 3.0 * onePixel; - float2 param_15 = colorTexCoord; - float _303; - if (wide) - { - float param_16 = 4.0 * onePixel; - float2 param_17 = colorTexCoord; - _303 = filterTextSample1Tap(param_16, colorTexture, param_17, uSampler); - } - else - { - _303 = 0.0; - } - outAlphaRight = float4(filterTextSample1Tap(param_10, colorTexture, param_11, uSampler), filterTextSample1Tap(param_12, colorTexture, param_13, uSampler), filterTextSample1Tap(param_14, colorTexture, param_15, uSampler), _303); -} - -static inline __attribute__((always_inline)) -float filterTextConvolve7Tap(thread const float4& alpha0, thread const float3& alpha1, thread const float4& kernel0) -{ - return dot(alpha0, kernel0) + dot(alpha1, kernel0.zyx); -} - -static inline __attribute__((always_inline)) -float filterTextGammaCorrectChannel(thread const float& bgColor, thread const float& fgColor, thread const texture2d gammaLUT, thread sampler uSampler) -{ - return gammaLUT.sample(uSampler, float2(fgColor, 1.0 - bgColor)).x; -} - -static inline __attribute__((always_inline)) -float3 filterTextGammaCorrect(thread const float3& bgColor, thread const float3& fgColor, thread const texture2d gammaLUT, thread sampler uSampler) -{ - float param = bgColor.x; - float param_1 = fgColor.x; - float param_2 = bgColor.y; - float param_3 = fgColor.y; - float param_4 = bgColor.z; - float param_5 = fgColor.z; - return float3(filterTextGammaCorrectChannel(param, param_1, gammaLUT, uSampler), filterTextGammaCorrectChannel(param_2, param_3, gammaLUT, uSampler), filterTextGammaCorrectChannel(param_4, param_5, gammaLUT, uSampler)); -} - -static inline __attribute__((always_inline)) -float4 filterText(thread const float2& colorTexCoord, thread const texture2d colorTexture, thread const texture2d gammaLUT, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1, thread const float4& filterParams2, thread sampler uSampler) -{ - float4 kernel0 = filterParams0; - float3 bgColor = filterParams1.xyz; - float3 fgColor = filterParams2.xyz; - bool gammaCorrectionEnabled = filterParams2.w != 0.0; - float3 alpha; - if (kernel0.w == 0.0) - { - alpha = colorTexture.sample(uSampler, colorTexCoord).xxx; - } - else - { - float2 param_3 = colorTexCoord; - float4 param_4 = kernel0; - float param_5 = 1.0 / colorTextureSize.x; - float4 param; - float param_1; - float4 param_2; - filterTextSample9Tap(param, param_1, param_2, colorTexture, param_3, param_4, param_5, uSampler); - float4 alphaLeft = param; - float alphaCenter = param_1; - float4 alphaRight = param_2; - float4 param_6 = alphaLeft; - float3 param_7 = float3(alphaCenter, alphaRight.xy); - float4 param_8 = kernel0; - float r = filterTextConvolve7Tap(param_6, param_7, param_8); - float4 param_9 = float4(alphaLeft.yzw, alphaCenter); - float3 param_10 = alphaRight.xyz; - float4 param_11 = kernel0; - float g = filterTextConvolve7Tap(param_9, param_10, param_11); - float4 param_12 = float4(alphaLeft.zw, alphaCenter, alphaRight.x); - float3 param_13 = alphaRight.yzw; - float4 param_14 = kernel0; - float b = filterTextConvolve7Tap(param_12, param_13, param_14); - alpha = float3(r, g, b); - } - if (gammaCorrectionEnabled) - { - float3 param_15 = bgColor; - float3 param_16 = alpha; - alpha = filterTextGammaCorrect(param_15, param_16, gammaLUT, uSampler); - } - return float4(mix(bgColor, fgColor, alpha), 1.0); -} - -static inline __attribute__((always_inline)) -float4 sampleColor(thread const texture2d colorTexture, thread const float2& colorTexCoord, thread sampler uSampler) -{ - return colorTexture.sample(uSampler, colorTexCoord); -} - -static inline __attribute__((always_inline)) -float4 filterNone(thread const float2& colorTexCoord, thread const texture2d colorTexture, thread sampler uSampler) -{ - float2 param = colorTexCoord; - return sampleColor(colorTexture, param, uSampler); -} - -static inline __attribute__((always_inline)) -float4 filterColor(thread const float2& colorTexCoord, thread const texture2d colorTexture, thread const texture2d gammaLUT, thread const float2& colorTextureSize, thread const float2& fragCoord, thread const float2& framebufferSize, thread const float4& filterParams0, thread const float4& filterParams1, thread const float4& filterParams2, thread const int& colorFilter, thread sampler uSampler) -{ - switch (colorFilter) - { - case 1: - { - float2 param = colorTexCoord; - float2 param_1 = colorTextureSize; - float2 param_2 = fragCoord; - float2 param_3 = framebufferSize; - float4 param_4 = filterParams0; - float4 param_5 = filterParams1; - return filterRadialGradient(param, colorTexture, param_1, param_2, param_3, param_4, param_5, uSampler); - } - case 3: - { - float2 param_6 = colorTexCoord; - float2 param_7 = colorTextureSize; - float4 param_8 = filterParams0; - float4 param_9 = filterParams1; - return filterBlur(param_6, colorTexture, param_7, param_8, param_9, uSampler); - } - case 2: - { - float2 param_10 = colorTexCoord; - float2 param_11 = colorTextureSize; - float4 param_12 = filterParams0; - float4 param_13 = filterParams1; - float4 param_14 = filterParams2; - return filterText(param_10, colorTexture, gammaLUT, param_11, param_12, param_13, param_14, uSampler); - } - } - float2 param_15 = colorTexCoord; - return filterNone(param_15, colorTexture, uSampler); -} - -static inline __attribute__((always_inline)) -float4 combineColor0(thread const float4& destColor, thread const float4& srcColor, thread const int& op) -{ - switch (op) - { - case 1: - { - return float4(srcColor.xyz, srcColor.w * destColor.w); - } - case 2: - { - return float4(destColor.xyz, srcColor.w * destColor.w); - } - } - return destColor; -} - -static inline __attribute__((always_inline)) -float3 compositeScreen(thread const float3& destColor, thread const float3& srcColor) -{ - return (destColor + srcColor) - (destColor * srcColor); -} - -static inline __attribute__((always_inline)) -float3 compositeSelect(thread const bool3& cond, thread const float3& ifTrue, thread const float3& ifFalse) -{ - float _745; - if (cond.x) - { - _745 = ifTrue.x; - } - else - { - _745 = ifFalse.x; - } - float _756; - if (cond.y) - { - _756 = ifTrue.y; - } - else - { - _756 = ifFalse.y; - } - float _767; - if (cond.z) - { - _767 = ifTrue.z; - } - else - { - _767 = ifFalse.z; - } - return float3(_745, _756, _767); -} - -static inline __attribute__((always_inline)) -float3 compositeHardLight(thread const float3& destColor, thread const float3& srcColor) -{ - float3 param = destColor; - float3 param_1 = (float3(2.0) * srcColor) - float3(1.0); - bool3 param_2 = srcColor <= float3(0.5); - float3 param_3 = (destColor * float3(2.0)) * srcColor; - float3 param_4 = compositeScreen(param, param_1); - return compositeSelect(param_2, param_3, param_4); -} - -static inline __attribute__((always_inline)) -float3 compositeColorDodge(thread const float3& destColor, thread const float3& srcColor) -{ - bool3 destZero = destColor == float3(0.0); - bool3 srcOne = srcColor == float3(1.0); - bool3 param = srcOne; - float3 param_1 = float3(1.0); - float3 param_2 = destColor / (float3(1.0) - srcColor); - bool3 param_3 = destZero; - float3 param_4 = float3(0.0); - float3 param_5 = compositeSelect(param, param_1, param_2); - return compositeSelect(param_3, param_4, param_5); -} - -static inline __attribute__((always_inline)) -float3 compositeSoftLight(thread const float3& destColor, thread const float3& srcColor) -{ - bool3 param = destColor <= float3(0.25); - float3 param_1 = ((((float3(16.0) * destColor) - float3(12.0)) * destColor) + float3(4.0)) * destColor; - float3 param_2 = sqrt(destColor); - float3 darkenedDestColor = compositeSelect(param, param_1, param_2); - bool3 param_3 = srcColor <= float3(0.5); - float3 param_4 = destColor * (float3(1.0) - destColor); - float3 param_5 = darkenedDestColor - destColor; - float3 factor = compositeSelect(param_3, param_4, param_5); - return destColor + (((srcColor * 2.0) - float3(1.0)) * factor); -} - -static inline __attribute__((always_inline)) -float compositeDivide(thread const float& num, thread const float& denom) -{ - float _781; - if (denom != 0.0) - { - _781 = num / denom; - } - else - { - _781 = 0.0; - } - return _781; -} - -static inline __attribute__((always_inline)) -float3 compositeRGBToHSL(thread const float3& rgb) -{ - float v = fast::max(fast::max(rgb.x, rgb.y), rgb.z); - float xMin = fast::min(fast::min(rgb.x, rgb.y), rgb.z); - float c = v - xMin; - float l = mix(xMin, v, 0.5); - float3 _887; - if (rgb.x == v) - { - _887 = float3(0.0, rgb.yz); - } - else - { - float3 _900; - if (rgb.y == v) - { - _900 = float3(2.0, rgb.zx); - } - else - { - _900 = float3(4.0, rgb.xy); - } - _887 = _900; - } - float3 terms = _887; - float param = ((terms.x * c) + terms.y) - terms.z; - float param_1 = c; - float h = 1.0471975803375244140625 * compositeDivide(param, param_1); - float param_2 = c; - float param_3 = v; - float s = compositeDivide(param_2, param_3); - return float3(h, s, l); -} - -static inline __attribute__((always_inline)) -float3 compositeHSL(thread const float3& destColor, thread const float3& srcColor, thread const int& op) -{ - switch (op) - { - case 12: - { - return float3(srcColor.x, destColor.y, destColor.z); - } - case 13: - { - return float3(destColor.x, srcColor.y, destColor.z); - } - case 14: - { - return float3(srcColor.x, srcColor.y, destColor.z); - } - default: - { - return float3(destColor.x, destColor.y, srcColor.z); - } - } -} - -static inline __attribute__((always_inline)) -float3 compositeHSLToRGB(thread const float3& hsl) -{ - float a = hsl.y * fast::min(hsl.z, 1.0 - hsl.z); - float3 ks = mod(float3(0.0, 8.0, 4.0) + float3(hsl.x * 1.90985929965972900390625), float3(12.0)); - return hsl.zzz - (fast::clamp(fast::min(ks - float3(3.0), float3(9.0) - ks), float3(-1.0), float3(1.0)) * a); -} - -static inline __attribute__((always_inline)) -float3 compositeRGB(thread const float3& destColor, thread const float3& srcColor, thread const int& op) -{ - switch (op) - { - case 1: - { - return destColor * srcColor; - } - case 2: - { - float3 param = destColor; - float3 param_1 = srcColor; - return compositeScreen(param, param_1); - } - case 3: - { - float3 param_2 = srcColor; - float3 param_3 = destColor; - return compositeHardLight(param_2, param_3); - } - case 4: - { - return fast::min(destColor, srcColor); - } - case 5: - { - return fast::max(destColor, srcColor); - } - case 6: - { - float3 param_4 = destColor; - float3 param_5 = srcColor; - return compositeColorDodge(param_4, param_5); - } - case 7: - { - float3 param_6 = float3(1.0) - destColor; - float3 param_7 = float3(1.0) - srcColor; - return float3(1.0) - compositeColorDodge(param_6, param_7); - } - case 8: - { - float3 param_8 = destColor; - float3 param_9 = srcColor; - return compositeHardLight(param_8, param_9); - } - case 9: - { - float3 param_10 = destColor; - float3 param_11 = srcColor; - return compositeSoftLight(param_10, param_11); - } - case 10: - { - return abs(destColor - srcColor); - } - case 11: - { - return (destColor + srcColor) - ((float3(2.0) * destColor) * srcColor); - } - case 12: - case 13: - case 14: - case 15: - { - float3 param_12 = destColor; - float3 param_13 = srcColor; - float3 param_14 = compositeRGBToHSL(param_12); - float3 param_15 = compositeRGBToHSL(param_13); - int param_16 = op; - float3 param_17 = compositeHSL(param_14, param_15, param_16); - return compositeHSLToRGB(param_17); - } - } - return srcColor; -} - -static inline __attribute__((always_inline)) -float4 composite(thread const float4& srcColor, thread const texture2d destTexture, thread const float2& destTextureSize, thread const float2& fragCoord, thread const int& op, thread sampler uSampler) -{ - if (op == 0) - { - return srcColor; - } - float2 destTexCoord = fragCoord / destTextureSize; - float4 destColor = destTexture.sample(uSampler, destTexCoord); - float3 param = destColor.xyz; - float3 param_1 = srcColor.xyz; - int param_2 = op; - float3 blendedRGB = compositeRGB(param, param_1, param_2); - return float4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0); -} - -static inline __attribute__((always_inline)) -void calculateColor(thread const int& tileCtrl, thread const int& ctrl, thread sampler uSampler, thread texture2d uMaskTexture0, constant uMaskTextureSize0& v_1279, thread float3& vMaskTexCoord0, thread float4& vBaseColor, thread float2& vColorTexCoord0, thread texture2d uColorTexture0, thread texture2d uGammaLUT, constant uColorTextureSize0& v_1317, thread float4& gl_FragCoord, constant uFramebufferSize& v_1321, constant uFilterParams0& v_1324, constant uFilterParams1& v_1327, constant uFilterParams2& v_1330, thread texture2d uDestTexture, thread float4& oFragColor) -{ - int maskCtrl0 = (tileCtrl >> 0) & 3; - float maskAlpha = 1.0; - float param = maskAlpha; - float2 param_1 = v_1279.iMaskTextureSize0; - float3 param_2 = vMaskTexCoord0; - int param_3 = maskCtrl0; - maskAlpha = sampleMask(param, uMaskTexture0, param_1, param_2, param_3, uSampler); - float4 color = vBaseColor; - int color0Combine = (ctrl >> 6) & 3; - if (color0Combine != 0) - { - int color0Filter = (ctrl >> 4) & 3; - float2 param_4 = vColorTexCoord0; - float2 param_5 = v_1317.iColorTextureSize0; - float2 param_6 = gl_FragCoord.xy; - float2 param_7 = v_1321.iFramebufferSize; - float4 param_8 = v_1324.iFilterParams0; - float4 param_9 = v_1327.iFilterParams1; - float4 param_10 = v_1330.iFilterParams2; - int param_11 = color0Filter; - float4 color0 = filterColor(param_4, uColorTexture0, uGammaLUT, param_5, param_6, param_7, param_8, param_9, param_10, param_11, uSampler); - float4 param_12 = color; - float4 param_13 = color0; - int param_14 = color0Combine; - color = combineColor0(param_12, param_13, param_14); - } - color.w *= maskAlpha; - int compositeOp = (ctrl >> 8) & 15; - float4 param_15 = color; - float2 param_16 = v_1321.iFramebufferSize; - float2 param_17 = gl_FragCoord.xy; - int param_18 = compositeOp; - color = composite(param_15, uDestTexture, param_16, param_17, param_18, uSampler); - float3 _1389 = color.xyz * color.w; - color = float4(_1389.x, _1389.y, _1389.z, color.w); - oFragColor = color; -} - -fragment main0_out main0(main0_in in [[stage_in]], constant uMaskTextureSize0& v_1279 [[buffer(0)]], constant uColorTextureSize0& v_1317 [[buffer(1)]], constant uFramebufferSize& v_1321 [[buffer(2)]], constant uFilterParams0& v_1324 [[buffer(3)]], constant uFilterParams1& v_1327 [[buffer(4)]], constant uFilterParams2& v_1330 [[buffer(5)]], constant uCtrl& _1401 [[buffer(6)]], texture2d uMaskTexture0 [[texture(0)]], texture2d uColorTexture0 [[texture(1)]], texture2d uGammaLUT [[texture(2)]], texture2d uDestTexture [[texture(3)]], sampler uSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) -{ - main0_out out = {}; - int param = int(in.vTileCtrl); - int param_1 = _1401.iCtrl; - calculateColor(param, param_1, uSampler, uMaskTexture0, v_1279, in.vMaskTexCoord0, in.vBaseColor, in.vColorTexCoord0, uColorTexture0, uGammaLUT, v_1317, gl_FragCoord, v_1321, v_1324, v_1327, v_1330, uDestTexture, out.oFragColor); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/tile.vs.metal b/crates/pathfinder/resources/shaders/metal/tile.vs.metal deleted file mode 100644 index 35a40cc57f..0000000000 --- a/crates/pathfinder/resources/shaders/metal/tile.vs.metal +++ /dev/null @@ -1,63 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uTileSize -{ - float2 tileSize; -}; - -struct uTextureMetadataSize -{ - int2 textureMetadataSize; -}; - -struct uTransform -{ - float4x4 transform; -}; - -struct main0_out -{ - float3 vMaskTexCoord0 [[user(locn0)]]; - float2 vColorTexCoord0 [[user(locn1)]]; - float4 vBaseColor [[user(locn2)]]; - float vTileCtrl [[user(locn3)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aTileOffset [[attribute(0)]]; - int2 aTileOrigin [[attribute(1)]]; - uint2 aMaskTexCoord0 [[attribute(2)]]; - int2 aMaskBackdrop [[attribute(3)]]; - int aColor [[attribute(4)]]; - int aTileCtrl [[attribute(5)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uTileSize& _26 [[buffer(0)]], constant uTextureMetadataSize& _49 [[buffer(1)]], constant uTransform& _161 [[buffer(2)]], texture2d uTextureMetadata [[texture(0)]], sampler uSampler [[sampler(0)]]) -{ - main0_out out = {}; - float2 tileOrigin = float2(in.aTileOrigin); - float2 tileOffset = float2(in.aTileOffset); - float2 position = (tileOrigin + tileOffset) * _26.tileSize; - float2 maskTexCoord0 = (float2(in.aMaskTexCoord0) + tileOffset) * _26.tileSize; - float2 textureMetadataScale = float2(1.0) / float2(_49.textureMetadataSize); - float2 metadataEntryCoord = float2(float((in.aColor % 128) * 4), float(in.aColor / 128)); - float2 colorTexMatrix0Coord = (metadataEntryCoord + float2(0.5)) * textureMetadataScale; - float2 colorTexOffsetsCoord = (metadataEntryCoord + float2(1.5, 0.5)) * textureMetadataScale; - float2 baseColorCoord = (metadataEntryCoord + float2(2.5, 0.5)) * textureMetadataScale; - float4 colorTexMatrix0 = uTextureMetadata.sample(uSampler, colorTexMatrix0Coord, level(0.0)); - float4 colorTexOffsets = uTextureMetadata.sample(uSampler, colorTexOffsetsCoord, level(0.0)); - float4 baseColor = uTextureMetadata.sample(uSampler, baseColorCoord, level(0.0)); - out.vColorTexCoord0 = (float2x2(float2(colorTexMatrix0.xy), float2(colorTexMatrix0.zw)) * position) + colorTexOffsets.xy; - out.vMaskTexCoord0 = float3(maskTexCoord0, float(in.aMaskBackdrop.x)); - out.vBaseColor = baseColor; - out.vTileCtrl = float(in.aTileCtrl); - out.gl_Position = _161.transform * float4(position, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/tile_clip.fs.metal b/crates/pathfinder/resources/shaders/metal/tile_clip.fs.metal deleted file mode 100644 index 03baf4b4d0..0000000000 --- a/crates/pathfinder/resources/shaders/metal/tile_clip.fs.metal +++ /dev/null @@ -1,24 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -struct main0_in -{ - float2 vTexCoord [[user(locn0)]]; - float vBackdrop [[user(locn1)]]; -}; - -fragment main0_out main0(main0_in in [[stage_in]], texture2d uSrc [[texture(0)]], sampler uSampler [[sampler(0)]]) -{ - main0_out out = {}; - out.oFragColor = fast::clamp(abs(uSrc.sample(uSampler, in.vTexCoord) + float4(in.vBackdrop)), float4(0.0), float4(1.0)); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/tile_clip.vs.metal b/crates/pathfinder/resources/shaders/metal/tile_clip.vs.metal deleted file mode 100644 index 3e3a6baf7c..0000000000 --- a/crates/pathfinder/resources/shaders/metal/tile_clip.vs.metal +++ /dev/null @@ -1,32 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct main0_out -{ - float2 vTexCoord [[user(locn0)]]; - float vBackdrop [[user(locn1)]]; - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aTileOffset [[attribute(0)]]; - int2 aDestTileOrigin [[attribute(1)]]; - int2 aSrcTileOrigin [[attribute(2)]]; - int aSrcBackdrop [[attribute(3)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]]) -{ - main0_out out = {}; - float2 destPosition = float2(in.aDestTileOrigin + in.aTileOffset) / float2(256.0); - float2 srcPosition = float2(in.aSrcTileOrigin + in.aTileOffset) / float2(256.0); - out.vTexCoord = srcPosition; - out.vBackdrop = float(in.aSrcBackdrop); - out.gl_Position = float4(mix(float2(-1.0), float2(1.0), destPosition), 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/tile_copy.fs.metal b/crates/pathfinder/resources/shaders/metal/tile_copy.fs.metal deleted file mode 100644 index 4106ef58b5..0000000000 --- a/crates/pathfinder/resources/shaders/metal/tile_copy.fs.metal +++ /dev/null @@ -1,24 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uFramebufferSize -{ - float2 framebufferSize; -}; - -struct main0_out -{ - float4 oFragColor [[color(0)]]; -}; - -fragment main0_out main0(constant uFramebufferSize& _17 [[buffer(0)]], texture2d uSrc [[texture(0)]], sampler uSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) -{ - main0_out out = {}; - float2 texCoord = gl_FragCoord.xy / _17.framebufferSize; - out.oFragColor = uSrc.sample(uSampler, texCoord); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/metal/tile_copy.vs.metal b/crates/pathfinder/resources/shaders/metal/tile_copy.vs.metal deleted file mode 100644 index baa3b90263..0000000000 --- a/crates/pathfinder/resources/shaders/metal/tile_copy.vs.metal +++ /dev/null @@ -1,34 +0,0 @@ -// Automatically generated from files in pathfinder/shaders/. Do not edit! -#include -#include - -using namespace metal; - -struct uTileSize -{ - float2 tileSize; -}; - -struct uTransform -{ - float4x4 transform; -}; - -struct main0_out -{ - float4 gl_Position [[position]]; -}; - -struct main0_in -{ - int2 aTilePosition [[attribute(0)]]; -}; - -vertex main0_out main0(main0_in in [[stage_in]], constant uTileSize& _18 [[buffer(0)]], constant uTransform& _34 [[buffer(1)]]) -{ - main0_out out = {}; - float2 position = float2(in.aTilePosition) * _18.tileSize; - out.gl_Position = _34.transform * float4(position, 0.0, 1.0); - return out; -} - diff --git a/crates/pathfinder/resources/shaders/vulkan/blit.fs.spv b/crates/pathfinder/resources/shaders/vulkan/blit.fs.spv deleted file mode 100644 index dca202be50a67299bf520ca5fa7a76ba0a91c827..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1040 zcmZ9K*-8UJ5JlU>n7FTryH4B}5Fb9@}eaLMhF@nNQNu4*!2 zNcI)>9cU34#qa!bXO8$^BC3gTChsDew_oeZxM#Z4gxOFiTm!2P#0?}T+nZFVJ`*3II1-^mYc?W^BUUSMhhk+h-JoaqLS zwZ?!x^#i)vLruT=DWG=74V-T})zn>Wz-RJFONCwV2j;gfdjJ3c diff --git a/crates/pathfinder/resources/shaders/vulkan/blit.vs.spv b/crates/pathfinder/resources/shaders/vulkan/blit.vs.spv deleted file mode 100644 index 354b57fbf1c7528888691dd8a1ba34117d9603f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1140 zcmYk4U279T6o$vdP1V-cTHE?j>(=@OO7KEO5EZEqFM1KFcq_p!vJkV9tRi0d&jf#! zH-gVIlZl;ha?W|rJLhY*+u2_V;Z!)o?_Nk}J#;`qSPs3Y2Zu+8{aJh5fAsjFjMdPM zL^*5R^PEOLkLro`d1M{Af^_+H(7z!Uuuk#{kh8?v>ia>{Oph@^igv+6~70l+0o&l3N_ipr(Z_n9(!wmN8SxzcsEbZD?2n)zMOe^nU(&z?<0b&KwiZoa>46GIM-KF(q?uMo#ME4z_*R;}i0_H)r*T{{bn` z?#A8w-CO%PzPnyU+-HL?zBb2Ra9ggvTw@mzF=OsnKj*oDeKJppFSbHe@9rV)xQ{ru xbCt+jx^p-Ch#XJGZeizpL$WyIZA7jy_uyV$k;6UQMSKJIZjbu%|5EuW@((r~MZN$4 diff --git a/crates/pathfinder/resources/shaders/vulkan/clear.fs.spv b/crates/pathfinder/resources/shaders/vulkan/clear.fs.spv deleted file mode 100644 index a03bd936c09ec1c21bd84c676c4b58ea490e2440..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 792 zcmYk4-AY1H5QVp%G|N)U)P7C#GLSARf~b%nyAUdR0x5`~60Giff*z`;>L%#>Jm-j8 zX3w5AYu3yhjIy2i5Hg{_Z#TqeC1gQDD2DvR_Xqcb?)%4M_weXIMkz!SqMS0EIlT5d ze10>rfK}j#A1*E9UiloL%foI}2~d(&JTw~5VHx%KVI4oHFh4c8u2o|g=Mh)Brfsx*t?9kx7zAe@Ybp?-`pA&!q#eANUK_qdC?cWBtB zhRN}ySe>_++!Sjhn6}E1E-+VKB_RL8ug9}GP7zjiB9)8$PG+NTJ!j!`quM} z=G(U?^_4K=V*Na8iuumO(-!la@GWUZ6LcVd4or^r6jlvgN2bZ}i_PM#pooQFeSK#y zW-rdzKCR_G%oCHV@0?SudbH)+i*LGwaGodsO5uKgX7AUr>xEzP^=}~FJwtpEZ4I$m zENAXJ#EdESbqnq6joX*L-=diJTK6{Mk?)^^J(hLKK35HLjIW{1^{!RE9pC3}JmP;q zs%YY{n^XR2?8Et!VB#r%3R_IEKl}17 z_j?X;hr9URbv|MvbgMsuc%L(}4{OQs{yKUNaj&U=bLJ!d6OR4@Vjb~c5xX35cixQH zRcv>z{D{~!Y`Mms(z){!^L}@87ZG!3zR5k^L&Ov3KDHdcjl@~T7XKD=RLloS_9yz5 z*_nC2cix#@QYZ{i zRHKvSm;d;^KTecrIEQ>|xBu?ayi4v*O{SICO(qXX+$JuA#n>|S?q%*SG4BfAuOfbw z<<2D5;qCn1$*tnm7n5u5JYp^Kd3=l&u$?Tg5qs=i$J<+N^%{6<)tB$x5*EV8sd}qK zu_?!U^EE85UnjQSx&ywQv3q&se1nwmXT0sTfH|)-9mBNmA|`&tmvFvjhKYOJMco@T z?9;;Jcyg>wEG9R{mNU%SYdYC$1+!kh_wIUzs(Wl;`E2g!eD>SS?mp?ShX0B>;wNmG L|35!;Kg50k75^b$ diff --git a/crates/pathfinder/resources/shaders/vulkan/debug_solid.vs.spv b/crates/pathfinder/resources/shaders/vulkan/debug_solid.vs.spv deleted file mode 100644 index 850396e9921f2e3521eeab893685f59b04c71e58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1324 zcmZ9LO>0w85Qa~a-dL@+*4oyO8k5HFYILC@h+inCi!K5Mw-WV6E~H6GZa~~9xN+r= z@>jVLe4d+o+Tw)C%)Il?oH=unYNd4|gt@T5=YE*UN~nN@uo!BE?d)lI7W`s1vic*F6Fg`kw`x zz8&}W(+|^rKOOI7UsE8ao8|QT|D_YoK4$_+U^cOz2k*P-_+2{A(=Xz+64y5uPqnXn z?^od2Fh72#Iqya8^&mTVlTGs8@MBucW6tfEn+^ttIp=XFN{3B&&w3Gsu)~DjQ90sN z3(RxmdzUQO@u>oz%^=@%#3KJRzVnGcC2#D0#Oj>oZtP|66|8k)a*f@M*fz0! zS5Za&d8|3>%{%7>6nPorOGWtog|l73m*XCs-8X8WihSd%sIzSnH?Z=>>Ud3T?9srV z>3s2a?$5k)Z(&=gSnPWZUvcj1SZB6(oO2srF@Fzht!F*v{k@|zE8>4Z60B$W9{$(t z_l(#*FQM*uL@d5sVy~!jXKSc?b(hx2Sx3cx!ZF@JtrP!MVi!x?9d}FYGQK-jf0WoJ zzFc$9nb`TnJm0(DK*ijN=eviSsCeYu!k6RjBj+~0__wm=4!&5IiQUy*)IQ?w)0yn& TKHI2o;+>tzSpHvL`w99FGM7}s diff --git a/crates/pathfinder/resources/shaders/vulkan/debug_texture.fs.spv b/crates/pathfinder/resources/shaders/vulkan/debug_texture.fs.spv deleted file mode 100644 index 526a0c377e4b9f5ceed4473a6ef46283389e4516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1324 zcmYk6Sx*#E5JvAX3nvY6!ncYJ=AK`cuU6(b15RKV(xsjqyfAdqIbTtQ`BXDwch^N%rS zz&!R}kDTOp@x6n-v()4I#@&o@=Sn?Z*JkK}D+8F$mFmf@*VDJ2d$OK0rg{rxnb)O; z{X8#m>R_`{TjkE(yVy!N#aEph*t#S?qsQqOkozq9?m=0l%UP_vnGy-5FHhh5D{IU# zkF~}!u?4I%IHS4evGx=!Tt3 zc=?3i31i+*fNOmpWY;&H(IUs4!W56jeoF8 zI0e=P>vY@6cKRj3WWpR*@ASFK^gJyHS??V3pmmsb(pEO3nDaI2J+K^x!$q*}t9D~A zdEedLP1?Oaxes^P{m*T$OFE!3^V~vyY`&`}?YBvrp~Rh2T+$c2R_H12E;Bsc@AUW7 z=kD-rG}D8Z>0zg_{~@7Tp64a>b(_s2pYynPX_IZc-?_}nZFj3}&(}=sUZsUxkW+G<{(54up1bO=r)@pS{J5vl96G}6C#H;X))aFO5mOiQ zJM&%Q4kwww_!^io>eFZyWEGwvgMWt1)>p+IK^iOmq_Ewm*cA40WEFNM^X)4Z^G_5$ zar3p^zwvWaarf4?*96i$F=Msez1SkQbthrT_%ld-G5>6#$64*=ZDKEP5M#ZUxnlOx z_71U^J6V9Cb$e@H$;@xheb4LYV(!>I+(1@g$$0IXu)Sxo7m>z`&7padSVN!eJKaXQ zTm8fQm9j9*6jPS%cduAxi%A0n-1uQk3c-_MyP@!ue2q`lmqzZ2GbitWwD zVDHt!79TIL7i78rDcBo%7jul6hQ)snqdfzgC;p7Cem`Qd#|3on6no5~i^U#QbZ^t& zqlPUWd(5MoC;qeOdl_BaJJt*AD!O-+zZcjwbYt~7pF0q{51g4V_2uqhfgU{m1 zH}Gk^Bqn}ePakZ1Qd9N+Rezm2)tyajPDV6hieD$j&q7SVL`+9s`tIKQz0SwuUgzHnWXlSO-+D)sk>(HAGf zUZoDY<{|IXucg#J7}L?^7T$HOhMh0rw{%Z4Y@XL-CFd^GJIBU(Zj9yJu=+GZT`!mP z&QjNl)tg`BU*D#i{ft+La(oM0<$T|b*jx)dg_qOBm+*}WKd-QNmQ%lgSMU3@{tDi+ zL=Zf2y z+%k9r-@+Q0bBNPU)!znNn|izB)cbywle4$jJzM{jJLtW?kN5lu)5KfrKK-_xTkIWf zR`>;2eg<==Gi*nE8FNl|Z!Xo%5?lAZ(#;XeeXjO6Ppnq{C6jorT+%NQ%YCKZ-Co1w z-2WGPy7%jt{15c$JN`{h{uXu@^M1y!>EqtEFn8@fJ|v!{CdB>gtA e`>gAC!175iuIufS&U#4f8P@u(iT|*{XV_oMNlkwM diff --git a/crates/pathfinder/resources/shaders/vulkan/demo_ground.vs.spv b/crates/pathfinder/resources/shaders/vulkan/demo_ground.vs.spv deleted file mode 100644 index 998fc9eaeb97f82c2c0b0778e491a27d9af4df10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1572 zcmY+E+iuf95QaB#k^(I)rSw24snc*Sgi~7~A&^jz(u-b1YQ-&zh^ba`ZR9wp;+hxW zm3S&{koZ4)H!%~9X6K)Ob`I+{sCO5PshKPK9hg#9O6iB!O7`KZ<3FD2Zt|U zESsPp!dX!s(vV(^Y{L1f^tyCc8t7M-{Vj4+tSYZ51n&2<=(3-t*;r>Va!12-Vso2v zVN-pq$W8~D9mh6_`sp;ujnIVxSIC_IA5uO1?z17~3H>jpJF2e%J@Xj2+qOSvE>0U(@WO z>;(+T=dhT8>6Wk=XnX9dUGdqK_m9P9PsltcC@}gZpPlSMka(ZE%t}6UBa5Y(VZ_0z zJnjTzUZ2YujCUR=!hP_Lg-{1+O5uCo_gcmDQJNc4?ttg}hg_*W3YAk*JML!O-eqqf%*I;7Xv3BpErTu)-Ak8c}Js| zhdeO2%*T6x?Ymkxbw=oIO_mt>)V?iC@9dV|wq-*pLO%W-DYbXx@5sUjYwN^Y_&bQ9 z)ZzPKUgGqHU;gbHvn4;2PabudsUsT}+9=+F9>K`nlkG~uxclDhJn!+LFogIc*_xCY z{SKZvjG22bMs4`7Wa*WDj`R&thZq9>M+o27JE6SEvXuAuAe4RHlCpF542QbwQt+WT z=Q&sq*+M}n=RDOJe5fwi&vPkx)FQ_I=#d@q51aTe)x>`(r4Mjwu+P_0-kW@8!H4%(FY`wF E4;cb_24byEq`f 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 diff --git a/crates/pathfinder/resources/shaders/vulkan/fill.vs.spv b/crates/pathfinder/resources/shaders/vulkan/fill.vs.spv deleted file mode 100644 index 5ab3299b1e8c13dcb567cd91fca3cfc8900bc012..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4468 zcmZ9N_j6QL6vrRLBq9hRh#iAiP@0Zd5QU8>v7iKry}*)eaCMW7TM#>_px8V1Ua_G6 zf#Wa!NtPKMKcDyRVcz6szB%W7Pr2vabKl#ZnM?b#Y({o0f4^qMnxD;t$+Fp5UoM9> zZ`-`8-5Fc8=F*Fdn49(FfyT_kavcLEs`W|r%fKMm42Hp-;9l?+I0O!Z9{y$`f2T+< z+2#D+Y*t=%v^g==?bJr`xF;d&p>9%Tj)WkixMkJZro_>*&4JJH1UWD5&A z&pRvmi^ht6)&$q8@A4$<(X*&pKQ-B#^=FsEbvL!D6SbY)@$p(K_UKDK+}ZdvxBIWv z!ScMWL{3!+O7>OL>LJMt+9d|g_~-&>z#TtzYF$y5U!aIu&C`l*ljOJ_w`Qf9%-WY zWgnvNYBYCN8^cGd$bXmf+nL<$wN@t|cRnk5*RIC)qY0j)y!SKb>yw@G8SC@z;oI1# zPhDGYcdC=4HR=_4-fn%}M&qc@KE1cD>*+M^$kKw!hVaW-cZg^9uAF_;SJ+Fi?Mc7) zVln6i*7Hud%Jt-;p0h94)3%<=dV@&o*}Jy$)sFp~r?&5ze&0#$sP8PbUHY9-*>*r>I#Mnn+8Fhdi0VTQ0M`IEAFUkni-ok#j%K)n4SaFF@+EAM2U75@gxl4Pmb;?DZ>{g>kEaG4j^91ZmB9 zrc2T7*L{5^ZSSC*&+lD}*elT8U(Q%<@0(oY4x*bI&$|(QUdbLpceeI&6Vh1go3HKt zl#6=a&#?7-Kef%jC+VJ#@!H;3{R`=8J2LucCqB;UMQm5tFXh&68{*tv#x_PiVqVQ- z_EsY1HEhpT-hSRhdN<>Fw0&RX>`&YGMLTMKfzGOUAHP9yrk>9k{QoxD_P&@2eTR$~ zeZqaeM_NpXn>%br$-zUV}ae{@^6+;aVWK5lp2$u1kF6Y{E9@_mp!zy4(sUhhKp9gJS@MmJ90UY)(%AkX5r=((W`J{n`TFN@3-r~2 zzKDyu55kQC>-yZ<#{Nx@_HVED9PthG3Mlt6fh`{~ljzfXZNkY%uT$vuvkaK8KYD!_ zU7u%TT-0r$mwVMVw%n^d8}H0Iz?ro{+@EgZV`jUt<$NAzwg>5QX8Ilhdx7Vm9=$w@ zUI9_tbA1fRN6h2soeHz_!u&y)HHr8{ne|z1F zjBm|8^o3v%DEIOVw!WzQEPA=FwlU?p){pq-O7WhreeMVH`b(G0-e23;cxPWperx-^c^$~T2A)ma8|eP#>GNz3BITmS+vt^&{SLY@`%?b9=*GyK z{}xg%^4~*ureVL2Ue5mj+ZcKC4sb1l<^U&*cK-r(i8`eo^N$ zbom!j-=CwK`x$sOabKd#e*xlK_!aufggCRWv5nPd&;E|d#a=(4%Wb8uGy4(9xq|x% zUCy}<^F8_o`7@&8ic3gGX%GoA}vv%%%W^&{sbeHQX~ z;C-Km^mkwX2|%g67%KsQf5xQo!`SA+kUfwr^zAIx2S=>Px# diff --git a/crates/pathfinder/resources/shaders/vulkan/reproject.fs.spv b/crates/pathfinder/resources/shaders/vulkan/reproject.fs.spv deleted file mode 100644 index e40464c80521aa71b774b97c5159e90ef511c1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1512 zcmY+DTTc^F6opTzQ}D(`Szd+Fvj2WaJ;6%2uuj)LoM5zy9c|Cqd~i|zVT4S zc&KJVH4|YpoM9Ke*5|&C8E_v=qLb$WPF1po@Q0uXwycl1=PbT{lJ*We-?oz^ZDXRV zQ9}sBy>9z3ZS{{nBEYVZQQnoDb`etz7mr<>qpX&%rKEDp_y?!%iL!c=w`uFscG68! z?~a0x9TGbjrX5qtt;-#>dSAL7R3Yr}lA@L&0=sdc-w?Qhw()^fbI@kM^?F8j6hJIQHO>grh9o@Y7L*kdrqeb%k*++zNZ zvCD~h>~r~9|1z<@O-}6vc5}+T$Zg*%H-kOS9)k*4%k2ryF5~T&TJu@UG`qFP>A%cw zti1WP?N^ac?Hq8KUwb~YzP~p9Dretey!F)CW8hM+y#nlwdgog7qsVs`j+N!R$3d+c z>(O>zatnOv4R&j&fbuGA?|6_q)S5%vnYtEv`D1ossQ*O?Go^@(tQA z=i{=Lzc7BY#yL-agGJ5u#P3V{4Jv0a1)Q6+c}a}(xCG?2?cXf0_wR`DjSD%)cET;@ zobTP|2Jct`YV|oQ=kuE!`mO-)QS*w*ty9iB^8s(|BvV z0p$157=JV8YbSoYT+Wk?NF|TP=^U&HZ%+0-9Omh8BdOOo;-c5 zVlFfap_+Nlxh`PG{cPy|Dz<>##2WnS_+N+VJRcpgPpEAL|7Gy8mySN9qe*%Oge`gZ#$uLcm?`wuT=3a&a*%yb)y0@~65AbQ zCvUUyq(A(WPGfd#>~t`g#u9q7N0;@i;ZNVC-wsFp;rMetItH6t-ZlI`#F<7|FGHcr zWbFGqw&#fVP}{F-wsawKTDKIb$8}m)SM(Eo_DJ<`o-jf&H@Nv?cK6f-n12Z!dCD ze}%m>kbgm3>`dj%XU*PiKI39%EN8w&y!SgF@4JC-VXo~8FR?e@8h9D6UT&S8^Ncx+ zY2LevFYhSt6_@{wGOs<{!d$E9Z{y8l9qXKQ#&YJmgSTHfdk{PKID?I9uil!iuYgVFcUjrN1bl>cfurvxqYl9|!?=ko6Ht&S& zE3Hpo+%v89;Pw`Pbe;e6V&GDrF87syJ=P7^WA61W-sAARJ4#HR!MwwL5B}T)JONz7 B5`zE$ diff --git a/crates/pathfinder/resources/shaders/vulkan/stencil.vs.spv b/crates/pathfinder/resources/shaders/vulkan/stencil.vs.spv deleted file mode 100644 index 6fc53e39d29bcb62c6bc11b4e87fc9599b0cc022..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 828 zcmYk2UrPc}5XC2L)v`1*wSS1wV<0_L1W^$M_TYmd^cqr2ENpA&2GLWWtxwfU(E07| z-J0d>%$zxQX6`K(_RD4qR?)X(sWq%1#;j_k#1Dq!VSgS!^^cAZQPixM2+`C{$7PGc z%Or@1SA}(a`@BTIrHHfacA_`u?%WZB{}$uPpNpkuQ=s3SSbDWGPA=!#8OKbg>tr1LN1DdG eO@Z_1sk7IW`m)ouz&mi)3vEJ<{zo@C6@CGWDm0q_ diff --git a/crates/pathfinder/resources/shaders/vulkan/tile.fs.spv b/crates/pathfinder/resources/shaders/vulkan/tile.fs.spv deleted file mode 100644 index ec9928be028ba5c20138bc9543a75fe33ae2810d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34884 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 Ezr#zdz5oCK diff --git a/crates/pathfinder/resources/shaders/vulkan/tile_clip.fs.spv b/crates/pathfinder/resources/shaders/vulkan/tile_clip.fs.spv deleted file mode 100644 index f1169cdb2c6a9c1d8a554138d6309b789e6582c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 968 zcmZ9K%}WAN6vbcKn5AiEX=)#1whN?1MGzGv zsW*Nmuq>;{>asOyiK&ThX;n7nONoxyWR4?-JpU%}9y|WT4?;~|+*>~w$VuU-9@v}C zCL<|Aj$ZS-(R;`DgJHzcdw%T=o`!)x)4i*1)ZdkHz|`Ki=npp&m|n>^m^gcrPv2xc zdV-0UwJEzc?bgQLE4pXIxw5MAc@r@2;Oc)?o|lC`k1#XC)inKzJbLOUPmHONivnn= z0cKB5T~id~(G@Mjw+wY)_Tx0vab2Do%()>?U-lt)Q=Xpi^Z@e~aKvlTogCs|-iG+0 zF#Mj36Jyt+kE>ICWBTwW9CrT}p9-m;2B1`Xx}W XB@5pytW6#CmwFX`7sP+0IFtPXk#8-7 diff --git a/crates/pathfinder/resources/shaders/vulkan/tile_clip.vs.spv b/crates/pathfinder/resources/shaders/vulkan/tile_clip.vs.spv deleted file mode 100644 index e6618d85f515e8de32be7d91d886affbe2eb9993..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1668 zcmYk6T~8BH5QYyeEhq>g0)8O2)^D(W0is3?mZV;gh$->bY}$ovtX;CZ65>@a{Z}Ub zDsN1DpR=91rxNS$HNWHs{f?*;T4;%KOtd z&qsr-6a5JBGaJ2;t<{fC_M$Wk2HkLMqwCnd_xod8Mg63&thTyIpSTk_!1M>q0^3Z; z1IAXgbNAwT<_0GB2i=(avS#va%#ja{-TcS_S3HXj$K2bp5D&jaeY4la;e);`t*X5y zS?8F-Ox_`_+kfJ|FGADu@f6RxGsUieoOm=gu1bV8zYW8-IdN+`p2E`X|G9G z)C|5aA;zlk1MT?3tx2Qb76ThX-gWKNfStVH{P&v2rJhF;mi4CT(eRh`=CLpwVm|&4 z!wuh=&r{jp5cAoT&Z3ST?bJby+v+*9J#aASax~yds@9VEC%^VvcLV_`q9^>(b#Z z$EtKV%fUV1h)GR1C2-{DJ%OEQJE>iX>X)yYd4%h4)` zavnt|?qSu*@Zy(=2j~{M0#BROQNjk0`j@h9_5n@;AG(VcWc|J5=@?WCA*T2>xU^KzzVICZ}svC^lHn7hii=RT^`aTmYNd#7EKhIBe@>>|2ONke-VygN3So791@2+#G_}eP>24cI^c30oZ zUBOycOs=u}i>(ts#-^xmkE(bLU-A7DtlYZZQ+)TRdmDQ%u`T{F+gN9{UQ_DWnmbiO zt}_{XQ)QcO-d^5bft24uo^e`XJ9ON~x5jI}xifkn#aV7a60GmMr~hSlct`B)4^eOS z1yp>w!o(Hl{XhlxUq`)}ck#Xa4OILu9OF&Y8shJnzhiSMN{-E@7<$^ c$`P-79N=5$8ysgoMCFOw!@G<9W7;?9e}9%dLI3~& diff --git a/crates/pathfinder/resources/shaders/vulkan/tile_copy.vs.spv b/crates/pathfinder/resources/shaders/vulkan/tile_copy.vs.spv deleted file mode 100644 index d48eab7232b802f6f398e05684257b0ecf0a4226..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1464 zcmYk6O;1x%5Qa}bMNtqD@B{Ids;Fpnp~l3hF`ASGixP-i(`Yp}DJ^Ml6XVK_Kfu4_ zuX1DJ^W59hcACkYdFP!uGjlFf%FS~jl)@6fC!vtFPzDKMIn;C8K0H2bjk7PUXV0I? zSP7M!C}%a0j#SapZaVOM3Ee>}{L0ur5Nj|NIW>^;#LkA}G)sqhUXAvl6ZpFz0&M=KQVysnZ*M?2WSCcSGiiE5_nYb!HKFt{hJX z+4MRxikR~dd*4sbKBVKUJNVq2#oUUqNxwgf*^j$WoFlsta)N`_1(AFKE8F=QAPe$ ztTFSw`x@3>-KD)<$0n#^zWxo=ySMRgV&#kNaB}ythB4K9ZeiVzar@FQ{x0QL!)<)? zyw{l$teCkCY_W?XSATbgRr9wrF5e#QXAf1Pe?RwrzO~+C&i3McFZd?R^>+2d658QrXWmEU8rwtN&2MTLdxUzA9D8wBE!4NN N7kky0_m8SC(f?_+TIB!$ diff --git a/crates/pathfinder/resources/src/embedded.rs b/crates/pathfinder/resources/src/embedded.rs deleted file mode 100644 index e9879cde69..0000000000 --- a/crates/pathfinder/resources/src/embedded.rs +++ /dev/null @@ -1,34 +0,0 @@ -// pathfinder/resources/src/embedded.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Embeds needed resources statically in the binary. - -use crate::ResourceLoader; -use std::io::{Error as IOError, ErrorKind}; - -include!(concat!(env!("OUT_DIR"), "/manifest.rs")); - -pub struct EmbeddedResourceLoader; - -impl EmbeddedResourceLoader { - #[inline] - pub fn new() -> EmbeddedResourceLoader { - EmbeddedResourceLoader - } -} - -impl ResourceLoader for EmbeddedResourceLoader { - fn slurp(&self, virtual_path: &str) -> Result, IOError> { - match RESOURCES.iter().filter(|&(path, _)| *path == virtual_path).next() { - Some((_, data)) => Ok(data.to_vec()), - None => Err(IOError::from(ErrorKind::NotFound)), - } - } -} diff --git a/crates/pathfinder/resources/src/fs.rs b/crates/pathfinder/resources/src/fs.rs deleted file mode 100644 index 37ffec91bb..0000000000 --- a/crates/pathfinder/resources/src/fs.rs +++ /dev/null @@ -1,62 +0,0 @@ -// pathfinder/resources/src/fs.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Reads resources from the filesystem. - -use crate::ResourceLoader; -use std::env; -use std::fs::File; -use std::io::{Error as IOError, Read}; -use std::path::PathBuf; - -pub struct FilesystemResourceLoader { - pub directory: PathBuf, -} - -impl FilesystemResourceLoader { - pub fn locate() -> FilesystemResourceLoader { - let mut parent_directory = env::current_dir().unwrap(); - loop { - // So ugly :( - let mut resources_directory = parent_directory.clone(); - resources_directory.push("resources"); - if resources_directory.is_dir() { - let mut shaders_directory = resources_directory.clone(); - let mut textures_directory = resources_directory.clone(); - shaders_directory.push("shaders"); - textures_directory.push("textures"); - if shaders_directory.is_dir() && textures_directory.is_dir() { - return FilesystemResourceLoader { - directory: resources_directory, - }; - } - } - - if !parent_directory.pop() { - break; - } - } - - panic!("No suitable `resources/` directory found!"); - } -} - -impl ResourceLoader for FilesystemResourceLoader { - fn slurp(&self, virtual_path: &str) -> Result, IOError> { - let mut path = self.directory.clone(); - virtual_path - .split('/') - .for_each(|segment| path.push(segment)); - - let mut data = vec![]; - File::open(&path)?.read_to_end(&mut data)?; - Ok(data) - } -} diff --git a/crates/pathfinder/resources/src/lib.rs b/crates/pathfinder/resources/src/lib.rs deleted file mode 100644 index 33fe80c5eb..0000000000 --- a/crates/pathfinder/resources/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -// pathfinder/resources/src/lib.rs -// -// Copyright © 2020 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An abstraction for reading resources. -//! -//! This accomplishes two purposes over just using the filesystem to locate shaders and so forth: -//! -//! 1. Downstream users of Pathfinder shouldn't be burdened with having to install the resources -//! alongside their binary. -//! -//! 2. There may not be a traditional filesystem available, as for example is the case on Android. - -use std::io::Error as IOError; - -pub mod embedded; -pub mod fs; - -pub trait ResourceLoader { - /// This is deliberately not a `Path`, because these are virtual paths - /// that do not necessarily correspond to real paths on a filesystem. - fn slurp(&self, path: &str) -> Result, IOError>; -} diff --git a/crates/pathfinder/resources/svg/Ghostscript_Tiger.svg b/crates/pathfinder/resources/svg/Ghostscript_Tiger.svg deleted file mode 100644 index 033611d911..0000000000 --- a/crates/pathfinder/resources/svg/Ghostscript_Tiger.svg +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/julius-caesar-with-bg.svg b/crates/pathfinder/resources/svg/julius-caesar-with-bg.svg deleted file mode 100644 index bdcd8f2a0f..0000000000 --- a/crates/pathfinder/resources/svg/julius-caesar-with-bg.svg +++ /dev/null @@ -1,10599 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/julius-caesar.svg b/crates/pathfinder/resources/svg/julius-caesar.svg deleted file mode 100644 index 89ece6ce66..0000000000 --- a/crates/pathfinder/resources/svg/julius-caesar.svg +++ /dev/null @@ -1,1478 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/magicleap-quickstart-p03.svg b/crates/pathfinder/resources/svg/magicleap-quickstart-p03.svg deleted file mode 100644 index dce06ca02e..0000000000 --- a/crates/pathfinder/resources/svg/magicleap-quickstart-p03.svg +++ /dev/null @@ -1,2685 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/material_design_icons.svg b/crates/pathfinder/resources/svg/material_design_icons.svg deleted file mode 100644 index e9d98180be..0000000000 --- a/crates/pathfinder/resources/svg/material_design_icons.svg +++ /dev/null @@ -1,2862 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/nba-notext.svg b/crates/pathfinder/resources/svg/nba-notext.svg deleted file mode 100644 index 898650f51f..0000000000 --- a/crates/pathfinder/resources/svg/nba-notext.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/paper.svg b/crates/pathfinder/resources/svg/paper.svg deleted file mode 100644 index 5544d478a2..0000000000 --- a/crates/pathfinder/resources/svg/paper.svg +++ /dev/null @@ -1,25021 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/pathfinder-magicleap-demo.svg b/crates/pathfinder/resources/svg/pathfinder-magicleap-demo.svg deleted file mode 100644 index d27cbf3516..0000000000 --- a/crates/pathfinder/resources/svg/pathfinder-magicleap-demo.svg +++ /dev/null @@ -1,4106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/pathfinder/resources/svg/pathfinder_logo.svg b/crates/pathfinder/resources/svg/pathfinder_logo.svg deleted file mode 100644 index ebae08bc0c..0000000000 --- a/crates/pathfinder/resources/svg/pathfinder_logo.svg +++ /dev/null @@ -1,115 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/crates/pathfinder/resources/swf/tiger-flat.swf b/crates/pathfinder/resources/swf/tiger-flat.swf deleted file mode 100644 index 70127188f531f9f5f5ad0fa219e95111bfbc1e83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25311 zcmV(rK<>XoS5pswWdHzpoTRq}Sliv#Ef}0&4Ta(o+=_cB9^9>^SaG)!+^tw~x8Po+ zxI4vNOVC1r;>C)?^!1c2g` z004jh0uUMi@W#d55~2x}1$#Q!JHjA;N20XWuCC4yPEHRG4-OAr4ks5YPHtggVNNa{ zP97fizX*1im!qqRC%Yqzj#do(F9oOt%*@5c+119$5&X~C#MH^nRg{+Y-(@`=oc~kS z5yoL+?qq7gVdmt(>1pE3$<4vV`H#+D5JcL=!o=0dMcc{AUQF^I6j;{Y1ZE9ZcX6_` zfWiLCo7jV;wFJQIU{g06dvh?K5ceC-|CRY)v@D>1-(oym+`{Z!{Or8k+FU{q9xe#4 zFdH`ygp2FHaQ_YYZ&VcvR}*s+SCjuj7Wn)6hs@6n;S>6=I}a0g3wFzYtbW7!KY;&VOZvMr6_x+{hC4WL{s$W7s%c^Qe~*S~ zdpTQhYFfaY++55o{=(`0{lKWGKolHdt|pFV779?YzX=W-8*>Q1l&pXZpMWrrkPx4Y zFgLfbtPoUEnx9XQTbP?qfJ^pY-v7_k|4@TEnYsPr+rO)s|G!ZCFVz1~Q**JgviW;t z{~J|dULjs#L0NubE+L+ORHb+Yc!XqmxS;&}+(Lpf(Em{VZzTS+Rtpy!cMEe_7bk~* zb-~%h1!nP&Q=+v0Vbwo2{mWm7&3|K)=YIsg3<6axdKk)NNRyI*1dy&#bQT4olG7GQ1w;F*xg_@9I)_$qeO z>g^YRCkoefy`@l2cZfEHk@NjNqdH)R9v5bBO6f^#l zd;SYKh}HO;+o}6ITle?lw8Hlpa5-4^47lBzdj?o!{1wO;=XnMkrWiZ}&RY`yF0?xQ zHZXMa86y{}KJI zHcGM$M2AhsyYB{14VdF z7IDr}f$8zoI`vgB4j(z^j-}&Osg$++(_sq4Qbk9lTqfw(9Gb zn2K%y6F&O?x^jqeqNw&h3#?K^5Fvgd0p>J$```KfcE6iR z@@r`?K0(Fy*?>t2E})7%Z28X#_IB=Hr<;o!P&=rrF5)Y`HqOdNFGYN!ml@gqwjLO4 zXY%WlBG>sBVrWy9&3JdJXk#avADk`ZDN-<>pCIq5JMfy2IyN6^=Sia8=S!(bVeL8Y z^klRc2{s4dAwp$P>Z=*Y9PyBfH@`y?f7iP_`ZmQ8;_B{w3s?xFf$-1YnQBoKLowR2 z9}5(0D%I-dc7u$&s5hJ?PpcVs+w6{CAd-rCQgt^NuVkg%28B|tGQw`;!o9x;LRJKX z0S@+z8NW}K3{%kACH)#=(i%;g$RhU4}Q`2OStPRtH7M+zpTc~L!T zYWitf1Hj2hUr$j3bfLkbm9&&J)pTwB4Ip;f&NO>DmmSPM`U%uyE@0s^;m($Bc8f=1 z(PgOfZGcf(l8b@&`yC$ywgU5e^W%NS?;~$k^des-bztBG9)@RH{CV>{GF58VWu_cs zQ$FIJ_%Qfk{3c;8$=lmQo@1Z19GP~OzQ;v&(nFS!ur7ga=QX^-C3CuXDh$(+aw<%| z-!=~nnI?+gm9mr|R_NHW4m-q*dyl?_5>=nEMV^vHZi-Tz1Px$~kF(IkU$u`Q(s^BS z7XOr8;8Q2rSWYX4kem=T6nrIk%CSZg*;p#o0&Bb*r^(v>%5h+AtvEPCKWKzX5P)!A>Rq zb5=@o60hGnot5zW#e4d!_0>*ZI=CnTek}CvArN`I44q+)#a9ucSXf$c@0)nl7{jKJ z^Frm0oNuPOZ>0Y5IJ#^}_84xQ#Xk1B-Va(YIvlf0_Hl>^_n=MrP?4m*yHRZ2+!rph zTsg&_!qij|%yc4>IWy2yYRzer7ydiND8GP#iVYU|EJ;n??gUGnEh`^&KCRKxtGpn& zi!VI8eTzqjg)f*x+%fR9@2RK5sk_OhjHK(BC1ecERUVIy5;7AM9h`{1Rba&S>sAk4 zN-awDn&9=cp_)Q9dO8Cg2P1~lJEXyC&LkAgH z!LKs4#>8`8G+uUO(;%CRmf|fHK{ZqLh^WqX2C;Kb@ds3;z4@bREe-3W#@-75c{fj( zq5#mdMA;GSOkclLexWx}m%ek0vr!bKAfi2eRr;gqyNbDyo$0)cYs=SYJ6!`=3nPsl z_DZS(?qWyYIw`i|s8B3(oF%oGUp=Cj!@4YwE)|DE=1+K(DB_~zI*$)i54Tf)>g{em zj5-XZPkHT|*@Oqi5A3QEzXNCW2s01aesn!~PEz0D)fkys-G2wZL}B?2u$d+}eQ+2V z3hTB+-!TwHG=;OgDblkeJ8%ZimwnAvt|xyi7Cb8U+kNZXXP!PT;+*>Do?c~LO>dh& zO9HZ*x0u8&Xp881_#)5s$SOU7Zl_D8#q=_C_W1(ak}>dD9y?tv=ySX6b!D*y&NhpE zbo(^%Gs$a(!TZS4*M44tD)&B>t8N@q?enY|nrUP%(&SmojOR`CRbi;v5~vDatuz{C zSri6=C^Ul~e*p2k@^lD$lPjEU-~IWNd+7ycu>@<;K(dASPMSZe2OS^VfmHk`Nq}$x zWFgE@5k=b6%Ll-=DS<4tN}!54sp1DwuI_K|9j*+r76y2{C3{~vq-R9nAm<>wn zCa_AHRmrM6O=z@B+*CyZkcqSPHaRuHT_0lM@~y1`6OTTt%$l|IIYczsf^%v2sk2us z^~XL&xVInJ4ZW8yVF43$vDeL_enH9LlN@y|I1iu{rop7BBwvr4#Z#`gV1aTFB%0t2)~BDMFgU9>y=2W$h8SV#t9n7_~- z@!5}lQoL$-;cLV4PF-VWgD8@@E%*1tb5NKU`&?Zt)s>5duoRM=x~4Xo0?BOsdAfwq z>#fbyT?}!^x1AofvA`THJ#9^yRf7MUgLr+l4R&v5uv#t~ zQ{^CYWg+&5mJRi;LmQI*u-5RK8I&qiPv_tlD|5KO>5AH4u{MvR>$kr;gA>}l*?EPR zIpL4{jU#S_zX}|}FPSn*c7NYk|9If#{;;K3#k*cZ3NI#&omOF3Rpul1<~IksvY2l@ zMB2~+<(0KVpoA7g<2*SW6O8LD`j=GTKsgbvC3X*rVk|I-$I5 zj0rj*iVAafc3%)-04-)q)FhW&bbK;4a7y}8>GOmV+0k;ruZHlwu)v!$%Gu?FRAl+x zWulDb73p&B`rJO{uGx>|l8VF6p}res_g$Bm`vKF8V>1E{dZv?#f(|ySM5z5A77~{+ ze!6za;Ta0E{33&@ljIq3ZZoWn;ANT=qIMzk**Zt@Mf7H+-V@3h6kY&8--8`$pVrir zx_oomb_4RH2%j$f1KOnL1JdT@o393!gmj&T3?>F!y9&F@$E$&hsRuq>1M(157|qe zJW*hTj+6^lynQwZXItS^c~qyt?f+*7ZZgWcpPi3TDM$`s`x$L{7z`TAK~mY z%RSFZ)9NeykilzbJ!)|rGLoI?RpI3^<5j<0*{3*OuuEA;Gf$dbUV2nq!#+~q>u}58 z`%PtX`kHM=P3y)mm{$Y%IY3q5ks)f1CJCNwp@tYhfiM$|k?b1?t3{~l!Okef+7b34 z+;zs>C-XWPDH-z#f08SejoJj*JB?l7JT4;uq5S@V6M`bEA0-kNtL*KRH9)QW$Rg)Z z8%I*XhzX~~4h4(8`Yk z0S+x)&M7o-*B`r|aZbubd5}XPi@FZ%GfQ1Q)5G^GnXaMoCfG*i2ij~%0dCPAlv<%0 zQ#Oebl>JjRkpNW12%+&-c9rH!-3&aWDA4;};|Lllsui3V!1w^w^3IE^Q57YKTbu)N zV6jXoRYj-Ig+-gq)6b{q2J@O?ma$EPF?%Z{2YBQ|1$9$z2lA1!$z*TEZyw9|i3Z)Z zS_rYMkf=iKq;^ecKeI-$Lvqhp2Lw642L3F1?z`Dn660k!mQXqJC#&K+rpB6IXUK~n z(0{-3bINH1d15YjV$srn)s-7}>3w?O@sQMt8Re8ED-!NX^b;6zRkm{pBBQPXWqB4!>mn^IV~fIWMpx;UAbH zD~7-Ve!Ob+w)2@N8x^XER^4^|?%Sk-ox(i)d0VmK<1@J-tE4x2U^?WzBE6x(wLp7M z;|gx|>00e(-Tt23YW?HEVJ`X#`xO6k8V1olv5x^9vTL526WPh%e^yOx-{g5XWp6du zIEGUFVVSFsqjCJS33h6~G4Kr@{eghI0SYqQB^Xxzv-p7;` zC05dtaHN6I#Hh9WF#%!a*A=Ly%SyHcuRJx zleS7xkY&QfSYqLLeF#3a-xW(94{i#;_Ck982kz)^virA%wGfpNuC}ANI zNw71%GSI5Tp&9JRI)4$<%Ko+FIgm>IqhYsj>3)_O(NvX;haGB1cJx~7F%iXeyKS2n$W zjy~rKJS|Uv3i^e*$q-68KF=@0U4zh(EgIA|?f=kc1yYe3Df8JRYav07vJ>Y~vRF*Q zVg#d&nG9XejRUUjimH{S(W5E)q2C!Z70Hjv&yX@7E?kvF)T%hSaS_FZ#f4A|CB~^h zz)nKjWgdPNq<{_rgUa&l21e}_)*WCHWyZf0wa>yNgwKcbe7;Qj1}qiB_M&C`Tu7u< zOR^X;9erp`pcIZdh{6LtAQ5N;z>2))HD1l$A)__=B4F}1w7?XSt6?0}@woK3B4cS) zN)?&0!|tL;CY+o2yoqX>U1ZnVj38iuZJLvVduW#%dOB@uzwdTLw9U%|(n;oh#xt^6 zKSpG$&X*Shv*>LVsCI@r$h_4DNF+;vzoYgFiYt7K&Pb--Z*KAC)Z>?PF3O-0z-|7Ol6;RsLHl=KgEP3YE4idgD0a2+7DJB8Mbq|q zj20YT*EibNi<_BeE;yK%WTd`fRnz@`o*1E@8t)Vj8S67u{!=UZC8wV2DK2?jNJY6+ z^zz~y>vX=vYNq^w-fzq8&DOUT`!6-;2`Gazff;L^`~YW(hCI>}i;ixs3^L;F0oeuw zC~z-r`}{S#5U~12)FbGyOwLf!Ym51aqg0H$TK$%ZW6))Iug7)#DT8tm*g(c@VhLF~@d)-up0c@ssIY`^JP@U|3*zNcjGOjz0=yYU`tBjVn11m|nk!p)8rK>+^;gbTn-$gQZ zg|vwpBb$ZvUldgyHqv|WIXc(n+Y#$If4de|UNOWI$)p!j_~iB%@c}8P^tiv6asrcr zYK&~)LpEpdwDzApz1Us<*jF}i(<)WszF!q9Zk$6fZl<`e)pO+8U+7xXk_Ji5Y}`)V zpdLKao=M8b?A^0|(yb|G{Xup%mgvdbl;aIlr10rbI*WCw&O;F z2QSz-Dqj_6`<#v7cR3^rdhheV**y4oAMUM$%!7f!l!oS6qu?t$t{~7p*7w3Y{1oyA zHTPxgWkP70DxJK5)tkl!dKwXpUUOJC8z@qA3~PYe=nXP%PSc#B6n2g1X6Sn9ooWn# zDYUSYQIdXEehCy9ZiiYXRciDx?m1(syU5z!qbQ7F_@lRKoZ3Rf+wK$@Z`nRGbUS`7 z+N*+n5o+h)`%^!Z5O32&?ZCqpEMH6>PsJ5&AsWO5Z?10)09!CMsXk%WYu){vt-$4T zk2mAGot&zzqMtNO?PT6!EY3M)MS+Tmw!Q;7!(!u8u=Q)4-wr=^Qm3H=NWA5k--0I_ z4&%ISkwxjSnCd(n^C+)2oljM|6gKNh+i$;aMxF*nj(|K$hUonj2v#0EeskQ%Lp@5` z>7f!Ahz`LXs{8ae9wpWEI6`yF_t)`6_wqv5dDpa5ASQbuin$H0=eHS0x0~C<`4_<%#wejRK`}xG4)c&Y}T1 zT)F^)PiycRX63^*r0`vOt--t+1k=fT8KEK$y?~(OViu5!TO=k&dnhWo5mMia5;`cI z3L2*4G76B5cn(#Bp&>r3@g7ysZt(9Tv6%_RVn}?5o!qdbTlqRTbM+TU)WMAe+;_tg zPFdCwjKVASg`+22Ibk4Sl*U_sUb@V%X+ba=0_>|x^83Tr5m1FP9?u8mTA7HCv@Q!(DmzO zs_CK_#9X{{BjO%?A7wcg_Kl!&b=qjZJc)n+OsIS`WO3SL7i&)ySOk>Hr%er_pfidr z4^k0I68H@sebrT(dHFSWyx|tLxCKkA4;RSg`XS1y6X%xIWRTqI62qHJQ)3&u8jpKD zY(8nYz^;ATBmaRh(**Xz^P>UaRJu-?tH5)kefm^7R;e;kaGa;G6F1{^BF2W*GTx=A z`Ir)0OhygfB^JZTB!=pR%5q%2Pxa5pW3QM+eaBAogYodDAi?!_{Pt^adqnRg*+0XM ze#N7IBt7wizeEWkX?Z6$_!-nDS#&Kf5c%C0&W+~-r10lKy!4C zQESOv^h5HT{?jjl#}V{YjllFY{b(+=x(ehQ0@RH<{7DuJW-L7|W)BgPh#=^G9Lr%A zRq3cr?2C}wr-7dYU$D}FqYwJo>)G(uGQF5us7~*==&)`{C`llj`&U zRaaL*Vdi)i(&c&}+11;eMFi&L3DfER(E+%11fF3ei*05xYA={mLpL2qBSWbFzAd7a7YMLw8M?rtQ ztDiUNfr4gSs;ucPC(aS~XDD-G^=gjZc@w`4BnCyYiNun*8mE`|{+w3etnZe*C~j?{ zr*G+*`#0J1s&Nf|GpC$V?~z8q(90RLwVy2;Z`aDec>&BV9aFO1jqj4ntL*c&ICrd zHUOJX+UihX9?U{Ot~PFc)Su?3831D@Jx7#Q_yD^kwu2=7BGg^RthGBdp&vu`^n^jUq#5a@X8*`u6DOp}kn zUO=Qk0-Dnzv>$j>Ww5IQ_b4x!zk`Oi4i=>h5~@AOSxzLGQ7@2C?U5zEBP6I^Mp<2E z4JpOp-N271=ZrB@=LPIQjyl4CtlioHM7yHMM_2C=_NX6ox;!tX`4MN;zm(50KOKL4z^p1;x}aIc zn2z0hO^`?*<4&&PnML6!lKo2+wZLNjAWOI&??@>eRsw7!F?ha|&6~)nqMR&Y8~`SL z(p;qrs&CoTE4aSs)QO*g5?(jnh1Rp)~Fy75uM z8g`--`(42ca-mmXRR%~p)v}HJn|V)KuyWeWeSZxHu!~3Ti+kM4_}XuvvgC^BUYE>o zOwxmmjA{C2k>J^kejz9D*EHV{heyP-@zUs<&g!@YIP+w-BE8+<%P%swjPR7CWE@&U z)K;ds(NNNP(`2rmAnwU@==z2yOLTh~zh&YS@^w7>(san=#S;%|=d7=r;1&RXeU2{} z?`pFM>|Xg9eXp17T116n3bG|DTm&2+1qr_ikxrNC{?v-BbrWxso?#-#p(7kWpT}LP zFl}kRIRL$l2?cU8(0Pis4z#LW4TOkJ!6AoEg&vq>r_~#U%1+Jf&D#m6q$%;Z_`6$c z1*3CRsK7hAy8s{Gxk!E%k2LoL`mEE?$*K2Pq@va@$_$NP!nnoR!s zR9?goAPs|HuZJ`_L;K|jDW(=@5(42hc}%Tw-)>ZdR0p+5LJ_0+Tm+5n55)XAL%ZKO zxA6#YvarC%MDMr=I-|(IDmAaf!Rk!5EtrlrGI52?L(jWp478s&Z~cgxrwk|NwEZa> z_k}}{L=5}J`IH+a1Q5nd-mk&f8G2?)J^>Ft8Maowb79%9<_rdr-RJK?0uTK0p2a4& z;lQ9O|1)LO(@O6?5yNhi)(lzOiN$Qj`OPGQ>CXm3xc>Hb*y=sXS4JfmlM>6C;K>|( zy3l;ePChoSnFg$~9@Tqd13`Y>`P5V}aa$!eLdC7;d%&sk37xx~yL*%+p+{@`7f6B$Y z;1Y|cUz}#P>2;^P)>FL5#^uKlP}4a#Xr4XL;rc{8U)`-SAfh69SbiyOF=~4Ov~TI# zbw+D8>;WA&i)A@=3<%iOmN$`B&D746qMZQ$#FV^}qGXR1YR0s08rBeeO_-Q;s%sPq z(I>ex%Iz;@T~=0~D2>Vb(9JH(HB(*g^+R5FlqX2Z--0`dkk?r&qUZ+PwQOi%Pp*`iI_8AE@cZmDER^5b7!dKfk8x%exkcL}$- zwH!2_rga!k=E*{(da=yttG<>uBiCG%e*azh_VrC~AbZ#T%9B#Yqs__pQroJBpWV1; zQ$x8{RE*J%CoeDjd9y#~zaPTAj_^<3U!w`AlCtPfdfl|B3e38D zq41~3XFTrufW2w8!ZzeHpYY~!tk1d5tma;J+Qb2aNAQQcA?b8!fZ%$(&P~89>f)X9 z@J;dR{7XN}zBuP<;Wd1u>uzf3056UcLOP zSo`Jb&1i;CKunogt12!2IX(>8qcTb4uqEUr!RN^5P&{n$HPRs|r(LGfwXcnz zSv{h}mO}KSHI8P`e97e%M9UNew^Xd=O2{lq2Elsqm46w?@P!KBoKS&O=sE`Ik_>=&P*{ zJcn+*(QR_8FKPe^gLIR3E>5T?+AKAJ%rBZ2sm7%~3Dh=ea9Gbcy%P3irAqb9P;;5d zax0uL3rHDbI?dr|7PX?gl}cUP)K8)OqrLt?tGQ;f6FT@PeLWlBML@CDqlBg=oX^L3 zB<~(FP17db0RGs>>*9S^y(o*adY)9sgzVlzecJso1lGtt7BaczmW2EA=0mS!V8+VN zZ$MpQfuDzGqIU z3K|pV|K`Hr@V#)E3f~wP+3DmMS=^RgIXo_7LF>fM7~@htsVkiC9K>Hz^F~8fktJek zb>Mw7w|YmeD-moJkUF~uGFZjh%UbgX89azk^9h=e1yToJDp{JA6?S0Ct@7`8M(U>X z_x%=`H%@FKIkuR_6vZ3?=Tr1|LTQ*KRcG^OHRqaTz9+>x0i?#UD& zG6G~#M@c#FxIR-!?%B1LBNfA?YAVtEL zHZD?f-eJf}1(a2^AJOvp8B(PmJjxas^MEVkSS(*p8*WPHJ}N31(II^JLPe{;k$*5X zth>;1`2bY&eVJpJ^@P3ugfq=TZ7pIPcBb^14Ad8T&BQ2Z43J7jrYbDI>B6eAWdHi7 zkFxvexbVi{4NYGhjxz};a$q-!SHMg*o}RQv#sK%++8X4dY!Tsjd1sYbubJe8k18~Z zKitIk7{IIZiwF^UhKG=v7_fZLL?hfa0OtpCQ1dfQe88~M5zVY1GCe+U&h3jhpt4v$ zbrEjX=%Lv?E!u6#g}WSOGA=n!>VptC$QI5f*o zku^21y{&3pJtjFISX4B1S^nH78$$|RRNjMoi>kFQNO;?RsM(I2^3oQHh5HKbKN2FO zn|i%tuT*y?V$gl6=N8Ekmz@yQBU;Gu6+G+2)gHxf+W>pco}?&s12>mx!Dlw?b!Bj; zxL1-^Q&45pm7JVj(E6;I>Yz#JQ2Sj26(8EUNUAJu#AX9Uj4AB0DSOz|mOg7{KnUT; zsR%xVeo%_tyE`8}Ywb{}H$3k32|9a;7pUz1IANu;rPV(2V$lN^0$Zt%7!4`fPh6@hYAw7R}~6Cuv8MG zmDPAU7|AZGS%oF|Co0;jRMI0F%O1K$@tkSDEn4^QF-`{@7UNoE!nHGsj>Y2I>`hkn`m|_CPT$$AmTUUZ)|w==bWo2$hr0;Fi7T`@X`bIuw{jE~8f>F^ zz3UP#g5sWWR~xD-3l62sWysz5db-u7t*pjM>NV)-sVmhJF?Ru%!(Y>%dvuc>{&QaC z>H%)kw&}H5l&C_A0C>V`xevnvUwf(=ETCe+cG0-gWTWSpI^z)CD-j!jE866 zGcIY(p(lK9dHQyq@kLs)2T$UM9(I+}q;G2CR5f2)r`iYP+E-1f!=m@DIw#9dm{!|p z)u2d7h^23w5F&$CyAP!;?v3S|FFJE|vOG+uY79Q0G*;Vsc&*+KARx_tt#(6px+%KY zXXge-t3CpvFoiP&^XVj4>12Z>^pOx%1PQceT&|p6=UgJ9M^;M^l+2(gb2(etrs}Q6 z*)L2uRt_lAjI-8_E&&3YaZjR3`xz*sQ~q6`DUh`x`%vt|j95T36Qh7R8yB#LNQTDk>^rzca< zHZm}ngT16MzJD=d;<<+^yGCR#X^l)X)Mb~cHq(iQPxy-}$v8CYopX=rM7sZ`naJL` zeBZ!hYus+!Insh^{kWjPlJwyJG^Q zLYYlZiY3FXwVLadHO~ge*^JeLUfKJ}bpp{A2fb0HboYmLJlE)}+p4W-AA)+nU&dYc z0q&n+J!?U&hvLFt&m+Hj%oA0a?<(^LYJ3v@<2p8Z=n!k}u)|>sGU_A&Hh)3Wsve~O zh&uMkAdC0r?s~0gRanqVYppKnH$O=ki!XYE4lQFkr&CiFBhR48#YQvsoxr$n(BF5HS=MLvcl@4UA}{#)FMlA5mh=&SkgY8 zPF)m(c4tP=H@p&jtkwTH$HOjTw^rGL>Zm_*eh ztEs(V;KmNfQl5YTQtf&mRPO$z$}>M#!lBNfV$|1OvX!H`6i*L^95Za?Q|vNp+@(Ng zuRQ#h{sUhEW+3!l%QLG5`}%t*4CRSkuu?ICT$ZJ^XgEYlMA?wxm)44cKj)&JC`m

qxcY4QPw%XhNy?MpOoTlxVJSX_mL%5nt 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 Cs4jm1 diff --git a/crates/pathfinder/resources/swf/tiger.swf b/crates/pathfinder/resources/swf/tiger.swf deleted file mode 100644 index 9bb17c65a7c9a2eebaa0a04b0f3cf8e68b302b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18971 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 GAu>ri%Vnql diff --git a/crates/pathfinder/resources/textures/area-lut.png b/crates/pathfinder/resources/textures/area-lut.png deleted file mode 100644 index 19b31cbfcd5881015cc9a2918b95c517f2d73269..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35027 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 diff --git a/crates/pathfinder/resources/textures/debug-corner-outline.png b/crates/pathfinder/resources/textures/debug-corner-outline.png deleted file mode 100644 index f3bce5267ff050561ee93564bdb7f65fb03af432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkEYGO%h zib8p2Nrr;Er*A-tUMf3K{G+FfV~EA+x&0e^k0=PZw)4icFc-0_^qu6Kz>WKRK()>Kd0kO zmGcgs=`dXMde?ugeTNwYoGiTl>U_A@JX6rgf_WcTTcSfYui>%HC)YQnMh19UUj7-I z(8Fka*z&vXAAu!PCKUhs_VxI;TK=@Qt>3(?PE_|tz1~`)bZ5)8E$g&n=gto5+Lo)# iaGfQ{>(=+bpV;lYy~H=DpK6-}3KdURKbLh*2~7aQ!ItCz diff --git a/crates/pathfinder/resources/textures/debug-font.png b/crates/pathfinder/resources/textures/debug-font.png deleted file mode 100644 index 82641e97f4c58eaa67697ad482f7e2fcfef969db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11814 zcma)hRa+HowC%fCG}1^&NOyO4cSxsnr*th4X{1v`Kw7#}LO@!&yOB<5_P6)>1LtNw z<8nUpVvdSZSCvCYeS-=BKv$5L)&v0VKL}vR|2y@BKhFWca;zXN@y=)Q*my-vTX%8o zrK@Epnu)D>y(cC)BY|7xO>#e$1+5B{qNt#^KMKoU(85xYE0=V_K+#Ta0wezuKAzH# zdfUSNn8MF^!at+ce+uu1NQmc3O57!RjIEvq&F5M5qQ`bFAGRh2Wy`qJTss#IT{bH7e~ev2(7 z%{k)5>5nEcFDEKyI+;&kvZ1cm$w*T0EwPYL5x%4;h+Gd$0br5zWjeNxmqEy_bue4!2UZF%Rk=)F>J zixN!ZU>aztt33a8Fm~}}-@-VU)qFJXTnW<<^VJr-@4|@nAA{Mwt2vkz`oicRJ!{c> zqrMgPhh-zfM%9d!uIk(G)RJUvjKR*e8~b_Wm8`FbPb!L#}cNL0cB2fsq)p6Io~_ybSqN2 zwFu1lt-4fI{f|I=oGed&c;vMUw`gCzAyw6numyDZyUuT0;&(z-3Wg2kQsJEF_0pyi z4OOBNZzE4O(99t-;of~T&&11pYIlTGjO%|8T4_&WkO&CNZ!+`CB4)D_D~va)+Thkb z9{ymSulF(jt?EeAc6ed4)cYibE``F`>{NOqA?QkGfsPS%2K-s8MWeUKH+ID z?cD84zHMVh9!*V9Eu|&l`ohjmwTS2e6VosbFb;*D9(LT)uhzA5Bp#FSH&PMPEbsxeXlG>`tz zlm3Nk!q#10&@LR`TM}Xax=n+3%HBBR&GYI4$ekqk@;Ut4P}8Eg`KpMwH5bX-OM@{< zzbc?$P$^|-3eK(ECZH$)7dcMWPx}2}hxn1IqPFyAjpVB&V~nhe)V8#1jw+X?^5G$O z_JIHokWGwXtO*h%`X5#wQu@<7L(dzW2Y&_#DzS4YE}W=@etV-3fiCKS`38YF*s_EB za$&u?4j+v^oAx+Z49l{1@lg(rqq28t%PzCn&SGytln@Ol+@#37ms&Be`?{}WSq+1m zr#;TmIx<;rgSe&z-S~A5*UuWdqgO#epV!;L=O}{WL^?W_xbmnwyl%b;3--u52KOg7 zS!-`M1IHT}k?Irn4Nse+k0Ngn=bKVifc5&w3N*j=LwWKN4)s!8!?KCCeob{z8(Jx} zSbhHO=tO3ULn1ql=x2oEYRY1PexqG8ZgAI0w|?&j{y1kakG(TZwywtFi{tN_Gkz=)>J^m3R;MS!YH8sR{sK7Cy zZf?7iFlW9xc0M|@hi>;~NTb-c9abL=^>vE|AZ zA|zlRv-K76-p?8V7|X93nISNKSy8FlSRFlfSORG|aWeakxd`aR6WpE&;tsI@` zs134CG2z64Y@o(M_p_(Rur&KzkF!g^=Fe>Dl@NrT(Fzu1v1N+U9#d`tBd-sF>S+}? z%WRRJV$ceMLyB7tP?z_&BgZ}CVEEk2%0Fo(`+4M!pAv2XsX!^E8%1<<>JGfk<7Y`NG`93(1XX(i}QaYu2=;}zpL&5MfQbCxiXSS%d zF(E?KS5!i(Xk@Tb;QbmqU)#zfTil_Y*JOO>0BH?7#JApWKl+coyLxz zN#ky;|1UUzTayVQeylB{(KvYo;bW3F97YB_@(+9XkaM(!VMG*DWQ{oDcTGmA`-pr1 z-W&>hqDEIlf#!z5rDarvA@&tR@G7Mk7(wf&Sp&7(Eizu`AIsK!aKVArz0|fM4WYNO z+Pnf>(72^p0L~M4v}(tQOY%TBM7D`9soRI~(N zh?Sty2$wLERr1*mlOoC09J*eK2(I4Iy#6Y_IiLLBHMlV}fO>+vbk{CMHazXzk+^y0 z)BOijWc!GND?>Z7;YDoui)p6suz7O^d73x1eNPxwQs} zI~mmloliRERM3sMJ>nq3(3c=~Q5NNaY4n1JYzlp8RMzZ=y*i9#mmhl0$Pz772lm>$T`Ls5$8MyZ^DrScCPN6hxt zG#PXk2`kE3;c=`72(SADrcr1K*r~b7QR;f*u5u2Ed&C|gp*z6AZa?Hlnrx1)=Og0& z&LIU#C*bTFjaFWo-8AyKAzSvt9Ocl5=tzE|ZRE@<&dkN_YAL+Rvwg%n=e|nHLEZ z?7vMwnk^U4JYwV^|D!DBCRs0&cv0$*AB?1KKVq{fO@*>qkhmOV%?kT zKp2j1AV55fk=sB?EUAglrA3PjqkQk9LsCDf>Gih-nOKFrP|URAPD>naY6K2m{CEDX z#QxOg+qiEuP)kla%Ste6V^S10?wEp1JOaXn*tV?d|6*}(|`de0GTP{|p!Iw|1mKgm;uRfwLsjVEyZ#cs%j3_`sZ4DSP? zIrDDhYyu<4&qAy5mPCxErGe$wZ7+CQ&0b!Fj|!!U9J@E38KH6|?P%`&1EUY(W9(`P z8zCZ$U!}d(LCKG1X~Z)#Kwc4f+r|~PJ&gS))PIO5li-|@;;>&r*v|Ex8{)$B81+df1e>lBOn0xHjjE9MREk9 zOEg>2KHoQ8yDPgk9L2AhQgs++#6QX7cU8;G1O6EpPD>?^}xedaZs$!QfM znxl~JU9~GvexI_VV>BlhSr1&Wa?WF>6USfD=2 zfprR-5?Jga^~ran0il8zV98fke!9Lyghg)k?8yRzfAC>Bl6{}|5&SAAC7-!!9vtYo zRLs{c;Z^G1S5>k&^iby9D6Fko7nbFb;IlF z;rb6MBk47BrIu94oXy_8`JO+u{*6NLaMkK$c(69uc-2X4_ScqaSI-R8Xj>2y#~}jo zC`)CTLh@=*-^`qVMR4UN{g;j@r^6mjge`Ky_#niku9v2hHxFqiwyt_%nEz1?7vk7c zRJ!2*NO$q#Wx)d8T>`pDr2nM_`Cd#8|1{;WUrbu)qFa4k$*w+srHG1fMR6O#8NX*N z!8tl;)JJ$aWcQsPQPBUTF8X?c@E+ItWVb~@5i6z!Zd#m$ifdm{cq(_e^vUiKqR$Z^ zG6Cx^))K=BhL2CDdaqn3QgEF>u@zQalm_qBb@={MdeOINEUo#|^0TedlD>avk%%?r ziDW)8XDKi%gBuLd`ImRS)o>o)dEU!PrYbq+#hDWELEa|)Jbp7gDFXVaX*|H+r-`_U;&Dy<#iOnJcKPE9R)!^RV7`KY+wJ`NSU2+#4Dtk?_ zdgatV7&KJB8XH`E$k!)YVIRJIx2R?yFQDJbzlcqTc*a66hgcFkis%yMO*I4;;q7)= ze;0N_^0ACcT6tWde0R7n1=kZ(mIdDqOD!22nD@eqzJ4QtP!~##40_^6IIow!VB@$% zQQS&$ypvxlI=9*6LUI3og=##T+n~*?pqdyQ-@x_rZJ_;Wr`~agFu~@i$&p5L`+)fq z+Xi=ja?1ORe>ZtUXiC#oL+v?6n=dR=~Uo^A|cWFD$Gy+G7XrBJ=#EX`eYEr&(e?A{PzztS! z{kM_%h6lAu$oivej}DrOZe>Ov19z0$ZLDDZ!WS zA4wENik;X`jcc8ajlaR7^(n|E{SNo&Rgba=%oOQ~_qb$jNV{Ws3MoV>l~#z|sNptL z?_E8M7bHn{sMzp+V&}H!rUu6A+DT_&6%oP6wB+lV<> zIeshx^d`GsmO9Ty&0Ji~MQo&@J|HFUcXz&S%!kB+X{4{U~PUDZP6= zYIjE>@a><-qno??_Kkqea~<1@=QlpgPGh^C8oV!cw(S}=$8^$a2y4dhhLtbC1!`D?leM~RDRvvZ-88?2E|2pebG$qC~r_zG=vz);ipDwy87z za@T}KWTr?wn`30B;&qTFb0D}eSxD%4%he2vIH;d)y-GF@K=U(%5m~p^3nG&*E^BTW z=;`#0s7hth&WMO_S`%}N)87cX+8 z+CMj%@gS9EvfNa&fP-c4qb(DX6sk@WA)0k8yg?#b&Mp-p5?!BV$w$_1{=gvnb&5@D zSS96dGl}NpS2VXnfi#(t)T?Ia!JWX#mueLV7^30ObCvmMPcoiFqkseDTNmdppJGxb zD^2~D@Js@Maqss>%Ana`sjoID7DTdf=V$JS zY4)roG1WBri@qe-JQi<~nL?|^!5ma$U=^eHc?}*>v&9G1Q->DI@EmK#w$gY5*joF_ zf3A1^l?y{pe+~(Rb4`7skF=Gw3j5F5SBrEV=Im!xYdQV3?{C1qhDy^bQtH@YI3cTD zBV`;xw9uZHBq&(-N|*ii0pA^=vv!_r%|^pHBy^`osKr8W&)_&K{xIy7Klv;1Tcs=+ zUP@BPvZE*o-LK7HV+eW9vY3TkUbA|czB^$MPU4Xc0Tk>p<4xhdWJkKm`Z9gX7r{<6 zsOmt(l?kX(!1a-sS`etr`uG}Thcokv9IY2+tobG4MJR-d#~_J5M%gxE0CuXOj@Ia7 z+}HfKNz$uldhRXnS8K)vWLQ7K3nx(&%=f@4!>o%*B^bsBVt816nwh|OGKCW62@TjK zqE;$pP5zoi4`3mmV#C0hL5i#_4Eh-XZg!m6bI*ai%rknWMFIg9OKEwhj~*>Q-dXTV$6m2UY($3neDkeIQ^<=U0^qNf`aFTN zfL4Qu(t|uWfVTys_lakg_8%_s%7n*f$_C&HWBi|mHojQIx{ zdBs7=n^?Go-9_X|l#7H%tdakW!7ol6p?uZl+#R?RO8q>gA8G58lgm?(ESxlS91M~*DsMr=Z0S3lA?K!21886jb$-us76XuMMpH*j09udDv7 zO8W}z#89_hrJBNniwxShMjbh67ml~w$n%4v)syT8UUq?~)f&yrU$uh z5v{z6dNG{%TIwMlV8Dr<+(U4K@;X03K|wRQuu_c@g57XdfCvD#)i@`CJhV$=u}ILs zDl7kQty)^bQLP4bz(Fg&fD1gD&ETc8nFm5wxYg?r8TpB)OiA*iPtHrk0xF}YJ}Hd4 zW{^8gm2nI${Ui4Cg$3 z+tzFYpb`4H)W3#MlMnhwaJtKH==IYxK|ls?63SEby=~!mMZ)5gG@lsg;w}y0nhq6& zW~kRN+)QK4bGpq6myZlP#va3nQ`T~iSg7D9pySh6o!k1NnhCn6oqtdrD%F{dnGsu#mdssamivAv`EY;SGdJ6ud*LabW@DQy-_l!@<4V zPQX4@tiyH~53Di+>_`#0uhgX+TNoP$DJoqFBP?cHKyJnbz5r561SXq996wl#=v$%<4LP;vGA3H$w73v$SyWH15PET#z?UtQ#)fF&;X&Ln6 zQ|D}AXZ6ADZU;s|yB^;*&{YC*e3olwc>HWVwtcNHR!FJ8;b2n^uxz@Wa3Qc9fRQ57 z3I(K`Fra?Qul<$Nzcg#cl@4JkR^6H7$fxWvOzx8>RJCCgAunGg>JX734pk_j*?ssu znr0=%y5$X))|64LMcv`|Y#g<7J3;Bup*t=Pz>Y!duNM^9`g(Rn&t2D)=SEdQ9y8Jp zIdHYsH^%s}%Gi(5siho8agvk(TsiA_JC)~jZ`4#B&krhQmH5{sq#xH<0lmTWSLi^b z>dSoy%E)X;$?j?wGLKwIG1>rTBFP|JF+CjduVW89O=M8K@M2+QOLjm{OLux##T?P*88XVJ z{F^q7kKIVudv_!=>;7KTiY?6P6|sUjN(f46h*LQW0>C7gKu3!Hb1P+}vFdmI$Zs=U z16=1OF;do7_?@Cg#P8h9oTnHpDI5h$WLyc7%croF_?W6LjEp$wlP2vqeFUS6eMkgR zo5hYWU{spu%CAkrV^ARCq@EE(W&?KbF>qO%ins;_ZU5#-?0Nry3Y7jH2WFaDt^KgR znqRHvw_X1crhNo4nrT26$==15KK5P)h5jV|I@h^C$7{}(W<+E45PF2s^FtirzHc8_ z-3+wnIDmnt61ka@=_?KhOt#q)KS?H?j*f0)psjRJj&w|+`7r;YZ4tsvd=_?&1_8T! zU*C1EeUqNbMK2ncGkuiUT4wdHrQjU?Pj4veHv03eH@;zvQUcr#F^F!0Ufk*C*QE(x zSkFc91?Xu=Z&B4arrQHCNxjM-xx??X1#ARVFBITG%CN*gP0B|T+NZ>uYbcm47c3A% z0>)`l2Gn;4r^y|fEv$#R!%?$l)nD2F%Mv4h>{)#HsGgrwkHj7;fBo9SA8RQpL+*1f z|G{>POxiwsQOeg{s$#->?)umQ+t*;6DNZ-}v*-&mFzZ<}(Gq^T6c86*(+b81aAV** z_|IcG%>FSU=xEosH?0KCW~JV&r7I<-#;wlu0LYZ-aA+v!26H_gW8;ga7Kmb}g#1vr zjpXc8v5ZJAHY@`rP0|^Ww%=DL<7NKKUMR2)$G7g02Y|>SR6OJ$BjOtrhT+^zH(Spx zgh5Arv%Htuw_2nG)OKFAK!8pKRpxvl@$IANV?f>XT~?yQmY4_xGtYz8dJ@ z-Pk^#5k$Satz7zxhz?6!6da(0)LH2zY+>W>6@$7mBDDZKTztl3prl2t&#X&P|sbhiQG_F2cfK|;f zmdabkvg21T4N163c_vk-BFwnD+dQBd9=#?Z0IngwF~d#qg7iY)ruvYG2amNNV>?ke znh<*;7DAv{IYb2DgPwD-R4+t4^9_@1qeJVd8ofGW-L?pQ2@95%Z^B%XpK<)jS)X}_ za|GHQ$qM!C(XuDBiwxOaaiKY2;r$g8-z*fYWwX-Q2rUKd$DqLGXPx-Kk@#+dvW+}W zV%1Pw2FRtDURGF+7glWL{ZCu!&NY#{w#`4k5M*_xBf;#Q+>jb|j4ey$Sm7*C7+G1F z5qVlX>ohOj=n!F?ZtZ#MMlSv=&F49 zqV+aWSpq6vMkd9^eNQ=6g9`!n2RchzXQBavz1ZzY#Z{9h`%ZC_*b_fo%08>G+fkxv zVWV1RxcIL&d^tlcddWH*|5(b8qAj*=k#g}fnEl!H$gn{CxKqHoX?mblvZ{3(KKozd2|i&7Yt7F__j&y z^-Wvddhygv>s@OfnAACjc>1LJl={7us+#}2_@Z$PcTHc@>PzW?{(%W=HYHZq8ud+cMRZgGSy>LY^HH@- zH0sfv=GN`79LtlQ+qU?+sfEF=$rKJKiQW{(W;8@hiOfRuy}f}HlwL65M16)yG2wa8 zdB6o1i+D)N3++qqqq05(hmiebR)JBjlJSiVI?`3Rqfa~6uZ%Zt z`VGzv|LPf(mW}rTK&!HH>}qdxkh2J@FuZmCUfsjKm^VYg`vxK~K!-l3XY6Ai$}?`U z&a$3QGmp6c%O${PIuacukuq9mV7}f-Kbjs%JvBm)KYkRSawhYHyI<3vi6TP+>jijC zq#uw)wSUDSQ~ab&4oU*!tkOrGCXxGV14ozGnM{5c%PpMu*k0?5%w{>n3dH1}LoLdG zl$6CC%!-&t<7Ze)%e>`3^Q5N*wm!!XMa>FK#~fUrKD(X{Nt6yc?GR8g?)4{6xW>$g zNCuXWVz7B9!xuJG%gjylfB;oi%54pvfD)h@dmN|Rn*d~N4-j))(}}umx@CV*)j-tk z&fAptsn&_}j9kK0(1)ZRe!zDXhdTAOU}5_OpW9D!zWC-9GU(SNUJY}+vBDQ{uFXjJ zUG;BZ#WvD8TR1S>*szX{F<biU4RgSQ;JMPYr4)bma<9n?68 zoI&rN{unPEcOUlVC(FTSQzVdNU9n$TjG?R6ojZshJW{8-!5_!e^E~yPr4`kI@wkN_ z%sZ3;Ks>?n6B;m}hkGjWNT_@yDQ!xX*j1;T-A^@2LCM{d6CuyoL6s*yB=)qZmFh@C zhD%pcG&Rh0ncq`*-V*|~s{!oC9=D}A7PSm#H||3`x=R_ph3RYziLL2_a=&e{2OXm99#B{|Wsj zRxI>_a;*-YcSz!X(g>j054rM(FFeK1qw4g|j@?g@%5ZAQnoE0#s1%7!9Asi{y>dTp zNk>j6Li*IS1)0weHgU+9GH`xXP(!-zRB9O5Sk08Mc$0x=bo}Bk^q+`bXEN{esFRKU z1lpX`w2m4)w^u*pOvm|cQmY;Xm>Cr;kGq6f^mlgB*`k{<{GqEx>@kc&fK>>B3+2{z zsqAwhHbKR0N$Ha=#nAv<)@*GI#t$ZaGG&!PZWu$E@7++6eTGc-1IkVLKTNZpYRet) z$ga{GL`L6z@ePNQ%P~a{#lw>}y zAjzl92;?#4$W5b2#vBP`sVttY?nN))2q32?h91vM8OP$Rf@@p=W_76(gMQKkz-6ES zP|yFOcNF#LrO+Z}z5;Tak`|a;`;p8t$c+kd7xWz2r;&Zx5CKMpUx_O4^;(XP>apmm zLQf#L8@#}1qkey`8Ne%^zf`v(>k|lI4mHtT`ZEX1HK&i!fc+(_tXF5fc#?{fkLApU zf7(k7m3}~YQn0?4jX|Fe*beC6>K`b$#xOB^*DRY**?bNQ^Q2zIAFuMgaqe^Taxf4V zne)jqqWlyW+o=8IjGB&Bz~CG9`wtx8{$L@c7rAbyW?T>BVUv!9fZx-t1D{yg-|LE{ zDHs-ikHgfV|MOSV>roo0BjUHZCB}RH73s4SaymU`c+UZU631b9L{YwaqF`(9 z#ZFC24PG>-Nveiv@PPU^a|TJLBxyIOVp5*s?TmZ%aPN+>8wBv}lNc5X<#f0r38pkS zdi<UtPEHB2IcQLD7tXuiV7r9K3kcJJwTmC7DlNyv3uL&*T#vGH{1P{{H`Ohn?n&hZ5z&?Tr(jBa8zgJ3YIiLRw+qyON z#W1^rVj&t+dQ!Zw>kFfJPQewksB*0K{g6Ze$`p(qC@K~7Y4P_4Y{Nb&3{})9{uO!^ z$E2{G=X;d`h z&Gd4{Sv$^q|7XRSPehf7NlL||{%c-is8XUKrz6K^y-9qK-$+1z`>98=e8&WP*x1lf z2$!9z_2NecaZG#7z!+WTWtFGSKAR@&2d^+L*d>u9zkad$!5T$}=0R0FA60p?#Iz|X zo%SvCw5 zj&Jw+Z870YA3`|Re>2n(oRamYNmkDsD;nu`22u^fj5Ngrt-leIJVCaXfPDIC!qsCL z+1{r}G4*ddV~6LEE}g~VE7iH3D$6bH@-l^>d#*P$+;pm`fi*Ec=_B3QzaKg8>aH4G z(~&(`^A;R7K3lHfbZW1r!-I9j{?Czkxj#tj=WM$d4J?fREB+%|6JP(pk3qhlahVj- z(Mo?{2ClwgNAGb|eiHwaq$?;s6%Jmymh4qg6%x0WX3d51u%uWt&uXB#4P!;P&}-`} zjvjvcBs4?hw-97qY;ftdvE=edU~2pt36MVa=0DGaffz=8Xve1Iry^3al zD0whkHz+mf?>`r1c<|lQudHy4{HXFMaSlx^zQj(-^G>S-KlKWacf!y>)wnD$Ut3%Ew z9!Qr}vU|~fMHhpTvAsI|aGIlL+$%}(Z^Py{?RoRYd2S2xtGv8VSaIp{$El_W;0^;+ ztEXU@-bxm`bhVVxNlNLGY}g=pld-X}sc{Y;wQP(R&Kol{V|F@gpVD0qbwYs1EgN%R zcl{~IIAuGVN_D!&{SXEduB}~aC0l1+7N$nd`7mvrNkrf8yMROw7XTH`5XXluMQw$luT8G?NFZWhc z9{th2uATjx|5)=+%kYBBBli1g8x*GO{fWPugC3~=;rj4mzRhYl3+kXNlQ3>(l39O( zbb^#xgF}_U&Zb+|+UohJ_^{2NW+M0G=43>)=T^_*9?b#g?eJYmhyWRpvpBNTN`{(c zx^7D&wodk`9XioBZDTvDGM*{%ZE7vb4c#WEbbJ#m>SF`re)AKB}e!9 zSM&?hU%Up{eF$*`K6(6q{a5U73rJTpA&u;M;1<@qZZ#|Rtp5H=UyH|YCN~qpn)YzZ za?1A`REH3l`Krq3;x_uhX~j`oH3%y}3P8)K;o)D~ajDv@T|ft^v$e&YPd}}?7wxb^ zlG-gtzeQ(gC+l#Fq`e{BhD>j-$xfKJ@`ZP9CC$m$2t102%;zSSg?xjVCmg_w_XjjD z(VH3vrSv=>r>G5(HwjA!}q>D z^s!*6Jj2H$BDTeo&U#o=r*{<}#!CZD;Lky{j{kL?oBvnR{htB-|AIT-rq7rm6Wb|h TV%z^Ie*hF@RHZ8=%|ibN^EU@C diff --git a/crates/pathfinder/resources/textures/demo-background.png b/crates/pathfinder/resources/textures/demo-background.png deleted file mode 100644 index c1d067373e75454da19d85fbff55aa5438cb2d30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@1_q{TPZ!6KjC*fq+GdCZinN|L4J=UXU2wEZ zu%0Vdmql=+Aa7Gd_pxJ69>M}8Yjf_Jh-E2im~n2~HO(w)MaPV=W=77eYsd7=0?(uy zZl18_^XF%0d(J1>%}Ba`XK(Sj*?Y_H&7LX+1Wrkzs!ZR!tyEvGSi(H1%v-8^{YIsg z`k8^3tUb@G?ls)Jb-}yFzy~%TWp=L(@ zftkCGzGrwQoc2NLLSMh1O`H22se;9RAGtXXS#fThD^$p^eU;dKuNglX-?scH>)gQ@ z9=j#C=u1KLrG;MUs`K><)@)%+aJsa#XTr&rjoqq`u4Pwqm7I<5^Sx6Y*+( zVG4De604o#^Cr%?Y3%vkJMQ_$nO8q0l&t-f>8hVvyO8hWQIP}RguZTI+2T|!F{9#j z<|65+{|AnQJ($vc=i*P4+|R_pI(?X*{6r$c}G>vy-knPGWJ%cePfv3Xt_xI z;Qh|Bu+uL8kNnY}y|@hsxPCG}I{*HF`~*QuV0u(7ag8WRNi0dVN-jzTQVd20hL*Yp zCc1_OAw~vPrbbpq#@Yr3Rt5&KckAY$Xvob^$xN%ntwE>Zm;z9P2FQluvUIDY#Nt%l y(xOa;w9K4TD}8UApnbz4rJq(_%elF{r5}E)xwve%jpOxToV!-=Zn^qnM|o~i>S zNj~U09!av)b+`5QOx2Tzz6u;)NXK~K*1=fQ52ILrR9V79>2FBB9?N`D(=N%EOt0UL zj-KT``Wv<*K+Y&?+93d#DOn}(fLX9ncrJ|l9$Kyxo}8K;U+2pClbjjg_LFbtB2UhY z!n=N4EG_Fou1}IF{rrj#`ckNV$d~r#CHLp;{zW7GcluIv-VR@ipO5cLao+c(@Vwna z|8QTPFXi$2zAvrgydT>{9#tZbY_7l0gA9@X&XDFdh=m`O^6g|Bh;ksUxGrX+877Y2q^y~;*FUbd%;V~EG`w^t4M8Web16Zx97t9;fs zcb+=PFdh=m`O^6S-|9Kz6?-^DaqU2g@IvJud)#X1LHzZ7sn8f<8P-#Yr6!BG+Zz2ON^20 zQ5VwuBep%}-W2uelj0pbJ2N`Yv`+eDbZTkaeuv&YC*&Wr7V>IN{(buS!NLpojFas@ zCfL8-{A=Il+u3XPy42la`fD344&<#1lSnyR*2}t{?;6vA9ew)MEuz;q zEQ))$#^OWBIzcCgO*j5-NZaEzU8Qltspt0@=gzJB5_)%UAka` z&;MWTIf8{u@ySbPGg$kz&D>ab^||k&J@<+mx_O$9#j&nSmQ_;9Ef!~xx%wyJO=riB z+k3lwavw`q|J1YLwTQG@TV(fD^Nvs0-vyR`-%f1lIC0Izd*7e+_Pg#eEKYOgE?=21 Q35+@hPgg&ebxsLQ05nJAmH+?% diff --git a/crates/pathfinder/resources/textures/demo-screenshot.png b/crates/pathfinder/resources/textures/demo-screenshot.png deleted file mode 100644 index 61fe99516421255cfeba499cc9969bec14e34fe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmV;P0b%}$P)Cf$eyi8W4=D^T}2Lcw4pyv?`>)vOCgS84v*Ii^KVRnCk$c7|6zedHgVu z30RpBSOfa8a*+=BLcjqQjhz9m5V!`O8oLKaN zn7W(%r&s-|N1?82MyL`&ysp7x&QzG07*qo IM6N<$f~|qAv;Y7A diff --git a/crates/pathfinder/resources/textures/demo-zoom-actual-size.png b/crates/pathfinder/resources/textures/demo-zoom-actual-size.png deleted file mode 100644 index a7013e6e316bdb1181872255c1f7bbb6b604b326..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 729 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIA^$sR$z3=CCj3=9n|3=F@3LJcn%7)pVryh>nTu$sZZAYL$MSD+10f-TA0 z-33Sk!B6Mi^+1ZVz$3C4NPB>>+sSM@pz*UjT^vI)?!BG4*NfRv#O-{qnG%bVU85_D zuFjD|_l{jVbj)po@cn~!{0dx+5!v|*Be-v6OjHtkB%Grl71U8Dre-iv_4i~`;Y7cw zDe39^jjPjVo^etT;PBy{bZU~Hs_QJfMRnUY&Axf(!vW@iueK97ZJ4Fgt}$v@vSu#SYehBoYr}w#^qMmDKlwF^c=EiZxJIgQXLGYzAANf$;_ZfS< zqI?R!hpX=O4@wB}%<=p+;mYJK^A@&zS63}iT{%VIS->U7TOZC<_U;$+2o5~ z0kWaEEZr(8u{c$?v?!AyEi)(8N?%{UB)336H!(dkIa@c%$T8Bh)H{sr_4h8I9tKZW KKbLh*2~7Yg-wOr+ diff --git a/crates/pathfinder/resources/textures/demo-zoom-in.png b/crates/pathfinder/resources/textures/demo-zoom-in.png deleted file mode 100644 index 1518f8a5aa744548ca9b6555d61aad9863cb247d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 434 zcmV;j0ZsmiP)`p>hwGv6XN<(5B)>~?FTfq#|#X~99CuNUowNz94jal!4s{~#k%aehDXhaKPZ z{BD>Kt{M{n+xmTNS%8u5e~tlDIxdt95I0o70Je0xQntWc^FIRY_XElEB?h#941iG5 zfP_F#0V1H21-igY0lrSE9{?X%D!>LPipa5RB2ol43a~y0+yU#nTUsf=Jny0V+O_tz z;GAh(8=$VWdI8vSYBZL>R1aHr_{s+OZtCMYPLFqc2{3d8$Rq*k);zDa574gaid@BH zBf0|uSB+8nCorsXfsjUY^iP}nL2(lD(3{!Kb~WqQMMQ!th`+gl_Zx97@h16*Tz-)t ckemKhKU_z43gW6mzyJUM07*qoM6N<$f=4*5$N&HU diff --git a/crates/pathfinder/resources/textures/demo-zoom-out.png b/crates/pathfinder/resources/textures/demo-zoom-out.png deleted file mode 100644 index e97ee503b452b05c764f34dfd255b8fbd924e290..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 429 zcmV;e0aE^nP)M;SZt>0tI0*rM3a}1c$u~0HV+)x1n*wSgGY=ODve*|bHYcDaN&0_$B>0$`< z6d(dR>0%d{DZp1&cOO_Pzy>Ia$Z={SQUo>%u+9SRfOXziS}DLh???BwYwc^n$rxN4 zpstO20oZb4HI~3s4_kWq$_Dsu>gzgAu6KJ0FmwgT>;u%Td0y=hpk37!xr#|gbO!{k z9;4(>U|8h>A&uzhpEeJR;w0puH?y1VYSyodhy+&<=edIS8*wf1Ci#e5evu%MoBmZl XkT7N%KQq+400000NkvXXu0mjfX|}4= diff --git a/crates/pathfinder/resources/textures/example-nanovg.png b/crates/pathfinder/resources/textures/example-nanovg.png deleted file mode 100644 index 84b2acc1c046a5567ac0891c570450540afa895d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398461 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

Eoijj-cNzQ6nGu|o5Qh{@pu31x-InF6u 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≥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%0AQG2*(c4piwpZ>}`(B$9Nbq8?(LeGt;(KF}V)dMT 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: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