From 3352f869f0a921e1c3e942a8dae2cf5cd39a3e43 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 6 Oct 2017 20:03:50 -0700 Subject: [PATCH] Remove Pathfinder 1 now that Pathfinder 2 supports all its features and more --- pathfinder-classic/Cargo.toml | 30 - pathfinder-classic/LICENSE-APACHE | 201 ---- pathfinder-classic/LICENSE-MIT | 25 - pathfinder-classic/examples/benchmark.rs | 162 --- pathfinder-classic/examples/dump-outlines.rs | 73 -- pathfinder-classic/examples/generate-atlas.rs | 182 ---- pathfinder-classic/examples/lorem-ipsum.rs | 983 ------------------ .../examples/lorem-ipsum/composite.fs.glsl | 16 - .../examples/lorem-ipsum/composite.vs.glsl | 15 - .../examples/lorem-ipsum/default.txt | 34 - .../examples/lorem-ipsum/solid_color.fs.glsl | 10 - .../examples/lorem-ipsum/solid_color.vs.glsl | 8 - .../resources/shaders/accum.cs.glsl | 43 - .../resources/shaders/accum_common.cl | 26 - .../resources/shaders/accum_common.cs.glsl | 14 - .../resources/shaders/accum_gray.cl | 37 - .../resources/shaders/accum_subpixel.cl | 40 - .../resources/shaders/draw.fs.glsl | 117 --- .../resources/shaders/draw.gs.glsl | 135 --- .../resources/shaders/draw.tcs.glsl | 118 --- .../resources/shaders/draw.tes.glsl | 108 -- .../resources/shaders/draw.vs.glsl | 66 -- .../resources/shaders/preamble.cs.glsl | 17 - .../resources/tests/nimbus-sans/COPYING | 340 ------ .../tests/nimbus-sans/NimbusSanL-Regu.ttf | Bin 106572 -> 0 bytes .../resources/tests/nimbus-sans/README | 51 - pathfinder-classic/src/atlas.rs | 358 ------- pathfinder-classic/src/charmap.rs | 265 ----- pathfinder-classic/src/containers/dfont.rs | 100 -- pathfinder-classic/src/containers/mod.rs | 17 - pathfinder-classic/src/containers/otf.rs | 173 --- pathfinder-classic/src/containers/ttc.rs | 52 - pathfinder-classic/src/containers/woff.rs | 114 -- pathfinder-classic/src/coverage.rs | 133 --- pathfinder-classic/src/error.rs | 179 ---- pathfinder-classic/src/font.rs | 259 ----- pathfinder-classic/src/hinting/insns.rs | 521 ---------- pathfinder-classic/src/hinting/interp.rs | 422 -------- pathfinder-classic/src/hinting/mod.rs | 221 ---- pathfinder-classic/src/lib.rs | 110 -- pathfinder-classic/src/outline.rs | 431 -------- pathfinder-classic/src/rasterizer.rs | 543 ---------- pathfinder-classic/src/rect_packer.rs | 119 --- pathfinder-classic/src/shaper.rs | 65 -- pathfinder-classic/src/tables/cff.rs | 597 ----------- pathfinder-classic/src/tables/cmap.rs | 337 ------ pathfinder-classic/src/tables/glyf.rs | 436 -------- pathfinder-classic/src/tables/head.rs | 84 -- pathfinder-classic/src/tables/hhea.rs | 54 - pathfinder-classic/src/tables/hmtx.rs | 66 -- pathfinder-classic/src/tables/kern.rs | 101 -- pathfinder-classic/src/tables/loca.rs | 63 -- pathfinder-classic/src/tables/mod.rs | 44 - pathfinder-classic/src/tables/os_2.rs | 60 -- pathfinder-classic/src/tests/buffers.rs | 30 - pathfinder-classic/src/tests/mod.rs | 13 - pathfinder-classic/src/tests/rect_packer.rs | 49 - pathfinder-classic/src/typesetter.rs | 230 ---- pathfinder-classic/src/util.rs | 140 --- 59 files changed, 9237 deletions(-) delete mode 100644 pathfinder-classic/Cargo.toml delete mode 100644 pathfinder-classic/LICENSE-APACHE delete mode 100644 pathfinder-classic/LICENSE-MIT delete mode 100644 pathfinder-classic/examples/benchmark.rs delete mode 100644 pathfinder-classic/examples/dump-outlines.rs delete mode 100644 pathfinder-classic/examples/generate-atlas.rs delete mode 100644 pathfinder-classic/examples/lorem-ipsum.rs delete mode 100644 pathfinder-classic/resources/examples/lorem-ipsum/composite.fs.glsl delete mode 100644 pathfinder-classic/resources/examples/lorem-ipsum/composite.vs.glsl delete mode 100644 pathfinder-classic/resources/examples/lorem-ipsum/default.txt delete mode 100644 pathfinder-classic/resources/examples/lorem-ipsum/solid_color.fs.glsl delete mode 100644 pathfinder-classic/resources/examples/lorem-ipsum/solid_color.vs.glsl delete mode 100644 pathfinder-classic/resources/shaders/accum.cs.glsl delete mode 100644 pathfinder-classic/resources/shaders/accum_common.cl delete mode 100644 pathfinder-classic/resources/shaders/accum_common.cs.glsl delete mode 100644 pathfinder-classic/resources/shaders/accum_gray.cl delete mode 100644 pathfinder-classic/resources/shaders/accum_subpixel.cl delete mode 100644 pathfinder-classic/resources/shaders/draw.fs.glsl delete mode 100644 pathfinder-classic/resources/shaders/draw.gs.glsl delete mode 100644 pathfinder-classic/resources/shaders/draw.tcs.glsl delete mode 100644 pathfinder-classic/resources/shaders/draw.tes.glsl delete mode 100644 pathfinder-classic/resources/shaders/draw.vs.glsl delete mode 100644 pathfinder-classic/resources/shaders/preamble.cs.glsl delete mode 100644 pathfinder-classic/resources/tests/nimbus-sans/COPYING delete mode 100644 pathfinder-classic/resources/tests/nimbus-sans/NimbusSanL-Regu.ttf delete mode 100644 pathfinder-classic/resources/tests/nimbus-sans/README delete mode 100644 pathfinder-classic/src/atlas.rs delete mode 100644 pathfinder-classic/src/charmap.rs delete mode 100644 pathfinder-classic/src/containers/dfont.rs delete mode 100644 pathfinder-classic/src/containers/mod.rs delete mode 100644 pathfinder-classic/src/containers/otf.rs delete mode 100644 pathfinder-classic/src/containers/ttc.rs delete mode 100644 pathfinder-classic/src/containers/woff.rs delete mode 100644 pathfinder-classic/src/coverage.rs delete mode 100644 pathfinder-classic/src/error.rs delete mode 100644 pathfinder-classic/src/font.rs delete mode 100644 pathfinder-classic/src/hinting/insns.rs delete mode 100644 pathfinder-classic/src/hinting/interp.rs delete mode 100644 pathfinder-classic/src/hinting/mod.rs delete mode 100644 pathfinder-classic/src/lib.rs delete mode 100644 pathfinder-classic/src/outline.rs delete mode 100644 pathfinder-classic/src/rasterizer.rs delete mode 100644 pathfinder-classic/src/rect_packer.rs delete mode 100644 pathfinder-classic/src/shaper.rs delete mode 100644 pathfinder-classic/src/tables/cff.rs delete mode 100644 pathfinder-classic/src/tables/cmap.rs delete mode 100644 pathfinder-classic/src/tables/glyf.rs delete mode 100644 pathfinder-classic/src/tables/head.rs delete mode 100644 pathfinder-classic/src/tables/hhea.rs delete mode 100644 pathfinder-classic/src/tables/hmtx.rs delete mode 100644 pathfinder-classic/src/tables/kern.rs delete mode 100644 pathfinder-classic/src/tables/loca.rs delete mode 100644 pathfinder-classic/src/tables/mod.rs delete mode 100644 pathfinder-classic/src/tables/os_2.rs delete mode 100644 pathfinder-classic/src/tests/buffers.rs delete mode 100644 pathfinder-classic/src/tests/mod.rs delete mode 100644 pathfinder-classic/src/tests/rect_packer.rs delete mode 100644 pathfinder-classic/src/typesetter.rs delete mode 100644 pathfinder-classic/src/util.rs diff --git a/pathfinder-classic/Cargo.toml b/pathfinder-classic/Cargo.toml deleted file mode 100644 index 21b44a9a..00000000 --- a/pathfinder-classic/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "pathfinder" -version = "0.1.0" -authors = ["Patrick Walton "] - -[dependencies] -bitflags = "0.7" -byteorder = "1" -euclid = "0.10" -flate2 = "0.2" -gl = "0.6" -memmap = "0.5" -num-traits = "0.1" -time = "0.1" - -[dependencies.compute-shader] -git = "https://github.com/pcwalton/compute-shader.git" - -[dev-dependencies] -bencher = "0.1" -clap = "2.20" -image = "0.12" -quickcheck = "0.4" - -[dev-dependencies.glfw] -git = "https://github.com/bjz/glfw-rs.git" - -[dev-dependencies.lord-drawquaad] -git = "https://github.com/pcwalton/lord-drawquaad.git" - diff --git a/pathfinder-classic/LICENSE-APACHE b/pathfinder-classic/LICENSE-APACHE deleted file mode 100644 index 16fe87b0..00000000 --- a/pathfinder-classic/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/pathfinder-classic/LICENSE-MIT b/pathfinder-classic/LICENSE-MIT deleted file mode 100644 index 25597d58..00000000 --- a/pathfinder-classic/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/pathfinder-classic/examples/benchmark.rs b/pathfinder-classic/examples/benchmark.rs deleted file mode 100644 index f23dbf57..00000000 --- a/pathfinder-classic/examples/benchmark.rs +++ /dev/null @@ -1,162 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -extern crate bencher; -extern crate compute_shader; -extern crate euclid; -extern crate gl; -extern crate glfw; -extern crate lord_drawquaad; -extern crate memmap; -extern crate pathfinder; -extern crate time; - -use bencher::stats::{self, Stats}; -use compute_shader::buffer; -use compute_shader::image::Format; -use compute_shader::instance::Instance; -use euclid::{Point2D, Rect, Size2D}; -use gl::types::GLuint; -use glfw::{Context, OpenGlProfileHint, WindowHint, WindowMode}; -use memmap::{Mmap, Protection}; -use pathfinder::atlas::{AtlasBuilder, AtlasOptions, GlyphRasterizationOptions}; -use pathfinder::charmap::CodepointRange; -use pathfinder::coverage::{CoverageBuffer, CoverageBufferOptions}; -use pathfinder::font::Font; -use pathfinder::outline::OutlineBuilder; -use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; -use std::env; -use std::os::raw::c_void; -use std::path::PathBuf; - -const ATLAS_SIZE: u32 = 2048; -const WIDTH: u32 = 512; -const HEIGHT: u32 = 384; - -const MIN_TIME_PER_SIZE: u64 = 300_000_000; -const MAX_TIME_PER_SIZE: u64 = 3_000_000_000; - -static SHADER_PATH: &'static str = "resources/shaders/"; - -fn main() { - let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); - glfw.window_hint(WindowHint::ContextVersion(3, 3)); - glfw.window_hint(WindowHint::OpenGlForwardCompat(true)); - glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core)); - let context = glfw.create_window(WIDTH, HEIGHT, "generate-atlas", WindowMode::Windowed); - - let (mut window, _events) = context.expect("Couldn't create a window!"); - window.make_current(); - gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void); - let (device_pixel_width, _) = window.get_framebuffer_size(); - - let instance = Instance::new().unwrap(); - let device = instance.open_device().unwrap(); - let queue = device.create_queue().unwrap(); - - let mut rasterizer_options = RasterizerOptions::from_env().unwrap(); - if env::var("PATHFINDER_SHADER_PATH").is_err() { - rasterizer_options.shader_path = PathBuf::from(SHADER_PATH) - } - - let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); - - for point_size in 6..201 { - // FIXME(pcwalton) - let shelf_height = point_size * 2; - - let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); - let mut buffer = vec![]; - - let mut results = vec![]; - let start = time::precise_time_ns(); - let mut last_time = start; - let (mut outlines, mut glyph_count, mut atlas); - - loop { - glyph_count = 0; - unsafe { - let font = Font::new(file.as_slice(), &mut buffer).unwrap(); - let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)]; - - let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges) - .unwrap(); - let mut outline_builder = OutlineBuilder::new(); - for (_, glyph_id) in glyph_mapping.iter() { - outline_builder.add_glyph(&font, glyph_id).unwrap(); - glyph_count += 1 - } - outlines = outline_builder.create_buffers().unwrap(); - - let mut atlas_builder = AtlasBuilder::new(&AtlasOptions { - available_width: device_pixel_width as GLuint, - shelf_height: shelf_height, - ..AtlasOptions::default() - }); - for glyph_index in 0..(glyph_count as u16) { - atlas_builder.pack_glyph(&outlines, - glyph_index, - &GlyphRasterizationOptions { - point_size: point_size as f32, - ..GlyphRasterizationOptions::default() - }).unwrap(); - } - atlas = atlas_builder.create_atlas().unwrap(); - } - - let end = time::precise_time_ns(); - results.push((end - last_time) as f64); - if end - start > MAX_TIME_PER_SIZE { - break - } - last_time = end - } - - stats::winsorize(&mut results, 5.0); - let time_per_glyph = results.mean() / 1_000.0 / glyph_count as f64; - println!("cpu,{}", time_per_glyph); - - let atlas_size = Size2D::new(ATLAS_SIZE, ATLAS_SIZE); - let coverage_buffer = CoverageBuffer::new(rasterizer.device(), - &CoverageBufferOptions { - size: atlas_size, - ..CoverageBufferOptions::default() - }).unwrap(); - - let image = rasterizer.device() - .create_image(Format::R8, buffer::Protection::WriteOnly, &atlas_size) - .unwrap(); - - let rect = Rect::new(Point2D::new(0, 0), atlas_size); - - let mut results = vec![]; - let start_time = time::precise_time_ns(); - loop { - let events = rasterizer.draw_atlas(&image, &rect, &atlas, &outlines, &coverage_buffer) - .unwrap(); - - let mut draw_time = 0u64; - unsafe { - gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_time); - } - let accum_time = events.accum.time_elapsed().unwrap() as f64; - let time_per_glyph = (draw_time as f64 + accum_time as f64) / - (1000.0 * glyph_count as f64); - results.push(time_per_glyph); - - let now = time::precise_time_ns(); - if (now - start_time > MIN_TIME_PER_SIZE && results.median_abs_dev_pct() < 1.0) || - now - start_time > MAX_TIME_PER_SIZE { - break - } - } - - stats::winsorize(&mut results, 5.0); - let time_per_glyph = results.mean(); - println!("{},{}", point_size, time_per_glyph); - } - - window.set_should_close(true); -} - - diff --git a/pathfinder-classic/examples/dump-outlines.rs b/pathfinder-classic/examples/dump-outlines.rs deleted file mode 100644 index 2167e6aa..00000000 --- a/pathfinder-classic/examples/dump-outlines.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -extern crate clap; -extern crate euclid; -extern crate memmap; -extern crate pathfinder; - -use clap::{App, Arg}; -use memmap::{Mmap, Protection}; -use pathfinder::charmap::CodepointRange; -use pathfinder::font::{Font, PointKind}; -use pathfinder::hinting::Hinter; -use std::char; - -fn main() { - let hint_arg = Arg::with_name("hint").short("H") - .long("hint") - .value_name("POINT-SIZE") - .help("Apply hinting instructions for a point size") - .takes_value(true); - let font_arg = Arg::with_name("FONT-FILE").help("Select the font file (`.ttf`, `.otf`, etc.)") - .required(true) - .index(1); - let matches = App::new("dump-outlines").arg(hint_arg).arg(font_arg).get_matches(); - - let file = Mmap::open_path(matches.value_of("FONT-FILE").unwrap(), Protection::Read).unwrap(); - let mut buffer = vec![]; - unsafe { - let font = Font::new(file.as_slice(), &mut buffer).unwrap(); - - let hinter = if let Some(point_size) = matches.value_of("hint") { - let mut hinter = Hinter::new(&font).unwrap(); - hinter.set_point_size(point_size.parse().unwrap()).unwrap(); - Some(hinter) - } else { - None - }; - - let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; - let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges).unwrap(); - for (glyph_index, (_, glyph_id)) in glyph_mapping.iter().enumerate() { - let codepoint = '!' as u32 + glyph_index as u32; - println!("Glyph {}: codepoint {} '{}':", - glyph_id, - codepoint, - char::from_u32(codepoint).unwrap_or('?')); - - let mut last_point_was_on_curve = false; - font.for_each_point(glyph_id, |point| { - let prefix = if point.index_in_contour == 0 { - "M " - } else { - match point.kind { - PointKind::OnCurve if last_point_was_on_curve => "L ", - PointKind::OnCurve => " ", - PointKind::QuadControl => "Q ", - PointKind::FirstCubicControl => "C ", - PointKind::SecondCubicControl => " ", - } - }; - - print!("{}{},{}", prefix, point.position.x, point.position.y); - - last_point_was_on_curve = point.kind == PointKind::OnCurve; - if last_point_was_on_curve { - println!("") - } - }).unwrap() - } - } -} - diff --git a/pathfinder-classic/examples/generate-atlas.rs b/pathfinder-classic/examples/generate-atlas.rs deleted file mode 100644 index a043da82..00000000 --- a/pathfinder-classic/examples/generate-atlas.rs +++ /dev/null @@ -1,182 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -extern crate clap; -extern crate compute_shader; -extern crate euclid; -extern crate gl; -extern crate glfw; -extern crate lord_drawquaad; -extern crate memmap; -extern crate pathfinder; - -use clap::{App, Arg}; -use compute_shader::buffer; -use compute_shader::image::{Color, ExternalImage, Format}; -use compute_shader::instance::{Instance, ShadingLanguage}; -use euclid::{Point2D, Rect, Size2D}; -use gl::types::{GLint, GLuint}; -use glfw::{Action, Context, Key, OpenGlProfileHint, WindowEvent, WindowHint, WindowMode}; -use memmap::{Mmap, Protection}; -use pathfinder::atlas::{AtlasBuilder, AtlasOptions, GlyphRasterizationOptions}; -use pathfinder::charmap::CodepointRange; -use pathfinder::coverage::{CoverageBuffer, CoverageBufferOptions}; -use pathfinder::font::Font; -use pathfinder::outline::OutlineBuilder; -use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; -use std::env; -use std::os::raw::c_void; -use std::path::PathBuf; - -const DEFAULT_POINT_SIZE: f32 = 24.0; -const WIDTH: u32 = 512; -const HEIGHT: u32 = 384; - -static SHADER_PATH: &'static str = "resources/shaders/"; - -fn main() { - let index_arg = Arg::with_name("index").short("i") - .long("index") - .help("Select an index within a font collection") - .takes_value(true); - let subpixel_antialiasing_arg = - Arg::with_name("subpixel-aa").short("s") - .long("subpixel-aa") - .help("Enable subpixel antialiasing"); - let font_arg = Arg::with_name("FONT-FILE").help("Select the font file (`.ttf`, `.otf`, etc.)") - .required(true) - .index(1); - let point_size_arg = Arg::with_name("POINT-SIZE").help("Select the point size") - .index(2); - let matches = App::new("generate-atlas").arg(index_arg) - .arg(subpixel_antialiasing_arg) - .arg(font_arg) - .arg(point_size_arg) - .get_matches(); - - let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); - glfw.window_hint(WindowHint::ContextVersion(3, 3)); - glfw.window_hint(WindowHint::OpenGlForwardCompat(true)); - glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core)); - let context = glfw.create_window(WIDTH, HEIGHT, "generate-atlas", WindowMode::Windowed); - - let (mut window, events) = context.expect("Couldn't create a window!"); - window.make_current(); - gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void); - let (device_pixel_width, device_pixel_height) = window.get_framebuffer_size(); - - let instance = Instance::new().unwrap(); - let device = instance.open_device().unwrap(); - let queue = device.create_queue().unwrap(); - - let mut rasterizer_options = RasterizerOptions::from_env().unwrap(); - if env::var("PATHFINDER_SHADER_PATH").is_err() { - rasterizer_options.shader_path = PathBuf::from(SHADER_PATH) - } - - let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); - - let file = Mmap::open_path(matches.value_of("FONT-FILE").unwrap(), Protection::Read).unwrap(); - let mut buffer = vec![]; - - let point_size = match matches.value_of("POINT-SIZE") { - Some(point_size) => point_size.parse().unwrap(), - None => DEFAULT_POINT_SIZE, - }; - - let font_index = match matches.value_of("index") { - Some(index) => index.parse().unwrap(), - None => 0, - }; - - let subpixel_aa = matches.is_present("subpixel-aa"); - - let (outlines, atlas); - unsafe { - let font = Font::from_collection_index(file.as_slice(), font_index, &mut buffer).unwrap(); - let codepoint_ranges = [CodepointRange::new(' ' as u32, '~' as u32)]; - let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges).unwrap(); - - let shelf_height = font.shelf_height(point_size); - - let mut outline_builder = OutlineBuilder::new(); - let mut glyph_count = 0; - for (_, glyph_id) in glyph_mapping.iter() { - outline_builder.add_glyph(&font, glyph_id).unwrap(); - glyph_count += 1 - } - outlines = outline_builder.create_buffers().unwrap(); - - let mut atlas_builder = AtlasBuilder::new(&AtlasOptions { - available_width: device_pixel_width as u32, - shelf_height: shelf_height, - subpixel_antialiasing: subpixel_aa, - }); - for glyph_index in 0..glyph_count { - atlas_builder.pack_glyph(&outlines, - glyph_index, - &GlyphRasterizationOptions { - point_size: point_size, - ..GlyphRasterizationOptions::default() - }).unwrap(); - } - atlas = atlas_builder.create_atlas().unwrap(); - } - - let atlas_size = Size2D::new(device_pixel_width as GLuint, device_pixel_height as GLuint); - let coverage_buffer = CoverageBuffer::new(rasterizer.device(), - &CoverageBufferOptions { - size: atlas_size, - subpixel_antialiasing: subpixel_aa, - ..CoverageBufferOptions::default() - }).unwrap(); - - let image = rasterizer.device() - .create_image(Format::RGBA8, buffer::Protection::ReadWrite, &atlas_size) - .unwrap(); - - rasterizer.queue().submit_clear(&image, &Color::UInt(0, 0, 0, 0), &[]).unwrap(); - - let rect = Rect::new(Point2D::new(0, 0), atlas_size); - - rasterizer.draw_atlas(&image, &rect, &atlas, &outlines, &coverage_buffer).unwrap(); - rasterizer.queue().flush().unwrap(); - - let draw_context = lord_drawquaad::Context::new(); - - let mut gl_texture = 0; - unsafe { - if instance.shading_language() == ShadingLanguage::Glsl { - gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT | gl::TEXTURE_FETCH_BARRIER_BIT); - } - - gl::GenTextures(1, &mut gl_texture); - image.bind_to(&ExternalImage::GlTexture(gl_texture)).unwrap(); - - gl::BindTexture(gl::TEXTURE_RECTANGLE, gl_texture); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint); - - gl::Viewport(0, 0, device_pixel_width, device_pixel_height); - gl::ClearColor(1.0, 1.0, 1.0, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - } - - draw_context.draw(gl_texture); - window.swap_buffers(); - - while !window.should_close() { - glfw.poll_events(); - for (_, event) in glfw::flush_messages(&events) { - match event { - WindowEvent::Key(Key::Escape, _, Action::Press, _) => { - window.set_should_close(true) - } - _ => {} - } - } - } -} - diff --git a/pathfinder-classic/examples/lorem-ipsum.rs b/pathfinder-classic/examples/lorem-ipsum.rs deleted file mode 100644 index e9c04f19..00000000 --- a/pathfinder-classic/examples/lorem-ipsum.rs +++ /dev/null @@ -1,983 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -extern crate clap; -extern crate compute_shader; -extern crate euclid; -extern crate gl; -extern crate glfw; -extern crate image; -extern crate memmap; -extern crate pathfinder; - -use clap::{App, Arg}; -use compute_shader::buffer; -use compute_shader::image::{Color, ExternalImage, Format, Image}; -use compute_shader::instance::{Instance, ShadingLanguage}; -use euclid::{Point2D, Rect, Size2D}; -use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid}; -use glfw::{Action, Context, Key, OpenGlProfileHint, SwapInterval, Window, WindowEvent}; -use glfw::{Glfw, WindowHint, WindowMode}; -use memmap::{Mmap, Protection}; -use pathfinder::atlas::{AtlasBuilder, AtlasOptions, GlyphRasterizationOptions}; -use pathfinder::charmap::CodepointRanges; -use pathfinder::coverage::{CoverageBuffer, CoverageBufferOptions}; -use pathfinder::error::RasterError; -use pathfinder::font::Font; -use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions}; -use pathfinder::typesetter::{GlyphStore, PositionedGlyph, Typesetter}; -use std::char; -use std::env; -use std::f32; -use std::fs::File; -use std::io::Read; -use std::mem; -use std::os::raw::c_void; -use std::path::{Path, PathBuf}; -use std::sync::mpsc::Receiver; - -const ATLAS_SIZE: u32 = 2048; -const WIDTH: u32 = 640; -const HEIGHT: u32 = 480; -const SCROLL_SPEED: f64 = 6.0; - -const SUBPIXEL_GRANULARITY_COUNT: u8 = 4; -const SUBPIXEL_GRANULARITY: f32 = 1.0 / SUBPIXEL_GRANULARITY_COUNT as f32; - -const INITIAL_POINT_SIZE: f32 = 24.0; -const MIN_POINT_SIZE: f32 = 6.0; -const MAX_POINT_SIZE: f32 = 512.0; - -const FPS_DISPLAY_POINT_SIZE: f32 = 24.0; -const FPS_PADDING: i32 = 6; - -static PATHFINDER_SHADER_PATH: &'static str = "resources/shaders/"; -static EXAMPLE_SHADER_PATH: &'static str = "resources/examples/lorem-ipsum/"; -static DEFAULT_TEXT_PATH: &'static str = "resources/examples/lorem-ipsum/default.txt"; - -static FPS_BACKGROUND_COLOR: [f32; 3] = [0.3, 0.3, 0.3]; -static FPS_FOREGROUND_COLOR: [f32; 3] = [1.0, 1.0, 1.0]; -static BACKGROUND_COLOR: [f32; 3] = [1.0, 1.0, 1.0]; -static TEXT_COLOR: [f32; 3] = [0.0, 0.0, 0.0]; - -static ATLAS_DUMP_FILENAME: &'static str = "lorem-ipsum-atlas.png"; - -static RECT_INDICES: [u16; 6] = [0, 1, 3, 1, 2, 3]; - -fn main() { - DemoApp::new(AppOptions::parse()).run() -} - -struct DemoApp { - options: AppOptions, - renderer: Renderer, - glfw: Glfw, - window: Window, - events: Receiver<(f64, WindowEvent)>, - point_size: f32, - translation: Point2D, - device_pixel_size: Size2D, -} - -impl DemoApp { - fn new(options: AppOptions) -> DemoApp { - let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); - glfw.window_hint(WindowHint::ContextVersion(3, 3)); - glfw.window_hint(WindowHint::OpenGlForwardCompat(true)); - glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core)); - let context = glfw.create_window(WIDTH, HEIGHT, "lorem-ipsum", WindowMode::Windowed); - - let (mut window, events) = context.expect("Couldn't create a window!"); - window.make_current(); - window.set_key_polling(true); - window.set_scroll_polling(true); - window.set_size_polling(true); - window.set_framebuffer_size_polling(true); - glfw.set_swap_interval(SwapInterval::Sync(1)); - - gl::load_with(|symbol| window.get_proc_address(symbol) as *const c_void); - - let (width, height) = window.get_framebuffer_size(); - let device_pixel_size = Size2D::new(width as u32, height as u32); - - let renderer = Renderer::new(options.subpixel_aa); - - DemoApp { - renderer: renderer, - glfw: glfw, - window: window, - events: events, - device_pixel_size: device_pixel_size, - point_size: INITIAL_POINT_SIZE, - translation: Point2D::zero(), - options: options, - } - } - - fn run(&mut self) { - let file = Mmap::open_path(&self.options.font_path, Protection::Read).unwrap(); - let mut buffer = vec![]; - let font = unsafe { - Font::from_collection_index(file.as_slice(), - self.options.font_index, - &mut buffer).unwrap() - }; - - let page_width = self.device_pixel_size.width as f32 * - font.units_per_em() as f32 / INITIAL_POINT_SIZE; - - let mut typesetter = Typesetter::new(page_width, &font, font.units_per_em() as f32); - typesetter.add_text(&font, font.units_per_em() as f32, &self.options.text); - - let glyph_stores = GlyphStores::new(&typesetter, &font); - - let mut dirty = true; - while !self.window.should_close() { - if dirty { - self.redraw(&glyph_stores, &typesetter, &font); - dirty = false - } - - self.glfw.wait_events(); - let events: Vec<_> = glfw::flush_messages(&self.events).map(|(_, e)| e).collect(); - for event in events { - dirty = self.handle_window_event(event) || dirty - } - } - } - - fn redraw(&mut self, glyph_stores: &GlyphStores, typesetter: &Typesetter, font: &Font) { - let redraw_result = self.renderer.redraw(self.point_size, - &font, - &glyph_stores.main, - typesetter, - &self.device_pixel_size, - &self.translation); - - let (draw_time, accum_time); - match redraw_result.events { - Some(events) => { - let mut draw_nanos = 0u64; - unsafe { - gl::Flush(); - gl::GetQueryObjectui64v(events.draw, gl::QUERY_RESULT, &mut draw_nanos); - } - - draw_time = draw_nanos as f64; - accum_time = events.accum.time_elapsed().unwrap() as f64; - } - None => { - draw_time = 0.0; - accum_time = 0.0; - } - } - - let timing = self.renderer.get_timing_in_ms(); - - self.renderer.draw_fps(&font, - &glyph_stores.fps, - &self.device_pixel_size, - draw_time, - accum_time, - timing, - redraw_result.glyphs_drawn); - - self.window.swap_buffers(); - } - - // Returns true if the window needs to be redrawn. - fn handle_window_event(&mut self, event: WindowEvent) -> bool { - match event { - WindowEvent::Key(Key::Escape, _, Action::Press, _) => { - self.window.set_should_close(true); - false - } - WindowEvent::Key(Key::S, _, Action::Press, _) => { - self.renderer.take_screenshot(); - println!("wrote screenshot to: {}", ATLAS_DUMP_FILENAME); - false - } - WindowEvent::Scroll(_, y) if self.window.get_key(Key::LeftAlt) == Action::Press || - self.window.get_key(Key::RightAlt) == Action::Press => { - let old_point_size = self.point_size; - self.point_size = old_point_size + y as f32; - - if self.point_size < MIN_POINT_SIZE { - self.point_size = MIN_POINT_SIZE - } else if self.point_size > MAX_POINT_SIZE { - self.point_size = MAX_POINT_SIZE - } - - let offset = Point2D::new(self.device_pixel_size.width as f32, - self.device_pixel_size.height as f32); - - let center = self.translation.cast().unwrap() - offset * 0.5; - - self.translation = (center * self.point_size / old_point_size + - offset * 0.5).round().cast().unwrap(); - - true - } - WindowEvent::Scroll(x, y) => { - let vector = Point2D::new(x, y) * SCROLL_SPEED.round(); - self.translation = self.translation + vector.cast().unwrap(); - true - } - WindowEvent::Size(_, _) | WindowEvent::FramebufferSize(_, _) => { - let (width, height) = self.window.get_framebuffer_size(); - self.device_pixel_size = Size2D::new(width as u32, height as u32); - true - } - _ => false, - } - } -} - -struct AppOptions { - font_path: String, - font_index: u32, - text: String, - subpixel_aa: bool, -} - -impl AppOptions { - fn parse() -> AppOptions { - let index_arg = Arg::with_name("index").short("i") - .long("index") - .help("Select an index within a font collection") - .takes_value(true); - let subpixel_antialiasing_arg = - Arg::with_name("subpixel-aa").short("s") - .long("subpixel-aa") - .help("Enable subpixel antialiasing"); - let font_arg = - Arg::with_name("FONT-FILE").help("Select the font file (`.ttf`, `.otf`, etc.)") - .required(true) - .index(1); - let text_arg = Arg::with_name("TEXT-FILE").help("Select a file containing text to display") - .index(2); - let matches = App::new("lorem-ipsum").arg(index_arg) - .arg(subpixel_antialiasing_arg) - .arg(font_arg) - .arg(text_arg) - .get_matches(); - - let mut text = "".to_string(); - let path = matches.value_of("TEXT-FILE").unwrap_or(DEFAULT_TEXT_PATH); - File::open(path).unwrap().read_to_string(&mut text).unwrap(); - text = text.replace(&['\n', '\r', '\t'][..], " "); - - let font_index = match matches.value_of("index") { - Some(index) => index.parse().unwrap(), - None => 0, - }; - - let font_path = matches.value_of("FONT-FILE").unwrap(); - let subpixel_aa = matches.is_present("subpixel-aa"); - - AppOptions { - text: text, - font_index: font_index, - font_path: font_path.to_string(), - subpixel_aa: subpixel_aa, - } - } -} - -struct GlyphStores { - main: GlyphStore, - fps: GlyphStore, -} - -impl GlyphStores { - fn new(typesetter: &Typesetter, font: &Font) -> GlyphStores { - let main_glyph_store = typesetter.create_glyph_store(&font).unwrap(); - - let mut fps_chars: Vec = vec![]; - fps_chars.extend(" ./,:()".chars()); - fps_chars.extend(('A' as u32..('Z' as u32 + 1)).flat_map(char::from_u32)); - fps_chars.extend(('a' as u32..('z' as u32 + 1)).flat_map(char::from_u32)); - fps_chars.extend(('0' as u32..('9' as u32 + 1)).flat_map(char::from_u32)); - fps_chars.sort(); - let fps_codepoint_ranges = CodepointRanges::from_sorted_chars(&fps_chars); - let fps_glyph_store = GlyphStore::from_codepoints(&fps_codepoint_ranges, &font).unwrap(); - - GlyphStores { - main: main_glyph_store, - fps: fps_glyph_store, - } - } -} - -struct Renderer { - rasterizer: Rasterizer, - - composite_program: GLuint, - composite_atlas_uniform: GLint, - composite_transform_uniform: GLint, - composite_translation_uniform: GLint, - composite_foreground_color_uniform: GLint, - composite_background_color_uniform: GLint, - - main_composite_vertex_array: CompositeVertexArray, - fps_composite_vertex_array: CompositeVertexArray, - - solid_color_program: GLuint, - solid_color_color_uniform: GLint, - - solid_color_vertex_array: GLuint, - solid_color_vertex_buffer: GLuint, - solid_color_index_buffer: GLuint, - - atlas_size: Size2D, - - main_coverage_buffer: CoverageBuffer, - fps_coverage_buffer: CoverageBuffer, - main_compute_image: Image, - main_gl_texture: GLuint, - fps_compute_image: Image, - fps_gl_texture: GLuint, - - query: GLuint, - - shading_language: ShadingLanguage, - - subpixel_aa: bool, -} - -impl Renderer { - fn new(subpixel_aa: bool) -> Renderer { - let instance = Instance::new().unwrap(); - let device = instance.open_device().unwrap(); - let queue = device.create_queue().unwrap(); - - let mut rasterizer_options = RasterizerOptions::from_env().unwrap(); - if env::var("PATHFINDER_SHADER_PATH").is_err() { - rasterizer_options.shader_path = PathBuf::from(PATHFINDER_SHADER_PATH) - } - - let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); - - let (composite_program, composite_position_attribute, composite_tex_coord_attribute); - let (composite_atlas_uniform, composite_transform_uniform); - let (composite_translation_uniform, composite_foreground_color_uniform); - let composite_background_color_uniform; - let (main_composite_vertex_array, fps_composite_vertex_array); - let (solid_color_program, solid_color_position_attribute, solid_color_color_uniform); - let (mut solid_color_vertex_buffer, mut solid_color_index_buffer) = (0, 0); - let mut solid_color_vertex_array = 0; - unsafe { - composite_program = create_program("composite"); - composite_position_attribute = - gl::GetAttribLocation(composite_program, "aPosition\0".as_ptr() as *const GLchar); - composite_tex_coord_attribute = - gl::GetAttribLocation(composite_program, "aTexCoord\0".as_ptr() as *const GLchar); - composite_atlas_uniform = - gl::GetUniformLocation(composite_program, "uAtlas\0".as_ptr() as *const GLchar); - composite_transform_uniform = - gl::GetUniformLocation(composite_program, - "uTransform\0".as_ptr() as *const GLchar); - composite_translation_uniform = - gl::GetUniformLocation(composite_program, - "uTranslation\0".as_ptr() as *const GLchar); - composite_foreground_color_uniform = - gl::GetUniformLocation(composite_program, - "uForegroundColor\0".as_ptr() as *const GLchar); - composite_background_color_uniform = - gl::GetUniformLocation(composite_program, - "uBackgroundColor\0".as_ptr() as *const GLchar); - - solid_color_program = create_program("solid_color"); - solid_color_position_attribute = - gl::GetAttribLocation(solid_color_program, - "aPosition\0".as_ptr() as *const GLchar); - solid_color_color_uniform = - gl::GetUniformLocation(solid_color_program, "uColor\0".as_ptr() as *const GLchar); - - gl::UseProgram(composite_program); - - main_composite_vertex_array = CompositeVertexArray::new(); - fps_composite_vertex_array = CompositeVertexArray::new(); - for vertex_array in &[&main_composite_vertex_array, &fps_composite_vertex_array] { - gl::BindVertexArray(vertex_array.vertex_array); - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vertex_array.index_buffer); - gl::BindBuffer(gl::ARRAY_BUFFER, vertex_array.vertex_buffer); - - gl::VertexAttribPointer(composite_position_attribute as GLuint, - 2, - gl::FLOAT, - gl::FALSE, - mem::size_of::() as GLsizei, - 0 as *const GLvoid); - gl::VertexAttribPointer(composite_tex_coord_attribute as GLuint, - 2, - gl::UNSIGNED_INT, - gl::FALSE, - mem::size_of::() as GLsizei, - (mem::size_of::() * 2) as *const GLvoid); - gl::EnableVertexAttribArray(composite_position_attribute as GLuint); - gl::EnableVertexAttribArray(composite_tex_coord_attribute as GLuint); - } - - gl::UseProgram(solid_color_program); - - gl::GenVertexArrays(1, &mut solid_color_vertex_array); - gl::BindVertexArray(solid_color_vertex_array); - - gl::GenBuffers(1, &mut solid_color_vertex_buffer); - gl::GenBuffers(1, &mut solid_color_index_buffer); - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, solid_color_index_buffer); - gl::BindBuffer(gl::ARRAY_BUFFER, solid_color_vertex_buffer); - - gl::VertexAttribPointer(solid_color_position_attribute as GLuint, - 2, - gl::FLOAT, - gl::FALSE, - mem::size_of::() as GLsizei * 2, - 0 as *const GLvoid); - gl::EnableVertexAttribArray(solid_color_position_attribute as GLuint); - - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, - (RECT_INDICES.len() * mem::size_of::()) as GLsizeiptr, - RECT_INDICES.as_ptr() as *const GLvoid, - gl::STATIC_DRAW); - } - - // FIXME(pcwalton): Dynamically resizing atlas. - let atlas_size = Size2D::new(ATLAS_SIZE, ATLAS_SIZE); - let coverage_buffer_options = CoverageBufferOptions { - size: atlas_size, - subpixel_antialiasing: subpixel_aa, - ..CoverageBufferOptions::default() - }; - - let main_coverage_buffer = CoverageBuffer::new(rasterizer.device(), - &coverage_buffer_options).unwrap(); - let fps_coverage_buffer = CoverageBuffer::new(rasterizer.device(), - &coverage_buffer_options).unwrap(); - - let (main_compute_image, main_gl_texture) = create_image(&rasterizer, &atlas_size); - let (fps_compute_image, fps_gl_texture) = create_image(&rasterizer, &atlas_size); - - let mut query = 0; - unsafe { - gl::GenQueries(1, &mut query); - } - - let shading_language = instance.shading_language(); - - Renderer { - rasterizer: rasterizer, - - composite_program: composite_program, - composite_atlas_uniform: composite_atlas_uniform, - composite_transform_uniform: composite_transform_uniform, - composite_translation_uniform: composite_translation_uniform, - composite_foreground_color_uniform: composite_foreground_color_uniform, - composite_background_color_uniform: composite_background_color_uniform, - - main_composite_vertex_array: main_composite_vertex_array, - fps_composite_vertex_array: fps_composite_vertex_array, - - solid_color_program: solid_color_program, - solid_color_color_uniform: solid_color_color_uniform, - - solid_color_vertex_array: solid_color_vertex_array, - solid_color_vertex_buffer: solid_color_vertex_buffer, - solid_color_index_buffer: solid_color_index_buffer, - - atlas_size: atlas_size, - - main_coverage_buffer: main_coverage_buffer, - fps_coverage_buffer: fps_coverage_buffer, - main_compute_image: main_compute_image, - main_gl_texture: main_gl_texture, - fps_compute_image: fps_compute_image, - fps_gl_texture: fps_gl_texture, - - query: query, - - shading_language: shading_language, - - subpixel_aa: subpixel_aa, - } - } - - fn redraw(&self, - point_size: f32, - font: &Font, - glyph_store: &GlyphStore, - typesetter: &Typesetter, - device_pixel_size: &Size2D, - translation: &Point2D) - -> RedrawResult { - let shelf_height = font.shelf_height(point_size); - let atlas_options = AtlasOptions { - available_width: ATLAS_SIZE, - shelf_height: shelf_height, - subpixel_antialiasing: self.subpixel_aa, - ..AtlasOptions::default() - }; - - let mut atlas_builder = AtlasBuilder::new(&atlas_options); - - let (positioned_glyphs, cached_glyphs) = self.determine_visible_glyphs(&mut atlas_builder, - font, - glyph_store, - typesetter, - device_pixel_size, - translation, - point_size); - - let atlas = atlas_builder.create_atlas().unwrap(); - let rect = Rect::new(Point2D::new(0, 0), self.atlas_size); - - let events = match self.rasterizer.draw_atlas(&self.main_compute_image, - &rect, - &atlas, - &glyph_store.outlines, - &self.main_coverage_buffer) { - Ok(events) => Some(events), - Err(RasterError::NoGlyphsToDraw) => None, - Err(error) => panic!("Failed to rasterize atlas: {:?}", error), - }; - - self.rasterizer.queue().flush().unwrap(); - - unsafe { - if self.shading_language == ShadingLanguage::Glsl { - gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT | - gl::TEXTURE_FETCH_BARRIER_BIT); - } - - gl::Viewport(0, - 0, - device_pixel_size.width as GLint, - device_pixel_size.height as GLint); - gl::ClearColor(BACKGROUND_COLOR[0], BACKGROUND_COLOR[1], BACKGROUND_COLOR[2], 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - } - - if events.is_some() { - self.draw_glyphs(glyph_store, - &self.main_composite_vertex_array, - &positioned_glyphs, - &cached_glyphs, - device_pixel_size, - translation, - self.main_gl_texture, - point_size, - &TEXT_COLOR, - &BACKGROUND_COLOR) - } - - RedrawResult { - events: events, - glyphs_drawn: cached_glyphs.len() as u32, - } - } - - fn determine_visible_glyphs(&self, - atlas_builder: &mut AtlasBuilder, - font: &Font, - glyph_store: &GlyphStore, - typesetter: &Typesetter, - device_pixel_size: &Size2D, - translation: &Point2D, - point_size: f32) - -> (Vec, Vec) { - let viewport = Rect::new(-translation.cast().unwrap(), device_pixel_size.cast().unwrap()); - - let scale = point_size / font.units_per_em() as f32; - let positioned_glyphs = typesetter.positioned_glyphs_in_rect(&viewport, - glyph_store, - font.units_per_em() as f32, - scale, - SUBPIXEL_GRANULARITY); - - let mut glyphs = vec![]; - for positioned_glyph in &positioned_glyphs { - if positioned_glyph.glyph_index as usize >= glyphs.len() { - glyphs.resize(positioned_glyph.glyph_index as usize + 1, 0) - } - - let subpixel = (positioned_glyph.subpixel_x / SUBPIXEL_GRANULARITY).round() as u8; - glyphs[positioned_glyph.glyph_index as usize] |= 1 << subpixel - } - - let mut cached_glyphs = vec![]; - for (glyph_index, &subpixels) in glyphs.iter().enumerate() { - for subpixel in 0..SUBPIXEL_GRANULARITY_COUNT { - if (subpixels & (1 << subpixel)) == 0 { - cached_glyphs.push(CachedGlyph(Point2D::zero())); - continue - } - - let subpixel_offset = (subpixel as f32) / (SUBPIXEL_GRANULARITY as f32); - let options = GlyphRasterizationOptions { - point_size: point_size, - horizontal_offset: subpixel_offset, - ..GlyphRasterizationOptions::default() - }; - - let origin = atlas_builder.pack_glyph(&glyph_store.outlines, - glyph_index as u16, - &options).unwrap(); - - cached_glyphs.push(CachedGlyph(origin)); - } - } - - (positioned_glyphs, cached_glyphs) - } - - fn get_timing_in_ms(&self) -> f64 { - unsafe { - let mut result = 0; - gl::GetQueryObjectui64v(self.query, gl::QUERY_RESULT, &mut result); - (result as f64) / (1_000_000.0) - } - } - - fn draw_glyphs(&self, - glyph_store: &GlyphStore, - vertex_array: &CompositeVertexArray, - positioned_glyphs: &[PositionedGlyph], - cached_glyphs: &[CachedGlyph], - device_pixel_size: &Size2D, - translation: &Point2D, - texture: GLuint, - point_size: f32, - foreground_color: &[f32], - background_color: &[f32]) { - unsafe { - gl::UseProgram(self.composite_program); - gl::BindVertexArray(vertex_array.vertex_array); - gl::BindBuffer(gl::ARRAY_BUFFER, vertex_array.vertex_buffer); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, vertex_array.index_buffer); - - let vertex_count = self.upload_quads_for_text(glyph_store, - positioned_glyphs, - cached_glyphs, - point_size); - - gl::ActiveTexture(gl::TEXTURE0); - gl::BindTexture(gl::TEXTURE_RECTANGLE, texture); - gl::Uniform1i(self.composite_atlas_uniform, 0); - - let matrix = [ - 2.0 / device_pixel_size.width as f32, 0.0, - 0.0, -2.0 / device_pixel_size.height as f32, - ]; - gl::UniformMatrix2fv(self.composite_transform_uniform, 1, gl::FALSE, matrix.as_ptr()); - - gl::Uniform2f(self.composite_translation_uniform, - -1.0 + 2.0 * translation.x as f32 / device_pixel_size.width as f32, - 1.0 - 2.0 * translation.y as f32 / device_pixel_size.height as f32); - - gl::Uniform3fv(self.composite_foreground_color_uniform, 1, foreground_color.as_ptr()); - gl::Uniform3fv(self.composite_background_color_uniform, 1, background_color.as_ptr()); - - gl::Enable(gl::BLEND); - gl::BlendEquation(gl::FUNC_ADD); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - - gl::BeginQuery(gl::TIME_ELAPSED, self.query); - - gl::DrawElements(gl::TRIANGLES, - vertex_count as GLsizei, - gl::UNSIGNED_SHORT, - 0 as *const GLvoid); - - gl::EndQuery(gl::TIME_ELAPSED); - } - } - - fn upload_quads_for_text(&self, - glyph_store: &GlyphStore, - positioned_glyphs: &[PositionedGlyph], - cached_glyphs: &[CachedGlyph], - point_size: f32) - -> usize { - let (mut vertices, mut indices) = (vec![], vec![]); - for positioned_glyph in positioned_glyphs { - let glyph_index = positioned_glyph.glyph_index; - let glyph_rect = glyph_store.outlines.glyph_subpixel_bounds(glyph_index, point_size); - - let subpixel = (positioned_glyph.subpixel_x / SUBPIXEL_GRANULARITY).round() as u8; - - let glyph_rect_i = glyph_rect.round_out(); - let glyph_size_i = glyph_rect_i.size(); - - let cached_glyph_index = glyph_index as usize * SUBPIXEL_GRANULARITY_COUNT as usize + - subpixel as usize; - let cached_glyph = cached_glyphs[cached_glyph_index]; - - let uv_tl: Point2D = cached_glyph.0.floor().cast().unwrap(); - let uv_br = uv_tl + glyph_size_i.cast().unwrap(); - - let left_pos = positioned_glyph.bounds.origin.x; - let top_pos = positioned_glyph.bounds.origin.y; - let right_pos = positioned_glyph.bounds.origin.x + glyph_size_i.width as f32; - let bottom_pos = positioned_glyph.bounds.origin.y + glyph_size_i.height as f32; - - let first_index = vertices.len() as u16; - - vertices.push(Vertex::new(left_pos, top_pos, uv_tl.x, uv_tl.y)); - vertices.push(Vertex::new(right_pos, top_pos, uv_br.x, uv_tl.y)); - vertices.push(Vertex::new(right_pos, bottom_pos, uv_br.x, uv_br.y)); - vertices.push(Vertex::new(left_pos, bottom_pos, uv_tl.x, uv_br.y)); - - indices.extend(RECT_INDICES.iter().map(|index| first_index + index)); - } - - unsafe { - gl::BufferData(gl::ARRAY_BUFFER, - (vertices.len() * mem::size_of::()) as GLsizeiptr, - vertices.as_ptr() as *const GLvoid, - gl::STATIC_DRAW); - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, - (indices.len() * mem::size_of::()) as GLsizeiptr, - indices.as_ptr() as *const GLvoid, - gl::STATIC_DRAW); - } - - indices.len() - } - - fn draw_fps(&self, - font: &Font, - fps_glyph_store: &GlyphStore, - device_pixel_size: &Size2D, - draw_time: f64, - accum_time: f64, - composite_time: f64, - glyphs_drawn: u32) { - // Draw the background color. - unsafe { - gl::BindVertexArray(self.solid_color_vertex_array); - gl::UseProgram(self.solid_color_program); - gl::BindBuffer(gl::ARRAY_BUFFER, self.solid_color_vertex_buffer); - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.solid_color_index_buffer); - - let tl = Point2D::new( - -1.0, - -1.0 + (FPS_DISPLAY_POINT_SIZE + FPS_PADDING as f32 * 2.0) / - (device_pixel_size.height as f32) * 2.0); - let br = Point2D::new(1.0, -1.0); - - let vertices = [(tl.x, tl.y), (br.x, tl.y), (br.x, br.y), (tl.x, br.y)]; - gl::BufferData(gl::ARRAY_BUFFER, - (vertices.len() * mem::size_of::<(f32, f32)>()) as GLsizeiptr, - vertices.as_ptr() as *const GLvoid, - gl::DYNAMIC_DRAW); - - gl::Uniform3fv(self.solid_color_color_uniform, 1, FPS_BACKGROUND_COLOR.as_ptr()); - - gl::Enable(gl::BLEND); - gl::BlendEquation(gl::FUNC_ADD); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - - gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_SHORT, 0 as *const GLvoid); - } - - let fps_text = format!("draw: {:.3}ms ({:.3}us/glyph), \ - accum: {:.3}ms ({:.3}us/glyph), \ - composite: {:.3}ms ({:.3}us/glyph)", - draw_time / 1_000_000.0, - draw_time / (1000.0 * glyphs_drawn as f64), - accum_time / 1_000_000.0, - accum_time / (1000.0 * glyphs_drawn as f64), - composite_time, - (composite_time * 1000.0) / (glyphs_drawn as f64)); - - // TODO(pcwalton): Subpixel positioning for the FPS display. - let mut fps_typesetter = Typesetter::new(f32::INFINITY, &font, font.units_per_em() as f32); - fps_typesetter.add_text(&font, font.units_per_em() as f32, &fps_text); - - let shelf_height = font.shelf_height(FPS_DISPLAY_POINT_SIZE); - let atlas_options = AtlasOptions { - available_width: ATLAS_SIZE, - shelf_height: shelf_height, - subpixel_antialiasing: self.subpixel_aa, - ..AtlasOptions::default() - }; - - let mut fps_atlas_builder = AtlasBuilder::new(&atlas_options); - - let (fps_positioned_glyphs, fps_cached_glyphs) = - self.determine_visible_glyphs(&mut fps_atlas_builder, - font, - fps_glyph_store, - &fps_typesetter, - device_pixel_size, - &Point2D::zero(), - FPS_DISPLAY_POINT_SIZE); - - let fps_atlas = fps_atlas_builder.create_atlas().unwrap(); - let rect = Rect::new(Point2D::new(0, 0), self.atlas_size); - self.rasterizer.draw_atlas(&self.fps_compute_image, - &rect, - &fps_atlas, - &fps_glyph_store.outlines, - &self.fps_coverage_buffer).unwrap(); - self.rasterizer.queue().flush().unwrap(); - - let fps_pixels_per_unit = FPS_DISPLAY_POINT_SIZE / font.units_per_em() as f32; - let fps_line_spacing = ((font.ascender() as f32 - font.descender() as f32 + - font.line_gap() as f32) * fps_pixels_per_unit).round() as i32; - - let fps_left = FPS_PADDING; - let fps_top = device_pixel_size.height as i32 - FPS_PADDING - fps_line_spacing; - - self.draw_glyphs(&fps_glyph_store, - &self.fps_composite_vertex_array, - &fps_positioned_glyphs, - &fps_cached_glyphs, - device_pixel_size, - &Point2D::new(fps_left, fps_top), - self.fps_gl_texture, - FPS_DISPLAY_POINT_SIZE, - &FPS_FOREGROUND_COLOR, - &FPS_BACKGROUND_COLOR); - } - - fn take_screenshot(&self) { - unsafe { - let mut fbo = 0; - gl::GenFramebuffers(1, &mut fbo); - gl::BindFramebuffer(gl::FRAMEBUFFER, fbo); - gl::FramebufferTexture2D(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_RECTANGLE, - self.main_gl_texture, - 0); - - let length = 4 * self.atlas_size.width as usize * self.atlas_size.height as usize; - let mut pixels: Vec = vec![0; length]; - gl::ReadPixels(0, 0, - self.atlas_size.width as GLint, self.atlas_size.height as GLint, - gl::RGBA, - gl::UNSIGNED_BYTE, - pixels.as_mut_ptr() as *mut c_void); - - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::DeleteFramebuffers(1, &mut fbo); - - image::save_buffer(&Path::new(ATLAS_DUMP_FILENAME), - &pixels, - self.atlas_size.width, - self.atlas_size.height, - image::RGBA(8)).unwrap(); - } - } -} - -#[derive(Clone, Copy, Debug)] -#[repr(C)] -struct Vertex { - x: f32, - y: f32, - u: u32, - v: u32, -} - -impl Vertex { - fn new(x: f32, y: f32, u: u32, v: u32) -> Vertex { - Vertex { - x: x, - y: y, - u: u, - v: v, - } - } -} - -#[derive(Clone, Copy, Debug)] -struct CachedGlyph(Point2D); - -#[derive(Debug)] -struct CompositeVertexArray { - vertex_array: GLuint, - vertex_buffer: GLuint, - index_buffer: GLuint, -} - -impl CompositeVertexArray { - fn new() -> CompositeVertexArray { - let (mut vertex_array, mut vertex_buffer, mut index_buffer) = (0, 0, 0); - - unsafe { - gl::GenVertexArrays(1, &mut vertex_array); - gl::GenBuffers(1, &mut vertex_buffer); - gl::GenBuffers(1, &mut index_buffer); - } - - CompositeVertexArray { - vertex_array: vertex_array, - vertex_buffer: vertex_buffer, - index_buffer: index_buffer, - } - } -} - -struct RedrawResult { - events: Option, - glyphs_drawn: u32, -} - -fn create_program(name: &str) -> GLuint { - unsafe { - let (mut vertex_shader_source, mut fragment_shader_source) = (vec![], vec![]); - File::open(&format!("{}/{}.vs.glsl", - EXAMPLE_SHADER_PATH, - name)).unwrap().read_to_end(&mut vertex_shader_source).unwrap(); - File::open(&format!("{}/{}.fs.glsl", - EXAMPLE_SHADER_PATH, - name)).unwrap().read_to_end(&mut fragment_shader_source).unwrap(); - - let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); - let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); - gl::ShaderSource(vertex_shader, - 1, - &(vertex_shader_source.as_ptr() as *const u8 as *const GLchar), - &(vertex_shader_source.len() as GLint)); - gl::ShaderSource(fragment_shader, - 1, - &(fragment_shader_source.as_ptr() as *const u8 as *const GLchar), - &(fragment_shader_source.len() as GLint)); - gl::CompileShader(vertex_shader); - gl::CompileShader(fragment_shader); - - let program = gl::CreateProgram(); - gl::AttachShader(program, vertex_shader); - gl::AttachShader(program, fragment_shader); - gl::LinkProgram(program); - program - } -} - -fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D) -> (Image, GLuint) { - let compute_image = rasterizer.device().create_image(Format::RGBA8, - buffer::Protection::ReadWrite, - &atlas_size).unwrap(); - - rasterizer.queue().submit_clear(&compute_image, &Color::UInt(0, 0, 0, 0), &[]).unwrap(); - - let mut gl_texture = 0; - unsafe { - gl::GenTextures(1, &mut gl_texture); - compute_image.bind_to(&ExternalImage::GlTexture(gl_texture)).unwrap(); - - gl::BindTexture(gl::TEXTURE_RECTANGLE, gl_texture); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint); - } - - (compute_image, gl_texture) -} - diff --git a/pathfinder-classic/resources/examples/lorem-ipsum/composite.fs.glsl b/pathfinder-classic/resources/examples/lorem-ipsum/composite.fs.glsl deleted file mode 100644 index 73017889..00000000 --- a/pathfinder-classic/resources/examples/lorem-ipsum/composite.fs.glsl +++ /dev/null @@ -1,16 +0,0 @@ -#version 330 - -uniform sampler2DRect uAtlas; -uniform vec3 uForegroundColor; -uniform vec3 uBackgroundColor; - -in vec2 vTexCoord; - -out vec4 oFragColor; - -void main() { - vec3 value = texture(uAtlas, vTexCoord).rgb; - vec3 color = mix(uBackgroundColor, uForegroundColor, value); - oFragColor = vec4(color, 1.0f); -} - diff --git a/pathfinder-classic/resources/examples/lorem-ipsum/composite.vs.glsl b/pathfinder-classic/resources/examples/lorem-ipsum/composite.vs.glsl deleted file mode 100644 index 6d53749b..00000000 --- a/pathfinder-classic/resources/examples/lorem-ipsum/composite.vs.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version 330 - -uniform mat2 uTransform; -uniform vec2 uTranslation; - -in vec2 aPosition; -in vec2 aTexCoord; - -out vec2 vTexCoord; - -void main() { - vTexCoord = aTexCoord; - gl_Position = vec4(uTransform * aPosition + uTranslation, 0.0f, 1.0f); -} - diff --git a/pathfinder-classic/resources/examples/lorem-ipsum/default.txt b/pathfinder-classic/resources/examples/lorem-ipsum/default.txt deleted file mode 100644 index 92dc8fde..00000000 --- a/pathfinder-classic/resources/examples/lorem-ipsum/default.txt +++ /dev/null @@ -1,34 +0,0 @@ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur scelerisque pellentesque risus -quis vehicula. Ut sollicitudin aliquet diam, vel lobortis orci porta in. Sed eu nisi egestas odio -tincidunt cursus eget ut lorem. Fusce lacinia ex nec lectus rutrum mollis. Donec in ultrices -purus. Integer id suscipit magna. Suspendisse congue pulvinar neque id ultrices. Curabitur nec -tellus et est pellentesque posuere. Duis ut metus euismod, feugiat arcu vitae, posuere libero. -Curabitur nunc urna, rhoncus vitae scelerisque quis, viverra et odio. Suspendisse accumsan -pretium mi, nec fringilla metus condimentum id. Duis dignissim quam eu felis lobortis, eget -dignissim lectus fermentum. Nunc et massa id orci pellentesque rutrum. Nam imperdiet quam vel -ligula efficitur ultricies vel eu tellus. Maecenas luctus risus a erat euismod ultricies. -Pellentesque neque mauris, laoreet vitae finibus quis, molestie ut velit. Donec laoreet justo -risus. In id mi sed odio placerat interdum ut vitae erat. Fusce quis mollis mauris, sit amet -efficitur libero. In efficitur tortor nulla, sollicitudin sodales mi tempor in. In egestas -ultrices fermentum. Quisque mattis egestas nulla. Interdum et malesuada fames ac ante ipsum -primis in faucibus. Etiam in tempus sapien, in dignissim arcu. Quisque diam nulla, rhoncus et -tempor nec, facilisis porta purus. Nulla ut eros laoreet, placerat dolor ut, interdum orci. Sed -posuere eleifend mollis. Integer at nunc ex. Vestibulum aliquet risus quis lacinia convallis. -Fusce et metus viverra, varius nulla in, rutrum justo. Interdum et malesuada fames ac ante ipsum -primis in faucibus. Praesent non est vel lectus suscipit malesuada id ut nisl. Aenean sem ipsum, -tincidunt non orci non, varius consectetur purus. Aenean sed mollis turpis, sit amet vestibulum -risus. Nunc ut hendrerit urna, sit amet lacinia arcu. Curabitur laoreet a enim et eleifend. Etiam -consectetur pharetra massa, sed elementum quam molestie nec. Integer eu justo lectus. Vestibulum -sed vulputate sapien. Curabitur pretium luctus orci et interdum. Quisque ligula nisi, varius id -sodales id, volutpat et lorem. Pellentesque ex urna, malesuada at ex non, elementum ultricies -nulla. Nunc sodales, turpis at maximus bibendum, neque lorem laoreet felis, eget convallis sem -mauris ac quam. Mauris non pretium nulla. Nam semper pulvinar convallis. Suspendisse ultricies -odio vitae tortor congue, rutrum finibus nisl malesuada. Interdum et malesuada fames ac ante -ipsum primis in faucibus. Vestibulum aliquam et lacus sit amet lobortis. In sed ligula quis urna -accumsan vehicula sit amet id magna. Cras mollis orci vitae turpis porta, sed gravida nunc -aliquam. Phasellus nec facilisis nunc. Suspendisse volutpat leo felis, in iaculis nisi dignissim -et. Phasellus at urna purus. Nullam vitae metus ante. Praesent porttitor libero quis velit -fermentum rhoncus. Cras vitae rhoncus nulla. In efficitur risus sapien, sed viverra neque -scelerisque at. Morbi fringilla odio massa. Donec tincidunt magna diam, eget congue leo tristique -eget. Cras et sapien nulla. - diff --git a/pathfinder-classic/resources/examples/lorem-ipsum/solid_color.fs.glsl b/pathfinder-classic/resources/examples/lorem-ipsum/solid_color.fs.glsl deleted file mode 100644 index 7c1742b2..00000000 --- a/pathfinder-classic/resources/examples/lorem-ipsum/solid_color.fs.glsl +++ /dev/null @@ -1,10 +0,0 @@ -#version 330 - -uniform vec3 uColor; - -out vec4 oFragColor; - -void main() { - oFragColor = vec4(uColor, 1.0f); -} - diff --git a/pathfinder-classic/resources/examples/lorem-ipsum/solid_color.vs.glsl b/pathfinder-classic/resources/examples/lorem-ipsum/solid_color.vs.glsl deleted file mode 100644 index 35067e51..00000000 --- a/pathfinder-classic/resources/examples/lorem-ipsum/solid_color.vs.glsl +++ /dev/null @@ -1,8 +0,0 @@ -#version 330 - -in vec2 aPosition; - -void main() { - gl_Position = vec4(aPosition, 0.0f, 1.0f); -} - diff --git a/pathfinder-classic/resources/shaders/accum.cs.glsl b/pathfinder-classic/resources/shaders/accum.cs.glsl deleted file mode 100644 index 9b9029bc..00000000 --- a/pathfinder-classic/resources/shaders/accum.cs.glsl +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Computes total coverage and writes into the output atlas. -// -// This proceeds top to bottom for better data locality. For details on the algorithm, see [1]. -// -// [1]: https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445 - -layout(local_size_x = 1024) in; - -layout(IMAGE_FORMAT, binding = 0) uniform restrict writeonly image2DRect uImage; -layout(r32f, binding = 1) uniform restrict readonly image2DRect uCoverage; -layout(location = 2) uniform uvec4 uAtlasRect; -layout(location = 3) uniform uint uAtlasShelfHeight; - -void main() { - // Determine the boundaries of the column we'll be traversing. - uint atlasWidth = uAtlasRect.z - uAtlasRect.x; - uint column = gl_GlobalInvocationID.x % atlasWidth; - uint shelfIndex = gl_GlobalInvocationID.x / atlasWidth; - uint firstRow = shelfIndex * uAtlasShelfHeight; - uint lastRow = (shelfIndex + 1u) * uAtlasShelfHeight; - - uint atlasHeight = uAtlasRect.w - uAtlasRect.y; - if (firstRow >= atlasHeight) - return; - - // Sweep down the column, accumulating coverage as we go. - float coverage = 0.0f; - for (uint row = firstRow; row < lastRow; row++) { - ivec2 coord = ivec2(column, row); - coverage += imageLoad(uCoverage, coord).r; - imageStore(uImage, coord + ivec2(uAtlasRect.xy), vec4(coverage, coverage, coverage, 1.0)); - } -} diff --git a/pathfinder-classic/resources/shaders/accum_common.cl b/pathfinder-classic/resources/shaders/accum_common.cl deleted file mode 100644 index 1e72c629..00000000 --- a/pathfinder-classic/resources/shaders/accum_common.cl +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Common functions for all accumulation operations. - -// Determines the boundaries of the column we'll be traversing. -void get_location(__private uint *lColumn, - __private uint *lFirstRow, - __private uint *lLastRow, - uint4 kAtlasRect, - uint kAtlasShelfHeight) { - uint atlasWidth = kAtlasRect.z - kAtlasRect.x, atlasHeight = kAtlasRect.w - kAtlasRect.y; - uint shelfIndex = get_global_id(0) / atlasWidth; - - *lColumn = get_global_id(0) % atlasWidth; - *lFirstRow = min(shelfIndex * kAtlasShelfHeight, atlasHeight); - *lLastRow = min((shelfIndex + 1) * kAtlasShelfHeight, atlasHeight); -} - diff --git a/pathfinder-classic/resources/shaders/accum_common.cs.glsl b/pathfinder-classic/resources/shaders/accum_common.cs.glsl deleted file mode 100644 index f265eac9..00000000 --- a/pathfinder-classic/resources/shaders/accum_common.cs.glsl +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Placeholder file. -// -// TODO(pcwalton): Fill this in. - diff --git a/pathfinder-classic/resources/shaders/accum_gray.cl b/pathfinder-classic/resources/shaders/accum_gray.cl deleted file mode 100644 index 1c6cd7e7..00000000 --- a/pathfinder-classic/resources/shaders/accum_gray.cl +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Computes total coverage and writes into the output atlas for grayscale antialiasing. -// -// This proceeds top to bottom for better data locality. For details on the algorithm, see [1]. -// -// [1]: https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445 - -const sampler_t SAMPLER = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST; - -__kernel void accum_gray(__write_only image2d_t gImage, - __read_only image2d_t gCoverage, - uint4 kAtlasRect, - uint kAtlasShelfHeight) { - // Determine the boundaries of the column we'll be traversing. - uint column = 0, firstRow = 0, lastRow = 0; - get_location(&column, &firstRow, &lastRow, kAtlasRect, kAtlasShelfHeight); - - // Sweep down the column, accumulating coverage as we go. - float coverage = 0.0f; - for (uint row = firstRow; row < lastRow; row++) { - int2 coord = (int2)((int)column, (int)row); - coverage += read_imagef(gCoverage, SAMPLER, coord).r; - - float gray = fabs(coverage); - write_imagef(gImage, coord + (int2)kAtlasRect.xy, (float4)(gray, gray, gray, 1.0f)); - } -} - diff --git a/pathfinder-classic/resources/shaders/accum_subpixel.cl b/pathfinder-classic/resources/shaders/accum_subpixel.cl deleted file mode 100644 index e4f83519..00000000 --- a/pathfinder-classic/resources/shaders/accum_subpixel.cl +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -// Computes total coverage and writes into the output atlas for subpixel antialiasing. -// -// This proceeds top to bottom for better data locality. For details on the algorithm, see [1]. -// -// [1]: https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445 - -const sampler_t SAMPLER = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST; - -__kernel void accum_subpixel(__write_only image2d_t gImage, - __read_only image2d_t gCoverage, - uint4 kAtlasRect, - uint kAtlasShelfHeight) { - // Determine the boundaries of the column we'll be traversing. - uint column = 0, firstRow = 0, lastRow = 0; - get_location(&column, &firstRow, &lastRow, kAtlasRect, kAtlasShelfHeight); - - // Sweep down the column, accumulating coverage as we go. - float3 coverage = (float3)(0.0f, 0.0f, 0.0f); - for (uint row = firstRow; row < lastRow; row++) { - int coverageColumn = (int)(column * 3); - coverage.r += read_imagef(gCoverage, SAMPLER, (int2)(coverageColumn, (int)row)).r; - coverage.g += read_imagef(gCoverage, SAMPLER, (int2)(coverageColumn + 1, (int)row)).r; - coverage.b += read_imagef(gCoverage, SAMPLER, (int2)(coverageColumn + 2, (int)row)).r; - - int2 coord = (int2)((int)column, (int)row) + (int2)kAtlasRect.xy; - float3 aa = fabs(coverage); - write_imagef(gImage, coord, (float4)(aa, 1.0f)); - } -} - diff --git a/pathfinder-classic/resources/shaders/draw.fs.glsl b/pathfinder-classic/resources/shaders/draw.fs.glsl deleted file mode 100644 index 45e2bb54..00000000 --- a/pathfinder-classic/resources/shaders/draw.fs.glsl +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#version 330 - -// The size of the atlas in pixels. -uniform uvec2 uAtlasSize; - -// The starting point of the segment. -flat in vec2 vP0; -// The endpoint of this segment. -flat in vec2 vP1; -// 1.0 if this segment runs left to right; -1.0 otherwise. -flat in float vDirection; -// The slope of this line. -flat in float vSlope; -// Minimum and maximum vertical extents, unrounded. -flat in vec2 vYMinMax; - -out vec4 oFragColor; - -void main() { - // Compute the X boundaries of this pixel. - float xMin = floor(gl_FragCoord.x); - float xMax = xMin + 1.0f; - - // Compute the horizontal span that the line segment covers across this pixel. - float dX = min(xMax, vP1.x) - max(xMin, vP0.x); - - // Compute the Y-intercepts of the portion of the line crossing this pixel. - float yMin = clamp(vP0.y + (xMin - vP0.x) * vSlope, vYMinMax.x, vYMinMax.y); - float yMax = clamp(yMin + vSlope, vYMinMax.x, vYMinMax.y); - if (yMin > yMax) { - float tmp = yMin; - yMin = yMax; - yMax = tmp; - } - - // Round the Y-intercepts out to the nearest pixel. - int yMinI = int(floor(yMin)), yMaxI = int(ceil(yMax)); - - // Determine which vertical pixel we're looking at. - int yI = int(floor(gl_FragCoord.y)); - - // Compute trapezoidal area coverage. - // - // It may be helpful to follow along with this explanation, keeping in mind that we compute - // downward coverage rather than rightward coverage: - // - // http://nothings.org/gamedev/rasterize/ - // - // Note that the algorithm above computes total area coverage for each pixel, while here we - // compute *delta* coverage: that is, the *difference* in the area covered between this pixel - // and the pixel above it. In general, this means that, in contrast to the stb_truetype - // algorithm, we have to specially handle the first fully covered pixel, in order to account - // for the residual area difference between that pixel and the one above it. - float coverage = 0.0f; - if (yMaxI <= yMinI + 1) { - // The line touches only one pixel (case 1). Compute the area of that trapezoid (or the - // residual area for the pixel right after that trapezoid). - float trapArea = 0.5f * (yMin + yMax) - float(yMinI); - if (yI == yMinI) - coverage = 1.0f - trapArea; - else if (yI == yMinI + 1) - coverage = trapArea; - } else { - // The line touches multiple pixels (case 2). There are several subcases to handle here. - - // Compute the area of the topmost triangle. - float yMinF = fract(yMin); - float dXdY = 1.0f / (yMax - yMin); - float triAreaMin = 0.5f * dXdY * (1.0f - yMinF) * (1.0f - yMinF); - - if (yI == yMinI) { - // We're looking at the pixel that triangle covers, so we're done. - coverage = triAreaMin; - } else { - // Compute the area of the bottommost triangle. - float yMaxF = yMax - ceil(yMax) + 1.0f; - float triAreaMax = 0.5f * dXdY * yMaxF * yMaxF; - - bool lineTouchesThreePixels = yMaxI == yMinI + 2; - if (lineTouchesThreePixels && yI == yMinI + 1) { - // The line touches exactly 3 pixels, and we're looking at the middle one. - coverage = 1.0f - triAreaMin - triAreaMax; - } else if (!lineTouchesThreePixels && yI < yMaxI) { - // The line touches more than 3 pixels. Compute the area of the first trapezoid. - float trapAreaMin = dXdY * (1.5f - yMinF); - if (yI == yMinI + 1) { - // We're looking at that trapezoid, so we're done. - coverage = trapAreaMin - triAreaMin; - } else if (yI == yMaxI - 1) { - // We're looking at the last trapezoid. Compute its area. - float trapAreaMax = trapAreaMin + float(yMaxI - yMinI - 3) * dXdY; - coverage = 1.0f - trapAreaMax - triAreaMax; - } else if (yI > yMinI + 1 && yI < yMaxI - 1) { - // We're looking at one of the pixels in between the two trapezoids. The delta - // coverage in this case is simply the inverse slope. - coverage = dXdY; - } - } else if (yI == yMaxI) { - // We're looking at the final pixel in the column. - coverage = triAreaMax; - } - } - } - - oFragColor = vec4(dX * vDirection * coverage, 1.0f, 1.0f, 1.0f); -} - diff --git a/pathfinder-classic/resources/shaders/draw.gs.glsl b/pathfinder-classic/resources/shaders/draw.gs.glsl deleted file mode 100644 index fa796b7b..00000000 --- a/pathfinder-classic/resources/shaders/draw.gs.glsl +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 geometry shader fallback when tessellation is not available. This is *not* linked into the -// program by default. -// -// This will probably not perform well, but it's useful for testing, since llvmpipe does not -// support tessellation as of Jan. 2017. -// -// To use this shader, set `RasterizerOptions::force_geometry_shader` to true or set the -// environment variable `PATHFINDER_FORCE_GEOMETRY_SHADER` to 1. - -#version 330 - -#define CURVE_THRESHOLD 0.333f -#define CURVE_TOLERANCE 3.0f - -#define PIXELS_TO_DEVICE(x, y) (vec2((x), (y)) / vec2(uAtlasSize) * 2.0f - 1.0f) - -#define SET_VARYINGS(primID) \ - vP0 = lP0; \ - vP1 = lP1; \ - vDirection = direction; \ - vSlope = slope; \ - vYMinMax = yMinMax; \ - gl_PrimitiveID = gl_PrimitiveIDIn + (primID) - -layout(triangles) in; -layout(triangle_strip, max_vertices = 256) out; - -// The size of the atlas in pixels. -uniform uvec2 uAtlasSize; - -// The vertex ID, passed into this shader. -flat in int vVertexID[]; - -// The starting point of the segment. -flat out vec2 vP0; -// The endpoint of this segment. -flat out vec2 vP1; -// 1.0 if this segment runs left to right; -1.0 otherwise. -flat out float vDirection; -// The slope of this line. -flat out float vSlope; -// Minimum and maximum vertical extents, unrounded. -flat out vec2 vYMinMax; - -void main() { - vec2 p0 = gl_in[0].gl_Position.xy; - vec2 p1 = gl_in[1].gl_Position.xy; - vec2 p2 = gl_in[2].gl_Position.xy; - - // Compute direction. Flip around if necessary so that p0 is to the left of p2. - float direction; - if (p0.x < p2.x) { - direction = 1.0f; - } else { - direction = -1.0f; - vec2 tmp = p0; - p0 = p2; - p2 = tmp; - } - - // Determine how many lines to divide into. - uint lineCount = 1u; - if (vVertexID[1] > 0) { - // Quadratic curve. - vec2 dev = p0 - 2.0f * p1 + p2; - float devSq = dot(dev, dev); - if (devSq >= CURVE_THRESHOLD) { - // Inverse square root is likely no slower and may be faster than regular square root - // (e.g. on x86). - lineCount += uint(floor(inversesqrt(inversesqrt(CURVE_TOLERANCE * devSq)))); - } - } - - // Divide into lines. - for (uint lineIndex = 0u; lineIndex < lineCount; lineIndex++) { - // Compute our endpoints (trivial if this is part of a line, less trivial if this is part - // of a curve). - vec2 lP0, lP1; - if (lineCount == 1u) { - lP0 = p0; - lP1 = p2; - } else { - float t0 = float(lineIndex + 0u) / float(lineCount); - float t1 = float(lineIndex + 1u) / float(lineCount); - lP0 = mix(mix(p0, p1, t0), mix(p1, p2, t0), t0); - lP1 = mix(mix(p0, p1, t1), mix(p1, p2, t1), t1); - } - - // Compute Y extents and slope. - vec2 yMinMax = lP0.y <= lP1.y ? vec2(lP0.y, lP1.y) : vec2(lP1.y, lP0.y); - float slope = (lP1.y - lP0.y) / (lP1.x - lP0.x); - - // Convert atlas space to device space. - vec2 pTL = PIXELS_TO_DEVICE(floor(lP0.x), floor(yMinMax.x)); - vec2 pBR = PIXELS_TO_DEVICE(ceil(lP1.x), ceil(yMinMax.y) + 1.0f); - vec2 pTR = vec2(pBR.x, pTL.y); - vec2 pBL = vec2(pTL.x, pBR.y); - - // Assign primitive IDs. - int primID0 = int(lineIndex) * 2, primID1 = int(lineIndex) * 2 + 1; - - // Emit vertices. - SET_VARYINGS(primID0); - gl_Position = vec4(pTL, 0.0f, 1.0f); - EmitVertex(); - SET_VARYINGS(primID0); - gl_Position = vec4(pTR, 0.0f, 1.0f); - EmitVertex(); - SET_VARYINGS(primID0); - gl_Position = vec4(pBL, 0.0f, 1.0f); - EmitVertex(); - EndPrimitive(); - SET_VARYINGS(primID1); - gl_Position = vec4(pBR, 0.0f, 1.0f); - EmitVertex(); - SET_VARYINGS(primID1); - gl_Position = vec4(pBL, 0.0f, 1.0f); - EmitVertex(); - SET_VARYINGS(primID1); - gl_Position = vec4(pTR, 0.0f, 1.0f); - EmitVertex(); - EndPrimitive(); - } -} - diff --git a/pathfinder-classic/resources/shaders/draw.tcs.glsl b/pathfinder-classic/resources/shaders/draw.tcs.glsl deleted file mode 100644 index d2d27cd9..00000000 --- a/pathfinder-classic/resources/shaders/draw.tcs.glsl +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#version 410 - -#define CURVE_THRESHOLD 0.333f -#define CURVE_TOLERANCE 3.0f - -layout(vertices = 1) out; - -// The vertex ID, passed into this shader. -flat in int vVertexID[]; - -// These outputs really should be patch outs, but that causes problems in Apple drivers. - -// The starting point of the segment. -out vec2 vpP0[]; -// The first control point, if this is a curve. If this is a line, this value must be ignored. -out vec2 vpP1[]; -// The second control point, if this is a curve. If this is a line, this value must be ignored. -// If this curve is quadratic, this will be the same as `vpP1`. -out vec2 vpP2[]; -// The endpoint of this segment. -out vec2 vpP3[]; -// The tessellation level. -// -// This is passed along explicitly instead of having the TES read it from `gl_TessLevelInner` in -// order to work around an Apple bug in the Radeon driver. -out float vpTessLevel[]; - -void main() { - vec2 p0 = gl_in[0].gl_Position.xy; - vec2 p1 = gl_in[1].gl_Position.xy; - vec2 p2 = gl_in[2].gl_Position.xy; - vec2 p3 = gl_in[3].gl_Position.xy; - - // Divide into lines. - float lineCount = 1.0f; - if (vVertexID[1] > 0) { - // A curve. - // - // FIXME(pcwalton): Is this formula good for cubic curves? - vec2 dev = p0 - 2.0f * mix(p1, p2, 0.5) + p3; - float devSq = dot(dev, dev); - if (devSq >= CURVE_THRESHOLD) { - // Inverse square root is likely no slower and may be faster than regular square - // root - // (e.g. on x86). - lineCount += floor(inversesqrt(inversesqrt(CURVE_TOLERANCE * devSq))); - } - } - - // Tessellate into lines. This is subtle, so a diagram may help. - // - // Suppose we decided to divide this curve up into 4 lines. Then our abstract tessellated patch - // space will look like this: - // - // x₀ x₁ x₂ x₃ x₄ x₅ x₆ x₇ - // ┌──┬──┬──┬──┬──┬──┬──┐ - // │▒▒│ │▒▒│ │▒▒│ │▒▒│ - // │▒▒│ │▒▒│ │▒▒│ │▒▒│ - // └──┴──┴──┴──┴──┴──┴──┘ - // - // The shaded areas are the only areas that will actually be drawn. They might look like this: - // - // x₅ - // x₆ x₇ - // x₃ ┌───────┐ - // x₄ │▒▒▒▒▒▒▒│ - // x₁ ┌─────┼───────┘ - // x₂ │▒▒▒▒▒│ - // ┌──┼─────┘ - // │▒▒│ - // │▒▒│ - // x₀ │▒▒│ - // ┌──┼──┘ - // │▒▒│ - // │▒▒│ - // └──┘ - // - // In this way, the unshaded areas become zero-size and are discarded by the rasterizer. - // - // Note that, in reality, it will often be the case that the quads overlap vertically by one - // pixel in the horizontal direction. In fact, this will occur whenever a line segment endpoint - // does not precisely straddle a pixel boundary. However, observe that we can guarantee that - // x₂ ≤ x₁, x₄ ≤ x₃, and so on, because there is never any horizontal space between endpoints. - // This means that all triangles inside the unshaded areas are guaranteed to be wound in the - // opposite direction from those inside the shaded areas. Because the OpenGL spec guarantees - // that, by default, all tessellated triangles are wound counterclockwise in abstract patch - // space, the triangles within the unshaded areas must be wound clockwise and are therefore - // candidates for backface culling. Backface culling is always enabled when running Pathfinder, - // so we're in the clear: the rasterizer will always discard the unshaded areas and render only - // the shaded ones. - - float tessLevel = min(p0.x == p3.x ? 0.0f : (lineCount * 2.0f - 1.0f), 31.0f); - gl_TessLevelInner[0] = tessLevel; - gl_TessLevelInner[1] = 1.0f; - gl_TessLevelOuter[0] = 1.0f; - gl_TessLevelOuter[1] = tessLevel; - gl_TessLevelOuter[2] = 1.0f; - gl_TessLevelOuter[3] = tessLevel; - - // NB: These per-patch outputs must be assigned in this order, or Apple's compiler will - // miscompile us. - vpP0[gl_InvocationID] = p0; - vpP1[gl_InvocationID] = p1; - vpP2[gl_InvocationID] = p2; - vpP3[gl_InvocationID] = p3; - vpTessLevel[gl_InvocationID] = tessLevel; -} - diff --git a/pathfinder-classic/resources/shaders/draw.tes.glsl b/pathfinder-classic/resources/shaders/draw.tes.glsl deleted file mode 100644 index 2224d6b2..00000000 --- a/pathfinder-classic/resources/shaders/draw.tes.glsl +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#version 410 - -layout(quads) in; - -// The size of the atlas in pixels. -uniform uvec2 uAtlasSize; - -// The starting point of the segment. -in vec2 vpP0[]; -// The first control point, if this is a curve. If this is a line, this value must be ignored. -in vec2 vpP1[]; -// The second control point, if this is a cubic curve. If this is a quadratic curve or a line, this -// is equal to `vpP1`. -in vec2 vpP2[]; -// The endpoint of this segment. -in vec2 vpP3[]; -// The tessellation level. -// -// This is passed along explicitly instead of having the TES read it from `gl_TessLevelInner` in -// order to work around an Apple bug in the Radeon driver. -in float vpTessLevel[]; - -// The starting point of the segment. -flat out vec2 vP0; -// The endpoint of this segment. -flat out vec2 vP1; -// 1.0 if this segment runs left to right; -1.0 otherwise. -flat out float vDirection; -// The slope of this line. -flat out float vSlope; -// Minimum and maximum vertical extents, unrounded. -flat out vec2 vYMinMax; - -void main() { - // Read in curve points. - vec2 cP0 = vpP0[0], cP1 = vpP1[0], cP2 = vpP2[0], cP3 = vpP3[0]; - - // Work out how many lines made up this segment, which line we're working on, and which - // endpoint of that line we're looking at. - uint tessPointCount = uint(vpTessLevel[0] + 1.0f); - uint tessIndex = uint(round(gl_TessCoord.x * float(tessPointCount - 1))); - uint lineCount = tessPointCount / 2, lineIndex = tessIndex / 2, endpoint = tessIndex % 2; - - // Compute our endpoints (trivial if this is part of a line, less trivial if this is part of a - // curve). - vec2 p0, p1; - if (lineCount == 1) { - p0 = cP0; - p1 = cP3; - } else { - float t0 = float(lineIndex + 0) / float(lineCount); - float t1 = float(lineIndex + 1) / float(lineCount); - - // These lerps are needed both for quadratic and cubic Béziers. - vec2 pP0P1T0 = mix(cP0, cP1, t0), pP0P1T1 = mix(cP0, cP1, t1); - vec2 pP2P3T0 = mix(cP2, cP3, t0), pP2P3T1 = mix(cP2, cP3, t1); - - if (cP1 == cP2) { - // Quadratic Bézier. - p0 = mix(pP0P1T0, pP2P3T0, t0); - p1 = mix(pP0P1T1, pP2P3T1, t1); - } else { - // Cubic Bézier. - vec2 pP1P2T0 = mix(cP1, cP2, t0), pP1P2T1 = mix(cP1, cP2, t1); - p0 = mix(mix(pP0P1T0, pP1P2T0, t0), mix(pP1P2T0, pP2P3T0, t0), t0); - p1 = mix(mix(pP0P1T1, pP1P2T1, t1), mix(pP1P2T1, pP2P3T1, t1), t1); - } - } - - // Compute direction. Flip the two points around so that p0 is on the left and p1 is on the - // right if necessary. - float direction; - if (p0.x < p1.x) { - direction = 1.0f; - } else { - direction = -1.0f; - vec2 tmp = p0; - p0 = p1; - p1 = tmp; - } - - // Forward points and direction onto the fragment shader. - vP0 = p0; - vP1 = p1; - vDirection = direction; - - // Compute Y extents and slope. - vSlope = (p1.y - p0.y) / (p1.x - p0.x); - vYMinMax = p0.y <= p1.y ? vec2(p0.y, p1.y) : vec2(p1.y, p0.y); - - // Compute our final position in atlas space, rounded out to the next pixel. - float x = endpoint == 0 ? floor(p0.x) : ceil(p1.x); - float y = gl_TessCoord.y == 0.0f ? floor(vYMinMax.x) : ceil(vYMinMax.y) + 1.0f; - - // Convert atlas space to device space. - gl_Position = vec4(vec2(x, y) / vec2(uAtlasSize) * 2.0f - 1.0f, 0.0f, 1.0f); -} - diff --git a/pathfinder-classic/resources/shaders/draw.vs.glsl b/pathfinder-classic/resources/shaders/draw.vs.glsl deleted file mode 100644 index f14b30c2..00000000 --- a/pathfinder-classic/resources/shaders/draw.vs.glsl +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#version 330 - -#define MAX_GLYPHS 2048 - -// Accessors to work around Apple driver bugs. -#define GLYPH_DESCRIPTOR_UNITS_PER_EM(d) (d).misc.x -#define IMAGE_DESCRIPTOR_ATLAS_POS(d) (d).xy -#define IMAGE_DESCRIPTOR_POINT_SIZE(d) (d).z - -// Information about the metrics of each glyph. -struct GlyphDescriptor { - // The left/bottom/right/top offsets of the glyph from point (0, 0) in glyph space. - ivec4 extents; - // x: Units per em. - uvec4 misc; -}; - -// The size of the atlas in pixels. -uniform uvec2 uAtlasSize; - -// Whether subpixel antialiasing is in use. -uniform bool uSubpixelAA; - -layout(std140) uniform ubGlyphDescriptors { - GlyphDescriptor uGlyphs[MAX_GLYPHS]; -}; - -layout(std140) uniform ubImageDescriptors { - vec4 uImages[MAX_GLYPHS]; -}; - -// The position of each vertex in glyph space. -in ivec2 aPosition; - -// Which glyph the vertex belongs to. -in uint aGlyphIndex; - -// The vertex ID, passed along onto the TCS. -flat out int vVertexID; - -void main() { - vVertexID = gl_VertexID; - - vec4 image = uImages[aGlyphIndex]; - GlyphDescriptor glyph = uGlyphs[aGlyphIndex]; - - vec2 glyphPos = vec2(aPosition.x - glyph.extents.x, glyph.extents.w - aPosition.y); - float pointSize = IMAGE_DESCRIPTOR_POINT_SIZE(image); - vec2 glyphPxPos = glyphPos * pointSize / GLYPH_DESCRIPTOR_UNITS_PER_EM(glyph); - vec2 atlasPos = glyphPxPos + IMAGE_DESCRIPTOR_ATLAS_POS(image); - if (uSubpixelAA) - atlasPos.x *= 3.0f; - - gl_Position = vec4(atlasPos, 0.0f, 1.0f); -} - diff --git a/pathfinder-classic/resources/shaders/preamble.cs.glsl b/pathfinder-classic/resources/shaders/preamble.cs.glsl deleted file mode 100644 index bb84d235..00000000 --- a/pathfinder-classic/resources/shaders/preamble.cs.glsl +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#version 330 -#extension GL_ARB_compute_shader : require -#extension GL_ARB_explicit_uniform_location : require -#extension GL_ARB_shader_image_load_store : require -#extension GL_ARB_shader_storage_buffer_object : require -#extension GL_ARB_shading_language_420pack : require - diff --git a/pathfinder-classic/resources/tests/nimbus-sans/COPYING b/pathfinder-classic/resources/tests/nimbus-sans/COPYING deleted file mode 100644 index d60c31a9..00000000 --- a/pathfinder-classic/resources/tests/nimbus-sans/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/pathfinder-classic/resources/tests/nimbus-sans/NimbusSanL-Regu.ttf b/pathfinder-classic/resources/tests/nimbus-sans/NimbusSanL-Regu.ttf deleted file mode 100644 index b0cb40419adae75189a279a1a1aed20fc1b5a94b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106572 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Atlases, which hold rendered glyphs on the GPU. - -use error::GlError; -use euclid::Point2D; -use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid}; -use gl; -use outline::Outlines; -use rect_packer::RectPacker; -use std::mem; -use std::os::raw::c_void; -use std::u16; - -/// Places glyphs in an atlas. -/// -/// Atlases are composed of vertically-stacked "shelves" of uniform height. No glyphs may cross -/// shelves. Therefore, the shelf height must be tall enough to encompass all of the glyphs you -/// wish to render into the atlas. -/// -/// Typically, when using Pathfinder, you first create an atlas builder, place all the glyphs into -/// it, generate the atlas, and then pass that glyph to a rasterizer for rendering on the GPU. -/// Afterward, you can retrieve the positions of each glyph in the atlas for final composition to -/// the screen. -pub struct AtlasBuilder { - rect_packer: RectPacker, - batch_builders: Vec, - options: AtlasOptions, -} - -impl AtlasBuilder { - /// Constructs a new atlas builder with the given options. - /// - /// At least `available_width` and `shelf_height` must be set in the options. - /// - /// The width can be any value at least as large as all glyphs in the font. It is recommended - /// to keep it fairly large in order to make efficient use of the space: 1024 or 2048 is a good - /// choice on modern GPUs. - /// - /// The shelf height should be the maximum of all minimum shelf heights for all fonts you wish - /// to render into the atlas. You can retrive the minimum shelf height for a font with the - /// `Font::shelf_height()` method. - /// - /// Remember to set the `subpixel_antialiasing` field to true if you're using subpixel AA. - #[inline] - pub fn new(options: &AtlasOptions) -> AtlasBuilder { - AtlasBuilder { - rect_packer: RectPacker::new(options.available_width, options.shelf_height), - batch_builders: vec![], - options: *options, - } - } - - /// Places a glyph into the atlas. - /// - /// The glyph is supplied as an *index* into the supplied outline buffer. Note that indices are - /// separate from IDs; the indices are returned from each call to - /// `OutlineBuilder::add_glyph()`. - /// - /// Returns the subpixel origin of the glyph in the atlas if successful or an error if there is - /// no space left for the glyph. - pub fn pack_glyph(&mut self, - outlines: &Outlines, - glyph_index: u16, - options: &GlyphRasterizationOptions) - -> Result, ()> { - let mut subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, options.point_size); - subpixel_bounds.left += options.horizontal_offset; - subpixel_bounds.right += options.horizontal_offset; - - let pixel_bounds = subpixel_bounds.round_out(); - let atlas_origin = try!(self.rect_packer.pack(&pixel_bounds.size().cast().unwrap())); - - for batch_builder in &mut self.batch_builders { - if let Ok(atlas_origin) = batch_builder.add_glyph(outlines, - &atlas_origin, - glyph_index, - options) { - return Ok(atlas_origin) - } - } - - let mut batch_builder = BatchBuilder::new(); - let atlas_origin = try!(batch_builder.add_glyph(outlines, - &atlas_origin, - glyph_index, - options)); - self.batch_builders.push(batch_builder); - Ok(atlas_origin) - } - - /// Creates an atlas by uploading the atlas info to the GPU. - pub fn create_atlas(self) -> Result { - let mut batches = vec![]; - for batch_builder in self.batch_builders.into_iter() { - batches.push(try!(batch_builder.create_batch())) - } - - Ok(Atlas { - batches: batches, - shelf_height: self.rect_packer.shelf_height(), - shelf_columns: self.rect_packer.shelf_columns(), - options: self.options, - }) - } -} - -struct BatchBuilder { - image_descriptors: Vec, - image_metadata: Vec, -} - -impl BatchBuilder { - fn new() -> BatchBuilder { - BatchBuilder { - image_descriptors: vec![], - image_metadata: vec![], - } - } - - fn add_glyph(&mut self, - outlines: &Outlines, - atlas_origin: &Point2D, - glyph_index: u16, - options: &GlyphRasterizationOptions) - -> Result, ()> { - // Check to see if we're already rendering this glyph. - let image_index = glyph_index as usize; - if let Some(image_descriptor) = self.image_descriptors.get(image_index) { - if image_descriptor.point_size == options.point_size && - self.image_metadata[image_index] - .horizontal_offset == options.horizontal_offset { - // Glyph is already present. - return Ok(Point2D::new(image_descriptor.atlas_x, image_descriptor.atlas_y)) - } else { - // Glyph is present at a different font size. We need a new batch. - return Err(()) - } - } - - let subpixel_bounds = outlines.glyph_subpixel_bounds(glyph_index, options.point_size); - let glyph_id = outlines.glyph_id(glyph_index); - - while self.image_descriptors.len() < image_index + 1 { - self.image_descriptors.push(ImageDescriptor::default()); - self.image_metadata.push(ImageMetadata::default()); - } - - let units_per_em = outlines.glyph_units_per_em(glyph_index) as f32; - let horizontal_px_offset = options.horizontal_offset / units_per_em * options.point_size; - - let atlas_origin = Point2D::new( - atlas_origin.x as f32 + subpixel_bounds.left.fract() + horizontal_px_offset, - atlas_origin.y as f32 + 1.0 - subpixel_bounds.top.fract()); - self.image_descriptors[image_index] = ImageDescriptor { - atlas_x: atlas_origin.x, - atlas_y: atlas_origin.y, - point_size: options.point_size, - pad: 0.0, - }; - - self.image_metadata[image_index] = ImageMetadata { - glyph_index: glyph_index as u32, - glyph_id: glyph_id, - horizontal_offset: options.horizontal_offset, - start_index: outlines.descriptor(glyph_index).unwrap().start_index(), - end_index: match outlines.descriptor(glyph_index + 1) { - Some(descriptor) => descriptor.start_index() as u32, - None => outlines.indices_count() as u32, - }, - }; - - Ok(atlas_origin) - } - - /// Uploads this batch data to the GPU. - fn create_batch(self) -> Result { - let (mut current_range, mut counts, mut start_indices) = (None, vec![], vec![]); - for image_metadata in &self.image_metadata { - let (start_index, end_index) = (image_metadata.start_index, image_metadata.end_index); - if start_index == 0 { - continue - } - - match current_range { - Some((current_first, current_last)) if start_index == current_last => { - current_range = Some((current_first, end_index)) - } - Some((current_first, current_last)) => { - counts.push((current_last - current_first) as GLsizei); - start_indices.push((current_first as usize) * mem::size_of::()); - current_range = Some((start_index, end_index)) - } - None => current_range = Some((start_index, end_index)), - } - } - if let Some((current_first, current_last)) = current_range { - counts.push((current_last - current_first) as GLsizei); - start_indices.push((current_first as usize) * mem::size_of::()); - } - - // TODO(pcwalton): Try using `glMapBuffer` here. - unsafe { - let mut images = 0; - gl::GenBuffers(1, &mut images); - - let length = self.image_descriptors.len() * mem::size_of::(); - let ptr = self.image_descriptors.as_ptr() as *const ImageDescriptor as *const c_void; - gl::BindBuffer(gl::UNIFORM_BUFFER, images); - gl::BufferData(gl::UNIFORM_BUFFER, length as GLsizeiptr, ptr, gl::DYNAMIC_DRAW); - - Ok(Batch { - images_buffer: images, - start_indices: start_indices, - counts: counts, - }) - } - } -} - -/// An atlas holding rendered glyphs on the GPU. -pub struct Atlas { - batches: Vec, - shelf_height: u32, - shelf_columns: u32, - options: AtlasOptions, -} - -impl Atlas { - #[doc(hidden)] - pub unsafe fn draw(&self, primitive: GLenum) { - for batch in &self.batches { - batch.draw(primitive) - } - } - - /// Returns the width of this atlas in pixels. - #[inline] - pub fn width(&self) -> u32 { - self.options.available_width - } - - /// Returns the height of each shelf. - #[inline] - pub fn shelf_height(&self) -> u32 { - self.shelf_height - } - - #[doc(hidden)] - #[inline] - pub fn shelf_columns(&self) -> u32 { - self.shelf_columns - } - - #[doc(hidden)] - #[inline] - pub fn uses_subpixel_antialiasing(&self) -> bool { - self.options.subpixel_antialiasing - } - - /// Returns true if this atlas has no glyphs of nonzero size in it. - #[inline] - pub fn is_empty(&self) -> bool { - self.shelf_columns == 0 - } -} - -struct Batch { - images_buffer: GLuint, - start_indices: Vec, - counts: Vec, -} - -impl Drop for Batch { - fn drop(&mut self) { - unsafe { - gl::DeleteBuffers(1, &mut self.images_buffer); - } - } -} - -impl Batch { - unsafe fn draw(&self, primitive: GLenum) { - debug_assert!(self.counts.len() == self.start_indices.len()); - - // The image descriptors are bound to binding point 2. See `draw.vs.glsl`. - gl::BindBufferBase(gl::UNIFORM_BUFFER, 2, self.images_buffer); - - gl::MultiDrawElements(primitive, - self.counts.as_ptr(), - gl::UNSIGNED_INT, - self.start_indices.as_ptr() as *const *const GLvoid, - self.counts.len() as GLsizei); - } -} - -/// Customizable attributes of the atlas. -#[derive(Clone, Copy, Debug, Default)] -pub struct AtlasOptions { - /// The width of the atlas. - /// - /// This width can be any value at least as large as all glyphs in the font. It is recommended - /// to keep it fairly large in order to make efficient use of the space: 1024 or 2048 is a good - /// choice on modern GPUs. - pub available_width: u32, - - /// The height of each shelf in the atlas. - /// - /// The shelf height should be the maximum of all minimum shelf heights for all fonts you wish - /// to render into the atlas. You can retrive the minimum shelf height for a font with the - /// `Font::shelf_height()` method. - pub shelf_height: u32, - - /// Whether subpixel antialiasing should be used. - pub subpixel_antialiasing: bool, -} - -/// Options that control how a glyph is rasterized. -#[derive(Clone, Copy, Debug, Default)] -pub struct GlyphRasterizationOptions { - /// The point size. - pub point_size: f32, - /// How much space we should leave on the left side of the glyph, in pixels. - /// - /// This is useful for rendering at a subpixel offset. - pub horizontal_offset: f32, -} - -// Information about each image that we send to the GPU. -#[repr(C)] -#[doc(hidden)] -#[derive(Clone, Copy, Default, Debug)] -pub struct ImageDescriptor { - atlas_x: f32, - atlas_y: f32, - point_size: f32, - pad: f32, -} - -// Information about each image that we keep around ourselves. -#[doc(hidden)] -#[derive(Clone, Copy, Default, Debug)] -pub struct ImageMetadata { - glyph_index: u32, - start_index: u32, - end_index: u32, - horizontal_offset: f32, - glyph_id: u16, -} - diff --git a/pathfinder-classic/src/charmap.rs b/pathfinder-classic/src/charmap.rs deleted file mode 100644 index db42c754..00000000 --- a/pathfinder-classic/src/charmap.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 font's mapping from Unicode codepoints (characters) to glyphs. -//! -//! Consulting this table is typically the first step when rendering some text. - -/// A consecutive series of Unicode codepoints. -#[derive(Clone, Copy, Debug)] -pub struct CodepointRange { - /// The starting code point, inclusive. - pub start: u32, - /// The ending code point, *inclusive*. - pub end: u32, -} - -/// A collection of Unicode codepoints, organized into consecutive series. -#[derive(Clone, Debug)] -pub struct CodepointRanges { - /// Consecutive series of codepoints. - pub ranges: Vec, -} - -impl CodepointRange { - /// Creates a new codepoint range from the given start and end codepoints, *inclusive*. - #[inline] - pub fn new(start: u32, end: u32) -> CodepointRange { - CodepointRange { - start: start, - end: end, - } - } - - /// Returns an iterator that iterates over all codepoints in this range. - #[inline] - pub fn iter(&self) -> CodepointRangeIter { - CodepointRangeIter { - start: self.start, - end: self.end, - } - } -} - -impl CodepointRanges { - /// Creates an empty set of codepoint ranges. - #[inline] - pub fn empty() -> CodepointRanges { - CodepointRanges { - ranges: vec![], - } - } - - /// Creates codepoint ranges from a sorted array of characters, collapsing duplicates. - /// - /// This is useful when creating an atlas from a string. The array can be readily produced with - /// an expression like `"Hello world".chars().collect()`. - pub fn from_sorted_chars(chars: &[char]) -> CodepointRanges { - let mut ranges: Vec = vec![]; - for &ch in chars { - match ranges.last_mut() { - Some(ref mut range) if range.end == ch as u32 => continue, - Some(ref mut range) if range.end == ch as u32 + 1 => { - range.end += 1; - continue - } - _ => {} - } - ranges.push(CodepointRange::new(ch as u32, ch as u32)) - } - - CodepointRanges { - ranges: ranges, - } - } -} - -/// An iterator over all codepoints in a range. -pub struct CodepointRangeIter { - start: u32, - end: u32, -} - -impl Iterator for CodepointRangeIter { - type Item = u32; - - #[inline] - fn next(&mut self) -> Option { - if self.start > self.end { - None - } else { - let item = self.start; - self.start += 1; - Some(item) - } - } -} - -#[doc(hidden)] -#[derive(Clone, Copy, Debug)] -pub struct GlyphRange { - /// The starting glyph ID in the range, inclusive. - pub start: u16, - /// The ending glyph ID in the range, *inclusive*. - pub end: u16, -} - -#[doc(hidden)] -#[derive(Clone, Copy, Debug)] -pub struct MappedGlyphRange { - pub codepoint_start: u32, - pub glyphs: GlyphRange, -} - -/// A map from Unicode codepoints to glyph IDs. -#[derive(Clone, Debug)] -pub struct GlyphMapping { - ranges: Vec, -} - -impl GlyphMapping { - #[doc(hidden)] - #[inline] - pub fn new() -> GlyphMapping { - GlyphMapping { - ranges: vec![], - } - } - - #[doc(hidden)] - #[inline] - pub fn push(&mut self, range: MappedGlyphRange) { - self.ranges.push(range) - } - - - #[inline] - pub fn iter(&self) -> GlyphMappingIter { - if self.ranges.is_empty() { - return GlyphMappingIter { - start: GlyphRangesIndex { - range_index: 0, - glyph_index: 0, - }, - end: GlyphRangesIndex { - range_index: -1, - glyph_index: 0, - }, - codepoint: 0, - ranges: &self.ranges, - } - } - - GlyphMappingIter { - start: GlyphRangesIndex { - range_index: 0, - glyph_index: self.ranges[0].glyphs.start as i32, - }, - end: GlyphRangesIndex { - range_index: self.ranges.len() as i32 - 1, - glyph_index: self.ranges.last().unwrap().glyphs.end as i32, - }, - codepoint: self.ranges[0].codepoint_start, - ranges: &self.ranges, - } - } - - pub fn glyph_for(&self, codepoint: u32) -> Option { - let (mut lo, mut hi) = (0, self.ranges.len()); - while lo < hi { - let mid = (lo + hi) / 2; - if codepoint < self.ranges[mid].codepoint_start { - hi = mid - } else if codepoint > self.ranges[mid].codepoint_end() { - lo = mid + 1 - } else { - return Some((codepoint - self.ranges[mid].codepoint_start) as u16 + - self.ranges[mid].glyphs.start) - } - } - None - } -} - -#[derive(Clone)] -struct GlyphRangeIter { - start: u16, - end: u16, -} - -impl Iterator for GlyphRangeIter { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.start > self.end { - None - } else { - let item = self.start; - self.start += 1; - Some(item) - } - } -} - -/// An iterator over the codepoint-to-glyph mapping. -/// -/// Every call to `next()` returns a tuple consisting of the codepoint and glyph ID, in that order. -#[derive(Clone)] -pub struct GlyphMappingIter<'a> { - start: GlyphRangesIndex, - end: GlyphRangesIndex, - codepoint: u32, - ranges: &'a [MappedGlyphRange], -} - -impl<'a> Iterator for GlyphMappingIter<'a> { - type Item = (u32, u16); - - #[inline] - fn next(&mut self) -> Option<(u32, u16)> { - if self.start.range_index > self.end.range_index { - return None - } - - let item = (self.codepoint, self.start.glyph_index as u16); - - self.codepoint += 1; - self.start.glyph_index += 1; - - while self.start.glyph_index > self.ranges[self.start.range_index as usize].glyphs.end as - i32 { - self.start.range_index += 1; - if self.start.range_index > self.end.range_index { - break - } - - self.start.glyph_index = self.ranges[self.start.range_index as usize].glyphs.start as - i32; - self.codepoint = self.ranges[self.start.range_index as usize].codepoint_start; - } - - Some(item) - } -} - -#[derive(Clone, Copy, Debug)] -struct GlyphRangesIndex { - range_index: i32, - glyph_index: i32, -} - -impl MappedGlyphRange { - /// Inclusive. - #[inline] - pub fn codepoint_end(&self) -> u32 { - self.codepoint_start + self.glyphs.end as u32 - self.glyphs.start as u32 - } -} diff --git a/pathfinder-classic/src/containers/dfont.rs b/pathfinder-classic/src/containers/dfont.rs deleted file mode 100644 index 3fbba7f5..00000000 --- a/pathfinder-classic/src/containers/dfont.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Apple Font Suitcase (`.dfont`) files. -//! -//! See: https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format - -use byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::Font; -use std::mem; -use util::Jump; - -pub const MAGIC_NUMBER: u32 = 0x0100; - -const SFNT: u32 = ((b's' as u32) << 24) | - ((b'f' as u32) << 16) | - ((b'n' as u32) << 8) | - (b't' as u32); - -impl<'a> Font<'a> { - /// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format - pub fn from_dfont_index<'b>(bytes: &'b [u8], index: u32) -> Result, FontError> { - let mut reader = bytes; - - // Read the Mac resource file header. - let resource_data_offset = try!(reader.read_u32::().map_err(FontError::eof)); - let resource_map_offset = try!(reader.read_u32::().map_err(FontError::eof)); - let _resource_data_size = try!(reader.read_u32::().map_err(FontError::eof)); - let _resource_map_size = try!(reader.read_u32::().map_err(FontError::eof)); - - // Move to the fields we care about in the resource map. - reader = bytes; - try!(reader.jump(resource_map_offset as usize + mem::size_of::() * 5 + - mem::size_of::() * 2).map_err(FontError::eof)); - - // Read the type list and name list offsets. - let type_list_offset = try!(reader.read_u16::().map_err(FontError::eof)); - let _name_list_offset = try!(reader.read_u16::().map_err(FontError::eof)); - - // Move to the type list. - reader = bytes; - try!(reader.jump(resource_map_offset as usize + type_list_offset as usize) - .map_err(FontError::eof)); - - // Find the 'sfnt' type. - let type_count = (try!(reader.read_i16::().map_err(FontError::eof)) + 1) as usize; - let mut resource_count_and_list_offset = None; - for _ in 0..type_count { - let type_id = try!(reader.read_u32::().map_err(FontError::eof)); - let resource_count = try!(reader.read_u16::().map_err(FontError::eof)); - let resource_list_offset = try!(reader.read_u16::().map_err(FontError::eof)); - if type_id == SFNT { - resource_count_and_list_offset = Some((resource_count, resource_list_offset)); - break - } - } - - // Unpack the resource count and list offset. - let resource_count; - match resource_count_and_list_offset { - None => return Err(FontError::Failed), - Some((count, resource_list_offset)) => { - resource_count = count; - reader = bytes; - try!(reader.jump(resource_map_offset as usize + type_list_offset as usize + - resource_list_offset as usize).map_err(FontError::eof)); - } - } - - // Check whether the index is in bounds. - if index >= resource_count as u32 + 1 { - return Err(FontError::FontIndexOutOfBounds) - } - - // Find the font we're interested in. - try!(reader.jump(index as usize * (mem::size_of::() * 2 + mem::size_of::() * 2)) - .map_err(FontError::eof)); - let _sfnt_id = try!(reader.read_u16::().map_err(FontError::eof)); - let _sfnt_name_offset = try!(reader.read_u16::().map_err(FontError::eof)); - let sfnt_data_offset = try!(reader.read_u32::().map_err(FontError::eof)) & - 0x00ffffff; - let _sfnt_ptr = try!(reader.read_u32::().map_err(FontError::eof)); - - // Load the resource. - reader = bytes; - try!(reader.jump(resource_data_offset as usize + sfnt_data_offset as usize) - .map_err(FontError::eof)); - let sfnt_size = try!(reader.read_u32::().map_err(FontError::eof)); - Font::from_otf(&reader[0..sfnt_size as usize], 0) - } -} - diff --git a/pathfinder-classic/src/containers/mod.rs b/pathfinder-classic/src/containers/mod.rs deleted file mode 100644 index 547b88c7..00000000 --- a/pathfinder-classic/src/containers/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 kinds of files that can contain OpenType fonts. - -pub mod dfont; -pub mod otf; -pub mod ttc; -pub mod woff; - diff --git a/pathfinder-classic/src/containers/otf.rs b/pathfinder-classic/src/containers/otf.rs deleted file mode 100644 index 63801384..00000000 --- a/pathfinder-classic/src/containers/otf.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! OpenType `.otf` files. -//! -//! See Microsoft's spec: https://www.microsoft.com/typography/otspec/otff.htm - -use byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::{Font, FontTable}; -use std::mem; -use tables::cff::{self, CffTable}; -use tables::cvt; -use tables::cmap::{self, CmapTable}; -use tables::fpgm; -use tables::glyf::{self, GlyfTable}; -use tables::head::{self, HeadTable}; -use tables::hhea::{self, HheaTable}; -use tables::hmtx::{self, HmtxTable}; -use tables::kern::{self, KernTable}; -use tables::loca::{self, LocaTable}; -use tables::os_2::{self, Os2Table}; -use tables::prep; -use util::Jump; - -const OTTO: u32 = ((b'O' as u32) << 24) | - ((b'T' as u32) << 16) | - ((b'T' as u32) << 8) | - (b'O' as u32); - -pub const KNOWN_TABLE_COUNT: usize = 12; - -pub static KNOWN_TABLES: [u32; KNOWN_TABLE_COUNT] = [ - cff::TAG, - os_2::TAG, - cmap::TAG, - cvt::TAG, - fpgm::TAG, - glyf::TAG, - head::TAG, - hhea::TAG, - hmtx::TAG, - kern::TAG, - loca::TAG, - prep::TAG, -]; - -// This must agree with the above. -const TABLE_INDEX_CFF: usize = 0; -const TABLE_INDEX_OS_2: usize = 1; -const TABLE_INDEX_CMAP: usize = 2; -const TABLE_INDEX_CVT: usize = 3; -const TABLE_INDEX_FPGM: usize = 4; -const TABLE_INDEX_GLYF: usize = 5; -const TABLE_INDEX_HEAD: usize = 6; -const TABLE_INDEX_HHEA: usize = 7; -const TABLE_INDEX_HMTX: usize = 8; -const TABLE_INDEX_KERN: usize = 9; -const TABLE_INDEX_LOCA: usize = 10; -const TABLE_INDEX_PREP: usize = 11; - -pub static SFNT_VERSIONS: [u32; 3] = [ - 0x10000, - ((b't' as u32) << 24) | ((b'r' as u32) << 16) | ((b'u' as u32) << 8) | (b'e' as u32), - OTTO, -]; - -#[doc(hidden)] -pub struct FontTables<'a> { - // Required tables. - pub cmap: CmapTable<'a>, - pub head: HeadTable, - pub hhea: HheaTable, - pub hmtx: HmtxTable<'a>, - pub os_2: Os2Table, - - // Optional tables. - pub cff: Option>, - pub glyf: Option>, - pub loca: Option>, - pub kern: Option>, - - // Optional tables that need no parsing. - pub cvt: Option>, - pub fpgm: Option>, - pub prep: Option>, -} - -impl<'a> Font<'a> { - pub fn from_otf<'b>(bytes: &'b [u8], offset: u32) -> Result, FontError> { - let mut reader = bytes; - try!(reader.jump(offset as usize).map_err(FontError::eof)); - - // Check the magic number. - if !SFNT_VERSIONS.contains(&try!(reader.read_u32::().map_err(FontError::eof))) { - return Err(FontError::UnknownFormat) - } - - let num_tables = try!(reader.read_u16::().map_err(FontError::eof)); - try!(reader.jump(mem::size_of::() * 3).map_err(FontError::eof)); - - let mut tables = [None; KNOWN_TABLE_COUNT]; - for _ in 0..num_tables { - let table_id = try!(reader.read_u32::().map_err(FontError::eof)); - let _checksum = try!(reader.read_u32::().map_err(FontError::eof)); - let offset = try!(reader.read_u32::().map_err(FontError::eof)) as usize; - let length = try!(reader.read_u32::().map_err(FontError::eof)) as usize; - - // Find the table ID in our list of known IDs, which must be sorted. - debug_assert!(KNOWN_TABLES.windows(2).all(|w| w[0] < w[1])); - let slot = match KNOWN_TABLES.binary_search(&table_id) { - Err(_) => continue, - Ok(table_index) => &mut tables[table_index], - }; - - // Make sure there isn't more than one copy of the table. - if slot.is_some() { - return Err(FontError::Failed) - } - - *slot = Some(FontTable { - bytes: &bytes[offset..offset + length], - }) - } - - Font::from_table_list(bytes, &tables) - } - - #[doc(hidden)] - pub fn from_table_list<'b>(bytes: &'b [u8], - tables: &[Option>; KNOWN_TABLE_COUNT]) - -> Result, FontError> { - let cff_table = match tables[TABLE_INDEX_CFF] { - None => None, - Some(cff_table) => Some(try!(CffTable::new(cff_table))), - }; - - let loca_table = match tables[TABLE_INDEX_LOCA] { - None => None, - Some(loca_table) => Some(try!(LocaTable::new(loca_table))), - }; - - // For brevity below… - let missing = FontError::RequiredTableMissing; - - let tables = FontTables { - cmap: CmapTable::new(try!(tables[TABLE_INDEX_CMAP].ok_or(missing))), - head: try!(HeadTable::new(try!(tables[TABLE_INDEX_HEAD].ok_or(missing)))), - hhea: try!(HheaTable::new(try!(tables[TABLE_INDEX_HHEA].ok_or(missing)))), - hmtx: HmtxTable::new(try!(tables[TABLE_INDEX_HMTX].ok_or(missing))), - os_2: try!(Os2Table::new(try!(tables[TABLE_INDEX_OS_2].ok_or(missing)))), - - cff: cff_table, - glyf: tables[TABLE_INDEX_GLYF].map(GlyfTable::new), - loca: loca_table, - kern: tables[TABLE_INDEX_KERN].and_then(|table| KernTable::new(table).ok()), - - cvt: tables[TABLE_INDEX_CVT], - fpgm: tables[TABLE_INDEX_FPGM], - prep: tables[TABLE_INDEX_PREP], - }; - - Ok(Font::from_tables(bytes, tables)) - } -} - diff --git a/pathfinder-classic/src/containers/ttc.rs b/pathfinder-classic/src/containers/ttc.rs deleted file mode 100644 index 820a4ef4..00000000 --- a/pathfinder-classic/src/containers/ttc.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! TrueType Collection (`.ttc`) files. -//! -//! See Microsoft's spec: https://www.microsoft.com/typography/otspec/otff.htm - -use byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::Font; -use std::mem; -use util::Jump; - -pub const MAGIC_NUMBER: u32 = ((b't' as u32) << 24) | - ((b't' as u32) << 16) | - ((b'c' as u32) << 8) | - (b'f' as u32); - -impl<'a> Font<'a> { - /// Creates a new font from a single font within a byte buffer containing the contents of a - /// font collection. - pub fn from_ttc_index<'b>(bytes: &'b [u8], index: u32) -> Result, FontError> { - let mut reader = bytes; - let magic_number = try!(reader.read_u32::().map_err(FontError::eof)); - if magic_number != MAGIC_NUMBER { - return Err(FontError::UnknownFormat) - } - - let major_version = try!(reader.read_u16::().map_err(FontError::eof)); - let minor_version = try!(reader.read_u16::().map_err(FontError::eof)); - if (major_version != 1 && major_version != 2) || minor_version != 0 { - return Err(FontError::UnsupportedVersion) - } - - let num_fonts = try!(reader.read_u32::().map_err(FontError::eof)); - if index >= num_fonts { - return Err(FontError::FontIndexOutOfBounds) - } - - try!(reader.jump(index as usize * mem::size_of::()).map_err(FontError::eof)); - let table_offset = try!(reader.read_u32::().map_err(FontError::eof)); - Font::from_otf(&bytes, table_offset) - } -} - diff --git a/pathfinder-classic/src/containers/woff.rs b/pathfinder-classic/src/containers/woff.rs deleted file mode 100644 index b2dc6134..00000000 --- a/pathfinder-classic/src/containers/woff.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Web Open Font Format 1.0 (`.woff`) files. -//! -//! See the specification: https://www.w3.org/TR/WOFF/ -//! -//! TODO(pcwalton): WOFF 2.0. - -use byteorder::{BigEndian, ReadBytesExt}; -use containers::otf::{KNOWN_TABLES, KNOWN_TABLE_COUNT, SFNT_VERSIONS}; -use error::FontError; -use flate2::FlateReadExt; -use font::{Font, FontTable}; -use std::io::Read; -use std::iter; -use std::mem; -use util::Jump; - -pub const MAGIC_NUMBER: u32 = ((b'w' as u32) << 24) | - ((b'O' as u32) << 16) | - ((b'F' as u32) << 8) | - (b'F' as u32); - -impl<'a> Font<'a> { - /// Creates a new font from a buffer containing data in the WOFF format. - /// - /// The given buffer will be used to decompress data. - /// - /// Decompresses eagerly. - pub fn from_woff<'b>(bytes: &'b [u8], buffer: &'b mut Vec) -> Result, FontError> { - let mut reader = bytes; - - // Check magic number. - let magic_number = try!(reader.read_u32::().map_err(FontError::eof)); - if magic_number != MAGIC_NUMBER { - return Err(FontError::UnknownFormat) - } - - // Check the flavor. - let flavor = try!(reader.read_u32::().map_err(FontError::eof)); - if !SFNT_VERSIONS.contains(&flavor) { - return Err(FontError::UnknownFormat) - } - - // Get the number of tables. - let _length = try!(reader.read_u32::().map_err(FontError::eof)); - let num_tables = try!(reader.read_u16::().map_err(FontError::eof)); - let _reserved = try!(reader.read_u16::().map_err(FontError::eof)); - - // Allocate size for uncompressed tables. - let total_sfnt_size = try!(reader.read_u32::().map_err(FontError::eof)); - try!(reader.jump(mem::size_of::() * 6).map_err(FontError::eof)); - let buffer_start = buffer.len(); - buffer.extend(iter::repeat(0).take(total_sfnt_size as usize)); - let mut buffer = &mut buffer[buffer_start..]; - - // Decompress and load tables as necessary. - let mut tables = [None; KNOWN_TABLE_COUNT]; - for _ in 0..num_tables { - let tag = try!(reader.read_u32::().map_err(FontError::eof)); - let offset = try!(reader.read_u32::().map_err(FontError::eof)); - let comp_length = try!(reader.read_u32::().map_err(FontError::eof)); - let orig_length = try!(reader.read_u32::().map_err(FontError::eof)); - let _orig_checksum = try!(reader.read_u32::().map_err(FontError::eof)); - - // Find the table ID in our list of known IDs, which must be sorted. - debug_assert!(KNOWN_TABLES.windows(2).all(|w| w[0] < w[1])); - let slot = match KNOWN_TABLES.binary_search(&tag) { - Err(_) => continue, - Ok(table_index) => &mut tables[table_index], - }; - - // Make sure there isn't more than one copy of the table. - if slot.is_some() { - return Err(FontError::Failed) - } - - // Allocate space in the buffer. - let comp_end = offset as usize + comp_length as usize; - let mut temp = buffer; // borrow check black magic - let (mut dest, mut rest) = temp.split_at_mut(orig_length as usize); - buffer = rest; - - // Decompress or copy as applicable. - // - // FIXME(pcwalton): Errors here may be zlib errors, not EOFs. - if comp_length != orig_length { - let mut table_reader = bytes; - try!(table_reader.jump(offset as usize).map_err(FontError::eof)); - let mut table_reader = table_reader.zlib_decode(); - try!(table_reader.read_exact(dest).map_err(FontError::eof)); - } else if comp_end <= bytes.len() { - dest.clone_from_slice(&bytes[offset as usize..comp_end]) - } else { - return Err(FontError::UnexpectedEof) - } - - *slot = Some(FontTable { - bytes: dest, - }) - } - - Font::from_table_list(bytes, &tables) - } -} - diff --git a/pathfinder-classic/src/coverage.rs b/pathfinder-classic/src/coverage.rs deleted file mode 100644 index 8de9707b..00000000 --- a/pathfinder-classic/src/coverage.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 intermediate surface on the GPU used during the rasterization process. - -use compute_shader::buffer::Protection; -use compute_shader::device::Device; -use compute_shader::image::{ExternalImage, Format, Image}; -use error::InitError; -use euclid::size::Size2D; -use gl::types::{GLint, GLuint}; -use gl; - -const DEFAULT_COVERAGE_BUFFER_SIZE: u32 = 1024; - -/// An intermediate surface on the GPU used during the rasterization process. -/// -/// You can reuse this surface from draw operation to draw operation. It only needs to be at least -/// as large as every atlas you will draw into it. -/// -/// The GPU memory usage of this buffer is `4 * width * height` bytes. -pub struct CoverageBuffer { - image: Image, - framebuffer: GLuint, -} - -impl CoverageBuffer { - /// Creates a new coverage buffer with the given options. - pub fn new(device: &Device, options: &CoverageBufferOptions) - -> Result { - let mut size = options.size; - if options.subpixel_antialiasing { - size.width *= 3 - } - - let image = try!(device.create_image(Format::R32F, Protection::ReadWrite, &size) - .map_err(InitError::ComputeError)); - - let mut framebuffer = 0; - unsafe { - let mut gl_texture = 0; - gl::GenTextures(1, &mut gl_texture); - try!(image.bind_to(&ExternalImage::GlTexture(gl_texture)) - .map_err(InitError::ComputeError)); - - gl::BindTexture(gl::TEXTURE_RECTANGLE, gl_texture); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, - gl::TEXTURE_WRAP_S, - gl::CLAMP_TO_EDGE as GLint); - gl::TexParameteri(gl::TEXTURE_RECTANGLE, - gl::TEXTURE_WRAP_T, - gl::CLAMP_TO_EDGE as GLint); - - gl::GenFramebuffers(1, &mut framebuffer); - gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer); - gl::FramebufferTexture2D(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_RECTANGLE, - gl_texture, - 0); - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - } - - Ok(CoverageBuffer { - image: image, - framebuffer: framebuffer, - }) - } - - #[doc(hidden)] - #[inline] - pub fn image(&self) -> &Image { - &self.image - } - - #[doc(hidden)] - #[inline] - pub fn framebuffer(&self) -> GLuint { - self.framebuffer - } -} - -impl Drop for CoverageBuffer { - fn drop(&mut self) { - unsafe { - let mut gl_texture = 0; - gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer); - gl::GetFramebufferAttachmentParameteriv(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, - &mut gl_texture as *mut GLuint as *mut GLint); - gl::DeleteTextures(1, &mut gl_texture); - - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::DeleteFramebuffers(1, &mut self.framebuffer); - } - } -} - -/// Options that control the format of the coverage buffer. -#[derive(Clone, Copy, Debug)] -pub struct CoverageBufferOptions { - /// The size of the coverage buffer. - /// - /// The size must be at least as large as every atlas you will render with the buffer. - pub size: Size2D, - - /// Whether this coverage buffer is intended for subpixel antialiasing. - /// - /// If this buffer is intended for subpixel AA, all atlas rendered with it must use subpixel - /// AA, and vice versa. - pub subpixel_antialiasing: bool, -} - -impl Default for CoverageBufferOptions { - #[inline] - fn default() -> CoverageBufferOptions { - CoverageBufferOptions { - size: Size2D::new(DEFAULT_COVERAGE_BUFFER_SIZE, DEFAULT_COVERAGE_BUFFER_SIZE), - subpixel_antialiasing: false, - } - } -} - diff --git a/pathfinder-classic/src/error.rs b/pathfinder-classic/src/error.rs deleted file mode 100644 index 8870eedc..00000000 --- a/pathfinder-classic/src/error.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Errors. - -use compute_shader; -use gl::types::GLenum; -use std::io; - -/// Errors that can occur when parsing OpenType fonts. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum FontError { - /// A miscellaneous error occurred. - Failed, - /// The file ended unexpectedly. - UnexpectedEof, - /// There is no font with this index in this font collection. - FontIndexOutOfBounds, - /// The file declared that it was in a version of the format we don't support. - UnsupportedVersion, - /// The file was of a format we don't support. - UnknownFormat, - /// The font had a glyph format we don't support. - UnsupportedGlyphFormat, - /// We don't support the declared version of the font's CFF outlines. - UnsupportedCffVersion, - /// We don't support the declared version of the font's character map. - UnsupportedCmapVersion, - /// The font character map has an unsupported platform/encoding ID. - UnsupportedCmapEncoding, - /// The font character map has an unsupported format. - UnsupportedCmapFormat, - /// We don't support the declared version of the font header. - UnsupportedHeadVersion, - /// We don't support the declared version of the font's horizontal metrics. - UnsupportedHheaVersion, - /// We don't support the declared version of the font's OS/2 and Windows table. - UnsupportedOs2Version, - /// A required table is missing. - RequiredTableMissing, - /// An integer in a CFF DICT was not found. - CffIntegerNotFound, - /// The CFF Top DICT was not found. - CffTopDictNotFound, - /// A CFF `Offset` value was formatted incorrectly. - CffBadOffset, - /// The CFF evaluation stack overflowed. - CffStackOverflow, - /// An unimplemented CFF CharString operator was encountered. - CffUnimplementedOperator, -} - -impl FontError { - #[doc(hidden)] - #[inline] - pub fn eof(_: T) -> FontError { - FontError::UnexpectedEof - } -} - - -/// An OpenGL error with the given code. -/// -/// You cannot depend on these being reliably returned. Pathfinder does not call `glGetError()` -/// unless necessary, to avoid driver stalls. -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct GlError(pub GLenum); - -/// An initialization error. This could be an OpenGL error or a shader compilation/link error. -#[derive(Debug)] -pub enum InitError { - /// An OpenGL error occurred. - GlError(GlError), - - /// A shader could not be loaded. - ShaderUnreadable(io::Error), - - /// Shader compilation failed. - /// - /// The first string specifies the type of shader (vertex, fragment, etc.); the second holds - /// the error message that the driver returned. - CompileFailed(&'static str, String), - - /// Shader linking failed. - /// - /// The string holds the error message that the driver returned. - LinkFailed(String), - - /// An error occurred setting up GPU compute. - ComputeError(compute_shader::error::Error), - - /// One of the rasterization options had an invalid syntax. - InvalidSetting, -} - -/// A rasterization error. This could be an OpenGL error or a compute error. -#[derive(Debug)] -pub enum RasterError { - /// No glyphs were supplied. - NoGlyphsToDraw, - /// An OpenGL error occurred. - GlError(GlError), - /// An error occurred during GPU compute. - ComputeError(compute_shader::error::Error), - /// An destination image with an unsupported format was supplied. - /// - /// Currently supported formats are R8 and RGBA8. - UnsupportedImageFormat, -} - -/// An error in glyph store creation. See `Typesetter::create_glyph_store()`. -#[derive(Debug)] -pub enum GlyphStoreCreationError { - /// An error occurred when looking up a glyph ID for a character in the font. - FontError(FontError), - /// An error occurred when uploading the outlines to the GPU. - GlError(GlError), -} - -/// An error in construction of a hinter. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum HinterCreationError { - /// A miscellaneous error occurred. - Failed, - /// An error was encountered while analyzing the font program. - FontProgramAnalysisError(HintingAnalysisError), - /// An error was encountered while analyzing the control value program. - ControlValueProgramAnalysisError(HintingAnalysisError), - /// An error was encountered during execution of the font program. - FontProgramExecutionError(HintingExecutionError), -} - -/// An error encountered during parsing of the TrueType hinting bytecode. -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum HintingParseError { - /// The instruction stream terminated normally. - Eof, - /// The instruction stream terminated abnormally. - UnexpectedEof, - /// An unexpected opcode was encountered. - UnknownOpcode(u8), - /// An unexpected value was encountered for `DistanceType`. - InvalidDistanceType, -} - -/// An error encountered during semantic analysis of the TrueType hinting bytecode. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum HintingAnalysisError { - /// An error occurred while parsing the instruction stream. - ParseError(HintingParseError), - /// A branch target (e.g. `Eif`) was found without a corresponding branch instruction. - BranchTargetMissingBranch, - /// A branch target was (e.g. `If`) was found without a corresponding branch target (e.g. - /// `Eif`). - BranchMissingBranchTarget, - /// A branch target was mismatched with its branch instruction (`Eif` vs. `If`, etc.) - MismatchedBranchInstruction, -} - -/// An error encountered during execution of the TrueType hinting bytecode. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum HintingExecutionError { - /// An error occurred while parsing the instruction stream. - ParseError(HintingParseError), - /// An instruction expected more values than were on the stack. - StackUnderflow, - /// An operation tried to read out of bounds of the control value table. - CvtReadOutOfBounds, - /// An undefined function ID was called. - CallToUndefinedFunction, -} - diff --git a/pathfinder-classic/src/font.rs b/pathfinder-classic/src/font.rs deleted file mode 100644 index a1894743..00000000 --- a/pathfinder-classic/src/font.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! OpenType fonts. - -use byteorder::{BigEndian, ReadBytesExt}; -use charmap::{CodepointRange, GlyphMapping}; -use containers::dfont; -use containers::otf::{FontTables, SFNT_VERSIONS}; -use containers::ttc; -use containers::woff; -use error::FontError; -use euclid::Point2D; -use outline::GlyphBounds; -use tables::hmtx::HorizontalMetrics; - -/// A handle to a font backed by a byte buffer containing the contents of the file (`.ttf`, -/// `.otf`), etc. -/// -/// For optimum performance, consider using the `memmap` crate to provide the byte buffer. -pub struct Font<'a> { - pub bytes: &'a [u8], - tables: FontTables<'a>, -} - -#[doc(hidden)] -#[derive(Clone, Copy, Debug)] -pub struct FontTable<'a> { - pub bytes: &'a [u8], -} - -impl<'a> Font<'a> { - #[doc(hidden)] - pub fn from_tables<'b>(bytes: &'b [u8], tables: FontTables<'b>) -> Font<'b> { - Font { - bytes: bytes, - tables: tables, - } - } - - /// Creates a new font from a byte buffer containing the contents of a file or font collection - /// (`.ttf`, `.ttc`, `.otf`, etc.) - /// - /// If this is a `.ttc` or `.dfont` collection, this returns the first font within it. If you - /// want to read another one, use the `Font::from_collection_index` API. - /// - /// The supplied `buffer` is an arbitrary vector that may or may not be used as a temporary - /// storage space. Typically you will want to just pass an empty vector here. - /// - /// Returns the font on success or an error on failure. - pub fn new<'b>(bytes: &'b [u8], buffer: &'b mut Vec) -> Result, FontError> { - Font::from_collection_index(bytes, 0, buffer) - } - - /// Creates a new font from a single font within a byte buffer containing the contents of a - /// file or a font collection (`.ttf`, `.ttc`, `.otf`, etc.) - /// - /// If this is a `.ttc` or `.dfont` collection, this returns the appropriate font within it. - /// - /// The supplied `buffer` is an arbitrary vector that may or may not be used as a temporary - /// storage space. Typically you will want to just pass an empty vector here. - /// - /// Returns the font on success or an error on failure. - pub fn from_collection_index<'b>(bytes: &'b [u8], index: u32, buffer: &'b mut Vec) - -> Result, FontError> { - // Check the magic number. - let mut reader = bytes; - let magic_number = try!(reader.read_u32::().map_err(FontError::eof)); - match magic_number { - ttc::MAGIC_NUMBER => Font::from_ttc_index(bytes, index), - woff::MAGIC_NUMBER => Font::from_woff(bytes, buffer), - dfont::MAGIC_NUMBER => Font::from_dfont_index(bytes, index), - magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes, 0), - _ => Err(FontError::UnknownFormat), - } - } - - /// Returns the glyph IDs that map to the given ranges of Unicode codepoints. - /// - /// The returned glyph ranges are in the same order as the codepoints. - #[inline] - pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) - -> Result { - self.tables.cmap.glyph_mapping_for_codepoint_ranges(codepoint_ranges) - } - - /// Calls the given callback for each point in the supplied glyph's contour. - /// - /// This function is the primary method for accessing a glyph's outline. - #[inline] - pub fn for_each_point(&self, glyph_id: u16, callback: F) -> Result<(), FontError> - where F: FnMut(&Point) { - match (self.tables.glyf, self.tables.cff) { - (Some(glyf), None) => { - let loca = match self.tables.loca { - Some(ref loca) => loca, - None => return Err(FontError::RequiredTableMissing), - }; - - glyf.for_each_point(&self.tables.head, loca, glyph_id, callback) - } - (None, Some(cff)) => cff.for_each_point(glyph_id, callback), - (Some(_), Some(_)) => Err(FontError::Failed), - (None, None) => Ok(()), - } - } - - /// Returns the boundaries of the given glyph in font units. - #[inline] - pub fn glyph_bounds(&self, glyph_id: u16) -> Result { - match (self.tables.glyf, self.tables.cff) { - (Some(glyf), None) => { - let loca = match self.tables.loca { - Some(ref loca) => loca, - None => return Err(FontError::RequiredTableMissing), - }; - - glyf.glyph_bounds(&self.tables.head, loca, glyph_id) - } - (None, Some(cff)) => cff.glyph_bounds(glyph_id), - (Some(_), Some(_)) => Err(FontError::Failed), - (None, None) => Err(FontError::RequiredTableMissing), - } - } - - /// Returns the minimum shelf height that an atlas containing glyphs from this font will need. - #[inline] - pub fn shelf_height(&self, point_size: f32) -> u32 { - // Add 2 to account for the border. - self.tables.head - .max_glyph_bounds - .subpixel_bounds(self.tables.head.units_per_em, point_size) - .round_out() - .size() - .height as u32 + 2 - } - - /// Returns the number of font units per em. - /// - /// An em is traditionally the width of the lowercase letter "m". A typical point size of a - /// font is expressed in number of pixels per em. Thus, in order to convert font units to - /// pixels, you can use an expression like `units * font_size / font.units_per_em()`. - #[inline] - pub fn units_per_em(&self) -> u16 { - self.tables.head.units_per_em - } - - /// Returns the horizontal metrics for the glyph with the given ID. - /// - /// Horizontal metrics are important for text shaping, as they specify the number of units to - /// advance the pen after typesetting a glyph. - #[inline] - pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result { - self.tables.hmtx.metrics_for_glyph(&self.tables.hhea, glyph_id) - } - - /// Returns the kerning between the given two glyph IDs in font units. - /// - /// Positive values move glyphs farther apart; negative values move glyphs closer together. - /// - /// Zero is returned if no kerning is available in the font. - #[inline] - pub fn kerning_for_glyph_pair(&self, left_glyph_id: u16, right_glyph_id: u16) -> i16 { - match self.tables.kern { - None => 0, - Some(kern) => kern.kerning_for_glyph_pair(left_glyph_id, right_glyph_id).unwrap_or(0), - } - } - - /// Returns the distance from the baseline to the top of the text box in font units. - /// - /// The following expression computes the baseline-to-baseline height: - /// `font.ascender() - font.descender() + font.line_gap()`. - #[inline] - pub fn ascender(&self) -> i16 { - self.tables.os_2.typo_ascender - } - - /// Returns the distance from the baseline to the bottom of the text box in font units. - /// - /// The following expression computes the baseline-to-baseline height: - /// `font.ascender() - font.descender() + font.line_gap()`. - #[inline] - pub fn descender(&self) -> i16 { - self.tables.os_2.typo_descender - } - - /// Returns the recommended extra gap between lines in font units. - /// - /// The following expression computes the baseline-to-baseline height: - /// `font.ascender() - font.descender() + font.line_gap()`. - #[inline] - pub fn line_gap(&self) -> i16 { - self.tables.os_2.typo_line_gap - } - - /// Returns the Control Value Table of the font. - #[inline] - pub fn control_value_table(&self) -> &[u8] { - match self.tables.cvt { - None => &[], - Some(cvt) => cvt.bytes, - } - } - - /// Returns the font program, which is run whenever the font is loaded. - #[inline] - pub fn font_program(&self) -> &[u8] { - match self.tables.fpgm { - None => &[], - Some(fpgm) => fpgm.bytes, - } - } - - /// Returns the control value program, which is run whenever the point size changes. - #[inline] - pub fn control_value_program(&self) -> &[u8] { - match self.tables.prep { - None => &[], - Some(prep) => prep.bytes, - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug)] -pub struct Point { - /// Where the point is located in glyph space. - pub position: Point2D, - - /// The index of the point in this contour. - /// - /// When iterating over points via `for_each_point`, a value of 0 here indicates that a new - /// contour begins. - pub index_in_contour: u16, - - /// The kind of point this is. - pub kind: PointKind, -} - -/// The type of point. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum PointKind { - /// The point is on the curve. - OnCurve, - /// The point is a quadratic control point. - QuadControl, - /// The point is the first cubic control point. - FirstCubicControl, - /// The point is the second cubic control point. - SecondCubicControl, -} - diff --git a/pathfinder-classic/src/hinting/insns.rs b/pathfinder-classic/src/hinting/insns.rs deleted file mode 100644 index ad5fe602..00000000 --- a/pathfinder-classic/src/hinting/insns.rs +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! TrueType instructions. - -use error::HintingParseError; -use euclid::Point2D; -use util::{F2DOT14_ONE, F2DOT14_ZERO, F2Dot14}; - -/// All TrueType instructions. -#[derive(Clone, Copy, Debug)] -pub enum Instruction<'a> { - /// Push N Bytes (0x40) (ttinst1.doc, p. 189) - /// Push Bytes (0xb0-0xb7) (ttinst1.doc, p. 191) - Pushb(&'a [u8]), - /// Push N Words (0x41) (ttinst1.doc, p. 190) - /// Push Words (0xb8-0xbf) (ttinst1.doc, p. 192) - Pushw(&'a [u8]), - /// Read Store (0x43) (ttinst1.doc, p. 194) - Rs, - /// Write Store (0x42) (ttinst1.doc, p. 195) - Ws, - /// Write Control Value Table in Pixel Units (0x44) (ttinst1.doc, 197) - Wcvtp, - /// Write Control Value Table in FUints (0x70) (ttinst1.doc, 198) - Wcvtf, - /// Read Control Value Table (0x45) (ttinst1.doc, 199) - Rcvt, - /// Set Freedom and Projection Vectors to Coordinate Axis (0x00-0x01) (ttinst1.doc, 202) - Svtca(Axis), - /// Set Projection Vector to Coordinate Axis (0x02-0x03) (ttinst1.doc, 203) - Spvtca(Axis), - /// Set Freedom Vector to Coordinate Axis (0x04-0x05) (ttinst1.doc, 204) - Sfvtca(Axis), - /// Set Projection Vector to Line (0x06-0x07) (ttinst1.doc, 205-207) - Spvtl(LineOrientation), - /// Set Freedom Vector to Line (0x08-0x09) (ttinst1.doc, 208-209) - Sfvtl(LineOrientation), - /// Set Freedom Vector to Projection Vector (0x0e) (ttinst1.doc, 210) - Sfvtpv, - /// Set Dual Projection Vector to Line (0x86-0x87) (ttinst1.doc, 211) - Sdpvtl(LineOrientation), - /// Set Projection Vector From Stack (0x0a) (ttinst1.doc, 212-213) - Spvfs, - /// Set Freedom Vector From Stack (0x0b) (ttinst1.doc, 214-215) - Sfvfs, - /// Get Projection Vector (0x0c) (ttinst1.doc, 216-217) - Gpv, - /// Get Freedom Vector (0x0d) (ttinst1.doc, 218-219) - Gfv, - /// Set Reference Point 0 (0x10) (ttinst1.doc, 220) - Srp0, - /// Set Reference Point 1 (0x11) (ttinst1.doc, 221) - Srp1, - /// Set Reference Point 2 (0x12) (ttinst1.doc, 222) - Srp2, - /// Set Zone Pointer 0 (0x13) (ttinst1.doc, 223) - Szp0, - /// Set Zone Pointer 1 (0x14) (ttinst1.doc, 224) - Szp1, - /// Set Zone Pointer 2 (0x15) (ttinst1.doc, 225) - Szp2, - /// Set Zone Pointers (0x16) (ttinst1.doc, 226) - Szps, - /// Round to Half Grid (0x19) (ttinst1.doc, 227) - Rthg, - /// Round to Grid (0x18) (ttinst1.doc, 228) - Rtg, - /// Round to Double Grid (0x3d) (ttinst1.doc, 229) - Rtdg, - /// Round Down to Grid (0x7d) (ttinst1.doc, 230) - Rdtg, - /// Round Up to Grid (0x7c) (ttinst1.doc, 231) - Rutg, - /// Round Off (0x7a) (ttinst1.doc, 232) - Roff, - /// Super Round (0x76) (ttinst1.doc, 233-238) - Sround, - /// Super Round 45 Degrees (0x77) (ttinst1.doc, 239) - S45round, - /// Set Loop Variable (0x17) (ttinst1.doc, 240) - Sloop, - /// Set Minimum Distance (0x1a) (ttinst1.doc, 241) - Smd, - /// Instruction Execution Control (0x8e) (ttinst1.doc, 242-243) - Instctrl, - /// Scan Conversion Control (0x85) (ttinst1.doc, 244-245) - Scanctrl, - /// ScanType (0x8d) (ttinst1.doc, 246) - Scantype, - /// Set Control Value Table Cut In (0x1d) (ttinst1.doc, 249) - Scvtci, - /// Set Single Width Cut In (0x1e) (ttinst1.doc, 250) - Sswci, - /// Set Single Width (0x1f) (ttinst1.doc, 251) - Ssw, - /// Set Auto Flip Boolean to On (0x4d) (ttinst1.doc, 252) - Flipon, - /// Set Auto Flip Boolean to Off (0x4e) (ttinst1.doc, 253) - Flipoff, - /// Set Angle Weight (0x7e) (ttinst1.doc, 254) - Sangw, - /// Set Delta Base (0x5e) (ttinst1.doc, 255) - Sdb, - /// Set Delta Shift (0x5f) (ttinst1.doc, 256) - Sds, - /// Get Coordinate (0x46-0x47) (ttinst1.doc, 258-259) - Gc(WhichPosition), - /// Set Coordinate From Stack (0x48) (ttinst1.doc, 260) - Scfs, - /// Measure Distance (0x49-0x4a) (ttinst1.doc, 261-262) - Md(WhichPosition), - /// Measure Pixels Per Em (0x4b) (ttinst1.doc, 263) - Mppem, - /// Measure Point Size (0x4c) (ttinst1.doc, 264) - Mps, - /// Flip Point (0x80) (ttinst2.doc, 263) - Flippt, - /// Flip Range On (0x81) (ttinst2.doc, 264) - Fliprgon, - /// Flip Range Off (0x82) (ttinst2.doc, 265) - Fliprgoff, - /// Shift Point by Last Point (0x32-0x33) (ttinst2.doc, 266) - Shp(ZonePoint), - /// Shift Contour by Last Point (0x34-0x35) (ttinst2.doc, 267) - Shc(ZonePoint), - /// Shift Zone by Last Point (0x36-0x37) (ttinst2.doc, 268) - Shz(ZonePoint), - /// Shift Point by Pixel Amount (0x38) (ttinst2.doc, 269) - Shpix, - /// Move Stack Indirect Relative Point (0x3a-0x3b) (ttinst2.doc, 270) - Msirp(SetRP0), - /// Move Direct Absolute Point (0x2e-0x2f) (ttinst2.doc, 271) - Mdap(ShouldRound), - /// Move Indirect Absolute Point (0x3e-0x3f) (ttinst2.doc, 272-275) - Miap(ShouldRound), - /// Move Direct Relative Point (0xc0-0xdf) (ttinst2.doc, 276-278) - Mdrp(SetRP0, ApplyMinimumDistance, ShouldRound, DistanceType), - /// Move Indirect Relative Point (0xe0-0xff) (ttinst2.doc, 279-283) - Mirp(SetRP0, ApplyMinimumDistance, ShouldRound, DistanceType), - /// Align Relative Point (0x3c) (ttinst2.doc, 284) - Alignrp, - /// Move Point to Intersection of Two Lines (0x0f) (ttinst2.doc, 286-288) - Isect, - /// Align Points (0x27) (ttinst2.doc, 289) - Alignpts, - /// Interpolate Point by Last Relative Stretch (0x39) (ttinst2.doc, 290) - Ip, - /// Untouch Point (0x29) (ttinst2.doc, 291) - Utp, - /// Interpolate Untouched Points Through Outline (0x30-0x31) (ttinst2.doc, 292) - Iup(Axis), - /// Delta Exception P1 (0x5d) (ttinst2.doc, 296) - Deltap1, - /// Delta Exception P2 (0x71) (ttinst2.doc, 297) - Deltap2, - /// Delta Exception P3 (0x72) (ttinst2.doc, 298) - Deltap3, - /// Delta Exception C1 (0x73) (ttinst2.doc, 299) - Deltac1, - /// Delta Exception C2 (0x74) (ttinst2.doc, 300) - Deltac2, - /// Delta Exception C3 (0x75) (ttinst2.doc, 301) - Deltac3, - /// Duplicate Top Stack Element (0x20) (ttinst2.doc, 304) - Dup, - /// Pop Top Stack Element (0x21) (ttinst2.doc, 305) - Pop, - /// Clear the Entire Stack (0x22) (ttinst2.doc, 306) - Clear, - /// Swap the Top Two Elements on the Stack (0x23) (ttinst2.doc, 307) - Swap, - /// Return the Depth of the Stack (0x24) (ttinst2.doc, 308) - Depth, - /// Copy an Indexed Element to the Top of the Stack (0x25) (ttinst2.doc, 309) - Cindex, - /// Move an Indexed Element to the Top of the Stack (0x26) (ttinst2.doc, 310) - Mindex, - /// Roll the Top Three Stack Elements (0x8a) (ttinst2.doc, 311) - Roll, - /// If Test (0x58) (ttinst2.doc, 313-314) - If, - /// Else (0x1b) (ttinst2.doc, 315) - Else, - /// End If (0x59) (ttinst2.doc, 316) - Eif, - /// Jump Relative on True (0x78) (ttinst2.doc, 317-318) - Jrot, - /// Jump (0x1c) (ttinst2.doc, 319) - Jmpr, - /// Jump Relative on False (0x79) (ttinst2.doc, 320-321) - Jrof, - /// Less Than (0x50) (ttinst2.doc, 323) - Lt, - /// Less Than or Equal (0x51) (ttinst2.doc, 324) - Lteq, - /// Greater Than (0x52) (ttinst2.doc, 325) - Gt, - /// Greater Than or Equal (0x53) (ttinst2.doc, 326) - Gteq, - /// Equal (0x54) (ttinst2.doc, 327) - Eq, - /// Not Equal (0x55) (ttinst2.doc, 328) - Neq, - /// Odd (0x56) (ttinst2.doc, 329) - Odd, - /// Even (0x57) (ttinst2.doc, 330) - Even, - /// Logical And (0x5a) (ttinst2.doc, 331-332) - And, - /// Logical Or (0x5b) (ttinst2.doc, 333) - Or, - /// Logical Not (0x5c) (ttinst2.doc, 334) - Not, - /// Add (0x60) (ttinst2.doc, 336) - Add, - /// Subtract (0x61) (ttinst2.doc, 337) - Sub, - /// Divide (0x62) (ttinst2.doc, 338) - Div, - /// Multiply (0x63) (ttinst2.doc, 339) - Mul, - /// Absolute Value (0x64) (ttinst2.doc, 340) - Abs, - /// Negate (0x65) (ttinst2.doc, 341) - Neg, - /// Floor (0x66) (ttinst2.doc, 342) - Floor, - /// Ceiling (0x67) (ttinst2.doc, 343) - Ceiling, - /// Maximum of Top Two Stack Elements (0x8b) (ttinst2.doc, 344) - Max, - /// Minimum of Top Two Stack Elements (0x8c) (ttinst2.doc, 345) - Min, - /// Round Value (0x68-0x6b) (ttinst2.doc, 347) - Round(DistanceType), - /// No Rounding of Value (0x6c-0x6f) (ttinst2.doc, 349) - Nround(DistanceType), - /// Function Definition (0x2c) (ttinst2.doc, 351) - Fdef, - /// End Function Definition (0x2d) (ttinst2.doc, 352) - Endf, - /// Call Function (0x2b) (ttinst2.doc, 353) - Call, - /// Loop and Call Function (0x2a) (ttinst2.doc, 354) - Loopcall, - /// Instruction Definition (0x89) (ttinst2.doc, 355) - Idef, - /// Debug Call (0x4f) (ttinst2.doc, 356) - Debug, - /// Get Information (0x88) (ttinst2.doc, 357-360) - Getinfo, - /// Get Variation (0x91) (ttinst2.doc, 361) - Getvariation, -} - -impl<'a> Instruction<'a> { - #[inline] - pub fn parse<'b, 'c>(data: &'b [u8], pc: &'c mut usize) - -> Result, HintingParseError> { - let op = try!(get(data, pc).ok_or(HintingParseError::Eof)); - match op { - 0x40 | 0xb0...0xb7 => { - let count = if op == 0x40 { - try!(get(data, pc).ok_or(HintingParseError::UnexpectedEof)) as usize - } else { - (op as usize & 7) + 1 - }; - if *pc + count <= data.len() { - let insn = Instruction::Pushb(&data[*pc..(*pc + count)]); - *pc += count; - Ok(insn) - } else { - Err(HintingParseError::UnexpectedEof) - } - } - 0x41 | 0xb8...0xbf => { - let count = if op == 0x41 { - try!(get(data, pc).ok_or(HintingParseError::UnexpectedEof)) as usize - } else { - (op as usize & 7) + 1 - }; - if *pc + count * 2 <= data.len() { - let insn = Instruction::Pushw(&data[*pc..(*pc + count * 2)]); - *pc += count * 2; - Ok(insn) - } else { - Err(HintingParseError::UnexpectedEof) - } - } - 0x43 => Ok(Instruction::Rs), - 0x42 => Ok(Instruction::Ws), - 0x44 => Ok(Instruction::Wcvtp), - 0x70 => Ok(Instruction::Wcvtf), - 0x45 => Ok(Instruction::Rcvt), - 0x00 => Ok(Instruction::Svtca(Axis::Y)), - 0x01 => Ok(Instruction::Svtca(Axis::X)), - 0x02 => Ok(Instruction::Spvtca(Axis::Y)), - 0x03 => Ok(Instruction::Spvtca(Axis::X)), - 0x04 => Ok(Instruction::Sfvtca(Axis::Y)), - 0x05 => Ok(Instruction::Sfvtca(Axis::X)), - 0x06 => Ok(Instruction::Spvtl(LineOrientation::Parallel)), - 0x07 => Ok(Instruction::Spvtl(LineOrientation::Perpendicular)), - 0x08 => Ok(Instruction::Sfvtl(LineOrientation::Parallel)), - 0x09 => Ok(Instruction::Sfvtl(LineOrientation::Perpendicular)), - 0x0e => Ok(Instruction::Sfvtpv), - 0x86 => Ok(Instruction::Sdpvtl(LineOrientation::Parallel)), - 0x87 => Ok(Instruction::Sdpvtl(LineOrientation::Perpendicular)), - 0x0a => Ok(Instruction::Spvfs), - 0x0b => Ok(Instruction::Sfvfs), - 0x0c => Ok(Instruction::Gpv), - 0x0d => Ok(Instruction::Gfv), - 0x10 => Ok(Instruction::Srp0), - 0x11 => Ok(Instruction::Srp1), - 0x12 => Ok(Instruction::Srp2), - 0x13 => Ok(Instruction::Szp0), - 0x14 => Ok(Instruction::Szp1), - 0x15 => Ok(Instruction::Szp2), - 0x16 => Ok(Instruction::Szps), - 0x19 => Ok(Instruction::Rthg), - 0x18 => Ok(Instruction::Rtg), - 0x3d => Ok(Instruction::Rtdg), - 0x7d => Ok(Instruction::Rdtg), - 0x7c => Ok(Instruction::Rutg), - 0x7a => Ok(Instruction::Roff), - 0x76 => Ok(Instruction::Sround), - 0x77 => Ok(Instruction::S45round), - 0x17 => Ok(Instruction::Sloop), - 0x1a => Ok(Instruction::Smd), - 0x8e => Ok(Instruction::Instctrl), - 0x85 => Ok(Instruction::Scanctrl), - 0x8d => Ok(Instruction::Scantype), - 0x1d => Ok(Instruction::Scvtci), - 0x1e => Ok(Instruction::Sswci), - 0x1f => Ok(Instruction::Ssw), - 0x4d => Ok(Instruction::Flipon), - 0x4e => Ok(Instruction::Flipoff), - 0x7e => Ok(Instruction::Sangw), - 0x5e => Ok(Instruction::Sdb), - 0x5f => Ok(Instruction::Sds), - 0x46 => Ok(Instruction::Gc(WhichPosition::Current)), - 0x47 => Ok(Instruction::Gc(WhichPosition::Original)), - 0x48 => Ok(Instruction::Scfs), - 0x49 => Ok(Instruction::Md(WhichPosition::Current)), - 0x4a => Ok(Instruction::Md(WhichPosition::Original)), - 0x4b => Ok(Instruction::Mppem), - 0x4c => Ok(Instruction::Mps), - 0x80 => Ok(Instruction::Flippt), - 0x81 => Ok(Instruction::Fliprgon), - 0x82 => Ok(Instruction::Fliprgoff), - 0x32 => Ok(Instruction::Shp(ZonePoint::Zone1Point2)), - 0x33 => Ok(Instruction::Shp(ZonePoint::Zone0Point1)), - 0x34 => Ok(Instruction::Shc(ZonePoint::Zone1Point2)), - 0x35 => Ok(Instruction::Shc(ZonePoint::Zone0Point1)), - 0x36 => Ok(Instruction::Shz(ZonePoint::Zone1Point2)), - 0x37 => Ok(Instruction::Shz(ZonePoint::Zone0Point1)), - 0x38 => Ok(Instruction::Shpix), - 0x3a | 0x3b => Ok(Instruction::Msirp(SetRP0(op == 0x3b))), - 0x2e | 0x2f => Ok(Instruction::Mdap(ShouldRound(op == 0x2f))), - 0x3e | 0x3f => Ok(Instruction::Miap(ShouldRound(op == 0x3f))), - 0xc0...0xdf => { - Ok(Instruction::Mdrp(SetRP0((op & 0b10000) != 0), - ApplyMinimumDistance((op & 0b01000) != 0), - ShouldRound((op & 0b00100) != 0), - try!(DistanceType::parse(op & 0b00011)))) - } - 0xe0...0xff => { - Ok(Instruction::Mirp(SetRP0((op & 0b10000) != 0), - ApplyMinimumDistance((op & 0b01000) != 0), - ShouldRound((op & 0b00100) != 0), - try!(DistanceType::parse(op & 0b00011)))) - } - 0x3c => Ok(Instruction::Alignrp), - 0x0f => Ok(Instruction::Isect), - 0x27 => Ok(Instruction::Alignpts), - 0x39 => Ok(Instruction::Ip), - 0x29 => Ok(Instruction::Utp), - 0x30 => Ok(Instruction::Iup(Axis::Y)), - 0x31 => Ok(Instruction::Iup(Axis::X)), - 0x5d => Ok(Instruction::Deltap1), - 0x71 => Ok(Instruction::Deltap2), - 0x72 => Ok(Instruction::Deltap3), - 0x73 => Ok(Instruction::Deltac1), - 0x74 => Ok(Instruction::Deltac2), - 0x75 => Ok(Instruction::Deltac3), - 0x20 => Ok(Instruction::Dup), - 0x21 => Ok(Instruction::Pop), - 0x22 => Ok(Instruction::Clear), - 0x23 => Ok(Instruction::Swap), - 0x24 => Ok(Instruction::Depth), - 0x25 => Ok(Instruction::Cindex), - 0x26 => Ok(Instruction::Mindex), - 0x8a => Ok(Instruction::Roll), - 0x58 => Ok(Instruction::If), - 0x1b => Ok(Instruction::Else), - 0x59 => Ok(Instruction::Eif), - 0x78 => Ok(Instruction::Jrot), - 0x1c => Ok(Instruction::Jmpr), - 0x79 => Ok(Instruction::Jrof), - 0x50 => Ok(Instruction::Lt), - 0x51 => Ok(Instruction::Lteq), - 0x52 => Ok(Instruction::Gt), - 0x53 => Ok(Instruction::Gteq), - 0x54 => Ok(Instruction::Eq), - 0x55 => Ok(Instruction::Neq), - 0x56 => Ok(Instruction::Odd), - 0x57 => Ok(Instruction::Even), - 0x5a => Ok(Instruction::And), - 0x5b => Ok(Instruction::Or), - 0x5c => Ok(Instruction::Not), - 0x60 => Ok(Instruction::Add), - 0x61 => Ok(Instruction::Sub), - 0x62 => Ok(Instruction::Div), - 0x63 => Ok(Instruction::Mul), - 0x64 => Ok(Instruction::Abs), - 0x65 => Ok(Instruction::Neg), - 0x66 => Ok(Instruction::Floor), - 0x67 => Ok(Instruction::Ceiling), - 0x8b => Ok(Instruction::Max), - 0x8c => Ok(Instruction::Min), - 0x68...0x6b => Ok(Instruction::Round(try!(DistanceType::parse(op & 0b11)))), - 0x6c...0x6f => Ok(Instruction::Nround(try!(DistanceType::parse(op & 0b11)))), - 0x2c => Ok(Instruction::Fdef), - 0x2d => Ok(Instruction::Endf), - 0x2b => Ok(Instruction::Call), - 0x2a => Ok(Instruction::Loopcall), - 0x89 => Ok(Instruction::Idef), - 0x4f => Ok(Instruction::Debug), - 0x88 => Ok(Instruction::Getinfo), - 0x91 => Ok(Instruction::Getvariation), - _ => Err(HintingParseError::UnknownOpcode(op)), - } - } -} - -fn get(data: &[u8], pc: &mut usize) -> Option { - match data.get(*pc) { - Some(&byte) => { - *pc += 1; - Some(byte) - } - None => None, - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(u8)] -pub enum Axis { - Y = 0, - X = 1, -} - -impl Axis { - #[inline] - pub fn as_point(self) -> Point2D { - match self { - Axis::Y => Point2D::new(F2DOT14_ZERO, F2DOT14_ONE), - Axis::X => Point2D::new(F2DOT14_ONE, F2DOT14_ZERO), - } - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(u8)] -pub enum LineOrientation { - Parallel = 0, - Perpendicular = 1, -} - -#[derive(Copy, Clone, Debug)] -#[repr(u8)] -pub enum WhichPosition { - // Use the current position. - Current = 0, - // Use the position in the original outline. - Original = 1, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(u8)] -pub enum ZonePoint { - Zone1Point2 = 0, - Zone0Point1 = 1, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct SetRP0(pub bool); - -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct ApplyMinimumDistance(pub bool); - -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct ShouldRound(pub bool); - -// See `MDRP` (ttinst2.doc, 277) -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(u8)] -pub enum DistanceType { - Gray = 0, - Black = 1, - White = 2, -} - -impl DistanceType { - fn parse(value: u8) -> Result { - match value { - 0 => Ok(DistanceType::Gray), - 1 => Ok(DistanceType::Black), - 2 => Ok(DistanceType::White), - _ => Err(HintingParseError::InvalidDistanceType), - } - } -} - diff --git a/pathfinder-classic/src/hinting/interp.rs b/pathfinder-classic/src/hinting/interp.rs deleted file mode 100644 index 5b5a7951..00000000 --- a/pathfinder-classic/src/hinting/interp.rs +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 TrueType interpreter. - -use byteorder::{BigEndian, ByteOrder}; -use error::{HintingAnalysisError, HintingExecutionError, HintingParseError}; -use hinting::insns::Instruction; -use hinting::{FONT_SMOOTHING_GRAYSCALE, GETINFO_VERSION, Hinter}; -use hinting::{INFO_RESULT_FONT_SMOOTHING_GRAYSCALE_SHIFT, InfoSelector, RoundState, VERSION}; -use num_traits::Zero; -use std::cmp; -use util::{F26DOT6_ZERO, F26Dot6}; - -impl<'a> Hinter<'a> { - pub fn exec(&mut self) -> Result<(), HintingExecutionError> { - loop { - // Fetch the current frame. - let frame = match self.call_stack.last() { - None => return Ok(()), - Some(&frame) if frame.pc == frame.end => { - self.call_stack.pop(); - continue - } - Some(&frame) => frame, - }; - - // Decode the next instruction, and advance the program counter. - let mut new_pc = frame.pc; - let bytecode = self.scripts[frame.script].bytecode; - let instruction = - try!(Instruction::parse(bytecode, - &mut new_pc).map_err(HintingExecutionError::ParseError)); - - // Execute it. - match instruction { - Instruction::Pushb(bytes) => { - self.stack.extend(bytes.iter().map(|&b| b as i32)) - } - Instruction::Pushw(bytes) => { - self.stack.extend(bytes.chunks(2).map(|bs| BigEndian::read_i16(bs) as i32)) - } - Instruction::Rs => { - // We should throw an exception here if the storage area isn't big enough, but - // let's follow Postel's law. - let addr = try!(self.pop()) as usize; - match self.storage_area.get(addr) { - Some(&value) => self.stack.push(value), - None => self.stack.push(0), - } - } - Instruction::Ws => { - // We should throw an exception here if the storage area isn't big enough, but - // let's follow Postel's law. - // - // FIXME(pcwalton): Cap the size of the storage area? - let (value, addr) = (try!(self.pop()), try!(self.pop()) as usize); - if self.storage_area.len() < addr + 1 { - self.storage_area.resize(addr + 1, 0) - } - self.storage_area[addr] = value - } - Instruction::Rcvt => { - let addr = try!(self.pop()) as usize; - let value = *try!(self.control_value_table - .get(addr) - .ok_or(HintingExecutionError::CvtReadOutOfBounds)); - self.stack.push(value.0) - } - Instruction::Svtca(axis) => { - self.projection_vector = axis.as_point(); - self.freedom_vector = axis.as_point(); - } - Instruction::Spvtca(axis) => self.projection_vector = axis.as_point(), - Instruction::Sfvtca(axis) => self.freedom_vector = axis.as_point(), - Instruction::Srp0 => self.reference_points[0] = try!(self.pop()) as u32, - Instruction::Srp1 => self.reference_points[1] = try!(self.pop()) as u32, - Instruction::Srp2 => self.reference_points[2] = try!(self.pop()) as u32, - Instruction::Szp0 => self.zone_points[0] = try!(self.pop()) as u32, - Instruction::Szp1 => self.zone_points[1] = try!(self.pop()) as u32, - Instruction::Szp2 => self.zone_points[2] = try!(self.pop()) as u32, - Instruction::Szps => { - let zone = try!(self.pop()) as u32; - self.zone_points = [zone; 3] - } - Instruction::Rthg => self.round_state = RoundState::RoundToHalfGrid, - Instruction::Rtg => self.round_state = RoundState::RoundToGrid, - Instruction::Rtdg => self.round_state = RoundState::RoundToDoubleGrid, - Instruction::Rutg => self.round_state = RoundState::RoundUpToGrid, - Instruction::Roff => self.round_state = RoundState::RoundOff, - Instruction::Sround => { - // TODO(pcwalton): Super rounding. - try!(self.pop()); - } - Instruction::Scanctrl | Instruction::Scantype => { - // Not applicable to antialiased glyphs. - try!(self.pop()); - } - Instruction::Mppem => { - // We always scale both axes in the same direction, so we don't have to look - // at the projection vector. - self.stack.push(self.point_size.round() as i32) - } - Instruction::Dup => { - let value = *try!(self.stack - .last() - .ok_or(HintingExecutionError::StackUnderflow)); - self.stack.push(value); - } - Instruction::Pop => { - try!(self.pop()); - } - Instruction::Clear => self.stack.clear(), - Instruction::Swap => { - let (a, b) = (try!(self.pop()), try!(self.pop())); - self.stack.push(a); - self.stack.push(b); - } - Instruction::Mindex => { - let index = try!(self.pop()) as usize; - if index >= self.stack.len() { - return Err(HintingExecutionError::StackUnderflow) - } - let rindex = self.stack.len() - 1 - index; - let value = self.stack.remove(rindex); - self.stack.push(value) - } - Instruction::If => { - let cond = try!(self.pop()); - if cond == 0 { - // Move to the instruction following `else` or `eif`. - let else_target_index = self.scripts[frame.script] - .branch_targets - .binary_search_by(|script| { - script.branch_location.cmp(&frame.pc) - }).unwrap(); - new_pc = self.scripts[frame.script] - .branch_targets[else_target_index] - .target_location + 1 - } - } - Instruction::Else => { - // The only way we get here is by falling off the end of a then-branch. So jump - // to the instruction following the matching `eif`. - let eif_target_index = self.scripts[frame.script] - .branch_targets - .binary_search_by(|script| { - script.branch_location.cmp(&frame.pc) - }).unwrap(); - new_pc = self.scripts[frame.script] - .branch_targets[eif_target_index] - .target_location + 1 - } - Instruction::Eif => { - // Likewise, the only way we get here is by falling off the end of a - // then-branch. - } - Instruction::Lt => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs < rhs) as i32) - } - Instruction::Lteq => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs <= rhs) as i32) - } - Instruction::Gt => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs > rhs) as i32) - } - Instruction::Gteq => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs >= rhs) as i32) - } - Instruction::Eq => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs == rhs) as i32) - } - Instruction::Neq => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs != rhs) as i32) - } - Instruction::And => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs != 0 && rhs != 0) as i32) - } - Instruction::Or => { - let (rhs, lhs) = (try!(self.pop()), try!(self.pop())); - self.stack.push((lhs != 0 || rhs != 0) as i32) - } - Instruction::Not => { - let cond = try!(self.pop()); - self.stack.push((cond == 0) as i32) - } - Instruction::Add => { - let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop()))); - self.stack.push((lhs + rhs).0) - } - Instruction::Sub => { - let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop()))); - self.stack.push((lhs - rhs).0) - } - Instruction::Div => { - let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop()))); - if rhs.is_zero() { - // Obey Postel's law… - self.stack.push(F26DOT6_ZERO.0) - } else { - self.stack.push((lhs / rhs).0) - } - } - Instruction::Mul => { - let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop()))); - self.stack.push((lhs * rhs).0) - } - Instruction::Abs => { - // Actually in fixed point, but it works out the same way. - let n = try!(self.pop()); - self.stack.push(n.abs()) - } - Instruction::Neg => { - let n = F26Dot6(try!(self.pop())); - self.stack.push((-n).0) - } - Instruction::Max => { - let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop()))); - self.stack.push(cmp::max(rhs, lhs).0) - } - Instruction::Min => { - let (rhs, lhs) = (F26Dot6(try!(self.pop())), F26Dot6(try!(self.pop()))); - self.stack.push(cmp::max(rhs, lhs).0) - } - Instruction::Fdef => { - // We should throw an exception here if the function definition list isn't big - // enough, but let's follow Postel's law. - // - // FIXME(pcwalton): Cap the size of the function definitions? - let id = try!(self.pop()) as usize; - if self.functions.len() < id + 1 { - self.functions.resize(id + 1, None) - } - - let branch_target_index = self.scripts[frame.script] - .branch_targets - .binary_search_by(|script| { - script.branch_location.cmp(&frame.pc) - }).unwrap(); - - let end_pc = self.scripts[frame.script] - .branch_targets[branch_target_index] - .target_location; - - self.functions[id] = Some(Frame::new(new_pc, end_pc, frame.script)); - new_pc = end_pc + 1 - } - Instruction::Call => { - let id = try!(self.pop()) as usize; - let new_frame = match self.functions.get(id) { - Some(&Some(new_frame)) => new_frame, - Some(&None) | None => { - return Err(HintingExecutionError::CallToUndefinedFunction) - } - }; - - // Save our return address. - self.call_stack.last_mut().unwrap().pc = new_pc; - - // Jump to the new function. - self.call_stack.push(new_frame); - new_pc = new_frame.pc - } - Instruction::Getinfo => { - let selector = InfoSelector::from_bits_truncate(try!(self.pop())); - - // We only handle a subset of the selectors here. - // - // TODO(pcwalton): Handle the ones relating to subpixel AA. - let mut result = 0; - if selector.contains(VERSION) { - result |= GETINFO_VERSION - } - if selector.contains(FONT_SMOOTHING_GRAYSCALE) { - result |= 1 << INFO_RESULT_FONT_SMOOTHING_GRAYSCALE_SHIFT - } - self.stack.push(result) - } - _ => { - println!("TODO: {:?}", instruction); - } - } - - // Advance the program counter. - self.call_stack.last_mut().unwrap().pc = new_pc; - } - } - - #[inline] - fn pop(&mut self) -> Result { - self.stack.pop().ok_or(HintingExecutionError::StackUnderflow) - } -} - -pub struct Script<'a> { - bytecode: &'a [u8], - branch_targets: Vec, -} - -impl<'a> Script<'a> { - pub fn new<'b>(bytecode: &'b [u8]) -> Result, HintingAnalysisError> { - let mut interpreter = Script { - bytecode: bytecode, - branch_targets: vec![], - }; - try!(interpreter.populate_branch_targets()); - Ok(interpreter) - } - - #[inline] - pub fn len(&self) -> usize { - self.bytecode.len() - } - - // This is a little bit tricky because we have to maintain sorted order of the `branch_targets` - // array for future binary searches. - fn populate_branch_targets(&mut self) -> Result<(), HintingAnalysisError> { - let (mut pc, mut pending_branch_targets) = (0, vec![]); - loop { - let location = pc; - let instruction = match Instruction::parse(self.bytecode, &mut pc) { - Ok(instruction) => instruction, - Err(HintingParseError::Eof) => break, - Err(err) => return Err(HintingAnalysisError::ParseError(err)), - }; - - match instruction { - Instruction::If | Instruction::Fdef | Instruction::Idef => { - pending_branch_targets.push((self.branch_targets.len(), instruction)); - self.branch_targets.push(BranchTarget { - branch_location: location, - target_location: 0, - }); - } - Instruction::Endf => { - let (index, branch_instruction) = try!(pending_branch_targets.pop().ok_or( - HintingAnalysisError::BranchTargetMissingBranch)); - match branch_instruction { - Instruction::Fdef | Instruction::Idef => { - self.branch_targets[index].target_location = location - } - _ => return Err(HintingAnalysisError::MismatchedBranchInstruction), - } - } - Instruction::Eif => { - let (index, branch_instruction) = try!(pending_branch_targets.pop().ok_or( - HintingAnalysisError::BranchTargetMissingBranch)); - match branch_instruction { - Instruction::If | Instruction::Else => { - self.branch_targets[index].target_location = location - } - _ => return Err(HintingAnalysisError::MismatchedBranchInstruction), - } - } - Instruction::Else => { - let (index, branch_instruction) = try!(pending_branch_targets.pop().ok_or( - HintingAnalysisError::BranchTargetMissingBranch)); - match branch_instruction { - Instruction::If => { - self.branch_targets[index].target_location = location; - - pending_branch_targets.push((self.branch_targets.len(), instruction)); - self.branch_targets.push(BranchTarget { - branch_location: location, - target_location: 0, - }); - } - _ => return Err(HintingAnalysisError::MismatchedBranchInstruction), - } - } - _ => {} - } - } - - if pending_branch_targets.is_empty() { - Ok(()) - } else { - Err(HintingAnalysisError::BranchMissingBranchTarget) - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct Frame { - /// The current program counter. - pc: usize, - /// The PC at which to stop execution. - end: usize, - /// The index of the script. - script: usize, -} - -impl Frame { - pub fn new(pc: usize, end: usize, script_index: usize) -> Frame { - Frame { - pc: pc, - end: end, - script: script_index, - } - } -} - -#[derive(Clone, Copy, Debug)] -struct BranchTarget { - branch_location: usize, - target_location: usize, -} - diff --git a/pathfinder-classic/src/hinting/mod.rs b/pathfinder-classic/src/hinting/mod.rs deleted file mode 100644 index 35cc7fc3..00000000 --- a/pathfinder-classic/src/hinting/mod.rs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -#![allow(dead_code)] - -//! The TrueType hinting VM. -//! -//! See: https://www.microsoft.com/typography/otspec/ttinst.htm - -use byteorder::{BigEndian, ByteOrder}; -use error::{HinterCreationError, HintingExecutionError}; -use euclid::Point2D; -use font::Font; -use hinting::interp::{Frame, Script}; -use util::{F26Dot6, F2Dot14}; - -mod insns; -mod interp; - -/// The version we return for the `getinfo` instruction. FreeType uses 35, so we do as well. (This -/// supposedly corresponds to Windows 98.) -pub const GETINFO_VERSION: i32 = 35; - -const FONT_PROGRAM: usize = 0; -const CONTROL_VALUE_PROGRAM: usize = 1; - -/// A TrueType hinting virtual machine. -pub struct Hinter<'a> { - // Scripts that we've analyzed so far. - scripts: Vec>, - // The VM's evaluation stack. - stack: Vec, - // The VM's call stack. - call_stack: Vec, - // The set of defined functions. - functions: Vec>, - // The Control Value Table: the VM's initialized memory. - control_value_table: Vec, - // The Storage Area: the VM's uninitialized memory. - storage_area: Vec, - // The current font size. - point_size: f32, - // The projection vector, in 2.14 fixed point. - projection_vector: Point2D, - // The dual projection vector, in 2.14 fixed point. - dual_projection_vector: Point2D, - // The freedom vector, in 2.14 fixed point. - freedom_vector: Point2D, - // The reference point indices. - reference_points: [u32; 3], - // The zone numbers. - zone_points: [u32; 3], - // The round state. - round_state: RoundState, - // The loop variable count. - loop_count: u32, - // The minimum distance value. - minimum_distance: u32, - // Instruction control flags. - instruction_control: InstructionControl, - // Threshold value for ppem. See `SCANCTRL` (ttinst1.doc, 244-245). - dropout_threshold: u8, - // Special dropout control. - dropout_control: DropoutControl, - // The scan type. See `SCANTYPE` (ttinst1.doc, 246-247). - scan_type: ScanType, - // The control value cut in. See `SCVTSI` (ttinst1.doc, 249). - control_value_cut_in: u32, - // The single width cut in. See `SSWCI` (ttinst1.doc, 250). - single_width_cut_in: u32, - // The single width value. See `SSW` (ttinst1.doc, 251). - single_width_value: i32, - // The angle weight. Per spec, does nothing. See `SANGW` (ttinst1.doc, 254). - angle_weight: u32, - // The delta base. See `SDB` (ttinst1.doc, 255). - delta_base: u32, - // The delta shift. See `SDS` (ttinst1.doc, 256). - delta_shift: u32, - // Various graphics state flags. - graphics_state_flags: GraphicsStateFlags, -} - -impl<'a> Hinter<'a> { - pub fn new<'b>(font: &'b Font) -> Result, HinterCreationError> { - let font_program = font.font_program(); - let control_value_program = font.control_value_program(); - let scripts = vec![ - try!(Script::new(font_program).map_err(HinterCreationError::FontProgramAnalysisError)), - try!(Script::new(control_value_program).map_err( - HinterCreationError::ControlValueProgramAnalysisError)), - ]; - - let cvt = font.control_value_table().chunks(2).map(|bytes| { - // FIXME(pcwalton): This is wrong! - let unscaled = BigEndian::read_i16(bytes) as i32; - F26Dot6(unscaled) - }).collect(); - - // Initialize the call stack to the font program, so that we'll start executing it. - let call_stack = vec![Frame::new(0, scripts[FONT_PROGRAM].len(), FONT_PROGRAM)]; - - let mut hinter = Hinter { - scripts: scripts, - stack: vec![], - call_stack: call_stack, - functions: vec![], - control_value_table: cvt, - storage_area: vec![], - point_size: 0.0, - projection_vector: Point2D::zero(), - dual_projection_vector: Point2D::zero(), - freedom_vector: Point2D::zero(), - reference_points: [0; 3], - zone_points: [0; 3], - round_state: RoundState::RoundToHalfGrid, - loop_count: 0, - minimum_distance: 0, - instruction_control: InstructionControl::empty(), - dropout_threshold: 0, - dropout_control: DropoutControl::empty(), - scan_type: ScanType::SimpleDropoutControlIncludingStubs, - control_value_cut_in: 0, - single_width_cut_in: 0, - single_width_value: 0, - angle_weight: 0, - delta_base: 0, - delta_shift: 0, - graphics_state_flags: AUTO_FLIP, - }; - - try!(hinter.exec().map_err(HinterCreationError::FontProgramExecutionError)); - - Ok(hinter) - } - - /// Sets the point size and reevaluates the control value program (`prep`). - pub fn set_point_size(&mut self, new_point_size: f32) -> Result<(), HintingExecutionError> { - self.point_size = new_point_size; - self.call_stack.push(Frame::new(0, - self.scripts[CONTROL_VALUE_PROGRAM].len(), - CONTROL_VALUE_PROGRAM)); - self.exec() - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(u8)] -enum RoundState { - RoundToHalfGrid = 0, - RoundToGrid = 1, - RoundToDoubleGrid = 2, - RoundDownToGrid = 3, - RoundUpToGrid = 4, - RoundOff = 5, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -#[repr(u8)] -pub enum ScanType { - SimpleDropoutControlIncludingStubs = 0, - SimpleDropoutControlExcludingStubs = 1, - NoDropoutControl = 2, - SmartDropoutControlIncludingStubs = 3, - SmartDropoutControlExcludingStubs = 4, -} - -bitflags! { - pub flags InstructionControl: u8 { - const INHIBIT_GRID_FITTING = 1 << 0, - const IGNORE_CVT_PARAMETERS = 1 << 1, - const NATIVE_SUBPIXEL_AA = 1 << 2, - } -} - -bitflags! { - pub flags DropoutControl: u8 { - const DROPOUT_IF_PPEM_LESS_THAN_THRESHOLD = 1 << 0, - const DROPOUT_IF_ROTATED = 1 << 1, - const DROPOUT_IF_STRETCHED = 1 << 2, - const NO_DROPOUT_IF_PPEM_GREATER_THAN_THRESHOLD = 1 << 3, - const NO_DROPOUT_IF_UNROTATED = 1 << 4, - const NO_DROPOUT_IF_UNSTRETCHED = 1 << 5, - } -} - -bitflags! { - flags GraphicsStateFlags: u8 { - // See `FLIPON` (default true) (ttinst1.doc, 252). - const AUTO_FLIP = 1 << 1, - } -} - -bitflags! { - // Info returned by the `getinfo` instruction. - pub flags InfoSelector: i32 { - const VERSION = 0x1, - const GLYPH_ROTATION = 0x2, - const GLYPH_STRETCHED = 0x4, - const FONT_VARIATIONS = 0x8, - const VERTICAL_PHANTOM_POINTS = 0x10, - const FONT_SMOOTHING_GRAYSCALE = 0x20, - const SUBPIXEL_AA_ENABLED = 0x40, - const SUBPIXEL_AA_COMPATIBLE_WIDTHS_ENABLED = 0x80, - const SUBPIXEL_AA_HORIZONTAL_LCD_STRIPE_ORIENTATION = 0x100, - const SUBPIXEL_AA_BGR_LCD_STRIPE_ORDER = 0x200, - const SUBPIXEL_AA_SUBPIXEL_POSITIONED_TEXT_ENABLED = 0x400, - const SUBPIXEL_AA_SYMMETRIC_RENDERING_ENABLED = 0x800, - const SUBPIXEL_AA_GRAY_RENDERING_ENABLED = 0x1000, - } -} - -pub const INFO_RESULT_VERSION_SHIFT: i32 = 0; -pub const INFO_RESULT_FONT_SMOOTHING_GRAYSCALE_SHIFT: i32 = 12; - diff --git a/pathfinder-classic/src/lib.rs b/pathfinder-classic/src/lib.rs deleted file mode 100644 index d323411b..00000000 --- a/pathfinder-classic/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 high-performance GPU rasterizer for OpenType fonts. -//! -//! ## Introduction -//! -//! Pathfinder is a fast, practical GPU-based rasterizer for OpenType fonts using OpenGL 4.3. It -//! features: -//! -//! * Very low setup time. Glyph outlines can go from the `.otf` file to the GPU in a form ready -//! for rasterization in less than a microsecond. There is no expensive tessellation or -//! preprocessing step. -//! -//! * High quality antialiasing. Unlike techniques that rely on multisample antialiasing, -//! Pathfinder computes exact fractional trapezoidal area coverage on a per-pixel basis. -//! -//! * Fast rendering, even at small pixel sizes. On typical systems, Pathfinder should easily -//! exceed the performance of the best CPU rasterizers. -//! -//! * Low memory consumption. The only memory overhead over the glyph and outline storage itself is -//! that of a coverage buffer which typically consumes somewhere between 4MB-16MB and can be -//! discarded under memory pressure. Outlines are stored on-GPU in a compressed format and usually -//! take up only a few dozen kilobytes. -//! -//! * Portability to most GPUs manufactured in the last few years, including integrated GPUs. -//! -//! ## Usage -//! -//! See `examples/generate-atlas.rs` for a simple example. -//! -//! Typically, the steps to use Pathfinder are: -//! -//! 1. Create a `Rasterizer` object. This holds the OpenGL state. -//! -//! 2. Open the font from disk (or elsewhere), and call `Font::new()` (or -//! `Font::from_collection_index` in the case of a `.ttc` or `.dfont` collection) to load it. -//! -//! 3. If the text to be rendered is not already shaped, call -//! `Font::glyph_mapping_for_codepoint_ranges()` to determine the glyphs needed to render the -//! text, and call `shaper::shape_text()` to convert the text to glyph IDs. -//! -//! 4. Create an `OutlineBuilder` and call `OutlineBuilder::add_glyph()` on each glyph to parse -//! each outline from the font. Then upload the outlines to the GPU with -//! `OutlineBuilder::create_buffers()`. -//! -//! 5. Create an `AtlasBuilder` with a suitable width (1024 or 2048 is usually fine) and call -//! `AtlasBuilder::pack_glyph()` on each glyph you need to render. Then call -//! `AtlasBuilder::create_atlas()` to upload the atlas buffer to the GPU. -//! -//! 6. Make a `CoverageBuffer` of an appropriate size (1024 or 2048 pixels on each side is -//! typically reasonable). -//! -//! 7. Create an image to render the atlas to with `Rasterizer::device().create_image()`. The -//! format should be `R8` and the buffer should be created read-write. -//! -//! 8. Draw the glyphs with `Rasterizer::draw_atlas()`. -//! -//! Don't forget to flush the queue (`Rasterizer::queue().flush()`) and/or perform appropriate -//! synchronization (`glMemoryBarrier()`) as necessary. -//! -//! ## Hardware requirements -//! -//! Pathfinder requires at least OpenGL 3.3 and either OpenGL 4.3 compute shader or OpenCL 1.2. -//! Intel GPUs in Sandy Bridge processors or later should be OK. - -#![cfg_attr(test, feature(test))] - -#[macro_use] -extern crate bitflags; -extern crate byteorder; -extern crate compute_shader; -extern crate euclid; -extern crate flate2; -extern crate gl; -#[cfg(test)] -extern crate memmap; -extern crate num_traits; -#[cfg(test)] -#[macro_use] -extern crate quickcheck; -#[cfg(test)] -extern crate test; - -pub mod atlas; -pub mod charmap; -pub mod coverage; -pub mod error; -pub mod font; -pub mod hinting; -pub mod outline; -pub mod rasterizer; -pub mod shaper; -pub mod typesetter; - -mod containers; -mod rect_packer; -mod tables; -mod util; - -#[cfg(test)] -mod tests; - diff --git a/pathfinder-classic/src/outline.rs b/pathfinder-classic/src/outline.rs deleted file mode 100644 index dd92d955..00000000 --- a/pathfinder-classic/src/outline.rs +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Glyph vectors, uploaded in a resolution-independent manner to the GPU. - -use error::{FontError, GlError}; -use euclid::{Point2D, Size2D}; -use font::{Font, PointKind}; -use gl::types::{GLsizeiptr, GLuint}; -use gl; -use std::mem; -use std::os::raw::c_void; - -static DUMMY_VERTEX: Vertex = Vertex { - x: 0, - y: 0, - glyph_index: 0, -}; - -/// Packs up outlines for glyphs into a format that the GPU can process. -pub struct OutlineBuilder { - vertices: Vec, - indices: Vec, - descriptors: Vec, -} - -impl OutlineBuilder { - /// Creates a new empty set of outlines. - #[inline] - pub fn new() -> OutlineBuilder { - OutlineBuilder { - vertices: vec![DUMMY_VERTEX], - indices: vec![], - descriptors: vec![], - } - } - - /// Begins a new path. - pub fn create_path(&mut self) -> PathBuilder { - let vertex_count = self.vertices.len(); - let index_count = self.indices.len(); - let descriptor_count = self.descriptors.len(); - - PathBuilder { - outline_builder: self, - vbo_start_index: vertex_count as u32, - vbo_end_index: vertex_count as u32, - ibo_start_index: index_count as u32, - point_index_in_path: 0, - glyph_index: descriptor_count as u16, - } - } - - /// Adds a new glyph to the outline builder. Returns the glyph index, which is useful for later - /// calls to `Atlas::pack_glyph()`. - pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result { - let glyph_index = self.descriptors.len() as u16; - let mut last_point_kind = PointKind::OnCurve; - let mut control_point_index = 0; - let mut control_points = [Point2D::zero(), Point2D::zero(), Point2D::zero()]; - let mut path_builder = self.create_path(); - - try!(font.for_each_point(glyph_id, |point| { - control_points[control_point_index] = point.position; - control_point_index += 1; - - if point.index_in_contour == 0 { - path_builder.move_to(&control_points[0]); - control_point_index = 0 - } else if point.kind == PointKind::OnCurve { - match last_point_kind { - PointKind::FirstCubicControl => {} - PointKind::SecondCubicControl => { - path_builder.cubic_curve_to(&control_points[0], - &control_points[1], - &control_points[2]) - } - PointKind::QuadControl => { - path_builder.quad_curve_to(&control_points[0], &control_points[1]) - } - PointKind::OnCurve => path_builder.line_to(&control_points[0]), - } - - control_point_index = 0 - } - - last_point_kind = point.kind - })); - - let bounds = try!(font.glyph_bounds(glyph_id)); - path_builder.finish(&bounds, font.units_per_em() as u32, glyph_id); - - Ok(glyph_index) - } - - /// Uploads the outlines to the GPU. - pub fn create_buffers(self) -> Result { - // TODO(pcwalton): Try using `glMapBuffer` here. Requires precomputing contour types and - // counts. - unsafe { - let (mut vertices, mut indices, mut descriptors) = (0, 0, 0); - gl::GenBuffers(1, &mut vertices); - gl::GenBuffers(1, &mut indices); - gl::GenBuffers(1, &mut descriptors); - - gl::BindBuffer(gl::ARRAY_BUFFER, vertices); - gl::BufferData(gl::ARRAY_BUFFER, - (self.vertices.len() * mem::size_of::()) as GLsizeiptr, - self.vertices.as_ptr() as *const Vertex as *const c_void, - gl::STATIC_DRAW); - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indices); - gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, - (self.indices.len() * mem::size_of::()) as GLsizeiptr, - self.indices.as_ptr() as *const u32 as *const c_void, - gl::STATIC_DRAW); - - let length = self.descriptors.len() * mem::size_of::(); - gl::BindBuffer(gl::UNIFORM_BUFFER, descriptors); - gl::BufferData(gl::UNIFORM_BUFFER, - length as GLsizeiptr, - self.descriptors.as_ptr() as *const GlyphDescriptor as *const c_void, - gl::STATIC_DRAW); - - Ok(Outlines { - vertices_buffer: vertices, - indices_buffer: indices, - descriptors_buffer: descriptors, - descriptors: self.descriptors, - indices_count: self.indices.len(), - }) - } - } -} - -/// Resolution-independent glyph vectors uploaded to the GPU. -pub struct Outlines { - vertices_buffer: GLuint, - indices_buffer: GLuint, - descriptors_buffer: GLuint, - descriptors: Vec, - indices_count: usize, -} - -impl Drop for Outlines { - fn drop(&mut self) { - unsafe { - gl::DeleteBuffers(1, &mut self.descriptors_buffer); - gl::DeleteBuffers(1, &mut self.indices_buffer); - gl::DeleteBuffers(1, &mut self.vertices_buffer); - } - } -} - -impl Outlines { - #[doc(hidden)] - #[inline] - pub fn vertices_buffer(&self) -> GLuint { - self.vertices_buffer - } - - #[doc(hidden)] - #[inline] - pub fn indices_buffer(&self) -> GLuint { - self.indices_buffer - } - - #[doc(hidden)] - #[inline] - pub fn descriptors_buffer(&self) -> GLuint { - self.descriptors_buffer - } - - #[doc(hidden)] - #[inline] - pub fn descriptor(&self, glyph_index: u16) -> Option<&GlyphDescriptor> { - self.descriptors.get(glyph_index as usize) - } - - #[doc(hidden)] - #[inline] - pub fn indices_count(&self) -> usize { - self.indices_count - } - - /// Returns the glyph rectangle in font units. - #[inline] - pub fn glyph_bounds(&self, glyph_index: u32) -> GlyphBounds { - self.descriptors[glyph_index as usize].bounds - } - - /// Returns the glyph rectangle in fractional pixels. - #[inline] - pub fn glyph_subpixel_bounds(&self, glyph_index: u16, point_size: f32) -> GlyphSubpixelBounds { - self.descriptors[glyph_index as usize].subpixel_bounds(point_size) - } - - /// Returns the ID of the glyph with the given index. - #[inline] - pub fn glyph_id(&self, glyph_index: u16) -> u16 { - self.descriptors[glyph_index as usize].glyph_id - } - - /// Returns the units per em for the glyph with the given index. - #[inline] - pub fn glyph_units_per_em(&self, glyph_index: u16) -> u32 { - self.descriptors[glyph_index as usize].units_per_em - } -} - -#[doc(hidden)] -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct GlyphDescriptor { - bounds: GlyphBounds, - units_per_em: u32, - start_point: u32, - start_index: u32, - glyph_id: u16, -} - -impl GlyphDescriptor { - #[doc(hidden)] - #[inline] - pub fn start_index(&self) -> u32 { - self.start_index - } - - #[doc(hidden)] - #[inline] - fn subpixel_bounds(&self, point_size: f32) -> GlyphSubpixelBounds { - self.bounds.subpixel_bounds(self.units_per_em as u16, point_size) - } -} - -#[doc(hidden)] -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct Vertex { - x: i16, - y: i16, - glyph_index: u16, -} - -/// The boundaries of the glyph in fractional pixels. -#[derive(Copy, Clone, Debug)] -pub struct GlyphSubpixelBounds { - pub left: f32, - pub bottom: f32, - pub right: f32, - pub top: f32, -} - -impl GlyphSubpixelBounds { - /// Scales the bounds by the given amount. - #[inline] - pub fn scale(&mut self, factor: f32) { - self.left *= factor; - self.bottom *= factor; - self.right *= factor; - self.top *= factor; - } - - /// Rounds these bounds out to the nearest pixel. - #[inline] - pub fn round_out(&self) -> GlyphPixelBounds { - GlyphPixelBounds { - left: self.left.floor() as i32, - bottom: self.bottom.floor() as i32, - right: self.right.ceil() as i32, - top: self.top.ceil() as i32, - } - } - - /// Returns the total size of the glyph in fractional pixels. - #[inline] - pub fn size(&self) -> Size2D { - Size2D::new(self.right - self.left, self.top - self.bottom) - } -} - -/// The boundaries of the glyph, rounded out to the nearest pixel. -#[derive(Copy, Clone, Debug)] -pub struct GlyphPixelBounds { - pub left: i32, - pub bottom: i32, - pub right: i32, - pub top: i32, -} - -impl GlyphPixelBounds { - /// Returns the total size of the glyph in whole pixels. - #[inline] - pub fn size(&self) -> Size2D { - Size2D::new(self.right - self.left, self.top - self.bottom) - } -} - -/// The boundaries of a glyph in font units. -#[derive(Copy, Clone, Default, Debug)] -pub struct GlyphBounds { - pub left: i32, - pub bottom: i32, - pub right: i32, - pub top: i32, -} - -impl GlyphBounds { - /// Given the units per em of the font and the point size, returns the fractional boundaries of - /// this glyph. - #[inline] - pub fn subpixel_bounds(&self, units_per_em: u16, point_size: f32) -> GlyphSubpixelBounds { - let pixels_per_unit = point_size / units_per_em as f32; - GlyphSubpixelBounds { - left: self.left as f32 * pixels_per_unit, - bottom: self.bottom as f32 * pixels_per_unit, - right: self.right as f32 * pixels_per_unit, - top: self.top as f32 * pixels_per_unit, - } - } - - /// Returns the total size of the glyph in font units. - #[inline] - pub fn size(&self) -> Size2D { - Size2D::new(self.right - self.left, self.top - self.bottom) - } -} - -/// A helper object to construct a single path. -pub struct PathBuilder<'a> { - outline_builder: &'a mut OutlineBuilder, - vbo_start_index: u32, - vbo_end_index: u32, - ibo_start_index: u32, - point_index_in_path: u16, - glyph_index: u16, -} - -impl<'a> PathBuilder<'a> { - fn add_point(&mut self, point: &Point2D) { - self.outline_builder.vertices.push(Vertex { - x: point.x, - y: point.y, - glyph_index: self.glyph_index, - }); - - self.point_index_in_path += 1; - self.vbo_end_index += 1; - } - - /// Moves the pen to the given point. - pub fn move_to(&mut self, point: &Point2D) { - self.add_point(point) - } - - /// Draws a straight line to the given point. - /// - /// Panics if a `move_to` has not been issued anywhere prior to this operation. - pub fn line_to(&mut self, point: &Point2D) { - if self.point_index_in_path == 0 { - panic!("`line_to` must not be the first operation in a path") - } - - self.add_point(point); - - self.outline_builder.indices.extend_from_slice(&[ - self.vbo_end_index - 2, - 0, - 0, - self.vbo_end_index - 1, - ]) - } - - /// Draws a quadratic Bézier curve to the given point. - /// - /// Panics if a `move_to` has not been issued anywhere prior to this operation. - pub fn quad_curve_to(&mut self, p1: &Point2D, p2: &Point2D) { - if self.point_index_in_path == 0 { - panic!("`quad_curve_to` must not be the first operation in a path") - } - - self.add_point(p1); - self.add_point(p2); - - self.outline_builder.indices.extend_from_slice(&[ - self.vbo_end_index - 3, - self.vbo_end_index - 2, - self.vbo_end_index - 2, - self.vbo_end_index - 1, - ]) - } - - /// Draws a cubic Bézier curve to the given point. - /// - /// Panics if a `move_to` has not been issued anywhere prior to this operation. - pub fn cubic_curve_to(&mut self, p1: &Point2D, p2: &Point2D, p3: &Point2D) { - if self.point_index_in_path == 0 { - panic!("`cubic_curve_to` must not be the first operation in a path") - } - - self.add_point(p1); - self.add_point(p2); - self.add_point(p3); - - self.outline_builder.indices.extend_from_slice(&[ - self.vbo_end_index - 4, - self.vbo_end_index - 3, - self.vbo_end_index - 2, - self.vbo_end_index - 1 - ]) - } - - /// Finishes the path. - pub fn finish(self, bounds: &GlyphBounds, units_per_em: u32, glyph_id: u16) { - self.outline_builder.descriptors.push(GlyphDescriptor { - bounds: *bounds, - units_per_em: units_per_em, - start_point: self.vbo_start_index, - start_index: self.ibo_start_index, - glyph_id: glyph_id, - }) - } -} - diff --git a/pathfinder-classic/src/rasterizer.rs b/pathfinder-classic/src/rasterizer.rs deleted file mode 100644 index 6e186a4d..00000000 --- a/pathfinder-classic/src/rasterizer.rs +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 GPU rasterizer for glyphs. - -use atlas::Atlas; -use compute_shader::device::Device; -use compute_shader::image::{Format, Image}; -use compute_shader::instance::{Instance, ShadingLanguage}; -use compute_shader::profile_event::ProfileEvent; -use compute_shader::program::Program; -use compute_shader::queue::{Queue, Uniform}; -use coverage::CoverageBuffer; -use error::{InitError, RasterError}; -use euclid::rect::Rect; -use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint, GLvoid}; -use gl; -use outline::{Outlines, Vertex}; -use std::ascii::AsciiExt; -use std::env; -use std::fs::File; -use std::io::Read; -use std::mem; -use std::path::{Path, PathBuf}; -use std::ptr; - -static COMPUTE_PREAMBLE_FILENAME: &'static str = "preamble.cs.glsl"; - -static ACCUM_COMMON_CL_SHADER_FILENAME: &'static str = "accum_common.cl"; -static ACCUM_COMMON_COMPUTE_SHADER_FILENAME: &'static str = "accum_common.cs.glsl"; - -static ACCUM_GRAY_CL_SHADER_FILENAME: &'static str = "accum_gray.cl"; -static ACCUM_SUBPIXEL_CL_SHADER_FILENAME: &'static str = "accum_subpixel.cl"; - -static ACCUM_GRAY_COMPUTE_SHADER_FILENAME: &'static str = "accum.cs.glsl"; -static ACCUM_SUBPIXEL_COMPUTE_SHADER_FILENAME: &'static str = "accum.cs.glsl"; - -static DRAW_VERTEX_SHADER_FILENAME: &'static str = "draw.vs.glsl"; -static DRAW_TESS_CONTROL_SHADER_FILENAME: &'static str = "draw.tcs.glsl"; -static DRAW_TESS_EVALUATION_SHADER_FILENAME: &'static str = "draw.tes.glsl"; -static DRAW_GEOMETRY_SHADER_FILENAME: &'static str = "draw.gs.glsl"; -static DRAW_FRAGMENT_SHADER_FILENAME: &'static str = "draw.fs.glsl"; - -/// A GPU rasterizer for glyphs. -pub struct Rasterizer { - device: Device, - queue: Queue, - shading_language: ShadingLanguage, - draw_program: GLuint, - accum_program_gray_r8: Program, - accum_program_gray_rgba8: Program, - accum_program_subpixel_rgba8: Program, - draw_vertex_array: GLuint, - draw_position_attribute: GLint, - draw_glyph_index_attribute: GLint, - draw_atlas_size_uniform: GLint, - draw_subpixel_aa_uniform: GLint, - draw_glyph_descriptors_uniform: GLuint, - draw_image_descriptors_uniform: GLuint, - draw_query: GLuint, - options: RasterizerOptions, -} - -/// Profiling events that can be used to profile Pathfinder's performance. -pub struct DrawAtlasProfilingEvents { - /// An OpenGL timer query object that measures the length of time that Pathfinder took to draw - /// the glyph edges. - /// - /// You can get the results with `gl::GetQueryObjectui64v(..., gl::TIME_ELAPSED, ...)`. - pub draw: GLuint, - - /// A `compute-shader` profile event that measures the length of time that Pathfinder took to - /// perform the accumulation (fill) step. - pub accum: ProfileEvent, -} - -impl Rasterizer { - /// Creates a new rasterizer. - /// - /// This rasterizer can be used for as many draw calls as you like. - /// - /// * `instance` is the `compute-shader` instance to use. - /// - /// * `device` is the compute device to use. - /// - /// * `queue` is the queue on that compute device to use. - /// - /// * `options` is a set of options that control the rasterizer's behavior. - pub fn new(instance: &Instance, device: Device, queue: Queue, options: RasterizerOptions) - -> Result { - let (draw_program, draw_position_attribute, draw_glyph_index_attribute); - let (draw_glyph_descriptors_uniform, draw_image_descriptors_uniform); - let (draw_atlas_size_uniform, draw_subpixel_aa_uniform); - let (mut draw_vertex_array, mut draw_query) = (0, 0); - unsafe { - draw_program = gl::CreateProgram(); - - let vertex_shader = try!(compile_gl_shader(gl::VERTEX_SHADER, - "Vertex shader", - DRAW_VERTEX_SHADER_FILENAME, - &options.shader_path)); - gl::AttachShader(draw_program, vertex_shader); - let fragment_shader = try!(compile_gl_shader(gl::FRAGMENT_SHADER, - "Fragment shader", - DRAW_FRAGMENT_SHADER_FILENAME, - &options.shader_path)); - gl::AttachShader(draw_program, fragment_shader); - - if options.force_geometry_shader { - let geometry_shader = try!(compile_gl_shader(gl::GEOMETRY_SHADER, - "Geometry shader", - DRAW_GEOMETRY_SHADER_FILENAME, - &options.shader_path)); - gl::AttachShader(draw_program, geometry_shader); - } else { - let tess_control_shader = try!(compile_gl_shader(gl::TESS_CONTROL_SHADER, - "Tessellation control shader", - DRAW_TESS_CONTROL_SHADER_FILENAME, - &options.shader_path)); - gl::AttachShader(draw_program, tess_control_shader); - let tess_evaluation_shader = - try!(compile_gl_shader(gl::TESS_EVALUATION_SHADER, - "Tessellation evaluation shader", - DRAW_TESS_EVALUATION_SHADER_FILENAME, - &options.shader_path)); - gl::AttachShader(draw_program, tess_evaluation_shader); - } - - gl::LinkProgram(draw_program); - - try!(check_gl_object_status(draw_program, - gl::LINK_STATUS, - gl::GetProgramiv, - gl::GetProgramInfoLog).map_err(InitError::LinkFailed)); - - gl::GenVertexArrays(1, &mut draw_vertex_array); - - draw_position_attribute = - gl::GetAttribLocation(draw_program, b"aPosition\0".as_ptr() as *const GLchar); - draw_glyph_index_attribute = - gl::GetAttribLocation(draw_program, b"aGlyphIndex\0".as_ptr() as *const GLchar); - - draw_atlas_size_uniform = - gl::GetUniformLocation(draw_program, b"uAtlasSize\0".as_ptr() as *const GLchar); - draw_subpixel_aa_uniform = - gl::GetUniformLocation(draw_program, b"uSubpixelAA\0".as_ptr() as *const GLchar); - draw_glyph_descriptors_uniform = - gl::GetUniformBlockIndex(draw_program, - b"ubGlyphDescriptors\0".as_ptr() as *const GLchar); - draw_image_descriptors_uniform = - gl::GetUniformBlockIndex(draw_program, - b"ubImageDescriptors\0".as_ptr() as *const GLchar); - - gl::GenQueries(1, &mut draw_query) - } - - // FIXME(pcwalton): Don't panic if this fails to compile; just return an error. - let (accum_filename_common, accum_filename_gray, accum_filename_subpixel); - let shading_language = instance.shading_language(); - match shading_language { - ShadingLanguage::Cl => { - accum_filename_common = ACCUM_COMMON_CL_SHADER_FILENAME; - accum_filename_gray = ACCUM_GRAY_CL_SHADER_FILENAME; - accum_filename_subpixel = ACCUM_SUBPIXEL_CL_SHADER_FILENAME; - } - ShadingLanguage::Glsl => { - accum_filename_common = ACCUM_COMMON_COMPUTE_SHADER_FILENAME; - accum_filename_gray = ACCUM_GRAY_COMPUTE_SHADER_FILENAME; - accum_filename_subpixel = ACCUM_SUBPIXEL_COMPUTE_SHADER_FILENAME; - } - } - - let mut accum_path_common = options.shader_path.to_owned(); - let mut accum_path_gray = accum_path_common.clone(); - let mut accum_path_subpixel = accum_path_common.clone(); - accum_path_common.push(accum_filename_common); - accum_path_gray.push(accum_filename_gray); - accum_path_subpixel.push(accum_filename_subpixel); - let mut accum_file_common = match File::open(&accum_path_common) { - Err(error) => return Err(InitError::ShaderUnreadable(error)), - Ok(file) => file, - }; - let mut accum_file_gray = match File::open(&accum_path_gray) { - Err(error) => return Err(InitError::ShaderUnreadable(error)), - Ok(file) => file, - }; - let mut accum_file_subpixel = match File::open(&accum_path_subpixel) { - Err(error) => return Err(InitError::ShaderUnreadable(error)), - Ok(file) => file, - }; - - let mut compute_preamble_source = String::new(); - match shading_language { - ShadingLanguage::Cl => {} - ShadingLanguage::Glsl => { - let mut compute_preamble_path = options.shader_path.to_owned(); - compute_preamble_path.push(COMPUTE_PREAMBLE_FILENAME); - let mut compute_preamble_file = match File::open(&compute_preamble_path) { - Err(error) => return Err(InitError::ShaderUnreadable(error)), - Ok(file) => file, - }; - - if compute_preamble_file.read_to_string(&mut compute_preamble_source).is_err() { - return Err(InitError::CompileFailed("Compute shader", - "Invalid UTF-8".to_string())) - } - } - } - - let mut accum_source_common = String::new(); - let mut accum_source_gray = String::new(); - let mut accum_source_subpixel = String::new(); - if accum_file_common.read_to_string(&mut accum_source_common).is_err() { - return Err(InitError::CompileFailed("Compute shader", "Invalid UTF-8".to_string())) - } - if accum_file_gray.read_to_string(&mut accum_source_gray).is_err() { - return Err(InitError::CompileFailed("Compute shader", "Invalid UTF-8".to_string())) - } - if accum_file_subpixel.read_to_string(&mut accum_source_subpixel).is_err() { - return Err(InitError::CompileFailed("Compute shader", "Invalid UTF-8".to_string())) - } - - let accum_source_gray_r8 = format!("{}\n#define IMAGE_FORMAT r8\n{}\n{}", - compute_preamble_source, - accum_source_common, - accum_source_gray); - let accum_source_gray_rgba8 = format!("{}\n#define IMAGE_FORMAT rgba8\n{}\n{}", - compute_preamble_source, - accum_source_common, - accum_source_gray); - let accum_source_subpixel_rgba8 = format!("{}\n#define IMAGE_FORMAT rgba8\n{}\n{}", - compute_preamble_source, - accum_source_common, - accum_source_subpixel); - - let accum_program_gray_r8 = try!(device.create_program(&accum_source_gray_r8) - .map_err(InitError::ComputeError)); - let accum_program_gray_rgba8 = try!(device.create_program(&accum_source_gray_rgba8) - .map_err(InitError::ComputeError)); - let accum_program_subpixel_rgba8 = try!(device.create_program(&accum_source_subpixel_rgba8) - .map_err(InitError::ComputeError)); - - Ok(Rasterizer { - device: device, - queue: queue, - shading_language: shading_language, - draw_program: draw_program, - accum_program_gray_r8: accum_program_gray_r8, - accum_program_gray_rgba8: accum_program_gray_rgba8, - accum_program_subpixel_rgba8: accum_program_subpixel_rgba8, - draw_vertex_array: draw_vertex_array, - draw_position_attribute: draw_position_attribute, - draw_glyph_index_attribute: draw_glyph_index_attribute, - draw_atlas_size_uniform: draw_atlas_size_uniform, - draw_subpixel_aa_uniform: draw_subpixel_aa_uniform, - draw_glyph_descriptors_uniform: draw_glyph_descriptors_uniform, - draw_image_descriptors_uniform: draw_image_descriptors_uniform, - draw_query: draw_query, - options: options, - }) - } - - /// Draws the supplied font atlas into the texture image at the given location. - /// - /// * `image` is the texture image that this rasterizer will draw into. - /// - /// * `rect` is the pixel boundaries of the atlas inside that image. - /// - /// * `atlas` is the glyph atlas to render. - /// - /// * `outlines` specifies the outlines for the font associated with that atlas. - /// - /// * `coverage_buffer` is a coverage buffer to use (see `CoverageBuffer`). This can be reused - /// from call to call. It must be at least as large as the atlas. - /// - /// Note that, if the atlas is empty, `RasterError::NoGlyphsToDraw` is returned. - pub fn draw_atlas(&self, - image: &Image, - rect: &Rect, - atlas: &Atlas, - outlines: &Outlines, - coverage_buffer: &CoverageBuffer) - -> Result { - if atlas.is_empty() { - return Err(RasterError::NoGlyphsToDraw) - } - - unsafe { - gl::BindFramebuffer(gl::FRAMEBUFFER, coverage_buffer.framebuffer()); - - // Save the old viewport so we can restore it later. - let mut old_viewport: [GLint; 4] = [0; 4]; - gl::GetIntegerv(gl::VIEWPORT, old_viewport.as_mut_ptr()); - - // Set up our new viewport. - let viewport_width = if atlas.uses_subpixel_antialiasing() { - rect.size.width * 3 - } else { - rect.size.width - }; - gl::Viewport(0, 0, viewport_width as GLint, rect.size.height as GLint); - - // TODO(pcwalton): Scissor to the image rect to clear faster? - gl::ClearColor(0.0, 0.0, 0.0, 1.0); - gl::Clear(gl::COLOR_BUFFER_BIT); - - gl::BindVertexArray(self.draw_vertex_array); - gl::UseProgram(self.draw_program); - - // Set up the buffer layout. - gl::BindBuffer(gl::ARRAY_BUFFER, outlines.vertices_buffer()); - gl::VertexAttribIPointer(self.draw_position_attribute as GLuint, - 2, - gl::SHORT, - mem::size_of::() as GLint, - 0 as *const GLvoid); - gl::VertexAttribIPointer(self.draw_glyph_index_attribute as GLuint, - 1, - gl::UNSIGNED_SHORT, - mem::size_of::() as GLint, - mem::size_of::<(i16, i16)>() as *const GLvoid); - gl::EnableVertexAttribArray(self.draw_position_attribute as GLuint); - gl::EnableVertexAttribArray(self.draw_glyph_index_attribute as GLuint); - - gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, outlines.indices_buffer()); - - // Don't bind the atlas uniform buffers (binding point 2) here; the batches will do - // that on their own. - gl::BindBufferBase(gl::UNIFORM_BUFFER, 1, outlines.descriptors_buffer()); - gl::UniformBlockBinding(self.draw_program, self.draw_glyph_descriptors_uniform, 1); - gl::UniformBlockBinding(self.draw_program, self.draw_image_descriptors_uniform, 2); - - gl::Uniform2ui(self.draw_atlas_size_uniform, viewport_width, rect.size.height); - gl::Uniform1i(self.draw_subpixel_aa_uniform, - atlas.uses_subpixel_antialiasing() as GLint); - - gl::PatchParameteri(gl::PATCH_VERTICES, 4); - - // Use blending on our floating point framebuffer to accumulate coverage. - gl::Enable(gl::BLEND); - gl::BlendEquation(gl::FUNC_ADD); - gl::BlendFunc(gl::ONE, gl::ONE); - - // Enable backface culling. See comments in `draw.tcs.glsl` for more information - // regarding why this is necessary. - gl::CullFace(gl::BACK); - gl::FrontFace(gl::CCW); - gl::Enable(gl::CULL_FACE); - - // If we're using a geometry shader for debugging, we draw fake triangles. Otherwise, - // we use patches. - let primitive = if self.options.force_geometry_shader { - gl::TRIANGLES - } else { - gl::PATCHES - }; - - // Now draw the glyph ranges. - gl::BeginQuery(gl::TIME_ELAPSED, self.draw_query); - atlas.draw(primitive); - gl::EndQuery(gl::TIME_ELAPSED); - - gl::Disable(gl::CULL_FACE); - gl::Disable(gl::BLEND); - - // Restore our old framebuffer and viewport. - gl::BindFramebuffer(gl::FRAMEBUFFER, 0); - gl::Viewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]); - - // FIXME(pcwalton): We should have some better synchronization here if we're using - // OpenCL, but I don't know how to do that portably (i.e. on Mac…) Just using - // `glFlush()` seems to work in practice. - gl::Flush(); - - if self.shading_language == ShadingLanguage::Glsl { - gl::MemoryBarrier(gl::ALL_BARRIER_BITS); - } - } - - let accum_uniforms = [ - (0, Uniform::Image(image)), - (1, Uniform::Image(coverage_buffer.image())), - (2, Uniform::UVec4([rect.origin.x, rect.origin.y, rect.max_x(), rect.max_y()])), - (3, Uniform::U32(atlas.shelf_height())), - ]; - - let accum_program = match (image.format(), atlas.uses_subpixel_antialiasing()) { - (Ok(Format::R8), false) => &self.accum_program_gray_r8, - (Ok(Format::RGBA8), false) => &self.accum_program_gray_rgba8, - (Ok(Format::RGBA8), true) => &self.accum_program_subpixel_rgba8, - (Ok(_), _) => return Err(RasterError::UnsupportedImageFormat), - (Err(err), _) => return Err(RasterError::ComputeError(err)), - }; - - let accum_event = try!(self.queue.submit_compute(accum_program, - &[atlas.shelf_columns()], - &accum_uniforms, - &[]).map_err(RasterError::ComputeError)); - - Ok(DrawAtlasProfilingEvents { - draw: self.draw_query, - accum: accum_event, - }) - } - - /// Returns the GPU compute device that this rasterizer is using. - #[inline] - pub fn device(&self) -> &Device { - &self.device - } - - /// Returns the GPU compute queue that this rasterizer is using. - #[inline] - pub fn queue(&self) -> &Queue { - &self.queue - } -} - -fn compile_gl_shader(shader_type: GLuint, - description: &'static str, - filename: &str, - shader_path: &Path) - -> Result { - unsafe { - let mut path = shader_path.to_owned(); - path.push(filename); - - let mut file = match File::open(&path) { - Err(error) => return Err(InitError::ShaderUnreadable(error)), - Ok(file) => file, - }; - - let mut source = String::new(); - if file.read_to_string(&mut source).is_err() { - return Err(InitError::CompileFailed(description, "Invalid UTF-8".to_string())) - } - - let shader = gl::CreateShader(shader_type); - gl::ShaderSource(shader, 1, &(source.as_ptr() as *const GLchar), &(source.len() as GLint)); - gl::CompileShader(shader); - match check_gl_object_status(shader, - gl::COMPILE_STATUS, - gl::GetShaderiv, - gl::GetShaderInfoLog) { - Ok(_) => Ok(shader), - Err(info_log) => Err(InitError::CompileFailed(description, info_log)), - } - } -} - -fn check_gl_object_status(object: GLuint, - parameter: GLenum, - get_status: unsafe fn(GLuint, GLenum, *mut GLint), - get_log: unsafe fn(GLuint, GLsizei, *mut GLsizei, *mut GLchar)) - -> Result<(), String> { - unsafe { - let mut status = 0; - get_status(object, parameter, &mut status); - if status == gl::TRUE as i32 { - return Ok(()) - } - - let mut info_log_length = 0; - get_status(object, gl::INFO_LOG_LENGTH, &mut info_log_length); - - let mut info_log = vec![0; info_log_length as usize]; - get_log(object, info_log_length, ptr::null_mut(), info_log.as_mut_ptr() as *mut GLchar); - - match String::from_utf8(info_log) { - Ok(string) => Err(string), - Err(_) => Err("(not UTF-8)".to_owned()), - } - } -} - -/// Options that control Pathfinder's behavior. -#[derive(Clone, Debug)] -pub struct RasterizerOptions { - /// The path to the shaders. - /// - /// If not specified, then the current directory is used. This is probably not what you want. - /// The corresponding environment variable is `PATHFINDER_SHADER_PATH`. - pub shader_path: PathBuf, - /// If true, then a geometry shader is used instead of a tessellation shader. - /// - /// This will probably negatively impact performance. This should be considered a debugging - /// feature only. - /// - /// The default is false. The corresponding environment variable is - /// `PATHFINDER_FORCE_GEOMETRY_SHADER`. - pub force_geometry_shader: bool, -} - -impl Default for RasterizerOptions { - fn default() -> RasterizerOptions { - RasterizerOptions { - shader_path: PathBuf::from("."), - force_geometry_shader: false, - } - } -} - -impl RasterizerOptions { - /// Takes rasterization options from environment variables. - /// - /// See the fields of `RasterizerOptions` for info on the settings, including the environment - /// variables that control them. - /// - /// Boolean variables may be set to true by setting the corresponding variable to `"on"`, - /// `"yes"`, or `1`; they may be set to false with `"off"`, `"no"`, or `0`. - /// - /// Environment variables not set cause their associated settings to take on default values. - pub fn from_env() -> Result { - let shader_path = match env::var("PATHFINDER_SHADER_PATH") { - Ok(ref string) => PathBuf::from(string), - Err(_) => PathBuf::from("."), - }; - - let force_geometry_shader = match env::var("PATHFINDER_FORCE_GEOMETRY_SHADER") { - Ok(ref string) if string.eq_ignore_ascii_case("on") || - string.eq_ignore_ascii_case("yes") || - string.eq_ignore_ascii_case("1") => true, - Ok(ref string) if string.eq_ignore_ascii_case("off") || - string.eq_ignore_ascii_case("no") || - string.eq_ignore_ascii_case("0") => false, - Err(_) => false, - Ok(_) => return Err(InitError::InvalidSetting), - }; - - Ok(RasterizerOptions { - shader_path: shader_path, - force_geometry_shader: force_geometry_shader, - }) - } -} - diff --git a/pathfinder-classic/src/rect_packer.rs b/pathfinder-classic/src/rect_packer.rs deleted file mode 100644 index 4c078f58..00000000 --- a/pathfinder-classic/src/rect_packer.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 euclid::{Point2D, Rect, Size2D}; - -pub struct RectPacker { - free_rects: Vec>, - available_width: u32, - shelf_height: u32, - shelf_count: u32, - /// The amount of horizontal space allocated in the last shelf. - width_of_last_shelf: u32, -} - -impl RectPacker { - #[inline] - pub fn new(available_width: u32, shelf_height: u32) -> RectPacker { - RectPacker { - free_rects: vec![], - available_width: available_width, - shelf_height: shelf_height, - shelf_count: 0, - width_of_last_shelf: 0, - } - } - - /// Packs a rectangle of the given size. - /// - /// Returns the top-left position of the rectangle or an error if there is no space left. - pub fn pack(&mut self, size: &Size2D) -> Result, ()> { - // Add a one-pixel border to prevent bleed. - let alloc_size = *size + Size2D::new(2, 2); - - // If the allocation size is less than our shelf height, we will always fail. - if alloc_size.height > self.shelf_height { - return Err(()) - } - - let chosen_index_and_rect = - self.free_rects - .iter() - .enumerate() - .filter(|&(_, rect)| { - alloc_size.width <= rect.size.width && alloc_size.height <= rect.size.height - }) - .min_by(|&(_, a), &(_, b)| area(a).cmp(&area(b))) - .map(|(index, rect)| (index, *rect)); - - let chosen_rect; - match chosen_index_and_rect { - None => { - // Make a new shelf. - chosen_rect = Rect::new(Point2D::new(0, self.shelf_height * self.shelf_count), - Size2D::new(self.available_width, self.shelf_height)); - self.shelf_count += 1; - self.width_of_last_shelf = 0 - } - Some((index, rect)) => { - self.free_rects.swap_remove(index); - chosen_rect = rect; - } - } - - // Guillotine to bottom. - let free_below = - Rect::new(Point2D::new(chosen_rect.origin.x, chosen_rect.origin.y + alloc_size.height), - Size2D::new(alloc_size.width, chosen_rect.size.height - alloc_size.height)); - if !free_below.is_empty() { - self.free_rects.push(free_below); - } - - // Guillotine to right. - let free_to_right = - Rect::new(Point2D::new(chosen_rect.origin.x + alloc_size.width, chosen_rect.origin.y), - Size2D::new(chosen_rect.size.width - alloc_size.width, - chosen_rect.size.height)); - if !free_to_right.is_empty() { - self.free_rects.push(free_to_right); - } - - // Update width of last shelf if necessary. - let on_last_shelf = chosen_rect.max_y() >= self.shelf_height * (self.shelf_count - 1); - if on_last_shelf && self.width_of_last_shelf < chosen_rect.max_x() { - self.width_of_last_shelf = chosen_rect.max_x() - } - - let object_origin = chosen_rect.origin + Point2D::new(1, 1); - Ok(object_origin) - } - - #[inline] - pub fn shelf_height(&self) -> u32 { - self.shelf_height - } - - #[inline] - pub fn shelf_columns(&self) -> u32 { - let full_shelf_count = if self.shelf_count == 0 { - 0 - } else { - self.shelf_count - 1 - }; - - full_shelf_count * self.available_width + self.width_of_last_shelf - } -} - -#[inline] -fn area(rect: &Rect) -> u32 { - rect.size.width * rect.size.height -} - diff --git a/pathfinder-classic/src/shaper.rs b/pathfinder-classic/src/shaper.rs deleted file mode 100644 index 8e6fb5fc..00000000 --- a/pathfinder-classic/src/shaper.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 very basic text shaper for simple needs. -//! -//! Do not use this for international or high-quality text. This shaper does not do kerning, -//! ligation, or advanced typography features (`GSUB`, `GPOS`, text morphing). Consider HarfBuzz or -//! the system shaper instead. - -use charmap::GlyphMapping; -use font::Font; - -/// Shapes the given Unicode text in the given font, returning the proper position for each glyph. -/// -/// See the description of this module for caveats. -/// -/// For proper operation, the given `glyph_mapping` must include all the glyphs necessary to render -/// the string. -pub fn shape_text(font: &Font, glyph_mapping: &GlyphMapping, string: &str) -> Vec { - let mut chars = string.chars().peekable(); - let mut next_glyph_id = None; - let mut result = vec![]; - - while let Some(ch) = chars.next() { - let glyph_id = match next_glyph_id.take() { - None => glyph_mapping.glyph_for(ch as u32).unwrap_or(0), - Some(next_glyph_id) => next_glyph_id, - }; - - let mut advance = match font.metrics_for_glyph(glyph_id) { - Err(_) => 0, - Ok(metrics) => metrics.advance_width as i16, - }; - - if let Some(&next_char) = chars.peek() { - let next_glyph = glyph_mapping.glyph_for(next_char as u32).unwrap_or(0); - next_glyph_id = Some(next_glyph); - advance += font.kerning_for_glyph_pair(glyph_id, next_glyph) - } - - result.push(GlyphPos { - glyph_id: glyph_id, - advance: advance, - }) - } - - result -} - -/// The position of a glyph after shaping. -#[derive(Clone, Copy, Debug)] -pub struct GlyphPos { - /// The glyph ID to emit. - pub glyph_id: u16, - /// The amount to move the cursor forward *after* emitting this glyph. - pub advance: i16, -} - diff --git a/pathfinder-classic/src/tables/cff.rs b/pathfinder-classic/src/tables/cff.rs deleted file mode 100644 index fdaca182..00000000 --- a/pathfinder-classic/src/tables/cff.rs +++ /dev/null @@ -1,597 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use euclid::Point2D; -use font::{FontTable, Point, PointKind}; -use outline::GlyphBounds; -use std::cmp; -use std::u16; -use util::Jump; - -pub const TAG: u32 = ((b'C' as u32) << 24) | - ((b'F' as u32) << 16) | - ((b'F' as u32) << 8) | - (b' ' as u32); - -#[derive(Clone, Copy, Debug)] -pub struct CffTable<'a> { - // The offset of the char strings INDEX. - char_strings: u32, - table: FontTable<'a>, -} - -impl<'a> CffTable<'a> { - #[inline] - pub fn new(table: FontTable) -> Result { - let mut reader = table.bytes; - - // Check version. - let major = try!(reader.read_u8().map_err(FontError::eof)); - let minor = try!(reader.read_u8().map_err(FontError::eof)); - if major != 1 || minor != 0 { - return Err(FontError::UnsupportedCffVersion) - } - - // Skip the header. - let hdr_size = try!(reader.read_u8().map_err(FontError::eof)); - try!(reader.jump(hdr_size as usize - 3).map_err(FontError::eof)); - - // Skip the name INDEX. - // - // TODO(pcwalton): What to do if there are multiple fonts here? - try!(skip_index(&mut reader)); - - // Get the top DICT for our font. - if try!(find_in_index(&mut reader, 0)).is_none() { - return Err(FontError::CffTopDictNotFound) - } - - // Find the CharStrings offset within the top DICT. - let char_strings = try!(get_integer_in_dict(&mut reader, 17)); - - // Skip the string INDEX. - try!(skip_index(&mut reader)); - - // Ignore the global subr INDEX for now. - // - // TODO(pcwalton): Support global subroutines. - - Ok(CffTable { - char_strings: char_strings as u32, - table: table, - }) - } - - pub fn for_each_point(&self, glyph_id: u16, mut callback: F) - -> Result<(), FontError> where F: FnMut(&Point) { - let mut reader = self.table.bytes; - try!(reader.jump(self.char_strings as usize).map_err(FontError::eof)); - - let char_string_length = match try!(find_in_index(&mut reader, glyph_id)) { - Some(char_string_length) => char_string_length, - None => return Err(FontError::UnexpectedEof), - }; - - let mut reader = &reader[0..char_string_length as usize]; - let mut stack = EvaluationStack::new(); - let (mut start, mut pos) = (Point2D::new(0, 0), Point2D::new(0, 0)); - let mut index_in_contour = 0; - let mut hint_count = 0; - - // FIXME(pcwalton): This shouldn't panic on stack bounds check failures. - while let Ok(b0) = reader.read_u8() { - match b0 { - 32...246 => try!(stack.push(b0 as i32 - 139)), - 247...250 => { - let b1 = try!(reader.read_u8().map_err(FontError::eof)); - try!(stack.push((b0 as i32 - 247) * 256 + b1 as i32 + 108)) - } - 251...254 => { - let b1 = try!(reader.read_u8().map_err(FontError::eof)); - try!(stack.push((b0 as i32 - 251) * -256 - b1 as i32 - 108)) - } - 255 => { - // FIXME(pcwalton): Don't truncate the lower 16 bits. - try!(stack.push(try!(reader.read_i32::().map_err(FontError::eof)) >> - 16)) - } - 28 => { - let number = try!(reader.read_i16::().map_err(FontError::eof)) as i32; - try!(stack.push(number)) - } - - 4 => { - // |- dy1 vmoveto - close_path_if_necessary(&start, index_in_contour, &mut callback); - pos.y += stack.array[0] as i16; - callback(&Point { - position: pos, - index_in_contour: 0, - kind: PointKind::OnCurve, - }); - start = pos; - index_in_contour = 1; - stack.clear() - } - 5 => { - // |- {dxa dya}+ rlineto - for points in stack.array[0..stack.size as usize].chunks(2) { - pos = pos + Point2D::new(points[0] as i16, points[1] as i16); - callback(&Point { - position: pos, - index_in_contour: index_in_contour, - kind: PointKind::OnCurve, - }); - index_in_contour += 1 - } - stack.clear() - } - 6 => { - // |- dx1 {dya dxb}* hlineto - // |- {dxa dyb}* hlineto - for (i, length) in stack.array[0..stack.size as usize].iter().enumerate() { - if i % 2 == 0 { - pos.x += *length as i16 - } else { - pos.y += *length as i16 - } - callback(&Point { - position: pos, - index_in_contour: index_in_contour, - kind: PointKind::OnCurve, - }); - index_in_contour += 1 - } - stack.clear() - } - 7 => { - // |- dy1 {dxa dyb}* vlineto - // |- {dya dxb}* vlineto - for (i, length) in stack.array[0..stack.size as usize].iter().enumerate() { - if i % 2 == 0 { - pos.y += *length as i16 - } else { - pos.x += *length as i16 - } - callback(&Point { - position: pos, - index_in_contour: index_in_contour, - kind: PointKind::OnCurve, - }); - index_in_contour += 1 - } - stack.clear() - } - 8 => { - // |- {dxa dya dxb dyb dxc dyc}+ rrcurveto (8) - for chunk in stack.array[0..stack.size as usize].chunks(6) { - add_curve(chunk[0] as i16, chunk[1] as i16, - chunk[2] as i16, chunk[3] as i16, - chunk[4] as i16, chunk[5] as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } - stack.clear() - } - 24 => { - // |- {dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline (24) - for chunk in stack.array[0..stack.size as usize - 2].chunks(6) { - add_curve(chunk[0] as i16, chunk[1] as i16, - chunk[2] as i16, chunk[3] as i16, - chunk[4] as i16, chunk[5] as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } - pos = pos + Point2D::new(stack.array[stack.size as usize - 2] as i16, - stack.array[stack.size as usize - 1] as i16); - callback(&Point { - position: pos, - index_in_contour: index_in_contour, - kind: PointKind::OnCurve, - }); - index_in_contour += 1; - stack.clear() - } - 25 => { - // |- {dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve (25) - for chunk in stack.array[0..stack.size as usize - 6].chunks(2) { - pos = pos + Point2D::new(chunk[0] as i16, chunk[1] as i16); - callback(&Point { - position: pos, - index_in_contour: index_in_contour, - kind: PointKind::OnCurve, - }); - index_in_contour += 1; - } - add_curve(stack.array[stack.size as usize - 6] as i16, - stack.array[stack.size as usize - 5] as i16, - stack.array[stack.size as usize - 4] as i16, - stack.array[stack.size as usize - 3] as i16, - stack.array[stack.size as usize - 2] as i16, - stack.array[stack.size as usize - 1] as i16, - &mut pos, - &mut index_in_contour, - &mut callback); - stack.clear() - } - 30 => { - // |- dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30) - // |- {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto (30) - for (i, chunk) in stack.array[0..stack.size as usize].chunks(4).enumerate() { - if chunk.len() != 4 { - break - } - - let dxyf = if i * 4 + 5 == stack.size as usize { - stack.array[stack.size as usize - 1] - } else { - 0 - }; - - if i % 2 == 0 { - add_curve(0, chunk[0] as i16, - chunk[1] as i16, chunk[2] as i16, - chunk[3] as i16, dxyf as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } else { - add_curve(chunk[0] as i16, 0, - chunk[1] as i16, chunk[2] as i16, - dxyf as i16, chunk[3] as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } - } - stack.clear() - } - 31 => { - // |- dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf? hvcurveto (31) - // |- {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? hvcurveto (31) - for (i, chunk) in stack.array[0..stack.size as usize].chunks(4).enumerate() { - if chunk.len() != 4 { - break - } - - let dxyf = if i * 4 + 5 == stack.size as usize { - stack.array[stack.size as usize - 1] - } else { - 0 - }; - - if i % 2 == 0 { - add_curve(chunk[0] as i16, 0, - chunk[1] as i16, chunk[2] as i16, - dxyf as i16, chunk[3] as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } else { - add_curve(0, chunk[0] as i16, - chunk[1] as i16, chunk[2] as i16, - chunk[3] as i16, dxyf as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } - } - stack.clear() - } - 26 => { - // |- dx1? {dya dxb dyb dyc}+ vvcurveto (26) - let start; - if stack.size % 2 == 0 { - start = 0 - } else { - pos.x += stack.array[0] as i16; - start = 1 - } - - for chunk in stack.array[start..stack.size as usize].chunks(4) { - add_curve(0, chunk[0] as i16, - chunk[1] as i16, chunk[2] as i16, - 0, chunk[3] as i16, - &mut pos, - &mut index_in_contour, - &mut callback) - } - stack.clear() - } - 27 => { - // |- dy1? {dxa dxb dyb dxc}+ hhcurveto (27) - let start; - if stack.size % 2 == 0 { - start = 0 - } else { - pos.y += stack.array[0] as i16; - start = 1 - } - - for chunk in stack.array[start..stack.size as usize].chunks(4) { - add_curve(chunk[0] as i16, 0, - chunk[1] as i16, chunk[2] as i16, - chunk[3] as i16, 0, - &mut pos, - &mut index_in_contour, - &mut callback) - } - stack.clear() - } - 14 => { - // endchar - break - } - 1 | 18 => { - // hstem hint (ignored) - hint_count += stack.size as u16 / 2; - stack.clear() - } - 3 | 23 => { - // vstem hint (ignored) - hint_count += stack.size as u16 / 2; - stack.clear() - } - 19 => { - // hintmask (ignored) - // - // First, process an implicit vstem hint. - // - // FIXME(pcwalton): Should only do that if we're in the header. - hint_count += stack.size as u16 / 2; - stack.clear(); - - // Now skip ⌈hint_count / 8⌉ bytes. - let hint_byte_count = (hint_count as usize + 7) / 8; - try!(reader.jump(hint_byte_count).map_err(FontError::eof)); - } - 20 => { - // Skip ⌈hint_count / 8⌉ bytes. - stack.clear(); - let hint_byte_count = (hint_count as usize + 7) / 8; - try!(reader.jump(hint_byte_count).map_err(FontError::eof)); - } - 21 => { - // |- dx1 dy1 rmoveto - close_path_if_necessary(&start, index_in_contour, &mut callback); - pos = pos + Point2D::new(stack.array[0] as i16, stack.array[1] as i16); - callback(&Point { - position: pos, - index_in_contour: 0, - kind: PointKind::OnCurve, - }); - start = pos; - index_in_contour = 1; - stack.clear() - } - 22 => { - // |- dx1 hmoveto - close_path_if_necessary(&start, index_in_contour, &mut callback); - pos.x += stack.array[0] as i16; - callback(&Point { - position: pos, - index_in_contour: 0, - kind: PointKind::OnCurve, - }); - start = pos; - index_in_contour = 1; - stack.clear() - } - - 12 => { - // TODO(pcwalton): Support these extended operators. - let _operator = (12 << 8) | - (try!(reader.read_u8().map_err(FontError::eof)) as u32); - stack.clear(); - return Err(FontError::CffUnimplementedOperator) - } - _ => { - stack.clear(); - return Err(FontError::CffUnimplementedOperator) - } - } - } - - close_path_if_necessary(&start, index_in_contour, &mut callback); - Ok(()) - } - - // TODO(pcwalton): Do some caching, perhaps? - // TODO(pcwalton): Compute this at the same time as `for_each_point`, perhaps? - pub fn glyph_bounds(&self, glyph_id: u16) -> Result { - let mut bounds = GlyphBounds::default(); - try!(self.for_each_point(glyph_id, |point| { - bounds.left = cmp::min(bounds.left, point.position.x as i32); - bounds.bottom = cmp::min(bounds.bottom, point.position.y as i32); - bounds.right = cmp::max(bounds.right, point.position.x as i32); - bounds.top = cmp::max(bounds.top, point.position.y as i32); - })); - Ok(bounds) - } -} - -// Moves the reader to the location of the given element in the index. Returns the length of the -// element if the element was found or `None` otherwise. -fn find_in_index(reader: &mut &[u8], index: u16) -> Result, FontError> { - let count = try!(reader.read_u16::().map_err(FontError::eof)); - if count == 0 { - return Ok(None) - } - - let off_size = try!(reader.read_u8().map_err(FontError::eof)); - - let mut offset_reader = *reader; - try!(offset_reader.jump(off_size as usize * cmp::min(index, count) as usize) - .map_err(FontError::eof)); - let offset = try!(read_offset(&mut offset_reader, off_size)); - - let next_offset = if index < count { - Some(try!(read_offset(&mut offset_reader, off_size)) - offset) - } else { - None - }; - - try!(reader.jump(off_size as usize * (count as usize + 1) + offset as usize - 1) - .map_err(FontError::eof)); - return Ok(next_offset) -} - -// Skips over an INDEX by reading the last element in the offset array and seeking the appropriate -// number of bytes forward. -fn skip_index(reader: &mut &[u8]) -> Result<(), FontError> { - find_in_index(reader, u16::MAX).map(drop) -} - -// Returns the integer with the given operator. -fn get_integer_in_dict(reader: &mut &[u8], operator: u16) -> Result { - let mut last_integer_operand = None; - loop { - let b0 = try!(reader.read_u8().map_err(FontError::eof)); - match b0 { - 32...246 => last_integer_operand = Some(b0 as i32 - 139), - 247...250 => { - let b1 = try!(reader.read_u8().map_err(FontError::eof)); - last_integer_operand = Some((b0 as i32 - 247) * 256 + b1 as i32 + 108) - } - 251...254 => { - let b1 = try!(reader.read_u8().map_err(FontError::eof)); - last_integer_operand = Some(-(b0 as i32 - 251) * 256 - b1 as i32 - 108) - } - 28 => { - last_integer_operand = - Some(try!(reader.read_i16::().map_err(FontError::eof)) as i32) - } - 29 => { - last_integer_operand = - Some(try!(reader.read_i32::().map_err(FontError::eof)) as i32) - } - 30 => { - // TODO(pcwalton): Real numbers. - while (try!(reader.read_u8().map_err(FontError::eof)) & 0xf) != 0xf {} - } - 12 => { - let b1 = try!(reader.read_u8().map_err(FontError::eof)); - if operator == (((b1 as u16) << 8) | (b0 as u16)) { - match last_integer_operand { - Some(last_integer_operand) => return Ok(last_integer_operand), - None => return Err(FontError::CffIntegerNotFound), - } - } - last_integer_operand = None - } - _ => { - if operator == b0 as u16 { - match last_integer_operand { - Some(last_integer_operand) => return Ok(last_integer_operand), - None => return Err(FontError::CffIntegerNotFound), - } - } - last_integer_operand = None - } - } - } -} - -// Reads an Offset with the given size. -fn read_offset(reader: &mut &[u8], size: u8) -> Result { - match size { - 1 => Ok(try!(reader.read_u8().map_err(FontError::eof)) as u32), - 2 => Ok(try!(reader.read_u16::().map_err(FontError::eof)) as u32), - 3 => { - let hi = try!(reader.read_u8().map_err(FontError::eof)) as u32; - let lo = try!(reader.read_u16::().map_err(FontError::eof)) as u32; - Ok((hi << 16) | lo) - } - 4 => Ok(try!(reader.read_u32::().map_err(FontError::eof))), - _ => Err(FontError::CffBadOffset), - } -} - -// The CFF evaluation stack used during CharString reading. -struct EvaluationStack { - array: [i32; 48], - size: u8, -} - -impl EvaluationStack { - fn new() -> EvaluationStack { - EvaluationStack { - array: [0; 48], - size: 0, - } - } - - fn push(&mut self, value: i32) -> Result<(), FontError> { - if (self.size as usize) < self.array.len() { - self.array[self.size as usize] = value; - self.size += 1; - Ok(()) - } else { - Err(FontError::CffStackOverflow) - } - } - - fn clear(&mut self) { - self.size = 0 - } -} - -fn close_path_if_necessary(start: &Point2D, index_in_contour: u16, mut callback: F) - where F: FnMut(&Point) { - if index_in_contour == 0 { - // No path to close. - return - } - - callback(&Point { - position: *start, - index_in_contour: index_in_contour, - kind: PointKind::OnCurve, - }); -} - -fn add_curve(dx0: i16, dy0: i16, - dx1: i16, dy1: i16, - dx2: i16, dy2: i16, - pos: &mut Point2D, - index_in_contour: &mut u16, - mut callback: F) - where F: FnMut(&Point) { - pos.x += dx0; - pos.y += dy0; - callback(&Point { - position: *pos, - index_in_contour: *index_in_contour + 0, - kind: PointKind::FirstCubicControl, - }); - - pos.x += dx1; - pos.y += dy1; - callback(&Point { - position: *pos, - index_in_contour: *index_in_contour + 1, - kind: PointKind::SecondCubicControl, - }); - - pos.x += dx2; - pos.y += dy2; - callback(&Point { - position: *pos, - index_in_contour: *index_in_contour + 2, - kind: PointKind::OnCurve, - }); - - *index_in_contour += 3 -} - diff --git a/pathfinder-classic/src/tables/cmap.rs b/pathfinder-classic/src/tables/cmap.rs deleted file mode 100644 index 38605424..00000000 --- a/pathfinder-classic/src/tables/cmap.rs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use charmap::{CodepointRange, GlyphMapping, GlyphRange, MappedGlyphRange}; -use error::FontError; -use font::FontTable; -use std::cmp; -use std::mem; -use std::u16; -use util::Jump; - -pub const TAG: u32 = ((b'c' as u32) << 24) | - ((b'm' as u32) << 16) | - ((b'a' as u32) << 8) | - (b'p' as u32); - -const PLATFORM_ID_UNICODE: u16 = 0; -const PLATFORM_ID_MICROSOFT: u16 = 3; - -const MICROSOFT_ENCODING_ID_UNICODE_BMP: u16 = 1; -const MICROSOFT_ENCODING_ID_UNICODE_UCS4: u16 = 10; - -const FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES: u16 = 4; -const FORMAT_SEGMENTED_COVERAGE: u16 = 12; - -const MISSING_GLYPH: u16 = 0; - -#[derive(Clone, Copy)] -pub struct CmapTable<'a> { - table: FontTable<'a>, -} - -impl<'a> CmapTable<'a> { - pub fn new(table: FontTable) -> CmapTable { - CmapTable { - table: table, - } - } - - pub fn glyph_mapping_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) - -> Result { - let mut cmap_reader = self.table.bytes; - - // Check version. - if try!(cmap_reader.read_u16::().map_err(FontError::eof)) != 0 { - return Err(FontError::UnsupportedCmapVersion) - } - - let num_tables = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - - // Check platform ID and encoding. - // TODO(pcwalton): Handle more. - let mut table_found = false; - for _ in 0..num_tables { - let platform_id = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let encoding_id = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let offset = try!(cmap_reader.read_u32::().map_err(FontError::eof)); - match (platform_id, encoding_id) { - (PLATFORM_ID_UNICODE, _) | - (PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_BMP) | - (PLATFORM_ID_MICROSOFT, MICROSOFT_ENCODING_ID_UNICODE_UCS4) => { - // Move to the mapping table. - cmap_reader = self.table.bytes; - try!(cmap_reader.jump(offset as usize).map_err(FontError::eof)); - table_found = true; - break - } - _ => {} - } - } - - if !table_found { - return Err(FontError::UnsupportedCmapEncoding) - } - - // Check the mapping table format. - let format = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - match format { - FORMAT_SEGMENT_MAPPING_TO_DELTA_VALUES => { - self.glyph_mapping_for_codepoint_ranges_segment_mapping_format(cmap_reader, - codepoint_ranges) - } - FORMAT_SEGMENTED_COVERAGE => { - self.glyph_mapping_for_codepoint_ranges_segmented_coverage(cmap_reader, - codepoint_ranges) - } - _ => Err(FontError::UnsupportedCmapFormat), - } - } - - fn glyph_mapping_for_codepoint_ranges_segment_mapping_format( - &self, - mut cmap_reader: &[u8], - codepoint_ranges: &[CodepointRange]) - -> Result { - // Read the mapping table header. - let _length = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let _language = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let seg_count = try!(cmap_reader.read_u16::().map_err(FontError::eof)) / 2; - let _search_range = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let _entry_selector = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let _range_shift = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - - // Set up parallel array pointers. - // - // NB: Microsoft's spec refers to `startCode` and `endCode` as `startCount` and `endCount` - // respectively in a few places. I believe this is a mistake, and `startCode` and `endCode` - // are the correct names. - let (end_codes, mut start_codes) = (cmap_reader, cmap_reader); - try!(start_codes.jump((seg_count as usize + 1) * mem::size_of::()) - .map_err(FontError::eof)); - let mut id_deltas = start_codes; - try!(id_deltas.jump(seg_count as usize * mem::size_of::()).map_err(FontError::eof)); - let mut id_range_offsets = id_deltas; - try!(id_range_offsets.jump(seg_count as usize * mem::size_of::()) - .map_err(FontError::eof)); - let mut glyph_ids = id_range_offsets; - try!(glyph_ids.jump(seg_count as usize * mem::size_of::()).map_err(FontError::eof)); - - // Now perform the lookups. - let mut glyph_mapping = GlyphMapping::new(); - for codepoint_range in codepoint_ranges { - let mut codepoint_range = *codepoint_range; - while codepoint_range.end >= codepoint_range.start { - if codepoint_range.start > u16::MAX as u32 { - glyph_mapping.push(MappedGlyphRange { - codepoint_start: codepoint_range.start, - glyphs: GlyphRange { - start: MISSING_GLYPH, - end: MISSING_GLYPH, - }, - }); - codepoint_range.start += 1; - continue - } - - let start_codepoint_range = codepoint_range.start as u16; - let mut end_codepoint_range = codepoint_range.end as u16; - - // Binary search to find the segment. - let (mut low, mut high) = (0, seg_count); - let mut segment_index = None; - while low < high { - let mid = (low + high) / 2; - - let mut end_code = end_codes; - try!(end_code.jump(mid as usize * 2).map_err(FontError::eof)); - let end_code = try!(end_code.read_u16::().map_err(FontError::eof)); - if start_codepoint_range > end_code { - low = mid + 1; - continue - } - - let mut start_code = start_codes; - try!(start_code.jump(mid as usize * 2).map_err(FontError::eof)); - let start_code = try!(start_code.read_u16::().map_err(FontError::eof)); - if start_codepoint_range < start_code { - high = mid; - continue - } - - segment_index = Some(mid); - break - } - - let segment_index = match segment_index { - Some(segment_index) => segment_index, - None => { - glyph_mapping.push(MappedGlyphRange { - codepoint_start: codepoint_range.start, - glyphs: GlyphRange { - start: MISSING_GLYPH, - end: MISSING_GLYPH, - }, - }); - codepoint_range.start += 1; - continue - } - }; - - // Read out the segment info. - let mut start_code = start_codes; - let mut end_code = end_codes; - let mut id_range_offset = id_range_offsets; - let mut id_delta = id_deltas; - try!(start_code.jump(segment_index as usize * 2).map_err(FontError::eof)); - try!(end_code.jump(segment_index as usize * 2).map_err(FontError::eof)); - try!(id_range_offset.jump(segment_index as usize * 2).map_err(FontError::eof)); - try!(id_delta.jump(segment_index as usize * 2).map_err(FontError::eof)); - let start_code = try!(start_code.read_u16::().map_err(FontError::eof)); - let end_code = try!(end_code.read_u16::().map_err(FontError::eof)); - let id_range_offset = try!(id_range_offset.read_u16::() - .map_err(FontError::eof)); - let id_delta = try!(id_delta.read_i16::().map_err(FontError::eof)); - - end_codepoint_range = cmp::min(end_codepoint_range, end_code); - codepoint_range.start = (end_codepoint_range + 1) as u32; - - let start_code_offset = start_codepoint_range - start_code; - let end_code_offset = end_codepoint_range - start_code; - - // If we're direct-mapped (`idRangeOffset` = 0), then try to convert as much of the - // codepoint range as possible to a contiguous glyph range. - if id_range_offset == 0 { - // Microsoft's documentation is contradictory as to whether the code offset or - // the actual code is added to the ID delta here. In reality it seems to be the - // latter. - glyph_mapping.push(MappedGlyphRange { - codepoint_start: start_codepoint_range as u32, - glyphs: GlyphRange { - start: (start_codepoint_range as i16).wrapping_add(id_delta) as u16, - end: (end_codepoint_range as i16).wrapping_add(id_delta) as u16, - }, - }); - continue - } - - // Otherwise, look up the glyphs individually. - for code_offset in start_code_offset..(end_code_offset + 1) { - let mut reader = id_range_offsets; - try!(reader.jump(segment_index as usize * 2 + code_offset as usize * 2 + - id_range_offset as usize).map_err(FontError::eof)); - let mut glyph_id = try!(reader.read_u16::().map_err(FontError::eof)); - if glyph_id == 0 { - glyph_mapping.push(MappedGlyphRange { - codepoint_start: start_code as u32 + code_offset as u32, - glyphs: GlyphRange { - start: MISSING_GLYPH, - end: MISSING_GLYPH, - }, - }) - } else { - glyph_id = (glyph_id as i16).wrapping_add(id_delta) as u16; - glyph_mapping.push(MappedGlyphRange { - codepoint_start: start_code as u32 + code_offset as u32, - glyphs: GlyphRange { - start: glyph_id, - end: glyph_id, - }, - }) - } - } - } - } - - Ok(glyph_mapping) - } - - fn glyph_mapping_for_codepoint_ranges_segmented_coverage(&self, - mut cmap_reader: &[u8], - codepoint_ranges: &[CodepointRange]) - -> Result { - let _reserved = try!(cmap_reader.read_u16::().map_err(FontError::eof)); - let _length = try!(cmap_reader.read_u32::().map_err(FontError::eof)); - let _language = try!(cmap_reader.read_u32::().map_err(FontError::eof)); - let num_groups = try!(cmap_reader.read_u32::().map_err(FontError::eof)); - - // Now perform the lookups. - let mut glyph_mapping = GlyphMapping::new(); - for codepoint_range in codepoint_ranges { - let mut codepoint_range = *codepoint_range; - while codepoint_range.end >= codepoint_range.start { - // Binary search to find the segment. - let (mut low, mut high) = (0, num_groups); - let mut found_segment = None; - while low < high { - let mid = (low + high) / 2; - - let mut reader = cmap_reader; - try!(reader.jump(mid as usize * mem::size_of::<[u32; 3]>()) - .map_err(FontError::eof)); - let segment = Segment { - start_char_code: try!(reader.read_u32::() - .map_err(FontError::eof)), - end_char_code: try!(reader.read_u32::() - .map_err(FontError::eof)), - start_glyph_id: try!(reader.read_u32::() - .map_err(FontError::eof)), - }; - - if codepoint_range.start < segment.start_char_code { - high = mid - } else if codepoint_range.start > segment.end_char_code { - low = mid + 1 - } else { - found_segment = Some(segment); - break - } - } - - match found_segment { - None => { - glyph_mapping.push(MappedGlyphRange { - codepoint_start: codepoint_range.start, - glyphs: GlyphRange { - start: MISSING_GLYPH, - end: MISSING_GLYPH, - }, - }); - codepoint_range.start += 1 - } - Some(segment) => { - let end = cmp::min(codepoint_range.end, segment.end_char_code); - glyph_mapping.push(MappedGlyphRange { - codepoint_start: codepoint_range.start, - glyphs: GlyphRange { - start: (segment.start_glyph_id + codepoint_range.start - - segment.start_char_code) as u16, - end: (segment.start_glyph_id + end - segment.start_char_code) as - u16, - }, - }); - codepoint_range.start = end + 1 - } - } - } - } - - Ok(glyph_mapping) - } -} - -#[derive(Clone, Copy)] -struct Segment { - start_char_code: u32, - end_char_code: u32, - start_glyph_id: u32, -} - diff --git a/pathfinder-classic/src/tables/glyf.rs b/pathfinder-classic/src/tables/glyf.rs deleted file mode 100644 index a0e23d75..00000000 --- a/pathfinder-classic/src/tables/glyf.rs +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use euclid::Point2D; -use font::{FontTable, Point, PointKind}; -use outline::GlyphBounds; -use std::mem; -use tables::head::HeadTable; -use tables::loca::LocaTable; -use util::{F2DOT14_ONE, F2DOT14_ZERO, F2Dot14, Jump}; - -pub const TAG: u32 = ((b'g' as u32) << 24) | - ((b'l' as u32) << 16) | - ((b'y' as u32) << 8) | - (b'f' as u32); - -bitflags! { - flags SimpleFlags: u8 { - const ON_CURVE = 1 << 0, - const X_SHORT_VECTOR = 1 << 1, - const Y_SHORT_VECTOR = 1 << 2, - const REPEAT = 1 << 3, - const THIS_X_IS_SAME = 1 << 4, - const THIS_Y_IS_SAME = 1 << 5, - } -} - -bitflags! { - flags CompositeFlags: u16 { - const ARG_1_AND_2_ARE_WORDS = 1 << 0, - const ARGS_ARE_XY_VALUES = 1 << 1, - const ROUND_XY_TO_GRID = 1 << 2, - const WE_HAVE_A_SCALE = 1 << 3, - const MORE_COMPONENTS = 1 << 5, - const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6, - const WE_HAVE_A_TWO_BY_TWO = 1 << 7, - } -} - -/// TODO(pcwalton): Add some caching so we don't keep going to the `loca` table all the time. -#[derive(Clone, Copy, Debug)] -pub struct GlyfTable<'a> { - pub table: FontTable<'a>, -} - -impl<'a> GlyfTable<'a> { - #[inline] - pub fn new(table: FontTable) -> GlyfTable { - GlyfTable { - table: table, - } - } - - pub fn for_each_point(&self, - head_table: &HeadTable, - loca_table: &LocaTable, - glyph_id: u16, - callback: F) - -> Result<(), FontError> where F: FnMut(&Point) { - let mut reader = self.table.bytes; - - match try!(loca_table.location_of(head_table, glyph_id)) { - None => { - // No points. - return Ok(()) - } - Some(offset) => try!(reader.jump(offset as usize).map_err(FontError::eof)), - } - - let glyph_start = reader; - let number_of_contours = try!(reader.read_i16::().map_err(FontError::eof)); - if number_of_contours >= 0 { - self.for_each_point_in_simple_glyph(glyph_start, callback) - } else { - self.for_each_point_in_composite_glyph(glyph_start, head_table, loca_table, callback) - } - } - - fn for_each_point_in_simple_glyph(&self, mut reader: &[u8], mut callback: F) - -> Result<(), FontError> where F: FnMut(&Point) { - // Determine how many contours we have. - let number_of_contours = try!(reader.read_i16::().map_err(FontError::eof)); - if number_of_contours == 0 { - return Ok(()) - } - - // Skip over the rest of the header. - try!(reader.jump(mem::size_of::() * 4).map_err(FontError::eof)); - - // Find out how many points we have. - let mut endpoints_reader = reader; - try!(reader.jump(mem::size_of::() as usize * (number_of_contours as usize - 1)) - .map_err(FontError::eof)); - let number_of_points = try!(reader.read_u16::().map_err(FontError::eof)) + 1; - - // Skip over hinting instructions. - let instruction_length = try!(reader.read_u16::().map_err(FontError::eof)); - try!(reader.jump(instruction_length as usize).map_err(FontError::eof)); - - // Find the offsets of the X and Y coordinates. - let flags_reader = reader; - let x_coordinate_length = try!(calculate_size_of_x_coordinates(&mut reader, - number_of_points)); - - // Set up the streams. - let mut flag_parser = try!(FlagParser::new(flags_reader)); - let mut x_coordinate_reader = reader; - try!(reader.jump(x_coordinate_length as usize).map_err(FontError::eof)); - let mut y_coordinate_reader = reader; - - // Now parse the contours. - let (mut position, mut point_index) = (Point2D::new(0, 0), 0); - for _ in 0..number_of_contours { - let contour_point_count = - try!(endpoints_reader.read_u16::().map_err(FontError::eof)) - - point_index + 1; - - let mut first_on_curve_point = None; - let mut initial_off_curve_point = None; - let mut last_point_was_off_curve = false; - let mut point_index_in_contour = 0; - - for _ in 0..contour_point_count { - let flags = SimpleFlags::from_bits_truncate(*flag_parser.current); - try!(flag_parser.next()); - - let mut delta = Point2D::new(0, 0); - if flags.contains(X_SHORT_VECTOR) { - delta.x = try!(x_coordinate_reader.read_u8().map_err(FontError::eof)) as i16; - if !flags.contains(THIS_X_IS_SAME) { - delta.x = -delta.x - } - } else if !flags.contains(THIS_X_IS_SAME) { - delta.x = try!(x_coordinate_reader.read_i16::() - .map_err(FontError::eof)) - } - if flags.contains(Y_SHORT_VECTOR) { - delta.y = try!(y_coordinate_reader.read_u8().map_err(FontError::eof)) as i16; - if !flags.contains(THIS_Y_IS_SAME) { - delta.y = -delta.y - } - } else if !flags.contains(THIS_Y_IS_SAME) { - delta.y = try!(y_coordinate_reader.read_i16::() - .map_err(FontError::eof)) - } - - if last_point_was_off_curve && !flags.contains(ON_CURVE) { - let position = position + delta / 2; - - // An important edge case! - if first_on_curve_point.is_none() { - first_on_curve_point = Some(position) - } - - callback(&Point { - position: position, - index_in_contour: point_index_in_contour, - kind: PointKind::OnCurve, - }); - point_index_in_contour += 1 - } - - position = position + delta; - - if flags.contains(ON_CURVE) && first_on_curve_point.is_none() { - first_on_curve_point = Some(position) - } - - // Sometimes the initial point is an off curve point. In that case, save it so we - // can emit it later when closing the path. - if !flags.contains(ON_CURVE) && first_on_curve_point.is_none() { - debug_assert!(initial_off_curve_point.is_none()); - initial_off_curve_point = Some(position) - } else { - callback(&Point { - position: position, - kind: if flags.contains(ON_CURVE) { - PointKind::OnCurve - } else { - PointKind::QuadControl - }, - index_in_contour: point_index_in_contour, - }); - point_index_in_contour += 1 - } - - last_point_was_off_curve = !flags.contains(ON_CURVE); - point_index += 1; - } - - // We're about to close the path. Emit the initial off curve point if there was one. - if let Some(initial_off_curve_point) = initial_off_curve_point { - if last_point_was_off_curve { - // Another important edge case! - let position = position + (initial_off_curve_point - position) / 2; - callback(&Point { - position: position, - index_in_contour: point_index_in_contour, - kind: PointKind::OnCurve, - }); - point_index_in_contour += 1 - } - - callback(&Point { - position: initial_off_curve_point, - kind: PointKind::QuadControl, - index_in_contour: point_index_in_contour, - }); - point_index_in_contour += 1 - } - - // Close the path. - if let Some(first_on_curve_point) = first_on_curve_point { - callback(&Point { - position: first_on_curve_point, - kind: PointKind::OnCurve, - index_in_contour: point_index_in_contour, - }) - } - } - - Ok(()) - } - - // TODO(pcwalton): Consider rasterizing pieces of composite glyphs independently and - // compositing them together. - fn for_each_point_in_composite_glyph(&self, - mut reader: &[u8], - head_table: &HeadTable, - loca_table: &LocaTable, - mut callback: F) - -> Result<(), FontError> where F: FnMut(&Point) { - try!(reader.jump(mem::size_of::() * 5).map_err(FontError::eof)); - - loop { - let flags = try!(reader.read_u16::().map_err(FontError::eof)); - let flags = CompositeFlags::from_bits_truncate(flags); - let glyph_index = try!(reader.read_u16::().map_err(FontError::eof)); - - let (arg0, arg1); - if flags.contains(ARG_1_AND_2_ARE_WORDS) { - arg0 = try!(reader.read_i16::().map_err(FontError::eof)); - arg1 = try!(reader.read_i16::().map_err(FontError::eof)); - } else { - arg0 = try!(reader.read_i8().map_err(FontError::eof)) as i16; - arg1 = try!(reader.read_i8().map_err(FontError::eof)) as i16; - } - - let mut transform = Mat3x2::identity(); - if flags.contains(ARGS_ARE_XY_VALUES) { - transform.m02 = arg0; - transform.m12 = arg1; - } - - if flags.contains(WE_HAVE_A_SCALE) { - let scale = F2Dot14(try!(reader.read_i16::().map_err(FontError::eof))); - transform.m00 = scale; - transform.m11 = scale; - } else if flags.contains(WE_HAVE_AN_X_AND_Y_SCALE) { - transform.m00 = F2Dot14(try!(reader.read_i16::() - .map_err(FontError::eof))); - transform.m11 = F2Dot14(try!(reader.read_i16::() - .map_err(FontError::eof))); - } else if flags.contains(WE_HAVE_A_TWO_BY_TWO) { - transform.m00 = F2Dot14(try!(reader.read_i16::() - .map_err(FontError::eof))); - transform.m01 = F2Dot14(try!(reader.read_i16::() - .map_err(FontError::eof))); - transform.m10 = F2Dot14(try!(reader.read_i16::() - .map_err(FontError::eof))); - transform.m11 = F2Dot14(try!(reader.read_i16::() - .map_err(FontError::eof))); - } - - if let Some(offset) = try!(loca_table.location_of(head_table, glyph_index)) { - let mut reader = self.table.bytes; - try!(reader.jump(offset as usize).map_err(FontError::eof)); - try!(self.for_each_point_in_simple_glyph(reader, |point| { - callback(&transform.transform(&point)) - })); - } - - if !flags.contains(MORE_COMPONENTS) { - break - } - } - - Ok(()) - } - - pub fn glyph_bounds(&self, head_table: &HeadTable, loca_table: &LocaTable, glyph_id: u16) - -> Result { - let mut reader = self.table.bytes; - - match try!(loca_table.location_of(head_table, glyph_id)) { - None => { - // No outlines. - return Ok(GlyphBounds { - left: 0, - bottom: 0, - right: 0, - top: 0, - }) - } - Some(offset) => try!(reader.jump(offset as usize).map_err(FontError::eof)), - } - - // Skip over the number of contours. - try!(reader.read_i16::().map_err(FontError::eof)); - - let x_min = try!(reader.read_i16::().map_err(FontError::eof)); - let y_min = try!(reader.read_i16::().map_err(FontError::eof)); - let x_max = try!(reader.read_i16::().map_err(FontError::eof)); - let y_max = try!(reader.read_i16::().map_err(FontError::eof)); - Ok(GlyphBounds { - left: x_min as i32, - bottom: y_min as i32, - right: x_max as i32, - top: y_max as i32, - }) - } -} - -// Given a reader pointing to the start of the list of flags, returns the size in bytes of the list -// of X coordinates and positions the reader at the start of that list. -#[inline] -fn calculate_size_of_x_coordinates<'a, 'b>(reader: &'a mut &'b [u8], number_of_points: u16) - -> Result { - let (mut x_coordinate_length, mut points_left) = (0, number_of_points); - while points_left > 0 { - let flags = SimpleFlags::from_bits_truncate(try!(reader.read_u8() - .map_err(FontError::eof))); - let repeat_count = if !flags.contains(REPEAT) { - 1 - } else { - try!(reader.read_u8().map_err(FontError::eof)) as u16 + 1 - }; - - if flags.contains(X_SHORT_VECTOR) { - x_coordinate_length += repeat_count - } else if !flags.contains(THIS_X_IS_SAME) { - x_coordinate_length += repeat_count * 2 - } - - points_left -= repeat_count - } - - Ok(x_coordinate_length) -} - -struct FlagParser<'a> { - next: &'a [u8], - current: &'a u8, - repeats_left: u8, -} - -impl<'a> FlagParser<'a> { - #[inline] - fn new(buffer: &[u8]) -> Result { - let mut parser = FlagParser { - next: buffer, - current: &buffer[0], - repeats_left: 0, - }; - try!(parser.next()); - Ok(parser) - } - - #[inline] - fn next(&mut self) -> Result<(), FontError> { - if self.repeats_left > 0 { - self.repeats_left -= 1; - return Ok(()) - } - - self.current = match self.next.get(0) { - Some(value) => value, - None => return Err(FontError::UnexpectedEof), - }; - - let flags = SimpleFlags::from_bits_truncate(*self.current); - self.next = &self.next[1..]; - - if flags.contains(REPEAT) { - self.repeats_left = match self.next.get(0) { - Some(&value) => value, - None => return Err(FontError::UnexpectedEof), - }; - - self.next = &self.next[1..]; - } else { - self.repeats_left = 0 - } - - Ok(()) - } -} - -#[derive(Copy, Clone, Debug)] -struct Mat3x2 { - m00: F2Dot14, - m01: F2Dot14, - m02: i16, - m10: F2Dot14, - m11: F2Dot14, - m12: i16, -} - -impl Mat3x2 { - fn identity() -> Mat3x2 { - Mat3x2 { - m00: F2DOT14_ONE, m01: F2DOT14_ZERO, m02: 0, - m10: F2DOT14_ZERO, m11: F2DOT14_ONE, m12: 0, - } - } - - // TODO(pcwalton): SIMD/FMA. - fn transform(&self, point: &Point) -> Point { - let p = point.position; - Point { - position: Point2D::new(self.m00 * p.x + self.m01 * p.y + self.m02, - self.m10 * p.x + self.m11 * p.y + self.m12), - ..*point - } - } -} - diff --git a/pathfinder-classic/src/tables/head.rs b/pathfinder-classic/src/tables/head.rs deleted file mode 100644 index ef57849a..00000000 --- a/pathfinder-classic/src/tables/head.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::FontTable; -use outline::GlyphBounds; -use std::mem; -use util::Jump; - -pub const TAG: u32 = ((b'h' as u32) << 24) | - ((b'e' as u32) << 16) | - ((b'a' as u32) << 8) | - (b'd' as u32); - -const MAGIC_NUMBER: u32 = 0x5f0f3cf5; - -#[derive(Clone, Debug)] -pub struct HeadTable { - pub units_per_em: u16, - pub index_to_loc_format: i16, - pub max_glyph_bounds: GlyphBounds, -} - -impl HeadTable { - pub fn new(table: FontTable) -> Result { - let mut reader = table.bytes; - - // Check the version. - let major_version = try!(reader.read_u16::().map_err(FontError::eof)); - let minor_version = try!(reader.read_u16::().map_err(FontError::eof)); - if (major_version, minor_version) != (1, 0) { - return Err(FontError::UnsupportedHeadVersion) - } - - // Check the magic number. - try!(reader.jump(mem::size_of::() * 2).map_err(FontError::eof)); - let magic_number = try!(reader.read_u32::().map_err(FontError::eof)); - if magic_number != MAGIC_NUMBER { - return Err(FontError::UnknownFormat) - } - - // Read the units per em. - try!(reader.jump(mem::size_of::()).map_err(FontError::eof)); - let units_per_em = try!(reader.read_u16::().map_err(FontError::eof)); - - // Read the maximum bounds. - try!(reader.jump(mem::size_of::() * 2).map_err(FontError::eof)); - let x_min = try!(reader.read_i16::().map_err(FontError::eof)); - let y_min = try!(reader.read_i16::().map_err(FontError::eof)); - let x_max = try!(reader.read_i16::().map_err(FontError::eof)); - let y_max = try!(reader.read_i16::().map_err(FontError::eof)); - let max_glyph_bounds = GlyphBounds { - left: x_min as i32, - bottom: y_min as i32, - right: x_max as i32, - top: y_max as i32, - }; - - // Read the index-to-location format. - try!(reader.jump(mem::size_of::() * 2 + mem::size_of::()) - .map_err(FontError::eof)); - let index_to_loc_format = try!(reader.read_i16::().map_err(FontError::eof)); - - // Check the glyph data format. - let glyph_data_format = try!(reader.read_i16::().map_err(FontError::eof)); - if glyph_data_format != 0 { - return Err(FontError::UnsupportedGlyphFormat) - } - - Ok(HeadTable { - units_per_em: units_per_em, - index_to_loc_format: index_to_loc_format, - max_glyph_bounds: max_glyph_bounds, - }) - } -} diff --git a/pathfinder-classic/src/tables/hhea.rs b/pathfinder-classic/src/tables/hhea.rs deleted file mode 100644 index b881a524..00000000 --- a/pathfinder-classic/src/tables/hhea.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::FontTable; -use std::mem; -use util::Jump; - -pub const TAG: u32 = ((b'h' as u32) << 24) | - ((b'h' as u32) << 16) | - ((b'e' as u32) << 8) | - (b'a' as u32); - -#[derive(Clone, Debug)] -pub struct HheaTable { - pub line_gap: i16, - pub number_of_h_metrics: u16, -} - -impl HheaTable { - pub fn new(table: FontTable) -> Result { - let mut reader = table.bytes; - - // Check the version. - let major_version = try!(reader.read_u16::().map_err(FontError::eof)); - let minor_version = try!(reader.read_u16::().map_err(FontError::eof)); - if (major_version, minor_version) != (1, 0) { - return Err(FontError::UnsupportedHheaVersion) - } - - // Read the height-related metrics. - let _ascender = try!(reader.read_i16::().map_err(FontError::eof)); - let _descender = try!(reader.read_i16::().map_err(FontError::eof)); - let line_gap = try!(reader.read_i16::().map_err(FontError::eof)); - - // Read the number of `hmtx` entries. - try!(reader.jump(mem::size_of::() * 12).map_err(FontError::eof)); - let number_of_h_metrics = try!(reader.read_u16::().map_err(FontError::eof)); - - Ok(HheaTable { - line_gap: line_gap, - number_of_h_metrics: number_of_h_metrics, - }) - } -} - diff --git a/pathfinder-classic/src/tables/hmtx.rs b/pathfinder-classic/src/tables/hmtx.rs deleted file mode 100644 index 9efb9e24..00000000 --- a/pathfinder-classic/src/tables/hmtx.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::FontTable; -use std::mem; -use tables::hhea::HheaTable; -use util::Jump; - -pub const TAG: u32 = ((b'h' as u32) << 24) | - ((b'm' as u32) << 16) | - ((b't' as u32) << 8) | - (b'x' as u32); - -#[derive(Clone, Copy)] -pub struct HmtxTable<'a> { - table: FontTable<'a>, -} - -impl<'a> HmtxTable<'a> { - pub fn new(table: FontTable) -> HmtxTable { - HmtxTable { - table: table, - } - } - - pub fn metrics_for_glyph(&self, hhea_table: &HheaTable, glyph_id: u16) - -> Result { - let mut reader = self.table.bytes; - - // Read the advance width. - let advance_width; - if glyph_id < hhea_table.number_of_h_metrics { - try!(reader.jump(mem::size_of::() * 2 * glyph_id as usize).map_err(FontError::eof)); - advance_width = try!(reader.read_u16::().map_err(FontError::eof)) - } else { - try!(reader.jump(mem::size_of::() * 2 * - (hhea_table.number_of_h_metrics - 1) as usize).map_err(FontError::eof)); - advance_width = try!(reader.read_u16::().map_err(FontError::eof)); - try!(reader.jump(mem::size_of::() * glyph_id as usize).map_err(FontError::eof)); - } - - // Read the left-side bearing. - let lsb = try!(reader.read_i16::().map_err(FontError::eof)); - - Ok(HorizontalMetrics { - advance_width: advance_width, - lsb: lsb, - }) - } -} - -#[derive(Clone, Copy, Default, Debug)] -pub struct HorizontalMetrics { - pub advance_width: u16, - pub lsb: i16, -} - diff --git a/pathfinder-classic/src/tables/kern.rs b/pathfinder-classic/src/tables/kern.rs deleted file mode 100644 index c96ced2d..00000000 --- a/pathfinder-classic/src/tables/kern.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::FontTable; -use std::mem; -use util::Jump; - -pub const TAG: u32 = ((b'k' as u32) << 24) | - ((b'e' as u32) << 16) | - ((b'r' as u32) << 8) | - (b'n' as u32); - -bitflags! { - flags Coverage: u16 { - const HORIZONTAL = 1 << 0, - const MINIMUM = 1 << 1, - const CROSS_STREAM = 1 << 2, - const OVERRIDE = 1 << 3, - } -} - -#[derive(Clone, Copy)] -pub struct KernTable<'a> { - horizontal_table: &'a [u8], -} - -impl<'a> KernTable<'a> { - pub fn new(table: FontTable) -> Result { - let mut kern_reader = table.bytes; - let version = try!(kern_reader.read_u16::().map_err(FontError::eof)); - if version != 0 { - return Err(FontError::UnknownFormat) - } - - let n_tables = try!(kern_reader.read_u16::().map_err(FontError::eof)); - let mut horizontal_table = None; - for _ in 0..n_tables { - let mut table_reader = kern_reader; - let _version = try!(table_reader.read_u16::().map_err(FontError::eof)); - let length = try!(table_reader.read_u16::().map_err(FontError::eof)); - let coverage = try!(table_reader.read_u16::().map_err(FontError::eof)); - let coverage_flags = Coverage::from_bits_truncate(coverage); - - if coverage_flags.contains(HORIZONTAL) && !coverage_flags.contains(MINIMUM) && - !coverage_flags.contains(CROSS_STREAM) && (coverage >> 8) == 0 { - let length = length as usize - mem::size_of::() * 3; - horizontal_table = Some(&table_reader[0..length]); - break - } - - try!(kern_reader.jump(length as usize).map_err(FontError::eof)); - } - - match horizontal_table { - Some(horizontal_table) => { - Ok(KernTable { - horizontal_table: horizontal_table, - }) - } - None => Err(FontError::UnknownFormat), - } - } - - pub fn kerning_for_glyph_pair(&self, left_glyph_id: u16, right_glyph_id: u16) - -> Result { - let mut table_reader = self.horizontal_table; - let n_pairs = try!(table_reader.read_u16::().map_err(FontError::eof)); - try!(table_reader.jump(mem::size_of::<[u16; 3]>()).map_err(FontError::eof)); - - let (mut low, mut high) = (0, n_pairs as u32); - while low < high { - let mut reader = table_reader; - let mid = (low + high) / 2; - - try!(reader.jump(mid as usize * mem::size_of::<[u16; 3]>()).map_err(FontError::eof)); - let left = try!(reader.read_u16::().map_err(FontError::eof)); - let right = try!(reader.read_u16::().map_err(FontError::eof)); - let value = try!(reader.read_i16::().map_err(FontError::eof)); - - if left_glyph_id < left || (left_glyph_id == left && right_glyph_id < right) { - high = mid - } else if left_glyph_id > left || (left_glyph_id == left && right_glyph_id > right) { - low = mid + 1 - } else { - return Ok(value) - } - } - - Ok(0) - } -} - diff --git a/pathfinder-classic/src/tables/loca.rs b/pathfinder-classic/src/tables/loca.rs deleted file mode 100644 index 5b5cb801..00000000 --- a/pathfinder-classic/src/tables/loca.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::FontTable; -use tables::head::HeadTable; -use util::Jump; - -pub const TAG: u32 = ((b'l' as u32) << 24) | - ((b'o' as u32) << 16) | - ((b'c' as u32) << 8) | - (b'a' as u32); - -pub struct LocaTable<'a> { - table: FontTable<'a>, -} - -impl<'a> LocaTable<'a> { - pub fn new(loca_table: FontTable<'a>) -> Result, FontError> { - Ok(LocaTable { - table: loca_table, - }) - } - - pub fn location_of(&self, head_table: &HeadTable, glyph_id: u16) - -> Result, FontError> { - let mut reader = self.table.bytes; - let (this_location, next_location) = match head_table.index_to_loc_format { - 0 => { - try!(reader.jump(glyph_id as usize * 2).map_err(FontError::eof)); - let this_location = - try!(reader.read_u16::().map_err(FontError::eof)) as u32 * 2; - let next_location = match reader.read_u16::() { - Ok(next_location) => Ok(next_location as u32 * 2), - Err(_) => Err(FontError::UnexpectedEof), - }; - (this_location, next_location) - } - 1 => { - try!(reader.jump(glyph_id as usize * 4).map_err(FontError::eof)); - let this_location = try!(reader.read_u32::().map_err(FontError::eof)); - let next_location = reader.read_u32::().map_err(FontError::eof); - (this_location, next_location) - } - _ => return Err(FontError::UnknownFormat), - }; - - if next_location == Ok(this_location) { - Ok(None) - } else { - Ok(Some(this_location)) - } - } -} - diff --git a/pathfinder-classic/src/tables/mod.rs b/pathfinder-classic/src/tables/mod.rs deleted file mode 100644 index 6b8bf70b..00000000 --- a/pathfinder-classic/src/tables/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! OpenType fonts. - -// These tables need no parsing and so don't need separate files. -pub mod cvt { - pub const TAG: u32 = ((b'c' as u32) << 24) | - ((b'v' as u32) << 16) | - ((b't' as u32) << 8) | - (b' ' as u32); -} - -pub mod fpgm { - pub const TAG: u32 = ((b'f' as u32) << 24) | - ((b'p' as u32) << 16) | - ((b'g' as u32) << 8) | - (b'm' as u32); -} - -pub mod prep { - pub const TAG: u32 = ((b'p' as u32) << 24) | - ((b'r' as u32) << 16) | - ((b'e' as u32) << 8) | - (b'p' as u32); -} - -pub mod cff; -pub mod cmap; -pub mod glyf; -pub mod head; -pub mod hhea; -pub mod hmtx; -pub mod kern; -pub mod loca; -pub mod os_2; - diff --git a/pathfinder-classic/src/tables/os_2.rs b/pathfinder-classic/src/tables/os_2.rs deleted file mode 100644 index e788371b..00000000 --- a/pathfinder-classic/src/tables/os_2.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 byteorder::{BigEndian, ReadBytesExt}; -use error::FontError; -use font::FontTable; -use std::mem; -use util::Jump; - -pub const TAG: u32 = ((b'O' as u32) << 24) | - ((b'S' as u32) << 16) | - ((b'/' as u32) << 8) | - (b'2' as u32); - -#[derive(Clone, Debug)] -pub struct Os2Table { - pub typo_ascender: i16, - pub typo_descender: i16, - pub typo_line_gap: i16, -} - -impl Os2Table { - pub fn new(table: FontTable) -> Result { - let mut reader = table.bytes; - - // We should be compatible with all versions. If this is greater than version 5, follow - // Postel's law and hope for the best. - let version = try!(reader.read_u16::().map_err(FontError::eof)); - - // Skip to the line gap. - try!(reader.jump(mem::size_of::() * 15).map_err(FontError::eof)); - try!(reader.jump(10).map_err(FontError::eof)); - if version == 0 { - try!(reader.jump(mem::size_of::() * 2).map_err(FontError::eof)); - } else { - try!(reader.jump(mem::size_of::() * 5).map_err(FontError::eof)); - } - try!(reader.jump(mem::size_of::() * 3).map_err(FontError::eof)); - - // Read the line spacing information. - let typo_ascender = try!(reader.read_i16::().map_err(FontError::eof)); - let typo_descender = try!(reader.read_i16::().map_err(FontError::eof)); - let typo_line_gap = try!(reader.read_i16::().map_err(FontError::eof)); - - Ok(Os2Table { - typo_ascender: typo_ascender, - typo_descender: typo_descender, - typo_line_gap: typo_line_gap, - }) - } -} - - diff --git a/pathfinder-classic/src/tests/buffers.rs b/pathfinder-classic/src/tests/buffers.rs deleted file mode 100644 index 8ecfc6c4..00000000 --- a/pathfinder-classic/src/tests/buffers.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -use charmap::CodepointRange; -use font::Font; -use memmap::{Mmap, Protection}; -use outline::OutlineBuilder; -use test::Bencher; - -static TEST_FONT_PATH: &'static str = "resources/tests/nimbus-sans/NimbusSanL-Regu.ttf"; - -#[bench] -fn bench_add_glyphs(bencher: &mut Bencher) { - let file = Mmap::open_path(TEST_FONT_PATH, Protection::Read).expect("Couldn't open test font"); - let mut buffer = vec![]; - unsafe { - let font = Font::new(file.as_slice(), &mut buffer).unwrap(); - let codepoint_ranges = [CodepointRange::new('!' as u32, '~' as u32)]; - let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges) - .expect("Couldn't find glyph ranges"); - - bencher.iter(|| { - let mut outline_builder = OutlineBuilder::new(); - for (_, glyph_id) in glyph_mapping.iter() { - outline_builder.add_glyph(&font, glyph_id).unwrap(); - } - }); - } -} - diff --git a/pathfinder-classic/src/tests/mod.rs b/pathfinder-classic/src/tests/mod.rs deleted file mode 100644 index 4b8516b9..00000000 --- a/pathfinder-classic/src/tests/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -mod buffers; -mod rect_packer; - diff --git a/pathfinder-classic/src/tests/rect_packer.rs b/pathfinder-classic/src/tests/rect_packer.rs deleted file mode 100644 index 54284da5..00000000 --- a/pathfinder-classic/src/tests/rect_packer.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -use rect_packer::RectPacker; -use euclid::{Rect, Size2D}; -use std::cmp; - -fn pack_objects(available_width: u32, objects: Vec<(u32, u32)>) - -> (RectPacker, Vec>, u32) { - let objects: Vec<_> = objects.iter() - .map(|&(width, height)| Size2D::new(width, height)) - .collect(); - - let available_width = 2 + - cmp::max(available_width, objects.iter().map(|object| object.width).max().unwrap_or(0)); - let shelf_height = objects.iter().map(|object| object.height).max().unwrap_or(0) + 2; - - let mut rect_packer = RectPacker::new(available_width, shelf_height); - let rects = objects.iter() - .map(|object| Rect::new(rect_packer.pack(object).unwrap(), *object)) - .collect(); - (rect_packer, rects, available_width) -} - -quickcheck! { - fn objects_dont_overlap(available_width: u32, objects: Vec<(u32, u32)>) -> bool { - let (_, rects, _) = pack_objects(available_width, objects); - for (i, a) in rects.iter().enumerate() { - for b in &rects[(i + 1)..] { - assert!(!a.intersects(b)) - } - } - true - } - - fn objects_dont_exceed_available_width(available_width: u32, objects: Vec<(u32, u32)>) -> bool { - let (_, rects, available_width) = pack_objects(available_width, objects); - rects.iter().all(|rect| rect.max_x() <= available_width) - } - - fn objects_dont_cross_shelves(available_width: u32, objects: Vec<(u32, u32)>) -> bool { - let (rect_packer, rects, _) = pack_objects(available_width, objects); - rects.iter().all(|rect| { - let shelf_height = rect_packer.shelf_height(); - rect.is_empty() || rect.origin.y / shelf_height == (rect.max_y() - 1) / shelf_height - }) - } -} - diff --git a/pathfinder-classic/src/typesetter.rs b/pathfinder-classic/src/typesetter.rs deleted file mode 100644 index f569ac5f..00000000 --- a/pathfinder-classic/src/typesetter.rs +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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. - -//! Simple text layout. -//! -//! Do not use this for international or high-quality text. This layout has all of the limitations -//! of the shaper; additionally, it only does left-to-right text with a uniform page width and no -//! control over line spacing. Use Cocoa's `NSLayoutManager`, Pango, etc. for real use. - -use charmap::CodepointRanges; -use error::GlyphStoreCreationError; -use euclid::{Point2D, Rect}; -use font::Font; -use outline::{OutlineBuilder, Outlines}; -use shaper; -use std::u16; - -#[derive(Clone)] -pub struct Typesetter { - pub glyph_positions: Vec, - page_width: f32, - cursor: Point2D, -} - -impl Typesetter { - pub fn new(page_width: f32, initial_font: &Font, initial_point_size: f32) -> Typesetter { - let pixels_per_unit = initial_point_size / initial_font.units_per_em() as f32; - let initial_position = initial_font.ascender() as f32 * pixels_per_unit; - - Typesetter { - glyph_positions: vec![], - page_width: page_width, - cursor: Point2D::new(0.0, initial_position), - } - } - - pub fn add_text(&mut self, font: &Font, point_size: f32, string: &str) { - // TODO(pcwalton): Cache this mapping. - let mut chars: Vec = string.chars().collect(); - chars.push(' '); - chars.sort(); - let codepoint_ranges = CodepointRanges::from_sorted_chars(&chars); - let glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges.ranges) - .unwrap(); - - // All of these values are in pixels. - let pixels_per_unit = point_size / font.units_per_em() as f32; - let space_advance = font.metrics_for_glyph(glyph_mapping.glyph_for(' ' as u32).unwrap()) - .unwrap() - .advance_width as f32 * pixels_per_unit; - let line_spacing = (font.ascender() as f32 - font.descender() as f32 + - font.line_gap() as f32) * pixels_per_unit; - - for word in string.split_whitespace() { - let shaped_glyph_positions = shaper::shape_text(&font, &glyph_mapping, word); - let total_advance = pixels_per_unit * - shaped_glyph_positions.iter().map(|p| p.advance as f32).sum::(); - if self.cursor.x + total_advance > self.page_width { - self.cursor.x = 0.0; - self.cursor.y += line_spacing; - } - - for glyph_position in &shaped_glyph_positions { - self.glyph_positions.push(GlyphPosition { - x: self.cursor.x, - y: self.cursor.y, - glyph_id: glyph_position.glyph_id, - }); - self.cursor.x += glyph_position.advance as f32 * pixels_per_unit; - } - - self.cursor.x += space_advance - } - } - - pub fn glyph_positions(&self) -> &[GlyphPosition] { - &self.glyph_positions - } - - pub fn create_glyph_store(&self, font: &Font) -> Result { - let glyph_ids = self.glyph_positions - .iter() - .map(|glyph_position| glyph_position.glyph_id) - .collect(); - GlyphStore::from_glyph_ids(glyph_ids, font) - } - - /// Returns the positions of the glyphs that intersect the given pixel rectangle. - /// - /// Requires a `GlyphStore` to be created first. - pub fn positioned_glyphs_in_rect(&self, - bounding_rect: &Rect, - glyph_store: &GlyphStore, - point_size: f32, - scale: f32, - subpixel_granularity: f32) - -> Vec { - let subpixel_inv_granularity = 1.0 / subpixel_granularity; - - let mut positioned_glyphs = vec![]; - for glyph_position in &self.glyph_positions { - // If this glyph is not in the glyph store, just skip it. - // - // TODO(pcwalton): Notify the caller somehow? - let glyph_index = match glyph_store.glyph_index(glyph_position.glyph_id) { - None => continue, - Some(glyph_index) => glyph_index, - }; - - let mut glyph_subpixel_bounds = glyph_store.outlines.glyph_subpixel_bounds(glyph_index, - point_size); - glyph_subpixel_bounds.scale(scale); - let glyph_pixel_bounds = glyph_subpixel_bounds.round_out(); - - // Snap the rect to the nearest granule. - let glyph_snapped_origin = - Point2D::new((glyph_position.x * scale * subpixel_inv_granularity).round() * - subpixel_granularity, - ((glyph_position.y * scale).round() - glyph_pixel_bounds.top as f32)); - let glyph_snapped_rect = Rect::new(glyph_snapped_origin, glyph_subpixel_bounds.size()); - - debug_assert!(glyph_snapped_rect.origin.y == glyph_snapped_rect.origin.y.round()); - - if !glyph_snapped_rect.intersects(bounding_rect) { - continue - } - - let subpixel_x = if glyph_snapped_origin.x >= 0.0 { - glyph_snapped_origin.x.fract() - } else { - 1.0 + glyph_snapped_origin.x.fract() - }; - - positioned_glyphs.push(PositionedGlyph { - bounds: glyph_snapped_rect, - subpixel_x: subpixel_x, - glyph_index: glyph_index, - }) - } - - positioned_glyphs - } -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct GlyphPosition { - pub x: f32, - pub y: f32, - pub glyph_id: u16, -} - -impl GlyphPosition { - #[inline] - pub fn position(&self) -> Point2D { - Point2D::new(self.x, self.y) - } -} - -pub struct GlyphStore { - pub outlines: Outlines, - pub glyph_id_to_glyph_index: Vec, - pub all_glyph_indices: Vec, -} - -impl GlyphStore { - fn from_glyph_ids(mut glyph_ids: Vec, font: &Font) - -> Result { - glyph_ids.sort(); - glyph_ids.dedup(); - - let last_glyph_id = match glyph_ids.last() { - Some(&id) => id + 1, - None => 0, - }; - - let mut outline_builder = OutlineBuilder::new(); - let mut glyph_id_to_glyph_index = vec![u16::MAX; last_glyph_id as usize]; - let mut all_glyph_indices = vec![]; - for glyph_id in glyph_ids { - let glyph_index = try!(outline_builder.add_glyph(font, glyph_id) - .map_err(GlyphStoreCreationError::FontError)); - glyph_id_to_glyph_index[glyph_id as usize] = glyph_index; - all_glyph_indices.push(glyph_index); - } - - let outlines = try!(outline_builder.create_buffers() - .map_err(GlyphStoreCreationError::GlError)); - - all_glyph_indices.sort(); - all_glyph_indices.dedup(); - - Ok(GlyphStore { - outlines: outlines, - glyph_id_to_glyph_index: glyph_id_to_glyph_index, - all_glyph_indices: all_glyph_indices, - }) - } - - pub fn from_codepoints(codepoints: &CodepointRanges, font: &Font) - -> Result { - let mapping = try!(font.glyph_mapping_for_codepoint_ranges(&codepoints.ranges) - .map_err(GlyphStoreCreationError::FontError)); - let glyph_ids = mapping.iter().map(|(_, glyph_id)| glyph_id).collect(); - GlyphStore::from_glyph_ids(glyph_ids, font) - } - - #[inline] - pub fn glyph_index(&self, glyph_id: u16) -> Option { - match self.glyph_id_to_glyph_index.get(glyph_id as usize) { - None | Some(&u16::MAX) => None, - Some(&index) => Some(index), - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct PositionedGlyph { - pub bounds: Rect, - pub subpixel_x: f32, - pub glyph_index: u16, -} - diff --git a/pathfinder-classic/src/util.rs b/pathfinder-classic/src/util.rs deleted file mode 100644 index 6480c3da..00000000 --- a/pathfinder-classic/src/util.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// 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 num_traits::identities::Zero; -use std::ops::{Add, Div, Mul, Neg, Sub}; - -pub const F26DOT6_ZERO: F26Dot6 = F26Dot6(0); - -pub const F2DOT14_ZERO: F2Dot14 = F2Dot14(0); -pub const F2DOT14_ONE: F2Dot14 = F2Dot14(1 << 14); - -/// 26.6 fixed point. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct F26Dot6(pub i32); - -impl Zero for F26Dot6 { - #[inline] - fn zero() -> F26Dot6 { - F26DOT6_ZERO - } - - #[inline] - fn is_zero(&self) -> bool { - *self == F26DOT6_ZERO - } -} - -impl Add for F26Dot6 { - type Output = F26Dot6; - - #[inline] - fn add(self, other: F26Dot6) -> F26Dot6 { - F26Dot6(self.0 + other.0) - } -} - -impl Sub for F26Dot6 { - type Output = F26Dot6; - - #[inline] - fn sub(self, other: F26Dot6) -> F26Dot6 { - F26Dot6(self.0 - other.0) - } -} - -impl Mul for F26Dot6 { - type Output = F26Dot6; - - #[inline] - fn mul(self, other: F26Dot6) -> F26Dot6 { - F26Dot6(((self.0 as i64 * other.0 as i64 + 1 << 5) >> 6) as i32) - } -} - -impl Div for F26Dot6 { - type Output = F26Dot6; - - #[inline] - fn div(self, other: F26Dot6) -> F26Dot6 { - F26Dot6((((self.0 as i64) << 6) / other.0 as i64) as i32) - } -} - -impl Neg for F26Dot6 { - type Output = F26Dot6; - - #[inline] - fn neg(self) -> F26Dot6 { - F26Dot6(-self.0) - } -} - -/// 2.14 fixed point. -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct F2Dot14(pub i16); - -impl Zero for F2Dot14 { - #[inline] - fn zero() -> F2Dot14 { - F2DOT14_ZERO - } - - #[inline] - fn is_zero(&self) -> bool { - *self == F2DOT14_ZERO - } -} - -impl Add for F2Dot14 { - type Output = F2Dot14; - - #[inline] - fn add(self, other: F2Dot14) -> F2Dot14 { - F2Dot14(self.0 + other.0) - } -} - -impl Mul for F2Dot14 { - type Output = i16; - - #[inline] - fn mul(self, other: i16) -> i16 { - ((self.0 as i32 * other as i32) >> 14) as i16 - } -} - -impl Neg for F2Dot14 { - type Output = F2Dot14; - - #[inline] - fn neg(self) -> F2Dot14 { - F2Dot14(-self.0) - } -} - -/// A faster version of `Seek` that supports only forward motion from the current position. -pub trait Jump { - /// Moves the pointer forward `n` bytes from the *current* position. - fn jump(&mut self, n: usize) -> Result<(), ()>; -} - -impl<'a> Jump for &'a [u8] { - #[inline] - fn jump(&mut self, n: usize) -> Result<(), ()> { - if n <= self.len() { - *self = &(*self)[n..]; - Ok(()) - } else { - Err(()) - } - } -} -