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 b0cb4041..00000000 Binary files a/pathfinder-classic/resources/tests/nimbus-sans/NimbusSanL-Regu.ttf and /dev/null differ diff --git a/pathfinder-classic/resources/tests/nimbus-sans/README b/pathfinder-classic/resources/tests/nimbus-sans/README deleted file mode 100644 index e05577db..00000000 --- a/pathfinder-classic/resources/tests/nimbus-sans/README +++ /dev/null @@ -1,51 +0,0 @@ -Cyrillized free URW fonts. - -These fonts were made from the free URW fonts distributed with ghostcript. -There are NO changes in the latin part of them (I hope). -Cyrillic glyphs were added by copying suitable latin ones -and painting oulines of unique cyrillic glyphs in same style as the others. -For all modification pfaedit was used. -The license for result is (of course) same as for original fonts, -i.e. GPL with an exception that you can put these fonts in your own non-GPLed -documents. (Looks like LGPL from my point of view =). - -The "sources" of these fonts in the native pfaedit format are available -at ftp://ftp.gnome.ru/fonts/sources - -The great font editor pfaedit is available at http://pfaedit.sf.net. -That page also includes some links to fonts created by -George Williams -- the author of pfaedit. - -Acknowledgements: -I would like to thank George Williams, the pfaedit's author and developer. -He is the most bug-reporter/feature-requester friendly developer -I ever saw in my not so short life. At some moment in the future -I must write a book about him: "George Williams and my best experience -in bug-reporting." George also greatly helped me bug-hunting these fonts, -explained to me some very important things about fonts and font design, -quickly adopted pfaedit to my needs (or pointed me to The Right Place in -documentation where I found better way of doing things). - -I would like to thank Alexey Novodvorsky (aka AEN), who -pointed me to pfaedit and George Williams' fonts, explained -The Task to me. He is also one of the main participators in the -development of Sysiphus -- free repository of free software. -I didn't loose my time for compiling/installing and supporting -my linux box coz I used the result of Sysiphus developers' works. - -I would like to thank Sergey Vlasov, who tested these fonts and reported -about bugs. Also he help me to make some bug-reports to George about -pfaedit bugs. - -I would like Dmitry 40in, who did big QA for some font outlines, drawn some glyphs, -and explain some The Truths for me. - -I would like to thank Vlad Harchev (aka hvv), who -proofread this text for me. - -Also I have to thank RMS for GPL and URW for releasing the fonts -under it. - -Thank you very much! -Valek Filippov frob@df.ru -(C)opyLeft 2001 \ No newline at end of file diff --git a/pathfinder-classic/src/atlas.rs b/pathfinder-classic/src/atlas.rs deleted file mode 100644 index 94e579ff..00000000 --- a/pathfinder-classic/src/atlas.rs +++ /dev/null @@ -1,358 +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. - -//! 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(()) - } - } -} -