Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/fontdue/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/fontdue/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"4d4169db018aee13f2e5daba618c9e05291ef8d1180e85cbe87f9f52a5ee6612","Cargo.toml":"8b70ed968fb186fe8dc68aedc5b50b5b52b6b3b06eeee8758892c1a88c8be213","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"a72354dd6206c20dff7c9fd45b137261e00a31f71ffb8fe67d5ca0c8170b5fd5","LICENSE-ZLIB":"d829cfa632d2a44fe9e303ff293add677ed70e4b8f5e2176aa7a19d0936e61ac","README.md":"7e0bee040678cc912fb5428727f2bf7b7acf29dc1513781a385b47dda8b70bfe","images/layout.png":"4f5667e41f032c0bdd88d8ebc8015746da9d959c0c0850b6592c3730bd3c503c","images/rasterize_cff.png":"cef7270e3cf1fb7982f75819cd0c24044c21474974d9d2b5e6cbf2eea1e9c71c","images/rasterize_glyf.png":"a5a6f0021f23265c8deaf7bd7138ff61ea050f57aa138b3497aa4bff6515b414","licenses/rust-lang/libm":"42926e590380d9377ad083dcd5a7618b0952dac81a929244f84d43fb5619acdd","licenses/xi-editor/xi-unicode":"58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd","rustfmt.toml":"3157dc102ad73cfffe8c1cb85b2a7df7c0b0b7e9d1df79fecd66f77cc78049b7","src/font.rs":"ed6624a3ce8cddc4fd335df3f6f57eb6c34f34d5715571ae1c734bbbaa89899f","src/hash.rs":"8de0d35512078b887720749178374f4066785c8a424399a48b5bcfad8232048e","src/layout.rs":"8c690306baab66c91c8f409166869bcc98715f29cfb2beb6c5e438d5297b8d44","src/lib.rs":"bb9a2361920b668ba6c0f911bc6e9e098bedb4bc35f87a651c5c628a2e4d2ab4","src/math.rs":"f8e862646c500abc9410c4871532fdc2d5514c4310650ccc4c6ae582a3c60c40","src/platform/float/as_i32.rs":"450b4592eb72732501c63567df813a5458a2c61c0f8471383e23265bf83986e6","src/platform/float/atan.rs":"5c9536f0ab1dc697f36f066d1744b68d2787dda3e7f0a12d27b8a3f8f087a8c5","src/platform/float/atan2.rs":"5af1769ceb089117672ddda2236e76cbc53793244e788db99a0b8abfa0611093","src/platform/float/ceil.rs":"9a30800c257f12edeedeb8033b429f435ca29ff264e41aed492779afe0af904d","src/platform/float/floor.rs":"692322e75e06e1c6e1f71f14b9c0ddc73c2c1ea2a1d4d55b5e3c91fa1a8b947d","src/platform/float/fract.rs":"e3571b76cc5bb9e79b9d24b4a2640b6601a85a60069ca8c9706d5a88ac583286","src/platform/float/get_bitmap.rs":"21105000f4e92eb5fcb9852240401cb079e0e0d4352480eb1774ff13fe122d23","src/platform/float/mod.rs":"e11c4a750f50901c453b894b66cb266d55916266b931891871c13efe329ee0cc","src/platform/float/sqrt.rs":"a4e84d7a8fa8e6463770c62a44e3068dfc7327a09ede3794267d8bf4a3220123","src/platform/float/trunc.rs":"280b93cbe3f7a6151b398334a72d7935de998f1c6e9403416fca0de7b220de93","src/platform/mod.rs":"332ac6cf424cb0576ab24b3084d41f2961dd0c24df3fceeea76057a100470b40","src/platform/simd_core.rs":"72736bf87d5a90887f68832121b503d28256bbe0873e20da53cef68c1b0ffa9e","src/platform/simd_x86.rs":"1e86090a28401183b8690c27ca0d479381157565011556a282449b007dde3cfc","src/raster.rs":"bf7faf0a104e8f28a898dfe7273ad9aeba3398b8c7c35f97f77439690019014f","src/table/kern.rs":"b562428f69e2ef354be205b5f25beeccad46f54c639ac3b96d5ae664e8b2b25c","src/table/mod.rs":"28b9bc3fa0c515c00b87ee550828d40ce62d66fec229597189e182db508197f6","src/table/parse.rs":"f667064eab75149d282b451035ab63e3196788952db44ebe362f505066780836","src/unicode/mod.rs":"caafbca7fd8f62d5bfaa2d90d5a1036bb52fd87ed35d7e722d7f7bbdffb54723","src/unicode/tables.rs":"60057ed6d4d95bd655d25380963fc78abd5f1b029dbfe55e007945657fec5627"},"package":"0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc"}
|
||||
46
third-party/vendor/fontdue/CHANGELOG.md
vendored
Normal file
46
third-party/vendor/fontdue/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Changes
|
||||
- Refactored development related artifacts into the `dev` folder. This reduces pollution in the main crate.
|
||||
- Removed some needless unsafe, documented other uses.
|
||||
|
||||
## [0.7.3] - 2023-04-16
|
||||
### Added
|
||||
- Expose layout settings on `Layout`
|
||||
- Add a line height option to LayoutSettings
|
||||
### Changes
|
||||
- Relicense to MIT OR Apache-2.0 OR Zlib
|
||||
- Update `hashbrown` to 0.13
|
||||
### Fixed
|
||||
- More doc typos.
|
||||
|
||||
## [0.7.2] - 2022-03-03
|
||||
### Added
|
||||
- Added `byte_offset` to GlyphPosition
|
||||
### Changes
|
||||
- Breaking - Renamed `line_start`/`line_end` to `glyph_start`/`glyph_end`
|
||||
### Fixed
|
||||
- More doc typos.
|
||||
- `line_start`/`glyph_start` skipping spacing characters.
|
||||
|
||||
## [0.7.1] - 2022-02-25
|
||||
### Changes
|
||||
- `ttf-parser` updated to 0.15
|
||||
### Fixed
|
||||
- `LinePosition` doc typo.
|
||||
- Benign compiler error in debug mode in layout.
|
||||
|
||||
## [0.7.0] - 2022-02-25
|
||||
### Added
|
||||
- A changelog.
|
||||
- `simd` flag, enabled by default. Leverages simd functions. This was implicitly always enabled prior.
|
||||
- `parallel` flag, disabled by default. Uses `std` + `rayon` to thread font loading.
|
||||
- `Font.chars()` gets all valid unicode codepoints that have mappings to glyph geometry in the font.
|
||||
- `LinePosition` holds various metadata on positioned lines computed during layout.
|
||||
### Changed
|
||||
- `Layout.lines()` returns a `Option<Vec<LinePosition>>` now instead of a line count.
|
||||
56
third-party/vendor/fontdue/Cargo.toml
vendored
Normal file
56
third-party/vendor/fontdue/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "fontdue"
|
||||
version = "0.7.3"
|
||||
authors = ["Joe Cumbo <mooman219@gmail.com>"]
|
||||
exclude = ["dev/**"]
|
||||
description = "A simple no_std font parser and rasterizer."
|
||||
homepage = "https://github.com/mooman219/fontdue"
|
||||
documentation = "https://docs.rs/fontdue"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"font",
|
||||
"text",
|
||||
"truetype",
|
||||
"opentype",
|
||||
"ttf",
|
||||
]
|
||||
categories = [
|
||||
"no-std",
|
||||
"gui",
|
||||
]
|
||||
license = "MIT OR Apache-2.0 OR Zlib"
|
||||
repository = "https://github.com/mooman219/fontdue"
|
||||
|
||||
[dependencies.hashbrown]
|
||||
version = "0.13"
|
||||
|
||||
[dependencies.rayon]
|
||||
version = "1.5.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.ttf-parser]
|
||||
version = "0.15"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["simd"]
|
||||
parallel = [
|
||||
"rayon",
|
||||
"hashbrown/rayon",
|
||||
]
|
||||
simd = []
|
||||
|
||||
[badges.maintenance]
|
||||
status = "experimental"
|
||||
176
third-party/vendor/fontdue/LICENSE-APACHE
vendored
Normal file
176
third-party/vendor/fontdue/LICENSE-APACHE
vendored
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
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
|
||||
21
third-party/vendor/fontdue/LICENSE-MIT
vendored
Normal file
21
third-party/vendor/fontdue/LICENSE-MIT
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Joe C (mooman219)
|
||||
|
||||
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.
|
||||
21
third-party/vendor/fontdue/LICENSE-ZLIB
vendored
Normal file
21
third-party/vendor/fontdue/LICENSE-ZLIB
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
Zlib License
|
||||
|
||||
Copyright (c) 2019 Joe C (mooman219)
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In
|
||||
no event will the authors be held liable for any damages arising from the use of
|
||||
this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including
|
||||
commercial applications, and to alter it and redistribute it freely, subject to
|
||||
the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim
|
||||
that you wrote the original software. If you use this software in a product, an
|
||||
acknowledgment in the product documentation would be appreciated but is not
|
||||
required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
110
third-party/vendor/fontdue/README.md
vendored
Normal file
110
third-party/vendor/fontdue/README.md
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Fontdue
|
||||
|
||||
[](https://github.com/mooman219/fontdue/actions)
|
||||
[](https://docs.rs/fontdue)
|
||||
[](https://crates.io/crates/fontdue)
|
||||
[](https://github.com/mooman219/fontdue/blob/master/LICENSE)
|
||||
|
||||
Fontdue is a simple, `no_std` (does not use the standard library for portability), pure Rust, TrueType (`.ttf/.ttc`) & OpenType (`.otf`) font rasterizer and layout tool. It strives to make interacting with fonts as fast as possible, and currently has the lowest end to end latency for a font rasterizer.
|
||||
|
||||
## Roadmap
|
||||
**Current goal:** `fontdue` is designed to be a replacement for `rusttype` [(link)](https://gitlab.redox-os.org/redox-os/rusttype), `ab_glyph` [(link)](https://github.com/alexheretic/ab-glyph), parts of `glyph_brush` [(link)](https://github.com/alexheretic/glyph-brush/tree/master/glyph-brush), and `glyph_brush_layout` [(link)](https://github.com/alexheretic/glyph-brush/tree/master/layout). This is a class of font libraries that don't tackle shaping.
|
||||
|
||||
**Future goals:** Shaping - the complex layout of text such as Arabic and Devanagari - will be added someday. There are a few potential pure Rust libraries (allsorts, rustybuzz, and swash) that are candidates for providing a shaping backend to Fontdue, but are relatively immature right now.
|
||||
|
||||
A **non-goal** of this library is to be allocation free and have a fast, "zero cost" initial load. This library _does_ make allocations and depends on the `alloc` crate. Fonts are fully parsed on creation and relevant information is stored in a more convenient to access format. Unlike other font libraries, the font structures have no lifetime dependencies since it allocates its own space.
|
||||
|
||||
## Example
|
||||
|
||||
[Live demo](https://mooman219.github.io/fontdue/). This demo is a web-assembly build of `fontdue` rasterizing to a browser canvas. It provides a side by side of characters being rasterized between `fontdue` and the browser's canvas text api provided with the same parameters.
|
||||
|
||||
Some other examples can be found under ./dev/examples.
|
||||
|
||||
### Rasterization
|
||||
The rasterization API should not see major changes in the near future.
|
||||
```rust
|
||||
// Read the font data.
|
||||
let font = include_bytes!("../resources/Roboto-Regular.ttf") as &[u8];
|
||||
// Parse it into the font type.
|
||||
let font = fontdue::Font::from_bytes(font, fontdue::FontSettings::default()).unwrap();
|
||||
// Rasterize and get the layout metrics for the letter 'g' at 17px.
|
||||
let (metrics, bitmap) = font.rasterize('g', 17.0);
|
||||
```
|
||||
|
||||
### Layout
|
||||
The layout API is immature and may see breaking changes. The layout `fontdue` provides is naïve and is only designed to be on par with existing libraries like `glyph_brush`.
|
||||
```rust
|
||||
// Read the font data.
|
||||
let font = include_bytes!("../resources/fonts/Roboto-Regular.ttf") as &[u8];
|
||||
// Parse it into the font type.
|
||||
let roboto_regular = Font::from_bytes(font, fontdue::FontSettings::default()).unwrap();
|
||||
// The list of fonts that will be used during layout.
|
||||
let fonts = &[roboto_regular];
|
||||
// Create a layout context. Laying out text needs some heap allocations; reusing this context
|
||||
// reduces the need to reallocate space. We inform layout of which way the Y axis points here.
|
||||
let mut layout = Layout::new(CoordinateSystem::PositiveYUp);
|
||||
// By default, layout is initialized with the default layout settings. This call is redundant, but
|
||||
// demonstrates setting the value with your custom settings.
|
||||
layout.reset(&LayoutSettings {
|
||||
..LayoutSettings::default()
|
||||
});
|
||||
// The text that will be laid out, its size, and the index of the font in the font list to use for
|
||||
// that section of text.
|
||||
layout.append(fonts, &TextStyle::new("Hello ", 35.0, 0));
|
||||
layout.append(fonts, &TextStyle::new("world!", 40.0, 0));
|
||||
// Prints the layout for "Hello world!"
|
||||
println!("{:?}", layout.glyphs());
|
||||
|
||||
// If you wanted to attached metadata based on the TextStyle to the glyphs returned in the
|
||||
// glyphs() function, you can use the TextStyle::with_metadata function. In this example, the
|
||||
// Layout type is now parameterized with u8 (Layout<u8>). All styles need to share the same
|
||||
// metadata type.
|
||||
let mut layout = Layout::new(CoordinateSystem::PositiveYUp);
|
||||
layout.append(fonts, &TextStyle::with_user_data("Hello ", 35.0, 0, 10u8));
|
||||
layout.append(fonts, &TextStyle::with_user_data("world!", 40.0, 0, 20u8));
|
||||
println!("{:?}", layout.glyphs());
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Rasterization
|
||||
|
||||
These benchmarks measure the time it takes to generate the glyph metrics and bitmap for the text "Sphinx of black quartz, judge my vow." over a range of sizes. The lower the line in the graph the better.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Layout
|
||||
|
||||
This benchmark measures the time it takes to layout latin characters of sample text with wrapping on word boundaries.
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
Licensed under any one of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
* Zlib license ([LICENSE-ZLIB](LICENSE-ZLIB) or http://opensource.org/licenses/Zlib)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be multi-licensed as above, without any additional terms or conditions.
|
||||
|
||||
## Notices
|
||||
|
||||
### Maintenance
|
||||
|
||||
Please bear with me on new features or quirks that you find. Bugs will take priority, but I don't have as much time as I would like to work on fontdue so please be patient, this is a solo project.
|
||||
|
||||
### TrueType & OpenType Table Support
|
||||
|
||||
Fontdue depends on `ttf-parser` ([link](https://github.com/RazrFalcon/ttf-parser)) for parsing fonts, which supports a wide range of TrueType and OpenType features.
|
||||
|
||||
### Attribution
|
||||
|
||||
`Fontdue` started as a vaguely more production ready fork of `font-rs` [(link)](https://github.com/raphlinus/font-rs) because of how fast it made rasterization look, and how simple the `rusttype` [(link)](https://gitlab.redox-os.org/redox-os/rusttype) crate made font parsing look. Since then, I've read a lot of font specification and modern rasterization techniques, rewriting `fontdue` from the ground up in the process into its own unique beast.
|
||||
BIN
third-party/vendor/fontdue/images/layout.png
vendored
Normal file
BIN
third-party/vendor/fontdue/images/layout.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
third-party/vendor/fontdue/images/rasterize_cff.png
vendored
Normal file
BIN
third-party/vendor/fontdue/images/rasterize_cff.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
third-party/vendor/fontdue/images/rasterize_glyf.png
vendored
Normal file
BIN
third-party/vendor/fontdue/images/rasterize_glyf.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
25
third-party/vendor/fontdue/licenses/rust-lang/libm
vendored
Normal file
25
third-party/vendor/fontdue/licenses/rust-lang/libm
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2018 Jorge Aparicio
|
||||
|
||||
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.
|
||||
202
third-party/vendor/fontdue/licenses/xi-editor/xi-unicode
vendored
Normal file
202
third-party/vendor/fontdue/licenses/xi-editor/xi-unicode
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
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.
|
||||
2
third-party/vendor/fontdue/rustfmt.toml
vendored
Normal file
2
third-party/vendor/fontdue/rustfmt.toml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
max_width = 110
|
||||
use_small_heuristics = "Off"
|
||||
620
third-party/vendor/fontdue/src/font.rs
vendored
Normal file
620
third-party/vendor/fontdue/src/font.rs
vendored
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
use crate::layout::GlyphRasterConfig;
|
||||
use crate::math::{Geometry, Line};
|
||||
use crate::platform::{as_i32, ceil, floor, fract, is_negative};
|
||||
use crate::raster::Raster;
|
||||
use crate::table::TableKern;
|
||||
use crate::unicode;
|
||||
use crate::FontResult;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::*;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::mem;
|
||||
use core::num::NonZeroU16;
|
||||
use core::ops::Deref;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use ttf_parser::{Face, FaceParsingError, GlyphId, Tag};
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
/// Defines the bounds for a glyph's outline in subpixels. A glyph's outline is always contained in
|
||||
/// its bitmap.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct OutlineBounds {
|
||||
/// Subpixel offset of the left-most edge of the glyph's outline.
|
||||
pub xmin: f32,
|
||||
/// Subpixel offset of the bottom-most edge of the glyph's outline.
|
||||
pub ymin: f32,
|
||||
/// The width of the outline in subpixels.
|
||||
pub width: f32,
|
||||
/// The height of the outline in subpixels.
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
impl Default for OutlineBounds {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
xmin: 0.0,
|
||||
ymin: 0.0,
|
||||
width: 0.0,
|
||||
height: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutlineBounds {
|
||||
/// Scales the bounding box by the given factor.
|
||||
#[inline(always)]
|
||||
pub fn scale(&self, scale: f32) -> OutlineBounds {
|
||||
OutlineBounds {
|
||||
xmin: self.xmin * scale,
|
||||
ymin: self.ymin * scale,
|
||||
width: self.width * scale,
|
||||
height: self.height * scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulates all layout information associated with a glyph for a fixed scale.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct Metrics {
|
||||
/// Whole pixel offset of the left-most edge of the bitmap. This may be negative to reflect the
|
||||
/// glyph is positioned to the left of the origin.
|
||||
pub xmin: i32,
|
||||
/// Whole pixel offset of the bottom-most edge of the bitmap. This may be negative to reflect
|
||||
/// the glyph is positioned below the baseline.
|
||||
pub ymin: i32,
|
||||
/// The width of the bitmap in whole pixels.
|
||||
pub width: usize,
|
||||
/// The height of the bitmap in whole pixels.
|
||||
pub height: usize,
|
||||
/// Advance width of the glyph in subpixels. Used in horizontal fonts.
|
||||
pub advance_width: f32,
|
||||
/// Advance height of the glyph in subpixels. Used in vertical fonts.
|
||||
pub advance_height: f32,
|
||||
/// The bounding box that contains the glyph's outline at the offsets specified by the font.
|
||||
/// This is always a smaller box than the bitmap bounds.
|
||||
pub bounds: OutlineBounds,
|
||||
}
|
||||
|
||||
impl Default for Metrics {
|
||||
fn default() -> Self {
|
||||
Metrics {
|
||||
xmin: 0,
|
||||
ymin: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
advance_width: 0.0,
|
||||
advance_height: 0.0,
|
||||
bounds: OutlineBounds::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics associated with line positioning.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct LineMetrics {
|
||||
/// The highest point that any glyph in the font extends to above the baseline. Typically
|
||||
/// positive.
|
||||
pub ascent: f32,
|
||||
/// The lowest point that any glyph in the font extends to below the baseline. Typically
|
||||
/// negative.
|
||||
pub descent: f32,
|
||||
/// The gap to leave between the descent of one line and the ascent of the next. This is of
|
||||
/// course only a guideline given by the font's designers.
|
||||
pub line_gap: f32,
|
||||
/// A precalculated value for the height or width of the line depending on if the font is laid
|
||||
/// out horizontally or vertically. It's calculated by: ascent - descent + line_gap.
|
||||
pub new_line_size: f32,
|
||||
}
|
||||
|
||||
impl LineMetrics {
|
||||
/// Creates a new line metrics struct and computes the new line size.
|
||||
fn new(ascent: i16, descent: i16, line_gap: i16) -> LineMetrics {
|
||||
// Operations between this values can exceed i16, so we extend to i32 here.
|
||||
let (ascent, descent, line_gap) = (ascent as i32, descent as i32, line_gap as i32);
|
||||
LineMetrics {
|
||||
ascent: ascent as f32,
|
||||
descent: descent as f32,
|
||||
line_gap: line_gap as f32,
|
||||
new_line_size: (ascent - descent + line_gap) as f32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Scales the line metrics by the given factor.
|
||||
#[inline(always)]
|
||||
fn scale(&self, scale: f32) -> LineMetrics {
|
||||
LineMetrics {
|
||||
ascent: self.ascent * scale,
|
||||
descent: self.descent * scale,
|
||||
line_gap: self.line_gap * scale,
|
||||
new_line_size: self.new_line_size * scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores compiled geometry and metric information.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Glyph {
|
||||
pub v_lines: Vec<Line>,
|
||||
pub m_lines: Vec<Line>,
|
||||
advance_width: f32,
|
||||
advance_height: f32,
|
||||
pub bounds: OutlineBounds,
|
||||
}
|
||||
|
||||
impl Default for Glyph {
|
||||
fn default() -> Self {
|
||||
Glyph {
|
||||
v_lines: Vec::new(),
|
||||
m_lines: Vec::new(),
|
||||
advance_width: 0.0,
|
||||
advance_height: 0.0,
|
||||
bounds: OutlineBounds::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for controlling specific font and layout behavior.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct FontSettings {
|
||||
/// The default is 0. The index of the font to use if parsing a font collection.
|
||||
pub collection_index: u32,
|
||||
/// The default is 40. The scale in px the font geometry is optimized for. Fonts rendered at
|
||||
/// the scale defined here will be the most optimal in terms of looks and performance. Glyphs
|
||||
/// rendered smaller than this scale will look the same but perform slightly worse, while
|
||||
/// glyphs rendered larger than this will looks worse but perform slightly better. The units of
|
||||
/// the scale are pixels per Em unit.
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
impl Default for FontSettings {
|
||||
fn default() -> FontSettings {
|
||||
FontSettings {
|
||||
collection_index: 0,
|
||||
scale: 40.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a font. Fonts are immutable after creation and owns its own copy of the font data.
|
||||
#[derive(Clone)]
|
||||
pub struct Font {
|
||||
name: Option<String>,
|
||||
units_per_em: f32,
|
||||
glyphs: Vec<Glyph>,
|
||||
char_to_glyph: HashMap<char, NonZeroU16>,
|
||||
horizontal_line_metrics: Option<LineMetrics>,
|
||||
horizontal_kern: Option<HashMap<u32, i16>>,
|
||||
vertical_line_metrics: Option<LineMetrics>,
|
||||
settings: FontSettings,
|
||||
hash: usize,
|
||||
}
|
||||
|
||||
impl Hash for Font {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.hash.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Font {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("Font")
|
||||
.field("name", &self.name)
|
||||
.field("settings", &self.settings)
|
||||
.field("units_per_em", &self.units_per_em)
|
||||
.field("hash", &self.hash)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a ttf-parser FaceParsingError into a string.
|
||||
fn convert_error(error: FaceParsingError) -> &'static str {
|
||||
use FaceParsingError::*;
|
||||
match error {
|
||||
MalformedFont => "An attempt to read out of bounds detected.",
|
||||
UnknownMagic => "Face data must start with 0x00010000, 0x74727565, 0x4F54544F or 0x74746366.",
|
||||
FaceIndexOutOfBounds => "The face index is larger than the number of faces in the font.",
|
||||
NoHeadTable => "The head table is missing or malformed.",
|
||||
NoHheaTable => "The hhea table is missing or malformed.",
|
||||
NoMaxpTable => "The maxp table is missing or malformed.",
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_name(face: &Face) -> Option<String> {
|
||||
for name in face.names() {
|
||||
if name.name_id == 4 && name.is_unicode() {
|
||||
return Some(unicode::decode_utf16(name.name));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl Font {
|
||||
/// Constructs a font from an array of bytes.
|
||||
pub fn from_bytes<Data: Deref<Target = [u8]>>(data: Data, settings: FontSettings) -> FontResult<Font> {
|
||||
let hash = crate::hash::hash(&data);
|
||||
|
||||
let face = match Face::from_slice(&data, settings.collection_index) {
|
||||
Ok(f) => f,
|
||||
Err(e) => return Err(convert_error(e)),
|
||||
};
|
||||
let name = convert_name(&face);
|
||||
|
||||
// Optionally get kerning values for the font. This should be a try block in the future.
|
||||
let horizontal_kern: Option<HashMap<u32, i16>> = (|| {
|
||||
let table: &[u8] = face.table_data(Tag::from_bytes(&b"kern"))?;
|
||||
let table: TableKern = TableKern::new(table)?;
|
||||
Some(table.horizontal_mappings)
|
||||
})();
|
||||
|
||||
// Collect all the unique codepoint to glyph mappings.
|
||||
let glyph_count = face.number_of_glyphs();
|
||||
let mut seen_mappings = HashSet::with_capacity(glyph_count as usize);
|
||||
let mut char_to_glyph = HashMap::with_capacity(glyph_count as usize);
|
||||
seen_mappings.insert(0u16);
|
||||
if let Some(subtable) = face.tables().cmap {
|
||||
for subtable in subtable.subtables {
|
||||
subtable.codepoints(|codepoint| {
|
||||
if let Some(mapping) = subtable.glyph_index(codepoint) {
|
||||
if let Some(mapping) = NonZeroU16::new(mapping.0) {
|
||||
seen_mappings.insert(mapping.get());
|
||||
char_to_glyph.insert(unsafe { mem::transmute(codepoint) }, mapping);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let units_per_em = face.units_per_em() as f32;
|
||||
|
||||
// Parse and store all unique codepoints.
|
||||
let mut glyphs: Vec<Glyph> = vec::from_elem(Glyph::default(), glyph_count as usize);
|
||||
|
||||
let generate_glyph = |index: u16| -> Result<Glyph, &'static str> {
|
||||
if index >= glyph_count {
|
||||
return Err("Attempted to map a codepoint out of bounds.");
|
||||
}
|
||||
|
||||
let mut glyph = Glyph::default();
|
||||
let glyph_id = GlyphId(index);
|
||||
if let Some(advance_width) = face.glyph_hor_advance(glyph_id) {
|
||||
glyph.advance_width = advance_width as f32;
|
||||
}
|
||||
if let Some(advance_height) = face.glyph_ver_advance(glyph_id) {
|
||||
glyph.advance_height = advance_height as f32;
|
||||
}
|
||||
|
||||
let mut geometry = Geometry::new(settings.scale, units_per_em);
|
||||
face.outline_glyph(glyph_id, &mut geometry);
|
||||
geometry.finalize(&mut glyph);
|
||||
Ok(glyph)
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
for index in seen_mappings {
|
||||
glyphs[index as usize] = generate_glyph(index)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
{
|
||||
let generated: Vec<(u16, Glyph)> = seen_mappings
|
||||
.into_par_iter()
|
||||
.map(|index| Ok((index, generate_glyph(index)?)))
|
||||
.collect::<Result<_, _>>()?;
|
||||
for (index, glyph) in generated {
|
||||
glyphs[index as usize] = glyph;
|
||||
}
|
||||
}
|
||||
|
||||
// New line metrics.
|
||||
let horizontal_line_metrics =
|
||||
Some(LineMetrics::new(face.ascender(), face.descender(), face.line_gap()));
|
||||
let vertical_line_metrics = if let Some(ascender) = face.vertical_ascender() {
|
||||
Some(LineMetrics::new(
|
||||
ascender,
|
||||
face.vertical_descender().unwrap_or(0),
|
||||
face.vertical_line_gap().unwrap_or(0),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Font {
|
||||
name,
|
||||
glyphs,
|
||||
char_to_glyph,
|
||||
units_per_em,
|
||||
horizontal_line_metrics,
|
||||
horizontal_kern,
|
||||
vertical_line_metrics,
|
||||
settings,
|
||||
hash,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns all valid unicode codepoints that have mappings to glyph geometry in the font, along
|
||||
/// with their associated index. This does not include grapheme cluster mappings. The mapped
|
||||
/// NonZeroU16 index can be used in the _indexed font functions.
|
||||
pub fn chars(&self) -> &HashMap<char, NonZeroU16> {
|
||||
&self.char_to_glyph
|
||||
}
|
||||
|
||||
/// Returns a precomputed hash for the font file.
|
||||
pub fn file_hash(&self) -> usize {
|
||||
self.hash
|
||||
}
|
||||
|
||||
/// New line metrics for fonts that append characters to lines horizontally, and append new
|
||||
/// lines vertically (above or below the current line). Only populated for fonts with the
|
||||
/// appropriate metrics, none if it's missing.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `px` - The size to scale the line metrics by. The units of the scale are pixels per Em
|
||||
/// unit.
|
||||
pub fn horizontal_line_metrics(&self, px: f32) -> Option<LineMetrics> {
|
||||
let metrics = self.horizontal_line_metrics?;
|
||||
Some(metrics.scale(self.scale_factor(px)))
|
||||
}
|
||||
|
||||
/// New line metrics for fonts that append characters to lines vertically, and append new
|
||||
/// lines horizontally (left or right of the current line). Only populated for fonts with the
|
||||
/// appropriate metrics, none if it's missing.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `px` - The size to scale the line metrics by. The units of the scale are pixels per Em
|
||||
/// unit.
|
||||
pub fn vertical_line_metrics(&self, px: f32) -> Option<LineMetrics> {
|
||||
let metrics = self.vertical_line_metrics?;
|
||||
Some(metrics.scale(self.scale_factor(px)))
|
||||
}
|
||||
|
||||
/// Gets the font's units per em.
|
||||
#[inline(always)]
|
||||
pub fn units_per_em(&self) -> f32 {
|
||||
self.units_per_em
|
||||
}
|
||||
|
||||
/// Calculates the glyph's outline scale factor for a given px size. The units of the scale are
|
||||
/// pixels per Em unit.
|
||||
#[inline(always)]
|
||||
pub fn scale_factor(&self, px: f32) -> f32 {
|
||||
px / self.units_per_em
|
||||
}
|
||||
|
||||
/// Retrieves the horizontal scaled kerning value for two adjacent characters.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `left` - The character on the left hand side of the pairing.
|
||||
/// * `right` - The character on the right hand side of the pairing.
|
||||
/// * `px` - The size to scale the kerning value for. The units of the scale are pixels per Em
|
||||
/// unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Option<f32>` - The horizontal scaled kerning value if one is present in the font for the
|
||||
/// given left and right pair, None otherwise.
|
||||
#[inline(always)]
|
||||
pub fn horizontal_kern(&self, left: char, right: char, px: f32) -> Option<f32> {
|
||||
self.horizontal_kern_indexed(self.lookup_glyph_index(left), self.lookup_glyph_index(right), px)
|
||||
}
|
||||
|
||||
/// Retrieves the horizontal scaled kerning value for two adjacent glyph indicies.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `left` - The glyph index on the left hand side of the pairing.
|
||||
/// * `right` - The glyph index on the right hand side of the pairing.
|
||||
/// * `px` - The size to scale the kerning value for. The units of the scale are pixels per Em
|
||||
/// unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Option<f32>` - The horizontal scaled kerning value if one is present in the font for the
|
||||
/// given left and right pair, None otherwise.
|
||||
#[inline(always)]
|
||||
pub fn horizontal_kern_indexed(&self, left: u16, right: u16, px: f32) -> Option<f32> {
|
||||
let scale = self.scale_factor(px);
|
||||
let map = self.horizontal_kern.as_ref()?;
|
||||
let key = u32::from(left) << 16 | u32::from(right);
|
||||
let value = map.get(&key)?;
|
||||
Some((*value as f32) * scale)
|
||||
}
|
||||
|
||||
/// Retrieves the layout metrics for the given character. If the character isn't present in the
|
||||
/// font, then the layout for the font's default character is returned instead.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The character in the font to to generate the layout metrics for.
|
||||
/// * `px` - The size to generate the layout metrics for the character at. Cannot be negative.
|
||||
/// The units of the scale are pixels per Em unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the glyph.
|
||||
#[inline]
|
||||
pub fn metrics(&self, character: char, px: f32) -> Metrics {
|
||||
self.metrics_indexed(self.lookup_glyph_index(character), px)
|
||||
}
|
||||
|
||||
/// Retrieves the layout metrics at the given index. You normally want to be using
|
||||
/// metrics(char, f32) instead, unless your glyphs are pre-indexed.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The glyph index in the font to to generate the layout metrics for.
|
||||
/// * `px` - The size to generate the layout metrics for the glyph at. Cannot be negative. The
|
||||
/// units of the scale are pixels per Em unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the glyph.
|
||||
pub fn metrics_indexed(&self, index: u16, px: f32) -> Metrics {
|
||||
let glyph = &self.glyphs[index as usize];
|
||||
let scale = self.scale_factor(px);
|
||||
let (metrics, _, _) = self.metrics_raw(scale, glyph, 0.0);
|
||||
metrics
|
||||
}
|
||||
|
||||
/// Internal function to generate the metrics, offset_x, and offset_y of the glyph.
|
||||
fn metrics_raw(&self, scale: f32, glyph: &Glyph, offset: f32) -> (Metrics, f32, f32) {
|
||||
let bounds = glyph.bounds.scale(scale);
|
||||
let mut offset_x = fract(bounds.xmin + offset);
|
||||
let mut offset_y = fract(1.0 - fract(bounds.height) - fract(bounds.ymin));
|
||||
if is_negative(offset_x) {
|
||||
offset_x += 1.0;
|
||||
}
|
||||
if is_negative(offset_y) {
|
||||
offset_y += 1.0;
|
||||
}
|
||||
let metrics = Metrics {
|
||||
xmin: as_i32(floor(bounds.xmin)),
|
||||
ymin: as_i32(floor(bounds.ymin)),
|
||||
width: as_i32(ceil(bounds.width + offset_x)) as usize,
|
||||
height: as_i32(ceil(bounds.height + offset_y)) as usize,
|
||||
advance_width: scale * glyph.advance_width,
|
||||
advance_height: scale * glyph.advance_height,
|
||||
bounds,
|
||||
};
|
||||
(metrics, offset_x, offset_y)
|
||||
}
|
||||
|
||||
/// Retrieves the layout rasterized bitmap for the given raster config. If the raster config's
|
||||
/// character isn't present in the font, then the layout and bitmap for the font's default
|
||||
/// character's raster is returned instead.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `config` - The settings to render the character at.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
|
||||
/// * `Vec<u8>` - Coverage vector for the glyph. Coverage is a linear scale where 0 represents
|
||||
/// 0% coverage of that pixel by the glyph and 255 represents 100% coverage. The vec starts at
|
||||
/// the top left corner of the glyph.
|
||||
#[inline]
|
||||
pub fn rasterize_config(&self, config: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
|
||||
self.rasterize_indexed(config.glyph_index, config.px)
|
||||
}
|
||||
|
||||
/// Retrieves the layout metrics and rasterized bitmap for the given character. If the
|
||||
/// character isn't present in the font, then the layout and bitmap for the font's default
|
||||
/// character is returned instead.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `character` - The character to rasterize.
|
||||
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
|
||||
/// are pixels per Em unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
|
||||
/// * `Vec<u8>` - Coverage vector for the glyph. Coverage is a linear scale where 0 represents
|
||||
/// 0% coverage of that pixel by the glyph and 255 represents 100% coverage. The vec starts at
|
||||
/// the top left corner of the glyph.
|
||||
#[inline]
|
||||
pub fn rasterize(&self, character: char, px: f32) -> (Metrics, Vec<u8>) {
|
||||
self.rasterize_indexed(self.lookup_glyph_index(character), px)
|
||||
}
|
||||
|
||||
/// Retrieves the layout rasterized bitmap for the given raster config. If the raster config's
|
||||
/// character isn't present in the font, then the layout and bitmap for the font's default
|
||||
/// character's raster is returned instead.
|
||||
///
|
||||
/// This will perform the operation with the width multiplied by 3, as to simulate subpixels.
|
||||
/// Taking these as RGB values will perform subpixel anti aliasing.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `config` - The settings to render the character at.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
|
||||
/// * `Vec<u8>` - Swizzled RGB coverage vector for the glyph. Coverage is a linear scale where 0
|
||||
/// represents 0% coverage of that subpixel by the glyph and 255 represents 100% coverage. The
|
||||
/// vec starts at the top left corner of the glyph.
|
||||
#[inline]
|
||||
pub fn rasterize_config_subpixel(&self, config: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
|
||||
self.rasterize_indexed_subpixel(config.glyph_index, config.px)
|
||||
}
|
||||
|
||||
/// Retrieves the layout metrics and rasterized bitmap for the given character. If the
|
||||
/// character isn't present in the font, then the layout and bitmap for the font's default
|
||||
/// character is returned instead.
|
||||
///
|
||||
/// This will perform the operation with the width multiplied by 3, as to simulate subpixels.
|
||||
/// Taking these as RGB values will perform subpixel anti aliasing.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `character` - The character to rasterize.
|
||||
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
|
||||
/// are pixels per Em unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
|
||||
/// * `Vec<u8>` - Swizzled RGB coverage vector for the glyph. Coverage is a linear scale where 0
|
||||
/// represents 0% coverage of that subpixel by the glyph and 255 represents 100% coverage. The
|
||||
/// vec starts at the top left corner of the glyph.
|
||||
#[inline]
|
||||
pub fn rasterize_subpixel(&self, character: char, px: f32) -> (Metrics, Vec<u8>) {
|
||||
self.rasterize_indexed_subpixel(self.lookup_glyph_index(character), px)
|
||||
}
|
||||
|
||||
/// Retrieves the layout metrics and rasterized bitmap at the given index. You normally want to
|
||||
/// be using rasterize(char, f32) instead, unless your glyphs are pre-indexed.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The glyph index in the font to rasterize.
|
||||
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
|
||||
/// are pixels per Em unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
|
||||
/// * `Vec<u8>` - Coverage vector for the glyph. Coverage is a linear scale where 0 represents
|
||||
/// 0% coverage of that pixel by the glyph and 255 represents 100% coverage. The vec starts at
|
||||
/// the top left corner of the glyph.
|
||||
pub fn rasterize_indexed(&self, index: u16, px: f32) -> (Metrics, Vec<u8>) {
|
||||
if px <= 0.0 {
|
||||
return (Metrics::default(), Vec::new());
|
||||
}
|
||||
let glyph = &self.glyphs[index as usize];
|
||||
let scale = self.scale_factor(px);
|
||||
let (metrics, offset_x, offset_y) = self.metrics_raw(scale, glyph, 0.0);
|
||||
let mut canvas = Raster::new(metrics.width, metrics.height);
|
||||
canvas.draw(&glyph, scale, scale, offset_x, offset_y);
|
||||
(metrics, canvas.get_bitmap())
|
||||
}
|
||||
|
||||
/// Retrieves the layout metrics and rasterized bitmap at the given index. You normally want to
|
||||
/// be using rasterize(char, f32) instead, unless your glyphs are pre-indexed.
|
||||
///
|
||||
/// This will perform the operation with the width multiplied by 3, as to simulate subpixels.
|
||||
/// Taking these as RGB values will perform subpixel anti aliasing.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index` - The glyph index in the font to rasterize.
|
||||
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
|
||||
/// are pixels per Em unit.
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
|
||||
/// * `Vec<u8>` - Swizzled RGB coverage vector for the glyph. Coverage is a linear scale where 0
|
||||
/// represents 0% coverage of that subpixel by the glyph and 255 represents 100% coverage. The
|
||||
/// vec starts at the top left corner of the glyph.
|
||||
pub fn rasterize_indexed_subpixel(&self, index: u16, px: f32) -> (Metrics, Vec<u8>) {
|
||||
if px <= 0.0 {
|
||||
return (Metrics::default(), Vec::new());
|
||||
}
|
||||
let glyph = &self.glyphs[index as usize];
|
||||
let scale = self.scale_factor(px);
|
||||
let (metrics, offset_x, offset_y) = self.metrics_raw(scale, glyph, 0.0);
|
||||
let mut canvas = Raster::new(metrics.width * 3, metrics.height);
|
||||
canvas.draw(&glyph, scale * 3.0, scale, offset_x, offset_y);
|
||||
(metrics, canvas.get_bitmap())
|
||||
}
|
||||
|
||||
/// Finds the internal glyph index for the given character. If the character is not present in
|
||||
/// the font then 0 is returned.
|
||||
#[inline]
|
||||
pub fn lookup_glyph_index(&self, character: char) -> u16 {
|
||||
// This is safe, Option<NonZeroU16> is documented to have the same layout as u16.
|
||||
unsafe { mem::transmute::<Option<NonZeroU16>, u16>(self.char_to_glyph.get(&character).copied()) }
|
||||
}
|
||||
|
||||
/// Gets the total glyphs in the font.
|
||||
pub fn glyph_count(&self) -> u16 {
|
||||
self.glyphs.len() as u16
|
||||
}
|
||||
}
|
||||
123
third-party/vendor/fontdue/src/hash.rs
vendored
Normal file
123
third-party/vendor/fontdue/src/hash.rs
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2015 The Rust 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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! # Fx Hash
|
||||
//!
|
||||
//! This hashing algorithm was extracted from the Rustc compiler. This is the same hashing
|
||||
//! algorithm used for some internal operations in Firefox. The strength of this algorithm
|
||||
//! is in hashing 8 bytes at a time on 64-bit platforms, where the FNV algorithm works on one
|
||||
//! byte at a time.
|
||||
//!
|
||||
//! ## Disclaimer
|
||||
//!
|
||||
//! It is **not a cryptographically secure** hash, so it is strongly recommended that you do
|
||||
//! not use this hash for cryptographic purproses. Furthermore, this hashing algorithm was
|
||||
//! not designed to prevent any attacks for determining collisions which could be used to
|
||||
//! potentially cause quadratic behavior in `HashMap`s. So it is not recommended to expose
|
||||
//! this hash in places where collissions or DDOS attacks may be a concern.
|
||||
|
||||
use core::convert::TryInto;
|
||||
use core::ops::BitXor;
|
||||
|
||||
const ROTATE: u32 = 5;
|
||||
const SEED64: u64 = 0x517cc1b727220a95;
|
||||
const SEED32: u32 = (SEED64 & 0xFFFF_FFFF) as u32;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const SEED: usize = SEED32 as usize;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const SEED: usize = SEED64 as usize;
|
||||
|
||||
trait HashWord {
|
||||
fn hash_word(&mut self, word: Self);
|
||||
}
|
||||
|
||||
impl HashWord for usize {
|
||||
#[inline]
|
||||
fn hash_word(&mut self, word: Self) {
|
||||
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED);
|
||||
}
|
||||
}
|
||||
|
||||
impl HashWord for u32 {
|
||||
#[inline]
|
||||
fn hash_word(&mut self, word: Self) {
|
||||
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED32);
|
||||
}
|
||||
}
|
||||
|
||||
impl HashWord for u64 {
|
||||
#[inline]
|
||||
fn hash_word(&mut self, word: Self) {
|
||||
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED64);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
fn read_u32(buf: &[u8]) -> u32 {
|
||||
u32::from_le_bytes(buf[..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
fn read_u64(buf: &[u8]) -> u64 {
|
||||
u64::from_le_bytes(buf[..8].try_into().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
fn read_u32(buf: &[u8]) -> u32 {
|
||||
u32::from_be_bytes(buf[..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
fn read_u64(buf: &[u8]) -> u64 {
|
||||
u64::from_be_bytes(buf[..8].try_into().unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
fn write(initial_state: usize, mut bytes: &[u8]) -> usize {
|
||||
let mut hash = initial_state as u32;
|
||||
while bytes.len() >= 4 {
|
||||
let n = read_u32(bytes);
|
||||
hash.hash_word(n);
|
||||
bytes = bytes.split_at(4).1;
|
||||
}
|
||||
|
||||
for byte in bytes {
|
||||
hash.hash_word(*byte as u32);
|
||||
}
|
||||
hash as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
fn write(initial_state: usize, mut bytes: &[u8]) -> usize {
|
||||
let mut hash = initial_state as u64;
|
||||
while bytes.len() >= 8 {
|
||||
let n = read_u64(bytes);
|
||||
hash.hash_word(n);
|
||||
bytes = bytes.split_at(8).1;
|
||||
}
|
||||
|
||||
if bytes.len() >= 4 {
|
||||
let n = read_u32(bytes);
|
||||
hash.hash_word(n as u64);
|
||||
bytes = bytes.split_at(4).1;
|
||||
}
|
||||
|
||||
for byte in bytes {
|
||||
hash.hash_word(*byte as u64);
|
||||
}
|
||||
hash as usize
|
||||
}
|
||||
|
||||
pub fn hash(bytes: &[u8]) -> usize {
|
||||
write(0usize, bytes)
|
||||
}
|
||||
567
third-party/vendor/fontdue/src/layout.rs
vendored
Normal file
567
third-party/vendor/fontdue/src/layout.rs
vendored
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
pub use crate::unicode::CharacterData;
|
||||
|
||||
use crate::unicode::{read_utf8, LinebreakData, Linebreaker, LINEBREAK_NONE};
|
||||
use crate::Font;
|
||||
use crate::{
|
||||
platform::{ceil, floor},
|
||||
Metrics,
|
||||
};
|
||||
use alloc::vec::*;
|
||||
use core::borrow::Borrow;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
/// Horizontal alignment options for text when a max_width is provided.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum HorizontalAlign {
|
||||
/// Aligns text to the left of the region defined by the max_width.
|
||||
Left,
|
||||
/// Aligns text to the center of the region defined by the max_width.
|
||||
Center,
|
||||
/// Aligns text to the right of the region defined by the max_width.
|
||||
Right,
|
||||
}
|
||||
|
||||
/// Vertical alignment options for text when a max_height is provided.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VerticalAlign {
|
||||
/// Aligns text to the top of the region defined by the max_height.
|
||||
Top,
|
||||
/// Aligns text to the middle of the region defined by the max_height.
|
||||
Middle,
|
||||
/// Aligns text to the bottom of the region defined by the max_height.
|
||||
Bottom,
|
||||
}
|
||||
|
||||
/// Wrap style is a hint for how strings of text should be wrapped to the next line. Line wrapping
|
||||
/// can happen when the max width/height is reached.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum WrapStyle {
|
||||
/// Word will break lines by the Unicode line breaking algorithm (Standard Annex #14) This will
|
||||
/// generally break lines where you expect them to be broken at and will preserve words.
|
||||
Word,
|
||||
/// Letter will not preserve words, breaking into a new line after the nearest letter.
|
||||
Letter,
|
||||
}
|
||||
|
||||
/// The direction that the Y coordinate increases in. Layout needs to be aware of your coordinate
|
||||
/// system to place the glyphs correctly.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum CoordinateSystem {
|
||||
/// The Y coordinate increases up relative to the window or image. The higher up on the window,
|
||||
/// the more positive Y becomes.
|
||||
PositiveYUp,
|
||||
/// The Y coordinate increases down relative to the window or image. The lower down on the
|
||||
/// window, the more positive Y becomes.
|
||||
PositiveYDown,
|
||||
}
|
||||
|
||||
/// Settings to configure how text layout is constrained. Text layout is considered best effort and
|
||||
/// layout may violate the constraints defined here if they prevent text from being laid out.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct LayoutSettings {
|
||||
/// The leftmost boundary of the text region.
|
||||
pub x: f32,
|
||||
/// The topmost boundary of the text region.
|
||||
pub y: f32,
|
||||
/// An optional rightmost boundary on the text region. A line of text that exceeds the
|
||||
/// max_width is wrapped to the line below. If the width of a glyph is larger than the
|
||||
/// max_width, the glyph will overflow past the max_width. The application is responsible for
|
||||
/// handling the overflow.
|
||||
pub max_width: Option<f32>,
|
||||
/// An optional bottom boundary on the text region. This is used for positioning the
|
||||
/// vertical_align option. Text that exceeds the defined max_height will overflow past it. The
|
||||
/// application is responsible for handling the overflow.
|
||||
pub max_height: Option<f32>,
|
||||
/// The default is Left. This option does nothing if the max_width isn't set.
|
||||
pub horizontal_align: HorizontalAlign,
|
||||
/// The default is Top. This option does nothing if the max_height isn't set.
|
||||
pub vertical_align: VerticalAlign,
|
||||
/// The height of each line as a multiplier of the default.
|
||||
pub line_height: f32,
|
||||
/// The default is Word. Wrap style is a hint for how strings of text should be wrapped to the
|
||||
/// next line. Line wrapping can happen when the max width/height is reached.
|
||||
pub wrap_style: WrapStyle,
|
||||
/// The default is true. This option enables hard breaks, like new line characters, to
|
||||
/// prematurely wrap lines. If false, hard breaks will not prematurely create a new line.
|
||||
pub wrap_hard_breaks: bool,
|
||||
}
|
||||
|
||||
impl Default for LayoutSettings {
|
||||
fn default() -> LayoutSettings {
|
||||
LayoutSettings {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
horizontal_align: HorizontalAlign::Left,
|
||||
vertical_align: VerticalAlign::Top,
|
||||
line_height: 1.0,
|
||||
wrap_style: WrapStyle::Word,
|
||||
wrap_hard_breaks: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for rasterizing a glyph. This struct is also a hashable key that can be used to
|
||||
/// uniquely identify a rasterized glyph for applications that want to cache glyphs.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct GlyphRasterConfig {
|
||||
/// The glyph index represented by the glyph being positioned.
|
||||
pub glyph_index: u16,
|
||||
/// The scale of the glyph being positioned in px.
|
||||
pub px: f32,
|
||||
/// The hash of the font used in layout to raster the glyph.
|
||||
pub font_hash: usize,
|
||||
}
|
||||
|
||||
impl Hash for GlyphRasterConfig {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.glyph_index.hash(state);
|
||||
self.px.to_bits().hash(state);
|
||||
self.font_hash.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for GlyphRasterConfig {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.glyph_index == other.glyph_index && self.px == other.px && self.font_hash == other.font_hash
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for GlyphRasterConfig {}
|
||||
|
||||
/// A positioned scaled glyph.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct GlyphPosition<U: Copy + Clone = ()> {
|
||||
/// Hashable key that can be used to uniquely identify a rasterized glyph.
|
||||
pub key: GlyphRasterConfig,
|
||||
/// The index of the font used to generate this glyph position.
|
||||
pub font_index: usize,
|
||||
/// The associated character that generated this glyph. A character may generate multiple
|
||||
/// glyphs.
|
||||
pub parent: char,
|
||||
/// The xmin of the glyph bounding box. This represents the left side of the glyph. Dimensions
|
||||
/// are in pixels, and are always whole numbers.
|
||||
pub x: f32,
|
||||
/// The ymin of the glyph bounding box. If your coordinate system is PositiveYUp, this
|
||||
/// represents the bottom side of the glyph. If your coordinate system is PositiveYDown, this
|
||||
/// represents the top side of the glyph. This is like this so that (y + height) always produces
|
||||
/// the other bound for the glyph.
|
||||
pub y: f32,
|
||||
/// The width of the glyph. Dimensions are in pixels.
|
||||
pub width: usize,
|
||||
/// The height of the glyph. Dimensions are in pixels.
|
||||
pub height: usize,
|
||||
/// The byte offset into the original string used in the append call which created
|
||||
/// this glyph.
|
||||
pub byte_offset: usize,
|
||||
/// Additional metadata associated with the character used to generate this glyph.
|
||||
pub char_data: CharacterData,
|
||||
/// Custom user data associated with the text styled used to generate this glyph.
|
||||
pub user_data: U,
|
||||
}
|
||||
|
||||
/// A style description for a segment of text.
|
||||
pub struct TextStyle<'a, U: Copy + Clone = ()> {
|
||||
/// The text to layout.
|
||||
pub text: &'a str,
|
||||
/// The scale of the text in pixel units. The units of the scale are pixels per Em unit.
|
||||
pub px: f32,
|
||||
/// The font to layout the text in.
|
||||
pub font_index: usize,
|
||||
/// Additional user data to associate with glyphs produced by this text style.
|
||||
pub user_data: U,
|
||||
}
|
||||
|
||||
impl<'a> TextStyle<'a> {
|
||||
pub fn new(text: &'a str, px: f32, font_index: usize) -> TextStyle<'a> {
|
||||
TextStyle {
|
||||
text,
|
||||
px,
|
||||
font_index,
|
||||
user_data: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: Copy + Clone> TextStyle<'a, U> {
|
||||
pub fn with_user_data(text: &'a str, px: f32, font_index: usize, user_data: U) -> TextStyle<'a, U> {
|
||||
TextStyle {
|
||||
text,
|
||||
px,
|
||||
font_index,
|
||||
user_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics about a positioned line.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct LinePosition {
|
||||
/// The y coordinate of the baseline of this line, in pixels.
|
||||
pub baseline_y: f32,
|
||||
/// How much empty space is left at the end of the line before any alignment. If no max width is
|
||||
/// specified, f32::MAX is used.
|
||||
pub padding: f32,
|
||||
/// The highest point that any glyph in the font extends to above the baseline. Typically
|
||||
/// positive. If there are multiple styles on this line, this is their max value.
|
||||
pub max_ascent: f32,
|
||||
/// The lowest point that any glyph in the font extends to below the baseline. Typically
|
||||
/// negative. If there are multiple styles on this line, this is their min value.
|
||||
pub min_descent: f32,
|
||||
/// The gap to leave between the descent of one line and the ascent of the next. This is of
|
||||
/// course only a guideline given by the font's designers. If there are multiple styles on this
|
||||
/// line, this is their max value.
|
||||
pub max_line_gap: f32,
|
||||
/// A precalculated value for the of the line depending. It's calculated by: ascent - descent +
|
||||
/// line_gap. If there are multiple styles on this line, this is their max value.
|
||||
pub max_new_line_size: f32,
|
||||
/// The GlyphPosition index of the first glyph in the line.
|
||||
pub glyph_start: usize,
|
||||
/// The GlyphPosition index of the last glyph in the line.
|
||||
pub glyph_end: usize,
|
||||
/// The x offset into the first layout pass.
|
||||
tracking_x: f32,
|
||||
}
|
||||
|
||||
impl Default for LinePosition {
|
||||
fn default() -> Self {
|
||||
LinePosition {
|
||||
baseline_y: 0.0,
|
||||
padding: 0.0,
|
||||
max_ascent: 0.0,
|
||||
min_descent: 0.0,
|
||||
max_line_gap: 0.0,
|
||||
max_new_line_size: 0.0,
|
||||
glyph_start: 0,
|
||||
glyph_end: 0,
|
||||
tracking_x: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Text layout requires a small amount of heap usage which is contained in the Layout struct. This
|
||||
/// context is reused between layout calls. Reusing the Layout struct will greatly reduce memory
|
||||
/// allocations and is advisable for performance.
|
||||
pub struct Layout<U: Copy + Clone = ()> {
|
||||
/// Marks if layout should be performed as if the Y axis is flipped (Positive Y incrementing
|
||||
/// down instead of up).
|
||||
flip: bool,
|
||||
/// Origin position. Left side of the region text is being laid out in.
|
||||
x: f32,
|
||||
/// Origin position. Top side of the region text is being laid out in.
|
||||
y: f32,
|
||||
/// A mask to filter only linebreak types being requested.
|
||||
wrap_mask: LinebreakData,
|
||||
/// The max width of the region text is being laid out in.
|
||||
max_width: f32,
|
||||
/// The max height of the region text is being laid out in.
|
||||
max_height: f32,
|
||||
/// A multiplier for how text fills unused vertical space.
|
||||
vertical_align: f32,
|
||||
/// A multiplier for how text fills unused horizontal space.
|
||||
horizontal_align: f32,
|
||||
/// A multiplier for the amount of space between lines.
|
||||
line_height: f32,
|
||||
/// The current height of all laid out text.
|
||||
height: f32,
|
||||
|
||||
/// Finalized glyph state.
|
||||
output: Vec<GlyphPosition<U>>,
|
||||
/// Intermediate glyph state.
|
||||
glyphs: Vec<GlyphPosition<U>>,
|
||||
|
||||
/// Linebreak state. Used to derive linebreaks from past glyphs.
|
||||
linebreaker: Linebreaker,
|
||||
/// The current highest priority linebreak (Hard > Soft > None).
|
||||
linebreak_prev: LinebreakData,
|
||||
/// The x position that the glyph that has the current highest priority linebreak status starts
|
||||
/// at.
|
||||
linebreak_pos: f32,
|
||||
/// The index of the glyph that has the current highest priority linebreak status. This glyph is
|
||||
/// the last glyph on a line if a linebreak is required.
|
||||
linebreak_idx: usize,
|
||||
|
||||
/// Layout state of each line currently laid out. This always has at least 1 element.
|
||||
line_metrics: Vec<LinePosition>,
|
||||
/// The x position the next glyph starts at.
|
||||
current_pos: f32,
|
||||
/// The ceil(ascent) of the current style.
|
||||
current_ascent: f32,
|
||||
/// The ceil(descent) of the current style.
|
||||
current_descent: f32,
|
||||
/// The ceil(line_gap) of the current style.
|
||||
current_line_gap: f32,
|
||||
/// The ceil(new_line_size) of the current style.
|
||||
current_new_line: f32,
|
||||
/// The x position the current line starts at.
|
||||
start_pos: f32,
|
||||
|
||||
/// The settings currently being used for layout.
|
||||
settings: LayoutSettings,
|
||||
}
|
||||
|
||||
impl<'a, U: Copy + Clone> Layout<U> {
|
||||
/// Creates a layout instance. This requires the direction that the Y coordinate increases in.
|
||||
/// Layout needs to be aware of your coordinate system to place the glyphs correctly.
|
||||
pub fn new(coordinate_system: CoordinateSystem) -> Layout<U> {
|
||||
let settings = LayoutSettings::default();
|
||||
|
||||
let mut layout = Layout {
|
||||
flip: coordinate_system == CoordinateSystem::PositiveYDown,
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
wrap_mask: LINEBREAK_NONE,
|
||||
max_width: 0.0,
|
||||
max_height: 0.0,
|
||||
vertical_align: 0.0,
|
||||
horizontal_align: 0.0,
|
||||
line_height: 1.0,
|
||||
output: Vec::new(),
|
||||
glyphs: Vec::new(),
|
||||
line_metrics: Vec::new(),
|
||||
linebreaker: Linebreaker::new(),
|
||||
linebreak_prev: LINEBREAK_NONE,
|
||||
linebreak_pos: 0.0,
|
||||
linebreak_idx: 0,
|
||||
current_pos: 0.0,
|
||||
current_ascent: 0.0,
|
||||
current_descent: 0.0,
|
||||
current_line_gap: 0.0,
|
||||
current_new_line: 0.0,
|
||||
start_pos: 0.0,
|
||||
height: 0.0,
|
||||
settings,
|
||||
};
|
||||
layout.reset(&settings);
|
||||
layout
|
||||
}
|
||||
|
||||
/// Resets the current layout settings and clears all appended text.
|
||||
pub fn reset(&mut self, settings: &LayoutSettings) {
|
||||
self.settings = *settings;
|
||||
self.x = settings.x;
|
||||
self.y = settings.y;
|
||||
self.wrap_mask = LinebreakData::from_mask(
|
||||
settings.wrap_style == WrapStyle::Word,
|
||||
settings.wrap_hard_breaks,
|
||||
settings.max_width.is_some(),
|
||||
);
|
||||
self.max_width = settings.max_width.unwrap_or(core::f32::MAX);
|
||||
self.max_height = settings.max_height.unwrap_or(core::f32::MAX);
|
||||
self.vertical_align = if settings.max_height.is_none() {
|
||||
0.0
|
||||
} else {
|
||||
match settings.vertical_align {
|
||||
VerticalAlign::Top => 0.0,
|
||||
VerticalAlign::Middle => 0.5,
|
||||
VerticalAlign::Bottom => 1.0,
|
||||
}
|
||||
};
|
||||
self.horizontal_align = if settings.max_width.is_none() {
|
||||
0.0
|
||||
} else {
|
||||
match settings.horizontal_align {
|
||||
HorizontalAlign::Left => 0.0,
|
||||
HorizontalAlign::Center => 0.5,
|
||||
HorizontalAlign::Right => 1.0,
|
||||
}
|
||||
};
|
||||
self.line_height = settings.line_height;
|
||||
self.clear();
|
||||
}
|
||||
|
||||
/// Keeps current layout settings but clears all appended text.
|
||||
pub fn clear(&mut self) {
|
||||
self.glyphs.clear();
|
||||
self.output.clear();
|
||||
self.line_metrics.clear();
|
||||
self.line_metrics.push(LinePosition::default());
|
||||
|
||||
self.linebreaker.reset();
|
||||
self.linebreak_prev = LINEBREAK_NONE;
|
||||
self.linebreak_pos = 0.0;
|
||||
self.linebreak_idx = 0;
|
||||
self.current_pos = 0.0;
|
||||
self.current_ascent = 0.0;
|
||||
self.current_descent = 0.0;
|
||||
self.current_line_gap = 0.0;
|
||||
self.current_new_line = 0.0;
|
||||
self.start_pos = 0.0;
|
||||
self.height = 0.0;
|
||||
}
|
||||
|
||||
/// Gets the current height of the appended text.
|
||||
pub fn height(&self) -> f32 {
|
||||
if let Some(line) = self.line_metrics.last() {
|
||||
self.height + line.max_new_line_size
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the currently positioned lines. If there are no lines positioned, this returns none.
|
||||
pub fn lines(&'a self) -> Option<&'a Vec<LinePosition>> {
|
||||
if self.glyphs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&self.line_metrics)
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs layout for text horizontally, and wrapping vertically. This makes a best effort
|
||||
/// attempt at laying out the text defined in the given styles with the provided layout
|
||||
/// settings. Text may overflow out of the bounds defined in the layout settings and it's up
|
||||
/// to the application to decide how to deal with this.
|
||||
///
|
||||
/// Characters from the input string can only be omitted from the output, they are never
|
||||
/// reordered. The output buffer will always contain characters in the order they were defined
|
||||
/// in the styles.
|
||||
pub fn append<T: Borrow<Font>>(&mut self, fonts: &[T], style: &TextStyle<U>) {
|
||||
// The first layout pass requires some text.
|
||||
if style.text.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let font: &Font = &fonts[style.font_index].borrow();
|
||||
|
||||
if let Some(metrics) = font.horizontal_line_metrics(style.px) {
|
||||
self.current_ascent = ceil(metrics.ascent);
|
||||
self.current_new_line = ceil(metrics.new_line_size);
|
||||
self.current_descent = ceil(metrics.descent);
|
||||
self.current_line_gap = ceil(metrics.line_gap);
|
||||
if let Some(line) = self.line_metrics.last_mut() {
|
||||
if self.current_ascent > line.max_ascent {
|
||||
line.max_ascent = self.current_ascent;
|
||||
}
|
||||
if self.current_descent < line.min_descent {
|
||||
line.min_descent = self.current_descent;
|
||||
}
|
||||
if self.current_line_gap > line.max_line_gap {
|
||||
line.max_line_gap = self.current_line_gap;
|
||||
}
|
||||
if self.current_new_line > line.max_new_line_size {
|
||||
line.max_new_line_size = self.current_new_line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut byte_offset = 0;
|
||||
while byte_offset < style.text.len() {
|
||||
let prev_byte_offset = byte_offset;
|
||||
let character = read_utf8(style.text.as_bytes(), &mut byte_offset);
|
||||
let linebreak = self.linebreaker.next(character).mask(self.wrap_mask);
|
||||
let glyph_index = font.lookup_glyph_index(character);
|
||||
let char_data = CharacterData::classify(character, glyph_index);
|
||||
let metrics = if !char_data.is_control() {
|
||||
font.metrics_indexed(glyph_index, style.px)
|
||||
} else {
|
||||
Metrics::default()
|
||||
};
|
||||
let advance = ceil(metrics.advance_width);
|
||||
|
||||
if linebreak >= self.linebreak_prev {
|
||||
self.linebreak_prev = linebreak;
|
||||
self.linebreak_pos = self.current_pos;
|
||||
self.linebreak_idx = self.glyphs.len().saturating_sub(1); // Mark the previous glyph
|
||||
}
|
||||
|
||||
// Perform a linebreak
|
||||
if linebreak.is_hard() || (self.current_pos - self.start_pos + advance > self.max_width) {
|
||||
self.linebreak_prev = LINEBREAK_NONE;
|
||||
let mut next_glyph_start = self.glyphs().len();
|
||||
if let Some(line) = self.line_metrics.last_mut() {
|
||||
line.glyph_end = self.linebreak_idx;
|
||||
line.padding = self.max_width - (self.linebreak_pos - self.start_pos);
|
||||
self.height += line.max_new_line_size * self.line_height;
|
||||
next_glyph_start = self.linebreak_idx + 1;
|
||||
}
|
||||
self.line_metrics.push(LinePosition {
|
||||
baseline_y: 0.0,
|
||||
padding: 0.0,
|
||||
max_ascent: self.current_ascent,
|
||||
min_descent: self.current_descent,
|
||||
max_line_gap: self.current_line_gap,
|
||||
max_new_line_size: self.current_new_line,
|
||||
glyph_start: next_glyph_start,
|
||||
glyph_end: 0,
|
||||
tracking_x: self.linebreak_pos,
|
||||
});
|
||||
self.start_pos = self.linebreak_pos;
|
||||
}
|
||||
|
||||
let y = if self.flip {
|
||||
floor(-metrics.bounds.height - metrics.bounds.ymin) // PositiveYDown
|
||||
} else {
|
||||
floor(metrics.bounds.ymin) // PositiveYUp
|
||||
};
|
||||
|
||||
self.glyphs.push(GlyphPosition {
|
||||
key: GlyphRasterConfig {
|
||||
glyph_index: glyph_index as u16,
|
||||
px: style.px,
|
||||
font_hash: font.file_hash(),
|
||||
},
|
||||
font_index: style.font_index,
|
||||
parent: character,
|
||||
byte_offset: prev_byte_offset,
|
||||
x: floor(self.current_pos + metrics.bounds.xmin),
|
||||
y,
|
||||
width: metrics.width,
|
||||
height: metrics.height,
|
||||
char_data,
|
||||
user_data: style.user_data,
|
||||
});
|
||||
self.current_pos += advance;
|
||||
}
|
||||
|
||||
if let Some(line) = self.line_metrics.last_mut() {
|
||||
line.padding = self.max_width - (self.current_pos - self.start_pos);
|
||||
line.glyph_end = self.glyphs.len().saturating_sub(1);
|
||||
}
|
||||
|
||||
self.finalize();
|
||||
}
|
||||
|
||||
fn finalize(&mut self) {
|
||||
// The second layout pass requires at least 1 glyph to layout.
|
||||
if self.glyphs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe { self.output.set_len(0) };
|
||||
self.output.reserve(self.glyphs.len());
|
||||
|
||||
let dir = if self.flip {
|
||||
-1.0 // PositiveYDown
|
||||
} else {
|
||||
1.0 // PositiveYUp
|
||||
};
|
||||
|
||||
let mut baseline_y = self.y - dir * floor((self.max_height - self.height()) * self.vertical_align);
|
||||
let mut idx = 0;
|
||||
for line in &mut self.line_metrics {
|
||||
let x_padding = self.x - line.tracking_x + floor(line.padding * self.horizontal_align);
|
||||
baseline_y -= dir * line.max_ascent;
|
||||
line.baseline_y = baseline_y;
|
||||
while idx <= line.glyph_end {
|
||||
let mut glyph = self.glyphs[idx];
|
||||
glyph.x += x_padding;
|
||||
glyph.y += baseline_y;
|
||||
self.output.push(glyph);
|
||||
idx += 1;
|
||||
}
|
||||
baseline_y -= dir * (line.max_new_line_size * self.line_height - line.max_ascent);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the currently laid out glyphs.
|
||||
pub fn glyphs(&'a self) -> &'a Vec<GlyphPosition<U>> {
|
||||
&self.output
|
||||
}
|
||||
|
||||
/// Gets the settings currently being used for layout.
|
||||
pub fn settings(&self) -> &LayoutSettings {
|
||||
&self.settings
|
||||
}
|
||||
}
|
||||
26
third-party/vendor/fontdue/src/lib.rs
vendored
Normal file
26
third-party/vendor/fontdue/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//! Fontdue is a font parser, rasterizer, and layout tool.
|
||||
//!
|
||||
//! This is a no_std crate, but still requires the alloc crate.
|
||||
|
||||
#![no_std]
|
||||
#![allow(dead_code)]
|
||||
#![allow(clippy::style)]
|
||||
#![allow(clippy::complexity)]
|
||||
#![allow(clippy::misnamed_getters)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod font;
|
||||
mod hash;
|
||||
/// Tools for laying out strings of text.
|
||||
pub mod layout;
|
||||
mod math;
|
||||
mod platform;
|
||||
mod raster;
|
||||
mod table;
|
||||
mod unicode;
|
||||
|
||||
pub use crate::font::*;
|
||||
|
||||
/// Alias for Result<T, &'static str>.
|
||||
pub type FontResult<T> = Result<T, &'static str>;
|
||||
480
third-party/vendor/fontdue/src/math.rs
vendored
Normal file
480
third-party/vendor/fontdue/src/math.rs
vendored
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
use crate::platform::{self, abs, atan2, f32x4, sqrt};
|
||||
use crate::{Glyph, OutlineBounds};
|
||||
use alloc::vec;
|
||||
use alloc::vec::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
struct AABB {
|
||||
/// Coordinate of the left-most edge.
|
||||
xmin: f32,
|
||||
/// Coordinate of the right-most edge.
|
||||
xmax: f32,
|
||||
/// Coordinate of the bottom-most edge.
|
||||
ymin: f32,
|
||||
/// Coordinate of the top-most edge.
|
||||
ymax: f32,
|
||||
}
|
||||
|
||||
impl Default for AABB {
|
||||
fn default() -> Self {
|
||||
AABB {
|
||||
xmin: 0.0,
|
||||
xmax: 0.0,
|
||||
ymin: 0.0,
|
||||
ymax: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
struct CubeCurve {
|
||||
a: Point,
|
||||
b: Point,
|
||||
c: Point,
|
||||
d: Point,
|
||||
}
|
||||
|
||||
impl CubeCurve {
|
||||
const fn new(a: Point, b: Point, c: Point, d: Point) -> CubeCurve {
|
||||
CubeCurve {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
}
|
||||
}
|
||||
|
||||
fn scale(&self, scale: f32) -> CubeCurve {
|
||||
CubeCurve {
|
||||
a: self.a.scale(scale),
|
||||
b: self.b.scale(scale),
|
||||
c: self.c.scale(scale),
|
||||
d: self.d.scale(scale),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_flat(&self, threshold: f32) -> bool {
|
||||
let (d1, d2, d3, d4) = f32x4::new(
|
||||
self.a.distance_squared(self.b),
|
||||
self.b.distance_squared(self.c),
|
||||
self.c.distance_squared(self.d),
|
||||
self.a.distance_squared(self.d),
|
||||
)
|
||||
.sqrt()
|
||||
.copied();
|
||||
(d1 + d2 + d3) < threshold * d4
|
||||
}
|
||||
|
||||
fn split(&self) -> (CubeCurve, CubeCurve) {
|
||||
let q0 = self.a.midpoint(self.b);
|
||||
let q1 = self.b.midpoint(self.c);
|
||||
let q2 = self.c.midpoint(self.d);
|
||||
let r0 = q0.midpoint(q1);
|
||||
let r1 = q1.midpoint(q2);
|
||||
let s0 = r0.midpoint(r1);
|
||||
(CubeCurve::new(self.a, q0, r0, s0), CubeCurve::new(s0, r1, q2, self.d))
|
||||
}
|
||||
|
||||
/// The point at time t in the curve.
|
||||
fn point(&self, t: f32) -> Point {
|
||||
let tm = 1.0 - t;
|
||||
let a = tm * tm * tm;
|
||||
let b = 3.0 * (tm * tm) * t;
|
||||
let c = 3.0 * tm * (t * t);
|
||||
let d = t * t * t;
|
||||
|
||||
let x = a * self.a.x + b * self.b.x + c * self.c.x + d * self.d.x;
|
||||
let y = a * self.a.y + b * self.b.y + c * self.c.y + d * self.d.y;
|
||||
Point::new(x, y)
|
||||
}
|
||||
|
||||
/// The slope of the tangent line at time t.
|
||||
fn slope(&self, t: f32) -> (f32, f32) {
|
||||
let tm = 1.0 - t;
|
||||
let a = 3.0 * (tm * tm);
|
||||
let b = 6.0 * tm * t;
|
||||
let c = 3.0 * (t * t);
|
||||
|
||||
let x = a * (self.b.x - self.a.x) + b * (self.c.x - self.b.x) + c * (self.d.x - self.c.x);
|
||||
let y = a * (self.b.y - self.a.y) + b * (self.c.y - self.b.y) + c * (self.d.y - self.c.y);
|
||||
(x, y)
|
||||
}
|
||||
|
||||
/// The angle of the tangent line at time t in rads.
|
||||
fn angle(&self, t: f32) -> f32 {
|
||||
let (x, y) = self.slope(t);
|
||||
abs(atan2(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
struct QuadCurve {
|
||||
a: Point,
|
||||
b: Point,
|
||||
c: Point,
|
||||
}
|
||||
|
||||
impl QuadCurve {
|
||||
fn new(a: Point, b: Point, c: Point) -> QuadCurve {
|
||||
QuadCurve {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
}
|
||||
}
|
||||
|
||||
fn scale(&self, scale: f32) -> QuadCurve {
|
||||
QuadCurve {
|
||||
a: self.a.scale(scale),
|
||||
b: self.b.scale(scale),
|
||||
c: self.c.scale(scale),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_flat(&self, threshold: f32) -> bool {
|
||||
let (d1, d2, d3, _) = f32x4::new(
|
||||
self.a.distance_squared(self.b),
|
||||
self.b.distance_squared(self.c),
|
||||
self.a.distance_squared(self.c),
|
||||
1.0,
|
||||
)
|
||||
.sqrt()
|
||||
.copied();
|
||||
(d1 + d2) < threshold * d3
|
||||
}
|
||||
|
||||
fn split(&self) -> (QuadCurve, QuadCurve) {
|
||||
let q0 = self.a.midpoint(self.b);
|
||||
let q1 = self.b.midpoint(self.c);
|
||||
let r0 = q0.midpoint(q1);
|
||||
(QuadCurve::new(self.a, q0, r0), QuadCurve::new(r0, q1, self.c))
|
||||
}
|
||||
|
||||
/// The point at time t in the curve.
|
||||
fn point(&self, t: f32) -> Point {
|
||||
let tm = 1.0 - t;
|
||||
let a = tm * tm;
|
||||
let b = 2.0 * tm * t;
|
||||
let c = t * t;
|
||||
|
||||
let x = a * self.a.x + b * self.b.x + c * self.c.x;
|
||||
let y = a * self.a.y + b * self.b.y + c * self.c.y;
|
||||
Point::new(x, y)
|
||||
}
|
||||
|
||||
/// The slope of the tangent line at time t.
|
||||
fn slope(&self, t: f32) -> (f32, f32) {
|
||||
let tm = 1.0 - t;
|
||||
let a = 2.0 * tm;
|
||||
let b = 2.0 * t;
|
||||
|
||||
let x = a * (self.b.x - self.a.x) + b * (self.c.x - self.b.x);
|
||||
let y = a * (self.b.y - self.a.y) + b * (self.c.y - self.b.y);
|
||||
(x, y)
|
||||
}
|
||||
|
||||
/// The angle of the tangent line at time t in rads.
|
||||
fn angle(&self, t: f32) -> f32 {
|
||||
let (x, y) = self.slope(t);
|
||||
abs(atan2(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Point {
|
||||
/// Absolute X coordinate.
|
||||
pub x: f32,
|
||||
/// Absolute Y coordinate.
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Default for Point {
|
||||
fn default() -> Self {
|
||||
Point {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub const fn new(x: f32, y: f32) -> Point {
|
||||
Point {
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale(&self, scale: f32) -> Point {
|
||||
Point {
|
||||
x: self.x * scale,
|
||||
y: self.y * scale,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn distance_squared(&self, other: Point) -> f32 {
|
||||
let x = self.x - other.x;
|
||||
let y = self.y - other.y;
|
||||
x * x + y * y
|
||||
}
|
||||
|
||||
pub fn distance(&self, other: Point) -> f32 {
|
||||
let x = self.x - other.x;
|
||||
let y = self.y - other.y;
|
||||
sqrt(x * x + y * y)
|
||||
}
|
||||
|
||||
pub fn midpoint(&self, other: Point) -> Point {
|
||||
Point {
|
||||
x: (self.x + other.x) / 2.0,
|
||||
y: (self.y + other.y) / 2.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Line {
|
||||
/// X0, Y0, X1, Y1.
|
||||
pub coords: f32x4,
|
||||
/// start_x_nudge, start_y_nudge, end_x_nudge, end_y_nudge.
|
||||
pub nudge: f32x4,
|
||||
/// x_first_adj, y_first_adj, none, none.
|
||||
pub adjustment: f32x4,
|
||||
/// tdx, tdy, dx, dy.
|
||||
pub params: f32x4,
|
||||
}
|
||||
|
||||
impl Line {
|
||||
pub fn new(start: Point, end: Point) -> Line {
|
||||
// Floor adjustment and nudge: 0.0, 0
|
||||
// Ceil adjustment and nudge: 1.0, 1
|
||||
const FLOOR_NUDGE: u32 = 0;
|
||||
const CEIL_NUDGE: u32 = 1;
|
||||
|
||||
let (x_start_nudge, x_first_adj) = if end.x >= start.x {
|
||||
(FLOOR_NUDGE, 1.0)
|
||||
} else {
|
||||
(CEIL_NUDGE, 0.0)
|
||||
};
|
||||
let (y_start_nudge, y_first_adj) = if end.y >= start.y {
|
||||
(FLOOR_NUDGE, 1.0)
|
||||
} else {
|
||||
(CEIL_NUDGE, 0.0)
|
||||
};
|
||||
|
||||
let x_end_nudge = if end.x > start.x {
|
||||
CEIL_NUDGE
|
||||
} else {
|
||||
FLOOR_NUDGE
|
||||
};
|
||||
let y_end_nudge = if end.y > start.y {
|
||||
CEIL_NUDGE
|
||||
} else {
|
||||
FLOOR_NUDGE
|
||||
};
|
||||
|
||||
let dx = end.x - start.x;
|
||||
let dy = end.y - start.y;
|
||||
let tdx = if dx == 0.0 {
|
||||
core::f32::MAX
|
||||
} else {
|
||||
1.0 / dx
|
||||
};
|
||||
let tdy = 1.0 / dy;
|
||||
|
||||
Line {
|
||||
coords: f32x4::new(start.x, start.y, end.x, end.y),
|
||||
nudge: f32x4::new_u32(x_start_nudge, y_start_nudge, x_end_nudge, y_end_nudge),
|
||||
adjustment: f32x4::new(x_first_adj, y_first_adj, 0.0, 0.0),
|
||||
params: f32x4::new(tdx, tdy, dx, dy),
|
||||
}
|
||||
}
|
||||
|
||||
fn reposition(&mut self, bounds: AABB, reverse: bool) {
|
||||
let (mut x0, mut y0, mut x1, mut y1) = if !reverse {
|
||||
self.coords.copied()
|
||||
} else {
|
||||
let (x0, y0, x1, y1) = self.coords.copied();
|
||||
(x1, y1, x0, y0)
|
||||
};
|
||||
|
||||
x0 -= bounds.xmin;
|
||||
y0 -= bounds.ymax;
|
||||
y0 = abs(y0);
|
||||
|
||||
x1 -= bounds.xmin;
|
||||
y1 -= bounds.ymax;
|
||||
y1 = abs(y1);
|
||||
|
||||
*self = Self::new(Point::new(x0, y0), Point::new(x1, y1));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Geometry {
|
||||
v_lines: Vec<Line>,
|
||||
m_lines: Vec<Line>,
|
||||
effective_bounds: AABB,
|
||||
start_point: Point,
|
||||
previous_point: Point,
|
||||
area: f32,
|
||||
reverse_points: bool,
|
||||
max_area: f32,
|
||||
}
|
||||
|
||||
struct Segment {
|
||||
a: Point,
|
||||
at: f32,
|
||||
c: Point,
|
||||
ct: f32,
|
||||
}
|
||||
|
||||
impl Segment {
|
||||
const fn new(a: Point, at: f32, c: Point, ct: f32) -> Segment {
|
||||
Segment {
|
||||
a,
|
||||
at,
|
||||
c,
|
||||
ct,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ttf_parser::OutlineBuilder for Geometry {
|
||||
fn move_to(&mut self, x0: f32, y0: f32) {
|
||||
let next_point = Point::new(x0, y0);
|
||||
self.start_point = next_point;
|
||||
self.previous_point = next_point;
|
||||
}
|
||||
|
||||
fn line_to(&mut self, x0: f32, y0: f32) {
|
||||
let next_point = Point::new(x0, y0);
|
||||
self.push(self.previous_point, next_point);
|
||||
self.previous_point = next_point;
|
||||
}
|
||||
|
||||
fn quad_to(&mut self, x0: f32, y0: f32, x1: f32, y1: f32) {
|
||||
let control_point = Point::new(x0, y0);
|
||||
let next_point = Point::new(x1, y1);
|
||||
|
||||
let curve = QuadCurve::new(self.previous_point, control_point, next_point);
|
||||
let mut stack = vec![Segment::new(self.previous_point, 0.0, next_point, 1.0)];
|
||||
while let Some(seg) = stack.pop() {
|
||||
let bt = (seg.at + seg.ct) * 0.5;
|
||||
let b = curve.point(bt);
|
||||
// This is twice the triangle area
|
||||
let area = (b.x - seg.a.x) * (seg.c.y - seg.a.y) - (seg.c.x - seg.a.x) * (b.y - seg.a.y);
|
||||
if platform::abs(area) > self.max_area {
|
||||
stack.push(Segment::new(seg.a, seg.at, b, bt));
|
||||
stack.push(Segment::new(b, bt, seg.c, seg.ct));
|
||||
} else {
|
||||
self.push(seg.a, seg.c);
|
||||
}
|
||||
}
|
||||
|
||||
self.previous_point = next_point;
|
||||
}
|
||||
|
||||
fn curve_to(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, x2: f32, y2: f32) {
|
||||
let first_control = Point::new(x0, y0);
|
||||
let second_control = Point::new(x1, y1);
|
||||
let next_point = Point::new(x2, y2);
|
||||
|
||||
let curve = CubeCurve::new(self.previous_point, first_control, second_control, next_point);
|
||||
let mut stack = vec![Segment::new(self.previous_point, 0.0, next_point, 1.0)];
|
||||
while let Some(seg) = stack.pop() {
|
||||
let bt = (seg.at + seg.ct) * 0.5;
|
||||
let b = curve.point(bt);
|
||||
// This is twice the triangle area
|
||||
let area = (b.x - seg.a.x) * (seg.c.y - seg.a.y) - (seg.c.x - seg.a.x) * (b.y - seg.a.y);
|
||||
if platform::abs(area) > self.max_area {
|
||||
stack.push(Segment::new(seg.a, seg.at, b, bt));
|
||||
stack.push(Segment::new(b, bt, seg.c, seg.ct));
|
||||
} else {
|
||||
self.push(seg.a, seg.c);
|
||||
}
|
||||
}
|
||||
self.previous_point = next_point;
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
if self.start_point != self.previous_point {
|
||||
self.push(self.previous_point, self.start_point);
|
||||
}
|
||||
self.previous_point = self.start_point;
|
||||
}
|
||||
}
|
||||
|
||||
impl Geometry {
|
||||
// Artisanal bespoke hand carved curves
|
||||
pub fn new(scale: f32, units_per_em: f32) -> Geometry {
|
||||
const ERROR_THRESHOLD: f32 = 3.0; // In pixels.
|
||||
let max_area = ERROR_THRESHOLD * 2.0 * (units_per_em / scale);
|
||||
|
||||
Geometry {
|
||||
v_lines: Vec::new(),
|
||||
m_lines: Vec::new(),
|
||||
effective_bounds: AABB {
|
||||
xmin: core::f32::MAX,
|
||||
xmax: core::f32::MIN,
|
||||
ymin: core::f32::MAX,
|
||||
ymax: core::f32::MIN,
|
||||
},
|
||||
start_point: Point::default(),
|
||||
previous_point: Point::default(),
|
||||
area: 0.0,
|
||||
reverse_points: false,
|
||||
max_area,
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, start: Point, end: Point) {
|
||||
// We're using to_bits here because we only care if they're _exactly_ the same.
|
||||
if start.y.to_bits() != end.y.to_bits() {
|
||||
self.area += (end.y - start.y) * (end.x + start.x);
|
||||
if start.x.to_bits() == end.x.to_bits() {
|
||||
self.v_lines.push(Line::new(start, end));
|
||||
} else {
|
||||
self.m_lines.push(Line::new(start, end));
|
||||
}
|
||||
Self::recalculate_bounds(&mut self.effective_bounds, start.x, start.y);
|
||||
Self::recalculate_bounds(&mut self.effective_bounds, end.x, end.y);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn finalize(mut self, glyph: &mut Glyph) {
|
||||
if self.v_lines.is_empty() && self.m_lines.is_empty() {
|
||||
self.effective_bounds = AABB::default();
|
||||
} else {
|
||||
self.reverse_points = self.area > 0.0;
|
||||
for line in self.v_lines.iter_mut().chain(self.m_lines.iter_mut()) {
|
||||
line.reposition(self.effective_bounds, self.reverse_points);
|
||||
}
|
||||
self.v_lines.shrink_to_fit();
|
||||
self.m_lines.shrink_to_fit();
|
||||
}
|
||||
glyph.v_lines = self.v_lines;
|
||||
glyph.m_lines = self.m_lines;
|
||||
glyph.bounds = OutlineBounds {
|
||||
xmin: self.effective_bounds.xmin,
|
||||
ymin: self.effective_bounds.ymin,
|
||||
width: self.effective_bounds.xmax - self.effective_bounds.xmin,
|
||||
height: self.effective_bounds.ymax - self.effective_bounds.ymin,
|
||||
};
|
||||
}
|
||||
|
||||
fn recalculate_bounds(bounds: &mut AABB, x: f32, y: f32) {
|
||||
if x < bounds.xmin {
|
||||
bounds.xmin = x;
|
||||
}
|
||||
if x > bounds.xmax {
|
||||
bounds.xmax = x;
|
||||
}
|
||||
if y < bounds.ymin {
|
||||
bounds.ymin = y;
|
||||
}
|
||||
if y > bounds.ymax {
|
||||
bounds.ymax = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
third-party/vendor/fontdue/src/platform/float/as_i32.rs
vendored
Normal file
16
third-party/vendor/fontdue/src/platform/float/as_i32.rs
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
#[inline(always)]
|
||||
pub fn as_i32(value: f32) -> i32 {
|
||||
value as i32
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
#[inline(always)]
|
||||
pub fn as_i32(value: f32) -> i32 {
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
unsafe { _mm_cvtss_si32(_mm_set_ss(value)) }
|
||||
}
|
||||
98
third-party/vendor/fontdue/src/platform/float/atan.rs
vendored
Normal file
98
third-party/vendor/fontdue/src/platform/float/atan.rs
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c
|
||||
*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/// The arctangent function.
|
||||
pub fn atan(mut x: f32) -> f32 {
|
||||
const ATAN_HI: [f32; 4] = [
|
||||
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
|
||||
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
|
||||
9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
|
||||
1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
|
||||
];
|
||||
|
||||
const ATAN_LO: [f32; 4] = [
|
||||
5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
|
||||
3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
|
||||
3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
|
||||
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
|
||||
];
|
||||
|
||||
const A_T: [f32; 5] =
|
||||
[3.3333328366e-01, -1.9999158382e-01, 1.4253635705e-01, -1.0648017377e-01, 6.1687607318e-02];
|
||||
let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120)
|
||||
|
||||
let z: f32;
|
||||
|
||||
let mut ix = x.to_bits();
|
||||
let sign = (ix >> 31) != 0;
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
if ix >= 0x4c800000 {
|
||||
/* if |x| >= 2**26 */
|
||||
if x.is_nan() {
|
||||
return x;
|
||||
}
|
||||
z = ATAN_HI[3] + x1p_120;
|
||||
return if sign {
|
||||
-z
|
||||
} else {
|
||||
z
|
||||
};
|
||||
}
|
||||
let id = if ix < 0x3ee00000 {
|
||||
/* |x| < 0.4375 */
|
||||
if ix < 0x39800000 {
|
||||
return x;
|
||||
}
|
||||
-1
|
||||
} else {
|
||||
x = super::abs(x);
|
||||
if ix < 0x3f980000 {
|
||||
/* |x| < 1.1875 */
|
||||
if ix < 0x3f300000 {
|
||||
/* 7/16 <= |x| < 11/16 */
|
||||
x = (2. * x - 1.) / (2. + x);
|
||||
0
|
||||
} else {
|
||||
/* 11/16 <= |x| < 19/16 */
|
||||
x = (x - 1.) / (x + 1.);
|
||||
1
|
||||
}
|
||||
} else if ix < 0x401c0000 {
|
||||
/* |x| < 2.4375 */
|
||||
x = (x - 1.5) / (1. + 1.5 * x);
|
||||
2
|
||||
} else {
|
||||
/* 2.4375 <= |x| < 2**26 */
|
||||
x = -1. / x;
|
||||
3
|
||||
}
|
||||
};
|
||||
/* end of argument reduction */
|
||||
z = x * x;
|
||||
let w = z * z;
|
||||
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
|
||||
let s1 = z * (A_T[0] + w * (A_T[2] + w * A_T[4]));
|
||||
let s2 = w * (A_T[1] + w * A_T[3]);
|
||||
if id < 0 {
|
||||
return x - x * (s1 + s2);
|
||||
}
|
||||
let id = id as usize;
|
||||
let z = ATAN_HI[id] - ((x * (s1 + s2) - ATAN_LO[id]) - x);
|
||||
if sign {
|
||||
-z
|
||||
} else {
|
||||
z
|
||||
}
|
||||
}
|
||||
118
third-party/vendor/fontdue/src/platform/float/atan2.rs
vendored
Normal file
118
third-party/vendor/fontdue/src/platform/float/atan2.rs
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c
|
||||
*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/// The arctangent function.
|
||||
pub fn atan2f(y: f32, x: f32) -> f32 {
|
||||
const PI: f32 = 3.1415927410e+00; /* 0x40490fdb */
|
||||
const PI_LO: f32 = -8.7422776573e-08; /* 0xb3bbbd2e */
|
||||
|
||||
if x.is_nan() || y.is_nan() {
|
||||
return x + y;
|
||||
}
|
||||
let mut ix = x.to_bits();
|
||||
let mut iy = y.to_bits();
|
||||
|
||||
if ix == 0x3f800000 {
|
||||
/* x=1.0 */
|
||||
return super::atan(y);
|
||||
}
|
||||
let m = ((iy >> 31) & 1) | ((ix >> 30) & 2); /* 2*sign(x)+sign(y) */
|
||||
ix &= 0x7fffffff;
|
||||
iy &= 0x7fffffff;
|
||||
|
||||
/* when y = 0 */
|
||||
if iy == 0 {
|
||||
return match m {
|
||||
0 | 1 => y, /* atan(+-0,+anything)=+-0 */
|
||||
2 => PI, /* atan(+0,-anything) = pi */
|
||||
3 | _ => -PI, /* atan(-0,-anything) =-pi */
|
||||
};
|
||||
}
|
||||
/* when x = 0 */
|
||||
if ix == 0 {
|
||||
return if m & 1 != 0 {
|
||||
-PI / 2.
|
||||
} else {
|
||||
PI / 2.
|
||||
};
|
||||
}
|
||||
/* when x is INF */
|
||||
if ix == 0x7f800000 {
|
||||
return if iy == 0x7f800000 {
|
||||
match m {
|
||||
0 => PI / 4., /* atan(+INF,+INF) */
|
||||
1 => -PI / 4., /* atan(-INF,+INF) */
|
||||
2 => 3. * PI / 4., /* atan(+INF,-INF)*/
|
||||
3 | _ => -3. * PI / 4., /* atan(-INF,-INF)*/
|
||||
}
|
||||
} else {
|
||||
match m {
|
||||
0 => 0., /* atan(+...,+INF) */
|
||||
1 => -0., /* atan(-...,+INF) */
|
||||
2 => PI, /* atan(+...,-INF) */
|
||||
3 | _ => -PI, /* atan(-...,-INF) */
|
||||
}
|
||||
};
|
||||
}
|
||||
/* |y/x| > 0x1p26 */
|
||||
if (ix + (26 << 23) < iy) || (iy == 0x7f800000) {
|
||||
return if m & 1 != 0 {
|
||||
-PI / 2.
|
||||
} else {
|
||||
PI / 2.
|
||||
};
|
||||
}
|
||||
|
||||
/* z = atan(|y/x|) with correct underflow */
|
||||
let z = if (m & 2 != 0) && (iy + (26 << 23) < ix) {
|
||||
/*|y/x| < 0x1p-26, x < 0 */
|
||||
0.
|
||||
} else {
|
||||
super::atan(super::abs(y / x))
|
||||
};
|
||||
match m {
|
||||
0 => z, /* atan(+,+) */
|
||||
1 => -z, /* atan(-,+) */
|
||||
2 => PI - (z - PI_LO), /* atan(+,-) */
|
||||
_ => (z - PI_LO) - PI, /* case 3 */ /* atan(-,-) */
|
||||
}
|
||||
}
|
||||
|
||||
pub fn atan2(x: f32, y: f32) -> f32 {
|
||||
use core::f32::consts::PI;
|
||||
const PI_2: f32 = PI / 2.0;
|
||||
const M_PI_2: f32 = -PI / 2.0;
|
||||
const PI_4: f32 = PI / 4.0;
|
||||
const PI_3_4: f32 = PI_4 * 3.0;
|
||||
let mut r;
|
||||
let c;
|
||||
let abs_y = super::abs(y);
|
||||
if x == 0.0 {
|
||||
if y > 0.0 {
|
||||
return PI_2;
|
||||
}
|
||||
if y == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
return M_PI_2;
|
||||
} else if x > 0.0 {
|
||||
r = (x - abs_y) / (x + abs_y);
|
||||
c = PI_4;
|
||||
} else {
|
||||
r = (x + abs_y) / (abs_y - x);
|
||||
c = PI_3_4;
|
||||
}
|
||||
r = 0.1963 * r * r * r - 0.9817 * r + c;
|
||||
return super::copysign(r, y);
|
||||
}
|
||||
25
third-party/vendor/fontdue/src/platform/float/ceil.rs
vendored
Normal file
25
third-party/vendor/fontdue/src/platform/float/ceil.rs
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
|
||||
pub fn ceil(x: f32) -> f32 {
|
||||
let mut ui = x.to_bits();
|
||||
let e = (((ui >> 23) & 0xff).wrapping_sub(0x7f)) as i32;
|
||||
if e >= 23 {
|
||||
return x;
|
||||
}
|
||||
if e >= 0 {
|
||||
let m = 0x007fffff >> e;
|
||||
if (ui & m) == 0 {
|
||||
return x;
|
||||
}
|
||||
if ui >> 31 == 0 {
|
||||
ui += m;
|
||||
}
|
||||
ui &= !m;
|
||||
} else {
|
||||
if ui >> 31 != 0 {
|
||||
return -0.0;
|
||||
} else if ui << 1 != 0 {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
f32::from_bits(ui)
|
||||
}
|
||||
26
third-party/vendor/fontdue/src/platform/float/floor.rs
vendored
Normal file
26
third-party/vendor/fontdue/src/platform/float/floor.rs
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
|
||||
pub fn floor(x: f32) -> f32 {
|
||||
let mut ui = x.to_bits();
|
||||
let e = (((ui >> 23) as i32) & 0xff) - 0x7f;
|
||||
|
||||
if e >= 23 {
|
||||
return x;
|
||||
}
|
||||
if e >= 0 {
|
||||
let m: u32 = 0x007fffff >> e;
|
||||
if (ui & m) == 0 {
|
||||
return x;
|
||||
}
|
||||
if ui >> 31 != 0 {
|
||||
ui += m;
|
||||
}
|
||||
ui &= !m;
|
||||
} else {
|
||||
if ui >> 31 == 0 {
|
||||
ui = 0;
|
||||
} else if ui << 1 != 0 {
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
f32::from_bits(ui)
|
||||
}
|
||||
19
third-party/vendor/fontdue/src/platform/float/fract.rs
vendored
Normal file
19
third-party/vendor/fontdue/src/platform/float/fract.rs
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
#[inline(always)]
|
||||
pub fn fract(value: f32) -> f32 {
|
||||
value - super::trunc(value)
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
#[inline(always)]
|
||||
pub fn fract(value: f32) -> f32 {
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
unsafe {
|
||||
let packed = _mm_set_ss(value);
|
||||
_mm_cvtss_f32(_mm_sub_ps(packed, _mm_cvtepi32_ps(_mm_cvttps_epi32(packed))))
|
||||
}
|
||||
}
|
||||
67
third-party/vendor/fontdue/src/platform/float/get_bitmap.rs
vendored
Normal file
67
third-party/vendor/fontdue/src/platform/float/get_bitmap.rs
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use alloc::vec::*;
|
||||
|
||||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
pub fn get_bitmap(a: &Vec<f32>, length: usize) -> Vec<u8> {
|
||||
use crate::platform::{abs, clamp};
|
||||
use alloc::vec;
|
||||
let mut height = 0.0;
|
||||
assert!(length <= a.len());
|
||||
let mut output = vec![0; length];
|
||||
for i in 0..length {
|
||||
unsafe {
|
||||
height += a.get_unchecked(i);
|
||||
// Clamping because as u8 is undefined outside of its range in rustc.
|
||||
*(output.get_unchecked_mut(i)) = clamp(abs(height) * 255.9, 0.0, 255.0) as u8;
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
#[allow(clippy::uninit_vec)]
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
pub fn get_bitmap(a: &Vec<f32>, length: usize) -> Vec<u8> {
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
let aligned_length = (length + 3) & !3;
|
||||
assert!(aligned_length <= a.len());
|
||||
// Turns out zeroing takes a while on very large sizes.
|
||||
let mut output = Vec::with_capacity(aligned_length);
|
||||
unsafe {
|
||||
output.set_len(aligned_length);
|
||||
// offset = Zeroed out lanes
|
||||
let mut offset = _mm_setzero_ps();
|
||||
// Negative zero is important here.
|
||||
let nzero = _mm_castps_si128(_mm_set1_ps(-0.0));
|
||||
for i in (0..aligned_length).step_by(4) {
|
||||
// x = Read 4 floats from self.a
|
||||
let mut x = _mm_loadu_ps(a.get_unchecked(i));
|
||||
// x += (0.0, x[0], x[1], x[2])
|
||||
x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 4)));
|
||||
// x += (0.0, 0.0, x[0], x[1])
|
||||
x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 8)));
|
||||
// x += offset
|
||||
x = _mm_add_ps(x, offset);
|
||||
|
||||
// y = x * 255.9
|
||||
let y = _mm_mul_ps(x, _mm_set1_ps(255.9));
|
||||
// y = abs(y)
|
||||
let y = _mm_andnot_ps(_mm_castsi128_ps(nzero), y);
|
||||
// y = Convert y to i32s and truncate
|
||||
let mut y = _mm_cvttps_epi32(y);
|
||||
// y = Take the first byte of each of the 4 values in y and pack them into
|
||||
// the first 4 bytes of y.
|
||||
y = _mm_packus_epi16(_mm_packs_epi32(y, nzero), nzero);
|
||||
|
||||
// Store the first 4 u8s from y in output.
|
||||
let pointer: &mut i32 = core::mem::transmute(output.get_unchecked_mut(i));
|
||||
*pointer = core::mem::transmute::<__m128i, [i32; 4]>(y)[0];
|
||||
// offset = (x[3], x[3], x[3], x[3])
|
||||
offset = _mm_set1_ps(core::mem::transmute::<__m128, [f32; 4]>(x)[3]);
|
||||
}
|
||||
}
|
||||
output.truncate(length);
|
||||
output
|
||||
}
|
||||
61
third-party/vendor/fontdue/src/platform/float/mod.rs
vendored
Normal file
61
third-party/vendor/fontdue/src/platform/float/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
mod as_i32;
|
||||
mod atan;
|
||||
mod atan2;
|
||||
mod ceil;
|
||||
mod floor;
|
||||
mod fract;
|
||||
mod get_bitmap;
|
||||
mod sqrt;
|
||||
mod trunc;
|
||||
|
||||
pub use as_i32::*;
|
||||
pub use atan::*;
|
||||
pub use atan2::*;
|
||||
pub use ceil::*;
|
||||
pub use floor::*;
|
||||
pub use fract::*;
|
||||
pub use get_bitmap::*;
|
||||
pub use sqrt::*;
|
||||
pub use trunc::*;
|
||||
|
||||
/// Sets the high bit 0x80000000 on a float.
|
||||
#[inline(always)]
|
||||
pub fn abs(value: f32) -> f32 {
|
||||
f32::from_bits(value.to_bits() & 0x7fffffff)
|
||||
}
|
||||
|
||||
/// Checks if the high bit 0x80000000 is set on a float.
|
||||
#[inline(always)]
|
||||
pub fn is_negative(value: f32) -> bool {
|
||||
value.to_bits() >= 0x80000000
|
||||
}
|
||||
|
||||
/// Checks if the high bit 0x80000000 is not set on a float.
|
||||
#[inline(always)]
|
||||
pub fn is_positive(value: f32) -> bool {
|
||||
value.to_bits() < 0x80000000
|
||||
}
|
||||
|
||||
/// Inverts the high bit 0x80000000 on a float.
|
||||
#[inline(always)]
|
||||
pub fn flipsign(value: f32) -> f32 {
|
||||
f32::from_bits(value.to_bits() ^ 0x80000000)
|
||||
}
|
||||
|
||||
/// Assigns the high bit 0x80000000 on the sign to the value.
|
||||
#[inline(always)]
|
||||
pub fn copysign(value: f32, sign: f32) -> f32 {
|
||||
f32::from_bits((value.to_bits() & 0x7fffffff) | (sign.to_bits() & 0x80000000))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn clamp(value: f32, min: f32, max: f32) -> f32 {
|
||||
let mut x = value;
|
||||
if x < min {
|
||||
x = min;
|
||||
}
|
||||
if x > max {
|
||||
x = max;
|
||||
}
|
||||
x
|
||||
}
|
||||
97
third-party/vendor/fontdue/src/platform/float/sqrt.rs
vendored
Normal file
97
third-party/vendor/fontdue/src/platform/float/sqrt.rs
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
|
||||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
pub fn sqrt(x: f32) -> f32 {
|
||||
const TINY: f32 = 1.0e-30;
|
||||
|
||||
let mut z: f32;
|
||||
let sign: i32 = 0x80000000u32 as i32;
|
||||
let mut ix: i32;
|
||||
let mut s: i32;
|
||||
let mut q: i32;
|
||||
let mut m: i32;
|
||||
let mut t: i32;
|
||||
let mut i: i32;
|
||||
let mut r: u32;
|
||||
|
||||
ix = x.to_bits() as i32;
|
||||
|
||||
/* take care of Inf and NaN */
|
||||
if (ix as u32 & 0x7f800000) == 0x7f800000 {
|
||||
return x * x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN */
|
||||
}
|
||||
|
||||
/* take care of zero */
|
||||
if ix <= 0 {
|
||||
if (ix & !sign) == 0 {
|
||||
return x; /* sqrt(+-0) = +-0 */
|
||||
}
|
||||
if ix < 0 {
|
||||
#[allow(clippy::eq_op)] // This has special semantics and is not wrong.
|
||||
return (x - x) / (x - x); /* sqrt(-ve) = sNaN */
|
||||
}
|
||||
}
|
||||
|
||||
/* normalize x */
|
||||
m = ix >> 23;
|
||||
if m == 0 {
|
||||
/* subnormal x */
|
||||
i = 0;
|
||||
while ix & 0x00800000 == 0 {
|
||||
ix <<= 1;
|
||||
i = i + 1;
|
||||
}
|
||||
m -= i - 1;
|
||||
}
|
||||
m -= 127; /* unbias exponent */
|
||||
ix = (ix & 0x007fffff) | 0x00800000;
|
||||
if m & 1 == 1 {
|
||||
/* odd m, double x to make it even */
|
||||
ix += ix;
|
||||
}
|
||||
m >>= 1; /* m = [m/2] */
|
||||
|
||||
/* generate sqrt(x) bit by bit */
|
||||
ix += ix;
|
||||
q = 0;
|
||||
s = 0;
|
||||
r = 0x01000000; /* r = moving bit from right to left */
|
||||
|
||||
while r != 0 {
|
||||
t = s + r as i32;
|
||||
if t <= ix {
|
||||
s = t + r as i32;
|
||||
ix -= t;
|
||||
q += r as i32;
|
||||
}
|
||||
ix += ix;
|
||||
r >>= 1;
|
||||
}
|
||||
|
||||
/* use floating add to find out rounding direction */
|
||||
if ix != 0 {
|
||||
z = 1.0 - TINY; /* raise inexact flag */
|
||||
if z >= 1.0 {
|
||||
z = 1.0 + TINY;
|
||||
if z > 1.0 {
|
||||
q += 2;
|
||||
} else {
|
||||
q += q & 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ix = (q >> 1) + 0x3f000000;
|
||||
ix += m << 23;
|
||||
f32::from_bits(ix as u32)
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
#[inline(always)]
|
||||
pub fn sqrt(value: f32) -> f32 {
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
unsafe { _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(value))) }
|
||||
}
|
||||
30
third-party/vendor/fontdue/src/platform/float/trunc.rs
vendored
Normal file
30
third-party/vendor/fontdue/src/platform/float/trunc.rs
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
|
||||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
pub fn trunc(x: f32) -> f32 {
|
||||
let mut i: u32 = x.to_bits();
|
||||
let mut e: i32 = (i >> 23 & 0xff) as i32 - 0x7f + 9;
|
||||
let m: u32;
|
||||
if e >= 23 + 9 {
|
||||
return x;
|
||||
}
|
||||
if e < 9 {
|
||||
e = 1;
|
||||
}
|
||||
m = -1i32 as u32 >> e;
|
||||
if (i & m) == 0 {
|
||||
return x;
|
||||
}
|
||||
i &= !m;
|
||||
f32::from_bits(i)
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
#[inline(always)]
|
||||
pub fn trunc(value: f32) -> f32 {
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
unsafe { _mm_cvtss_f32(_mm_cvtepi32_ps(_mm_cvttps_epi32(_mm_set_ss(value)))) }
|
||||
}
|
||||
75
third-party/vendor/fontdue/src/platform/mod.rs
vendored
Normal file
75
third-party/vendor/fontdue/src/platform/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
mod simd_core;
|
||||
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
|
||||
pub use simd_core::*;
|
||||
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
mod simd_x86;
|
||||
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
|
||||
pub use simd_x86::*;
|
||||
|
||||
mod float;
|
||||
pub use float::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn platform_ceil_test() {
|
||||
use core::mem::transmute;
|
||||
let mut y = 3.0;
|
||||
while y < 9.0 {
|
||||
assert_eq!(ceil(y), f32::ceil(y));
|
||||
y = unsafe { transmute::<u32, f32>(transmute::<f32, u32>(y) + 1) };
|
||||
}
|
||||
|
||||
assert_eq!(ceil(-1.5), -1.0);
|
||||
assert_eq!(ceil(-1.0), -1.0);
|
||||
assert_eq!(ceil(-0.5), 0.0);
|
||||
assert_eq!(ceil(0.0), 0.0);
|
||||
assert_eq!(ceil(0.5), 1.0);
|
||||
assert_eq!(ceil(1.0), 1.0);
|
||||
assert_eq!(ceil(1.5), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_floor_test() {
|
||||
use core::mem::transmute;
|
||||
let mut y = -3.0;
|
||||
while y > -9.0 {
|
||||
assert_eq!(ceil(y), f32::ceil(y));
|
||||
y = unsafe { transmute::<u32, f32>(transmute::<f32, u32>(y) + 1) };
|
||||
}
|
||||
|
||||
assert_eq!(floor(-1.5), -2.0);
|
||||
assert_eq!(floor(-1.0), -1.0);
|
||||
assert_eq!(floor(-0.5), -1.0);
|
||||
assert_eq!(floor(0.0), 0.0);
|
||||
assert_eq!(floor(0.5), 0.0);
|
||||
assert_eq!(floor(1.0), 1.0);
|
||||
assert_eq!(floor(1.5), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_fract_test() {
|
||||
assert_eq!(fract(-1.5), -0.5);
|
||||
assert_eq!(fract(-1.0), 0.0);
|
||||
assert_eq!(fract(-0.5), -0.5);
|
||||
assert_eq!(fract(0.0), 0.0);
|
||||
assert_eq!(fract(0.5), 0.5);
|
||||
assert_eq!(fract(1.0), 0.0);
|
||||
assert_eq!(fract(1.5), 0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_trunc_test() {
|
||||
assert_eq!(trunc(-1.5), -1.0);
|
||||
assert_eq!(trunc(-1.0), -1.0);
|
||||
assert_eq!(trunc(-0.5), 0.0);
|
||||
assert_eq!(trunc(0.0), 0.0);
|
||||
assert_eq!(trunc(0.5), 0.0);
|
||||
assert_eq!(trunc(1.0), 1.0);
|
||||
assert_eq!(trunc(1.5), 1.0);
|
||||
}
|
||||
}
|
||||
141
third-party/vendor/fontdue/src/platform/simd_core.rs
vendored
Normal file
141
third-party/vendor/fontdue/src/platform/simd_core.rs
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use core::mem::transmute;
|
||||
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct f32x4 {
|
||||
x0: f32,
|
||||
x1: f32,
|
||||
x2: f32,
|
||||
x3: f32,
|
||||
}
|
||||
|
||||
impl f32x4 {
|
||||
#[inline(always)]
|
||||
pub const fn new(x0: f32, x1: f32, x2: f32, x3: f32) -> Self {
|
||||
f32x4 {
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new_u32(x0: u32, x1: u32, x2: u32, x3: u32) -> Self {
|
||||
unsafe { Self::new(transmute(x0), transmute(x1), transmute(x2), transmute(x3)) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sub_integer(&self, other: f32x4) -> f32x4 {
|
||||
unsafe {
|
||||
Self::new(
|
||||
transmute(transmute::<f32, u32>(self.x0) - transmute::<f32, u32>(other.x0)),
|
||||
transmute(transmute::<f32, u32>(self.x1) - transmute::<f32, u32>(other.x1)),
|
||||
transmute(transmute::<f32, u32>(self.x2) - transmute::<f32, u32>(other.x2)),
|
||||
transmute(transmute::<f32, u32>(self.x3) - transmute::<f32, u32>(other.x3)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn splat(value: f32) -> Self {
|
||||
Self::new(value, value, value, value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn zero() -> Self {
|
||||
Self::splat(0.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn copied(self) -> (f32, f32, f32, f32) {
|
||||
(self.x0, self.x1, self.x2, self.x3)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn trunc(self) -> Self {
|
||||
use super::trunc;
|
||||
Self::new(trunc(self.x0), trunc(self.x1), trunc(self.x2), trunc(self.x3))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sqrt(self) -> Self {
|
||||
use super::sqrt;
|
||||
Self::new(sqrt(self.x0), sqrt(self.x1), sqrt(self.x2), sqrt(self.x3))
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn add(self, other: f32x4) -> f32x4 {
|
||||
Self::new(self.x0 + other.x0, self.x1 + other.x1, self.x2 + other.x2, self.x3 + other.x3)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, other: f32x4) {
|
||||
self.x0 += other.x0;
|
||||
self.x1 += other.x1;
|
||||
self.x2 += other.x2;
|
||||
self.x3 += other.x3;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn sub(self, other: f32x4) -> f32x4 {
|
||||
Self::new(self.x0 - other.x0, self.x1 - other.x1, self.x2 - other.x2, self.x3 - other.x3)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, other: f32x4) {
|
||||
self.x0 -= other.x0;
|
||||
self.x1 -= other.x1;
|
||||
self.x2 -= other.x2;
|
||||
self.x3 -= other.x3;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn mul(self, other: f32x4) -> f32x4 {
|
||||
Self::new(self.x0 * other.x0, self.x1 * other.x1, self.x2 * other.x2, self.x3 * other.x3)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn mul_assign(&mut self, other: f32x4) {
|
||||
self.x0 *= other.x0;
|
||||
self.x1 *= other.x1;
|
||||
self.x2 *= other.x2;
|
||||
self.x3 *= other.x3;
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn div(self, other: f32x4) -> f32x4 {
|
||||
Self::new(self.x0 / other.x0, self.x1 / other.x1, self.x2 / other.x2, self.x3 / other.x3)
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn div_assign(&mut self, other: f32x4) {
|
||||
self.x0 /= other.x0;
|
||||
self.x1 /= other.x1;
|
||||
self.x2 /= other.x2;
|
||||
self.x3 /= other.x3;
|
||||
}
|
||||
}
|
||||
120
third-party/vendor/fontdue/src/platform/simd_x86.rs
vendored
Normal file
120
third-party/vendor/fontdue/src/platform/simd_x86.rs
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct f32x4(__m128);
|
||||
|
||||
impl f32x4 {
|
||||
#[inline(always)]
|
||||
pub fn new(x0: f32, x1: f32, x2: f32, x3: f32) -> Self {
|
||||
f32x4(unsafe { _mm_set_ps(x3, x2, x1, x0) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new_u32(x0: u32, x1: u32, x2: u32, x3: u32) -> Self {
|
||||
f32x4(unsafe {
|
||||
_mm_set_ps(
|
||||
core::mem::transmute(x3),
|
||||
core::mem::transmute(x2),
|
||||
core::mem::transmute(x1),
|
||||
core::mem::transmute(x0),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn splat(value: f32) -> Self {
|
||||
f32x4(unsafe { _mm_set1_ps(value) })
|
||||
}
|
||||
|
||||
pub fn sub_integer(&self, other: f32x4) -> f32x4 {
|
||||
f32x4(unsafe { _mm_castsi128_ps(_mm_sub_epi32(_mm_castps_si128(self.0), _mm_castps_si128(other.0))) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn zero() -> Self {
|
||||
f32x4(unsafe { _mm_setzero_ps() })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn copied(self) -> (f32, f32, f32, f32) {
|
||||
unsafe { core::mem::transmute(self.0) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn trunc(self) -> Self {
|
||||
unsafe { f32x4(_mm_cvtepi32_ps(_mm_cvttps_epi32(self.0))) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sqrt(self) -> Self {
|
||||
unsafe { f32x4(_mm_sqrt_ps(self.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn add(self, other: f32x4) -> f32x4 {
|
||||
unsafe { f32x4(_mm_add_ps(self.0, other.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, other: f32x4) {
|
||||
self.0 = unsafe { _mm_add_ps(self.0, other.0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn sub(self, other: f32x4) -> f32x4 {
|
||||
unsafe { f32x4(_mm_sub_ps(self.0, other.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, other: f32x4) {
|
||||
self.0 = unsafe { _mm_sub_ps(self.0, other.0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn mul(self, other: f32x4) -> f32x4 {
|
||||
unsafe { f32x4(_mm_mul_ps(self.0, other.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn mul_assign(&mut self, other: f32x4) {
|
||||
self.0 = unsafe { _mm_mul_ps(self.0, other.0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for f32x4 {
|
||||
type Output = f32x4;
|
||||
#[inline(always)]
|
||||
fn div(self, other: f32x4) -> f32x4 {
|
||||
unsafe { f32x4(_mm_div_ps(self.0, other.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign for f32x4 {
|
||||
#[inline(always)]
|
||||
fn div_assign(&mut self, other: f32x4) {
|
||||
self.0 = unsafe { _mm_div_ps(self.0, other.0) };
|
||||
}
|
||||
}
|
||||
125
third-party/vendor/fontdue/src/raster.rs
vendored
Normal file
125
third-party/vendor/fontdue/src/raster.rs
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* Notice to anyone that wants to repurpose the raster for your library:
|
||||
* Please don't reuse this raster. Fontdue's raster is very unsafe, with nuanced invariants that
|
||||
* need to be accounted for. Fontdue sanitizes the input that the raster will consume to ensure it
|
||||
* is safe. Please be aware of this.
|
||||
*/
|
||||
|
||||
use crate::math::Line;
|
||||
use crate::platform::{abs, as_i32, copysign, f32x4, fract};
|
||||
use crate::Glyph;
|
||||
use alloc::vec;
|
||||
use alloc::vec::*;
|
||||
|
||||
pub struct Raster {
|
||||
w: usize,
|
||||
h: usize,
|
||||
a: Vec<f32>,
|
||||
}
|
||||
|
||||
impl Raster {
|
||||
pub fn new(w: usize, h: usize) -> Raster {
|
||||
Raster {
|
||||
w,
|
||||
h,
|
||||
a: vec![0.0; w * h + 3],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn draw(&mut self, glyph: &Glyph, scale_x: f32, scale_y: f32, offset_x: f32, offset_y: f32) {
|
||||
let params = f32x4::new(1.0 / scale_x, 1.0 / scale_y, scale_x, scale_y);
|
||||
let scale = f32x4::new(scale_x, scale_y, scale_x, scale_y);
|
||||
let offset = f32x4::new(offset_x, offset_y, offset_x, offset_y);
|
||||
for line in &glyph.v_lines {
|
||||
self.v_line(line, line.coords * scale + offset);
|
||||
}
|
||||
for line in &glyph.m_lines {
|
||||
self.m_line(line, line.coords * scale + offset, line.params * params);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn add(&mut self, index: usize, height: f32, mid_x: f32) {
|
||||
// This is fast and hip.
|
||||
unsafe {
|
||||
let m = height * mid_x;
|
||||
*self.a.get_unchecked_mut(index) += height - m;
|
||||
*self.a.get_unchecked_mut(index + 1) += m;
|
||||
}
|
||||
|
||||
// This is safe but slow.
|
||||
// let m = height * mid_x;
|
||||
// self.a[index] += height - m;
|
||||
// self.a[index + 1] += m;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn v_line(&mut self, line: &Line, coords: f32x4) {
|
||||
let (x0, y0, _, y1) = coords.copied();
|
||||
let temp = coords.sub_integer(line.nudge).trunc();
|
||||
let (start_x, start_y, end_x, end_y) = temp.copied();
|
||||
let (_, mut target_y, _, _) = (temp + line.adjustment).copied();
|
||||
let sy = copysign(1f32, y1 - y0);
|
||||
let mut y_prev = y0;
|
||||
let mut index = as_i32(start_x + start_y * self.w as f32);
|
||||
let index_y_inc = as_i32(copysign(self.w as f32, sy));
|
||||
let mut dist = as_i32(abs(start_y - end_y));
|
||||
let mid_x = fract(x0);
|
||||
while dist > 0 {
|
||||
dist -= 1;
|
||||
self.add(index as usize, y_prev - target_y, mid_x);
|
||||
index += index_y_inc;
|
||||
y_prev = target_y;
|
||||
target_y += sy;
|
||||
}
|
||||
self.add(as_i32(end_x + end_y * self.w as f32) as usize, y_prev - y1, mid_x);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn m_line(&mut self, line: &Line, coords: f32x4, params: f32x4) {
|
||||
let (x0, y0, x1, y1) = coords.copied();
|
||||
let temp = coords.sub_integer(line.nudge).trunc();
|
||||
let (start_x, start_y, end_x, end_y) = temp.copied();
|
||||
let (tdx, tdy, dx, dy) = params.copied();
|
||||
let (mut target_x, mut target_y, _, _) = (temp + line.adjustment).copied();
|
||||
let sx = copysign(1f32, tdx);
|
||||
let sy = copysign(1f32, tdy);
|
||||
let mut tmx = tdx * (target_x - x0);
|
||||
let mut tmy = tdy * (target_y - y0);
|
||||
let tdx = abs(tdx);
|
||||
let tdy = abs(tdy);
|
||||
let mut x_prev = x0;
|
||||
let mut y_prev = y0;
|
||||
let mut index = as_i32(start_x + start_y * self.w as f32);
|
||||
let index_x_inc = as_i32(sx);
|
||||
let index_y_inc = as_i32(copysign(self.w as f32, sy));
|
||||
let mut dist = as_i32(abs(start_x - end_x) + abs(start_y - end_y));
|
||||
while dist > 0 {
|
||||
dist -= 1;
|
||||
let prev_index = index;
|
||||
let y_next: f32;
|
||||
let x_next: f32;
|
||||
if tmx < tmy {
|
||||
y_next = tmx * dy + y0; // FMA is not faster.
|
||||
x_next = target_x;
|
||||
tmx += tdx;
|
||||
target_x += sx;
|
||||
index += index_x_inc;
|
||||
} else {
|
||||
y_next = target_y;
|
||||
x_next = tmy * dx + x0;
|
||||
tmy += tdy;
|
||||
target_y += sy;
|
||||
index += index_y_inc;
|
||||
}
|
||||
self.add(prev_index as usize, y_prev - y_next, fract((x_prev + x_next) / 2.0));
|
||||
x_prev = x_next;
|
||||
y_prev = y_next;
|
||||
}
|
||||
self.add(as_i32(end_x + end_y * self.w as f32) as usize, y_prev - y1, fract((x_prev + x1) / 2.0));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_bitmap(&self) -> Vec<u8> {
|
||||
crate::platform::get_bitmap(&self.a, self.w * self.h)
|
||||
}
|
||||
}
|
||||
200
third-party/vendor/fontdue/src/table/kern.rs
vendored
Normal file
200
third-party/vendor/fontdue/src/table/kern.rs
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
use crate::table::parse::*;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
// Apple: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
|
||||
// Microsoft: https://docs.microsoft.com/en-us/typography/opentype/spec/kern
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TableKern {
|
||||
pub horizontal_mappings: HashMap<u32, i16>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Header {
|
||||
pub version_major: u16,
|
||||
pub version_minor: u16,
|
||||
pub number_sub_tables: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct SubTableHeader {
|
||||
pub version: u16,
|
||||
pub length: usize,
|
||||
pub coverage: Coverage,
|
||||
pub format: u8,
|
||||
pub tuple_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Coverage {
|
||||
is_horizontal: bool,
|
||||
}
|
||||
|
||||
impl Coverage {
|
||||
pub const fn aat(cov: u8) -> Coverage {
|
||||
Coverage {
|
||||
is_horizontal: cov & 0x80 != 0x80,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ot(cov: u8) -> Coverage {
|
||||
Coverage {
|
||||
is_horizontal: cov & 0x01 == 0x01,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableKern {
|
||||
pub fn new(kern: &[u8]) -> Option<TableKern> {
|
||||
let mut stream = Stream::new(kern);
|
||||
let version_major = stream.read_u16()?;
|
||||
|
||||
let header;
|
||||
match version_major {
|
||||
0x0000 => header = Self::read_ot_header(&mut stream)?,
|
||||
0x0001 => header = Self::read_aat_header(&mut stream)?,
|
||||
_ => return None, // Font.kern: Unsupported kern table version.
|
||||
}
|
||||
|
||||
for _ in 0..header.number_sub_tables {
|
||||
let sub_table_start = stream.offset();
|
||||
let sub_header = if version_major == 0x0000 {
|
||||
Self::read_ot_subtable(&mut stream)?
|
||||
} else {
|
||||
Self::read_aat_subtable(&mut stream)?
|
||||
};
|
||||
match sub_header.format {
|
||||
// Ordered List of Kerning Pairs
|
||||
0 => {
|
||||
if sub_header.coverage.is_horizontal {
|
||||
let mappings = Self::read_format0(&mut stream)?;
|
||||
return Some(TableKern {
|
||||
horizontal_mappings: mappings,
|
||||
});
|
||||
}
|
||||
}
|
||||
// State Table for Contextual Kerning
|
||||
// 1 => { /* TODO: State Table for Contextual Kerning */ }
|
||||
// Simple n x m Array of Kerning Values
|
||||
// 2 => { /* TODO: Simple n x m Array of Kerning Values */ }
|
||||
// Simple n x m Array of Kerning Indices
|
||||
3 => {
|
||||
if sub_header.coverage.is_horizontal {
|
||||
let mappings = Self::read_format3(&mut stream)?;
|
||||
return Some(TableKern {
|
||||
horizontal_mappings: mappings,
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
stream.seek(sub_table_start + sub_header.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None // Font.kern: No supported sub-table format available.
|
||||
}
|
||||
|
||||
fn read_format0(stream: &mut Stream) -> Option<HashMap<u32, i16>> {
|
||||
let pairs = stream.read_u16()?;
|
||||
stream.skip(6); // searchRange: u16, entrySelector: u16, rangeShift: u16
|
||||
let mut mappings = HashMap::new();
|
||||
for _ in 0..pairs {
|
||||
let left = stream.read_u16()?;
|
||||
let right = stream.read_u16()?;
|
||||
let id = u32::from(left) << 16 | u32::from(right);
|
||||
let value = stream.read_i16()?;
|
||||
mappings.insert(id, value);
|
||||
}
|
||||
Some(mappings)
|
||||
}
|
||||
|
||||
fn read_format3(stream: &mut Stream) -> Option<HashMap<u32, i16>> {
|
||||
let glyph_count = stream.read_u16()?;
|
||||
let kerning_values_count = stream.read_u8()?;
|
||||
let left_hand_classes_count = stream.read_u8()?;
|
||||
let right_hand_classes_count = stream.read_u8()?;
|
||||
stream.skip(1); // flags - reserved
|
||||
let indices_count = u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count);
|
||||
|
||||
let kerning_values = stream.read_i16_slice(usize::from(kerning_values_count))?;
|
||||
let left_hand_classes = stream.read_u8_slice(usize::from(glyph_count))?;
|
||||
let right_hand_classes = stream.read_u8_slice(usize::from(glyph_count))?;
|
||||
let indices = stream.read_u8_slice(usize::from(indices_count))?;
|
||||
|
||||
let mut mappings = HashMap::new();
|
||||
for left in 0..glyph_count {
|
||||
for right in 0..glyph_count {
|
||||
if let Some((id, value)) = {
|
||||
let left_class = left_hand_classes.get(usize::from(left))?;
|
||||
let right_class = right_hand_classes.get(usize::from(right))?;
|
||||
|
||||
if left_class > left_hand_classes_count || right_class > right_hand_classes_count {
|
||||
continue;
|
||||
}
|
||||
|
||||
let index =
|
||||
u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class);
|
||||
let index = indices.get(usize::from(index))?;
|
||||
let id = u32::from(left) << 16 | u32::from(right);
|
||||
let value = kerning_values.get(usize::from(index))?;
|
||||
Some((id, value))
|
||||
} {
|
||||
mappings.insert(id, value);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Some(mappings)
|
||||
}
|
||||
|
||||
fn read_ot_header(stream: &mut Stream) -> Option<Header> {
|
||||
let version_major = 0x0000;
|
||||
let version_minor = 0x0000;
|
||||
let number_sub_tables = stream.read_u16()? as u32;
|
||||
Some(Header {
|
||||
version_major,
|
||||
version_minor,
|
||||
number_sub_tables,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_aat_header(stream: &mut Stream) -> Option<Header> {
|
||||
let version_major = 0x0001;
|
||||
let version_minor = stream.read_u16()?;
|
||||
let number_sub_tables = stream.read_u32()?;
|
||||
Some(Header {
|
||||
version_major,
|
||||
version_minor,
|
||||
number_sub_tables,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_ot_subtable(stream: &mut Stream) -> Option<SubTableHeader> {
|
||||
let version = stream.read_u16()?;
|
||||
let length = stream.read_u16()? as usize;
|
||||
let format = stream.read_u8()?;
|
||||
let coverage = Coverage::ot(stream.read_u8()?);
|
||||
Some(SubTableHeader {
|
||||
version,
|
||||
length,
|
||||
coverage,
|
||||
format,
|
||||
tuple_index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_aat_subtable(stream: &mut Stream) -> Option<SubTableHeader> {
|
||||
let length = stream.read_u32()? as usize;
|
||||
let coverage = Coverage::aat(stream.read_u8()?);
|
||||
let format = stream.read_u8()?;
|
||||
let tuple_index = stream.read_u16()?;
|
||||
Some(SubTableHeader {
|
||||
version: 0x0001,
|
||||
length,
|
||||
coverage,
|
||||
format,
|
||||
tuple_index,
|
||||
})
|
||||
}
|
||||
}
|
||||
4
third-party/vendor/fontdue/src/table/mod.rs
vendored
Normal file
4
third-party/vendor/fontdue/src/table/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
mod kern;
|
||||
pub mod parse;
|
||||
|
||||
pub use self::kern::*;
|
||||
196
third-party/vendor/fontdue/src/table/parse.rs
vendored
Normal file
196
third-party/vendor/fontdue/src/table/parse.rs
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
use core::convert::TryInto;
|
||||
|
||||
pub struct StreamSliceU8<'a>(&'a [u8]);
|
||||
pub struct StreamSliceU16<'a>(&'a [u8]);
|
||||
pub struct StreamSliceU32<'a>(&'a [u8]);
|
||||
impl<'a> StreamSliceU8<'a> {
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<u8> {
|
||||
const SIZE: usize = 1;
|
||||
let offset = index * SIZE;
|
||||
let slice = self.0.get(offset..offset + SIZE)?;
|
||||
Some(slice[0])
|
||||
}
|
||||
}
|
||||
impl<'a> StreamSliceU16<'a> {
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<u16> {
|
||||
const SIZE: usize = 2;
|
||||
let offset = index * SIZE;
|
||||
let slice = self.0.get(offset..offset + SIZE)?;
|
||||
Some(u16::from_be_bytes(slice.try_into().unwrap()))
|
||||
}
|
||||
}
|
||||
impl<'a> StreamSliceU32<'a> {
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<u32> {
|
||||
const SIZE: usize = 4;
|
||||
let offset = index * SIZE;
|
||||
let slice = self.0.get(offset..offset + SIZE)?;
|
||||
Some(u32::from_be_bytes(slice.try_into().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StreamSliceI8<'a>(StreamSliceU8<'a>);
|
||||
pub struct StreamSliceI16<'a>(StreamSliceU16<'a>);
|
||||
pub struct StreamSliceI32<'a>(StreamSliceU32<'a>);
|
||||
impl<'a> StreamSliceI8<'a> {
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<i8> {
|
||||
Some(unsafe { core::mem::transmute(self.0.get(index)?) })
|
||||
}
|
||||
}
|
||||
impl<'a> StreamSliceI16<'a> {
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<i16> {
|
||||
Some(unsafe { core::mem::transmute(self.0.get(index)?) })
|
||||
}
|
||||
}
|
||||
impl<'a> StreamSliceI32<'a> {
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<i32> {
|
||||
Some(unsafe { core::mem::transmute(self.0.get(index)?) })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Stream<'a> {
|
||||
bytes: &'a [u8],
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Stream<'a> {
|
||||
pub const fn new(bytes: &'a [u8]) -> Stream<'a> {
|
||||
Stream {
|
||||
bytes,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// UTILITY
|
||||
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
self.offset = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn seek(&mut self, offset: usize) {
|
||||
self.offset = offset;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn skip(&mut self, offset: usize) {
|
||||
self.offset += offset;
|
||||
}
|
||||
|
||||
// UNSIGNED SLICE
|
||||
|
||||
#[inline]
|
||||
pub fn read_u8_slice(&mut self, len: usize) -> Option<StreamSliceU8<'a>> {
|
||||
let end = self.offset + len;
|
||||
self.bytes.get(self.offset..end).map(|slice| {
|
||||
self.offset = end;
|
||||
StreamSliceU8(slice)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u16_slice(&mut self, len: usize) -> Option<StreamSliceU16<'a>> {
|
||||
let end = self.offset + len * 2;
|
||||
self.bytes.get(self.offset..end).map(|slice| {
|
||||
self.offset = end;
|
||||
StreamSliceU16(slice)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u32_slice(&mut self, len: usize) -> Option<StreamSliceU32<'a>> {
|
||||
let end = self.offset + len * 4;
|
||||
self.bytes.get(self.offset..end).map(|slice| {
|
||||
self.offset = end;
|
||||
StreamSliceU32(slice)
|
||||
})
|
||||
}
|
||||
|
||||
// SIGNED SLICE
|
||||
|
||||
#[inline]
|
||||
pub fn read_i8_slice(&mut self, len: usize) -> Option<StreamSliceI8<'a>> {
|
||||
Some(StreamSliceI8(self.read_u8_slice(len)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_i16_slice(&mut self, len: usize) -> Option<StreamSliceI16<'a>> {
|
||||
Some(StreamSliceI16(self.read_u16_slice(len)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_i32_slice(&mut self, len: usize) -> Option<StreamSliceI32<'a>> {
|
||||
Some(StreamSliceI32(self.read_u32_slice(len)?))
|
||||
}
|
||||
|
||||
// UNSIGNED
|
||||
|
||||
#[inline]
|
||||
pub fn read_u8(&mut self) -> Option<u8> {
|
||||
const SIZE: usize = 1;
|
||||
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
|
||||
self.offset += SIZE;
|
||||
Some(slice[0])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u16(&mut self) -> Option<u16> {
|
||||
const SIZE: usize = 2;
|
||||
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
|
||||
self.offset += SIZE;
|
||||
Some(u16::from_be_bytes(slice.try_into().unwrap()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u32(&mut self) -> Option<u32> {
|
||||
const SIZE: usize = 4;
|
||||
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
|
||||
self.offset += SIZE;
|
||||
Some(u32::from_be_bytes(slice.try_into().unwrap()))
|
||||
}
|
||||
|
||||
// SIGNED
|
||||
|
||||
#[inline]
|
||||
pub fn read_i8(&mut self) -> Option<i8> {
|
||||
Some(unsafe { core::mem::transmute(self.read_u8()?) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_i16(&mut self) -> Option<i16> {
|
||||
Some(unsafe { core::mem::transmute(self.read_u16()?) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_i32(&mut self) -> Option<i32> {
|
||||
Some(unsafe { core::mem::transmute(self.read_u32()?) })
|
||||
}
|
||||
|
||||
// FONT
|
||||
|
||||
#[inline]
|
||||
pub fn read_f2dot14(&mut self) -> Option<f32> {
|
||||
let val = self.read_i16()?;
|
||||
let result = val as f32 * (1.0 / (1 << 14) as f32);
|
||||
Some(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_tag(&mut self) -> Option<[u8; 4]> {
|
||||
const SIZE: usize = 4;
|
||||
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
|
||||
self.offset += SIZE;
|
||||
Some(slice.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
202
third-party/vendor/fontdue/src/unicode/mod.rs
vendored
Normal file
202
third-party/vendor/fontdue/src/unicode/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
mod tables;
|
||||
|
||||
use crate::unicode::tables::*;
|
||||
use alloc::string::String;
|
||||
|
||||
const CONT_MASK: u8 = 0b0011_1111;
|
||||
|
||||
#[inline(always)]
|
||||
fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {
|
||||
(ch << 6) | (byte & CONT_MASK) as u32
|
||||
}
|
||||
|
||||
pub fn decode_utf16(bytes: &[u8]) -> String {
|
||||
let mut output = String::new();
|
||||
let mut offset = 0;
|
||||
while offset < bytes.len() {
|
||||
output.push(read_utf16(bytes, &mut offset));
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
pub fn read_utf16(bytes: &[u8], offset: &mut usize) -> char {
|
||||
let a = ((bytes[*offset] as u16) << 8) | bytes[*offset + 1] as u16;
|
||||
*offset += 2;
|
||||
if a < 0xD800 || 0xDFFF < a {
|
||||
unsafe { core::char::from_u32_unchecked(a as u32) }
|
||||
} else {
|
||||
let b = ((bytes[*offset] as u16) << 8) | bytes[*offset + 1] as u16;
|
||||
*offset += 2;
|
||||
let c = (((a - 0xD800) as u32) << 10 | (b - 0xDC00) as u32) + 0x1_0000;
|
||||
unsafe { core::char::from_u32_unchecked(c as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns (length, character). Cannot be run at the end of the string.
|
||||
pub fn read_utf8(bytes: &[u8], byte_offset: &mut usize) -> char {
|
||||
let x = bytes[*byte_offset];
|
||||
*byte_offset += 1;
|
||||
if x < 128 {
|
||||
return unsafe { core::char::from_u32_unchecked(x as u32) };
|
||||
}
|
||||
let init = (x & (0x7F >> 2)) as u32;
|
||||
let y = bytes[*byte_offset];
|
||||
*byte_offset += 1;
|
||||
let mut ch = utf8_acc_cont_byte(init, y);
|
||||
if x >= 0xE0 {
|
||||
let z = bytes[*byte_offset];
|
||||
*byte_offset += 1;
|
||||
let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z);
|
||||
ch = init << 12 | y_z;
|
||||
if x >= 0xF0 {
|
||||
let w = bytes[*byte_offset];
|
||||
*byte_offset += 1;
|
||||
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
|
||||
}
|
||||
}
|
||||
unsafe { core::char::from_u32_unchecked(ch) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
/// Ordering is based on linebreak priority. Ordering is Hard > Soft > None.
|
||||
pub struct LinebreakData {
|
||||
bits: u8,
|
||||
}
|
||||
|
||||
pub const LINEBREAK_NONE: LinebreakData = LinebreakData::new(0b0000_0000);
|
||||
pub const LINEBREAK_SOFT: LinebreakData = LinebreakData::new(0b0000_0001);
|
||||
pub const LINEBREAK_HARD: LinebreakData = LinebreakData::new(0b0000_0010);
|
||||
|
||||
impl LinebreakData {
|
||||
const NONE: u8 = 0b0000_0000;
|
||||
const SOFT: u8 = 0b0000_0001;
|
||||
const HARD: u8 = 0b0000_0010;
|
||||
|
||||
const fn new(bits: u8) -> LinebreakData {
|
||||
LinebreakData {
|
||||
bits,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_mask(wrap_soft_breaks: bool, wrap_hard_breaks: bool, has_width: bool) -> LinebreakData {
|
||||
let mut mask = 0;
|
||||
if wrap_hard_breaks {
|
||||
mask |= LinebreakData::HARD;
|
||||
}
|
||||
if wrap_soft_breaks && has_width {
|
||||
mask |= LinebreakData::SOFT;
|
||||
}
|
||||
LinebreakData {
|
||||
bits: mask,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_hard(&self) -> bool {
|
||||
self.bits == LinebreakData::HARD
|
||||
}
|
||||
|
||||
pub fn is_soft(&self) -> bool {
|
||||
self.bits == LinebreakData::SOFT
|
||||
}
|
||||
|
||||
pub fn mask(&self, other: LinebreakData) -> LinebreakData {
|
||||
Self::new(self.bits & other.bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Linebreaker {
|
||||
state: u8,
|
||||
}
|
||||
|
||||
impl Linebreaker {
|
||||
pub fn new() -> Linebreaker {
|
||||
Linebreaker {
|
||||
state: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.state = 0;
|
||||
}
|
||||
|
||||
// [See license/xi-editor/xi-unicode] Copyright 2016 The xi-editor Authors
|
||||
pub fn next(&mut self, codepoint: char) -> LinebreakData {
|
||||
let cp = codepoint as usize;
|
||||
let lb = if cp < 0x800 {
|
||||
LINEBREAK_1_2[cp]
|
||||
} else if cp < 0x10000 {
|
||||
let child = LINEBREAK_3_ROOT[cp >> 6];
|
||||
LINEBREAK_3_CHILD[(child as usize) * 0x40 + (cp & 0x3f)]
|
||||
} else {
|
||||
let mid = LINEBREAK_4_ROOT[cp >> 12];
|
||||
let leaf = LINEBREAK_4_MID[(mid as usize) * 0x40 + ((cp >> 6) & 0x3f)];
|
||||
LINEBREAK_4_LEAVES[(leaf as usize) * 0x40 + (cp & 0x3f)]
|
||||
};
|
||||
let i = (self.state as usize) * N_LINEBREAK_CATEGORIES + (lb as usize);
|
||||
let new = LINEBREAK_STATE_MACHINE[i];
|
||||
if (new as i8) < 0 {
|
||||
self.state = new & 0x3f;
|
||||
if new >= 0xc0 {
|
||||
LINEBREAK_HARD
|
||||
} else {
|
||||
LINEBREAK_SOFT
|
||||
}
|
||||
} else {
|
||||
self.state = new;
|
||||
LINEBREAK_NONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Miscellaneous metadata associated with a character to assist in layout.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct CharacterData {
|
||||
bits: u8,
|
||||
}
|
||||
|
||||
impl CharacterData {
|
||||
const WHITESPACE: u8 = 0b0000_0001;
|
||||
const CONTROL: u8 = 0b0000_0010;
|
||||
const MISSING: u8 = 0b0000_0100;
|
||||
|
||||
/// Classifies a character given its index in the font.
|
||||
pub fn classify(c: char, index: u16) -> CharacterData {
|
||||
let mut class = 0;
|
||||
if index == 0 {
|
||||
class |= CharacterData::MISSING;
|
||||
}
|
||||
match c {
|
||||
'\t' | '\n' | '\x0C' | '\r' | ' ' => class |= CharacterData::WHITESPACE,
|
||||
_ => {}
|
||||
}
|
||||
match c {
|
||||
'\0'..='\x1F' | '\x7F' => class |= CharacterData::CONTROL,
|
||||
_ => {}
|
||||
}
|
||||
CharacterData {
|
||||
bits: class,
|
||||
}
|
||||
}
|
||||
|
||||
/// A heuristic for if the glpyh this was classified from should be rasterized. Missing glyphs,
|
||||
/// whitespace, and control characters will return false.
|
||||
pub fn rasterize(&self) -> bool {
|
||||
self.bits == 0
|
||||
}
|
||||
|
||||
/// Marks if the character is an ASCII whitespace character.
|
||||
pub fn is_whitespace(&self) -> bool {
|
||||
self.bits & CharacterData::WHITESPACE != 0
|
||||
}
|
||||
|
||||
/// Marks if the character is an ASCII control character.
|
||||
pub fn is_control(&self) -> bool {
|
||||
self.bits & CharacterData::CONTROL != 0
|
||||
}
|
||||
|
||||
/// Marks if the character is missing from its associated font.
|
||||
pub fn is_missing(&self) -> bool {
|
||||
self.bits & CharacterData::MISSING != 0
|
||||
}
|
||||
}
|
||||
1520
third-party/vendor/fontdue/src/unicode/tables.rs
vendored
Normal file
1520
third-party/vendor/fontdue/src/unicode/tables.rs
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue