From a0b23592b5ace28b2bcd47052165e522e17121e2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 8 Feb 2017 15:34:16 -0800 Subject: [PATCH] Allow specific fonts in collections to be selected --- Cargo.toml | 1 + examples/generate-atlas.rs | 45 +++++++++++++++++++++++++-------- examples/lorem-ipsum.rs | 51 ++++++++++++++++++++++++-------------- src/otf/mod.rs | 38 +++++++++++++++++++++------- src/rasterizer.rs | 10 ++++++-- 5 files changed, 104 insertions(+), 41 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 96be331d..a236f48e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ git = "https://github.com/pcwalton/compute-shader.git" [dev-dependencies] bencher = "0.1" +clap = "2.20" image = "0.12" quickcheck = "0.4" diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index 7edf0c09..6107b0ad 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -1,6 +1,7 @@ /* 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; @@ -9,9 +10,10 @@ extern crate lord_drawquaad; extern crate memmap; extern crate pathfinder; +use clap::{App, Arg}; use compute_shader::buffer; -use compute_shader::image::{ExternalImage, Format}; -use compute_shader::instance::Instance; +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}; @@ -22,7 +24,6 @@ use pathfinder::coverage::CoverageBuffer; use pathfinder::outline::OutlineBuilder; use pathfinder::otf::Font; use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; -use std::env; use std::os::raw::c_void; const DEFAULT_POINT_SIZE: f32 = 24.0; @@ -30,6 +31,20 @@ const WIDTH: u32 = 512; const HEIGHT: u32 = 384; 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 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(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)); @@ -48,23 +63,27 @@ fn main() { let rasterizer_options = RasterizerOptions::from_env().unwrap(); let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); - let file = Mmap::open_path(env::args().nth(1).unwrap(), Protection::Read).unwrap(); + let file = Mmap::open_path(matches.value_of("FONT-FILE").unwrap(), Protection::Read).unwrap(); - let point_size = match env::args().nth(2) { + let point_size = match matches.value_of("POINT-SIZE") { + Some(point_size) => point_size.parse().unwrap(), None => DEFAULT_POINT_SIZE, - Some(point_size) => point_size.parse().unwrap() + }; + + let font_index = match matches.value_of("index") { + Some(index) => index.parse().unwrap(), + None => 0, }; // FIXME(pcwalton) - let shelf_height = (point_size * 2.0).ceil() as u32; - - let (outlines, atlas); unsafe { - let font = Font::new(file.as_slice()).unwrap(); + let font = Font::from_collection_index(file.as_slice(), font_index).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() { @@ -87,6 +106,8 @@ fn main() { .create_image(Format::R8, 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(); @@ -96,7 +117,9 @@ fn main() { let mut gl_texture = 0; unsafe { - gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT | gl::TEXTURE_FETCH_BARRIER_BIT); + 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(); diff --git a/examples/lorem-ipsum.rs b/examples/lorem-ipsum.rs index 6d24ba1c..2f462d27 100644 --- a/examples/lorem-ipsum.rs +++ b/examples/lorem-ipsum.rs @@ -1,9 +1,7 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -#![feature(alloc_system)] - -extern crate alloc_system; +extern crate clap; extern crate compute_shader; extern crate euclid; extern crate gl; @@ -12,9 +10,10 @@ extern crate image; extern crate memmap; extern crate pathfinder; +use clap::{App, Arg}; use compute_shader::buffer; use compute_shader::image::{ExternalImage, Format, Image}; -use compute_shader::instance::Instance; +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, WindowEvent}; @@ -28,13 +27,11 @@ use pathfinder::outline::{OutlineBuilder, Outlines}; use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions}; use pathfinder::shaper; use std::char; -use std::env; use std::fs::File; use std::io::Read; use std::mem; use std::os::raw::c_void; use std::path::Path; -use std::process; const ATLAS_SIZE: u32 = 2048; const WIDTH: u32 = 640; @@ -55,6 +52,17 @@ static TEXT_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 1.0]; static ATLAS_DUMP_FILENAME: &'static str = "lorem-ipsum-atlas.png"; 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 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(font_arg).arg(text_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)); @@ -74,12 +82,8 @@ fn main() { let (width, height) = window.get_framebuffer_size(); let mut device_pixel_size = Size2D::new(width as u32, height as u32); - let mut args = env::args(); - args.next(); - let font_path = args.next().unwrap_or_else(|| usage()); - let mut text = "".to_string(); - match args.next() { + match matches.value_of("TEXT-FILE") { Some(path) => drop(File::open(path).unwrap().read_to_string(&mut text).unwrap()), None => text.push_str(TEXT), } @@ -94,10 +98,15 @@ fn main() { chars.sort(); let codepoint_ranges = CodepointRanges::from_sorted_chars(&chars); - let file = Mmap::open_path(font_path, Protection::Read).unwrap(); + let font_index = match matches.value_of("index") { + Some(index) => index.parse().unwrap(), + None => 0, + }; + + let file = Mmap::open_path(matches.value_of("FONT-FILE").unwrap(), Protection::Read).unwrap(); let (font, glyph_mapping); unsafe { - font = Font::new(file.as_slice()).unwrap(); + font = Font::from_collection_index(file.as_slice(), font_index).unwrap(); glyph_mapping = font.glyph_mapping_for_codepoint_ranges(&codepoint_ranges.ranges).unwrap(); } @@ -270,6 +279,8 @@ struct Renderer { fps_gl_texture: GLuint, query: GLuint, + + shading_language: ShadingLanguage, } impl Renderer { @@ -378,6 +389,8 @@ impl Renderer { gl::GenQueries(1, &mut query); } + let shading_language = instance.shading_language(); + Renderer { rasterizer: rasterizer, @@ -407,6 +420,8 @@ impl Renderer { fps_gl_texture: fps_gl_texture, query: query, + + shading_language: shading_language, } } @@ -438,7 +453,10 @@ impl Renderer { self.rasterizer.queue().flush().unwrap(); unsafe { - gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT | gl::TEXTURE_FETCH_BARRIER_BIT); + if self.shading_language == ShadingLanguage::Glsl { + gl::MemoryBarrier(gl::SHADER_IMAGE_ACCESS_BARRIER_BIT | + gl::TEXTURE_FETCH_BARRIER_BIT); + } gl::Viewport(0, 0, @@ -794,11 +812,6 @@ fn create_image(rasterizer: &Rasterizer, atlas_size: &Size2D) -> (Image, GL (compute_image, gl_texture) } -fn usage() -> ! { - println!("usage: lorem-ipsum /path/to/font.ttf [/path/to/text.txt]"); - process::exit(0) -} - static COMPOSITE_VERTEX_SHADER: &'static str = "\ #version 330 diff --git a/src/otf/mod.rs b/src/otf/mod.rs index 81741967..4894f66f 100644 --- a/src/otf/mod.rs +++ b/src/otf/mod.rs @@ -96,11 +96,24 @@ pub struct FontTable<'a> { } impl<'a> Font<'a> { - /// Creates a new font from a byte buffer containing the contents of a file (`.ttf`, `.otf`, - /// etc.) + /// 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. /// /// Returns the font on success or an error on failure. pub fn new<'b>(bytes: &'b [u8]) -> Result, Error> { + Font::from_collection_index(bytes, 0) + } + + /// 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. + /// + /// Returns the font on success or an error on failure. + pub fn from_collection_index<'b>(bytes: &'b [u8], index: u32) -> Result, Error> { // Check magic number. let mut reader = bytes; let mut magic_number = try!(reader.read_u32::().map_err(Error::eof)); @@ -116,15 +129,16 @@ impl<'a> Font<'a> { } let num_fonts = try!(reader.read_u32::().map_err(Error::eof)); - if num_fonts == 0 { - return Err(Error::Failed) + if index >= num_fonts { + return Err(Error::FontIndexOutOfBounds) } + try!(reader.jump(index as usize * mem::size_of::()).map_err(Error::eof)); let table_offset = try!(reader.read_u32::().map_err(Error::eof)); Font::from_otf(&bytes, table_offset) } magic_number if SFNT_VERSIONS.contains(&magic_number) => Font::from_otf(bytes, 0), - 0x0100 => Font::from_dfont(bytes), + 0x0100 => Font::from_dfont_index(bytes, index), OTTO => { // TODO(pcwalton): Support CFF outlines. Err(Error::UnsupportedCffOutlines) @@ -202,7 +216,7 @@ impl<'a> Font<'a> { } /// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format - fn from_dfont<'b>(bytes: &'b [u8]) -> Result, Error> { + fn from_dfont_index<'b>(bytes: &'b [u8], index: u32) -> Result, Error> { let mut reader = bytes; // Read the Mac resource file header. @@ -250,10 +264,14 @@ impl<'a> Font<'a> { } } + // Check whether the index is in bounds. + if index >= resource_count as u32 + 1 { + return Err(Error::FontIndexOutOfBounds) + } + // Find the font we're interested in. - // - // TODO(pcwalton): This only gets the first one. Allow the user of this library to select - // others. + try!(reader.jump(index as usize * (mem::size_of::() * 2 + mem::size_of::() * 2)) + .map_err(Error::eof)); let sfnt_id = try!(reader.read_u16::().map_err(Error::eof)); let sfnt_name_offset = try!(reader.read_u16::().map_err(Error::eof)); let sfnt_data_offset = try!(reader.read_u32::().map_err(Error::eof)) & @@ -351,6 +369,8 @@ pub enum Error { 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. diff --git a/src/rasterizer.rs b/src/rasterizer.rs index fb5a5433..358a90c5 100644 --- a/src/rasterizer.rs +++ b/src/rasterizer.rs @@ -43,6 +43,7 @@ static DRAW_FRAGMENT_SHADER: &'static str = include_str!("../resources/shaders/d pub struct Rasterizer { device: Device, queue: Queue, + shading_language: ShadingLanguage, draw_program: GLuint, accum_program: Program, draw_vertex_array: GLuint, @@ -142,7 +143,8 @@ impl Rasterizer { } // FIXME(pcwalton): Don't panic if this fails to compile; just return an error. - let accum_source = match instance.shading_language() { + let shading_language = instance.shading_language(); + let accum_source = match shading_language { ShadingLanguage::Cl => ACCUM_CL_SHADER, ShadingLanguage::Glsl => ACCUM_COMPUTE_SHADER, }; @@ -153,6 +155,7 @@ impl Rasterizer { Ok(Rasterizer { device: device, queue: queue, + shading_language: shading_language, draw_program: draw_program, accum_program: accum_program, draw_vertex_array: draw_vertex_array, @@ -255,7 +258,10 @@ impl Rasterizer { // 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(); - gl::MemoryBarrier(gl::ALL_BARRIER_BITS); + + if self.shading_language == ShadingLanguage::Glsl { + gl::MemoryBarrier(gl::ALL_BARRIER_BITS); + } } let accum_uniforms = [