Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View 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
View 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
View 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"

View 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
View 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
View 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
View file

@ -0,0 +1,110 @@
# Fontdue
[![Test](https://github.com/mooman219/fontdue/workflows/Test/badge.svg)](https://github.com/mooman219/fontdue/actions)
[![Documentation](https://docs.rs/fontdue/badge.svg)](https://docs.rs/fontdue)
[![Crates.io](https://img.shields.io/crates/v/fontdue.svg)](https://crates.io/crates/fontdue)
[![License](https://img.shields.io/crates/l/fontdue.svg)](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.
![Rasterize benchmarks](/images/rasterize_glyf.png)
![Rasterize benchmarks](/images/rasterize_cff.png)
### Layout
This benchmark measures the time it takes to layout latin characters of sample text with wrapping on word boundaries.
![Layout benchmarks](/images/layout.png)
## 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View 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.

View 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.

View file

@ -0,0 +1,2 @@
max_width = 110
use_small_heuristics = "Off"

620
third-party/vendor/fontdue/src/font.rs vendored Normal file
View 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
View 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
View 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
View 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
View 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;
}
}
}

View 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)) }
}

View 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
}
}

View 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);
}

View 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)
}

View 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)
}

View 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))))
}
}

View 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
}

View 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
}

View 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))) }
}

View 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)))) }
}

View 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);
}
}

View 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;
}
}

View 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
View 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)
}
}

View 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,
})
}
}

View file

@ -0,0 +1,4 @@
mod kern;
pub mod parse;
pub use self::kern::*;

View 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())
}
}

View 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
}
}

File diff suppressed because one or more lines are too long