forked from firka/flutter
Subtree import txt into //third_party.
This commit is contained in:
8
engine/src/flutter/third_party/txt/.clang-format
vendored
Normal file
8
engine/src/flutter/third_party/txt/.clang-format
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Defines the Chromium style for automatic reformatting.
|
||||
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
BasedOnStyle: Chromium
|
||||
# This defaults to 'Auto'. Explicitly set it for a while, so that
|
||||
# 'vector<vector<int> >' in existing files gets formatted to
|
||||
# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
|
||||
# 'int>>' if the file already contains at least one such instance.)
|
||||
Standard: Cpp11
|
||||
10
engine/src/flutter/third_party/txt/.gitattributes
vendored
Normal file
10
engine/src/flutter/third_party/txt/.gitattributes
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Always perform LF normalization on these files
|
||||
*.c text
|
||||
*.cc text
|
||||
*.cpp text
|
||||
*.h text
|
||||
*.gn text
|
||||
*.md text
|
||||
17
engine/src/flutter/third_party/txt/.gitignore
vendored
Normal file
17
engine/src/flutter/third_party/txt/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
*.pyc
|
||||
*~
|
||||
.*.sw?
|
||||
.DS_Store
|
||||
.classpath
|
||||
.cproject
|
||||
.gdb_history
|
||||
.gdbinit
|
||||
.landmines
|
||||
.project
|
||||
.pydevproject
|
||||
.checkstyle
|
||||
.vscode
|
||||
cscope.*
|
||||
Session.vim
|
||||
tags
|
||||
Thumbs.db
|
||||
11
engine/src/flutter/third_party/txt/Android.bp
vendored
Normal file
11
engine/src/flutter/third_party/txt/Android.bp
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
cc_library_headers {
|
||||
name: "libminikin_headers",
|
||||
host_supported: true,
|
||||
export_include_dirs: ["include"],
|
||||
}
|
||||
|
||||
subdirs = [
|
||||
"app",
|
||||
"libs/minikin",
|
||||
"tests",
|
||||
]
|
||||
62
engine/src/flutter/third_party/txt/BUILD.gn
vendored
Normal file
62
engine/src/flutter/third_party/txt/BUILD.gn
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2017 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
config("txt_config") {
|
||||
include_dirs = [
|
||||
"//lib/txt/include",
|
||||
"//third_party/harfbuzz/src",
|
||||
"//lib/txt/shims",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("txt") {
|
||||
if (current_toolchain == host_toolchain && !is_mac) {
|
||||
defines = [ "DIRECTORY_FONT_MANAGER_AVAILABLE" ]
|
||||
}
|
||||
if (is_android) {
|
||||
defines = [ "ANDROID_FONT_MANAGER_AVAILABLE" ]
|
||||
}
|
||||
|
||||
sources = [
|
||||
"src/font_collection.cc",
|
||||
"src/font_collection.h",
|
||||
"src/font_skia.cc",
|
||||
"src/font_skia.h",
|
||||
"src/font_style.h",
|
||||
"src/font_weight.h",
|
||||
"src/paint_record.cc",
|
||||
"src/paint_record.h",
|
||||
"src/paragraph.cc",
|
||||
"src/paragraph.h",
|
||||
"src/paragraph_builder.cc",
|
||||
"src/paragraph_builder.h",
|
||||
"src/paragraph_style.cc",
|
||||
"src/paragraph_style.h",
|
||||
"src/styled_runs.cc",
|
||||
"src/styled_runs.h",
|
||||
"src/text_align.h",
|
||||
"src/text_baseline.h",
|
||||
"src/text_decoration.cc",
|
||||
"src/text_decoration.h",
|
||||
"src/text_style.cc",
|
||||
"src/text_style.h",
|
||||
]
|
||||
|
||||
public_configs = [ ":txt_config" ]
|
||||
|
||||
deps = [
|
||||
"//lib/txt/libs/minikin",
|
||||
"//third_party/skia",
|
||||
]
|
||||
}
|
||||
202
engine/src/flutter/third_party/txt/LICENSE
vendored
Normal file
202
engine/src/flutter/third_party/txt/LICENSE
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
22
engine/src/flutter/third_party/txt/PATENTS
vendored
Normal file
22
engine/src/flutter/third_party/txt/PATENTS
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Fuchsia project.
|
||||
|
||||
Google 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, transfer, and otherwise run, modify and propagate the contents
|
||||
of this implementation of Fuchsia, where such license applies only to
|
||||
those patent claims, both currently owned by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by
|
||||
this implementation. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute
|
||||
or order or agree to the institution of patent litigation or any other
|
||||
patent enforcement activity against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that this
|
||||
implementation of Fuchsia constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of
|
||||
Fuchsia shall terminate as of the date such litigation is filed.
|
||||
30
engine/src/flutter/third_party/txt/app/Android.bp
vendored
Normal file
30
engine/src/flutter/third_party/txt/app/Android.bp
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2015 The Android Open Source Project
|
||||
//
|
||||
// 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.
|
||||
|
||||
// see how_to_run.txt for instructions on running these tests
|
||||
|
||||
cc_binary_host {
|
||||
name: "hyphtool",
|
||||
|
||||
static_libs: ["libminikin"],
|
||||
|
||||
// Shared libraries which are dependencies of minikin; these are not automatically
|
||||
// pulled in by the build system (and thus sadly must be repeated).
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libicuuc",
|
||||
],
|
||||
|
||||
srcs: ["HyphTool.cpp"],
|
||||
}
|
||||
64
engine/src/flutter/third_party/txt/app/HyphTool.cpp
vendored
Normal file
64
engine/src/flutter/third_party/txt/app/HyphTool.cpp
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unicode/locid.h"
|
||||
|
||||
#include <vector>
|
||||
#include <minikin/Hyphenator.h>
|
||||
|
||||
using minikin::HyphenationType;
|
||||
using minikin::Hyphenator;
|
||||
|
||||
Hyphenator* loadHybFile(const char* fn, int minPrefix, int minSuffix) {
|
||||
struct stat statbuf;
|
||||
int status = stat(fn, &statbuf);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "error opening %s\n", fn);
|
||||
return nullptr;
|
||||
}
|
||||
size_t size = statbuf.st_size;
|
||||
FILE* f = fopen(fn, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "error opening %s\n", fn);
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* buf = new uint8_t[size];
|
||||
size_t read_size = fread(buf, 1, size, f);
|
||||
fclose(f);
|
||||
if (read_size < size) {
|
||||
fprintf(stderr, "error reading %s\n", fn);
|
||||
delete[] buf;
|
||||
return nullptr;
|
||||
}
|
||||
return Hyphenator::loadBinary(buf, minPrefix, minSuffix);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Hyphenator* hyph = loadHybFile("/tmp/en.hyb", 2, 3); // should also be configurable
|
||||
std::vector<HyphenationType> result;
|
||||
std::vector<uint16_t> word;
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: hyphtool word\n");
|
||||
return 1;
|
||||
}
|
||||
char* asciiword = argv[1];
|
||||
size_t len = strlen(asciiword);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint32_t c = asciiword[i];
|
||||
if (c == '-') {
|
||||
c = 0x00AD;
|
||||
}
|
||||
// ASCII (or possibly ISO Latin 1), but kinda painful to do utf conversion :(
|
||||
word.push_back(c);
|
||||
}
|
||||
hyph->hyphenate(&result, word.data(), word.size(), icu::Locale::getUS());
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (result[i] != HyphenationType::DONT_BREAK) {
|
||||
printf("-");
|
||||
}
|
||||
printf("%c", word[i]);
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
47
engine/src/flutter/third_party/txt/benchmarks/BUILD.gn
vendored
Normal file
47
engine/src/flutter/third_party/txt/benchmarks/BUILD.gn
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# Copyright 2017 Google, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
executable("benchmarks") {
|
||||
output_name = "txt_benchmarks"
|
||||
testonly = true
|
||||
|
||||
include_dirs = [
|
||||
"//lib/txt/src",
|
||||
"//lib/txt/include",
|
||||
"//third_party/icu/source",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"font_collection_benchmarks.cc",
|
||||
"paint_record_benchmarks.cc",
|
||||
"paragraph_benchmarks.cc",
|
||||
"paragraph_builder_benchmarks.cc",
|
||||
"styled_runs_benchmarks.cc",
|
||||
"txt_run_all_benchmarks.cc",
|
||||
"utils.cc",
|
||||
"utils.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//dart/runtime:libdart_jit", # Logging
|
||||
"//flutter/fml",
|
||||
"//lib/txt/shims",
|
||||
"//lib/txt",
|
||||
"//third_party/benchmark",
|
||||
"//third_party/skia",
|
||||
"//third_party/harfbuzz",
|
||||
"//lib/txt/libs/minikin",
|
||||
]
|
||||
|
||||
}
|
||||
68
engine/src/flutter/third_party/txt/benchmarks/font_collection_benchmarks.cc
vendored
Normal file
68
engine/src/flutter/third_party/txt/benchmarks/font_collection_benchmarks.cc
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "third_party/benchmark/include/benchmark/benchmark_api.h"
|
||||
|
||||
#include "lib/ftl/command_line.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
#include "third_party/skia/include/ports/SkFontMgr.h"
|
||||
#include "third_party/skia/include/ports/SkFontMgr_directory.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
// Include this fake bench first because the first benchmark produces
|
||||
// inconsistent times.
|
||||
static void BM_FAKE_BENCHMARK(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FAKE_BENCHMARK);
|
||||
|
||||
static void BM_FontCollectionCustomInit(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
FontCollection::GetFontCollection(txt::GetFontDir()));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontCollectionCustomInit);
|
||||
|
||||
static void BM_FontCollectionInit(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(FontCollection::GetFontCollection());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontCollectionInit);
|
||||
|
||||
static void BM_FontCollectionSkFontMgr(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
auto mgr = SkFontMgr_New_Custom_Directory(txt::GetFontDir().c_str());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontCollectionSkFontMgr);
|
||||
|
||||
static void BM_FontCollectionGetMinikinFontCollectionForFamily(
|
||||
benchmark::State& state) {
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
while (state.KeepRunning()) {
|
||||
font_collection.GetMinikinFontCollectionForFamily("Roboto");
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontCollectionGetMinikinFontCollectionForFamily);
|
||||
|
||||
} // namespace txt
|
||||
46
engine/src/flutter/third_party/txt/benchmarks/paint_record_benchmarks.cc
vendored
Normal file
46
engine/src/flutter/third_party/txt/benchmarks/paint_record_benchmarks.cc
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "third_party/benchmark/include/benchmark/benchmark_api.h"
|
||||
|
||||
#include "lib/ftl/command_line.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/src/paint_record.h"
|
||||
#include "lib/txt/src/text_style.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
static void BM_PaintRecordInit(benchmark::State& state) {
|
||||
TextStyle style;
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
paint.setTextSize(14);
|
||||
paint.setFakeBoldText(false);
|
||||
|
||||
SkTextBlobBuilder builder;
|
||||
builder.allocRunPos(paint, 100);
|
||||
auto text_blob = builder.make();
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
PaintRecord PaintRecord(style, text_blob, SkPaint::FontMetrics(), 0, 0);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_PaintRecordInit);
|
||||
|
||||
} // namespace txt
|
||||
442
engine/src/flutter/third_party/txt/benchmarks/paragraph_benchmarks.cc
vendored
Normal file
442
engine/src/flutter/third_party/txt/benchmarks/paragraph_benchmarks.cc
vendored
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "third_party/benchmark/include/benchmark/benchmark_api.h"
|
||||
|
||||
#include <minikin/Layout.h>
|
||||
#include "lib/ftl/command_line.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/libs/minikin/LayoutUtils.h"
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
#include "lib/txt/src/font_skia.h"
|
||||
#include "lib/txt/src/font_style.h"
|
||||
#include "lib/txt/src/font_weight.h"
|
||||
#include "lib/txt/src/paragraph.h"
|
||||
#include "lib/txt/src/paragraph_builder.h"
|
||||
#include "lib/txt/src/text_align.h"
|
||||
#include "third_party/icu/source/common/unicode/unistr.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkCanvas.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
static void BM_ParagraphShortLayout(benchmark::State& state) {
|
||||
const char* text = "Hello World";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->SetDirty();
|
||||
paragraph->Layout(300, true);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphShortLayout);
|
||||
|
||||
static void BM_ParagraphLongLayout(benchmark::State& state) {
|
||||
const char* text =
|
||||
"This is a very long sentence to test if the text will properly wrap "
|
||||
"around and go to the next line. Sometimes, short sentence. Longer "
|
||||
"sentences are okay too because they are necessary. Very short. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->SetDirty();
|
||||
paragraph->Layout(300, true);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphLongLayout);
|
||||
|
||||
static void BM_ParagraphJustifyLayout(benchmark::State& state) {
|
||||
const char* text =
|
||||
"This is a very long sentence to test if the text will properly wrap "
|
||||
"around and go to the next line. Sometimes, short sentence. Longer "
|
||||
"sentences are okay too because they are necessary. Very short. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
paragraph_style.text_align = TextAlign::justify;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->SetDirty();
|
||||
paragraph->Layout(300, true);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphJustifyLayout);
|
||||
|
||||
static void BM_ParagraphManyStylesLayout(benchmark::State& state) {
|
||||
const char* text = "-";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
}
|
||||
auto paragraph = builder.Build();
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->SetDirty();
|
||||
paragraph->Layout(300, true);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphManyStylesLayout);
|
||||
|
||||
static void BM_ParagraphTextBigO(benchmark::State& state) {
|
||||
std::vector<uint16_t> text;
|
||||
for (uint16_t i = 0; i < state.range(0); ++i) {
|
||||
text.push_back(i % 5 == 0 ? ' ' : i);
|
||||
}
|
||||
std::u16string u16_text(text.data(), text.data() + text.size());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->SetDirty();
|
||||
paragraph->Layout(300, true);
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_ParagraphTextBigO)
|
||||
->RangeMultiplier(4)
|
||||
->Range(1 << 6, 1 << 14)
|
||||
->Complexity(benchmark::oN);
|
||||
|
||||
static void BM_ParagraphStylesBigO(benchmark::State& state) {
|
||||
const char* text = "vry shrt ";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
|
||||
for (int i = 0; i < state.range(0); ++i) {
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
}
|
||||
auto paragraph = builder.Build();
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->SetDirty();
|
||||
paragraph->Layout(300, true);
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_ParagraphStylesBigO)
|
||||
->RangeMultiplier(4)
|
||||
->Range(1 << 3, 1 << 12)
|
||||
->Complexity(benchmark::oN);
|
||||
|
||||
static void BM_ParagraphPaintSimple(benchmark::State& state) {
|
||||
const char* text = "Hello world! This is a simple sentence to test drawing.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->Layout(300, true);
|
||||
|
||||
std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();
|
||||
std::unique_ptr<SkCanvas> canvas = std::make_unique<SkCanvas>(*bitmap);
|
||||
bitmap->allocN32Pixels(1000, 1000);
|
||||
canvas->clear(SK_ColorWHITE);
|
||||
int offset = 0;
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->Paint(canvas.get(), offset % 700, 10);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphPaintSimple);
|
||||
|
||||
static void BM_ParagraphPaintLarge(benchmark::State& state) {
|
||||
const char* text =
|
||||
"Hello world! This is a simple sentence to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing. Hello world! This is a "
|
||||
"simple sentence to test drawing.Hello world! This is a simple sentence "
|
||||
"to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing. Hello world! This is a "
|
||||
"simple sentence to test drawing.Hello world! This is a simple sentence "
|
||||
"to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing. Hello world! This is a "
|
||||
"simple sentence to test drawing.Hello world! This is a simple sentence "
|
||||
"to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing. Hello world! This is a "
|
||||
"simple sentence to test drawing.Hello world! This is a simple sentence "
|
||||
"to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing. Hello world! This is a "
|
||||
"simple sentence to test drawing.Hello world! This is a simple sentence "
|
||||
"to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing. Hello world! This is a "
|
||||
"simple sentence to test drawing.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->Layout(300, true);
|
||||
|
||||
std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();
|
||||
std::unique_ptr<SkCanvas> canvas = std::make_unique<SkCanvas>(*bitmap);
|
||||
bitmap->allocN32Pixels(1000, 1000);
|
||||
canvas->clear(SK_ColorWHITE);
|
||||
int offset = 0;
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->Paint(canvas.get(), offset % 700, 10);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphPaintLarge);
|
||||
|
||||
static void BM_ParagraphPaintDecoration(benchmark::State& state) {
|
||||
const char* text =
|
||||
"Hello world! This is a simple sentence to test drawing. Hello world! "
|
||||
"This is a simple sentence to test drawing.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.decoration = TextDecoration(0x1 | 0x2 | 0x4);
|
||||
text_style.decoration_style = TextDecorationStyle(kSolid);
|
||||
text_style.color = SK_ColorBLACK;
|
||||
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
|
||||
text_style.decoration_style = TextDecorationStyle(kDotted);
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
|
||||
text_style.decoration_style = TextDecorationStyle(kWavy);
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->Layout(300, true);
|
||||
|
||||
std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();
|
||||
std::unique_ptr<SkCanvas> canvas = std::make_unique<SkCanvas>(*bitmap);
|
||||
bitmap->allocN32Pixels(1000, 1000);
|
||||
canvas->clear(SK_ColorWHITE);
|
||||
int offset = 0;
|
||||
while (state.KeepRunning()) {
|
||||
paragraph->Paint(canvas.get(), offset % 700, 10);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphPaintDecoration);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// The following benchmarks break down the layout function and attempts to time
|
||||
// each of the components to more finely attribute latency.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void BM_ParagraphMinikinDoLayout(benchmark::State& state) {
|
||||
std::vector<uint16_t> text;
|
||||
for (uint16_t i = 0; i < 16000 * 2; ++i) {
|
||||
text.push_back(i % 5 == 0 ? ' ' : i);
|
||||
}
|
||||
minikin::FontStyle font;
|
||||
txt::TextStyle text_style;
|
||||
text_style.font_family = "Roboto";
|
||||
minikin::MinikinPaint paint;
|
||||
|
||||
font = minikin::FontStyle(4, false);
|
||||
paint.size = text_style.font_size;
|
||||
paint.letterSpacing = text_style.letter_spacing;
|
||||
paint.wordSpacing = text_style.word_spacing;
|
||||
|
||||
auto collection =
|
||||
FontCollection::GetFontCollection(txt::GetFontDir())
|
||||
.GetMinikinFontCollectionForFamily(text_style.font_family);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
minikin::Layout layout;
|
||||
layout.doLayout(text.data(), 0, state.range(0), state.range(0), 0, font,
|
||||
paint, collection);
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_ParagraphMinikinDoLayout)
|
||||
->RangeMultiplier(4)
|
||||
->Range(1 << 7, 1 << 14)
|
||||
->Complexity(benchmark::oN);
|
||||
|
||||
static void BM_ParagraphMinikinAddStyleRun(benchmark::State& state) {
|
||||
std::vector<uint16_t> text;
|
||||
for (uint16_t i = 0; i < 16000 * 2; ++i) {
|
||||
text.push_back(i % 5 == 0 ? ' ' : i);
|
||||
}
|
||||
minikin::FontStyle font;
|
||||
txt::TextStyle text_style;
|
||||
text_style.font_family = "Roboto";
|
||||
minikin::MinikinPaint paint;
|
||||
|
||||
font = minikin::FontStyle(4, false);
|
||||
paint.size = text_style.font_size;
|
||||
paint.letterSpacing = text_style.letter_spacing;
|
||||
paint.wordSpacing = text_style.word_spacing;
|
||||
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
|
||||
minikin::LineBreaker breaker;
|
||||
breaker.setLocale(icu::Locale(), nullptr);
|
||||
breaker.resize(text.size());
|
||||
memcpy(breaker.buffer(), text.data(), text.size() * sizeof(text[0]));
|
||||
breaker.setText();
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
breaker.addStyleRun(
|
||||
&paint, font_collection.GetMinikinFontCollectionForFamily("Roboto"),
|
||||
font, state.range(0) / 20 * i, state.range(0) / 20 * (i + 1), false,
|
||||
0);
|
||||
}
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_ParagraphMinikinAddStyleRun)
|
||||
->RangeMultiplier(4)
|
||||
->Range(1 << 7, 1 << 14)
|
||||
->Complexity(benchmark::oN);
|
||||
|
||||
static void BM_ParagraphSkTextBlobAlloc(benchmark::State& state) {
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
paint.setTextSize(14);
|
||||
paint.setFakeBoldText(false);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
SkTextBlobBuilder builder;
|
||||
builder.allocRunPos(paint, state.range(0));
|
||||
}
|
||||
state.SetComplexityN(state.range(0));
|
||||
}
|
||||
BENCHMARK(BM_ParagraphSkTextBlobAlloc)
|
||||
->RangeMultiplier(4)
|
||||
->Range(1 << 7, 1 << 14)
|
||||
->Complexity(benchmark::oN);
|
||||
|
||||
} // namespace txt
|
||||
197
engine/src/flutter/third_party/txt/benchmarks/paragraph_builder_benchmarks.cc
vendored
Normal file
197
engine/src/flutter/third_party/txt/benchmarks/paragraph_builder_benchmarks.cc
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "third_party/benchmark/include/benchmark/benchmark_api.h"
|
||||
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
#include "lib/txt/src/font_style.h"
|
||||
#include "lib/txt/src/font_weight.h"
|
||||
#include "lib/txt/src/paragraph.h"
|
||||
#include "lib/txt/src/paragraph_builder.h"
|
||||
#include "lib/txt/src/text_align.h"
|
||||
#include "third_party/icu/source/common/unicode/unistr.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
static void BM_ParagraphBuilderConstruction(benchmark::State& state) {
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderConstruction);
|
||||
|
||||
static void BM_ParagraphBuilderPushStyle(benchmark::State& state) {
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.PushStyle(text_style);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderPushStyle);
|
||||
|
||||
static void BM_ParagraphBuilderPushPop(benchmark::State& state) {
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
txt::ParagraphBuilder builder(paragraph_style);
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
while (state.KeepRunning()) {
|
||||
builder.PushStyle(text_style);
|
||||
builder.Pop();
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderPushPop);
|
||||
|
||||
static void BM_ParagraphBuilderAddTextString(benchmark::State& state) {
|
||||
std::string text = "Hello World";
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style);
|
||||
builder.AddText(text);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderAddTextString);
|
||||
|
||||
static void BM_ParagraphBuilderAddTextChar(benchmark::State& state) {
|
||||
const char* text = "Hello World";
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.AddText(text);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderAddTextChar);
|
||||
|
||||
static void BM_ParagraphBuilderAddTextU16stringShort(benchmark::State& state) {
|
||||
const char* text = "H";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.AddText(u16_text);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderAddTextU16stringShort);
|
||||
|
||||
static void BM_ParagraphBuilderAddTextU16stringLong(benchmark::State& state) {
|
||||
const char* text =
|
||||
"This is a very long sentence to test if the text will properly wrap "
|
||||
"around and go to the next line. Sometimes, short sentence. Longer "
|
||||
"sentences are okay too because they are necessary. Very short. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style);
|
||||
builder.AddText(u16_text);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderAddTextU16stringLong);
|
||||
|
||||
static void BM_ParagraphBuilderShortParagraphConstruct(
|
||||
benchmark::State& state) {
|
||||
const char* text = "Hello World";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderShortParagraphConstruct);
|
||||
|
||||
static void BM_ParagraphBuilderLongParagraphConstruct(benchmark::State& state) {
|
||||
const char* text =
|
||||
"This is a very long sentence to test if the text will properly wrap "
|
||||
"around and go to the next line. Sometimes, short sentence. Longer "
|
||||
"sentences are okay too because they are necessary. Very short. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum. "
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum.";
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
txt::ParagraphStyle paragraph_style;
|
||||
|
||||
txt::TextStyle text_style;
|
||||
text_style.color = SK_ColorBLACK;
|
||||
auto font_collection = FontCollection::GetFontCollection(txt::GetFontDir());
|
||||
while (state.KeepRunning()) {
|
||||
txt::ParagraphBuilder builder(paragraph_style, &font_collection);
|
||||
builder.PushStyle(text_style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_ParagraphBuilderLongParagraphConstruct);
|
||||
|
||||
} // namespace txt
|
||||
39
engine/src/flutter/third_party/txt/benchmarks/styled_runs_benchmarks.cc
vendored
Normal file
39
engine/src/flutter/third_party/txt/benchmarks/styled_runs_benchmarks.cc
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "third_party/benchmark/include/benchmark/benchmark_api.h"
|
||||
|
||||
#include "lib/ftl/command_line.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/src/styled_runs.h"
|
||||
#include "lib/txt/src/text_style.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
static void BM_StyledRunsGetRun(benchmark::State& state) {
|
||||
StyledRuns runs;
|
||||
TextStyle style;
|
||||
runs.AddStyle(style);
|
||||
runs.StartRun(0, 0);
|
||||
runs.EndRunIfNeeded(11);
|
||||
while (state.KeepRunning()) {
|
||||
runs.GetRun(0);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StyledRunsGetRun);
|
||||
|
||||
} // namespace txt
|
||||
41
engine/src/flutter/third_party/txt/benchmarks/txt_run_all_benchmarks.cc
vendored
Normal file
41
engine/src/flutter/third_party/txt/benchmarks/txt_run_all_benchmarks.cc
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "third_party/benchmark/include/benchmark/benchmark_api.h"
|
||||
|
||||
#include "flutter/fml/icu_util.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
// We will use a custom main to allow custom font directories for consistency.
|
||||
int main(int argc, char** argv) {
|
||||
::benchmark::Initialize(&argc, argv);
|
||||
ftl::CommandLine cmd = ftl::CommandLineFromArgcArgv(argc, argv);
|
||||
txt::SetCommandLine(cmd);
|
||||
std::string dir = txt::GetCommandLineForProcess().GetOptionValueWithDefault(
|
||||
"font-directory", "");
|
||||
txt::SetFontDir(dir);
|
||||
if (txt::GetFontDir().length() <= 0) {
|
||||
FTL_LOG(ERROR) << "Font directory must be specified with "
|
||||
"--font-directoy=\"<directoy>\" to run this test.";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
FTL_DCHECK(txt::GetFontDir().length() > 0);
|
||||
|
||||
fml::icu::InitializeICU();
|
||||
|
||||
::benchmark::RunSpecifiedBenchmarks();
|
||||
}
|
||||
43
engine/src/flutter/third_party/txt/benchmarks/utils.cc
vendored
Normal file
43
engine/src/flutter/third_party/txt/benchmarks/utils.cc
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lib/ftl/command_line.h"
|
||||
#include "lib/txt/tests/txt/utils.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
static std::string gFontDir;
|
||||
static ftl::CommandLine gCommandLine;
|
||||
|
||||
const std::string GetFontDir() {
|
||||
return gFontDir;
|
||||
}
|
||||
|
||||
void SetFontDir(const std::string& dir) {
|
||||
gFontDir = dir;
|
||||
}
|
||||
|
||||
const ftl::CommandLine& GetCommandLineForProcess() {
|
||||
return gCommandLine;
|
||||
}
|
||||
|
||||
void SetCommandLine(ftl::CommandLine cmd) {
|
||||
gCommandLine = std::move(cmd);
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
31
engine/src/flutter/third_party/txt/benchmarks/utils.h
vendored
Normal file
31
engine/src/flutter/third_party/txt/benchmarks/utils.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lib/ftl/command_line.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
const std::string GetFontDir();
|
||||
|
||||
void SetFontDir(const std::string& dir);
|
||||
|
||||
const ftl::CommandLine& GetCommandLineForProcess();
|
||||
|
||||
void SetCommandLine(ftl::CommandLine cmd);
|
||||
|
||||
} // namespace txt
|
||||
135
engine/src/flutter/third_party/txt/doc/hyb_file_format.md
vendored
Normal file
135
engine/src/flutter/third_party/txt/doc/hyb_file_format.md
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
# Hyb (hyphenation pattern binary) file format
|
||||
|
||||
The hyb file format is how hyphenation patterns are stored in the system image.
|
||||
|
||||
Goals include:
|
||||
|
||||
* Concise (system image space is at a premium)
|
||||
* Usable when mmap'ed, so it doesn't take significant physical RAM
|
||||
* Fast to compute
|
||||
* Simple
|
||||
|
||||
It is _not_ intended as an interchange format, so there is no attempt to make the format
|
||||
extensible or facilitate backward and forward compatibility.
|
||||
|
||||
Further, at some point we will probably pack patterns for multiple languages into a single
|
||||
physical file, to reduce number of open mmap'ed files. This document doesn't cover that.
|
||||
|
||||
## Theoretical basis
|
||||
|
||||
At heart, the file contains packed tries with suffix compression, actually quite similar
|
||||
to the implementation in TeX.
|
||||
|
||||
The file contains three sections. The first section represents the "alphabet," including
|
||||
case folding. It is effectively a map from Unicode code point to a small integer.
|
||||
|
||||
The second section contains the trie in packed form. It is an array of 3-tuples, packed
|
||||
into a 32 bit integer. Each (suffix-compressed) trie node has a unique index within this
|
||||
array, and the pattern field in the tuple is the pattern for that node. Further, each edge
|
||||
in the trie has an entry in the array, and the character and link fields in the tuple
|
||||
represent the label and destination of the edge. The packing strategy is as in
|
||||
[Word Hy-phen-a-tion by Com-put-er](http://www.tug.org/docs/liang/liang-thesis.pdf) by
|
||||
Franklin Mark Liang.
|
||||
|
||||
The trie representation is similar but not identical to the "double-array trie".
|
||||
The fundamental operation of lookup of the edge from `s` to `t` with label `c` is
|
||||
to compare `c == character[s + c]`, and if so, `t = link[s + c]`.
|
||||
|
||||
The third section contains the pattern strings. This section is in two parts: first,
|
||||
an array with a 3-tuple for each pattern (length, number of trailing 0's, and offset
|
||||
into the string pool); and second, the string pool. Each pattern is encoded as a byte
|
||||
(packing 2 per byte would be possible but the space savings would not be signficant).
|
||||
|
||||
As much as possible of the file is represented as 32 bit integers, as that is especially
|
||||
efficent to access. All are little-endian (this could be revised if the code ever needs
|
||||
to be ported to big-endian systems).
|
||||
|
||||
## Header
|
||||
|
||||
```
|
||||
uint32_t magic == 0x62ad7968
|
||||
uint32_t version = 0
|
||||
uint32_t alphabet_offset (in bytes)
|
||||
uint32_t trie_offset (in bytes)
|
||||
uint32_t pattern_offset (in bytes)
|
||||
uint32_t file_size (in bytes)
|
||||
```
|
||||
|
||||
Offsets are from the front of the file, and in bytes.
|
||||
|
||||
## Alphabet
|
||||
|
||||
The alphabet table comes in two versions. The first is well suited to dense Unicode
|
||||
ranges and is limited to 256. The second is more general, but lookups will be slower.
|
||||
|
||||
### Alphabet, direct version
|
||||
|
||||
```
|
||||
uint32_t version = 0
|
||||
uint32_t min_codepoint
|
||||
uint32_t max_codepoint (exclusive)
|
||||
uint8_t[] data
|
||||
```
|
||||
|
||||
The size of the data array is max_codepoint - min_codepoint. 0 represents an unmapped
|
||||
character. Note that, in the current implementation, automatic hyphenation is disabled
|
||||
for any word containing an unmapped character.
|
||||
|
||||
In general, pad bytes follow this table, aligning the next table to a 4-byte boundary.
|
||||
|
||||
### Alphabet, general version
|
||||
|
||||
```
|
||||
uint32_t version = 1
|
||||
uint32_t n_entries
|
||||
uint32_t[n_entries] data
|
||||
```
|
||||
|
||||
Each element in the data table is `(codepoint << 11) | value`. Note that this is
|
||||
restricted to 11 bits (2048 possible values). The largest known current value is 483
|
||||
(for Sanskrit).
|
||||
|
||||
The entries are sorted by codepoint, to facilitate binary search. Another reasonable
|
||||
implementation for consumers of the data would be to build a hash table at load time.
|
||||
|
||||
## Trie
|
||||
|
||||
```
|
||||
uint32_t version = 0
|
||||
uint32_t char_mask
|
||||
uint32_t link_shift
|
||||
uint32_t link_mask
|
||||
uint32_t pattern_shift
|
||||
uint32_t n_entries
|
||||
uint32_t[n_entries] data
|
||||
```
|
||||
|
||||
Each element in the data table is `(pattern << pattern_shift) | (link << link_shift) | char`.
|
||||
|
||||
All known pattern tables fit in 32 bits total. If this is exceeded, there is a fairly
|
||||
straightforward tweak, where each node occupies a slot by itself (as opposed to sharing
|
||||
it with edge slots), which would require very minimal changes to the implementation (TODO
|
||||
present in more detail).
|
||||
|
||||
## Pattern
|
||||
|
||||
```
|
||||
uint32_t version = 0
|
||||
uint32_t n_entries
|
||||
uint32_t pattern_offset (in bytes)
|
||||
uint32_t pattern_size (in bytes)
|
||||
uint32_t[n_entries] data
|
||||
uint8_t[] pattern_buf
|
||||
```
|
||||
|
||||
Each element in data table is `(len << 26) | (shift << 20) | offset`, where an offset of 0
|
||||
points to the first byte of pattern_buf.
|
||||
|
||||
Generally pattern_offset is `16 + 4 * n_entries`.
|
||||
|
||||
For example, 'a4m5ato' would be represented as `[4, 5, 0, 0, 0]`, then len = 2, shift = 3, and
|
||||
offset points to [4, 5] in the pattern buffer.
|
||||
|
||||
Future extension: additional data representing nonstandard hyphenation. See
|
||||
[Automatic non-standard hyphenation in OpenOffice.org](https://www.tug.org/TUGboat/tb27-1/tb86nemeth.pdf)
|
||||
for more information about that issue.
|
||||
28
engine/src/flutter/third_party/txt/examples/BUILD.gn
vendored
Normal file
28
engine/src/flutter/third_party/txt/examples/BUILD.gn
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright 2017 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
executable("txt_example") {
|
||||
sources = [
|
||||
"main.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//dart/runtime:libdart_jit",
|
||||
"//flutter/fml",
|
||||
"//lib/txt/libs/minikin",
|
||||
"//lib/txt",
|
||||
"//third_party/icu:icuuc",
|
||||
"//third_party/skia",
|
||||
]
|
||||
}
|
||||
75
engine/src/flutter/third_party/txt/examples/main.cc
vendored
Normal file
75
engine/src/flutter/third_party/txt/examples/main.cc
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <unicode/unistr.h>
|
||||
|
||||
#include <SkCanvas.h>
|
||||
#include <SkGraphics.h>
|
||||
#include <SkImageEncoder.h>
|
||||
|
||||
#include "flutter/fml/icu_util.h"
|
||||
#include "lib/txt/src/paragraph_builder.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
int runTest() {
|
||||
const char* utf8_text =
|
||||
"fine world that we live in is called Earth. It's pretty nice, as far as "
|
||||
"I can tell. Rock, rock on. "
|
||||
"\xe0\xa4\xa8\xe0\xa4\xae\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\xa4\xe0\xa5"
|
||||
"\x87";
|
||||
icu::UnicodeString icu_text = icu::UnicodeString::fromUTF8(utf8_text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
|
||||
ParagraphStyle paragraph_style;
|
||||
ParagraphBuilder builder(paragraph_style);
|
||||
TextStyle style;
|
||||
style.color = SK_ColorBLUE;
|
||||
style.font_size = 32.0;
|
||||
builder.PushStyle(style);
|
||||
builder.AddText(u16_text);
|
||||
style.color = SK_ColorYELLOW;
|
||||
builder.PushStyle(style);
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
builder.AddText(u16_text);
|
||||
builder.Pop();
|
||||
auto paragraph = builder.Build();
|
||||
|
||||
int width = 800;
|
||||
int height = 600;
|
||||
paragraph->Layout(width);
|
||||
|
||||
SkAutoGraphics ag;
|
||||
SkBitmap bitmap;
|
||||
bitmap.allocN32Pixels(width, height);
|
||||
SkCanvas canvas(bitmap);
|
||||
paragraph->Paint(&canvas, 10.0, 200.0);
|
||||
|
||||
SkFILEWStream file("foo.png");
|
||||
|
||||
return SkEncodeImage(&file, bitmap, SkEncodedImageFormat::kPNG, 100)
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
fml::icu::InitializeICU();
|
||||
return txt::runTest();
|
||||
}
|
||||
32
engine/src/flutter/third_party/txt/include/minikin/CmapCoverage.h
vendored
Normal file
32
engine/src/flutter/third_party/txt/include/minikin/CmapCoverage.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_CMAP_COVERAGE_H
|
||||
#define MINIKIN_CMAP_COVERAGE_H
|
||||
|
||||
#include <minikin/SparseBitSet.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class CmapCoverage {
|
||||
public:
|
||||
static SparseBitSet getCoverage(const uint8_t* cmap_data, size_t cmap_size,
|
||||
bool* has_cmap_format14_subtable);
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_CMAP_COVERAGE_H
|
||||
34
engine/src/flutter/third_party/txt/include/minikin/Emoji.h
vendored
Normal file
34
engine/src/flutter/third_party/txt/include/minikin/Emoji.h
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <unicode/uchar.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
// Returns true if c is emoji.
|
||||
bool isEmoji(uint32_t c);
|
||||
|
||||
// Returns true if c is emoji modifier base.
|
||||
bool isEmojiBase(uint32_t c);
|
||||
|
||||
// Returns true if c is emoji modifier.
|
||||
bool isEmojiModifier(uint32_t c);
|
||||
|
||||
// Bidi override for ICU that knows about new emoji.
|
||||
UCharDirection emojiBidiOverride(const void* context, UChar32 c);
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
124
engine/src/flutter/third_party/txt/include/minikin/FontCollection.h
vendored
Normal file
124
engine/src/flutter/third_party/txt/include/minikin/FontCollection.h
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_FONT_COLLECTION_H
|
||||
#define MINIKIN_FONT_COLLECTION_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
#include <minikin/FontFamily.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class FontCollection {
|
||||
public:
|
||||
explicit FontCollection(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
|
||||
explicit FontCollection(std::shared_ptr<FontFamily>&& typeface);
|
||||
|
||||
struct Run {
|
||||
FakedFont fakedFont;
|
||||
int start;
|
||||
int end;
|
||||
};
|
||||
|
||||
void itemize(const uint16_t *string, size_t string_length, FontStyle style,
|
||||
std::vector<Run>* result) const;
|
||||
|
||||
// Returns true if there is a glyph for the code point and variation selector pair.
|
||||
// Returns false if no fonts have a glyph for the code point and variation
|
||||
// selector pair, or invalid variation selector is passed.
|
||||
bool hasVariationSelector(uint32_t baseCodepoint, uint32_t variationSelector) const;
|
||||
|
||||
// Get base font with fakery information (fake bold could affect metrics)
|
||||
FakedFont baseFontFaked(FontStyle style);
|
||||
|
||||
// Creates new FontCollection based on this collection while applying font variations. Returns
|
||||
// nullptr if none of variations apply to this collection.
|
||||
std::shared_ptr<FontCollection>
|
||||
createCollectionWithVariation(const std::vector<FontVariation>& variations);
|
||||
|
||||
const std::unordered_set<AxisTag>& getSupportedTags() const {
|
||||
return mSupportedAxes;
|
||||
}
|
||||
|
||||
uint32_t getId() const;
|
||||
|
||||
private:
|
||||
static const int kLogCharsPerPage = 8;
|
||||
static const int kPageMask = (1 << kLogCharsPerPage) - 1;
|
||||
|
||||
// mFamilyVec holds the indices of the mFamilies and mRanges holds the range of indices of
|
||||
// mFamilyVec. The maximum number of pages is 0x10FF (U+10FFFF >> 8). The maximum number of
|
||||
// the fonts is 0xFF. Thus, technically the maximum length of mFamilyVec is 0x10EE01
|
||||
// (0x10FF * 0xFF). However, in practice, 16-bit integers are enough since most fonts supports
|
||||
// only limited range of code points.
|
||||
struct Range {
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
};
|
||||
|
||||
// Initialize the FontCollection.
|
||||
void init(const std::vector<std::shared_ptr<FontFamily>>& typefaces);
|
||||
|
||||
const std::shared_ptr<FontFamily>& getFamilyForChar(uint32_t ch, uint32_t vs,
|
||||
uint32_t langListId, int variant) const;
|
||||
|
||||
uint32_t calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId,
|
||||
const std::shared_ptr<FontFamily>& fontFamily) const;
|
||||
|
||||
uint32_t calcCoverageScore(uint32_t ch, uint32_t vs,
|
||||
const std::shared_ptr<FontFamily>& fontFamily) const;
|
||||
|
||||
static uint32_t calcLanguageMatchingScore(uint32_t userLangListId,
|
||||
const FontFamily& fontFamily);
|
||||
|
||||
static uint32_t calcVariantMatchingScore(int variant, const FontFamily& fontFamily);
|
||||
|
||||
// static for allocating unique id's
|
||||
static uint32_t sNextId;
|
||||
|
||||
// unique id for this font collection (suitable for cache key)
|
||||
uint32_t mId;
|
||||
|
||||
// Highest UTF-32 code point that can be mapped
|
||||
uint32_t mMaxChar;
|
||||
|
||||
// This vector has pointers to the all font family instances in this collection.
|
||||
// This vector can't be empty.
|
||||
std::vector<std::shared_ptr<FontFamily>> mFamilies;
|
||||
|
||||
// Following two vectors are pre-calculated tables for resolving coverage faster.
|
||||
// For example, to iterate over all fonts which support Unicode code point U+XXYYZZ,
|
||||
// iterate font families index from mFamilyVec[mRanges[0xXXYY].start] to
|
||||
// mFamilyVec[mRange[0xXXYY].end] instead of whole mFamilies.
|
||||
// This vector contains indices into mFamilies.
|
||||
// This vector can't be empty.
|
||||
std::vector<Range> mRanges;
|
||||
std::vector<uint8_t> mFamilyVec;
|
||||
|
||||
// This vector has pointers to the font family instances which have cmap 14 subtables.
|
||||
std::vector<std::shared_ptr<FontFamily>> mVSFamilyVec;
|
||||
|
||||
// Set of supported axes in this collection.
|
||||
std::unordered_set<AxisTag> mSupportedAxes;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_FONT_COLLECTION_H
|
||||
179
engine/src/flutter/third_party/txt/include/minikin/FontFamily.h
vendored
Normal file
179
engine/src/flutter/third_party/txt/include/minikin/FontFamily.h
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_FONT_FAMILY_H
|
||||
#define MINIKIN_FONT_FAMILY_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <hb.h>
|
||||
|
||||
#include <utils/TypeHelpers.h>
|
||||
|
||||
#include <minikin/SparseBitSet.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class MinikinFont;
|
||||
|
||||
// FontStyle represents all style information needed to select an actual font
|
||||
// from a collection. The implementation is packed into two 32-bit words
|
||||
// so it can be efficiently copied, embedded in other objects, etc.
|
||||
class FontStyle {
|
||||
public:
|
||||
FontStyle() : FontStyle(0 /* variant */, 4 /* weight */, false /* italic */) {}
|
||||
FontStyle(int weight, bool italic) : FontStyle(0 /* variant */, weight, italic) {}
|
||||
FontStyle(uint32_t langListId) // NOLINT(implicit)
|
||||
: FontStyle(langListId, 0 /* variant */, 4 /* weight */, false /* italic */) {}
|
||||
|
||||
FontStyle(int variant, int weight, bool italic);
|
||||
FontStyle(uint32_t langListId, int variant, int weight, bool italic);
|
||||
|
||||
int getWeight() const { return bits & kWeightMask; }
|
||||
bool getItalic() const { return (bits & kItalicMask) != 0; }
|
||||
int getVariant() const { return (bits >> kVariantShift) & kVariantMask; }
|
||||
uint32_t getLanguageListId() const { return mLanguageListId; }
|
||||
|
||||
bool operator==(const FontStyle other) const {
|
||||
return bits == other.bits && mLanguageListId == other.mLanguageListId;
|
||||
}
|
||||
|
||||
android::hash_t hash() const;
|
||||
|
||||
// Looks up a language list from an internal cache and returns its ID.
|
||||
// If the passed language list is not in the cache, registers it and returns newly assigned ID.
|
||||
static uint32_t registerLanguageList(const std::string& languages);
|
||||
private:
|
||||
static const uint32_t kWeightMask = (1 << 4) - 1;
|
||||
static const uint32_t kItalicMask = 1 << 4;
|
||||
static const int kVariantShift = 5;
|
||||
static const uint32_t kVariantMask = (1 << 2) - 1;
|
||||
|
||||
static uint32_t pack(int variant, int weight, bool italic);
|
||||
|
||||
uint32_t bits;
|
||||
uint32_t mLanguageListId;
|
||||
};
|
||||
|
||||
enum FontVariant {
|
||||
VARIANT_DEFAULT = 0,
|
||||
VARIANT_COMPACT = 1,
|
||||
VARIANT_ELEGANT = 2,
|
||||
};
|
||||
|
||||
inline android::hash_t hash_type(const FontStyle &style) {
|
||||
return style.hash();
|
||||
}
|
||||
|
||||
// attributes representing transforms (fake bold, fake italic) to match styles
|
||||
class FontFakery {
|
||||
public:
|
||||
FontFakery() : mFakeBold(false), mFakeItalic(false) { }
|
||||
FontFakery(bool fakeBold, bool fakeItalic) : mFakeBold(fakeBold), mFakeItalic(fakeItalic) { }
|
||||
// TODO: want to support graded fake bolding
|
||||
bool isFakeBold() { return mFakeBold; }
|
||||
bool isFakeItalic() { return mFakeItalic; }
|
||||
private:
|
||||
bool mFakeBold;
|
||||
bool mFakeItalic;
|
||||
};
|
||||
|
||||
struct FakedFont {
|
||||
// ownership is the enclosing FontCollection
|
||||
MinikinFont* font;
|
||||
FontFakery fakery;
|
||||
};
|
||||
|
||||
typedef uint32_t AxisTag;
|
||||
|
||||
struct Font {
|
||||
Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style);
|
||||
Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style);
|
||||
Font(Font&& o);
|
||||
Font(const Font& o);
|
||||
|
||||
std::shared_ptr<MinikinFont> typeface;
|
||||
FontStyle style;
|
||||
|
||||
std::unordered_set<AxisTag> getSupportedAxesLocked() const;
|
||||
};
|
||||
|
||||
struct FontVariation {
|
||||
FontVariation(AxisTag axisTag, float value) : axisTag(axisTag), value(value) {}
|
||||
AxisTag axisTag;
|
||||
float value;
|
||||
};
|
||||
|
||||
class FontFamily {
|
||||
public:
|
||||
explicit FontFamily(std::vector<Font>&& fonts);
|
||||
FontFamily(int variant, std::vector<Font>&& fonts);
|
||||
FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts);
|
||||
|
||||
// TODO: Good to expose FontUtil.h.
|
||||
static bool analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
|
||||
bool* italic);
|
||||
FakedFont getClosestMatch(FontStyle style) const;
|
||||
|
||||
uint32_t langId() const { return mLangId; }
|
||||
int variant() const { return mVariant; }
|
||||
|
||||
// API's for enumerating the fonts in a family. These don't guarantee any particular order
|
||||
size_t getNumFonts() const { return mFonts.size(); }
|
||||
const std::shared_ptr<MinikinFont>& getFont(size_t index) const {
|
||||
return mFonts[index].typeface;
|
||||
}
|
||||
FontStyle getStyle(size_t index) const { return mFonts[index].style; }
|
||||
bool isColorEmojiFamily() const;
|
||||
const std::unordered_set<AxisTag>& supportedAxes() const { return mSupportedAxes; }
|
||||
|
||||
// Get Unicode coverage.
|
||||
const SparseBitSet& getCoverage() const { return mCoverage; }
|
||||
|
||||
// Returns true if the font has a glyph for the code point and variation selector pair.
|
||||
// Caller should acquire a lock before calling the method.
|
||||
bool hasGlyph(uint32_t codepoint, uint32_t variationSelector) const;
|
||||
|
||||
// Returns true if this font family has a variaion sequence table (cmap format 14 subtable).
|
||||
bool hasVSTable() const { return mHasVSTable; }
|
||||
|
||||
// Creates new FontFamily based on this family while applying font variations. Returns nullptr
|
||||
// if none of variations apply to this family.
|
||||
std::shared_ptr<FontFamily> createFamilyWithVariation(
|
||||
const std::vector<FontVariation>& variations) const;
|
||||
|
||||
private:
|
||||
void computeCoverage();
|
||||
|
||||
uint32_t mLangId;
|
||||
int mVariant;
|
||||
std::vector<Font> mFonts;
|
||||
std::unordered_set<AxisTag> mSupportedAxes;
|
||||
|
||||
SparseBitSet mCoverage;
|
||||
bool mHasVSTable;
|
||||
|
||||
// Forbid copying and assignment.
|
||||
FontFamily(const FontFamily&) = delete;
|
||||
void operator=(const FontFamily&) = delete;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_FONT_FAMILY_H
|
||||
47
engine/src/flutter/third_party/txt/include/minikin/GraphemeBreak.h
vendored
Normal file
47
engine/src/flutter/third_party/txt/include/minikin/GraphemeBreak.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_GRAPHEME_BREAK_H
|
||||
#define MINIKIN_GRAPHEME_BREAK_H
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class GraphemeBreak {
|
||||
public:
|
||||
// These values must be kept in sync with CURSOR_AFTER etc in Paint.java
|
||||
enum MoveOpt {
|
||||
AFTER = 0,
|
||||
AT_OR_AFTER = 1,
|
||||
BEFORE = 2,
|
||||
AT_OR_BEFORE = 3,
|
||||
AT = 4
|
||||
};
|
||||
|
||||
// Determine whether the given offset is a grapheme break.
|
||||
// This implementation generally follows Unicode's UTR #29 extended
|
||||
// grapheme break, with various tweaks.
|
||||
static bool isGraphemeBreak(const float* advances, const uint16_t* buf, size_t start,
|
||||
size_t count, size_t offset);
|
||||
|
||||
// Matches Android's Java API. Note, return (size_t)-1 for AT to
|
||||
// signal non-break because unsigned return type.
|
||||
static size_t getTextRunCursor(const float* advances, const uint16_t* buf, size_t start,
|
||||
size_t count, size_t offset, MoveOpt opt);
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_GRAPHEME_BREAK_H
|
||||
178
engine/src/flutter/third_party/txt/include/minikin/Hyphenator.h
vendored
Normal file
178
engine/src/flutter/third_party/txt/include/minikin/Hyphenator.h
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* An implementation of Liang's hyphenation algorithm.
|
||||
*/
|
||||
|
||||
#ifndef U_USING_ICU_NAMESPACE
|
||||
#define U_USING_ICU_NAMESPACE 0
|
||||
#endif // U_USING_ICU_NAMESPACE
|
||||
|
||||
#include "unicode/locid.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifndef MINIKIN_HYPHENATOR_H
|
||||
#define MINIKIN_HYPHENATOR_H
|
||||
|
||||
namespace minikin {
|
||||
|
||||
enum class HyphenationType : uint8_t {
|
||||
// Note: There are implicit assumptions scattered in the code that DONT_BREAK is 0.
|
||||
|
||||
// Do not break.
|
||||
DONT_BREAK = 0,
|
||||
// Break the line and insert a normal hyphen.
|
||||
BREAK_AND_INSERT_HYPHEN = 1,
|
||||
// Break the line and insert an Armenian hyphen (U+058A).
|
||||
BREAK_AND_INSERT_ARMENIAN_HYPHEN = 2,
|
||||
// Break the line and insert a maqaf (Hebrew hyphen, U+05BE).
|
||||
BREAK_AND_INSERT_MAQAF = 3,
|
||||
// Break the line and insert a Canadian Syllabics hyphen (U+1400).
|
||||
BREAK_AND_INSERT_UCAS_HYPHEN = 4,
|
||||
// Break the line, but don't insert a hyphen. Used for cases when there is already a hyphen
|
||||
// present or the script does not use a hyphen (e.g. in Malayalam).
|
||||
BREAK_AND_DONT_INSERT_HYPHEN = 5,
|
||||
// Break and replace the last code unit with hyphen. Used for Catalan "l·l" which hyphenates
|
||||
// as "l-/l".
|
||||
BREAK_AND_REPLACE_WITH_HYPHEN = 6,
|
||||
// Break the line, and repeat the hyphen (which is the last character) at the beginning of the
|
||||
// next line. Used in Polish, where "czerwono-niebieska" should hyphenate as
|
||||
// "czerwono-/-niebieska".
|
||||
BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE = 7,
|
||||
// Break the line, insert a ZWJ and hyphen at the first line, and a ZWJ at the second line.
|
||||
// This is used in Arabic script, mostly for writing systems of Central Asia. It's our default
|
||||
// behavior when a soft hyphen is used in Arabic script.
|
||||
BREAK_AND_INSERT_HYPHEN_AND_ZWJ = 8
|
||||
};
|
||||
|
||||
// The hyphen edit represents an edit to the string when a word is
|
||||
// hyphenated. The most common hyphen edit is adding a "-" at the end
|
||||
// of a syllable, but nonstandard hyphenation allows for more choices.
|
||||
// Note that a HyphenEdit can hold two types of edits at the same time,
|
||||
// One at the beginning of the string/line and one at the end.
|
||||
class HyphenEdit {
|
||||
public:
|
||||
static const uint32_t NO_EDIT = 0x00;
|
||||
|
||||
static const uint32_t INSERT_HYPHEN_AT_END = 0x01;
|
||||
static const uint32_t INSERT_ARMENIAN_HYPHEN_AT_END = 0x02;
|
||||
static const uint32_t INSERT_MAQAF_AT_END = 0x03;
|
||||
static const uint32_t INSERT_UCAS_HYPHEN_AT_END = 0x04;
|
||||
static const uint32_t INSERT_ZWJ_AND_HYPHEN_AT_END = 0x05;
|
||||
static const uint32_t REPLACE_WITH_HYPHEN_AT_END = 0x06;
|
||||
static const uint32_t BREAK_AT_END = 0x07;
|
||||
|
||||
static const uint32_t INSERT_HYPHEN_AT_START = 0x01 << 3;
|
||||
static const uint32_t INSERT_ZWJ_AT_START = 0x02 << 3;
|
||||
static const uint32_t BREAK_AT_START = 0x03 << 3;
|
||||
|
||||
// Keep in sync with the definitions in the Java code at:
|
||||
// frameworks/base/graphics/java/android/graphics/Paint.java
|
||||
static const uint32_t MASK_END_OF_LINE = 0x07;
|
||||
static const uint32_t MASK_START_OF_LINE = 0x03 << 3;
|
||||
|
||||
inline static bool isReplacement(uint32_t hyph) {
|
||||
return hyph == REPLACE_WITH_HYPHEN_AT_END;
|
||||
}
|
||||
|
||||
inline static bool isInsertion(uint32_t hyph) {
|
||||
return (hyph == INSERT_HYPHEN_AT_END
|
||||
|| hyph == INSERT_ARMENIAN_HYPHEN_AT_END
|
||||
|| hyph == INSERT_MAQAF_AT_END
|
||||
|| hyph == INSERT_UCAS_HYPHEN_AT_END
|
||||
|| hyph == INSERT_ZWJ_AND_HYPHEN_AT_END
|
||||
|| hyph == INSERT_HYPHEN_AT_START
|
||||
|| hyph == INSERT_ZWJ_AT_START);
|
||||
}
|
||||
|
||||
const static uint32_t* getHyphenString(uint32_t hyph);
|
||||
static uint32_t editForThisLine(HyphenationType type);
|
||||
static uint32_t editForNextLine(HyphenationType type);
|
||||
|
||||
HyphenEdit() : hyphen(NO_EDIT) { }
|
||||
HyphenEdit(uint32_t hyphenInt) : hyphen(hyphenInt) { } // NOLINT(implicit)
|
||||
uint32_t getHyphen() const { return hyphen; }
|
||||
bool operator==(const HyphenEdit &other) const { return hyphen == other.hyphen; }
|
||||
|
||||
uint32_t getEnd() const { return hyphen & MASK_END_OF_LINE; }
|
||||
uint32_t getStart() const { return hyphen & MASK_START_OF_LINE; }
|
||||
|
||||
private:
|
||||
uint32_t hyphen;
|
||||
};
|
||||
|
||||
// hyb file header; implementation details are in the .cpp file
|
||||
struct Header;
|
||||
|
||||
class Hyphenator {
|
||||
public:
|
||||
// Compute the hyphenation of a word, storing the hyphenation in result vector. Each entry in
|
||||
// the vector is a "hyphenation type" for a potential hyphenation that can be applied at the
|
||||
// corresponding code unit offset in the word.
|
||||
//
|
||||
// Example: word is "hyphen", result is the following, corresponding to "hy-phen":
|
||||
// [DONT_BREAK, DONT_BREAK, BREAK_AND_INSERT_HYPHEN, DONT_BREAK, DONT_BREAK, DONT_BREAK]
|
||||
void hyphenate(std::vector<HyphenationType>* result, const uint16_t* word, size_t len,
|
||||
const icu::Locale& locale);
|
||||
|
||||
// Returns true if the codepoint is like U+2010 HYPHEN in line breaking and usage: a character
|
||||
// immediately after which line breaks are allowed, but words containing it should not be
|
||||
// automatically hyphenated.
|
||||
static bool isLineBreakingHyphen(uint32_t cp);
|
||||
|
||||
// pattern data is in binary format, as described in doc/hyb_file_format.md. Note:
|
||||
// the caller is responsible for ensuring that the lifetime of the pattern data is
|
||||
// at least as long as the Hyphenator object.
|
||||
|
||||
// Note: nullptr is valid input, in which case the hyphenator only processes soft hyphens.
|
||||
static Hyphenator* loadBinary(const uint8_t* patternData, size_t minPrefix, size_t minSuffix);
|
||||
|
||||
private:
|
||||
// apply various hyphenation rules including hard and soft hyphens, ignoring patterns
|
||||
void hyphenateWithNoPatterns(HyphenationType* result, const uint16_t* word, size_t len,
|
||||
const icu::Locale& locale);
|
||||
|
||||
// Try looking up word in alphabet table, return DONT_BREAK if any code units fail to map.
|
||||
// Otherwise, returns BREAK_AND_INSERT_HYPHEN, BREAK_AND_INSERT_ARMENIAN_HYPHEN, or
|
||||
// BREAK_AND_DONT_INSERT_HYPHEN based on the the script of the characters seen.
|
||||
// Note that this method writes len+2 entries into alpha_codes (including start and stop)
|
||||
HyphenationType alphabetLookup(uint16_t* alpha_codes, const uint16_t* word, size_t len);
|
||||
|
||||
// calculate hyphenation from patterns, assuming alphabet lookup has already been done
|
||||
void hyphenateFromCodes(HyphenationType* result, const uint16_t* codes, size_t len,
|
||||
HyphenationType hyphenValue);
|
||||
|
||||
// See also LONGEST_HYPHENATED_WORD in LineBreaker.cpp. Here the constant is used so
|
||||
// that temporary buffers can be stack-allocated without waste, which is a slightly
|
||||
// different use case. It measures UTF-16 code units.
|
||||
static const size_t MAX_HYPHENATED_SIZE = 64;
|
||||
|
||||
const uint8_t* patternData;
|
||||
size_t minPrefix, minSuffix;
|
||||
|
||||
// accessors for binary data
|
||||
const Header* getHeader() const {
|
||||
return reinterpret_cast<const Header*>(patternData);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_HYPHENATOR_H
|
||||
143
engine/src/flutter/third_party/txt/include/minikin/Layout.h
vendored
Normal file
143
engine/src/flutter/third_party/txt/include/minikin/Layout.h
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_LAYOUT_H
|
||||
#define MINIKIN_LAYOUT_H
|
||||
|
||||
#include <hb.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <minikin/FontCollection.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
struct LayoutGlyph {
|
||||
// index into mFaces and mHbFonts vectors. We could imagine
|
||||
// moving this into a run length representation, because it's
|
||||
// more efficient for long strings, and we'll probably need
|
||||
// something like that for paint attributes (color, underline,
|
||||
// fake b/i, etc), as having those per-glyph is bloated.
|
||||
int font_ix;
|
||||
|
||||
unsigned int glyph_id;
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
// Internal state used during layout operation
|
||||
struct LayoutContext;
|
||||
|
||||
enum {
|
||||
kBidi_LTR = 0,
|
||||
kBidi_RTL = 1,
|
||||
kBidi_Default_LTR = 2,
|
||||
kBidi_Default_RTL = 3,
|
||||
kBidi_Force_LTR = 4,
|
||||
kBidi_Force_RTL = 5,
|
||||
|
||||
kBidi_Mask = 0x7
|
||||
};
|
||||
|
||||
// Lifecycle and threading assumptions for Layout:
|
||||
// The object is assumed to be owned by a single thread; multiple threads
|
||||
// may not mutate it at the same time.
|
||||
class Layout {
|
||||
public:
|
||||
|
||||
Layout() : mGlyphs(), mAdvances(), mFaces(), mAdvance(0), mBounds() {
|
||||
mBounds.setEmpty();
|
||||
}
|
||||
|
||||
Layout(Layout&& layout) = default;
|
||||
|
||||
// Forbid copying and assignment.
|
||||
Layout(const Layout&) = delete;
|
||||
void operator=(const Layout&) = delete;
|
||||
|
||||
void dump() const;
|
||||
|
||||
void doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
|
||||
int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
|
||||
const std::shared_ptr<FontCollection>& collection);
|
||||
|
||||
static float measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
|
||||
int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
|
||||
const std::shared_ptr<FontCollection>& collection, float* advances);
|
||||
|
||||
// public accessors
|
||||
size_t nGlyphs() const;
|
||||
const MinikinFont* getFont(int i) const;
|
||||
FontFakery getFakery(int i) const;
|
||||
unsigned int getGlyphId(int i) const;
|
||||
float getX(int i) const;
|
||||
float getY(int i) const;
|
||||
|
||||
float getAdvance() const;
|
||||
|
||||
// Get advances, copying into caller-provided buffer. The size of this
|
||||
// buffer must match the length of the string (count arg to doLayout).
|
||||
void getAdvances(float* advances);
|
||||
|
||||
// The i parameter is an offset within the buf relative to start, it is < count, where
|
||||
// start and count are the parameters to doLayout
|
||||
float getCharAdvance(size_t i) const { return mAdvances[i]; }
|
||||
|
||||
void getBounds(MinikinRect* rect) const;
|
||||
|
||||
// Purge all caches, useful in low memory conditions
|
||||
static void purgeCaches();
|
||||
|
||||
private:
|
||||
friend class LayoutCacheKey;
|
||||
|
||||
// Find a face in the mFaces vector, or create a new entry
|
||||
int findFace(const FakedFont& face, LayoutContext* ctx);
|
||||
|
||||
// Clears layout, ready to be used again
|
||||
void reset();
|
||||
|
||||
// Lay out a single bidi run
|
||||
// When layout is not null, layout info will be stored in the object.
|
||||
// When advances is not null, measurement results will be stored in the array.
|
||||
static float doLayoutRunCached(const uint16_t* buf, size_t runStart, size_t runLength,
|
||||
size_t bufSize, bool isRtl, LayoutContext* ctx, size_t dstStart,
|
||||
const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances);
|
||||
|
||||
// Lay out a single word
|
||||
static float doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
|
||||
bool isRtl, LayoutContext* ctx, size_t bufStart,
|
||||
const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances);
|
||||
|
||||
// Lay out a single bidi run
|
||||
void doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
|
||||
bool isRtl, LayoutContext* ctx, const std::shared_ptr<FontCollection>& collection);
|
||||
|
||||
// Append another layout (for example, cached value) into this one
|
||||
void appendLayout(Layout* src, size_t start, float extraAdvance);
|
||||
|
||||
std::vector<LayoutGlyph> mGlyphs;
|
||||
std::vector<float> mAdvances;
|
||||
|
||||
std::vector<FakedFont> mFaces;
|
||||
float mAdvance;
|
||||
MinikinRect mBounds;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_LAYOUT_H
|
||||
265
engine/src/flutter/third_party/txt/include/minikin/LineBreaker.h
vendored
Normal file
265
engine/src/flutter/third_party/txt/include/minikin/LineBreaker.h
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A module for breaking paragraphs into lines, supporting high quality
|
||||
* hyphenation and justification.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_LINE_BREAKER_H
|
||||
#define MINIKIN_LINE_BREAKER_H
|
||||
|
||||
#ifndef U_USING_ICU_NAMESPACE
|
||||
#define U_USING_ICU_NAMESPACE 0
|
||||
#endif // U_USING_ICU_NAMESPACE
|
||||
|
||||
#include "unicode/brkiter.h"
|
||||
#include "unicode/locid.h"
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include "minikin/FontCollection.h"
|
||||
#include "minikin/Hyphenator.h"
|
||||
#include "minikin/MinikinFont.h"
|
||||
#include "minikin/WordBreaker.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
enum BreakStrategy {
|
||||
kBreakStrategy_Greedy = 0,
|
||||
kBreakStrategy_HighQuality = 1,
|
||||
kBreakStrategy_Balanced = 2
|
||||
};
|
||||
|
||||
enum HyphenationFrequency {
|
||||
kHyphenationFrequency_None = 0,
|
||||
kHyphenationFrequency_Normal = 1,
|
||||
kHyphenationFrequency_Full = 2
|
||||
};
|
||||
|
||||
// TODO: want to generalize to be able to handle array of line widths
|
||||
class LineWidths {
|
||||
public:
|
||||
void setWidths(float firstWidth, int firstWidthLineCount, float restWidth) {
|
||||
mFirstWidth = firstWidth;
|
||||
mFirstWidthLineCount = firstWidthLineCount;
|
||||
mRestWidth = restWidth;
|
||||
}
|
||||
void setIndents(const std::vector<float>& indents) {
|
||||
mIndents = indents;
|
||||
}
|
||||
bool isConstant() const {
|
||||
// technically mFirstWidthLineCount == 0 would count too, but doesn't actually happen
|
||||
return mRestWidth == mFirstWidth && mIndents.empty();
|
||||
}
|
||||
float getLineWidth(int line) const {
|
||||
float width = (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
|
||||
if (!mIndents.empty()) {
|
||||
if ((size_t)line < mIndents.size()) {
|
||||
width -= mIndents[line];
|
||||
} else {
|
||||
width -= mIndents.back();
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
void clear() {
|
||||
mIndents.clear();
|
||||
}
|
||||
private:
|
||||
float mFirstWidth;
|
||||
int mFirstWidthLineCount;
|
||||
float mRestWidth;
|
||||
std::vector<float> mIndents;
|
||||
};
|
||||
|
||||
class TabStops {
|
||||
public:
|
||||
void set(const int* stops, size_t nStops, int tabWidth) {
|
||||
if (stops != nullptr) {
|
||||
mStops.assign(stops, stops + nStops);
|
||||
} else {
|
||||
mStops.clear();
|
||||
}
|
||||
mTabWidth = tabWidth;
|
||||
}
|
||||
float nextTab(float widthSoFar) const {
|
||||
for (size_t i = 0; i < mStops.size(); i++) {
|
||||
if (mStops[i] > widthSoFar) {
|
||||
return mStops[i];
|
||||
}
|
||||
}
|
||||
return floor(widthSoFar / mTabWidth + 1) * mTabWidth;
|
||||
}
|
||||
private:
|
||||
std::vector<int> mStops;
|
||||
int mTabWidth;
|
||||
};
|
||||
|
||||
class LineBreaker {
|
||||
public:
|
||||
const static int kTab_Shift = 29; // keep synchronized with TAB_MASK in StaticLayout.java
|
||||
|
||||
// Note: Locale persists across multiple invocations (it is not cleaned up by finish()),
|
||||
// explicitly to avoid the cost of creating ICU BreakIterator objects. It should always
|
||||
// be set on the first invocation, but callers are encouraged not to call again unless
|
||||
// locale has actually changed.
|
||||
// That logic could be here but it's better for performance that it's upstream because of
|
||||
// the cost of constructing and comparing the ICU Locale object.
|
||||
// Note: caller is responsible for managing lifetime of hyphenator
|
||||
void setLocale(const icu::Locale& locale, Hyphenator* hyphenator);
|
||||
|
||||
void resize(size_t size) {
|
||||
mTextBuf.resize(size);
|
||||
mCharWidths.resize(size);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return mTextBuf.size();
|
||||
}
|
||||
|
||||
uint16_t* buffer() {
|
||||
return mTextBuf.data();
|
||||
}
|
||||
|
||||
float* charWidths() {
|
||||
return mCharWidths.data();
|
||||
}
|
||||
|
||||
// set text to current contents of buffer
|
||||
void setText();
|
||||
|
||||
void setLineWidths(float firstWidth, int firstWidthLineCount, float restWidth);
|
||||
|
||||
void setIndents(const std::vector<float>& indents);
|
||||
|
||||
void setTabStops(const int* stops, size_t nStops, int tabWidth) {
|
||||
mTabStops.set(stops, nStops, tabWidth);
|
||||
}
|
||||
|
||||
BreakStrategy getStrategy() const { return mStrategy; }
|
||||
|
||||
void setStrategy(BreakStrategy strategy) { mStrategy = strategy; }
|
||||
|
||||
void setJustified(bool justified) { mJustified = justified; }
|
||||
|
||||
HyphenationFrequency getHyphenationFrequency() const { return mHyphenationFrequency; }
|
||||
|
||||
void setHyphenationFrequency(HyphenationFrequency frequency) {
|
||||
mHyphenationFrequency = frequency;
|
||||
}
|
||||
|
||||
// TODO: this class is actually fairly close to being general and not tied to using
|
||||
// Minikin to do the shaping of the strings. The main thing that would need to be changed
|
||||
// is having some kind of callback (or virtual class, or maybe even template), which could
|
||||
// easily be instantiated with Minikin's Layout. Future work for when needed.
|
||||
float addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCollection>& typeface,
|
||||
FontStyle style, size_t start, size_t end, bool isRtl, double letterSpacing = 0);
|
||||
|
||||
void addReplacement(size_t start, size_t end, float width);
|
||||
|
||||
size_t computeBreaks();
|
||||
|
||||
const int* getBreaks() const {
|
||||
return mBreaks.data();
|
||||
}
|
||||
|
||||
const float* getWidths() const {
|
||||
return mWidths.data();
|
||||
}
|
||||
|
||||
const int* getFlags() const {
|
||||
return mFlags.data();
|
||||
}
|
||||
|
||||
void finish();
|
||||
|
||||
private:
|
||||
// ParaWidth is used to hold cumulative width from beginning of paragraph. Note that for
|
||||
// very large paragraphs, accuracy could degrade using only 32-bit float. Note however
|
||||
// that float is used extensively on the Java side for this. This is a typedef so that
|
||||
// we can easily change it based on performance/accuracy tradeoff.
|
||||
typedef double ParaWidth;
|
||||
|
||||
// A single candidate break
|
||||
struct Candidate {
|
||||
size_t offset; // offset to text buffer, in code units
|
||||
size_t prev; // index to previous break
|
||||
ParaWidth preBreak; // width of text until this point, if we decide to not break here
|
||||
ParaWidth postBreak; // width of text until this point, if we decide to break here
|
||||
float penalty; // penalty of this break (for example, hyphen penalty)
|
||||
float score; // best score found for this break
|
||||
size_t lineNumber; // only updated for non-constant line widths
|
||||
size_t preSpaceCount; // preceding space count before breaking
|
||||
size_t postSpaceCount; // preceding space count after breaking
|
||||
HyphenationType hyphenType;
|
||||
};
|
||||
|
||||
float currentLineWidth() const;
|
||||
|
||||
void addWordBreak(size_t offset, ParaWidth preBreak, ParaWidth postBreak,
|
||||
size_t preSpaceCount, size_t postSpaceCount, float penalty, HyphenationType hyph);
|
||||
|
||||
void addCandidate(Candidate cand);
|
||||
void pushGreedyBreak();
|
||||
|
||||
// push an actual break to the output. Takes care of setting flags for tab
|
||||
void pushBreak(int offset, float width, uint8_t hyphenEdit);
|
||||
|
||||
float getSpaceWidth() const;
|
||||
|
||||
void computeBreaksGreedy();
|
||||
|
||||
void computeBreaksOptimal(bool isRectangular);
|
||||
|
||||
void finishBreaksOptimal();
|
||||
|
||||
WordBreaker mWordBreaker;
|
||||
icu::Locale mLocale;
|
||||
std::vector<uint16_t>mTextBuf;
|
||||
std::vector<float>mCharWidths;
|
||||
|
||||
Hyphenator* mHyphenator;
|
||||
std::vector<HyphenationType> mHyphBuf;
|
||||
|
||||
// layout parameters
|
||||
BreakStrategy mStrategy = kBreakStrategy_Greedy;
|
||||
HyphenationFrequency mHyphenationFrequency = kHyphenationFrequency_Normal;
|
||||
bool mJustified;
|
||||
LineWidths mLineWidths;
|
||||
TabStops mTabStops;
|
||||
|
||||
// result of line breaking
|
||||
std::vector<int> mBreaks;
|
||||
std::vector<float> mWidths;
|
||||
std::vector<int> mFlags;
|
||||
|
||||
ParaWidth mWidth = 0;
|
||||
std::vector<Candidate> mCandidates;
|
||||
float mLinePenalty = 0.0f;
|
||||
|
||||
// the following are state for greedy breaker (updated while adding style runs)
|
||||
size_t mLastBreak;
|
||||
size_t mBestBreak;
|
||||
float mBestScore;
|
||||
ParaWidth mPreBreak; // prebreak of last break
|
||||
uint32_t mLastHyphenation; // hyphen edit of last break kept for next line
|
||||
int mFirstTabIndex;
|
||||
size_t mSpaceCount;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_LINE_BREAKER_H
|
||||
32
engine/src/flutter/third_party/txt/include/minikin/Measurement.h
vendored
Normal file
32
engine/src/flutter/third_party/txt/include/minikin/Measurement.h
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_MEASUREMENT_H
|
||||
#define MINIKIN_MEASUREMENT_H
|
||||
|
||||
#include <minikin/Layout.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
|
||||
size_t offset);
|
||||
|
||||
size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
|
||||
float advance);
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_MEASUREMENT_H
|
||||
122
engine/src/flutter/third_party/txt/include/minikin/MinikinFont.h
vendored
Normal file
122
engine/src/flutter/third_party/txt/include/minikin/MinikinFont.h
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_FONT_H
|
||||
#define MINIKIN_FONT_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <minikin/FontFamily.h>
|
||||
#include <minikin/Hyphenator.h>
|
||||
|
||||
// An abstraction for platform fonts, allowing Minikin to be used with
|
||||
// multiple actual implementations of fonts.
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class MinikinFont;
|
||||
|
||||
// Possibly move into own .h file?
|
||||
// Note: if you add a field here, either add it to LayoutCacheKey or to skipCache()
|
||||
struct MinikinPaint {
|
||||
MinikinPaint() : font(nullptr), size(0), scaleX(0), skewX(0), letterSpacing(0), wordSpacing(0),
|
||||
paintFlags(0), fakery(), hyphenEdit(), fontFeatureSettings() { }
|
||||
|
||||
bool skipCache() const {
|
||||
return !fontFeatureSettings.empty();
|
||||
}
|
||||
|
||||
MinikinFont *font;
|
||||
float size;
|
||||
float scaleX;
|
||||
float skewX;
|
||||
float letterSpacing;
|
||||
float wordSpacing;
|
||||
uint32_t paintFlags;
|
||||
FontFakery fakery;
|
||||
HyphenEdit hyphenEdit;
|
||||
std::string fontFeatureSettings;
|
||||
};
|
||||
|
||||
// Only a few flags affect layout, but those that do should have values
|
||||
// consistent with Android's paint flags.
|
||||
enum MinikinPaintFlags {
|
||||
LinearTextFlag = 0x40,
|
||||
};
|
||||
|
||||
struct MinikinRect {
|
||||
float mLeft, mTop, mRight, mBottom;
|
||||
bool isEmpty() const {
|
||||
return mLeft == mRight || mTop == mBottom;
|
||||
}
|
||||
void set(const MinikinRect& r) {
|
||||
mLeft = r.mLeft;
|
||||
mTop = r.mTop;
|
||||
mRight = r.mRight;
|
||||
mBottom = r.mBottom;
|
||||
}
|
||||
void offset(float dx, float dy) {
|
||||
mLeft += dx;
|
||||
mTop += dy;
|
||||
mRight += dx;
|
||||
mBottom += dy;
|
||||
}
|
||||
void setEmpty() {
|
||||
mLeft = mTop = mRight = mBottom = 0;
|
||||
}
|
||||
void join(const MinikinRect& r);
|
||||
};
|
||||
|
||||
// Callback for freeing data
|
||||
typedef void (*MinikinDestroyFunc) (void* data);
|
||||
|
||||
class MinikinFont {
|
||||
public:
|
||||
explicit MinikinFont(int32_t uniqueId) : mUniqueId(uniqueId) {}
|
||||
|
||||
virtual ~MinikinFont();
|
||||
|
||||
virtual float GetHorizontalAdvance(uint32_t glyph_id,
|
||||
const MinikinPaint &paint) const = 0;
|
||||
|
||||
virtual void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
|
||||
const MinikinPaint &paint) const = 0;
|
||||
|
||||
virtual hb_face_t* CreateHarfBuzzFace() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual const std::vector<minikin::FontVariation>& GetAxes() const = 0;
|
||||
|
||||
virtual std::shared_ptr<MinikinFont> createFontWithVariation(
|
||||
const std::vector<FontVariation>&) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static uint32_t MakeTag(char c1, char c2, char c3, char c4) {
|
||||
return ((uint32_t)c1 << 24) | ((uint32_t)c2 << 16) |
|
||||
((uint32_t)c3 << 8) | (uint32_t)c4;
|
||||
}
|
||||
|
||||
int32_t GetUniqueId() const { return mUniqueId; }
|
||||
private:
|
||||
const int32_t mUniqueId;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_FONT_H
|
||||
99
engine/src/flutter/third_party/txt/include/minikin/SparseBitSet.h
vendored
Normal file
99
engine/src/flutter/third_party/txt/include/minikin/SparseBitSet.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_SPARSE_BIT_SET_H
|
||||
#define MINIKIN_SPARSE_BIT_SET_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace minikin {
|
||||
|
||||
// This is an implementation of a set of integers. It is optimized for
|
||||
// values that are somewhat sparse, in the ballpark of a maximum value
|
||||
// of thousands to millions. It is particularly efficient when there are
|
||||
// large gaps. The motivating example is Unicode coverage of a font, but
|
||||
// the abstraction itself is fully general.
|
||||
class SparseBitSet {
|
||||
public:
|
||||
// Create an empty bit set.
|
||||
SparseBitSet() : mMaxVal(0) {}
|
||||
|
||||
// Initialize the set to a new value, represented by ranges. For
|
||||
// simplicity, these ranges are arranged as pairs of values,
|
||||
// inclusive of start, exclusive of end, laid out in a uint32 array.
|
||||
SparseBitSet(const uint32_t* ranges, size_t nRanges) : SparseBitSet() {
|
||||
initFromRanges(ranges, nRanges);
|
||||
}
|
||||
|
||||
SparseBitSet(SparseBitSet&&) = default;
|
||||
SparseBitSet& operator=(SparseBitSet&&) = default;
|
||||
|
||||
// Determine whether the value is included in the set
|
||||
bool get(uint32_t ch) const {
|
||||
if (ch >= mMaxVal) return false;
|
||||
const uint32_t *bitmap = &mBitmaps[mIndices[ch >> kLogValuesPerPage]];
|
||||
uint32_t index = ch & kPageMask;
|
||||
return (bitmap[index >> kLogBitsPerEl] & (kElFirst >> (index & kElMask))) != 0;
|
||||
}
|
||||
|
||||
// One more than the maximum value in the set, or zero if empty
|
||||
uint32_t length() const {
|
||||
return mMaxVal;
|
||||
}
|
||||
|
||||
// The next set bit starting at fromIndex, inclusive, or kNotFound
|
||||
// if none exists.
|
||||
uint32_t nextSetBit(uint32_t fromIndex) const;
|
||||
|
||||
static const uint32_t kNotFound = ~0u;
|
||||
|
||||
private:
|
||||
void initFromRanges(const uint32_t* ranges, size_t nRanges);
|
||||
|
||||
static const uint32_t kMaximumCapacity = 0xFFFFFF;
|
||||
static const int kLogValuesPerPage = 8;
|
||||
static const int kPageMask = (1 << kLogValuesPerPage) - 1;
|
||||
static const int kLogBytesPerEl = 2;
|
||||
static const int kLogBitsPerEl = kLogBytesPerEl + 3;
|
||||
static const int kElMask = (1 << kLogBitsPerEl) - 1;
|
||||
// invariant: sizeof(element) == (1 << kLogBytesPerEl)
|
||||
typedef uint32_t element;
|
||||
static const element kElAllOnes = ~((element)0);
|
||||
static const element kElFirst = ((element)1) << kElMask;
|
||||
static const uint16_t noZeroPage = 0xFFFF;
|
||||
|
||||
static uint32_t calcNumPages(const uint32_t* ranges, size_t nRanges);
|
||||
static int CountLeadingZeros(element x);
|
||||
|
||||
uint32_t mMaxVal;
|
||||
|
||||
std::unique_ptr<uint16_t[]> mIndices;
|
||||
std::unique_ptr<element[]> mBitmaps;
|
||||
uint16_t mZeroPageIndex;
|
||||
|
||||
// Forbid copy and assign.
|
||||
SparseBitSet(const SparseBitSet&) = delete;
|
||||
void operator=(const SparseBitSet&) = delete;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_SPARSE_BIT_SET_H
|
||||
77
engine/src/flutter/third_party/txt/include/minikin/WordBreaker.h
vendored
Normal file
77
engine/src/flutter/third_party/txt/include/minikin/WordBreaker.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A wrapper around ICU's line break iterator, that gives customized line
|
||||
* break opportunities, as well as identifying words for the purpose of
|
||||
* hyphenation.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_WORD_BREAKER_H
|
||||
#define MINIKIN_WORD_BREAKER_H
|
||||
|
||||
#include "unicode/brkiter.h"
|
||||
#include <memory>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class WordBreaker {
|
||||
public:
|
||||
~WordBreaker() {
|
||||
finish();
|
||||
}
|
||||
|
||||
void setLocale(const icu::Locale& locale);
|
||||
|
||||
void setText(const uint16_t* data, size_t size);
|
||||
|
||||
// Advance iterator to next word break. Return offset, or -1 if EOT
|
||||
ssize_t next();
|
||||
|
||||
// Current offset of iterator, equal to 0 at BOT or last return from next()
|
||||
ssize_t current() const;
|
||||
|
||||
// After calling next(), wordStart() and wordEnd() are offsets defining the previous
|
||||
// word. If wordEnd <= wordStart, it's not a word for the purpose of hyphenation.
|
||||
ssize_t wordStart() const;
|
||||
|
||||
ssize_t wordEnd() const;
|
||||
|
||||
int breakBadness() const;
|
||||
|
||||
void finish();
|
||||
|
||||
private:
|
||||
int32_t iteratorNext();
|
||||
void detectEmailOrUrl();
|
||||
ssize_t findNextBreakInEmailOrUrl();
|
||||
|
||||
std::unique_ptr<icu::BreakIterator> mBreakIterator;
|
||||
UText mUText = UTEXT_INITIALIZER;
|
||||
const uint16_t* mText = nullptr;
|
||||
size_t mTextSize;
|
||||
ssize_t mLast;
|
||||
ssize_t mCurrent;
|
||||
bool mIteratorWasReset;
|
||||
|
||||
// state for the email address / url detector
|
||||
ssize_t mScanOffset;
|
||||
bool mInEmailOrUrl;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_WORD_BREAKER_H
|
||||
86
engine/src/flutter/third_party/txt/libs/minikin/Android.bp
vendored
Normal file
86
engine/src/flutter/third_party/txt/libs/minikin/Android.bp
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2013 The Android Open Source Project
|
||||
//
|
||||
// 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.
|
||||
|
||||
cc_library_headers {
|
||||
name: "libminikin-headers-for-tests",
|
||||
export_include_dirs: ["."],
|
||||
shared_libs: ["libharfbuzz_ng"],
|
||||
export_shared_lib_headers: ["libharfbuzz_ng"],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libminikin",
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"Hyphenator.cpp",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
srcs: [
|
||||
"CmapCoverage.cpp",
|
||||
"Emoji.cpp",
|
||||
"FontCollection.cpp",
|
||||
"FontFamily.cpp",
|
||||
"FontLanguage.cpp",
|
||||
"FontLanguageListCache.cpp",
|
||||
"FontUtils.cpp",
|
||||
"GraphemeBreak.cpp",
|
||||
"HbFontCache.cpp",
|
||||
"Layout.cpp",
|
||||
"LayoutUtils.cpp",
|
||||
"LineBreaker.cpp",
|
||||
"Measurement.cpp",
|
||||
"MinikinInternal.cpp",
|
||||
"MinikinFont.cpp",
|
||||
"SparseBitSet.cpp",
|
||||
"WordBreaker.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libharfbuzz_ng",
|
||||
"libft2",
|
||||
"libz",
|
||||
"libutils",
|
||||
],
|
||||
// TODO: clean up Minikin so it doesn't need the freetype include
|
||||
export_shared_lib_headers: ["libft2"],
|
||||
},
|
||||
},
|
||||
cppflags: [
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
],
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
// Enable race detection on eng and userdebug build.
|
||||
cppflags: ["-DENABLE_RACE_DETECTION"],
|
||||
},
|
||||
},
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libicuuc",
|
||||
],
|
||||
header_libs: ["libminikin_headers"],
|
||||
export_header_lib_headers: ["libminikin_headers"],
|
||||
export_shared_lib_headers: ["libicuuc"],
|
||||
|
||||
clang: true,
|
||||
sanitize: {
|
||||
misc_undefined: [
|
||||
"signed-integer-overflow",
|
||||
// b/26432628.
|
||||
//"unsigned-integer-overflow",
|
||||
],
|
||||
},
|
||||
}
|
||||
61
engine/src/flutter/third_party/txt/libs/minikin/BUILD.gn
vendored
Normal file
61
engine/src/flutter/third_party/txt/libs/minikin/BUILD.gn
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# Copyright 2017 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
config("minikin_config") {
|
||||
include_dirs = [ "../../include" ]
|
||||
}
|
||||
|
||||
source_set("minikin") {
|
||||
defines = [ "WIP_NEEDS_ICU_UPDATE" ]
|
||||
|
||||
sources = [
|
||||
"CmapCoverage.cpp",
|
||||
"Emoji.cpp",
|
||||
"FontCollection.cpp",
|
||||
"FontFamily.cpp",
|
||||
"FontLanguage.cpp",
|
||||
"FontLanguage.h",
|
||||
"FontLanguageListCache.cpp",
|
||||
"FontLanguageListCache.h",
|
||||
"FontUtils.cpp",
|
||||
"FontUtils.h",
|
||||
"GraphemeBreak.cpp",
|
||||
"HbFontCache.cpp",
|
||||
"HbFontCache.h",
|
||||
"Hyphenator.cpp",
|
||||
"Layout.cpp",
|
||||
"LayoutUtils.cpp",
|
||||
"LayoutUtils.h",
|
||||
"LineBreaker.cpp",
|
||||
"Measurement.cpp",
|
||||
"MinikinFont.cpp",
|
||||
"MinikinInternal.cpp",
|
||||
"MinikinInternal.h",
|
||||
"SparseBitSet.cpp",
|
||||
"WordBreaker.cpp",
|
||||
]
|
||||
|
||||
public_configs = [ ":minikin_config" ]
|
||||
|
||||
public_deps = [
|
||||
"//lib/txt/shims",
|
||||
"//third_party/freetype2",
|
||||
"//third_party/harfbuzz",
|
||||
"//third_party/icu:icuuc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//third_party/zlib",
|
||||
]
|
||||
}
|
||||
288
engine/src/flutter/third_party/txt/libs/minikin/CmapCoverage.cpp
vendored
Normal file
288
engine/src/flutter/third_party/txt/libs/minikin/CmapCoverage.cpp
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Determine coverage of font given its raw "cmap" OpenType table
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include <minikin/SparseBitSet.h>
|
||||
#include <minikin/CmapCoverage.h>
|
||||
#include "MinikinInternal.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
// These could perhaps be optimized to use __builtin_bswap16 and friends.
|
||||
static uint32_t readU16(const uint8_t* data, size_t offset) {
|
||||
return ((uint32_t)data[offset]) << 8 | ((uint32_t)data[offset + 1]);
|
||||
}
|
||||
|
||||
static uint32_t readU32(const uint8_t* data, size_t offset) {
|
||||
return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 |
|
||||
((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]);
|
||||
}
|
||||
|
||||
static void addRange(vector<uint32_t> &coverage, uint32_t start, uint32_t end) {
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("adding range %d-%d\n", start, end);
|
||||
#endif
|
||||
if (coverage.empty() || coverage.back() < start) {
|
||||
coverage.push_back(start);
|
||||
coverage.push_back(end);
|
||||
} else {
|
||||
coverage.back() = end;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the coverage information out of a Format 4 subtable, storing it in the coverage vector
|
||||
static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
|
||||
const size_t kSegCountOffset = 6;
|
||||
const size_t kEndCountOffset = 14;
|
||||
const size_t kHeaderSize = 16;
|
||||
const size_t kSegmentSize = 8; // total size of array elements for one segment
|
||||
if (kEndCountOffset > size) {
|
||||
return false;
|
||||
}
|
||||
size_t segCount = readU16(data, kSegCountOffset) >> 1;
|
||||
if (kHeaderSize + segCount * kSegmentSize > size) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < segCount; i++) {
|
||||
uint32_t end = readU16(data, kEndCountOffset + 2 * i);
|
||||
uint32_t start = readU16(data, kHeaderSize + 2 * (segCount + i));
|
||||
if (end < start) {
|
||||
// invalid segment range: size must be positive
|
||||
android_errorWriteLog(0x534e4554, "26413177");
|
||||
return false;
|
||||
}
|
||||
uint32_t rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i));
|
||||
if (rangeOffset == 0) {
|
||||
uint32_t delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i));
|
||||
if (((end + delta) & 0xffff) > end - start) {
|
||||
addRange(coverage, start, end + 1);
|
||||
} else {
|
||||
for (uint32_t j = start; j < end + 1; j++) {
|
||||
if (((j + delta) & 0xffff) != 0) {
|
||||
addRange(coverage, j, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t j = start; j < end + 1; j++) {
|
||||
uint32_t actualRangeOffset = kHeaderSize + 6 * segCount + rangeOffset +
|
||||
(i + j - start) * 2;
|
||||
if (actualRangeOffset + 2 > size) {
|
||||
// invalid rangeOffset is considered a "warning" by OpenType Sanitizer
|
||||
continue;
|
||||
}
|
||||
uint32_t glyphId = readU16(data, actualRangeOffset);
|
||||
if (glyphId != 0) {
|
||||
addRange(coverage, j, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the coverage information out of a Format 12 subtable, storing it in the coverage vector
|
||||
static bool getCoverageFormat12(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
|
||||
const size_t kNGroupsOffset = 12;
|
||||
const size_t kFirstGroupOffset = 16;
|
||||
const size_t kGroupSize = 12;
|
||||
const size_t kStartCharCodeOffset = 0;
|
||||
const size_t kEndCharCodeOffset = 4;
|
||||
const size_t kMaxNGroups = 0xfffffff0 / kGroupSize; // protection against overflow
|
||||
// For all values < kMaxNGroups, kFirstGroupOffset + nGroups * kGroupSize fits in 32 bits.
|
||||
if (kFirstGroupOffset > size) {
|
||||
return false;
|
||||
}
|
||||
uint32_t nGroups = readU32(data, kNGroupsOffset);
|
||||
if (nGroups >= kMaxNGroups || kFirstGroupOffset + nGroups * kGroupSize > size) {
|
||||
android_errorWriteLog(0x534e4554, "25645298");
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < nGroups; i++) {
|
||||
uint32_t groupOffset = kFirstGroupOffset + i * kGroupSize;
|
||||
uint32_t start = readU32(data, groupOffset + kStartCharCodeOffset);
|
||||
uint32_t end = readU32(data, groupOffset + kEndCharCodeOffset);
|
||||
if (end < start) {
|
||||
// invalid group range: size must be positive
|
||||
android_errorWriteLog(0x534e4554, "26413177");
|
||||
return false;
|
||||
}
|
||||
|
||||
// No need to read outside of Unicode code point range.
|
||||
if (start > MAX_UNICODE_CODE_POINT) {
|
||||
return true;
|
||||
}
|
||||
if (end > MAX_UNICODE_CODE_POINT) {
|
||||
// file is inclusive, vector is exclusive
|
||||
addRange(coverage, start, MAX_UNICODE_CODE_POINT + 1);
|
||||
return true;
|
||||
}
|
||||
addRange(coverage, start, end + 1); // file is inclusive, vector is exclusive
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Lower value has higher priority. 0 for the highest priority table.
|
||||
// kLowestPriority for unsupported tables.
|
||||
// This order comes from HarfBuzz's hb-ot-font.cc and needs to be kept in sync with it.
|
||||
constexpr uint8_t kLowestPriority = 255;
|
||||
uint8_t getTablePriority(uint16_t platformId, uint16_t encodingId) {
|
||||
if (platformId == 3 && encodingId == 10) {
|
||||
return 0;
|
||||
}
|
||||
if (platformId == 0 && encodingId == 6) {
|
||||
return 1;
|
||||
}
|
||||
if (platformId == 0 && encodingId == 4) {
|
||||
return 2;
|
||||
}
|
||||
if (platformId == 3 && encodingId == 1) {
|
||||
return 3;
|
||||
}
|
||||
if (platformId == 0 && encodingId == 3) {
|
||||
return 4;
|
||||
}
|
||||
if (platformId == 0 && encodingId == 2) {
|
||||
return 5;
|
||||
}
|
||||
if (platformId == 0 && encodingId == 1) {
|
||||
return 6;
|
||||
}
|
||||
if (platformId == 0 && encodingId == 0) {
|
||||
return 7;
|
||||
}
|
||||
// Tables other than above are not supported.
|
||||
return kLowestPriority;
|
||||
}
|
||||
|
||||
SparseBitSet CmapCoverage::getCoverage(const uint8_t* cmap_data, size_t cmap_size,
|
||||
bool* has_cmap_format14_subtable) {
|
||||
constexpr size_t kHeaderSize = 4;
|
||||
constexpr size_t kNumTablesOffset = 2;
|
||||
constexpr size_t kTableSize = 8;
|
||||
constexpr size_t kPlatformIdOffset = 0;
|
||||
constexpr size_t kEncodingIdOffset = 2;
|
||||
constexpr size_t kOffsetOffset = 4;
|
||||
constexpr size_t kFormatOffset = 0;
|
||||
constexpr uint32_t kInvalidOffset = UINT32_MAX;
|
||||
|
||||
if (kHeaderSize > cmap_size) {
|
||||
return SparseBitSet();
|
||||
}
|
||||
uint32_t numTables = readU16(cmap_data, kNumTablesOffset);
|
||||
if (kHeaderSize + numTables * kTableSize > cmap_size) {
|
||||
return SparseBitSet();
|
||||
}
|
||||
|
||||
uint32_t bestTableOffset = kInvalidOffset;
|
||||
uint16_t bestTableFormat = 0;
|
||||
uint8_t bestTablePriority = kLowestPriority;
|
||||
*has_cmap_format14_subtable = false;
|
||||
for (uint32_t i = 0; i < numTables; ++i) {
|
||||
const uint32_t tableHeadOffset = kHeaderSize + i * kTableSize;
|
||||
const uint16_t platformId = readU16(cmap_data, tableHeadOffset + kPlatformIdOffset);
|
||||
const uint16_t encodingId = readU16(cmap_data, tableHeadOffset + kEncodingIdOffset);
|
||||
const uint32_t offset = readU32(cmap_data, tableHeadOffset + kOffsetOffset);
|
||||
|
||||
if (offset > cmap_size - 2) {
|
||||
continue; // Invalid table: not enough space to read.
|
||||
}
|
||||
const uint16_t format = readU16(cmap_data, offset + kFormatOffset);
|
||||
|
||||
if (platformId == 0 /* Unicode */ && encodingId == 5 /* Variation Sequences */) {
|
||||
if (!(*has_cmap_format14_subtable) && format == 14) {
|
||||
*has_cmap_format14_subtable = true;
|
||||
} else {
|
||||
// Ignore the (0, 5) table if we have already seen another valid one or it's in a
|
||||
// format we don't understand.
|
||||
}
|
||||
} else {
|
||||
uint32_t length;
|
||||
uint32_t language;
|
||||
|
||||
if (format == 4) {
|
||||
constexpr size_t lengthOffset = 2;
|
||||
constexpr size_t languageOffset = 4;
|
||||
constexpr size_t minTableSize = languageOffset + 2;
|
||||
if (offset > cmap_size - minTableSize) {
|
||||
continue; // Invalid table: not enough space to read.
|
||||
}
|
||||
length = readU16(cmap_data, offset + lengthOffset);
|
||||
language = readU16(cmap_data, offset + languageOffset);
|
||||
} else if (format == 12) {
|
||||
constexpr size_t lengthOffset = 4;
|
||||
constexpr size_t languageOffset = 8;
|
||||
constexpr size_t minTableSize = languageOffset + 4;
|
||||
if (offset > cmap_size - minTableSize) {
|
||||
continue; // Invalid table: not enough space to read.
|
||||
}
|
||||
length = readU32(cmap_data, offset + lengthOffset);
|
||||
language = readU32(cmap_data, offset + languageOffset);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (length > cmap_size - offset) {
|
||||
continue; // Invalid table: table length is larger than whole cmap data size.
|
||||
}
|
||||
if (language != 0) {
|
||||
// Unsupported or invalid table: this is either a subtable for the Macintosh
|
||||
// platform (which we don't support), or an invalid subtable since language field
|
||||
// should be zero for non-Macintosh subtables.
|
||||
continue;
|
||||
}
|
||||
const uint8_t priority = getTablePriority(platformId, encodingId);
|
||||
if (priority < bestTablePriority) {
|
||||
bestTableOffset = offset;
|
||||
bestTablePriority = priority;
|
||||
bestTableFormat = format;
|
||||
}
|
||||
}
|
||||
if (*has_cmap_format14_subtable && bestTablePriority == 0 /* highest priority */) {
|
||||
// Already found the highest priority table and variation sequences table. No need to
|
||||
// look at remaining tables.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bestTableOffset == kInvalidOffset) {
|
||||
return SparseBitSet();
|
||||
}
|
||||
const uint8_t* tableData = cmap_data + bestTableOffset;
|
||||
const size_t tableSize = cmap_size - bestTableOffset;
|
||||
vector<uint32_t> coverageVec;
|
||||
bool success;
|
||||
if (bestTableFormat == 4) {
|
||||
success = getCoverageFormat4(coverageVec, tableData, tableSize);
|
||||
} else {
|
||||
success = getCoverageFormat12(coverageVec, tableData, tableSize);
|
||||
}
|
||||
if (success) {
|
||||
return SparseBitSet(&coverageVec.front(), coverageVec.size() >> 1);
|
||||
} else {
|
||||
return SparseBitSet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
89
engine/src/flutter/third_party/txt/libs/minikin/Emoji.cpp
vendored
Normal file
89
engine/src/flutter/third_party/txt/libs/minikin/Emoji.cpp
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <minikin/Emoji.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
bool isNewEmoji(uint32_t c) {
|
||||
// Emoji characters new in Unicode emoji 5.0.
|
||||
// From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
|
||||
// TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
|
||||
if (c < 0x1F6F7 || c > 0x1F9E6) {
|
||||
// Optimization for characters outside the new emoji range.
|
||||
return false;
|
||||
}
|
||||
return (0x1F6F7 <= c && c <= 0x1F6F8)
|
||||
|| c == 0x1F91F
|
||||
|| (0x1F928 <= c && c <= 0x1F92F)
|
||||
|| (0x1F931 <= c && c <= 0x1F932)
|
||||
|| c == 0x1F94C
|
||||
|| (0x1F95F <= c && c <= 0x1F96B)
|
||||
|| (0x1F992 <= c && c <= 0x1F997)
|
||||
|| (0x1F9D0 <= c && c <= 0x1F9E6);
|
||||
}
|
||||
|
||||
bool isEmoji(uint32_t c) {
|
||||
#if WIP_NEEDS_ICU_UPDATE
|
||||
return false;
|
||||
#else // WIP_NEEDS_ICU_UPDATE
|
||||
return isNewEmoji(c) || u_hasBinaryProperty(c, UCHAR_EMOJI);
|
||||
#endif // WIP_NEEDS_ICU_UPDATE
|
||||
}
|
||||
|
||||
bool isEmojiModifier(uint32_t c) {
|
||||
#if WIP_NEEDS_ICU_UPDATE
|
||||
return false;
|
||||
#else // WIP_NEEDS_ICU_UPDATE
|
||||
// Emoji modifier are not expected to change, so there's a small change we need to customize
|
||||
// this.
|
||||
return u_hasBinaryProperty(c, UCHAR_EMOJI_MODIFIER);
|
||||
#endif // WIP_NEEDS_ICU_UPDATE
|
||||
}
|
||||
|
||||
bool isEmojiBase(uint32_t c) {
|
||||
#if WIP_NEEDS_ICU_UPDATE
|
||||
return false;
|
||||
#else // WIP_NEEDS_ICU_UPDATE
|
||||
// These two characters were removed from Emoji_Modifier_Base in Emoji 4.0, but we need to keep
|
||||
// them as emoji modifier bases since there are fonts and user-generated text out there that
|
||||
// treats these as potential emoji bases.
|
||||
if (c == 0x1F91D || c == 0x1F93C) {
|
||||
return true;
|
||||
}
|
||||
// Emoji Modifier Base characters new in Unicode emoji 5.0.
|
||||
// From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
|
||||
// TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
|
||||
if (c == 0x1F91F
|
||||
|| (0x1F931 <= c && c <= 0x1F932)
|
||||
|| (0x1F9D1 <= c && c <= 0x1F9DD)) {
|
||||
return true;
|
||||
}
|
||||
return u_hasBinaryProperty(c, UCHAR_EMOJI_MODIFIER_BASE);
|
||||
#endif // WIP_NEEDS_ICU_UPDATE
|
||||
}
|
||||
|
||||
UCharDirection emojiBidiOverride(const void* /* context */, UChar32 c) {
|
||||
if (isNewEmoji(c)) {
|
||||
// All new emoji characters in Unicode 10.0 are of the bidi class ON.
|
||||
return U_OTHER_NEUTRAL;
|
||||
} else {
|
||||
return u_charDirection(c);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
471
engine/src/flutter/third_party/txt/libs/minikin/FontCollection.cpp
vendored
Normal file
471
engine/src/flutter/third_party/txt/libs/minikin/FontCollection.cpp
vendored
Normal file
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// #define VERBOSE_DEBUG
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <log/log.h>
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/unorm2.h"
|
||||
|
||||
#include "FontLanguage.h"
|
||||
#include "FontLanguageListCache.h"
|
||||
#include "MinikinInternal.h"
|
||||
#include <minikin/Emoji.h>
|
||||
#include <minikin/FontCollection.h>
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace minikin {
|
||||
|
||||
template <typename T>
|
||||
static inline T max(T a, T b) {
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
const uint32_t EMOJI_STYLE_VS = 0xFE0F;
|
||||
const uint32_t TEXT_STYLE_VS = 0xFE0E;
|
||||
|
||||
uint32_t FontCollection::sNextId = 0;
|
||||
|
||||
FontCollection::FontCollection(std::shared_ptr<FontFamily>&& typeface) : mMaxChar(0) {
|
||||
std::vector<std::shared_ptr<FontFamily>> typefaces;
|
||||
typefaces.push_back(typeface);
|
||||
init(typefaces);
|
||||
}
|
||||
|
||||
FontCollection::FontCollection(const vector<std::shared_ptr<FontFamily>>& typefaces) :
|
||||
mMaxChar(0) {
|
||||
init(typefaces);
|
||||
}
|
||||
|
||||
void FontCollection::init(const vector<std::shared_ptr<FontFamily>>& typefaces) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
mId = sNextId++;
|
||||
vector<uint32_t> lastChar;
|
||||
size_t nTypefaces = typefaces.size();
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("nTypefaces = %zd\n", nTypefaces);
|
||||
#endif
|
||||
const FontStyle defaultStyle;
|
||||
for (size_t i = 0; i < nTypefaces; i++) {
|
||||
const std::shared_ptr<FontFamily>& family = typefaces[i];
|
||||
if (family->getClosestMatch(defaultStyle).font == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const SparseBitSet& coverage = family->getCoverage();
|
||||
mFamilies.push_back(family); // emplace_back would be better
|
||||
if (family->hasVSTable()) {
|
||||
mVSFamilyVec.push_back(family);
|
||||
}
|
||||
mMaxChar = max(mMaxChar, coverage.length());
|
||||
lastChar.push_back(coverage.nextSetBit(0));
|
||||
|
||||
const std::unordered_set<AxisTag>& supportedAxes = family->supportedAxes();
|
||||
mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
|
||||
}
|
||||
nTypefaces = mFamilies.size();
|
||||
LOG_ALWAYS_FATAL_IF(nTypefaces == 0,
|
||||
"Font collection must have at least one valid typeface");
|
||||
LOG_ALWAYS_FATAL_IF(nTypefaces > 254,
|
||||
"Font collection may only have up to 254 font families.");
|
||||
size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage;
|
||||
// TODO: Use variation selector map for mRanges construction.
|
||||
// A font can have a glyph for a base code point and variation selector pair but no glyph for
|
||||
// the base code point without variation selector. The family won't be listed in the range in
|
||||
// this case.
|
||||
for (size_t i = 0; i < nPages; i++) {
|
||||
Range dummy;
|
||||
mRanges.push_back(dummy);
|
||||
Range* range = &mRanges.back();
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("i=%zd: range start = %zd\n", i, offset);
|
||||
#endif
|
||||
range->start = mFamilyVec.size();
|
||||
for (size_t j = 0; j < nTypefaces; j++) {
|
||||
if (lastChar[j] < (i + 1) << kLogCharsPerPage) {
|
||||
const std::shared_ptr<FontFamily>& family = mFamilies[j];
|
||||
mFamilyVec.push_back(static_cast<uint8_t>(j));
|
||||
uint32_t nextChar = family->getCoverage().nextSetBit((i + 1) << kLogCharsPerPage);
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("nextChar = %d (j = %zd)\n", nextChar, j);
|
||||
#endif
|
||||
lastChar[j] = nextChar;
|
||||
}
|
||||
}
|
||||
range->end = mFamilyVec.size();
|
||||
}
|
||||
// See the comment in Range for more details.
|
||||
LOG_ALWAYS_FATAL_IF(mFamilyVec.size() >= 0xFFFF,
|
||||
"Exceeded the maximum indexable cmap coverage.");
|
||||
}
|
||||
|
||||
// Special scores for the font fallback.
|
||||
const uint32_t kUnsupportedFontScore = 0;
|
||||
const uint32_t kFirstFontScore = UINT32_MAX;
|
||||
|
||||
// Calculates a font score.
|
||||
// The score of the font family is based on three subscores.
|
||||
// - Coverage Score: How well the font family covers the given character or variation sequence.
|
||||
// - Language Score: How well the font family is appropriate for the language.
|
||||
// - Variant Score: Whether the font family matches the variant. Note that this variant is not the
|
||||
// one in BCP47. This is our own font variant (e.g., elegant, compact).
|
||||
//
|
||||
// Then, there is a priority for these three subscores as follow:
|
||||
// Coverage Score > Language Score > Variant Score
|
||||
// The returned score reflects this priority order.
|
||||
//
|
||||
// Note that there are two special scores.
|
||||
// - kUnsupportedFontScore: When the font family doesn't support the variation sequence or even its
|
||||
// base character.
|
||||
// - kFirstFontScore: When the font is the first font family in the collection and it supports the
|
||||
// given character or variation sequence.
|
||||
uint32_t FontCollection::calcFamilyScore(uint32_t ch, uint32_t vs, int variant, uint32_t langListId,
|
||||
const std::shared_ptr<FontFamily>& fontFamily) const {
|
||||
|
||||
const uint32_t coverageScore = calcCoverageScore(ch, vs, fontFamily);
|
||||
if (coverageScore == kFirstFontScore || coverageScore == kUnsupportedFontScore) {
|
||||
// No need to calculate other scores.
|
||||
return coverageScore;
|
||||
}
|
||||
|
||||
const uint32_t languageScore = calcLanguageMatchingScore(langListId, *fontFamily);
|
||||
const uint32_t variantScore = calcVariantMatchingScore(variant, *fontFamily);
|
||||
|
||||
// Subscores are encoded into 31 bits representation to meet the subscore priority.
|
||||
// The highest 2 bits are for coverage score, then following 28 bits are for language score,
|
||||
// then the last 1 bit is for variant score.
|
||||
return coverageScore << 29 | languageScore << 1 | variantScore;
|
||||
}
|
||||
|
||||
// Calculates a font score based on variation sequence coverage.
|
||||
// - Returns kUnsupportedFontScore if the font doesn't support the variation sequence or its base
|
||||
// character.
|
||||
// - Returns kFirstFontScore if the font family is the first font family in the collection and it
|
||||
// supports the given character or variation sequence.
|
||||
// - Returns 3 if the font family supports the variation sequence.
|
||||
// - Returns 2 if the vs is a color variation selector (U+FE0F) and if the font is an emoji font.
|
||||
// - Returns 2 if the vs is a text variation selector (U+FE0E) and if the font is not an emoji font.
|
||||
// - Returns 1 if the variation selector is not specified or if the font family only supports the
|
||||
// variation sequence's base character.
|
||||
uint32_t FontCollection::calcCoverageScore(uint32_t ch, uint32_t vs,
|
||||
const std::shared_ptr<FontFamily>& fontFamily) const {
|
||||
const bool hasVSGlyph = (vs != 0) && fontFamily->hasGlyph(ch, vs);
|
||||
if (!hasVSGlyph && !fontFamily->getCoverage().get(ch)) {
|
||||
// The font doesn't support either variation sequence or even the base character.
|
||||
return kUnsupportedFontScore;
|
||||
}
|
||||
|
||||
if ((vs == 0 || hasVSGlyph) && mFamilies[0] == fontFamily) {
|
||||
// If the first font family supports the given character or variation sequence, always use
|
||||
// it.
|
||||
return kFirstFontScore;
|
||||
}
|
||||
|
||||
if (vs == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (hasVSGlyph) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (vs == EMOJI_STYLE_VS || vs == TEXT_STYLE_VS) {
|
||||
const FontLanguages& langs = FontLanguageListCache::getById(fontFamily->langId());
|
||||
bool hasEmojiFlag = false;
|
||||
for (size_t i = 0; i < langs.size(); ++i) {
|
||||
if (langs[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
|
||||
hasEmojiFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vs == EMOJI_STYLE_VS) {
|
||||
return hasEmojiFlag ? 2 : 1;
|
||||
} else { // vs == TEXT_STYLE_VS
|
||||
return hasEmojiFlag ? 1 : 2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Calculate font scores based on the script matching, subtag matching and primary langauge matching.
|
||||
//
|
||||
// 1. If only the font's language matches or there is no matches between requested font and
|
||||
// supported font, then the font obtains a score of 0.
|
||||
// 2. Without a match in language, considering subtag may change font's EmojiStyle over script,
|
||||
// a match in subtag gets a score of 2 and a match in scripts gains a score of 1.
|
||||
// 3. Regarding to two elements matchings, language-and-subtag matching has a score of 4, while
|
||||
// language-and-script obtains a socre of 3 with the same reason above.
|
||||
//
|
||||
// If two languages in the requested list have the same language score, the font matching with
|
||||
// higher priority language gets a higher score. For example, in the case the user requested
|
||||
// language list is "ja-Jpan,en-Latn". The score of for the font of "ja-Jpan" gets a higher score
|
||||
// than the font of "en-Latn".
|
||||
//
|
||||
// To achieve score calculation with priorities, the language score is determined as follows:
|
||||
// LanguageScore = s(0) * 5^(m - 1) + s(1) * 5^(m - 2) + ... + s(m - 2) * 5 + s(m - 1)
|
||||
// Here, m is the maximum number of languages to be compared, and s(i) is the i-th language's
|
||||
// matching score. The possible values of s(i) are 0, 1, 2, 3 and 4.
|
||||
uint32_t FontCollection::calcLanguageMatchingScore(
|
||||
uint32_t userLangListId, const FontFamily& fontFamily) {
|
||||
const FontLanguages& langList = FontLanguageListCache::getById(userLangListId);
|
||||
const FontLanguages& fontLanguages = FontLanguageListCache::getById(fontFamily.langId());
|
||||
|
||||
const size_t maxCompareNum = std::min(langList.size(), FONT_LANGUAGES_LIMIT);
|
||||
uint32_t score = 0;
|
||||
for (size_t i = 0; i < maxCompareNum; ++i) {
|
||||
score = score * 5u + langList[i].calcScoreFor(fontLanguages);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
// Calculates a font score based on variant ("compact" or "elegant") matching.
|
||||
// - Returns 1 if the font doesn't have variant or the variant matches with the text style.
|
||||
// - No score if the font has a variant but it doesn't match with the text style.
|
||||
uint32_t FontCollection::calcVariantMatchingScore(int variant, const FontFamily& fontFamily) {
|
||||
return (fontFamily.variant() == 0 || fontFamily.variant() == variant) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Implement heuristic for choosing best-match font. Here are the rules:
|
||||
// 1. If first font in the collection has the character, it wins.
|
||||
// 2. Calculate a score for the font family. See comments in calcFamilyScore for the detail.
|
||||
// 3. Highest score wins, with ties resolved to the first font.
|
||||
// This method never returns nullptr.
|
||||
const std::shared_ptr<FontFamily>& FontCollection::getFamilyForChar(uint32_t ch, uint32_t vs,
|
||||
uint32_t langListId, int variant) const {
|
||||
if (ch >= mMaxChar) {
|
||||
return mFamilies[0];
|
||||
}
|
||||
|
||||
Range range = mRanges[ch >> kLogCharsPerPage];
|
||||
|
||||
if (vs != 0) {
|
||||
range = { 0, static_cast<uint16_t>(mFamilies.size()) };
|
||||
}
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("querying range %zd:%zd\n", range.start, range.end);
|
||||
#endif
|
||||
int bestFamilyIndex = -1;
|
||||
uint32_t bestScore = kUnsupportedFontScore;
|
||||
for (size_t i = range.start; i < range.end; i++) {
|
||||
const std::shared_ptr<FontFamily>& family =
|
||||
vs == 0 ? mFamilies[mFamilyVec[i]] : mFamilies[i];
|
||||
const uint32_t score = calcFamilyScore(ch, vs, variant, langListId, family);
|
||||
if (score == kFirstFontScore) {
|
||||
// If the first font family supports the given character or variation sequence, always
|
||||
// use it.
|
||||
return family;
|
||||
}
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestFamilyIndex = i;
|
||||
}
|
||||
}
|
||||
if (bestFamilyIndex == -1) {
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
UChar decomposed[4];
|
||||
int len = unorm2_getRawDecomposition(normalizer, ch, decomposed, 4, &errorCode);
|
||||
if (U_SUCCESS(errorCode) && len > 0) {
|
||||
int off = 0;
|
||||
U16_NEXT_UNSAFE(decomposed, off, ch);
|
||||
return getFamilyForChar(ch, vs, langListId, variant);
|
||||
}
|
||||
}
|
||||
return mFamilies[0];
|
||||
}
|
||||
return vs == 0 ? mFamilies[mFamilyVec[bestFamilyIndex]] : mFamilies[bestFamilyIndex];
|
||||
}
|
||||
|
||||
const uint32_t NBSP = 0x00A0;
|
||||
const uint32_t SOFT_HYPHEN = 0x00AD;
|
||||
const uint32_t ZWJ = 0x200C;
|
||||
const uint32_t ZWNJ = 0x200D;
|
||||
const uint32_t HYPHEN = 0x2010;
|
||||
const uint32_t NB_HYPHEN = 0x2011;
|
||||
const uint32_t NNBSP = 0x202F;
|
||||
const uint32_t FEMALE_SIGN = 0x2640;
|
||||
const uint32_t MALE_SIGN = 0x2642;
|
||||
const uint32_t STAFF_OF_AESCULAPIUS = 0x2695;
|
||||
|
||||
// Characters where we want to continue using existing font run instead of
|
||||
// recomputing the best match in the fallback list.
|
||||
static const uint32_t stickyWhitelist[] = {
|
||||
'!', ',', '-', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ,
|
||||
HYPHEN, NB_HYPHEN, NNBSP, FEMALE_SIGN, MALE_SIGN, STAFF_OF_AESCULAPIUS };
|
||||
|
||||
static bool isStickyWhitelisted(uint32_t c) {
|
||||
for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) {
|
||||
if (stickyWhitelist[i] == c) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isVariationSelector(uint32_t c) {
|
||||
return (0xFE00 <= c && c <= 0xFE0F) || (0xE0100 <= c && c <= 0xE01EF);
|
||||
}
|
||||
|
||||
bool FontCollection::hasVariationSelector(uint32_t baseCodepoint,
|
||||
uint32_t variationSelector) const {
|
||||
if (!isVariationSelector(variationSelector)) {
|
||||
return false;
|
||||
}
|
||||
if (baseCodepoint >= mMaxChar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
|
||||
// Currently mRanges can not be used here since it isn't aware of the variation sequence.
|
||||
for (size_t i = 0; i < mVSFamilyVec.size(); i++) {
|
||||
if (mVSFamilyVec[i]->hasGlyph(baseCodepoint, variationSelector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Even if there is no cmap format 14 subtable entry for the given sequence, should return true
|
||||
// for <char, text presentation selector> case since we have special fallback rule for the
|
||||
// sequence. Note that we don't need to restrict this to already standardized variation
|
||||
// sequences, since Unicode is adding variation sequences more frequently now and may even move
|
||||
// towards allowing text and emoji variation selectors on any character.
|
||||
if (variationSelector == TEXT_STYLE_VS) {
|
||||
for (size_t i = 0; i < mFamilies.size(); ++i) {
|
||||
if (!mFamilies[i]->isColorEmojiFamily() && mFamilies[i]->hasGlyph(baseCodepoint, 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style,
|
||||
vector<Run>* result) const {
|
||||
const uint32_t langListId = style.getLanguageListId();
|
||||
int variant = style.getVariant();
|
||||
const FontFamily* lastFamily = nullptr;
|
||||
Run* run = NULL;
|
||||
|
||||
if (string_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t kEndOfString = 0xFFFFFFFF;
|
||||
|
||||
uint32_t nextCh = 0;
|
||||
uint32_t prevCh = 0;
|
||||
size_t nextUtf16Pos = 0;
|
||||
size_t readLength = 0;
|
||||
U16_NEXT(string, readLength, string_size, nextCh);
|
||||
|
||||
do {
|
||||
const uint32_t ch = nextCh;
|
||||
const size_t utf16Pos = nextUtf16Pos;
|
||||
nextUtf16Pos = readLength;
|
||||
if (readLength < string_size) {
|
||||
U16_NEXT(string, readLength, string_size, nextCh);
|
||||
} else {
|
||||
nextCh = kEndOfString;
|
||||
}
|
||||
|
||||
bool shouldContinueRun = false;
|
||||
if (lastFamily != nullptr) {
|
||||
if (isStickyWhitelisted(ch)) {
|
||||
// Continue using existing font as long as it has coverage and is whitelisted
|
||||
shouldContinueRun = lastFamily->getCoverage().get(ch);
|
||||
} else if (ch == SOFT_HYPHEN || isVariationSelector(ch)) {
|
||||
// Always continue if the character is the soft hyphen or a variation selector.
|
||||
shouldContinueRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldContinueRun) {
|
||||
const std::shared_ptr<FontFamily>& family = getFamilyForChar(
|
||||
ch, isVariationSelector(nextCh) ? nextCh : 0, langListId, variant);
|
||||
if (utf16Pos == 0 || family.get() != lastFamily) {
|
||||
size_t start = utf16Pos;
|
||||
// Workaround for combining marks and emoji modifiers until we implement
|
||||
// per-cluster font selection: if a combining mark or an emoji modifier is found in
|
||||
// a different font that also supports the previous character, attach previous
|
||||
// character to the new run. U+20E3 COMBINING ENCLOSING KEYCAP, used in emoji, is
|
||||
// handled properly by this since it's a combining mark too.
|
||||
if (utf16Pos != 0 &&
|
||||
((U_GET_GC_MASK(ch) & U_GC_M_MASK) != 0 ||
|
||||
(isEmojiModifier(ch) && isEmojiBase(prevCh))) &&
|
||||
family != nullptr && family->getCoverage().get(prevCh)) {
|
||||
const size_t prevChLength = U16_LENGTH(prevCh);
|
||||
run->end -= prevChLength;
|
||||
if (run->start == run->end) {
|
||||
result->pop_back();
|
||||
}
|
||||
start -= prevChLength;
|
||||
}
|
||||
result->push_back({family->getClosestMatch(style), static_cast<int>(start), 0});
|
||||
run = &result->back();
|
||||
lastFamily = family.get();
|
||||
}
|
||||
}
|
||||
prevCh = ch;
|
||||
run->end = nextUtf16Pos; // exclusive
|
||||
} while (nextCh != kEndOfString);
|
||||
}
|
||||
|
||||
FakedFont FontCollection::baseFontFaked(FontStyle style) {
|
||||
return mFamilies[0]->getClosestMatch(style);
|
||||
}
|
||||
|
||||
std::shared_ptr<FontCollection> FontCollection::createCollectionWithVariation(
|
||||
const std::vector<FontVariation>& variations) {
|
||||
if (variations.empty() || mSupportedAxes.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool hasSupportedAxis = false;
|
||||
for (const FontVariation& variation : variations) {
|
||||
if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
|
||||
hasSupportedAxis = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasSupportedAxis) {
|
||||
// None of variation axes are supported by this font collection.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<FontFamily> > families;
|
||||
for (const std::shared_ptr<FontFamily>& family : mFamilies) {
|
||||
std::shared_ptr<FontFamily> newFamily = family->createFamilyWithVariation(variations);
|
||||
if (newFamily) {
|
||||
families.push_back(newFamily);
|
||||
} else {
|
||||
families.push_back(family);
|
||||
}
|
||||
}
|
||||
|
||||
return std::shared_ptr<FontCollection>(new FontCollection(families));
|
||||
}
|
||||
|
||||
uint32_t FontCollection::getId() const {
|
||||
return mId;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
246
engine/src/flutter/third_party/txt/libs/minikin/FontFamily.cpp
vendored
Normal file
246
engine/src/flutter/third_party/txt/libs/minikin/FontFamily.cpp
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <log/log.h>
|
||||
#include <utils/JenkinsHash.h>
|
||||
|
||||
#include <hb.h>
|
||||
#include <hb-ot.h>
|
||||
|
||||
#include "FontLanguage.h"
|
||||
#include "FontLanguageListCache.h"
|
||||
#include "FontUtils.h"
|
||||
#include "HbFontCache.h"
|
||||
#include "MinikinInternal.h"
|
||||
#include <minikin/CmapCoverage.h>
|
||||
#include <minikin/MinikinFont.h>
|
||||
#include <minikin/FontFamily.h>
|
||||
#include <minikin/MinikinFont.h>
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace minikin {
|
||||
|
||||
FontStyle::FontStyle(int variant, int weight, bool italic)
|
||||
: FontStyle(FontLanguageListCache::kEmptyListId, variant, weight, italic) {
|
||||
}
|
||||
|
||||
FontStyle::FontStyle(uint32_t languageListId, int variant, int weight, bool italic)
|
||||
: bits(pack(variant, weight, italic)), mLanguageListId(languageListId) {
|
||||
}
|
||||
|
||||
android::hash_t FontStyle::hash() const {
|
||||
uint32_t hash = android::JenkinsHashMix(0, bits);
|
||||
hash = android::JenkinsHashMix(hash, mLanguageListId);
|
||||
return android::JenkinsHashWhiten(hash);
|
||||
}
|
||||
|
||||
// static
|
||||
uint32_t FontStyle::registerLanguageList(const std::string& languages) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
return FontLanguageListCache::getId(languages);
|
||||
}
|
||||
|
||||
// static
|
||||
uint32_t FontStyle::pack(int variant, int weight, bool italic) {
|
||||
return (weight & kWeightMask) | (italic ? kItalicMask : 0) | (variant << kVariantShift);
|
||||
}
|
||||
|
||||
Font::Font(const std::shared_ptr<MinikinFont>& typeface, FontStyle style)
|
||||
: typeface(typeface), style(style) {
|
||||
}
|
||||
|
||||
Font::Font(std::shared_ptr<MinikinFont>&& typeface, FontStyle style)
|
||||
: typeface(typeface), style(style) {
|
||||
}
|
||||
|
||||
std::unordered_set<AxisTag> Font::getSupportedAxesLocked() const {
|
||||
const uint32_t fvarTag = MinikinFont::MakeTag('f', 'v', 'a', 'r');
|
||||
HbBlob fvarTable(getFontTable(typeface.get(), fvarTag));
|
||||
if (fvarTable.size() == 0) {
|
||||
return std::unordered_set<AxisTag>();
|
||||
}
|
||||
|
||||
std::unordered_set<AxisTag> supportedAxes;
|
||||
analyzeAxes(fvarTable.get(), fvarTable.size(), &supportedAxes);
|
||||
return supportedAxes;
|
||||
}
|
||||
|
||||
Font::Font(Font&& o) {
|
||||
typeface = std::move(o.typeface);
|
||||
style = o.style;
|
||||
o.typeface = nullptr;
|
||||
}
|
||||
|
||||
Font::Font(const Font& o) {
|
||||
typeface = o.typeface;
|
||||
style = o.style;
|
||||
}
|
||||
|
||||
// static
|
||||
FontFamily::FontFamily(std::vector<Font>&& fonts) : FontFamily(0 /* variant */, std::move(fonts)) {
|
||||
}
|
||||
|
||||
FontFamily::FontFamily(int variant, std::vector<Font>&& fonts)
|
||||
: FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts)) {
|
||||
}
|
||||
|
||||
FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
|
||||
: mLangId(langId), mVariant(variant), mFonts(std::move(fonts)), mHasVSTable(false) {
|
||||
computeCoverage();
|
||||
}
|
||||
|
||||
bool FontFamily::analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
|
||||
bool* italic) {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
const uint32_t os2Tag = MinikinFont::MakeTag('O', 'S', '/', '2');
|
||||
HbBlob os2Table(getFontTable(typeface.get(), os2Tag));
|
||||
if (os2Table.get() == nullptr) return false;
|
||||
return ::minikin::analyzeStyle(os2Table.get(), os2Table.size(), weight, italic);
|
||||
}
|
||||
|
||||
// Compute a matching metric between two styles - 0 is an exact match
|
||||
static int computeMatch(FontStyle style1, FontStyle style2) {
|
||||
if (style1 == style2) return 0;
|
||||
int score = abs(style1.getWeight() - style2.getWeight());
|
||||
if (style1.getItalic() != style2.getItalic()) {
|
||||
score += 2;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
static FontFakery computeFakery(FontStyle wanted, FontStyle actual) {
|
||||
// If desired weight is semibold or darker, and 2 or more grades
|
||||
// higher than actual (for example, medium 500 -> bold 700), then
|
||||
// select fake bold.
|
||||
int wantedWeight = wanted.getWeight();
|
||||
bool isFakeBold = wantedWeight >= 6 && (wantedWeight - actual.getWeight()) >= 2;
|
||||
bool isFakeItalic = wanted.getItalic() && !actual.getItalic();
|
||||
return FontFakery(isFakeBold, isFakeItalic);
|
||||
}
|
||||
|
||||
FakedFont FontFamily::getClosestMatch(FontStyle style) const {
|
||||
const Font* bestFont = nullptr;
|
||||
int bestMatch = 0;
|
||||
for (size_t i = 0; i < mFonts.size(); i++) {
|
||||
const Font& font = mFonts[i];
|
||||
int match = computeMatch(font.style, style);
|
||||
if (i == 0 || match < bestMatch) {
|
||||
bestFont = &font;
|
||||
bestMatch = match;
|
||||
}
|
||||
}
|
||||
if (bestFont != nullptr) {
|
||||
return FakedFont{ bestFont->typeface.get(), computeFakery(style, bestFont->style) };
|
||||
}
|
||||
return FakedFont{ nullptr, FontFakery() };
|
||||
}
|
||||
|
||||
bool FontFamily::isColorEmojiFamily() const {
|
||||
const FontLanguages& languageList = FontLanguageListCache::getById(mLangId);
|
||||
for (size_t i = 0; i < languageList.size(); ++i) {
|
||||
if (languageList[i].getEmojiStyle() == FontLanguage::EMSTYLE_EMOJI) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FontFamily::computeCoverage() {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
const FontStyle defaultStyle;
|
||||
const MinikinFont* typeface = getClosestMatch(defaultStyle).font;
|
||||
const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p');
|
||||
HbBlob cmapTable(getFontTable(typeface, cmapTag));
|
||||
if (cmapTable.get() == nullptr) {
|
||||
ALOGE("Could not get cmap table size!\n");
|
||||
return;
|
||||
}
|
||||
mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mHasVSTable);
|
||||
|
||||
for (size_t i = 0; i < mFonts.size(); ++i) {
|
||||
std::unordered_set<AxisTag> supportedAxes = mFonts[i].getSupportedAxesLocked();
|
||||
mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
|
||||
}
|
||||
}
|
||||
|
||||
bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
|
||||
assertMinikinLocked();
|
||||
if (variationSelector != 0 && !mHasVSTable) {
|
||||
// Early exit if the variation selector is specified but the font doesn't have a cmap format
|
||||
// 14 subtable.
|
||||
return false;
|
||||
}
|
||||
|
||||
const FontStyle defaultStyle;
|
||||
hb_font_t* font = getHbFontLocked(getClosestMatch(defaultStyle).font);
|
||||
uint32_t unusedGlyph;
|
||||
bool result = hb_font_get_glyph(font, codepoint, variationSelector, &unusedGlyph);
|
||||
hb_font_destroy(font);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
|
||||
const std::vector<FontVariation>& variations) const {
|
||||
if (variations.empty() || mSupportedAxes.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool hasSupportedAxis = false;
|
||||
for (const FontVariation& variation : variations) {
|
||||
if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
|
||||
hasSupportedAxis = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasSupportedAxis) {
|
||||
// None of variation axes are suppored by this family.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Font> fonts;
|
||||
for (const Font& font : mFonts) {
|
||||
bool supportedVariations = false;
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
std::unordered_set<AxisTag> supportedAxes = font.getSupportedAxesLocked();
|
||||
if (!supportedAxes.empty()) {
|
||||
for (const FontVariation& variation : variations) {
|
||||
if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) {
|
||||
supportedVariations = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::shared_ptr<MinikinFont> minikinFont;
|
||||
if (supportedVariations) {
|
||||
minikinFont = font.typeface->createFontWithVariation(variations);
|
||||
}
|
||||
if (minikinFont == nullptr) {
|
||||
minikinFont = font.typeface;
|
||||
}
|
||||
fonts.push_back(Font(std::move(minikinFont), font.style));
|
||||
}
|
||||
|
||||
return std::shared_ptr<FontFamily>(new FontFamily(mLangId, mVariant, std::move(fonts)));
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
345
engine/src/flutter/third_party/txt/libs/minikin/FontLanguage.cpp
vendored
Normal file
345
engine/src/flutter/third_party/txt/libs/minikin/FontLanguage.cpp
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include "FontLanguage.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <hb.h>
|
||||
#include <string.h>
|
||||
#include <unicode/uloc.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
#define SCRIPT_TAG(c1, c2, c3, c4) \
|
||||
(((uint32_t)(c1)) << 24 | ((uint32_t)(c2)) << 16 | ((uint32_t)(c3)) << 8 | \
|
||||
((uint32_t)(c4)))
|
||||
|
||||
// Check if a language code supports emoji according to its subtag
|
||||
static bool isEmojiSubtag(const char* buf, size_t bufLen, const char* subtag, size_t subtagLen) {
|
||||
if (bufLen < subtagLen) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(buf, subtag, subtagLen) != 0) {
|
||||
return false; // no match between two strings
|
||||
}
|
||||
return (bufLen == subtagLen || buf[subtagLen] == '\0' ||
|
||||
buf[subtagLen] == '-' || buf[subtagLen] == '_');
|
||||
}
|
||||
|
||||
// Pack the three letter code into 15 bits and stored to 16 bit integer. The highest bit is 0.
|
||||
// For the region code, the letters must be all digits in three letter case, so the number of
|
||||
// possible values are 10. For the language code, the letters must be all small alphabets, so the
|
||||
// number of possible values are 26. Thus, 5 bits are sufficient for each case and we can pack the
|
||||
// three letter language code or region code to 15 bits.
|
||||
//
|
||||
// In case of two letter code, use fullbit(0x1f) for the first letter instead.
|
||||
static uint16_t packLanguageOrRegion(const char* c, size_t length, uint8_t twoLetterBase,
|
||||
uint8_t threeLetterBase) {
|
||||
if (length == 2) {
|
||||
return 0x7c00u | // 0x1fu << 10
|
||||
(uint16_t)(c[0] - twoLetterBase) << 5 |
|
||||
(uint16_t)(c[1] - twoLetterBase);
|
||||
} else {
|
||||
return ((uint16_t)(c[0] - threeLetterBase) << 10) |
|
||||
(uint16_t)(c[1] - threeLetterBase) << 5 |
|
||||
(uint16_t)(c[2] - threeLetterBase);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t unpackLanguageOrRegion(uint16_t in, char* out, uint8_t twoLetterBase,
|
||||
uint8_t threeLetterBase) {
|
||||
uint8_t first = (in >> 10) & 0x1f;
|
||||
uint8_t second = (in >> 5) & 0x1f;
|
||||
uint8_t third = in & 0x1f;
|
||||
|
||||
if (first == 0x1f) {
|
||||
out[0] = second + twoLetterBase;
|
||||
out[1] = third + twoLetterBase;
|
||||
return 2;
|
||||
} else {
|
||||
out[0] = first + threeLetterBase;
|
||||
out[1] = second + threeLetterBase;
|
||||
out[2] = third + threeLetterBase;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the next '-' or '_' index from startOffset position. If not found, returns bufferLength.
|
||||
static size_t nextDelimiterIndex(const char* buffer, size_t bufferLength, size_t startOffset) {
|
||||
for (size_t i = startOffset; i < bufferLength; ++i) {
|
||||
if (buffer[i] == '-' || buffer[i] == '_') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return bufferLength;
|
||||
}
|
||||
|
||||
static inline bool isLowercase(char c) {
|
||||
return 'a' <= c && c <= 'z';
|
||||
}
|
||||
|
||||
static inline bool isUppercase(char c) {
|
||||
return 'A' <= c && c <= 'Z';
|
||||
}
|
||||
|
||||
static inline bool isDigit(char c) {
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
// Returns true if the buffer is valid for language code.
|
||||
static inline bool isValidLanguageCode(const char* buffer, size_t length) {
|
||||
if (length != 2 && length != 3) return false;
|
||||
if (!isLowercase(buffer[0])) return false;
|
||||
if (!isLowercase(buffer[1])) return false;
|
||||
if (length == 3 && !isLowercase(buffer[2])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if buffer is valid for script code. The length of buffer must be 4.
|
||||
static inline bool isValidScriptCode(const char* buffer) {
|
||||
return isUppercase(buffer[0]) && isLowercase(buffer[1]) && isLowercase(buffer[2]) &&
|
||||
isLowercase(buffer[3]);
|
||||
}
|
||||
|
||||
// Returns true if the buffer is valid for region code.
|
||||
static inline bool isValidRegionCode(const char* buffer, size_t length) {
|
||||
return (length == 2 && isUppercase(buffer[0]) && isUppercase(buffer[1])) ||
|
||||
(length == 3 && isDigit(buffer[0]) && isDigit(buffer[1]) && isDigit(buffer[2]));
|
||||
}
|
||||
|
||||
// Parse BCP 47 language identifier into internal structure
|
||||
FontLanguage::FontLanguage(const char* buf, size_t length) : FontLanguage() {
|
||||
size_t firstDelimiterPos = nextDelimiterIndex(buf, length, 0);
|
||||
if (isValidLanguageCode(buf, firstDelimiterPos)) {
|
||||
mLanguage = packLanguageOrRegion(buf, firstDelimiterPos, 'a', 'a');
|
||||
} else {
|
||||
// We don't understand anything other than two-letter or three-letter
|
||||
// language codes, so we skip parsing the rest of the string.
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstDelimiterPos == length) {
|
||||
mHbLanguage = hb_language_from_string(getString().c_str(), -1);
|
||||
return; // Language code only.
|
||||
}
|
||||
|
||||
size_t nextComponentStartPos = firstDelimiterPos + 1;
|
||||
size_t nextDelimiterPos = nextDelimiterIndex(buf, length, nextComponentStartPos);
|
||||
size_t componentLength = nextDelimiterPos - nextComponentStartPos;
|
||||
|
||||
if (componentLength == 4) {
|
||||
// Possibly script code.
|
||||
const char* p = buf + nextComponentStartPos;
|
||||
if (isValidScriptCode(p)) {
|
||||
mScript = SCRIPT_TAG(p[0], p[1], p[2], p[3]);
|
||||
mSubScriptBits = scriptToSubScriptBits(mScript);
|
||||
}
|
||||
|
||||
if (nextDelimiterPos == length) {
|
||||
mHbLanguage = hb_language_from_string(getString().c_str(), -1);
|
||||
mEmojiStyle = resolveEmojiStyle(buf, length, mScript);
|
||||
return; // No region code.
|
||||
}
|
||||
|
||||
nextComponentStartPos = nextDelimiterPos + 1;
|
||||
nextDelimiterPos = nextDelimiterIndex(buf, length, nextComponentStartPos);
|
||||
componentLength = nextDelimiterPos - nextComponentStartPos;
|
||||
}
|
||||
|
||||
if (componentLength == 2 || componentLength == 3) {
|
||||
// Possibly region code.
|
||||
const char* p = buf + nextComponentStartPos;
|
||||
if (isValidRegionCode(p, componentLength)) {
|
||||
mRegion = packLanguageOrRegion(p, componentLength, 'A', '0');
|
||||
}
|
||||
}
|
||||
|
||||
mHbLanguage = hb_language_from_string(getString().c_str(), -1);
|
||||
mEmojiStyle = resolveEmojiStyle(buf, length, mScript);
|
||||
}
|
||||
|
||||
// static
|
||||
FontLanguage::EmojiStyle FontLanguage::resolveEmojiStyle(const char* buf, size_t length,
|
||||
uint32_t script) {
|
||||
// First, lookup emoji subtag.
|
||||
// 10 is the length of "-u-em-text", which is the shortest emoji subtag,
|
||||
// unnecessary comparison can be avoided if total length is smaller than 10.
|
||||
const size_t kMinSubtagLength = 10;
|
||||
if (length >= kMinSubtagLength) {
|
||||
static const char kPrefix[] = "-u-em-";
|
||||
const char *pos = std::search(buf, buf + length, kPrefix, kPrefix + strlen(kPrefix));
|
||||
if (pos != buf + length) { // found
|
||||
pos += strlen(kPrefix);
|
||||
const size_t remainingLength = length - (pos - buf);
|
||||
if (isEmojiSubtag(pos, remainingLength, "emoji", 5)){
|
||||
return EMSTYLE_EMOJI;
|
||||
} else if (isEmojiSubtag(pos, remainingLength, "text", 4)){
|
||||
return EMSTYLE_TEXT;
|
||||
} else if (isEmojiSubtag(pos, remainingLength, "default", 7)){
|
||||
return EMSTYLE_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no emoji subtag was provided, resolve the emoji style from script code.
|
||||
if (script == SCRIPT_TAG('Z', 's', 'y', 'e')) {
|
||||
return EMSTYLE_EMOJI;
|
||||
} else if (script == SCRIPT_TAG('Z', 's', 'y', 'm')) {
|
||||
return EMSTYLE_TEXT;
|
||||
}
|
||||
|
||||
return EMSTYLE_EMPTY;
|
||||
}
|
||||
|
||||
//static
|
||||
uint8_t FontLanguage::scriptToSubScriptBits(uint32_t script) {
|
||||
uint8_t subScriptBits = 0u;
|
||||
switch (script) {
|
||||
case SCRIPT_TAG('B', 'o', 'p', 'o'):
|
||||
subScriptBits = kBopomofoFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'a', 'n', 'g'):
|
||||
subScriptBits = kHangulFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'a', 'n', 'b'):
|
||||
// Bopomofo is almost exclusively used in Taiwan.
|
||||
subScriptBits = kHanFlag | kBopomofoFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'a', 'n', 'i'):
|
||||
subScriptBits = kHanFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'a', 'n', 's'):
|
||||
subScriptBits = kHanFlag | kSimplifiedChineseFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'a', 'n', 't'):
|
||||
subScriptBits = kHanFlag | kTraditionalChineseFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'i', 'r', 'a'):
|
||||
subScriptBits = kHiraganaFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('H', 'r', 'k', 't'):
|
||||
subScriptBits = kKatakanaFlag | kHiraganaFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('J', 'p', 'a', 'n'):
|
||||
subScriptBits = kHanFlag | kKatakanaFlag | kHiraganaFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('K', 'a', 'n', 'a'):
|
||||
subScriptBits = kKatakanaFlag;
|
||||
break;
|
||||
case SCRIPT_TAG('K', 'o', 'r', 'e'):
|
||||
subScriptBits = kHanFlag | kHangulFlag;
|
||||
break;
|
||||
}
|
||||
return subScriptBits;
|
||||
}
|
||||
|
||||
std::string FontLanguage::getString() const {
|
||||
if (isUnsupported()) {
|
||||
return "und";
|
||||
}
|
||||
char buf[16];
|
||||
size_t i = unpackLanguageOrRegion(mLanguage, buf, 'a', 'a');
|
||||
if (mScript != 0) {
|
||||
buf[i++] = '-';
|
||||
buf[i++] = (mScript >> 24) & 0xFFu;
|
||||
buf[i++] = (mScript >> 16) & 0xFFu;
|
||||
buf[i++] = (mScript >> 8) & 0xFFu;
|
||||
buf[i++] = mScript & 0xFFu;
|
||||
}
|
||||
if (mRegion != INVALID_CODE) {
|
||||
buf[i++] = '-';
|
||||
i += unpackLanguageOrRegion(mRegion, buf + i, 'A', '0');
|
||||
}
|
||||
return std::string(buf, i);
|
||||
}
|
||||
|
||||
bool FontLanguage::isEqualScript(const FontLanguage& other) const {
|
||||
return other.mScript == mScript;
|
||||
}
|
||||
|
||||
// static
|
||||
bool FontLanguage::supportsScript(uint8_t providedBits, uint8_t requestedBits) {
|
||||
return requestedBits != 0 && (providedBits & requestedBits) == requestedBits;
|
||||
}
|
||||
|
||||
bool FontLanguage::supportsHbScript(hb_script_t script) const {
|
||||
static_assert(SCRIPT_TAG('J', 'p', 'a', 'n') == HB_TAG('J', 'p', 'a', 'n'),
|
||||
"The Minikin script and HarfBuzz hb_script_t have different encodings.");
|
||||
if (script == mScript) return true;
|
||||
return supportsScript(mSubScriptBits, scriptToSubScriptBits(script));
|
||||
}
|
||||
|
||||
int FontLanguage::calcScoreFor(const FontLanguages& supported) const {
|
||||
bool languageScriptMatch = false;
|
||||
bool subtagMatch = false;
|
||||
bool scriptMatch = false;
|
||||
|
||||
for (size_t i = 0; i < supported.size(); ++i) {
|
||||
if (mEmojiStyle != EMSTYLE_EMPTY &&
|
||||
mEmojiStyle == supported[i].mEmojiStyle) {
|
||||
subtagMatch = true;
|
||||
if (mLanguage == supported[i].mLanguage) {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
if (isEqualScript(supported[i]) ||
|
||||
supportsScript(supported[i].mSubScriptBits, mSubScriptBits)) {
|
||||
scriptMatch = true;
|
||||
if (mLanguage == supported[i].mLanguage) {
|
||||
languageScriptMatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (supportsScript(supported.getUnionOfSubScriptBits(), mSubScriptBits)) {
|
||||
scriptMatch = true;
|
||||
if (mLanguage == supported[0].mLanguage && supported.isAllTheSameLanguage()) {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (languageScriptMatch) {
|
||||
return 3;
|
||||
} else if (subtagMatch) {
|
||||
return 2;
|
||||
} else if (scriptMatch) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FontLanguages::FontLanguages(std::vector<FontLanguage>&& languages)
|
||||
: mLanguages(std::move(languages)) {
|
||||
if (mLanguages.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const FontLanguage& lang = mLanguages[0];
|
||||
|
||||
mIsAllTheSameLanguage = true;
|
||||
mUnionOfSubScriptBits = lang.mSubScriptBits;
|
||||
for (size_t i = 1; i < mLanguages.size(); ++i) {
|
||||
mUnionOfSubScriptBits |= mLanguages[i].mSubScriptBits;
|
||||
if (mIsAllTheSameLanguage && lang.mLanguage != mLanguages[i].mLanguage) {
|
||||
mIsAllTheSameLanguage = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef SCRIPT_TAG
|
||||
} // namespace minikin
|
||||
158
engine/src/flutter/third_party/txt/libs/minikin/FontLanguage.h
vendored
Normal file
158
engine/src/flutter/third_party/txt/libs/minikin/FontLanguage.h
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_FONT_LANGUAGE_H
|
||||
#define MINIKIN_FONT_LANGUAGE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hb.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
// Due to the limits in font fallback score calculation, we can't use anything more than 12
|
||||
// languages.
|
||||
const size_t FONT_LANGUAGES_LIMIT = 12;
|
||||
|
||||
// The language or region code is encoded to 15 bits.
|
||||
const uint16_t INVALID_CODE = 0x7fff;
|
||||
|
||||
class FontLanguages;
|
||||
|
||||
// FontLanguage is a compact representation of a BCP 47 language tag. It
|
||||
// does not capture all possible information, only what directly affects
|
||||
// font rendering.
|
||||
struct FontLanguage {
|
||||
public:
|
||||
enum EmojiStyle : uint8_t {
|
||||
EMSTYLE_EMPTY = 0,
|
||||
EMSTYLE_DEFAULT = 1,
|
||||
EMSTYLE_EMOJI = 2,
|
||||
EMSTYLE_TEXT = 3,
|
||||
};
|
||||
// Default constructor creates the unsupported language.
|
||||
FontLanguage()
|
||||
: mScript(0ul),
|
||||
mLanguage(INVALID_CODE),
|
||||
mRegion(INVALID_CODE),
|
||||
mHbLanguage(HB_LANGUAGE_INVALID),
|
||||
mSubScriptBits(0ul),
|
||||
mEmojiStyle(EMSTYLE_EMPTY) {}
|
||||
|
||||
// Parse from string
|
||||
FontLanguage(const char* buf, size_t length);
|
||||
|
||||
bool operator==(const FontLanguage other) const {
|
||||
return !isUnsupported() && isEqualScript(other) && mLanguage == other.mLanguage &&
|
||||
mRegion == other.mRegion && mEmojiStyle == other.mEmojiStyle;
|
||||
}
|
||||
|
||||
bool operator!=(const FontLanguage other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool isUnsupported() const { return mLanguage == INVALID_CODE; }
|
||||
EmojiStyle getEmojiStyle() const { return mEmojiStyle; }
|
||||
hb_language_t getHbLanguage() const { return mHbLanguage; }
|
||||
|
||||
|
||||
bool isEqualScript(const FontLanguage& other) const;
|
||||
|
||||
// Returns true if this script supports the given script. For example, ja-Jpan supports Hira,
|
||||
// ja-Hira doesn't support Jpan.
|
||||
bool supportsHbScript(hb_script_t script) const;
|
||||
|
||||
std::string getString() const;
|
||||
|
||||
// Calculates a matching score. This score represents how well the input languages cover this
|
||||
// language. The maximum score in the language list is returned.
|
||||
// 0 = no match, 1 = script match, 2 = script and primary language match.
|
||||
int calcScoreFor(const FontLanguages& supported) const;
|
||||
|
||||
uint64_t getIdentifier() const {
|
||||
return ((uint64_t)mLanguage << 49) | ((uint64_t)mScript << 17) | ((uint64_t)mRegion << 2) |
|
||||
mEmojiStyle;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FontLanguages; // for FontLanguages constructor
|
||||
|
||||
// ISO 15924 compliant script code. The 4 chars script code are packed into a 32 bit integer.
|
||||
uint32_t mScript;
|
||||
|
||||
// ISO 639-1 or ISO 639-2 compliant language code.
|
||||
// The two- or three-letter language code is packed into a 15 bit integer.
|
||||
// mLanguage = 0 means the FontLanguage is unsupported.
|
||||
uint16_t mLanguage;
|
||||
|
||||
// ISO 3166-1 or UN M.49 compliant region code. The two-letter or three-digit region code is
|
||||
// packed into a 15 bit integer.
|
||||
uint16_t mRegion;
|
||||
|
||||
// The language to be passed HarfBuzz shaper.
|
||||
hb_language_t mHbLanguage;
|
||||
|
||||
// For faster comparing, use 7 bits for specific scripts.
|
||||
static const uint8_t kBopomofoFlag = 1u;
|
||||
static const uint8_t kHanFlag = 1u << 1;
|
||||
static const uint8_t kHangulFlag = 1u << 2;
|
||||
static const uint8_t kHiraganaFlag = 1u << 3;
|
||||
static const uint8_t kKatakanaFlag = 1u << 4;
|
||||
static const uint8_t kSimplifiedChineseFlag = 1u << 5;
|
||||
static const uint8_t kTraditionalChineseFlag = 1u << 6;
|
||||
uint8_t mSubScriptBits;
|
||||
|
||||
EmojiStyle mEmojiStyle;
|
||||
|
||||
static uint8_t scriptToSubScriptBits(uint32_t script);
|
||||
|
||||
static EmojiStyle resolveEmojiStyle(const char* buf, size_t length, uint32_t script);
|
||||
|
||||
// Returns true if the provide subscript bits has the requested subscript bits.
|
||||
// Note that this function returns false if the requested subscript bits are empty.
|
||||
static bool supportsScript(uint8_t providedBits, uint8_t requestedBits);
|
||||
};
|
||||
|
||||
// An immutable list of languages.
|
||||
class FontLanguages {
|
||||
public:
|
||||
explicit FontLanguages(std::vector<FontLanguage>&& languages);
|
||||
FontLanguages() : mUnionOfSubScriptBits(0), mIsAllTheSameLanguage(false) {}
|
||||
FontLanguages(FontLanguages&&) = default;
|
||||
|
||||
size_t size() const { return mLanguages.size(); }
|
||||
bool empty() const { return mLanguages.empty(); }
|
||||
const FontLanguage& operator[] (size_t n) const { return mLanguages[n]; }
|
||||
|
||||
private:
|
||||
friend struct FontLanguage; // for calcScoreFor
|
||||
|
||||
std::vector<FontLanguage> mLanguages;
|
||||
uint8_t mUnionOfSubScriptBits;
|
||||
bool mIsAllTheSameLanguage;
|
||||
|
||||
uint8_t getUnionOfSubScriptBits() const { return mUnionOfSubScriptBits; }
|
||||
bool isAllTheSameLanguage() const { return mIsAllTheSameLanguage; }
|
||||
|
||||
// Do not copy and assign.
|
||||
FontLanguages(const FontLanguages&) = delete;
|
||||
void operator=(const FontLanguages&) = delete;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_FONT_LANGUAGE_H
|
||||
156
engine/src/flutter/third_party/txt/libs/minikin/FontLanguageListCache.cpp
vendored
Normal file
156
engine/src/flutter/third_party/txt/libs/minikin/FontLanguageListCache.cpp
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include "FontLanguageListCache.h"
|
||||
|
||||
#include <unicode/uloc.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include "FontLanguage.h"
|
||||
#include "MinikinInternal.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const uint32_t FontLanguageListCache::kEmptyListId;
|
||||
|
||||
// Returns the text length of output.
|
||||
static size_t toLanguageTag(char* output, size_t outSize, const std::string& locale) {
|
||||
output[0] = '\0';
|
||||
if (locale.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t outLength = 0;
|
||||
UErrorCode uErr = U_ZERO_ERROR;
|
||||
outLength = uloc_canonicalize(locale.c_str(), output, outSize, &uErr);
|
||||
if (U_FAILURE(uErr)) {
|
||||
// unable to build a proper language identifier
|
||||
ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale.c_str(), u_errorName(uErr));
|
||||
output[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Preserve "und" and "und-****" since uloc_addLikelySubtags changes "und" to "en-Latn-US".
|
||||
if (strncmp(output, "und", 3) == 0 &&
|
||||
(outLength == 3 || (outLength == 8 && output[3] == '_'))) {
|
||||
return outLength;
|
||||
}
|
||||
|
||||
char likelyChars[ULOC_FULLNAME_CAPACITY];
|
||||
uErr = U_ZERO_ERROR;
|
||||
uloc_addLikelySubtags(output, likelyChars, ULOC_FULLNAME_CAPACITY, &uErr);
|
||||
if (U_FAILURE(uErr)) {
|
||||
// unable to build a proper language identifier
|
||||
ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s", output, u_errorName(uErr));
|
||||
output[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
uErr = U_ZERO_ERROR;
|
||||
outLength = uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr);
|
||||
if (U_FAILURE(uErr)) {
|
||||
// unable to build a proper language identifier
|
||||
ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars, u_errorName(uErr));
|
||||
output[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#ifdef VERBOSE_DEBUG
|
||||
ALOGD("ICU normalized '%s' to '%s'", locale.c_str(), output);
|
||||
#endif
|
||||
return outLength;
|
||||
}
|
||||
|
||||
static std::vector<FontLanguage> parseLanguageList(const std::string& input) {
|
||||
std::vector<FontLanguage> result;
|
||||
size_t currentIdx = 0;
|
||||
size_t commaLoc = 0;
|
||||
char langTag[ULOC_FULLNAME_CAPACITY];
|
||||
std::unordered_set<uint64_t> seen;
|
||||
std::string locale(input.size(), 0);
|
||||
|
||||
while ((commaLoc = input.find_first_of(',', currentIdx)) != std::string::npos) {
|
||||
locale.assign(input, currentIdx, commaLoc - currentIdx);
|
||||
currentIdx = commaLoc + 1;
|
||||
size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
|
||||
FontLanguage lang(langTag, length);
|
||||
uint64_t identifier = lang.getIdentifier();
|
||||
if (!lang.isUnsupported() && seen.count(identifier) == 0) {
|
||||
result.push_back(lang);
|
||||
if (result.size() == FONT_LANGUAGES_LIMIT) {
|
||||
break;
|
||||
}
|
||||
seen.insert(identifier);
|
||||
}
|
||||
}
|
||||
if (result.size() < FONT_LANGUAGES_LIMIT) {
|
||||
locale.assign(input, currentIdx, input.size() - currentIdx);
|
||||
size_t length = toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
|
||||
FontLanguage lang(langTag, length);
|
||||
uint64_t identifier = lang.getIdentifier();
|
||||
if (!lang.isUnsupported() && seen.count(identifier) == 0) {
|
||||
result.push_back(lang);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
uint32_t FontLanguageListCache::getId(const std::string& languages) {
|
||||
FontLanguageListCache* inst = FontLanguageListCache::getInstance();
|
||||
std::unordered_map<std::string, uint32_t>::const_iterator it =
|
||||
inst->mLanguageListLookupTable.find(languages);
|
||||
if (it != inst->mLanguageListLookupTable.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Given language list is not in cache. Insert it and return newly assigned ID.
|
||||
const uint32_t nextId = inst->mLanguageLists.size();
|
||||
FontLanguages fontLanguages(parseLanguageList(languages));
|
||||
if (fontLanguages.empty()) {
|
||||
return kEmptyListId;
|
||||
}
|
||||
inst->mLanguageLists.push_back(std::move(fontLanguages));
|
||||
inst->mLanguageListLookupTable.insert(std::make_pair(languages, nextId));
|
||||
return nextId;
|
||||
}
|
||||
|
||||
// static
|
||||
const FontLanguages& FontLanguageListCache::getById(uint32_t id) {
|
||||
FontLanguageListCache* inst = FontLanguageListCache::getInstance();
|
||||
LOG_ALWAYS_FATAL_IF(id >= inst->mLanguageLists.size(), "Lookup by unknown language list ID.");
|
||||
return inst->mLanguageLists[id];
|
||||
}
|
||||
|
||||
// static
|
||||
FontLanguageListCache* FontLanguageListCache::getInstance() {
|
||||
assertMinikinLocked();
|
||||
static FontLanguageListCache* instance = nullptr;
|
||||
if (instance == nullptr) {
|
||||
instance = new FontLanguageListCache();
|
||||
|
||||
// Insert an empty language list for mapping default language list to kEmptyListId.
|
||||
// The default language list has only one FontLanguage and it is the unsupported language.
|
||||
instance->mLanguageLists.push_back(FontLanguages());
|
||||
instance->mLanguageListLookupTable.insert(std::make_pair("", kEmptyListId));
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
56
engine/src/flutter/third_party/txt/libs/minikin/FontLanguageListCache.h
vendored
Normal file
56
engine/src/flutter/third_party/txt/libs/minikin/FontLanguageListCache.h
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_FONT_LANGUAGE_LIST_CACHE_H
|
||||
#define MINIKIN_FONT_LANGUAGE_LIST_CACHE_H
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <minikin/FontFamily.h>
|
||||
#include "FontLanguage.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class FontLanguageListCache {
|
||||
public:
|
||||
// A special ID for the empty language list.
|
||||
// This value must be 0 since the empty language list is inserted into mLanguageLists by
|
||||
// default.
|
||||
const static uint32_t kEmptyListId = 0;
|
||||
|
||||
// Returns language list ID for the given string representation of FontLanguages.
|
||||
// Caller should acquire a lock before calling the method.
|
||||
static uint32_t getId(const std::string& languages);
|
||||
|
||||
// Caller should acquire a lock before calling the method.
|
||||
static const FontLanguages& getById(uint32_t id);
|
||||
|
||||
private:
|
||||
FontLanguageListCache() {} // Singleton
|
||||
~FontLanguageListCache() {}
|
||||
|
||||
// Caller should acquire a lock before calling the method.
|
||||
static FontLanguageListCache* getInstance();
|
||||
|
||||
std::vector<FontLanguages> mLanguageLists;
|
||||
|
||||
// A map from string representation of the font language list to the ID.
|
||||
std::unordered_map<std::string, uint32_t> mLanguageListLookupTable;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_FONT_LANGUAGE_LIST_CACHE_H
|
||||
77
engine/src/flutter/third_party/txt/libs/minikin/FontUtils.cpp
vendored
Normal file
77
engine/src/flutter/third_party/txt/libs/minikin/FontUtils.cpp
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "FontUtils.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
static uint16_t readU16(const uint8_t* data, size_t offset) {
|
||||
return data[offset] << 8 | data[offset + 1];
|
||||
}
|
||||
|
||||
static uint32_t readU32(const uint8_t* data, size_t offset) {
|
||||
return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 |
|
||||
((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]);
|
||||
}
|
||||
|
||||
bool analyzeStyle(const uint8_t* os2_data, size_t os2_size, int* weight, bool* italic) {
|
||||
const size_t kUsWeightClassOffset = 4;
|
||||
const size_t kFsSelectionOffset = 62;
|
||||
const uint16_t kItalicFlag = (1 << 0);
|
||||
if (os2_size < kFsSelectionOffset + 2) {
|
||||
return false;
|
||||
}
|
||||
uint16_t weightClass = readU16(os2_data, kUsWeightClassOffset);
|
||||
*weight = weightClass / 100;
|
||||
uint16_t fsSelection = readU16(os2_data, kFsSelectionOffset);
|
||||
*italic = (fsSelection & kItalicFlag) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void analyzeAxes(const uint8_t* fvar_data, size_t fvar_size, std::unordered_set<uint32_t>* axes) {
|
||||
const size_t kMajorVersionOffset = 0;
|
||||
const size_t kMinorVersionOffset = 2;
|
||||
const size_t kOffsetToAxesArrayOffset = 4;
|
||||
const size_t kAxisCountOffset = 8;
|
||||
const size_t kAxisSizeOffset = 10;
|
||||
|
||||
axes->clear();
|
||||
|
||||
if (fvar_size < kAxisSizeOffset + 2) {
|
||||
return;
|
||||
}
|
||||
const uint16_t majorVersion = readU16(fvar_data, kMajorVersionOffset);
|
||||
const uint16_t minorVersion = readU16(fvar_data, kMinorVersionOffset);
|
||||
const uint32_t axisOffset = readU16(fvar_data, kOffsetToAxesArrayOffset);
|
||||
const uint32_t axisCount = readU16(fvar_data, kAxisCountOffset);
|
||||
const uint32_t axisSize = readU16(fvar_data, kAxisSizeOffset);
|
||||
|
||||
if (majorVersion != 1 || minorVersion != 0 || axisOffset != 0x10 || axisSize != 0x14) {
|
||||
return; // Unsupported version.
|
||||
}
|
||||
if (fvar_size < axisOffset + axisOffset * axisCount) {
|
||||
return; // Invalid table size.
|
||||
}
|
||||
for (uint32_t i = 0; i < axisCount; ++i) {
|
||||
size_t axisRecordOffset = axisOffset + i * axisSize;
|
||||
uint32_t tag = readU32(fvar_data, axisRecordOffset);
|
||||
axes->insert(tag);
|
||||
}
|
||||
}
|
||||
} // namespace minikin
|
||||
29
engine/src/flutter/third_party/txt/libs/minikin/FontUtils.h
vendored
Normal file
29
engine/src/flutter/third_party/txt/libs/minikin/FontUtils.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_FONT_UTILS_H
|
||||
#define MINIKIN_FONT_UTILS_H
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
bool analyzeStyle(const uint8_t* os2_data, size_t os2_size, int* weight, bool* italic);
|
||||
void analyzeAxes(const uint8_t* fvar_data, size_t fvar_size, std::unordered_set<uint32_t>* axes);
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_ANALYZE_STYLE_H
|
||||
241
engine/src/flutter/third_party/txt/libs/minikin/GraphemeBreak.cpp
vendored
Normal file
241
engine/src/flutter/third_party/txt/libs/minikin/GraphemeBreak.cpp
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <unicode/uchar.h>
|
||||
#include <unicode/utf16.h>
|
||||
|
||||
#include <minikin/GraphemeBreak.h>
|
||||
#include <minikin/Emoji.h>
|
||||
#include "MinikinInternal.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
int32_t tailoredGraphemeClusterBreak(uint32_t c) {
|
||||
// Characters defined as Control that we want to treat them as Extend.
|
||||
// These are curated manually.
|
||||
if (c == 0x00AD // SHY
|
||||
|| c == 0x061C // ALM
|
||||
|| c == 0x180E // MONGOLIAN VOWEL SEPARATOR
|
||||
|| c == 0x200B // ZWSP
|
||||
|| c == 0x200E // LRM
|
||||
|| c == 0x200F // RLM
|
||||
|| (0x202A <= c && c <= 0x202E) // LRE, RLE, PDF, LRO, RLO
|
||||
|| ((c | 0xF) == 0x206F) // WJ, invisible math operators, LRI, RLI, FSI, PDI,
|
||||
// and the deprecated invisible format controls
|
||||
|| c == 0xFEFF // BOM
|
||||
|| ((c | 0x7F) == 0xE007F)) // recently undeprecated tag characters in Plane 14
|
||||
return U_GCB_EXTEND;
|
||||
// THAI CHARACTER SARA AM is treated as a normal letter by most other implementations: they
|
||||
// allow a grapheme break before it.
|
||||
else if (c == 0x0E33)
|
||||
return U_GCB_OTHER;
|
||||
else
|
||||
return u_getIntPropertyValue(c, UCHAR_GRAPHEME_CLUSTER_BREAK);
|
||||
}
|
||||
|
||||
// Returns true for all characters whose IndicSyllabicCategory is Pure_Killer.
|
||||
// From http://www.unicode.org/Public/9.0.0/ucd/IndicSyllabicCategory.txt
|
||||
bool isPureKiller(uint32_t c) {
|
||||
return (c == 0x0E3A || c == 0x0E4E || c == 0x0F84 || c == 0x103A || c == 0x1714 || c == 0x1734
|
||||
|| c == 0x17D1 || c == 0x1BAA || c == 0x1BF2 || c == 0x1BF3 || c == 0xA806
|
||||
|| c == 0xA953 || c == 0xABED || c == 0x11134 || c == 0x112EA || c == 0x1172B);
|
||||
}
|
||||
|
||||
bool GraphemeBreak::isGraphemeBreak(const float* advances, const uint16_t* buf, size_t start,
|
||||
size_t count, const size_t offset) {
|
||||
// This implementation closely follows Unicode Standard Annex #29 on
|
||||
// Unicode Text Segmentation (http://www.unicode.org/reports/tr29/),
|
||||
// implementing a tailored version of extended grapheme clusters.
|
||||
// The GB rules refer to section 3.1.1, Grapheme Cluster Boundary Rules.
|
||||
|
||||
// Rule GB1, sot ÷; Rule GB2, ÷ eot
|
||||
if (offset <= start || offset >= start + count) {
|
||||
return true;
|
||||
}
|
||||
if (U16_IS_TRAIL(buf[offset])) {
|
||||
// Don't break a surrogate pair, but a lonely trailing surrogate pair is a break
|
||||
return !U16_IS_LEAD(buf[offset - 1]);
|
||||
}
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
size_t offset_back = offset;
|
||||
size_t offset_forward = offset;
|
||||
U16_PREV(buf, start, offset_back, c1);
|
||||
U16_NEXT(buf, offset_forward, start + count, c2);
|
||||
int32_t p1 = tailoredGraphemeClusterBreak(c1);
|
||||
int32_t p2 = tailoredGraphemeClusterBreak(c2);
|
||||
// Rule GB3, CR x LF
|
||||
if (p1 == U_GCB_CR && p2 == U_GCB_LF) {
|
||||
return false;
|
||||
}
|
||||
// Rule GB4, (Control | CR | LF) ÷
|
||||
if (p1 == U_GCB_CONTROL || p1 == U_GCB_CR || p1 == U_GCB_LF) {
|
||||
return true;
|
||||
}
|
||||
// Rule GB5, ÷ (Control | CR | LF)
|
||||
if (p2 == U_GCB_CONTROL || p2 == U_GCB_CR || p2 == U_GCB_LF) {
|
||||
return true;
|
||||
}
|
||||
// Rule GB6, L x ( L | V | LV | LVT )
|
||||
if (p1 == U_GCB_L && (p2 == U_GCB_L || p2 == U_GCB_V || p2 == U_GCB_LV || p2 == U_GCB_LVT)) {
|
||||
return false;
|
||||
}
|
||||
// Rule GB7, ( LV | V ) x ( V | T )
|
||||
if ((p1 == U_GCB_LV || p1 == U_GCB_V) && (p2 == U_GCB_V || p2 == U_GCB_T)) {
|
||||
return false;
|
||||
}
|
||||
// Rule GB8, ( LVT | T ) x T
|
||||
if ((p1 == U_GCB_LVT || p1 == U_GCB_T) && p2 == U_GCB_T) {
|
||||
return false;
|
||||
}
|
||||
// Rule GB9, x (Extend | ZWJ); Rule GB9a, x SpacingMark; Rule GB9b, Prepend x
|
||||
// TODO(abarth): Add U_GCB_ZWJ once we update ICU.
|
||||
if (p2 == U_GCB_EXTEND || /* p2 == U_GCB_ZWJ || */ p2 == U_GCB_SPACING_MARK || p1 == U_GCB_PREPEND) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is used to decide font-dependent grapheme clusters. If we don't have the advance
|
||||
// information, we become conservative in grapheme breaking and assume that it has no advance.
|
||||
const bool c2_has_advance = (advances != nullptr && advances[offset - start] != 0.0);
|
||||
|
||||
// All the following rules are font-dependent, in the way that if we know c2 has an advance,
|
||||
// we definitely know that it cannot form a grapheme with the character(s) before it. So we
|
||||
// make the decision in favor a grapheme break early.
|
||||
if (c2_has_advance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: For Rule GB10 and GB11 below, we do not use the Unicode line breaking properties for
|
||||
// determining emoji-ness and carry our own data, because our data could be more fresh than what
|
||||
// ICU provides.
|
||||
//
|
||||
// Tailored version of Rule GB10, (E_Base | EBG) Extend* × E_Modifier.
|
||||
// The rule itself says do not break between emoji base and emoji modifiers, skipping all Extend
|
||||
// characters. Variation selectors are considered Extend, so they are handled fine.
|
||||
//
|
||||
// We tailor this by requiring that an actual ligature is formed. If the font doesn't form a
|
||||
// ligature, we allow a break before the modifier.
|
||||
if (isEmojiModifier(c2)) {
|
||||
uint32_t c0 = c1;
|
||||
size_t offset_backback = offset_back;
|
||||
int32_t p0 = p1;
|
||||
if (p0 == U_GCB_EXTEND && offset_backback > start) {
|
||||
// skip over emoji variation selector
|
||||
U16_PREV(buf, start, offset_backback, c0);
|
||||
p0 = tailoredGraphemeClusterBreak(c0);
|
||||
}
|
||||
if (isEmojiBase(c0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(abarth): Enablet his code once we update ICU.
|
||||
// Tailored version of Rule GB11, ZWJ × (Glue_After_Zwj | EBG)
|
||||
// We try to make emoji sequences with ZWJ a single grapheme cluster, but only if they actually
|
||||
// merge to one cluster. So we are more relaxed than the UAX #29 rules in accepting any emoji
|
||||
// character after the ZWJ, but are tighter in that we only treat it as one cluster if a
|
||||
// ligature is actually formed and we also require the character before the ZWJ to also be an
|
||||
// emoji.
|
||||
// if (p1 == U_GCB_ZWJ && isEmoji(c2) && offset_back > start) {
|
||||
// // look at character before ZWJ to see that both can participate in an emoji zwj sequence
|
||||
// uint32_t c0 = 0;
|
||||
// size_t offset_backback = offset_back;
|
||||
// U16_PREV(buf, start, offset_backback, c0);
|
||||
// if (c0 == 0xFE0F && offset_backback > start) {
|
||||
// // skip over emoji variation selector
|
||||
// U16_PREV(buf, start, offset_backback, c0);
|
||||
// }
|
||||
// if (isEmoji(c0)) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Tailored version of Rule GB12 and Rule GB13 that look at even-odd cases.
|
||||
// sot (RI RI)* RI x RI
|
||||
// [^RI] (RI RI)* RI x RI
|
||||
//
|
||||
// If we have font information, we have already broken the cluster if and only if the second
|
||||
// character had no advance, which means a ligature was formed. If we don't, we look back like
|
||||
// UAX #29 recommends, but only up to 1000 code units.
|
||||
if (p1 == U_GCB_REGIONAL_INDICATOR && p2 == U_GCB_REGIONAL_INDICATOR) {
|
||||
if (advances != nullptr) {
|
||||
// We have advances information. But if we are here, we already know c2 has no advance.
|
||||
// So we should definitely disallow a break.
|
||||
return false;
|
||||
} else {
|
||||
// Look at up to 1000 code units.
|
||||
const size_t lookback_barrier = std::max((ssize_t)start, (ssize_t)offset_back - 1000);
|
||||
size_t offset_backback = offset_back;
|
||||
while (offset_backback > lookback_barrier) {
|
||||
uint32_t c0 = 0;
|
||||
U16_PREV(buf, lookback_barrier, offset_backback, c0);
|
||||
if (tailoredGraphemeClusterBreak(c0) != U_GCB_REGIONAL_INDICATOR) {
|
||||
offset_backback += U16_LENGTH(c0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The number 4 comes from the number of code units in a whole flag.
|
||||
return (offset - offset_backback) % 4 == 0;
|
||||
}
|
||||
}
|
||||
// Cluster Indic syllables together (tailoring of UAX #29).
|
||||
// Immediately after each virama (that is not just a pure killer) followed by a letter, we
|
||||
// disallow grapheme breaks (if we are here, we don't know about advances, or we already know
|
||||
// that c2 has no advance).
|
||||
if (u_getIntPropertyValue(c1, UCHAR_CANONICAL_COMBINING_CLASS) == 9 // virama
|
||||
&& !isPureKiller(c1)
|
||||
&& u_getIntPropertyValue(c2, UCHAR_GENERAL_CATEGORY) == U_OTHER_LETTER) {
|
||||
return false;
|
||||
}
|
||||
// Rule GB999, Any ÷ Any
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t GraphemeBreak::getTextRunCursor(const float* advances, const uint16_t* buf, size_t start,
|
||||
size_t count, size_t offset, MoveOpt opt) {
|
||||
switch (opt) {
|
||||
case AFTER:
|
||||
if (offset < start + count) {
|
||||
offset++;
|
||||
}
|
||||
// fall through
|
||||
case AT_OR_AFTER:
|
||||
while (!isGraphemeBreak(advances, buf, start, count, offset)) {
|
||||
offset++;
|
||||
}
|
||||
break;
|
||||
case BEFORE:
|
||||
if (offset > start) {
|
||||
offset--;
|
||||
}
|
||||
// fall through
|
||||
case AT_OR_BEFORE:
|
||||
while (!isGraphemeBreak(advances, buf, start, count, offset)) {
|
||||
offset--;
|
||||
}
|
||||
break;
|
||||
case AT:
|
||||
if (!isGraphemeBreak(advances, buf, start, count, offset)) {
|
||||
offset = (size_t)-1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
126
engine/src/flutter/third_party/txt/libs/minikin/HbFontCache.cpp
vendored
Normal file
126
engine/src/flutter/third_party/txt/libs/minikin/HbFontCache.cpp
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include "HbFontCache.h"
|
||||
|
||||
#include <log/log.h>
|
||||
#include <utils/LruCache.h>
|
||||
|
||||
#include <hb.h>
|
||||
#include <hb-ot.h>
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
#include "MinikinInternal.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
class HbFontCache : private android::OnEntryRemoved<int32_t, hb_font_t*> {
|
||||
public:
|
||||
HbFontCache() : mCache(kMaxEntries) {
|
||||
mCache.setOnEntryRemovedListener(this);
|
||||
}
|
||||
|
||||
// callback for OnEntryRemoved
|
||||
void operator()(int32_t& /* key */, hb_font_t*& value) {
|
||||
hb_font_destroy(value);
|
||||
}
|
||||
|
||||
hb_font_t* get(int32_t fontId) {
|
||||
return mCache.get(fontId);
|
||||
}
|
||||
|
||||
void put(int32_t fontId, hb_font_t* font) {
|
||||
mCache.put(fontId, font);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
void remove(int32_t fontId) {
|
||||
mCache.remove(fontId);
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t kMaxEntries = 100;
|
||||
|
||||
android::LruCache<int32_t, hb_font_t*> mCache;
|
||||
};
|
||||
|
||||
HbFontCache* getFontCacheLocked() {
|
||||
assertMinikinLocked();
|
||||
static HbFontCache* cache = nullptr;
|
||||
if (cache == nullptr) {
|
||||
cache = new HbFontCache();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
void purgeHbFontCacheLocked() {
|
||||
assertMinikinLocked();
|
||||
getFontCacheLocked()->clear();
|
||||
}
|
||||
|
||||
void purgeHbFontLocked(const MinikinFont* minikinFont) {
|
||||
assertMinikinLocked();
|
||||
const int32_t fontId = minikinFont->GetUniqueId();
|
||||
getFontCacheLocked()->remove(fontId);
|
||||
}
|
||||
|
||||
// Returns a new reference to a hb_font_t object, caller is
|
||||
// responsible for calling hb_font_destroy() on it.
|
||||
hb_font_t* getHbFontLocked(const MinikinFont* minikinFont) {
|
||||
assertMinikinLocked();
|
||||
// TODO: get rid of nullFaceFont
|
||||
static hb_font_t* nullFaceFont = nullptr;
|
||||
if (minikinFont == nullptr) {
|
||||
if (nullFaceFont == nullptr) {
|
||||
nullFaceFont = hb_font_create(nullptr);
|
||||
}
|
||||
return hb_font_reference(nullFaceFont);
|
||||
}
|
||||
|
||||
HbFontCache* fontCache = getFontCacheLocked();
|
||||
const int32_t fontId = minikinFont->GetUniqueId();
|
||||
hb_font_t* font = fontCache->get(fontId);
|
||||
if (font != nullptr) {
|
||||
return hb_font_reference(font);
|
||||
}
|
||||
|
||||
hb_face_t* face = minikinFont->CreateHarfBuzzFace();
|
||||
|
||||
hb_font_t* parent_font = hb_font_create(face);
|
||||
hb_ot_font_set_funcs(parent_font);
|
||||
|
||||
unsigned int upem = hb_face_get_upem(face);
|
||||
hb_font_set_scale(parent_font, upem, upem);
|
||||
|
||||
font = hb_font_create_sub_font(parent_font);
|
||||
// TODO(abarth): Enable this code once we update harfbuzz.
|
||||
// std::vector<hb_variation_t> variations;
|
||||
// for (const FontVariation& variation : minikinFont->GetAxes()) {
|
||||
// variations.push_back({variation.axisTag, variation.value});
|
||||
// }
|
||||
// hb_font_set_variations(font, variations.data(), variations.size());
|
||||
hb_font_destroy(parent_font);
|
||||
hb_face_destroy(face);
|
||||
fontCache->put(fontId, font);
|
||||
return hb_font_reference(font);
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
30
engine/src/flutter/third_party/txt/libs/minikin/HbFontCache.h
vendored
Normal file
30
engine/src/flutter/third_party/txt/libs/minikin/HbFontCache.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_HBFONT_CACHE_H
|
||||
#define MINIKIN_HBFONT_CACHE_H
|
||||
|
||||
struct hb_font_t;
|
||||
|
||||
namespace minikin {
|
||||
class MinikinFont;
|
||||
|
||||
void purgeHbFontCacheLocked();
|
||||
void purgeHbFontLocked(const MinikinFont* minikinFont);
|
||||
hb_font_t* getHbFontLocked(const MinikinFont* minikinFont);
|
||||
|
||||
} // namespace minikin
|
||||
#endif // MINIKIN_HBFONT_CACHE_H
|
||||
434
engine/src/flutter/third_party/txt/libs/minikin/Hyphenator.cpp
vendored
Normal file
434
engine/src/flutter/third_party/txt/libs/minikin/Hyphenator.cpp
vendored
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unicode/uchar.h>
|
||||
#include <unicode/uscript.h>
|
||||
|
||||
// HACK: for reading pattern file
|
||||
#include <fcntl.h>
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include "minikin/Hyphenator.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace minikin {
|
||||
|
||||
static const uint16_t CHAR_HYPHEN_MINUS = 0x002D;
|
||||
static const uint16_t CHAR_SOFT_HYPHEN = 0x00AD;
|
||||
static const uint16_t CHAR_MIDDLE_DOT = 0x00B7;
|
||||
static const uint16_t CHAR_HYPHEN = 0x2010;
|
||||
|
||||
// The following are structs that correspond to tables inside the hyb file format
|
||||
|
||||
struct AlphabetTable0 {
|
||||
uint32_t version;
|
||||
uint32_t min_codepoint;
|
||||
uint32_t max_codepoint;
|
||||
uint8_t data[1]; // actually flexible array, size is known at runtime
|
||||
};
|
||||
|
||||
struct AlphabetTable1 {
|
||||
uint32_t version;
|
||||
uint32_t n_entries;
|
||||
uint32_t data[1]; // actually flexible array, size is known at runtime
|
||||
|
||||
static uint32_t codepoint(uint32_t entry) { return entry >> 11; }
|
||||
static uint32_t value(uint32_t entry) { return entry & 0x7ff; }
|
||||
};
|
||||
|
||||
struct Trie {
|
||||
uint32_t version;
|
||||
uint32_t char_mask;
|
||||
uint32_t link_shift;
|
||||
uint32_t link_mask;
|
||||
uint32_t pattern_shift;
|
||||
uint32_t n_entries;
|
||||
uint32_t data[1]; // actually flexible array, size is known at runtime
|
||||
};
|
||||
|
||||
struct Pattern {
|
||||
uint32_t version;
|
||||
uint32_t n_entries;
|
||||
uint32_t pattern_offset;
|
||||
uint32_t pattern_size;
|
||||
uint32_t data[1]; // actually flexible array, size is known at runtime
|
||||
|
||||
// accessors
|
||||
static uint32_t len(uint32_t entry) { return entry >> 26; }
|
||||
static uint32_t shift(uint32_t entry) { return (entry >> 20) & 0x3f; }
|
||||
const uint8_t* buf(uint32_t entry) const {
|
||||
return reinterpret_cast<const uint8_t*>(this) + pattern_offset + (entry & 0xfffff);
|
||||
}
|
||||
};
|
||||
|
||||
struct Header {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t alphabet_offset;
|
||||
uint32_t trie_offset;
|
||||
uint32_t pattern_offset;
|
||||
uint32_t file_size;
|
||||
|
||||
// accessors
|
||||
const uint8_t* bytes() const { return reinterpret_cast<const uint8_t*>(this); }
|
||||
uint32_t alphabetVersion() const {
|
||||
return *reinterpret_cast<const uint32_t*>(bytes() + alphabet_offset);
|
||||
}
|
||||
const AlphabetTable0* alphabetTable0() const {
|
||||
return reinterpret_cast<const AlphabetTable0*>(bytes() + alphabet_offset);
|
||||
}
|
||||
const AlphabetTable1* alphabetTable1() const {
|
||||
return reinterpret_cast<const AlphabetTable1*>(bytes() + alphabet_offset);
|
||||
}
|
||||
const Trie* trieTable() const {
|
||||
return reinterpret_cast<const Trie*>(bytes() + trie_offset);
|
||||
}
|
||||
const Pattern* patternTable() const {
|
||||
return reinterpret_cast<const Pattern*>(bytes() + pattern_offset);
|
||||
}
|
||||
};
|
||||
|
||||
Hyphenator* Hyphenator::loadBinary(const uint8_t* patternData, size_t minPrefix, size_t minSuffix) {
|
||||
Hyphenator* result = new Hyphenator;
|
||||
result->patternData = patternData;
|
||||
result->minPrefix = minPrefix;
|
||||
result->minSuffix = minSuffix;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Hyphenator::hyphenate(vector<HyphenationType>* result, const uint16_t* word, size_t len,
|
||||
const icu::Locale& locale) {
|
||||
result->clear();
|
||||
result->resize(len);
|
||||
const size_t paddedLen = len + 2; // start and stop code each count for 1
|
||||
if (patternData != nullptr &&
|
||||
len >= minPrefix + minSuffix && paddedLen <= MAX_HYPHENATED_SIZE) {
|
||||
uint16_t alpha_codes[MAX_HYPHENATED_SIZE];
|
||||
const HyphenationType hyphenValue = alphabetLookup(alpha_codes, word, len);
|
||||
if (hyphenValue != HyphenationType::DONT_BREAK) {
|
||||
hyphenateFromCodes(result->data(), alpha_codes, paddedLen, hyphenValue);
|
||||
return;
|
||||
}
|
||||
// TODO: try NFC normalization
|
||||
// TODO: handle non-BMP Unicode (requires remapping of offsets)
|
||||
}
|
||||
// Note that we will always get here if the word contains a hyphen or a soft hyphen, because the
|
||||
// alphabet is not expected to contain a hyphen or a soft hyphen character, so alphabetLookup
|
||||
// would return DONT_BREAK.
|
||||
hyphenateWithNoPatterns(result->data(), word, len, locale);
|
||||
}
|
||||
|
||||
// This function determines whether a character is like U+2010 HYPHEN in
|
||||
// line breaking and usage: a character immediately after which line breaks
|
||||
// are allowed, but words containing it should not be automatically
|
||||
// hyphenated using patterns. This is a curated set, created by manually
|
||||
// inspecting all the characters that have the Unicode line breaking
|
||||
// property of BA or HY and seeing which ones are hyphens.
|
||||
bool Hyphenator::isLineBreakingHyphen(uint32_t c) {
|
||||
return (c == 0x002D || // HYPHEN-MINUS
|
||||
c == 0x058A || // ARMENIAN HYPHEN
|
||||
c == 0x05BE || // HEBREW PUNCTUATION MAQAF
|
||||
c == 0x1400 || // CANADIAN SYLLABICS HYPHEN
|
||||
c == 0x2010 || // HYPHEN
|
||||
c == 0x2013 || // EN DASH
|
||||
c == 0x2027 || // HYPHENATION POINT
|
||||
c == 0x2E17 || // DOUBLE OBLIQUE HYPHEN
|
||||
c == 0x2E40); // DOUBLE HYPHEN
|
||||
}
|
||||
|
||||
const static uint32_t HYPHEN_STR[] = {0x2010, 0};
|
||||
const static uint32_t ARMENIAN_HYPHEN_STR[] = {0x058A, 0};
|
||||
const static uint32_t MAQAF_STR[] = {0x05BE, 0};
|
||||
const static uint32_t UCAS_HYPHEN_STR[] = {0x1400, 0};
|
||||
const static uint32_t ZWJ_STR[] = {0x200D, 0};
|
||||
const static uint32_t ZWJ_AND_HYPHEN_STR[] = {0x200D, 0x2010, 0};
|
||||
|
||||
const uint32_t* HyphenEdit::getHyphenString(uint32_t hyph) {
|
||||
switch (hyph) {
|
||||
case INSERT_HYPHEN_AT_END:
|
||||
case REPLACE_WITH_HYPHEN_AT_END:
|
||||
case INSERT_HYPHEN_AT_START:
|
||||
return HYPHEN_STR;
|
||||
case INSERT_ARMENIAN_HYPHEN_AT_END:
|
||||
return ARMENIAN_HYPHEN_STR;
|
||||
case INSERT_MAQAF_AT_END:
|
||||
return MAQAF_STR;
|
||||
case INSERT_UCAS_HYPHEN_AT_END:
|
||||
return UCAS_HYPHEN_STR;
|
||||
case INSERT_ZWJ_AND_HYPHEN_AT_END:
|
||||
return ZWJ_AND_HYPHEN_STR;
|
||||
case INSERT_ZWJ_AT_START:
|
||||
return ZWJ_STR;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HyphenEdit::editForThisLine(HyphenationType type) {
|
||||
switch (type) {
|
||||
case HyphenationType::DONT_BREAK:
|
||||
return NO_EDIT;
|
||||
case HyphenationType::BREAK_AND_INSERT_HYPHEN:
|
||||
return INSERT_HYPHEN_AT_END;
|
||||
case HyphenationType::BREAK_AND_INSERT_ARMENIAN_HYPHEN:
|
||||
return INSERT_ARMENIAN_HYPHEN_AT_END;
|
||||
case HyphenationType::BREAK_AND_INSERT_MAQAF:
|
||||
return INSERT_MAQAF_AT_END;
|
||||
case HyphenationType::BREAK_AND_INSERT_UCAS_HYPHEN:
|
||||
return INSERT_UCAS_HYPHEN_AT_END;
|
||||
case HyphenationType::BREAK_AND_REPLACE_WITH_HYPHEN:
|
||||
return REPLACE_WITH_HYPHEN_AT_END;
|
||||
case HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ:
|
||||
return INSERT_ZWJ_AND_HYPHEN_AT_END;
|
||||
default:
|
||||
return BREAK_AT_END;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t HyphenEdit::editForNextLine(HyphenationType type) {
|
||||
switch (type) {
|
||||
case HyphenationType::DONT_BREAK:
|
||||
return NO_EDIT;
|
||||
case HyphenationType::BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE:
|
||||
return INSERT_HYPHEN_AT_START;
|
||||
case HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ:
|
||||
return INSERT_ZWJ_AT_START;
|
||||
default:
|
||||
return BREAK_AT_START;
|
||||
}
|
||||
}
|
||||
|
||||
static UScriptCode getScript(uint32_t codePoint) {
|
||||
UErrorCode errorCode = U_ZERO_ERROR;
|
||||
const UScriptCode script = uscript_getScript(static_cast<UChar32>(codePoint), &errorCode);
|
||||
if (U_SUCCESS(errorCode)) {
|
||||
return script;
|
||||
} else {
|
||||
return USCRIPT_INVALID_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
static HyphenationType hyphenationTypeBasedOnScript(uint32_t codePoint) {
|
||||
// Note: It's not clear what the best hyphen for Hebrew is. While maqaf is the "correct" hyphen
|
||||
// for Hebrew, modern practice may have shifted towards Western hyphens. We use normal hyphens
|
||||
// for now to be safe. BREAK_AND_INSERT_MAQAF is already implemented, so if we want to switch
|
||||
// to maqaf for Hebrew, we can simply add a condition here.
|
||||
const UScriptCode script = getScript(codePoint);
|
||||
if (script == USCRIPT_KANNADA
|
||||
|| script == USCRIPT_MALAYALAM
|
||||
|| script == USCRIPT_TAMIL
|
||||
|| script == USCRIPT_TELUGU) {
|
||||
// Grantha is not included, since we don't support non-BMP hyphenation yet.
|
||||
return HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN;
|
||||
} else if (script == USCRIPT_ARMENIAN) {
|
||||
return HyphenationType::BREAK_AND_INSERT_ARMENIAN_HYPHEN;
|
||||
} else if (script == USCRIPT_CANADIAN_ABORIGINAL) {
|
||||
return HyphenationType::BREAK_AND_INSERT_UCAS_HYPHEN;
|
||||
} else {
|
||||
return HyphenationType::BREAK_AND_INSERT_HYPHEN;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int32_t getJoiningType(UChar32 codepoint) {
|
||||
return u_getIntPropertyValue(codepoint, UCHAR_JOINING_TYPE);
|
||||
}
|
||||
|
||||
// Assumption for caller: location must be >= 2 and word[location] == CHAR_SOFT_HYPHEN.
|
||||
// This function decides if the letters before and after the hyphen should appear as joining.
|
||||
static inline HyphenationType getHyphTypeForArabic(const uint16_t* word, size_t len,
|
||||
size_t location) {
|
||||
ssize_t i = location;
|
||||
int32_t type = U_JT_NON_JOINING;
|
||||
while (static_cast<size_t>(i) < len && (type = getJoiningType(word[i])) == U_JT_TRANSPARENT) {
|
||||
i++;
|
||||
}
|
||||
if (type == U_JT_DUAL_JOINING || type == U_JT_RIGHT_JOINING || type == U_JT_JOIN_CAUSING) {
|
||||
// The next character is of the type that may join the last character. See if the last
|
||||
// character is also of the right type.
|
||||
i = location - 2; // Skip the soft hyphen
|
||||
type = U_JT_NON_JOINING;
|
||||
while (i >= 0 && (type = getJoiningType(word[i])) == U_JT_TRANSPARENT) {
|
||||
i--;
|
||||
}
|
||||
if (type == U_JT_DUAL_JOINING || type == U_JT_LEFT_JOINING || type == U_JT_JOIN_CAUSING) {
|
||||
return HyphenationType::BREAK_AND_INSERT_HYPHEN_AND_ZWJ;
|
||||
}
|
||||
}
|
||||
return HyphenationType::BREAK_AND_INSERT_HYPHEN;
|
||||
}
|
||||
|
||||
// Use various recommendations of UAX #14 Unicode Line Breaking Algorithm for hyphenating words
|
||||
// that didn't match patterns, especially words that contain hyphens or soft hyphens (See sections
|
||||
// 5.3, Use of Hyphen, and 5.4, Use of Soft Hyphen).
|
||||
void Hyphenator::hyphenateWithNoPatterns(HyphenationType* result, const uint16_t* word, size_t len,
|
||||
const icu::Locale& locale) {
|
||||
result[0] = HyphenationType::DONT_BREAK;
|
||||
for (size_t i = 1; i < len; i++) {
|
||||
const uint16_t prevChar = word[i - 1];
|
||||
if (i > 1 && isLineBreakingHyphen(prevChar)) {
|
||||
// Break after hyphens, but only if they don't start the word.
|
||||
|
||||
if ((prevChar == CHAR_HYPHEN_MINUS || prevChar == CHAR_HYPHEN)
|
||||
&& strcmp(locale.getLanguage(), "pl") == 0
|
||||
&& getScript(word[i]) == USCRIPT_LATIN ) {
|
||||
// In Polish, hyphens get repeated at the next line. To be safe,
|
||||
// we will do this only if the next character is Latin.
|
||||
result[i] = HyphenationType::BREAK_AND_INSERT_HYPHEN_AT_NEXT_LINE;
|
||||
} else {
|
||||
result[i] = HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN;
|
||||
}
|
||||
} else if (i > 1 && prevChar == CHAR_SOFT_HYPHEN) {
|
||||
// Break after soft hyphens, but only if they don't start the word (a soft hyphen
|
||||
// starting the word doesn't give any useful break opportunities). The type of the break
|
||||
// is based on the script of the character we break on.
|
||||
if (getScript(word[i]) == USCRIPT_ARABIC) {
|
||||
// For Arabic, we need to look and see if the characters around the soft hyphen
|
||||
// actually join. If they don't, we'll just insert a normal hyphen.
|
||||
result[i] = getHyphTypeForArabic(word, len, i);
|
||||
} else {
|
||||
result[i] = hyphenationTypeBasedOnScript(word[i]);
|
||||
}
|
||||
} else if (prevChar == CHAR_MIDDLE_DOT
|
||||
&& minPrefix < i && i <= len - minSuffix
|
||||
&& ((word[i - 2] == 'l' && word[i] == 'l')
|
||||
|| (word[i - 2] == 'L' && word[i] == 'L'))
|
||||
&& strcmp(locale.getLanguage(), "ca") == 0) {
|
||||
// In Catalan, "l·l" should break as "l-" on the first line
|
||||
// and "l" on the next line.
|
||||
result[i] = HyphenationType::BREAK_AND_REPLACE_WITH_HYPHEN;
|
||||
} else {
|
||||
result[i] = HyphenationType::DONT_BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HyphenationType Hyphenator::alphabetLookup(uint16_t* alpha_codes, const uint16_t* word,
|
||||
size_t len) {
|
||||
const Header* header = getHeader();
|
||||
HyphenationType result = HyphenationType::BREAK_AND_INSERT_HYPHEN;
|
||||
// TODO: check header magic
|
||||
uint32_t alphabetVersion = header->alphabetVersion();
|
||||
if (alphabetVersion == 0) {
|
||||
const AlphabetTable0* alphabet = header->alphabetTable0();
|
||||
uint32_t min_codepoint = alphabet->min_codepoint;
|
||||
uint32_t max_codepoint = alphabet->max_codepoint;
|
||||
alpha_codes[0] = 0; // word start
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint16_t c = word[i];
|
||||
if (c < min_codepoint || c >= max_codepoint) {
|
||||
return HyphenationType::DONT_BREAK;
|
||||
}
|
||||
uint8_t code = alphabet->data[c - min_codepoint];
|
||||
if (code == 0) {
|
||||
return HyphenationType::DONT_BREAK;
|
||||
}
|
||||
if (result == HyphenationType::BREAK_AND_INSERT_HYPHEN) {
|
||||
result = hyphenationTypeBasedOnScript(c);
|
||||
}
|
||||
alpha_codes[i + 1] = code;
|
||||
}
|
||||
alpha_codes[len + 1] = 0; // word termination
|
||||
return result;
|
||||
} else if (alphabetVersion == 1) {
|
||||
const AlphabetTable1* alphabet = header->alphabetTable1();
|
||||
size_t n_entries = alphabet->n_entries;
|
||||
const uint32_t* begin = alphabet->data;
|
||||
const uint32_t* end = begin + n_entries;
|
||||
alpha_codes[0] = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint16_t c = word[i];
|
||||
auto p = std::lower_bound(begin, end, c << 11);
|
||||
if (p == end) {
|
||||
return HyphenationType::DONT_BREAK;
|
||||
}
|
||||
uint32_t entry = *p;
|
||||
if (AlphabetTable1::codepoint(entry) != c) {
|
||||
return HyphenationType::DONT_BREAK;
|
||||
}
|
||||
if (result == HyphenationType::BREAK_AND_INSERT_HYPHEN) {
|
||||
result = hyphenationTypeBasedOnScript(c);
|
||||
}
|
||||
alpha_codes[i + 1] = AlphabetTable1::value(entry);
|
||||
}
|
||||
alpha_codes[len + 1] = 0;
|
||||
return result;
|
||||
}
|
||||
return HyphenationType::DONT_BREAK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation, after conversion to codes. All case folding and normalization
|
||||
* has been done by now, and all characters have been found in the alphabet.
|
||||
* Note: len here is the padded length including 0 codes at start and end.
|
||||
**/
|
||||
void Hyphenator::hyphenateFromCodes(HyphenationType* result, const uint16_t* codes, size_t len,
|
||||
HyphenationType hyphenValue) {
|
||||
static_assert(sizeof(HyphenationType) == sizeof(uint8_t), "HyphnationType must be uint8_t.");
|
||||
// Reuse the result array as a buffer for calculating intermediate hyphenation numbers.
|
||||
uint8_t* buffer = reinterpret_cast<uint8_t*>(result);
|
||||
|
||||
const Header* header = getHeader();
|
||||
const Trie* trie = header->trieTable();
|
||||
const Pattern* pattern = header->patternTable();
|
||||
uint32_t char_mask = trie->char_mask;
|
||||
uint32_t link_shift = trie->link_shift;
|
||||
uint32_t link_mask = trie->link_mask;
|
||||
uint32_t pattern_shift = trie->pattern_shift;
|
||||
size_t maxOffset = len - minSuffix - 1;
|
||||
for (size_t i = 0; i < len - 1; i++) {
|
||||
uint32_t node = 0; // index into Trie table
|
||||
for (size_t j = i; j < len; j++) {
|
||||
uint16_t c = codes[j];
|
||||
uint32_t entry = trie->data[node + c];
|
||||
if ((entry & char_mask) == c) {
|
||||
node = (entry & link_mask) >> link_shift;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
uint32_t pat_ix = trie->data[node] >> pattern_shift;
|
||||
// pat_ix contains a 3-tuple of length, shift (number of trailing zeros), and an offset
|
||||
// into the buf pool. This is the pattern for the substring (i..j) we just matched,
|
||||
// which we combine (via point-wise max) into the buffer vector.
|
||||
if (pat_ix != 0) {
|
||||
uint32_t pat_entry = pattern->data[pat_ix];
|
||||
int pat_len = Pattern::len(pat_entry);
|
||||
int pat_shift = Pattern::shift(pat_entry);
|
||||
const uint8_t* pat_buf = pattern->buf(pat_entry);
|
||||
int offset = j + 1 - (pat_len + pat_shift);
|
||||
// offset is the index within buffer that lines up with the start of pat_buf
|
||||
int start = std::max((int)minPrefix - offset, 0);
|
||||
int end = std::min(pat_len, (int)maxOffset - offset);
|
||||
for (int k = start; k < end; k++) {
|
||||
buffer[offset + k] = std::max(buffer[offset + k], pat_buf[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Since the above calculation does not modify values outside
|
||||
// [minPrefix, len - minSuffix], they are left as 0 = DONT_BREAK.
|
||||
for (size_t i = minPrefix; i < maxOffset; i++) {
|
||||
// Hyphenation opportunities happen when the hyphenation numbers are odd.
|
||||
result[i] = (buffer[i] & 1u) ? hyphenValue : HyphenationType::DONT_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
1121
engine/src/flutter/third_party/txt/libs/minikin/Layout.cpp
vendored
Normal file
1121
engine/src/flutter/third_party/txt/libs/minikin/Layout.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
89
engine/src/flutter/third_party/txt/libs/minikin/LayoutUtils.cpp
vendored
Normal file
89
engine/src/flutter/third_party/txt/libs/minikin/LayoutUtils.cpp
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include "LayoutUtils.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const uint16_t CHAR_NBSP = 0x00A0;
|
||||
|
||||
/*
|
||||
* Determine whether the code unit is a word space for the purposes of justification.
|
||||
*/
|
||||
bool isWordSpace(uint16_t code_unit) {
|
||||
return code_unit == ' ' || code_unit == CHAR_NBSP;
|
||||
}
|
||||
|
||||
/**
|
||||
* For the purpose of layout, a word break is a boundary with no
|
||||
* kerning or complex script processing. This is necessarily a
|
||||
* heuristic, but should be accurate most of the time.
|
||||
*/
|
||||
static bool isWordBreakAfter(uint16_t c) {
|
||||
if (isWordSpace(c) || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
|
||||
// spaces
|
||||
return true;
|
||||
}
|
||||
// Note: kana is not included, as sophisticated fonts may kern kana
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isWordBreakBefore(uint16_t c) {
|
||||
// CJK ideographs (and yijing hexagram symbols)
|
||||
return isWordBreakAfter(c) || (c >= 0x3400 && c <= 0x9fff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return offset of previous word break. It is either < offset or == 0.
|
||||
*/
|
||||
size_t getPrevWordBreakForCache(
|
||||
const uint16_t* chars, size_t offset, size_t len) {
|
||||
if (offset == 0) return 0;
|
||||
if (offset > len) offset = len;
|
||||
if (isWordBreakBefore(chars[offset - 1])) {
|
||||
return offset - 1;
|
||||
}
|
||||
for (size_t i = offset - 1; i > 0; i--) {
|
||||
if (isWordBreakBefore(chars[i]) || isWordBreakAfter(chars[i - 1])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return offset of next word break. It is either > offset or == len.
|
||||
*/
|
||||
size_t getNextWordBreakForCache(
|
||||
const uint16_t* chars, size_t offset, size_t len) {
|
||||
if (offset >= len) return len;
|
||||
if (isWordBreakAfter(chars[offset])) {
|
||||
return offset + 1;
|
||||
}
|
||||
for (size_t i = offset + 1; i < len; i++) {
|
||||
// No need to check isWordBreakAfter(chars[i - 1]) since it is checked
|
||||
// in previous iteration. Note that isWordBreakBefore returns true
|
||||
// whenever isWordBreakAfter returns true.
|
||||
if (isWordBreakBefore(chars[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
51
engine/src/flutter/third_party/txt/libs/minikin/LayoutUtils.h
vendored
Normal file
51
engine/src/flutter/third_party/txt/libs/minikin/LayoutUtils.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MINIKIN_LAYOUT_UTILS_H
|
||||
#define MINIKIN_LAYOUT_UTILS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
/*
|
||||
* Determine whether the code unit is a word space for the purposes of justification.
|
||||
*/
|
||||
bool isWordSpace(uint16_t code_unit);
|
||||
|
||||
/**
|
||||
* Return offset of previous word break. It is either < offset or == 0.
|
||||
*
|
||||
* For the purpose of layout, a word break is a boundary with no
|
||||
* kerning or complex script processing. This is necessarily a
|
||||
* heuristic, but should be accurate most of the time.
|
||||
*/
|
||||
size_t getPrevWordBreakForCache(
|
||||
const uint16_t* chars, size_t offset, size_t len);
|
||||
|
||||
/**
|
||||
* Return offset of next word break. It is either > offset or == len.
|
||||
*
|
||||
* For the purpose of layout, a word break is a boundary with no
|
||||
* kerning or complex script processing. This is necessarily a
|
||||
* heuristic, but should be accurate most of the time.
|
||||
*/
|
||||
size_t getNextWordBreakForCache(
|
||||
const uint16_t* chars, size_t offset, size_t len);
|
||||
|
||||
} // namespace minikin
|
||||
#endif // MINIKIN_LAYOUT_UTILS_H
|
||||
523
engine/src/flutter/third_party/txt/libs/minikin/LineBreaker.cpp
vendored
Normal file
523
engine/src/flutter/third_party/txt/libs/minikin/LineBreaker.cpp
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define VERBOSE_DEBUG 0
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include "LayoutUtils.h"
|
||||
#include <minikin/Layout.h>
|
||||
#include <minikin/LineBreaker.h>
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const int CHAR_TAB = 0x0009;
|
||||
|
||||
// Large scores in a hierarchy; we prefer desperate breaks to an overfull line. All these
|
||||
// constants are larger than any reasonable actual width score.
|
||||
const float SCORE_INFTY = std::numeric_limits<float>::max();
|
||||
const float SCORE_OVERFULL = 1e12f;
|
||||
const float SCORE_DESPERATE = 1e10f;
|
||||
|
||||
// Multiplier for hyphen penalty on last line.
|
||||
const float LAST_LINE_PENALTY_MULTIPLIER = 4.0f;
|
||||
// Penalty assigned to each line break (to try to minimize number of lines)
|
||||
// TODO: when we implement full justification (so spaces can shrink and stretch), this is
|
||||
// probably not the most appropriate method.
|
||||
const float LINE_PENALTY_MULTIPLIER = 2.0f;
|
||||
|
||||
// Penalty assigned to shrinking the whitepsace.
|
||||
const float SHRINK_PENALTY_MULTIPLIER = 4.0f;
|
||||
|
||||
// Very long words trigger O(n^2) behavior in hyphenation, so we disable hyphenation for
|
||||
// unreasonably long words. This is somewhat of a heuristic because extremely long words
|
||||
// are possible in some languages. This does mean that very long real words can get
|
||||
// broken by desperate breaks, with no hyphens.
|
||||
const size_t LONGEST_HYPHENATED_WORD = 45;
|
||||
|
||||
// When the text buffer is within this limit, capacity of vectors is retained at finish(),
|
||||
// to avoid allocation.
|
||||
const size_t MAX_TEXT_BUF_RETAIN = 32678;
|
||||
|
||||
// Maximum amount that spaces can shrink, in justified text.
|
||||
const float SHRINKABILITY = 1.0 / 3.0;
|
||||
|
||||
void LineBreaker::setLocale(const icu::Locale& locale, Hyphenator* hyphenator) {
|
||||
mWordBreaker.setLocale(locale);
|
||||
mLocale = locale;
|
||||
mHyphenator = hyphenator;
|
||||
}
|
||||
|
||||
void LineBreaker::setText() {
|
||||
mWordBreaker.setText(mTextBuf.data(), mTextBuf.size());
|
||||
|
||||
// handle initial break here because addStyleRun may never be called
|
||||
mWordBreaker.next();
|
||||
mCandidates.clear();
|
||||
Candidate cand = {0, 0, 0.0, 0.0, 0.0, 0.0, 0, 0, 0, HyphenationType::DONT_BREAK};
|
||||
mCandidates.push_back(cand);
|
||||
|
||||
// reset greedy breaker state
|
||||
mBreaks.clear();
|
||||
mWidths.clear();
|
||||
mFlags.clear();
|
||||
mLastBreak = 0;
|
||||
mBestBreak = 0;
|
||||
mBestScore = SCORE_INFTY;
|
||||
mPreBreak = 0;
|
||||
mLastHyphenation = HyphenEdit::NO_EDIT;
|
||||
mFirstTabIndex = INT_MAX;
|
||||
mSpaceCount = 0;
|
||||
}
|
||||
|
||||
void LineBreaker::setLineWidths(float firstWidth, int firstWidthLineCount, float restWidth) {
|
||||
mLineWidths.setWidths(firstWidth, firstWidthLineCount, restWidth);
|
||||
}
|
||||
|
||||
|
||||
void LineBreaker::setIndents(const std::vector<float>& indents) {
|
||||
mLineWidths.setIndents(indents);
|
||||
}
|
||||
|
||||
// This function determines whether a character is a space that disappears at end of line.
|
||||
// It is the Unicode set: [[:General_Category=Space_Separator:]-[:Line_Break=Glue:]],
|
||||
// plus '\n'.
|
||||
// Note: all such characters are in the BMP, so it's ok to use code units for this.
|
||||
static bool isLineEndSpace(uint16_t c) {
|
||||
return c == '\n' || c == ' ' || c == 0x1680 || (0x2000 <= c && c <= 0x200A && c != 0x2007) ||
|
||||
c == 0x205F || c == 0x3000;
|
||||
}
|
||||
|
||||
// Ordinarily, this method measures the text in the range given. However, when paint
|
||||
// is nullptr, it assumes the widths have already been calculated and stored in the
|
||||
// width buffer.
|
||||
// This method finds the candidate word breaks (using the ICU break iterator) and sends them
|
||||
// to addCandidate.
|
||||
float LineBreaker::addStyleRun(MinikinPaint* paint, const std::shared_ptr<FontCollection>& typeface,
|
||||
FontStyle style, size_t start, size_t end, bool isRtl, double letterSpacing) {
|
||||
float width = 0.0f;
|
||||
int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
|
||||
|
||||
float hyphenPenalty = 0.0;
|
||||
if (paint != nullptr) {
|
||||
width = Layout::measureText(mTextBuf.data(), start, end - start, mTextBuf.size(), bidiFlags,
|
||||
style, *paint, typeface, mCharWidths.data() + start);
|
||||
|
||||
// a heuristic that seems to perform well
|
||||
hyphenPenalty = 0.5 * paint->size * paint->scaleX * mLineWidths.getLineWidth(0);
|
||||
if (mHyphenationFrequency == kHyphenationFrequency_Normal) {
|
||||
hyphenPenalty *= 4.0; // TODO: Replace with a better value after some testing
|
||||
}
|
||||
|
||||
if (mJustified) {
|
||||
// Make hyphenation more aggressive for fully justified text (so that "normal" in
|
||||
// justified mode is the same as "full" in ragged-right).
|
||||
hyphenPenalty *= 0.25;
|
||||
} else {
|
||||
// Line penalty is zero for justified text.
|
||||
mLinePenalty = std::max(mLinePenalty, hyphenPenalty * LINE_PENALTY_MULTIPLIER);
|
||||
}
|
||||
}
|
||||
|
||||
size_t current = (size_t)mWordBreaker.current();
|
||||
size_t afterWord = start;
|
||||
size_t lastBreak = start;
|
||||
ParaWidth lastBreakWidth = mWidth;
|
||||
ParaWidth postBreak = mWidth;
|
||||
size_t postSpaceCount = mSpaceCount;
|
||||
for (size_t i = start; i < end; i++) {
|
||||
uint16_t c = mTextBuf[i];
|
||||
if (c == CHAR_TAB) {
|
||||
mWidth = mPreBreak + mTabStops.nextTab(mWidth - mPreBreak);
|
||||
if (mFirstTabIndex == INT_MAX) {
|
||||
mFirstTabIndex = (int)i;
|
||||
}
|
||||
// fall back to greedy; other modes don't know how to deal with tabs
|
||||
mStrategy = kBreakStrategy_Greedy;
|
||||
} else {
|
||||
if (isWordSpace(c)) mSpaceCount += 1;
|
||||
mWidth += mCharWidths[i];
|
||||
if (c == '\n') mWidth += INT_MAX;
|
||||
if (!isLineEndSpace(c)) {
|
||||
postBreak = mWidth;
|
||||
postSpaceCount = mSpaceCount;
|
||||
afterWord = i + 1;
|
||||
}
|
||||
}
|
||||
if (i + 1 == current) {
|
||||
size_t wordStart = mWordBreaker.wordStart();
|
||||
size_t wordEnd = mWordBreaker.wordEnd();
|
||||
if (paint != nullptr && mHyphenator != nullptr &&
|
||||
mHyphenationFrequency != kHyphenationFrequency_None &&
|
||||
wordStart >= start && wordEnd > wordStart &&
|
||||
wordEnd - wordStart <= LONGEST_HYPHENATED_WORD) {
|
||||
mHyphenator->hyphenate(&mHyphBuf,
|
||||
&mTextBuf[wordStart],
|
||||
wordEnd - wordStart,
|
||||
mLocale);
|
||||
#if VERBOSE_DEBUG
|
||||
std::string hyphenatedString;
|
||||
for (size_t j = wordStart; j < wordEnd; j++) {
|
||||
if (mHyphBuf[j - wordStart] == HyphenationType::BREAK_AND_INSERT_HYPHEN) {
|
||||
hyphenatedString.push_back('-');
|
||||
}
|
||||
// Note: only works with ASCII, should do UTF-8 conversion here
|
||||
hyphenatedString.push_back(buffer()[j]);
|
||||
}
|
||||
ALOGD("hyphenated string: %s", hyphenatedString.c_str());
|
||||
#endif
|
||||
|
||||
// measure hyphenated substrings
|
||||
for (size_t j = wordStart; j < wordEnd; j++) {
|
||||
HyphenationType hyph = mHyphBuf[j - wordStart];
|
||||
if (hyph != HyphenationType::DONT_BREAK) {
|
||||
paint->hyphenEdit = HyphenEdit::editForThisLine(hyph);
|
||||
const float firstPartWidth = Layout::measureText(mTextBuf.data(),
|
||||
lastBreak, j - lastBreak, mTextBuf.size(), bidiFlags, style,
|
||||
*paint, typeface, nullptr);
|
||||
ParaWidth hyphPostBreak = lastBreakWidth + firstPartWidth;
|
||||
|
||||
paint->hyphenEdit = HyphenEdit::editForNextLine(hyph);
|
||||
const float secondPartWidth = Layout::measureText(mTextBuf.data(), j,
|
||||
afterWord - j, mTextBuf.size(), bidiFlags, style, *paint,
|
||||
typeface, nullptr);
|
||||
ParaWidth hyphPreBreak = postBreak - secondPartWidth;
|
||||
|
||||
addWordBreak(j, hyphPreBreak, hyphPostBreak, postSpaceCount, postSpaceCount,
|
||||
hyphenPenalty, hyph);
|
||||
|
||||
paint->hyphenEdit = HyphenEdit::NO_EDIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip break for zero-width characters inside replacement span
|
||||
if (paint != nullptr || current == end || mCharWidths[current] > 0) {
|
||||
float penalty = hyphenPenalty * mWordBreaker.breakBadness();
|
||||
addWordBreak(current, mWidth, postBreak, mSpaceCount, postSpaceCount, penalty,
|
||||
HyphenationType::DONT_BREAK);
|
||||
}
|
||||
lastBreak = current;
|
||||
lastBreakWidth = mWidth;
|
||||
current = (size_t)mWordBreaker.next();
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
// add a word break (possibly for a hyphenated fragment), and add desperate breaks if
|
||||
// needed (ie when word exceeds current line width)
|
||||
void LineBreaker::addWordBreak(size_t offset, ParaWidth preBreak, ParaWidth postBreak,
|
||||
size_t preSpaceCount, size_t postSpaceCount, float penalty, HyphenationType hyph) {
|
||||
Candidate cand;
|
||||
ParaWidth width = mCandidates.back().preBreak;
|
||||
if (postBreak - width > currentLineWidth()) {
|
||||
// Add desperate breaks.
|
||||
// Note: these breaks are based on the shaping of the (non-broken) original text; they
|
||||
// are imprecise especially in the presence of kerning, ligatures, and Arabic shaping.
|
||||
size_t i = mCandidates.back().offset;
|
||||
width += mCharWidths[i++];
|
||||
for (; i < offset; i++) {
|
||||
float w = mCharWidths[i];
|
||||
if (w > 0) {
|
||||
cand.offset = i;
|
||||
cand.preBreak = width;
|
||||
cand.postBreak = width;
|
||||
// postSpaceCount doesn't include trailing spaces
|
||||
cand.preSpaceCount = postSpaceCount;
|
||||
cand.postSpaceCount = postSpaceCount;
|
||||
cand.penalty = SCORE_DESPERATE;
|
||||
cand.hyphenType = HyphenationType::BREAK_AND_DONT_INSERT_HYPHEN;
|
||||
#if VERBOSE_DEBUG
|
||||
ALOGD("desperate cand: %zd %g:%g",
|
||||
mCandidates.size(), cand.postBreak, cand.preBreak);
|
||||
#endif
|
||||
addCandidate(cand);
|
||||
width += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cand.offset = offset;
|
||||
cand.preBreak = preBreak;
|
||||
cand.postBreak = postBreak;
|
||||
cand.penalty = penalty;
|
||||
cand.preSpaceCount = preSpaceCount;
|
||||
cand.postSpaceCount = postSpaceCount;
|
||||
cand.hyphenType = hyph;
|
||||
#if VERBOSE_DEBUG
|
||||
ALOGD("cand: %zd %g:%g", mCandidates.size(), cand.postBreak, cand.preBreak);
|
||||
#endif
|
||||
addCandidate(cand);
|
||||
}
|
||||
|
||||
// Helper method for addCandidate()
|
||||
void LineBreaker::pushGreedyBreak() {
|
||||
const Candidate& bestCandidate = mCandidates[mBestBreak];
|
||||
pushBreak(bestCandidate.offset, bestCandidate.postBreak - mPreBreak,
|
||||
mLastHyphenation | HyphenEdit::editForThisLine(bestCandidate.hyphenType));
|
||||
mBestScore = SCORE_INFTY;
|
||||
#if VERBOSE_DEBUG
|
||||
ALOGD("break: %d %g", mBreaks.back(), mWidths.back());
|
||||
#endif
|
||||
mLastBreak = mBestBreak;
|
||||
mPreBreak = bestCandidate.preBreak;
|
||||
mLastHyphenation = HyphenEdit::editForNextLine(bestCandidate.hyphenType);
|
||||
}
|
||||
|
||||
// TODO performance: could avoid populating mCandidates if greedy only
|
||||
void LineBreaker::addCandidate(Candidate cand) {
|
||||
const size_t candIndex = mCandidates.size();
|
||||
mCandidates.push_back(cand);
|
||||
|
||||
// mLastBreak is the index of the last line break we decided to do in mCandidates,
|
||||
// and mPreBreak is its preBreak value. mBestBreak is the index of the best line breaking candidate
|
||||
// we have found since then, and mBestScore is its penalty.
|
||||
if (cand.postBreak - mPreBreak > currentLineWidth()) {
|
||||
// This break would create an overfull line, pick the best break and break there (greedy)
|
||||
if (mBestBreak == mLastBreak) {
|
||||
// No good break has been found since last break. Break here.
|
||||
mBestBreak = candIndex;
|
||||
}
|
||||
pushGreedyBreak();
|
||||
}
|
||||
|
||||
while (mLastBreak != candIndex && cand.postBreak - mPreBreak > currentLineWidth()) {
|
||||
// We should rarely come here. But if we are here, we have broken the line, but the
|
||||
// remaining part still doesn't fit. We now need to break at the second best place after the
|
||||
// last break, but we have not kept that information, so we need to go back and find it.
|
||||
//
|
||||
// In some really rare cases, postBreak - preBreak of a candidate itself may be over the
|
||||
// current line width. We protect ourselves against an infinite loop in that case by
|
||||
// checking that we have not broken the line at this candidate already.
|
||||
for (size_t i = mLastBreak + 1; i < candIndex; i++) {
|
||||
const float penalty = mCandidates[i].penalty;
|
||||
if (penalty <= mBestScore) {
|
||||
mBestBreak = i;
|
||||
mBestScore = penalty;
|
||||
}
|
||||
}
|
||||
if (mBestBreak == mLastBreak) {
|
||||
// We didn't find anything good. Break here.
|
||||
mBestBreak = candIndex;
|
||||
}
|
||||
pushGreedyBreak();
|
||||
}
|
||||
|
||||
if (cand.penalty <= mBestScore) {
|
||||
mBestBreak = candIndex;
|
||||
mBestScore = cand.penalty;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBreaker::pushBreak(int offset, float width, uint8_t hyphenEdit) {
|
||||
mBreaks.push_back(offset);
|
||||
mWidths.push_back(width);
|
||||
int flags = (mFirstTabIndex < mBreaks.back()) << kTab_Shift;
|
||||
flags |= hyphenEdit;
|
||||
mFlags.push_back(flags);
|
||||
mFirstTabIndex = INT_MAX;
|
||||
}
|
||||
|
||||
void LineBreaker::addReplacement(size_t start, size_t end, float width) {
|
||||
mCharWidths[start] = width;
|
||||
std::fill(&mCharWidths[start + 1], &mCharWidths[end], 0.0f);
|
||||
addStyleRun(nullptr, nullptr, FontStyle(), start, end, false, 0);
|
||||
}
|
||||
|
||||
// Get the width of a space. May return 0 if there are no spaces.
|
||||
// Note: if there are multiple different widths for spaces (for example, because of mixing of
|
||||
// fonts), it's only guaranteed to pick one.
|
||||
float LineBreaker::getSpaceWidth() const {
|
||||
for (size_t i = 0; i < mTextBuf.size(); i++) {
|
||||
if (isWordSpace(mTextBuf[i])) {
|
||||
return mCharWidths[i];
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float LineBreaker::currentLineWidth() const {
|
||||
return mLineWidths.getLineWidth(mBreaks.size());
|
||||
}
|
||||
|
||||
void LineBreaker::computeBreaksGreedy() {
|
||||
// All breaks but the last have been added in addCandidate already.
|
||||
size_t nCand = mCandidates.size();
|
||||
if (nCand > 0 && (nCand == 1 || mLastBreak != nCand - 1)) {
|
||||
pushBreak(mCandidates[nCand - 1].offset, mCandidates[nCand - 1].postBreak - mPreBreak,
|
||||
mLastHyphenation);
|
||||
// don't need to update mBestScore, because we're done
|
||||
#if VERBOSE_DEBUG
|
||||
ALOGD("final break: %d %g", mBreaks.back(), mWidths.back());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Follow "prev" links in mCandidates array, and copy to result arrays.
|
||||
void LineBreaker::finishBreaksOptimal() {
|
||||
// clear existing greedy break result
|
||||
mBreaks.clear();
|
||||
mWidths.clear();
|
||||
mFlags.clear();
|
||||
size_t nCand = mCandidates.size();
|
||||
size_t prev;
|
||||
for (size_t i = nCand - 1; i > 0; i = prev) {
|
||||
prev = mCandidates[i].prev;
|
||||
mBreaks.push_back(mCandidates[i].offset);
|
||||
mWidths.push_back(mCandidates[i].postBreak - mCandidates[prev].preBreak);
|
||||
int flags = HyphenEdit::editForThisLine(mCandidates[i].hyphenType);
|
||||
if (prev > 0) {
|
||||
flags |= HyphenEdit::editForNextLine(mCandidates[prev].hyphenType);
|
||||
}
|
||||
mFlags.push_back(flags);
|
||||
}
|
||||
std::reverse(mBreaks.begin(), mBreaks.end());
|
||||
std::reverse(mWidths.begin(), mWidths.end());
|
||||
std::reverse(mFlags.begin(), mFlags.end());
|
||||
}
|
||||
|
||||
void LineBreaker::computeBreaksOptimal(bool isRectangle) {
|
||||
size_t active = 0;
|
||||
size_t nCand = mCandidates.size();
|
||||
float width = mLineWidths.getLineWidth(0);
|
||||
float shortLineFactor = mJustified ? 0.75f : 0.5f;
|
||||
float maxShrink = mJustified ? SHRINKABILITY * getSpaceWidth() : 0.0f;
|
||||
|
||||
// "i" iterates through candidates for the end of the line.
|
||||
for (size_t i = 1; i < nCand; i++) {
|
||||
bool atEnd = i == nCand - 1;
|
||||
float best = SCORE_INFTY;
|
||||
size_t bestPrev = 0;
|
||||
size_t lineNumberLast = 0;
|
||||
|
||||
if (!isRectangle) {
|
||||
size_t lineNumberLast = mCandidates[active].lineNumber;
|
||||
width = mLineWidths.getLineWidth(lineNumberLast);
|
||||
}
|
||||
ParaWidth leftEdge = mCandidates[i].postBreak - width;
|
||||
float bestHope = 0;
|
||||
|
||||
// "j" iterates through candidates for the beginning of the line.
|
||||
for (size_t j = active; j < i; j++) {
|
||||
if (!isRectangle) {
|
||||
size_t lineNumber = mCandidates[j].lineNumber;
|
||||
if (lineNumber != lineNumberLast) {
|
||||
float widthNew = mLineWidths.getLineWidth(lineNumber);
|
||||
if (widthNew != width) {
|
||||
leftEdge = mCandidates[i].postBreak - width;
|
||||
bestHope = 0;
|
||||
width = widthNew;
|
||||
}
|
||||
lineNumberLast = lineNumber;
|
||||
}
|
||||
}
|
||||
float jScore = mCandidates[j].score;
|
||||
if (jScore + bestHope >= best) continue;
|
||||
float delta = mCandidates[j].preBreak - leftEdge;
|
||||
|
||||
// compute width score for line
|
||||
|
||||
// Note: the "bestHope" optimization makes the assumption that, when delta is
|
||||
// non-negative, widthScore will increase monotonically as successive candidate
|
||||
// breaks are considered.
|
||||
float widthScore = 0.0f;
|
||||
float additionalPenalty = 0.0f;
|
||||
if ((atEnd || !mJustified) && delta < 0) {
|
||||
widthScore = SCORE_OVERFULL;
|
||||
} else if (atEnd && mStrategy != kBreakStrategy_Balanced) {
|
||||
// increase penalty for hyphen on last line
|
||||
additionalPenalty = LAST_LINE_PENALTY_MULTIPLIER * mCandidates[j].penalty;
|
||||
// Penalize very short (< 1 - shortLineFactor of total width) lines.
|
||||
float underfill = delta - shortLineFactor * width;
|
||||
widthScore = underfill > 0 ? underfill * underfill : 0;
|
||||
} else {
|
||||
widthScore = delta * delta;
|
||||
if (delta < 0) {
|
||||
if (-delta < maxShrink *
|
||||
(mCandidates[i].postSpaceCount - mCandidates[j].preSpaceCount)) {
|
||||
widthScore *= SHRINK_PENALTY_MULTIPLIER;
|
||||
} else {
|
||||
widthScore = SCORE_OVERFULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delta < 0) {
|
||||
active = j + 1;
|
||||
} else {
|
||||
bestHope = widthScore;
|
||||
}
|
||||
|
||||
float score = jScore + widthScore + additionalPenalty;
|
||||
if (score <= best) {
|
||||
best = score;
|
||||
bestPrev = j;
|
||||
}
|
||||
}
|
||||
mCandidates[i].score = best + mCandidates[i].penalty + mLinePenalty;
|
||||
mCandidates[i].prev = bestPrev;
|
||||
mCandidates[i].lineNumber = mCandidates[bestPrev].lineNumber + 1;
|
||||
#if VERBOSE_DEBUG
|
||||
ALOGD("break %zd: score=%g, prev=%zd", i, mCandidates[i].score, mCandidates[i].prev);
|
||||
#endif
|
||||
}
|
||||
finishBreaksOptimal();
|
||||
}
|
||||
|
||||
size_t LineBreaker::computeBreaks() {
|
||||
if (mStrategy == kBreakStrategy_Greedy) {
|
||||
computeBreaksGreedy();
|
||||
} else {
|
||||
computeBreaksOptimal(mLineWidths.isConstant());
|
||||
}
|
||||
return mBreaks.size();
|
||||
}
|
||||
|
||||
void LineBreaker::finish() {
|
||||
mWordBreaker.finish();
|
||||
mWidth = 0;
|
||||
mLineWidths.clear();
|
||||
mCandidates.clear();
|
||||
mBreaks.clear();
|
||||
mWidths.clear();
|
||||
mFlags.clear();
|
||||
if (mTextBuf.size() > MAX_TEXT_BUF_RETAIN) {
|
||||
mTextBuf.clear();
|
||||
mTextBuf.shrink_to_fit();
|
||||
mCharWidths.clear();
|
||||
mCharWidths.shrink_to_fit();
|
||||
mHyphBuf.clear();
|
||||
mHyphBuf.shrink_to_fit();
|
||||
mCandidates.shrink_to_fit();
|
||||
mBreaks.shrink_to_fit();
|
||||
mWidths.shrink_to_fit();
|
||||
mFlags.shrink_to_fit();
|
||||
}
|
||||
mStrategy = kBreakStrategy_Greedy;
|
||||
mHyphenationFrequency = kHyphenationFrequency_Normal;
|
||||
mLinePenalty = 0.0f;
|
||||
mJustified = false;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
124
engine/src/flutter/third_party/txt/libs/minikin/Measurement.cpp
vendored
Normal file
124
engine/src/flutter/third_party/txt/libs/minikin/Measurement.cpp
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include <cmath>
|
||||
#include <unicode/uchar.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include <minikin/GraphemeBreak.h>
|
||||
#include <minikin/Measurement.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
// These could be considered helper methods of layout, but need only be loosely coupled, so
|
||||
// are separate.
|
||||
|
||||
static float getRunAdvance(const float* advances, const uint16_t* buf, size_t layoutStart,
|
||||
size_t start, size_t count, size_t offset) {
|
||||
float advance = 0.0f;
|
||||
size_t lastCluster = start;
|
||||
float clusterWidth = 0.0f;
|
||||
for (size_t i = start; i < offset; i++) {
|
||||
float charAdvance = advances[i - layoutStart];
|
||||
if (charAdvance != 0.0f) {
|
||||
advance += charAdvance;
|
||||
lastCluster = i;
|
||||
clusterWidth = charAdvance;
|
||||
}
|
||||
}
|
||||
if (offset < start + count && advances[offset - layoutStart] == 0.0f) {
|
||||
// In the middle of a cluster, distribute width of cluster so that each grapheme cluster
|
||||
// gets an equal share.
|
||||
// TODO: get caret information out of font when that's available
|
||||
size_t nextCluster;
|
||||
for (nextCluster = offset + 1; nextCluster < start + count; nextCluster++) {
|
||||
if (advances[nextCluster - layoutStart] != 0.0f) break;
|
||||
}
|
||||
int numGraphemeClusters = 0;
|
||||
int numGraphemeClustersAfter = 0;
|
||||
for (size_t i = lastCluster; i < nextCluster; i++) {
|
||||
bool isAfter = i >= offset;
|
||||
if (GraphemeBreak::isGraphemeBreak(
|
||||
advances + (start - layoutStart), buf, start, count, i)) {
|
||||
numGraphemeClusters++;
|
||||
if (isAfter) {
|
||||
numGraphemeClustersAfter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numGraphemeClusters > 0) {
|
||||
advance -= clusterWidth * numGraphemeClustersAfter / numGraphemeClusters;
|
||||
}
|
||||
}
|
||||
return advance;
|
||||
}
|
||||
|
||||
float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
|
||||
size_t offset) {
|
||||
return getRunAdvance(advances, buf, start, start, count, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Essentially the inverse of getRunAdvance. Compute the value of offset for which the
|
||||
* measured caret comes closest to the provided advance param, and which is on a grapheme
|
||||
* cluster boundary.
|
||||
*
|
||||
* The actual implementation fast-forwards through clusters to get "close", then does a finer-grain
|
||||
* search within the cluster and grapheme breaks.
|
||||
*/
|
||||
size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count,
|
||||
float advance) {
|
||||
float x = 0.0f, xLastClusterStart = 0.0f, xSearchStart = 0.0f;
|
||||
size_t lastClusterStart = start, searchStart = start;
|
||||
for (size_t i = start; i < start + count; i++) {
|
||||
if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) {
|
||||
searchStart = lastClusterStart;
|
||||
xSearchStart = xLastClusterStart;
|
||||
}
|
||||
float width = advances[i - start];
|
||||
if (width != 0.0f) {
|
||||
lastClusterStart = i;
|
||||
xLastClusterStart = x;
|
||||
x += width;
|
||||
if (x > advance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t best = searchStart;
|
||||
float bestDist = FLT_MAX;
|
||||
for (size_t i = searchStart; i <= start + count; i++) {
|
||||
if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) {
|
||||
// "getRunAdvance(layout, buf, start, count, i) - advance" but more efficient
|
||||
float delta = getRunAdvance(advances, buf, start, searchStart, count - searchStart, i)
|
||||
|
||||
+ xSearchStart - advance;
|
||||
if (std::abs(delta) < bestDist) {
|
||||
bestDist = std::abs(delta);
|
||||
best = i;
|
||||
}
|
||||
if (delta >= 0.0f) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
28
engine/src/flutter/third_party/txt/libs/minikin/MinikinFont.cpp
vendored
Normal file
28
engine/src/flutter/third_party/txt/libs/minikin/MinikinFont.cpp
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
#include "HbFontCache.h"
|
||||
#include "MinikinInternal.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
MinikinFont::~MinikinFont() {
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
purgeHbFontLocked(this);
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
44
engine/src/flutter/third_party/txt/libs/minikin/MinikinInternal.cpp
vendored
Normal file
44
engine/src/flutter/third_party/txt/libs/minikin/MinikinInternal.cpp
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Definitions internal to Minikin
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include "MinikinInternal.h"
|
||||
#include "HbFontCache.h"
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
std::mutex gMinikinLock;
|
||||
|
||||
void assertMinikinLocked() {
|
||||
#ifdef ENABLE_RACE_DETECTION
|
||||
LOG_ALWAYS_FATAL_IF(gMinikinLock.tryLock() == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag) {
|
||||
assertMinikinLocked();
|
||||
hb_font_t* font = getHbFontLocked(minikinFont);
|
||||
hb_face_t* face = hb_font_get_face(font);
|
||||
hb_blob_t* blob = hb_face_reference_table(face, tag);
|
||||
hb_font_destroy(font);
|
||||
return blob;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
70
engine/src/flutter/third_party/txt/libs/minikin/MinikinInternal.h
vendored
Normal file
70
engine/src/flutter/third_party/txt/libs/minikin/MinikinInternal.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Definitions internal to Minikin
|
||||
|
||||
#ifndef MINIKIN_INTERNAL_H
|
||||
#define MINIKIN_INTERNAL_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <hb.h>
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
// All external Minikin interfaces are designed to be thread-safe.
|
||||
// Presently, that's implemented by through a global lock, and having
|
||||
// all external interfaces take that lock.
|
||||
|
||||
extern std::mutex gMinikinLock;
|
||||
|
||||
// Aborts if gMinikinLock is not acquired. Do nothing on the release build.
|
||||
void assertMinikinLocked();
|
||||
|
||||
hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag);
|
||||
|
||||
constexpr uint32_t MAX_UNICODE_CODE_POINT = 0x10FFFF;
|
||||
|
||||
// An RAII wrapper for hb_blob_t
|
||||
class HbBlob {
|
||||
public:
|
||||
// Takes ownership of hb_blob_t object, caller is no longer
|
||||
// responsible for calling hb_blob_destroy().
|
||||
explicit HbBlob(hb_blob_t* blob) : mBlob(blob) {
|
||||
}
|
||||
|
||||
~HbBlob() {
|
||||
hb_blob_destroy(mBlob);
|
||||
}
|
||||
|
||||
const uint8_t* get() const {
|
||||
const char* data = hb_blob_get_data(mBlob, nullptr);
|
||||
return reinterpret_cast<const uint8_t*>(data);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return (size_t)hb_blob_get_length(mBlob);
|
||||
}
|
||||
|
||||
private:
|
||||
hb_blob_t* mBlob;
|
||||
};
|
||||
|
||||
} // namespace minikin
|
||||
|
||||
#endif // MINIKIN_INTERNAL_H
|
||||
146
engine/src/flutter/third_party/txt/libs/minikin/SparseBitSet.cpp
vendored
Normal file
146
engine/src/flutter/third_party/txt/libs/minikin/SparseBitSet.cpp
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "SparseBitSet"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include <minikin/SparseBitSet.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const uint32_t SparseBitSet::kNotFound;
|
||||
|
||||
uint32_t SparseBitSet::calcNumPages(const uint32_t* ranges, size_t nRanges) {
|
||||
bool haveZeroPage = false;
|
||||
uint32_t nonzeroPageEnd = 0;
|
||||
uint32_t nPages = 0;
|
||||
for (size_t i = 0; i < nRanges; i++) {
|
||||
uint32_t start = ranges[i * 2];
|
||||
uint32_t end = ranges[i * 2 + 1];
|
||||
uint32_t startPage = start >> kLogValuesPerPage;
|
||||
uint32_t endPage = (end - 1) >> kLogValuesPerPage;
|
||||
if (startPage >= nonzeroPageEnd) {
|
||||
if (startPage > nonzeroPageEnd) {
|
||||
if (!haveZeroPage) {
|
||||
haveZeroPage = true;
|
||||
nPages++;
|
||||
}
|
||||
}
|
||||
nPages++;
|
||||
}
|
||||
nPages += endPage - startPage;
|
||||
nonzeroPageEnd = endPage + 1;
|
||||
}
|
||||
return nPages;
|
||||
}
|
||||
|
||||
void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
|
||||
if (nRanges == 0) {
|
||||
return;
|
||||
}
|
||||
const uint32_t maxVal = ranges[nRanges * 2 - 1];
|
||||
if (maxVal >= kMaximumCapacity) {
|
||||
return;
|
||||
}
|
||||
mMaxVal = maxVal;
|
||||
mIndices.reset(new uint16_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
|
||||
uint32_t nPages = calcNumPages(ranges, nRanges);
|
||||
mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]());
|
||||
mZeroPageIndex = noZeroPage;
|
||||
uint32_t nonzeroPageEnd = 0;
|
||||
uint32_t currentPage = 0;
|
||||
for (size_t i = 0; i < nRanges; i++) {
|
||||
uint32_t start = ranges[i * 2];
|
||||
uint32_t end = ranges[i * 2 + 1];
|
||||
LOG_ALWAYS_FATAL_IF(end < start); // make sure range size is nonnegative
|
||||
uint32_t startPage = start >> kLogValuesPerPage;
|
||||
uint32_t endPage = (end - 1) >> kLogValuesPerPage;
|
||||
if (startPage >= nonzeroPageEnd) {
|
||||
if (startPage > nonzeroPageEnd) {
|
||||
if (mZeroPageIndex == noZeroPage) {
|
||||
mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
|
||||
}
|
||||
for (uint32_t j = nonzeroPageEnd; j < startPage; j++) {
|
||||
mIndices[j] = mZeroPageIndex;
|
||||
}
|
||||
}
|
||||
mIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
|
||||
}
|
||||
|
||||
size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) +
|
||||
((start & kPageMask) >> kLogBitsPerEl);
|
||||
size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl;
|
||||
if (nElements == 1) {
|
||||
mBitmaps[index] |= (kElAllOnes >> (start & kElMask)) &
|
||||
(kElAllOnes << ((~end + 1) & kElMask));
|
||||
} else {
|
||||
mBitmaps[index] |= kElAllOnes >> (start & kElMask);
|
||||
for (size_t j = 1; j < nElements - 1; j++) {
|
||||
mBitmaps[index + j] = kElAllOnes;
|
||||
}
|
||||
mBitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
|
||||
}
|
||||
for (size_t j = startPage + 1; j < endPage + 1; j++) {
|
||||
mIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
|
||||
}
|
||||
nonzeroPageEnd = endPage + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int SparseBitSet::CountLeadingZeros(element x) {
|
||||
// Note: GCC / clang builtin
|
||||
return sizeof(element) <= sizeof(int) ? __builtin_clz(x) : __builtin_clzl(x);
|
||||
}
|
||||
|
||||
uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
|
||||
if (fromIndex >= mMaxVal) {
|
||||
return kNotFound;
|
||||
}
|
||||
uint32_t fromPage = fromIndex >> kLogValuesPerPage;
|
||||
const element* bitmap = &mBitmaps[mIndices[fromPage]];
|
||||
uint32_t offset = (fromIndex & kPageMask) >> kLogBitsPerEl;
|
||||
element e = bitmap[offset] & (kElAllOnes >> (fromIndex & kElMask));
|
||||
if (e != 0) {
|
||||
return (fromIndex & ~kElMask) + CountLeadingZeros(e);
|
||||
}
|
||||
for (uint32_t j = offset + 1; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) {
|
||||
e = bitmap[j];
|
||||
if (e != 0) {
|
||||
return (fromIndex & ~kPageMask) + (j << kLogBitsPerEl) + CountLeadingZeros(e);
|
||||
}
|
||||
}
|
||||
uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
|
||||
for (uint32_t page = fromPage + 1; page < maxPage; page++) {
|
||||
uint16_t index = mIndices[page];
|
||||
if (index == mZeroPageIndex) {
|
||||
continue;
|
||||
}
|
||||
bitmap = &mBitmaps[index];
|
||||
for (uint32_t j = 0; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) {
|
||||
e = bitmap[j];
|
||||
if (e != 0) {
|
||||
return (page << kLogValuesPerPage) + (j << kLogBitsPerEl) + CountLeadingZeros(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return kNotFound;
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
271
engine/src/flutter/third_party/txt/libs/minikin/WordBreaker.cpp
vendored
Normal file
271
engine/src/flutter/third_party/txt/libs/minikin/WordBreaker.cpp
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "Minikin"
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include <minikin/Emoji.h>
|
||||
#include <minikin/Hyphenator.h>
|
||||
#include <minikin/WordBreaker.h>
|
||||
#include "MinikinInternal.h"
|
||||
|
||||
#include <unicode/uchar.h>
|
||||
#include <unicode/utf16.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const uint32_t CHAR_SOFT_HYPHEN = 0x00AD;
|
||||
const uint32_t CHAR_ZWJ = 0x200D;
|
||||
|
||||
void WordBreaker::setLocale(const icu::Locale& locale) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
mBreakIterator.reset(icu::BreakIterator::createLineInstance(locale, status));
|
||||
// TODO: handle failure status
|
||||
if (mText != nullptr) {
|
||||
mBreakIterator->setText(&mUText, status);
|
||||
}
|
||||
mIteratorWasReset = true;
|
||||
}
|
||||
|
||||
void WordBreaker::setText(const uint16_t* data, size_t size) {
|
||||
mText = data;
|
||||
mTextSize = size;
|
||||
mIteratorWasReset = false;
|
||||
mLast = 0;
|
||||
mCurrent = 0;
|
||||
mScanOffset = 0;
|
||||
mInEmailOrUrl = false;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
utext_openUChars(&mUText, data, size, &status);
|
||||
mBreakIterator->setText(&mUText, status);
|
||||
mBreakIterator->first();
|
||||
}
|
||||
|
||||
ssize_t WordBreaker::current() const {
|
||||
return mCurrent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a line break at position i within the buffer buf is valid. This
|
||||
* represents customization beyond the ICU behavior, because plain ICU provides some
|
||||
* line break opportunities that we don't want.
|
||||
**/
|
||||
static bool isBreakValid(const uint16_t* buf, size_t bufEnd, size_t i) {
|
||||
uint32_t codePoint;
|
||||
size_t prev_offset = i;
|
||||
U16_PREV(buf, 0, prev_offset, codePoint);
|
||||
// Do not break on hard or soft hyphens. These are handled by automatic hyphenation.
|
||||
if (Hyphenator::isLineBreakingHyphen(codePoint) || codePoint == CHAR_SOFT_HYPHEN) {
|
||||
// txt addition: Temporarily always break on hyphen. Changed from false to true.
|
||||
return true;
|
||||
}
|
||||
// For Myanmar kinzi sequences, created by <consonant, ASAT, VIRAMA, consonant>. This is to go
|
||||
// around a bug in ICU line breaking: http://bugs.icu-project.org/trac/ticket/12561. To avoid
|
||||
// too much looking around in the strings, we simply avoid breaking after any Myanmar virama,
|
||||
// where no line break could be imagined, since the Myanmar virama is a pure stacker.
|
||||
if (codePoint == 0x1039) { // MYANMAR SIGN VIRAMA
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t next_codepoint;
|
||||
size_t next_offset = i;
|
||||
U16_NEXT(buf, next_offset, bufEnd, next_codepoint);
|
||||
|
||||
// Rule LB8 for Emoji ZWJ sequences. We need to do this ourselves since we may have fresher
|
||||
// emoji data than ICU does.
|
||||
if (codePoint == CHAR_ZWJ && isEmoji(next_codepoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule LB30b. We need to this ourselves since we may have fresher emoji data than ICU does.
|
||||
if (isEmojiModifier(next_codepoint)) {
|
||||
if (codePoint == 0xFE0F && prev_offset > 0) {
|
||||
// skip over emoji variation selector
|
||||
U16_PREV(buf, 0, prev_offset, codePoint);
|
||||
}
|
||||
if (isEmojiBase(codePoint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Customized iteratorNext that takes care of both resets and our modifications
|
||||
// to ICU's behavior.
|
||||
int32_t WordBreaker::iteratorNext() {
|
||||
int32_t result;
|
||||
do {
|
||||
if (mIteratorWasReset) {
|
||||
result = mBreakIterator->following(mCurrent);
|
||||
mIteratorWasReset = false;
|
||||
} else {
|
||||
result = mBreakIterator->next();
|
||||
}
|
||||
} while (!(result == icu::BreakIterator::DONE || (size_t)result == mTextSize
|
||||
|| isBreakValid(mText, mTextSize, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Chicago Manual of Style recommends breaking after these characters in URLs and email addresses
|
||||
static bool breakAfter(uint16_t c) {
|
||||
return c == ':' || c == '=' || c == '&';
|
||||
}
|
||||
|
||||
// Chicago Manual of Style recommends breaking before these characters in URLs and email addresses
|
||||
static bool breakBefore(uint16_t c) {
|
||||
return c == '~' || c == '.' || c == ',' || c == '-' || c == '_' || c == '?' || c == '#'
|
||||
|| c == '%' || c == '=' || c == '&';
|
||||
}
|
||||
|
||||
enum ScanState {
|
||||
START,
|
||||
SAW_AT,
|
||||
SAW_COLON,
|
||||
SAW_COLON_SLASH,
|
||||
SAW_COLON_SLASH_SLASH,
|
||||
};
|
||||
|
||||
void WordBreaker::detectEmailOrUrl() {
|
||||
// scan forward from current ICU position for email address or URL
|
||||
if (mLast >= mScanOffset) {
|
||||
ScanState state = START;
|
||||
size_t i;
|
||||
for (i = mLast; i < mTextSize; i++) {
|
||||
uint16_t c = mText[i];
|
||||
// scan only ASCII characters, stop at space
|
||||
if (!(' ' < c && c <= 0x007E)) {
|
||||
break;
|
||||
}
|
||||
if (state == START && c == '@') {
|
||||
state = SAW_AT;
|
||||
} else if (state == START && c == ':') {
|
||||
state = SAW_COLON;
|
||||
} else if (state == SAW_COLON || state == SAW_COLON_SLASH) {
|
||||
if (c == '/') {
|
||||
state = static_cast<ScanState>((int)state + 1); // next state adds a slash
|
||||
} else {
|
||||
state = START;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state == SAW_AT || state == SAW_COLON_SLASH_SLASH) {
|
||||
if (!mBreakIterator->isBoundary(i)) {
|
||||
// If there are combining marks or such at the end of the URL or the email address,
|
||||
// consider them a part of the URL or the email, and skip to the next actual
|
||||
// boundary.
|
||||
i = mBreakIterator->following(i);
|
||||
}
|
||||
mInEmailOrUrl = true;
|
||||
mIteratorWasReset = true;
|
||||
} else {
|
||||
mInEmailOrUrl = false;
|
||||
}
|
||||
mScanOffset = i;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t WordBreaker::findNextBreakInEmailOrUrl() {
|
||||
// special rules for email addresses and URL's as per Chicago Manual of Style (16th ed.)
|
||||
uint16_t lastChar = mText[mLast];
|
||||
ssize_t i;
|
||||
for (i = mLast + 1; i < mScanOffset; i++) {
|
||||
if (breakAfter(lastChar)) {
|
||||
break;
|
||||
}
|
||||
// break after double slash
|
||||
if (lastChar == '/' && i >= mLast + 2 && mText[i - 2] == '/') {
|
||||
break;
|
||||
}
|
||||
const uint16_t thisChar = mText[i];
|
||||
// never break after hyphen
|
||||
if (lastChar != '-') {
|
||||
if (breakBefore(thisChar)) {
|
||||
break;
|
||||
}
|
||||
// break before single slash
|
||||
if (thisChar == '/' && lastChar != '/' &&
|
||||
!(i + 1 < mScanOffset && mText[i + 1] == '/')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
lastChar = thisChar;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
ssize_t WordBreaker::next() {
|
||||
mLast = mCurrent;
|
||||
|
||||
detectEmailOrUrl();
|
||||
if (mInEmailOrUrl) {
|
||||
mCurrent = findNextBreakInEmailOrUrl();
|
||||
} else { // Business as usual
|
||||
mCurrent = (ssize_t) iteratorNext();
|
||||
}
|
||||
return mCurrent;
|
||||
}
|
||||
|
||||
ssize_t WordBreaker::wordStart() const {
|
||||
if (mInEmailOrUrl) {
|
||||
return mLast;
|
||||
}
|
||||
ssize_t result = mLast;
|
||||
while (result < mCurrent) {
|
||||
UChar32 c;
|
||||
ssize_t ix = result;
|
||||
U16_NEXT(mText, ix, mCurrent, c);
|
||||
const int32_t lb = u_getIntPropertyValue(c, UCHAR_LINE_BREAK);
|
||||
// strip leading punctuation, defined as OP and QU line breaking classes,
|
||||
// see UAX #14
|
||||
if (!(lb == U_LB_OPEN_PUNCTUATION || lb == U_LB_QUOTATION)) {
|
||||
break;
|
||||
}
|
||||
result = ix;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t WordBreaker::wordEnd() const {
|
||||
if (mInEmailOrUrl) {
|
||||
return mLast;
|
||||
}
|
||||
ssize_t result = mCurrent;
|
||||
while (result > mLast) {
|
||||
UChar32 c;
|
||||
ssize_t ix = result;
|
||||
U16_PREV(mText, mLast, ix, c);
|
||||
const int32_t gc_mask = U_GET_GC_MASK(c);
|
||||
// strip trailing space and punctuation
|
||||
if ((gc_mask & (U_GC_ZS_MASK | U_GC_P_MASK)) == 0) {
|
||||
break;
|
||||
}
|
||||
result = ix;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int WordBreaker::breakBadness() const {
|
||||
return (mInEmailOrUrl && mCurrent < mScanOffset) ? 1 : 0;
|
||||
}
|
||||
|
||||
void WordBreaker::finish() {
|
||||
mText = nullptr;
|
||||
// Note: calling utext_close multiply is safe
|
||||
utext_close(&mUText);
|
||||
}
|
||||
|
||||
} // namespace minikin
|
||||
34
engine/src/flutter/third_party/txt/shims/BUILD.gn
vendored
Normal file
34
engine/src/flutter/third_party/txt/shims/BUILD.gn
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Copyright 2017 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
config("shims_config") {
|
||||
include_dirs = [ "." ]
|
||||
}
|
||||
|
||||
source_set("shims") {
|
||||
sources = [
|
||||
"log/log.h",
|
||||
"log/log.cc",
|
||||
"utils/JenkinsHash.cpp",
|
||||
"utils/JenkinsHash.h",
|
||||
"utils/LruCache.h",
|
||||
"utils/TypeHelpers.h",
|
||||
]
|
||||
|
||||
public_configs = [ ":shims_config" ]
|
||||
|
||||
public_deps = [
|
||||
"//lib/ftl",
|
||||
]
|
||||
}
|
||||
25
engine/src/flutter/third_party/txt/shims/log/log.cc
vendored
Normal file
25
engine/src/flutter/third_party/txt/shims/log/log.cc
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
int __android_log_error_write(int tag,
|
||||
const char* subTag,
|
||||
int32_t uid,
|
||||
const char* data,
|
||||
uint32_t dataLen) {
|
||||
return 0;
|
||||
}
|
||||
57
engine/src/flutter/third_party/txt/shims/log/log.h
vendored
Normal file
57
engine/src/flutter/third_party/txt/shims/log/log.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL_IF
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
((cond) ? (FTL_LOG(FATAL) << #cond) : (void)0)
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL
|
||||
#define LOG_ALWAYS_FATAL(...) FTL_LOG(FATAL)
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ASSERT
|
||||
#define LOG_ASSERT(cond, ...) FTL_CHECK(cond)
|
||||
#define ALOG_ASSERT LOG_ASSERT
|
||||
#endif
|
||||
|
||||
#ifndef ALOGD
|
||||
#define ALOGD(message, ...) FTL_DLOG(INFO) << (message)
|
||||
#endif
|
||||
|
||||
#ifndef ALOGW
|
||||
#define ALOGW(message, ...) FTL_LOG(WARNING) << (message)
|
||||
#endif
|
||||
|
||||
#ifndef ALOGE
|
||||
#define ALOGE(message, ...) FTL_LOG(ERROR) << (message)
|
||||
#endif
|
||||
|
||||
#define android_errorWriteLog(tag, subTag) \
|
||||
__android_log_error_write(tag, subTag, -1, NULL, 0)
|
||||
#define android_errorWriteWithInfoLog(tag, subTag, uid, data, dataLen) \
|
||||
__android_log_error_write(tag, subTag, uid, data, dataLen)
|
||||
int __android_log_error_write(int tag,
|
||||
const char* subTag,
|
||||
int32_t uid,
|
||||
const char* data,
|
||||
uint32_t dataLen);
|
||||
73
engine/src/flutter/third_party/txt/shims/utils/JenkinsHash.cpp
vendored
Normal file
73
engine/src/flutter/third_party/txt/shims/utils/JenkinsHash.cpp
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Implementation of Jenkins one-at-a-time hash function. These choices are
|
||||
* optimized for code size and portability, rather than raw speed. But speed
|
||||
* should still be quite good.
|
||||
**/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <utils/JenkinsHash.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
#ifdef __clang__
|
||||
__attribute__((no_sanitize("integer")))
|
||||
#endif
|
||||
hash_t JenkinsHashWhiten(uint32_t hash) {
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
|
||||
if (size > UINT32_MAX) {
|
||||
abort();
|
||||
}
|
||||
hash = JenkinsHashMix(hash, (uint32_t)size);
|
||||
size_t i;
|
||||
for (i = 0; i < (size & -4); i += 4) {
|
||||
uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
|
||||
hash = JenkinsHashMix(hash, data);
|
||||
}
|
||||
if (size & 3) {
|
||||
uint32_t data = bytes[i];
|
||||
data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0;
|
||||
data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0;
|
||||
hash = JenkinsHashMix(hash, data);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
|
||||
if (size > UINT32_MAX) {
|
||||
abort();
|
||||
}
|
||||
hash = JenkinsHashMix(hash, (uint32_t)size);
|
||||
size_t i;
|
||||
for (i = 0; i < (size & -2); i += 2) {
|
||||
uint32_t data = shorts[i] | (shorts[i+1] << 16);
|
||||
hash = JenkinsHashMix(hash, data);
|
||||
}
|
||||
if (size & 1) {
|
||||
uint32_t data = shorts[i];
|
||||
hash = JenkinsHashMix(hash, data);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
||||
51
engine/src/flutter/third_party/txt/shims/utils/JenkinsHash.h
vendored
Normal file
51
engine/src/flutter/third_party/txt/shims/utils/JenkinsHash.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Implementation of Jenkins one-at-a-time hash function. These choices are
|
||||
* optimized for code size and portability, rather than raw speed. But speed
|
||||
* should still be quite good.
|
||||
**/
|
||||
|
||||
#ifndef ANDROID_JENKINS_HASH_H
|
||||
#define ANDROID_JENKINS_HASH_H
|
||||
|
||||
#include <utils/TypeHelpers.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
/* The Jenkins hash of a sequence of 32 bit words A, B, C is:
|
||||
* Whiten(Mix(Mix(Mix(0, A), B), C)) */
|
||||
|
||||
#ifdef __clang__
|
||||
__attribute__((no_sanitize("integer")))
|
||||
#endif
|
||||
inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
|
||||
hash += data;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
return hash;
|
||||
}
|
||||
|
||||
hash_t JenkinsHashWhiten(uint32_t hash);
|
||||
|
||||
/* Helpful utility functions for hashing data in 32 bit chunks */
|
||||
uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size);
|
||||
|
||||
uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size);
|
||||
|
||||
}
|
||||
|
||||
#endif // ANDROID_JENKINS_HASH_H
|
||||
298
engine/src/flutter/third_party/txt/shims/utils/LruCache.h
vendored
Normal file
298
engine/src/flutter/third_party/txt/shims/utils/LruCache.h
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_UTILS_LRU_CACHE_H
|
||||
#define ANDROID_UTILS_LRU_CACHE_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "utils/TypeHelpers.h" // hash_t
|
||||
|
||||
namespace android {
|
||||
|
||||
/**
|
||||
* GenerationCache callback used when an item is removed
|
||||
*/
|
||||
template<typename EntryKey, typename EntryValue>
|
||||
class OnEntryRemoved {
|
||||
public:
|
||||
virtual ~OnEntryRemoved() { };
|
||||
virtual void operator()(EntryKey& key, EntryValue& value) = 0;
|
||||
}; // class OnEntryRemoved
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
class LruCache {
|
||||
public:
|
||||
explicit LruCache(uint32_t maxCapacity);
|
||||
virtual ~LruCache();
|
||||
|
||||
enum Capacity {
|
||||
kUnlimitedCapacity,
|
||||
};
|
||||
|
||||
void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
|
||||
size_t size() const;
|
||||
const TValue& get(const TKey& key);
|
||||
bool put(const TKey& key, const TValue& value);
|
||||
bool remove(const TKey& key);
|
||||
bool removeOldest();
|
||||
void clear();
|
||||
const TValue& peekOldestValue();
|
||||
|
||||
private:
|
||||
LruCache(const LruCache& that); // disallow copy constructor
|
||||
|
||||
// Super class so that we can have entries having only a key reference, for searches.
|
||||
class KeyedEntry {
|
||||
public:
|
||||
virtual const TKey& getKey() const = 0;
|
||||
// Make sure the right destructor is executed so that keys and values are deleted.
|
||||
virtual ~KeyedEntry() {}
|
||||
};
|
||||
|
||||
class Entry final : public KeyedEntry {
|
||||
public:
|
||||
TKey key;
|
||||
TValue value;
|
||||
Entry* parent;
|
||||
Entry* child;
|
||||
|
||||
Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(NULL), child(NULL) {
|
||||
}
|
||||
const TKey& getKey() const final { return key; }
|
||||
};
|
||||
|
||||
class EntryForSearch : public KeyedEntry {
|
||||
public:
|
||||
const TKey& key;
|
||||
EntryForSearch(const TKey& key_) : key(key_) {
|
||||
}
|
||||
const TKey& getKey() const final { return key; }
|
||||
};
|
||||
|
||||
struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
|
||||
size_t operator() (const KeyedEntry* entry) const {
|
||||
return hash_type(entry->getKey());
|
||||
};
|
||||
};
|
||||
|
||||
struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
|
||||
bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
|
||||
return lhs->getKey() == rhs->getKey();
|
||||
};
|
||||
};
|
||||
|
||||
// All entries in the set will be Entry*. Using the weaker KeyedEntry as to allow entries
|
||||
// that have only a key reference, for searching.
|
||||
typedef std::unordered_set<KeyedEntry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
|
||||
|
||||
void attachToCache(Entry& entry);
|
||||
void detachFromCache(Entry& entry);
|
||||
|
||||
typename LruCacheSet::iterator findByKey(const TKey& key) {
|
||||
EntryForSearch entryForSearch(key);
|
||||
typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<LruCacheSet> mSet;
|
||||
OnEntryRemoved<TKey, TValue>* mListener;
|
||||
Entry* mOldest;
|
||||
Entry* mYoungest;
|
||||
uint32_t mMaxCapacity;
|
||||
TValue mNullValue;
|
||||
|
||||
public:
|
||||
// To be used like:
|
||||
// while (it.next()) {
|
||||
// it.value(); it.key();
|
||||
// }
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(const LruCache<TKey, TValue>& cache):
|
||||
mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
|
||||
}
|
||||
|
||||
bool next() {
|
||||
if (mIterator == mCache.mSet->end()) {
|
||||
return false;
|
||||
}
|
||||
if (!mBeginReturned) {
|
||||
// mIterator has been initialized to the beginning and
|
||||
// hasn't been returned. Do not advance:
|
||||
mBeginReturned = true;
|
||||
} else {
|
||||
std::advance(mIterator, 1);
|
||||
}
|
||||
bool ret = (mIterator != mCache.mSet->end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
const TValue& value() const {
|
||||
// All the elements in the set are of type Entry. See comment in the definition
|
||||
// of LruCacheSet above.
|
||||
return reinterpret_cast<Entry *>(*mIterator)->value;
|
||||
}
|
||||
|
||||
const TKey& key() const {
|
||||
return (*mIterator)->getKey();
|
||||
}
|
||||
private:
|
||||
const LruCache<TKey, TValue>& mCache;
|
||||
typename LruCacheSet::iterator mIterator;
|
||||
bool mBeginReturned;
|
||||
};
|
||||
};
|
||||
|
||||
// Implementation is here, because it's fully templated
|
||||
template <typename TKey, typename TValue>
|
||||
LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
|
||||
: mSet(new LruCacheSet())
|
||||
, mListener(NULL)
|
||||
, mOldest(NULL)
|
||||
, mYoungest(NULL)
|
||||
, mMaxCapacity(maxCapacity)
|
||||
, mNullValue(0) {
|
||||
mSet->max_load_factor(1.0);
|
||||
};
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
LruCache<TKey, TValue>::~LruCache() {
|
||||
// Need to delete created entries.
|
||||
clear();
|
||||
};
|
||||
|
||||
template<typename K, typename V>
|
||||
void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
size_t LruCache<TKey, TValue>::size() const {
|
||||
return mSet->size();
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
|
||||
typename LruCacheSet::const_iterator find_result = findByKey(key);
|
||||
if (find_result == mSet->end()) {
|
||||
return mNullValue;
|
||||
}
|
||||
// All the elements in the set are of type Entry. See comment in the definition
|
||||
// of LruCacheSet above.
|
||||
Entry *entry = reinterpret_cast<Entry*>(*find_result);
|
||||
detachFromCache(*entry);
|
||||
attachToCache(*entry);
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
|
||||
if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
|
||||
removeOldest();
|
||||
}
|
||||
|
||||
if (findByKey(key) != mSet->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Entry* newEntry = new Entry(key, value);
|
||||
mSet->insert(newEntry);
|
||||
attachToCache(*newEntry);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
bool LruCache<TKey, TValue>::remove(const TKey& key) {
|
||||
typename LruCacheSet::const_iterator find_result = findByKey(key);
|
||||
if (find_result == mSet->end()) {
|
||||
return false;
|
||||
}
|
||||
// All the elements in the set are of type Entry. See comment in the definition
|
||||
// of LruCacheSet above.
|
||||
Entry* entry = reinterpret_cast<Entry*>(*find_result);
|
||||
mSet->erase(entry);
|
||||
if (mListener) {
|
||||
(*mListener)(entry->key, entry->value);
|
||||
}
|
||||
detachFromCache(*entry);
|
||||
delete entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
bool LruCache<TKey, TValue>::removeOldest() {
|
||||
if (mOldest != NULL) {
|
||||
return remove(mOldest->key);
|
||||
// TODO: should probably abort if false
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
const TValue& LruCache<TKey, TValue>::peekOldestValue() {
|
||||
if (mOldest) {
|
||||
return mOldest->value;
|
||||
}
|
||||
return mNullValue;
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
void LruCache<TKey, TValue>::clear() {
|
||||
if (mListener) {
|
||||
for (Entry* p = mOldest; p != NULL; p = p->child) {
|
||||
(*mListener)(p->key, p->value);
|
||||
}
|
||||
}
|
||||
mYoungest = NULL;
|
||||
mOldest = NULL;
|
||||
for (auto entry : *mSet.get()) {
|
||||
delete entry;
|
||||
}
|
||||
mSet->clear();
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
|
||||
if (mYoungest == NULL) {
|
||||
mYoungest = mOldest = &entry;
|
||||
} else {
|
||||
entry.parent = mYoungest;
|
||||
mYoungest->child = &entry;
|
||||
mYoungest = &entry;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
|
||||
if (entry.parent != NULL) {
|
||||
entry.parent->child = entry.child;
|
||||
} else {
|
||||
mOldest = entry.child;
|
||||
}
|
||||
if (entry.child != NULL) {
|
||||
entry.child->parent = entry.parent;
|
||||
} else {
|
||||
mYoungest = entry.parent;
|
||||
}
|
||||
|
||||
entry.parent = NULL;
|
||||
entry.child = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
#endif // ANDROID_UTILS_LRU_CACHE_H
|
||||
336
engine/src/flutter/third_party/txt/shims/utils/TypeHelpers.h
vendored
Normal file
336
engine/src/flutter/third_party/txt/shims/utils/TypeHelpers.h
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright (C) 2005 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_TYPE_HELPERS_H
|
||||
#define ANDROID_TYPE_HELPERS_H
|
||||
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Types traits
|
||||
*/
|
||||
|
||||
template <typename T> struct trait_trivial_ctor { enum { value = false }; };
|
||||
template <typename T> struct trait_trivial_dtor { enum { value = false }; };
|
||||
template <typename T> struct trait_trivial_copy { enum { value = false }; };
|
||||
template <typename T> struct trait_trivial_move { enum { value = false }; };
|
||||
template <typename T> struct trait_pointer { enum { value = false }; };
|
||||
template <typename T> struct trait_pointer<T*> { enum { value = true }; };
|
||||
|
||||
template <typename TYPE>
|
||||
struct traits {
|
||||
enum {
|
||||
// whether this type is a pointer
|
||||
is_pointer = trait_pointer<TYPE>::value,
|
||||
// whether this type's constructor is a no-op
|
||||
has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
|
||||
// whether this type's destructor is a no-op
|
||||
has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
|
||||
// whether this type type can be copy-constructed with memcpy
|
||||
has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
|
||||
// whether this type can be moved with memmove
|
||||
has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct aggregate_traits {
|
||||
enum {
|
||||
is_pointer = false,
|
||||
has_trivial_ctor =
|
||||
traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
|
||||
has_trivial_dtor =
|
||||
traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
|
||||
has_trivial_copy =
|
||||
traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
|
||||
has_trivial_move =
|
||||
traits<T>::has_trivial_move && traits<U>::has_trivial_move
|
||||
};
|
||||
};
|
||||
|
||||
#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
|
||||
template<> struct trait_trivial_ctor< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
|
||||
template<> struct trait_trivial_dtor< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \
|
||||
template<> struct trait_trivial_copy< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \
|
||||
template<> struct trait_trivial_move< T > { enum { value = true }; };
|
||||
|
||||
#define ANDROID_BASIC_TYPES_TRAITS( T ) \
|
||||
ANDROID_TRIVIAL_CTOR_TRAIT( T ) \
|
||||
ANDROID_TRIVIAL_DTOR_TRAIT( T ) \
|
||||
ANDROID_TRIVIAL_COPY_TRAIT( T ) \
|
||||
ANDROID_TRIVIAL_MOVE_TRAIT( T )
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* basic types traits
|
||||
*/
|
||||
|
||||
ANDROID_BASIC_TYPES_TRAITS( void )
|
||||
ANDROID_BASIC_TYPES_TRAITS( bool )
|
||||
ANDROID_BASIC_TYPES_TRAITS( char )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned char )
|
||||
ANDROID_BASIC_TYPES_TRAITS( short )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned short )
|
||||
ANDROID_BASIC_TYPES_TRAITS( int )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned int )
|
||||
ANDROID_BASIC_TYPES_TRAITS( long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( long long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
|
||||
ANDROID_BASIC_TYPES_TRAITS( float )
|
||||
ANDROID_BASIC_TYPES_TRAITS( double )
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
* compare and order types
|
||||
*/
|
||||
|
||||
template<typename TYPE> inline
|
||||
int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
|
||||
return (lhs < rhs) ? 1 : 0;
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
int compare_type(const TYPE& lhs, const TYPE& rhs) {
|
||||
return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
|
||||
}
|
||||
|
||||
/*
|
||||
* create, destroy, copy and move types...
|
||||
*/
|
||||
|
||||
template<typename TYPE> inline
|
||||
void construct_type(TYPE* p, size_t n) {
|
||||
if (!traits<TYPE>::has_trivial_ctor) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
new(p++) TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void destroy_type(TYPE* p, size_t n) {
|
||||
if (!traits<TYPE>::has_trivial_dtor) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
p->~TYPE();
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<traits<TYPE>::has_trivial_copy>::type
|
||||
inline
|
||||
copy_type(TYPE* d, const TYPE* s, size_t n) {
|
||||
memcpy(d,s,n*sizeof(TYPE));
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<!traits<TYPE>::has_trivial_copy>::type
|
||||
inline
|
||||
copy_type(TYPE* d, const TYPE* s, size_t n) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
new(d) TYPE(*s);
|
||||
d++, s++;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE> inline
|
||||
void splat_type(TYPE* where, const TYPE* what, size_t n) {
|
||||
if (!traits<TYPE>::has_trivial_copy) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
new(where) TYPE(*what);
|
||||
where++;
|
||||
}
|
||||
} else {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
*where++ = *what;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
struct use_trivial_move : public std::integral_constant<bool,
|
||||
(traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
|
||||
|| traits<TYPE>::has_trivial_move
|
||||
> {};
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
memmove(d, s, n*sizeof(TYPE));
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<!use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
d += n;
|
||||
s += n;
|
||||
while (n > 0) {
|
||||
n--;
|
||||
--d, --s;
|
||||
if (!traits<TYPE>::has_trivial_copy) {
|
||||
new(d) TYPE(*s);
|
||||
} else {
|
||||
*d = *s;
|
||||
}
|
||||
if (!traits<TYPE>::has_trivial_dtor) {
|
||||
s->~TYPE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
memmove(d, s, n*sizeof(TYPE));
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
typename std::enable_if<!use_trivial_move<TYPE>::value>::type
|
||||
inline
|
||||
move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
|
||||
while (n > 0) {
|
||||
n--;
|
||||
if (!traits<TYPE>::has_trivial_copy) {
|
||||
new(d) TYPE(*s);
|
||||
} else {
|
||||
*d = *s;
|
||||
}
|
||||
if (!traits<TYPE>::has_trivial_dtor) {
|
||||
s->~TYPE();
|
||||
}
|
||||
d++, s++;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* a key/value pair
|
||||
*/
|
||||
|
||||
template <typename KEY, typename VALUE>
|
||||
struct key_value_pair_t {
|
||||
typedef KEY key_t;
|
||||
typedef VALUE value_t;
|
||||
|
||||
KEY key;
|
||||
VALUE value;
|
||||
key_value_pair_t() { }
|
||||
key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
|
||||
key_value_pair_t& operator=(const key_value_pair_t& o) {
|
||||
key = o.key;
|
||||
value = o.value;
|
||||
return *this;
|
||||
}
|
||||
key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
|
||||
explicit key_value_pair_t(const KEY& k) : key(k) { }
|
||||
inline bool operator < (const key_value_pair_t& o) const {
|
||||
return strictly_order_type(key, o.key);
|
||||
}
|
||||
inline const KEY& getKey() const {
|
||||
return key;
|
||||
}
|
||||
inline const VALUE& getValue() const {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_ctor< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_dtor< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_copy< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
|
||||
template <typename K, typename V>
|
||||
struct trait_trivial_move< key_value_pair_t<K, V> >
|
||||
{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Hash codes.
|
||||
*/
|
||||
typedef uint32_t hash_t;
|
||||
|
||||
template <typename TKey>
|
||||
hash_t hash_type(const TKey& key);
|
||||
|
||||
/* Built-in hash code specializations */
|
||||
#define ANDROID_INT32_HASH(T) \
|
||||
template <> inline hash_t hash_type(const T& value) { return hash_t(value); }
|
||||
#define ANDROID_INT64_HASH(T) \
|
||||
template <> inline hash_t hash_type(const T& value) { \
|
||||
return hash_t((value >> 32) ^ value); }
|
||||
#define ANDROID_REINTERPRET_HASH(T, R) \
|
||||
template <> inline hash_t hash_type(const T& value) { \
|
||||
R newValue; \
|
||||
static_assert(sizeof(newValue) == sizeof(value), "size mismatch"); \
|
||||
memcpy(&newValue, &value, sizeof(newValue)); \
|
||||
return hash_type(newValue); \
|
||||
}
|
||||
|
||||
ANDROID_INT32_HASH(bool)
|
||||
ANDROID_INT32_HASH(int8_t)
|
||||
ANDROID_INT32_HASH(uint8_t)
|
||||
ANDROID_INT32_HASH(int16_t)
|
||||
ANDROID_INT32_HASH(uint16_t)
|
||||
ANDROID_INT32_HASH(int32_t)
|
||||
ANDROID_INT32_HASH(uint32_t)
|
||||
ANDROID_INT64_HASH(int64_t)
|
||||
ANDROID_INT64_HASH(uint64_t)
|
||||
ANDROID_REINTERPRET_HASH(float, uint32_t)
|
||||
ANDROID_REINTERPRET_HASH(double, uint64_t)
|
||||
|
||||
template <typename T> inline hash_t hash_type(T* const & value) {
|
||||
return hash_type(uintptr_t(value));
|
||||
}
|
||||
|
||||
}; // namespace android
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#endif // ANDROID_TYPE_HELPERS_H
|
||||
17
engine/src/flutter/third_party/txt/src/BUILD.gn
vendored
Normal file
17
engine/src/flutter/third_party/txt/src/BUILD.gn
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copyright 2017 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
source_set("src") {
|
||||
|
||||
}
|
||||
249
engine/src/flutter/third_party/txt/src/font_collection.cc
vendored
Normal file
249
engine/src/flutter/third_party/txt/src/font_collection.cc
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/src/font_skia.h"
|
||||
#include "third_party/skia/include/core/SkString.h"
|
||||
#include "third_party/skia/include/ports/SkFontMgr.h"
|
||||
#include "third_party/skia/include/ports/SkFontMgr_android.h"
|
||||
#include "third_party/skia/include/ports/SkFontMgr_directory.h"
|
||||
|
||||
namespace txt {
|
||||
// TODO(garyq): Will be deprecated when full compatibility with Flutter Engine
|
||||
// is complete.
|
||||
FontCollection& FontCollection::GetFontCollection(std::string dir) {
|
||||
std::vector<std::string> dirs = {dir};
|
||||
return GetFontCollection(std::move(dirs));
|
||||
}
|
||||
|
||||
// TODO(garyq): Will be deprecated when full compatibility with Flutter Engine
|
||||
// is complete.
|
||||
FontCollection& FontCollection::GetFontCollection(
|
||||
std::vector<std::string> dirs) {
|
||||
static FontCollection* collection = nullptr;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [dirs]() { collection = new FontCollection(dirs); });
|
||||
return *collection;
|
||||
}
|
||||
|
||||
// TODO(garyq): Will be deprecated when full compatibility with Flutter Engine
|
||||
// is complete.
|
||||
FontCollection& FontCollection::GetDefaultFontCollection() {
|
||||
return GetFontCollection("");
|
||||
}
|
||||
|
||||
FontCollection::FontCollection() {
|
||||
FontCollection("");
|
||||
}
|
||||
|
||||
FontCollection::FontCollection(CacheMethod cache_method) {
|
||||
FontCollection("", cache_method);
|
||||
}
|
||||
|
||||
FontCollection::FontCollection(std::string dir, CacheMethod cache_method) {
|
||||
std::vector<std::string> dirs = {dir};
|
||||
FontCollection(std::move(dirs), cache_method);
|
||||
}
|
||||
|
||||
FontCollection::FontCollection(const std::vector<std::string>& dirs,
|
||||
CacheMethod cache_method) {
|
||||
for (std::string dir : dirs) {
|
||||
if (dir.length() != 0) {
|
||||
AddFontMgr(dir, false);
|
||||
}
|
||||
}
|
||||
skia_font_managers_.push_back(SkFontMgr::RefDefault());
|
||||
|
||||
DiscoverFamilyNames();
|
||||
|
||||
cache_method_ = cache_method;
|
||||
}
|
||||
|
||||
FontCollection::~FontCollection() = default;
|
||||
|
||||
void FontCollection::AddFontMgr(std::string dir, bool rediscover_family_names) {
|
||||
#ifdef DIRECTORY_FONT_MANAGER_AVAILABLE
|
||||
// On Linux systems:
|
||||
skia_font_managers_.push_back(SkFontMgr_New_Custom_Directory(dir.c_str()));
|
||||
#endif
|
||||
#ifdef ANDROID_FONT_MANAGER_AVAILABLE
|
||||
// On Android:
|
||||
SkFontMgr_Android_CustomFonts android_custom_font_data;
|
||||
// Ensure the dir string is '/' terminated.
|
||||
if (dir.back() != '/')
|
||||
dir += '/';
|
||||
android_custom_font_data.fBasePath = dir.data();
|
||||
android_custom_font_data.fSystemFontUse =
|
||||
SkFontMgr_Android_CustomFonts::SystemFontUse::kOnlyCustom;
|
||||
skia_font_managers_.push_back(
|
||||
SkFontMgr_New_Android(&android_custom_font_data));
|
||||
#endif
|
||||
|
||||
if (rediscover_family_names)
|
||||
DiscoverFamilyNames(skia_font_managers_.back());
|
||||
}
|
||||
|
||||
void FontCollection::AddFontMgr(sk_sp<SkFontMgr> font_mgr,
|
||||
bool rediscover_family_names) {
|
||||
skia_font_managers_.push_back(font_mgr);
|
||||
if (rediscover_family_names)
|
||||
DiscoverFamilyNames(font_mgr);
|
||||
}
|
||||
|
||||
void FontCollection::DiscoverFamilyNames() {
|
||||
for (sk_sp<SkFontMgr> mgr : skia_font_managers_) {
|
||||
DiscoverFamilyNames(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
void FontCollection::DiscoverFamilyNames(sk_sp<SkFontMgr> mgr) {
|
||||
SkString str;
|
||||
for (int i = 0; i < mgr->countFamilies(); i++) {
|
||||
mgr->getFamilyName(i, &str);
|
||||
family_names_.insert(std::string{str.writable_str()});
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::string> FontCollection::GetFamilyNames() {
|
||||
return family_names_;
|
||||
}
|
||||
|
||||
bool FontCollection::HasFamily(const std::string family) const {
|
||||
return family_names_.count(family) == 1;
|
||||
}
|
||||
|
||||
void FontCollection::FlushCache() {
|
||||
minikin_font_collection_map_.clear();
|
||||
lru_tracker_.clear();
|
||||
}
|
||||
|
||||
void FontCollection::SetCacheCapacity(const size_t cap) {
|
||||
cache_capacity_ = cap;
|
||||
}
|
||||
|
||||
void FontCollection::SetLowMemoryMode(bool mode, size_t cap) {
|
||||
cache_capacity_ = cap;
|
||||
if (mode) {
|
||||
cache_method_ = CacheMethod::kLRU;
|
||||
TrimCache();
|
||||
} else {
|
||||
cache_method_ = CacheMethod::kUnlimited;
|
||||
}
|
||||
}
|
||||
|
||||
void FontCollection::TrimCache() {
|
||||
while (minikin_font_collection_map_.size() > cache_capacity_) {
|
||||
std::string family_to_evict = lru_tracker_.back();
|
||||
lru_tracker_.pop_back();
|
||||
minikin_font_collection_map_.erase(family_to_evict);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string FontCollection::ProcessFamilyName(const std::string& family) {
|
||||
#ifdef DIRECTORY_FONT_MANAGER_AVAILABLE
|
||||
return family.length() == 0 ? DEFAULT_FAMILY_NAME : family;
|
||||
#else
|
||||
if (family.length() > 0 &&
|
||||
GetFamilyNames().count(family) > 0) { // Ensure family exists.
|
||||
return family;
|
||||
} else {
|
||||
if (GetFamilyNames().count(DEFAULT_FAMILY_NAME) > 0) {
|
||||
return DEFAULT_FAMILY_NAME;
|
||||
}
|
||||
return *GetFamilyNames().begin(); // First family available.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<minikin::FontCollection>
|
||||
FontCollection::GetMinikinFontCollectionForFamily(const std::string& family) {
|
||||
FTL_DCHECK(skia_font_managers_.size() > 0);
|
||||
std::string processed_family_name = ProcessFamilyName(family);
|
||||
// Only obtain new font family if the font has changed between runs.
|
||||
if (cache_method_ == CacheMethod::kNone ||
|
||||
minikin_font_collection_map_.count(processed_family_name) == 0) {
|
||||
// Ask Skia to resolve a font style set for a font family name.
|
||||
// FIXME(chinmaygarde): CoreText crashes when passed a null string. This
|
||||
// seems to be a bug in Skia as SkFontMgr explicitly says passing in
|
||||
// nullptr gives the default font.
|
||||
for (sk_sp<SkFontMgr> mgr : skia_font_managers_) {
|
||||
FTL_DCHECK(mgr != nullptr);
|
||||
auto font_style_set = mgr->matchFamily(processed_family_name.c_str());
|
||||
if (font_style_set != nullptr) {
|
||||
std::vector<minikin::Font> minikin_fonts;
|
||||
|
||||
// Add fonts to the Minikin font family.
|
||||
for (int i = 0, style_count = font_style_set->count(); i < style_count;
|
||||
++i) {
|
||||
// Create the skia typeface
|
||||
auto skia_typeface =
|
||||
sk_ref_sp<SkTypeface>(font_style_set->createTypeface(i));
|
||||
if (skia_typeface == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the minikin font from the skia typeface.
|
||||
// Divide by 100 because the weights are given as "100", "200", etc.
|
||||
minikin::Font minikin_font(
|
||||
std::make_shared<FontSkia>(skia_typeface),
|
||||
minikin::FontStyle{skia_typeface->fontStyle().weight() / 100,
|
||||
skia_typeface->isItalic()});
|
||||
|
||||
minikin_fonts.emplace_back(std::move(minikin_font));
|
||||
}
|
||||
|
||||
// Create a Minikin font family.
|
||||
auto minikin_family =
|
||||
std::make_shared<minikin::FontFamily>(std::move(minikin_fonts));
|
||||
|
||||
// Create a vector of font families for the Minkin font collection. For
|
||||
// now, we only have one family in our collection.
|
||||
std::vector<std::shared_ptr<minikin::FontFamily>> minikin_families = {
|
||||
minikin_family,
|
||||
};
|
||||
|
||||
// Assign the font collection.
|
||||
minikin_font_collection_map_[processed_family_name] =
|
||||
std::make_shared<minikin::FontCollection>(minikin_families);
|
||||
return minikin_font_collection_map_[processed_family_name];
|
||||
}
|
||||
}
|
||||
// Uh oh! Font family not found in any of the font managers!
|
||||
minikin_font_collection_map_[processed_family_name] = nullptr;
|
||||
}
|
||||
|
||||
// Maintain LRU and evict old fonts no longer used.
|
||||
|
||||
lru_tracker_.remove(processed_family_name);
|
||||
lru_tracker_.push_front(processed_family_name);
|
||||
if (cache_method_ == CacheMethod::kLRU) {
|
||||
TrimCache();
|
||||
}
|
||||
|
||||
return minikin_font_collection_map_[processed_family_name];
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
148
engine/src/flutter/third_party/txt/src/font_collection.h
vendored
Normal file
148
engine/src/flutter/third_party/txt/src/font_collection.h
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_FONT_COLLECTION_H_
|
||||
#define LIB_TXT_SRC_FONT_COLLECTION_H_
|
||||
|
||||
#define DEFAULT_FAMILY_NAME "Roboto"
|
||||
#ifdef ANDROID_FONT_MANAGER_AVAILABLE
|
||||
#undef DEFAULT_FAMILY_NAME
|
||||
// On Android, Roboto is called 'sans-serif'
|
||||
#define DEFAULT_FAMILY_NAME "sans-serif"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CACHE_CAPACITY 20
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/ftl/macros.h"
|
||||
#include "minikin/FontCollection.h"
|
||||
#include "minikin/FontFamily.h"
|
||||
#include "third_party/gtest/include/gtest/gtest_prod.h"
|
||||
#include "third_party/skia/include/core/SkRefCnt.h"
|
||||
#include "third_party/skia/include/ports/SkFontMgr.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
// FontCollection holds a vector of Skia Font Managers and handles font
|
||||
// fallback. If no additional font directories are provided, then only the
|
||||
// default font directory will be available.
|
||||
class FontCollection {
|
||||
public:
|
||||
enum CacheMethod {
|
||||
kNone,
|
||||
kLRU, // Least Recently Used.
|
||||
kUnlimited,
|
||||
};
|
||||
// TODO(garyq): Will be deprecated when full compatibility with Flutter Engine
|
||||
// is complete.
|
||||
static FontCollection& GetDefaultFontCollection();
|
||||
|
||||
// TODO(garyq): Will be deprecated when full compatibility with Flutter Engine
|
||||
// is complete..
|
||||
static FontCollection& GetFontCollection(std::string dir = "");
|
||||
|
||||
// TODO(garyq): Will be deprecated when full compatibility with Flutter Engine
|
||||
// is complete.
|
||||
static FontCollection& GetFontCollection(std::vector<std::string> dirs);
|
||||
|
||||
// Provides a pointer to the minikin FontCollection for the given font family.
|
||||
// If the famly is not in any font manager, this will return a nullptr. Once a
|
||||
// font is loaded, it is cached and future calls will be very efficient
|
||||
// (until/if the font is flushed).
|
||||
std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForFamily(
|
||||
const std::string& family);
|
||||
|
||||
FontCollection(const std::vector<std::string>& dirs,
|
||||
CacheMethod cache_method = CacheMethod::kUnlimited);
|
||||
|
||||
FontCollection(std::string dir,
|
||||
CacheMethod cache_method = CacheMethod::kUnlimited);
|
||||
|
||||
FontCollection(CacheMethod cache_method);
|
||||
|
||||
FontCollection();
|
||||
|
||||
~FontCollection();
|
||||
|
||||
// Provides a set of all available family names.
|
||||
std::set<std::string> GetFamilyNames();
|
||||
|
||||
// Returns true when the supplied font family exists in any of the font
|
||||
// managers.
|
||||
bool HasFamily(const std::string family) const;
|
||||
|
||||
// Adds a new SkFontMgr to the front of the stack of font managers.
|
||||
void AddFontMgr(std::string dir, bool rediscover_family_names = true);
|
||||
|
||||
// Adds the provided SkFontMgr to the front of the stack of font managers.
|
||||
void AddFontMgr(sk_sp<SkFontMgr> font_mgr,
|
||||
bool rediscover_family_names = true);
|
||||
|
||||
// Removes all fonts that do not fit in the cache capacity from memory.
|
||||
void FlushCache();
|
||||
|
||||
// When in LRU mode, the cache will only hold the <cap> most recently used
|
||||
// fonts. This may be used when the application becomes low on memory or a
|
||||
// very large number of fonts are used.
|
||||
void SetCacheCapacity(const size_t cap);
|
||||
|
||||
// Call this to limit memory usage by cached fonts. SetLowMemoryMode() will
|
||||
// enable default LRU policy and flush fonts beyond capacity.
|
||||
void SetLowMemoryMode(bool mode = true, size_t cap = DEFAULT_CACHE_CAPACITY);
|
||||
|
||||
private:
|
||||
std::vector<sk_sp<SkFontMgr>> skia_font_managers_;
|
||||
// Cache the names because GetFamilyNames() can be frequently called.
|
||||
std::set<std::string> family_names_;
|
||||
CacheMethod cache_method_ = CacheMethod::kUnlimited;
|
||||
std::list<std::string> lru_tracker_;
|
||||
size_t cache_capacity_ = DEFAULT_CACHE_CAPACITY;
|
||||
|
||||
// Cache minikin font collections to prevent slow disk reads.
|
||||
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>
|
||||
minikin_font_collection_map_;
|
||||
|
||||
FRIEND_TEST(FontCollection, HasDefaultRegistrations);
|
||||
FRIEND_TEST(FontCollection, GetMinikinFontCollections);
|
||||
FRIEND_TEST(FontCollection, GetFamilyNames);
|
||||
|
||||
// Postprocess the family name to handle the following properties: fallback
|
||||
// when not found and reverting to the default name when no fallback is found.
|
||||
const std::string ProcessFamilyName(const std::string& family);
|
||||
|
||||
// Polls all of the SkFontMgrs to obtain a set of all available font family
|
||||
// names.
|
||||
void DiscoverFamilyNames();
|
||||
|
||||
// Add the family names of mgr to set of available font family names.
|
||||
void DiscoverFamilyNames(sk_sp<SkFontMgr> mgr);
|
||||
|
||||
void TrimCache();
|
||||
|
||||
static const std::string GetDefaultFamilyName() {
|
||||
return DEFAULT_FAMILY_NAME;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_FONT_COLLECTION_H_
|
||||
95
engine/src/flutter/third_party/txt/src/font_skia.cc
vendored
Normal file
95
engine/src/flutter/third_party/txt/src/font_skia.cc
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/font_skia.h"
|
||||
|
||||
#include <minikin/MinikinFont.h>
|
||||
|
||||
namespace txt {
|
||||
namespace {
|
||||
|
||||
hb_blob_t* GetTable(hb_face_t* face, hb_tag_t tag, void* context) {
|
||||
SkTypeface* typeface = reinterpret_cast<SkTypeface*>(context);
|
||||
|
||||
const size_t table_size = typeface->getTableSize(tag);
|
||||
if (table_size == 0)
|
||||
return nullptr;
|
||||
void* buffer = malloc(table_size);
|
||||
if (buffer == nullptr)
|
||||
return nullptr;
|
||||
|
||||
size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer);
|
||||
if (table_size != actual_size) {
|
||||
free(buffer);
|
||||
return nullptr;
|
||||
}
|
||||
return hb_blob_create(reinterpret_cast<char*>(buffer), table_size,
|
||||
HB_MEMORY_MODE_WRITABLE, buffer, free);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FontSkia::FontSkia(sk_sp<SkTypeface> typeface)
|
||||
: MinikinFont(typeface->uniqueID()), typeface_(std::move(typeface)) {}
|
||||
|
||||
FontSkia::~FontSkia() = default;
|
||||
|
||||
static void FontSkia_SetSkiaPaint(sk_sp<SkTypeface> typeface,
|
||||
SkPaint* skPaint,
|
||||
const minikin::MinikinPaint& paint) {
|
||||
skPaint->setTypeface(std::move(typeface));
|
||||
skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
// TODO: set more paint parameters from Minikin
|
||||
skPaint->setTextSize(paint.size);
|
||||
}
|
||||
|
||||
float FontSkia::GetHorizontalAdvance(uint32_t glyph_id,
|
||||
const minikin::MinikinPaint& paint) const {
|
||||
SkPaint skPaint;
|
||||
uint16_t glyph16 = glyph_id;
|
||||
SkScalar skWidth;
|
||||
FontSkia_SetSkiaPaint(typeface_, &skPaint, paint);
|
||||
skPaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, NULL);
|
||||
return skWidth;
|
||||
}
|
||||
|
||||
void FontSkia::GetBounds(minikin::MinikinRect* bounds,
|
||||
uint32_t glyph_id,
|
||||
const minikin::MinikinPaint& paint) const {
|
||||
SkPaint skPaint;
|
||||
uint16_t glyph16 = glyph_id;
|
||||
SkRect skBounds;
|
||||
FontSkia_SetSkiaPaint(typeface_, &skPaint, paint);
|
||||
skPaint.getTextWidths(&glyph16, sizeof(glyph16), NULL, &skBounds);
|
||||
bounds->mLeft = skBounds.fLeft;
|
||||
bounds->mTop = skBounds.fTop;
|
||||
bounds->mRight = skBounds.fRight;
|
||||
bounds->mBottom = skBounds.fBottom;
|
||||
}
|
||||
|
||||
hb_face_t* FontSkia::CreateHarfBuzzFace() const {
|
||||
return hb_face_create_for_tables(GetTable, typeface_.get(), 0);
|
||||
}
|
||||
|
||||
const std::vector<minikin::FontVariation>& FontSkia::GetAxes() const {
|
||||
return variations_;
|
||||
}
|
||||
|
||||
const sk_sp<SkTypeface>& FontSkia::GetSkTypeface() const {
|
||||
return typeface_;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
50
engine/src/flutter/third_party/txt/src/font_skia.h
vendored
Normal file
50
engine/src/flutter/third_party/txt/src/font_skia.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <SkPaint.h>
|
||||
#include <SkTypeface.h>
|
||||
#include <minikin/MinikinFont.h>
|
||||
#include "lib/ftl/macros.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
class FontSkia : public minikin::MinikinFont {
|
||||
public:
|
||||
explicit FontSkia(sk_sp<SkTypeface> typeface);
|
||||
|
||||
~FontSkia();
|
||||
|
||||
float GetHorizontalAdvance(uint32_t glyph_id,
|
||||
const minikin::MinikinPaint& paint) const override;
|
||||
|
||||
void GetBounds(minikin::MinikinRect* bounds,
|
||||
uint32_t glyph_id,
|
||||
const minikin::MinikinPaint& paint) const override;
|
||||
|
||||
hb_face_t* CreateHarfBuzzFace() const override;
|
||||
|
||||
const std::vector<minikin::FontVariation>& GetAxes() const override;
|
||||
|
||||
const sk_sp<SkTypeface>& GetSkTypeface() const;
|
||||
|
||||
private:
|
||||
sk_sp<SkTypeface> typeface_;
|
||||
std::vector<minikin::FontVariation> variations_;
|
||||
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(FontSkia);
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
29
engine/src/flutter/third_party/txt/src/font_style.h
vendored
Normal file
29
engine/src/flutter/third_party/txt/src/font_style.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_FONT_STYLE_H_
|
||||
#define LIB_TXT_SRC_FONT_STYLE_H_
|
||||
|
||||
namespace txt {
|
||||
|
||||
enum class FontStyle {
|
||||
normal,
|
||||
italic,
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_FONT_STYLE_H_
|
||||
36
engine/src/flutter/third_party/txt/src/font_weight.h
vendored
Normal file
36
engine/src/flutter/third_party/txt/src/font_weight.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_FONT_WEIGHT_H_
|
||||
#define LIB_TXT_SRC_FONT_WEIGHT_H_
|
||||
|
||||
namespace txt {
|
||||
|
||||
enum class FontWeight {
|
||||
w100, // Thin
|
||||
w200, // Extra-Light
|
||||
w300, // Light
|
||||
w400, // Normal/Regular
|
||||
w500, // Medium
|
||||
w600, // Semi-bold
|
||||
w700, // Bold
|
||||
w800, // Extra-Bold
|
||||
w900, // Black
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_FONT_WEIGHT_H_
|
||||
71
engine/src/flutter/third_party/txt/src/paint_record.cc
vendored
Normal file
71
engine/src/flutter/third_party/txt/src/paint_record.cc
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/paint_record.h"
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
PaintRecord::~PaintRecord() = default;
|
||||
|
||||
PaintRecord::PaintRecord(TextStyle style,
|
||||
SkPoint offset,
|
||||
sk_sp<SkTextBlob> text,
|
||||
SkPaint::FontMetrics metrics,
|
||||
size_t line,
|
||||
double run_width)
|
||||
: style_(style),
|
||||
offset_(offset),
|
||||
text_(std::move(text)),
|
||||
metrics_(metrics),
|
||||
line_(line),
|
||||
run_width_(run_width) {}
|
||||
|
||||
PaintRecord::PaintRecord(TextStyle style,
|
||||
sk_sp<SkTextBlob> text,
|
||||
SkPaint::FontMetrics metrics,
|
||||
size_t line,
|
||||
double run_width)
|
||||
: style_(style),
|
||||
text_(std::move(text)),
|
||||
metrics_(metrics),
|
||||
line_(line),
|
||||
run_width_(run_width) {}
|
||||
|
||||
PaintRecord::PaintRecord(PaintRecord&& other) {
|
||||
style_ = other.style_;
|
||||
offset_ = other.offset_;
|
||||
text_ = std::move(other.text_);
|
||||
metrics_ = other.metrics_;
|
||||
line_ = other.line_;
|
||||
run_width_ = other.run_width_;
|
||||
}
|
||||
|
||||
PaintRecord& PaintRecord::operator=(PaintRecord&& other) {
|
||||
style_ = other.style_;
|
||||
offset_ = other.offset_;
|
||||
text_ = std::move(other.text_);
|
||||
metrics_ = other.metrics_;
|
||||
line_ = other.line_;
|
||||
run_width_ = other.run_width_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void PaintRecord::SetOffset(SkPoint pt) {
|
||||
offset_ = pt;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
84
engine/src/flutter/third_party/txt/src/paint_record.h
vendored
Normal file
84
engine/src/flutter/third_party/txt/src/paint_record.h
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_PAINT_RECORD_H_
|
||||
#define LIB_TXT_SRC_PAINT_RECORD_H_
|
||||
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/ftl/macros.h"
|
||||
#include "lib/txt/src/text_style.h"
|
||||
#include "third_party/skia/include/core/SkPaint.h"
|
||||
#include "third_party/skia/include/core/SkTextBlob.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
// PaintRecord holds the layout data after Paragraph::Layout() is called. This
|
||||
// stores all nessecary offsets, blobs, metrics, and more for Skia to draw the
|
||||
// text.
|
||||
class PaintRecord {
|
||||
public:
|
||||
PaintRecord() = delete;
|
||||
|
||||
~PaintRecord();
|
||||
|
||||
PaintRecord(TextStyle style,
|
||||
SkPoint offset,
|
||||
sk_sp<SkTextBlob> text,
|
||||
SkPaint::FontMetrics metrics,
|
||||
size_t line,
|
||||
double run_width);
|
||||
|
||||
PaintRecord(TextStyle style,
|
||||
sk_sp<SkTextBlob> text,
|
||||
SkPaint::FontMetrics metrics,
|
||||
size_t line,
|
||||
double run_width);
|
||||
|
||||
PaintRecord(PaintRecord&& other);
|
||||
|
||||
PaintRecord& operator=(PaintRecord&& other);
|
||||
|
||||
SkPoint offset() const { return offset_; }
|
||||
|
||||
void SetOffset(SkPoint pt);
|
||||
|
||||
SkTextBlob* text() const { return text_.get(); }
|
||||
|
||||
const SkPaint::FontMetrics& metrics() const { return metrics_; }
|
||||
|
||||
const TextStyle& style() const { return style_; }
|
||||
|
||||
size_t line() const { return line_; }
|
||||
|
||||
size_t GetRunWidth() const { return run_width_; }
|
||||
|
||||
private:
|
||||
TextStyle style_;
|
||||
// offset_ is the overall offset of the origin of the SkTextBlob.
|
||||
SkPoint offset_;
|
||||
// SkTextBlob stores the glyphs and coordinates to draw them.
|
||||
sk_sp<SkTextBlob> text_;
|
||||
// FontMetrics stores the measurements of the font used.
|
||||
SkPaint::FontMetrics metrics_;
|
||||
size_t line_;
|
||||
double run_width_ = 0.0f;
|
||||
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(PaintRecord);
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_PAINT_RECORD_H_
|
||||
889
engine/src/flutter/third_party/txt/src/paragraph.cc
vendored
Normal file
889
engine/src/flutter/third_party/txt/src/paragraph.cc
vendored
Normal file
@@ -0,0 +1,889 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/paragraph.h"
|
||||
|
||||
#include <hb.h>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <minikin/Layout.h>
|
||||
#include "lib/ftl/logging.h"
|
||||
#include "lib/txt/include/minikin/MinikinFont.h"
|
||||
#include "lib/txt/libs/minikin/HbFontCache.h"
|
||||
#include "lib/txt/libs/minikin/LayoutUtils.h"
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
#include "lib/txt/src/font_skia.h"
|
||||
#include "minikin/LineBreaker.h"
|
||||
#include "third_party/skia/include/core/SkCanvas.h"
|
||||
#include "third_party/skia/include/core/SkPaint.h"
|
||||
#include "third_party/skia/include/core/SkTextBlob.h"
|
||||
#include "third_party/skia/include/core/SkTypeface.h"
|
||||
#include "third_party/skia/include/effects/SkDashPathEffect.h"
|
||||
#include "third_party/skia/include/effects/SkDiscretePathEffect.h"
|
||||
|
||||
namespace txt {
|
||||
namespace {
|
||||
|
||||
const sk_sp<SkTypeface>& GetTypefaceForGlyph(const minikin::Layout& layout,
|
||||
size_t index) {
|
||||
const FontSkia* font = static_cast<const FontSkia*>(layout.getFont(index));
|
||||
return font->GetSkTypeface();
|
||||
}
|
||||
|
||||
// Return the number of glyphs until the typeface changes.
|
||||
size_t GetBlobLength(const minikin::Layout& layout, size_t blob_start) {
|
||||
const size_t glyph_count = layout.nGlyphs();
|
||||
const sk_sp<SkTypeface>& typeface = GetTypefaceForGlyph(layout, blob_start);
|
||||
for (size_t blob_end = blob_start + 1; blob_end < glyph_count; ++blob_end) {
|
||||
if (GetTypefaceForGlyph(layout, blob_end).get() != typeface.get())
|
||||
return blob_end - blob_start;
|
||||
}
|
||||
return glyph_count - blob_start;
|
||||
}
|
||||
|
||||
int GetWeight(const FontWeight weight) {
|
||||
switch (weight) {
|
||||
case FontWeight::w100:
|
||||
return 1;
|
||||
case FontWeight::w200:
|
||||
return 2;
|
||||
case FontWeight::w300:
|
||||
return 3;
|
||||
case FontWeight::w400: // Normal.
|
||||
return 4;
|
||||
case FontWeight::w500:
|
||||
return 5;
|
||||
case FontWeight::w600:
|
||||
return 6;
|
||||
case FontWeight::w700: // Bold.
|
||||
return 7;
|
||||
case FontWeight::w800:
|
||||
return 8;
|
||||
case FontWeight::w900:
|
||||
return 9;
|
||||
}
|
||||
}
|
||||
|
||||
int GetWeight(const TextStyle& style) {
|
||||
return GetWeight(style.font_weight);
|
||||
}
|
||||
|
||||
bool GetItalic(const TextStyle& style) {
|
||||
switch (style.font_style) {
|
||||
case FontStyle::normal:
|
||||
return false;
|
||||
case FontStyle::italic:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void GetFontAndMinikinPaint(const TextStyle& style,
|
||||
minikin::FontStyle* font,
|
||||
minikin::MinikinPaint* paint) {
|
||||
*font = minikin::FontStyle(GetWeight(style), GetItalic(style));
|
||||
paint->size = style.font_size;
|
||||
// Divide by font size so letter spacing is pixels, not proportional to font
|
||||
// size.
|
||||
paint->letterSpacing = style.letter_spacing / style.font_size;
|
||||
paint->wordSpacing = style.word_spacing;
|
||||
paint->scaleX = 1.0f;
|
||||
// Prevent spacing rounding in Minikin. This causes jitter when switching
|
||||
// between same text content with different runs composing it, however, it
|
||||
// also produces more accurate layouts.
|
||||
paint->paintFlags |= minikin::LinearTextFlag;
|
||||
}
|
||||
|
||||
void GetPaint(const TextStyle& style, SkPaint* paint) {
|
||||
paint->setTextSize(style.font_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static const float kDoubleDecorationSpacing = 3.0f;
|
||||
|
||||
Paragraph::Paragraph() = default;
|
||||
|
||||
Paragraph::~Paragraph() = default;
|
||||
|
||||
void Paragraph::SetText(std::vector<uint16_t> text, StyledRuns runs) {
|
||||
needs_layout_ = true;
|
||||
if (text.size() == 0)
|
||||
return;
|
||||
text_ = std::move(text);
|
||||
runs_ = std::move(runs);
|
||||
}
|
||||
|
||||
void Paragraph::InitBreaker() {
|
||||
breaker_.setLocale(icu::Locale(), nullptr);
|
||||
breaker_.resize(text_.size());
|
||||
memcpy(breaker_.buffer(), text_.data(), text_.size() * sizeof(text_[0]));
|
||||
breaker_.setText();
|
||||
}
|
||||
|
||||
// NOTE: Minikin LineBreaker addStyleRun() has an O(N^2) (according to
|
||||
// benchmarks) time complexity where N is the total number of characters.
|
||||
// However, this is not significant for reasonably sized paragraphs. It is
|
||||
// currently recommended to break up very long paragraphs (10k+ characters) to
|
||||
// ensure speedy layout.
|
||||
void Paragraph::AddRunsToLineBreaker(
|
||||
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>&
|
||||
collection_map) {
|
||||
minikin::FontStyle font;
|
||||
minikin::MinikinPaint paint;
|
||||
for (size_t i = 0; i < runs_.size(); ++i) {
|
||||
auto run = runs_.GetRun(i);
|
||||
GetFontAndMinikinPaint(run.style, &font, &paint);
|
||||
breaker_.addStyleRun(&paint,
|
||||
font_collection_->GetMinikinFontCollectionForFamily(
|
||||
run.style.font_family),
|
||||
font, run.start, run.end, false,
|
||||
run.style.letter_spacing);
|
||||
}
|
||||
}
|
||||
|
||||
void Paragraph::FillWhitespaceSet(size_t start,
|
||||
size_t end,
|
||||
hb_font_t* hb_font) {
|
||||
uint32_t unusedGlyph;
|
||||
for (size_t i = start; i < end; ++i) {
|
||||
if (minikin::isWordSpace(text_[i])) {
|
||||
hb_font_get_glyph(hb_font, text_[i], 0, &unusedGlyph);
|
||||
whitespace_set_.insert(unusedGlyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Paragraph::Layout(double width, bool force) {
|
||||
// Do not allow calling layout multiple times without changing anything.
|
||||
if (!needs_layout_ && width == width_ && !force)
|
||||
return;
|
||||
needs_layout_ = false;
|
||||
|
||||
width_ = width;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>
|
||||
collection_map;
|
||||
|
||||
breaker_.setLineWidths(0.0f, 0, width_);
|
||||
// TODO(garyq): Get hyphenator working. Hyphenator should be created with
|
||||
// a pattern binary dataset. Should be something along these lines:
|
||||
//
|
||||
// minikin::Hyphenator* hyph =
|
||||
// minikin::Hyphenator::loadBinary(<paramsgohere>);
|
||||
// breaker_.setLocale(icu::Locale::getRoot(), &hyph);
|
||||
//
|
||||
InitBreaker();
|
||||
AddRunsToLineBreaker(collection_map);
|
||||
breaker_.setJustified(paragraph_style_.text_align == TextAlign::justify);
|
||||
breaker_.setStrategy(paragraph_style_.break_strategy);
|
||||
size_t breaks_count = breaker_.computeBreaks();
|
||||
const int* breaks = breaker_.getBreaks();
|
||||
|
||||
// Create a copy of text_ to use locally so that any changes made to the
|
||||
// vector (such as removing newline characters) is not permanent.
|
||||
std::vector<uint16_t> text(text_);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
|
||||
minikin::FontStyle font;
|
||||
minikin::MinikinPaint minikin_paint;
|
||||
minikin::Layout layout;
|
||||
|
||||
// Disable ligatures
|
||||
// TODO(garyq): Re-enable ligatures.
|
||||
minikin_paint.fontFeatureSettings += "-liga,-clig,";
|
||||
|
||||
SkTextBlobBuilder builder;
|
||||
|
||||
// Reset member variables so Layout still works when called more than once
|
||||
lines_ = 0;
|
||||
line_widths_ = std::vector<double>();
|
||||
line_heights_ = std::vector<double>();
|
||||
line_heights_.push_back(0);
|
||||
records_ = std::vector<PaintRecord>();
|
||||
|
||||
// Set padding elements to have a minimum point.
|
||||
glyph_position_x_ = std::vector<std::vector<double>>();
|
||||
glyph_position_x_.push_back(std::vector<double>());
|
||||
std::vector<double> glyph_single_line_position_x;
|
||||
glyph_single_line_position_x.push_back(0);
|
||||
// Track the x of the previous run to maintain accurate xposition when
|
||||
// multiple SkTextBlobs make up a single line.
|
||||
double previous_run_x_position = 0.0f;
|
||||
|
||||
SkScalar x = 0.0f;
|
||||
SkScalar y = 0.0f;
|
||||
size_t break_index = 0;
|
||||
double max_line_spacing = 0.0f;
|
||||
double max_descent = 0.0f;
|
||||
double prev_max_descent = 0.0f;
|
||||
double line_width = 0.0f;
|
||||
std::vector<SkScalar> x_queue;
|
||||
double justify_spacing = 0.0f;
|
||||
double prev_word_pos = 0.0f;
|
||||
double prev_char_advance = 0.0f;
|
||||
double current_x_position = previous_run_x_position;
|
||||
|
||||
std::vector<const SkTextBlobBuilder::RunBuffer*> buffers;
|
||||
std::vector<size_t> buffer_sizes;
|
||||
int word_count = 0;
|
||||
|
||||
auto postprocess_line = [this, &x_queue, &y]() -> void {
|
||||
size_t record_index = 0;
|
||||
for (size_t i = 0; i < x_queue.size(); ++i) {
|
||||
record_index = records_.size() - (x_queue.size() - i);
|
||||
records_[record_index].SetOffset(SkPoint::Make(x_queue[i], y));
|
||||
// Adjust the offsets for each of the different alignments.
|
||||
switch (paragraph_style_.text_align) {
|
||||
case TextAlign::left:
|
||||
break;
|
||||
case TextAlign::right: {
|
||||
records_[record_index].SetOffset(SkPoint::Make(
|
||||
records_[record_index].offset().x() + width_ -
|
||||
breaker_.getWidths()[records_[record_index].line()],
|
||||
records_[record_index].offset().y()));
|
||||
break;
|
||||
}
|
||||
case TextAlign::center: {
|
||||
records_[record_index].SetOffset(SkPoint::Make(
|
||||
records_[record_index].offset().x() +
|
||||
(width_ -
|
||||
breaker_.getWidths()[records_[record_index].line()]) /
|
||||
2,
|
||||
records_[record_index].offset().y()));
|
||||
break;
|
||||
}
|
||||
case TextAlign::justify: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Correct positions stored in the member vars.
|
||||
for (size_t y_index = 0; y_index < lines_; ++y_index) {
|
||||
switch (paragraph_style_.text_align) {
|
||||
case TextAlign::left:
|
||||
break;
|
||||
case TextAlign::right: {
|
||||
for (size_t i = 0; i < glyph_position_x_[y_index].size(); ++i) {
|
||||
glyph_position_x_[y_index][i] +=
|
||||
width_ - breaker_.getWidths()[y_index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TextAlign::center: {
|
||||
for (size_t i = 0; i < glyph_position_x_[y_index].size(); ++i) {
|
||||
glyph_position_x_[y_index][i] +=
|
||||
(width_ - breaker_.getWidths()[y_index]) / 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TextAlign::justify: {
|
||||
// TODO(garyq): Track position changes due to justify in justify
|
||||
// method.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
x_queue.clear();
|
||||
};
|
||||
for (size_t run_index = 0; run_index < runs_.size(); ++run_index) {
|
||||
auto run = runs_.GetRun(run_index);
|
||||
bool is_newline = text_[run.start] == '\n' && run.end - run.start == 1;
|
||||
// Replace '\n' with a null character so that a 'missing glyph' box is not
|
||||
// drawn.
|
||||
if (is_newline)
|
||||
text[run.start] = '\0';
|
||||
|
||||
GetFontAndMinikinPaint(run.style, &font, &minikin_paint);
|
||||
GetPaint(run.style, &paint);
|
||||
|
||||
size_t layout_start = run.start;
|
||||
// Layout until the end of the run or too many lines.
|
||||
while (layout_start < run.end && lines_ < paragraph_style_.max_lines) {
|
||||
const size_t next_break = (break_index > breaks_count - 1)
|
||||
? std::numeric_limits<size_t>::max()
|
||||
: breaks[break_index];
|
||||
const size_t layout_end = std::min(run.end, next_break);
|
||||
|
||||
bool bidiFlags = paragraph_style_.rtl;
|
||||
// Minikin Layout doLayout() has an O(N^2) (according to
|
||||
// benchmarks) time complexity where N is the total number of characters.
|
||||
// However, this is not significant for reasonably sized paragraphs. It is
|
||||
// currently recommended to break up very long paragraphs (10k+
|
||||
// characters) to ensure speedy layout.
|
||||
layout.doLayout(text.data() + layout_start, 0, layout_end - layout_start,
|
||||
layout_end - layout_start, bidiFlags, font, minikin_paint,
|
||||
font_collection_->GetMinikinFontCollectionForFamily(
|
||||
run.style.font_family));
|
||||
FillWhitespaceSet(layout_start, layout_end,
|
||||
minikin::getHbFontLocked(layout.getFont(0)));
|
||||
|
||||
const size_t glyph_count = layout.nGlyphs();
|
||||
size_t blob_start = 0;
|
||||
|
||||
// Each blob.
|
||||
buffers = std::vector<const SkTextBlobBuilder::RunBuffer*>();
|
||||
buffer_sizes = std::vector<size_t>();
|
||||
word_count = 0;
|
||||
double temp_line_spacing = 0;
|
||||
current_x_position = 0;
|
||||
while (blob_start < glyph_count) {
|
||||
const size_t blob_length = GetBlobLength(layout, blob_start);
|
||||
buffer_sizes.push_back(blob_length);
|
||||
// TODO(abarth): Precompute when we can use allocRunPosH.
|
||||
paint.setTypeface(GetTypefaceForGlyph(layout, blob_start));
|
||||
|
||||
// Check if we should remove trailing whitespace of blobs.
|
||||
size_t trailing_length = 0;
|
||||
while ((paragraph_style_.text_align == TextAlign::center ||
|
||||
paragraph_style_.text_align == TextAlign::right) &&
|
||||
whitespace_set_.count(layout.getGlyphId(
|
||||
blob_start + blob_length - trailing_length - 1)) > 0 &&
|
||||
layout_end == next_break) {
|
||||
++trailing_length;
|
||||
}
|
||||
|
||||
buffers.push_back(
|
||||
&builder.allocRunPos(paint, blob_length - trailing_length));
|
||||
|
||||
// TODO(garyq): Implement RTL.
|
||||
// Each Glyph/Letter.
|
||||
bool whitespace_ended = true;
|
||||
float letter_spacing = 0;
|
||||
for (size_t blob_index = 0; blob_index < blob_length - trailing_length;
|
||||
++blob_index) {
|
||||
const size_t glyph_index = blob_start + blob_index;
|
||||
buffers.back()->glyphs[blob_index] = layout.getGlyphId(glyph_index);
|
||||
|
||||
const size_t pos_index = 2 * blob_index;
|
||||
// Extract the letter spacing by itself out of the minikin layout.
|
||||
letter_spacing = run.style.letter_spacing == 0
|
||||
? 0
|
||||
: layout.getX(glyph_index) - current_x_position;
|
||||
buffers.back()->pos[pos_index] = current_x_position + letter_spacing;
|
||||
glyph_single_line_position_x.push_back(
|
||||
current_x_position + previous_run_x_position + letter_spacing);
|
||||
buffers.back()->pos[pos_index + 1] = layout.getY(glyph_index);
|
||||
|
||||
current_x_position += layout.getCharAdvance(glyph_index);
|
||||
// Check if the current Glyph is a whitespace and handle multiple
|
||||
// whitespaces in a row.
|
||||
if (whitespace_set_.count(layout.getGlyphId(glyph_index)) > 0) {
|
||||
// Only increment word_count if it is the first in a series of
|
||||
// whitespaces.
|
||||
if (whitespace_ended) {
|
||||
++word_count;
|
||||
}
|
||||
whitespace_ended = false;
|
||||
} else {
|
||||
whitespace_ended = true;
|
||||
}
|
||||
|
||||
prev_char_advance = layout.getCharAdvance(glyph_index);
|
||||
}
|
||||
blob_start += blob_length;
|
||||
previous_run_x_position += current_x_position + letter_spacing;
|
||||
}
|
||||
|
||||
// TODO(abarth): We could keep the same SkTextBlobBuilder as long as the
|
||||
// color stayed the same.
|
||||
SkPaint::FontMetrics metrics;
|
||||
paint.getFontMetrics(&metrics);
|
||||
// Apply additional word spacing if the text is justified.
|
||||
if (paragraph_style_.text_align == TextAlign::justify &&
|
||||
buffer_sizes.size() > 0) {
|
||||
JustifyLine(buffers, buffer_sizes, word_count, justify_spacing);
|
||||
}
|
||||
records_.push_back(PaintRecord{run.style, builder.make(), metrics, lines_,
|
||||
layout.getAdvance()});
|
||||
line_width +=
|
||||
std::abs(records_[records_.size() - 1].text()->bounds().fRight +
|
||||
records_[records_.size() - 1].text()->bounds().fLeft);
|
||||
// Must adjust each line to the largest text in the line, so cannot
|
||||
// directly push the offset property of PaintRecord until line is
|
||||
// finished.
|
||||
x_queue.push_back(x);
|
||||
|
||||
temp_line_spacing = lines_ == 0 ? -metrics.fAscent * run.style.height
|
||||
: (-metrics.fAscent + metrics.fLeading) *
|
||||
run.style.height;
|
||||
if (max_line_spacing < temp_line_spacing) {
|
||||
max_line_spacing = temp_line_spacing;
|
||||
// Record the alphabetic_baseline_ and idegraphic_baseline_:
|
||||
if (lines_ == 0) {
|
||||
alphabetic_baseline_ = -metrics.fAscent * run.style.height;
|
||||
// TODO(garyq): Properly implement ideographic_baseline_.
|
||||
ideographic_baseline_ =
|
||||
(metrics.fUnderlinePosition - metrics.fAscent) * run.style.height;
|
||||
}
|
||||
}
|
||||
temp_line_spacing = metrics.fDescent * run.style.height;
|
||||
if (max_descent < temp_line_spacing)
|
||||
max_descent = temp_line_spacing;
|
||||
|
||||
if (layout_end == next_break || is_newline) {
|
||||
y += roundf(max_line_spacing + prev_max_descent);
|
||||
line_heights_.push_back(
|
||||
(line_heights_.empty() ? 0 : line_heights_.back()) +
|
||||
roundf(max_line_spacing + max_descent));
|
||||
glyph_single_line_position_x.push_back(
|
||||
glyph_single_line_position_x.back() + prev_char_advance);
|
||||
glyph_single_line_position_x.push_back(FLT_MAX);
|
||||
glyph_position_x_.push_back(glyph_single_line_position_x);
|
||||
prev_max_descent = max_descent;
|
||||
line_widths_.push_back(line_width);
|
||||
postprocess_line();
|
||||
|
||||
// Reset Variables for next line.
|
||||
max_line_spacing = 0.0f;
|
||||
max_descent = 0.0f;
|
||||
x = 0.0f;
|
||||
prev_word_pos = 0;
|
||||
prev_char_advance = 0.0f;
|
||||
previous_run_x_position = 0.0f;
|
||||
current_x_position = 0.0f;
|
||||
line_width = 0.0f;
|
||||
break_index += 1;
|
||||
lines_++;
|
||||
glyph_single_line_position_x = std::vector<double>();
|
||||
glyph_single_line_position_x.push_back(0);
|
||||
} else {
|
||||
x += layout.getAdvance();
|
||||
}
|
||||
|
||||
layout_start = layout_end;
|
||||
}
|
||||
}
|
||||
// Handle last line tasks.
|
||||
y += roundf(max_line_spacing + prev_max_descent);
|
||||
postprocess_line();
|
||||
if (line_width != 0)
|
||||
line_widths_.push_back(line_width);
|
||||
|
||||
// Finalize measurements
|
||||
line_heights_.push_back((line_heights_.empty() ? 0 : line_heights_.back()) +
|
||||
roundf(max_line_spacing + max_descent));
|
||||
glyph_single_line_position_x.push_back(glyph_single_line_position_x.back() +
|
||||
prev_char_advance);
|
||||
glyph_single_line_position_x.push_back(FLT_MAX);
|
||||
glyph_position_x_.push_back(glyph_single_line_position_x);
|
||||
|
||||
// Remove justification on the last line.
|
||||
if (paragraph_style_.text_align == TextAlign::justify &&
|
||||
buffer_sizes.size() > 0) {
|
||||
JustifyLine(buffers, buffer_sizes, word_count, justify_spacing, -1);
|
||||
}
|
||||
line_widths_ =
|
||||
std::vector<double>(breaker_.getWidths(), breaker_.getWidths() + lines_);
|
||||
CalculateIntrinsicWidths();
|
||||
breaker_.finish();
|
||||
}
|
||||
|
||||
// Amends the buffers to incorporate justification.
|
||||
void Paragraph::JustifyLine(
|
||||
std::vector<const SkTextBlobBuilder::RunBuffer*>& buffers,
|
||||
std::vector<size_t>& buffer_sizes,
|
||||
int word_count,
|
||||
double& justify_spacing,
|
||||
double multiplier) {
|
||||
// We will use the previous justification spacing when undoing justification.
|
||||
if (multiplier > 0) {
|
||||
justify_spacing =
|
||||
(width_ - breaker_.getWidths()[lines_]) / (word_count - 1);
|
||||
}
|
||||
word_count = 0;
|
||||
bool whitespace_ended = true;
|
||||
for (size_t i = 0; i < buffers.size(); ++i) {
|
||||
for (size_t glyph_index = 0; glyph_index < buffer_sizes[i]; ++glyph_index) {
|
||||
// Check if the current Glyph is a whitespace and handle multiple
|
||||
// whitespaces in a row.
|
||||
if (whitespace_set_.count(buffers[i]->glyphs[glyph_index]) > 0) {
|
||||
// Only increment word_count and add justification spacing to
|
||||
// whitespace if it is the first in a series of whitespaces.
|
||||
if (whitespace_ended) {
|
||||
++word_count;
|
||||
buffers[i]->pos[glyph_index * 2] +=
|
||||
justify_spacing * multiplier * word_count;
|
||||
}
|
||||
whitespace_ended = false;
|
||||
} else {
|
||||
// Add justification spacing for all non-whitespace glyphs.
|
||||
buffers[i]->pos[glyph_index * 2] +=
|
||||
justify_spacing * multiplier * word_count;
|
||||
whitespace_ended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ParagraphStyle& Paragraph::GetParagraphStyle() const {
|
||||
return paragraph_style_;
|
||||
}
|
||||
|
||||
double Paragraph::GetAlphabeticBaseline() const {
|
||||
// Currently -fAscent
|
||||
return alphabetic_baseline_;
|
||||
}
|
||||
|
||||
double Paragraph::GetIdeographicBaseline() const {
|
||||
// TODO(garyq): Currently -fAscent + fUnderlinePosition. Verify this.
|
||||
return ideographic_baseline_;
|
||||
}
|
||||
|
||||
void Paragraph::CalculateIntrinsicWidths() {
|
||||
max_intrinsic_width_ = 0;
|
||||
for (size_t i = 0; i < line_widths_.size(); ++i) {
|
||||
max_intrinsic_width_ += line_widths_[i];
|
||||
}
|
||||
|
||||
// TODO(garyq): Investigate correctness of the following implementation of min
|
||||
// intrinsic width. This is currently the longest line in the text after
|
||||
// layout.
|
||||
min_intrinsic_width_ = 0;
|
||||
for (size_t i = 0; i < line_widths_.size(); ++i) {
|
||||
min_intrinsic_width_ = std::max(min_intrinsic_width_, line_widths_[i]);
|
||||
}
|
||||
|
||||
// Ensure that min < max widths.
|
||||
min_intrinsic_width_ = std::min(max_intrinsic_width_, min_intrinsic_width_);
|
||||
max_intrinsic_width_ = std::max(max_intrinsic_width_, min_intrinsic_width_);
|
||||
}
|
||||
|
||||
double Paragraph::GetMaxIntrinsicWidth() const {
|
||||
return max_intrinsic_width_;
|
||||
}
|
||||
|
||||
double Paragraph::GetMinIntrinsicWidth() const {
|
||||
return min_intrinsic_width_;
|
||||
}
|
||||
|
||||
size_t Paragraph::TextSize() const {
|
||||
return text_.size();
|
||||
}
|
||||
|
||||
double Paragraph::GetHeight() const {
|
||||
return line_heights_[line_heights_.size() - 2];
|
||||
}
|
||||
|
||||
double Paragraph::GetLayoutWidth() const {
|
||||
double w = 0;
|
||||
for (size_t i = 0; i < line_widths_.size(); ++i) {
|
||||
w = std::max(w, line_widths_[i]);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
double Paragraph::GetMaxWidth() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
void Paragraph::SetParagraphStyle(const ParagraphStyle& style) {
|
||||
needs_layout_ = true;
|
||||
paragraph_style_ = style;
|
||||
}
|
||||
|
||||
void Paragraph::SetFontCollection(FontCollection* font_collection) {
|
||||
font_collection_ = font_collection;
|
||||
}
|
||||
|
||||
// The x,y coordinates will be the very top left corner of the rendered
|
||||
// paragraph.
|
||||
void Paragraph::Paint(SkCanvas* canvas, double x, double y) {
|
||||
SkAutoCanvasRestore canvas_restore(canvas, true);
|
||||
canvas->translate(x, y);
|
||||
SkPaint paint;
|
||||
for (size_t index = 0; index < records_.size(); ++index) {
|
||||
PaintRecord& record = records_[index];
|
||||
paint.setColor(record.style().color);
|
||||
SkPoint offset = record.offset();
|
||||
canvas->drawTextBlob(record.text(), offset.x(), offset.y(), paint);
|
||||
PaintDecorations(canvas, offset.x(), offset.y(), index);
|
||||
}
|
||||
}
|
||||
|
||||
void Paragraph::PaintDecorations(SkCanvas* canvas,
|
||||
double x,
|
||||
double y,
|
||||
size_t record_index) {
|
||||
PaintRecord& record = records_[record_index];
|
||||
if (record.style().decoration == TextDecoration::kNone)
|
||||
return;
|
||||
|
||||
const SkPaint::FontMetrics& metrics = record.metrics();
|
||||
SkPaint paint;
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
if (record.style().decoration_color == SK_ColorTRANSPARENT) {
|
||||
paint.setColor(record.style().color);
|
||||
} else {
|
||||
paint.setColor(record.style().decoration_color);
|
||||
}
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
// This is set to 2 for the double line style
|
||||
int decoration_count = 1;
|
||||
|
||||
// Filled when drawing wavy decorations.
|
||||
SkPath path;
|
||||
|
||||
double width = 0;
|
||||
if (paragraph_style_.text_align == TextAlign::justify &&
|
||||
record.line() != lines_ - 1) {
|
||||
width = width_;
|
||||
} else {
|
||||
width = record.GetRunWidth();
|
||||
}
|
||||
|
||||
paint.setStrokeWidth(
|
||||
(SkToBool(metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags::
|
||||
kUnderlineThicknessIsValid_Flag))
|
||||
? metrics.fUnderlineThickness *
|
||||
record.style().decoration_thickness_multiplier
|
||||
// Backup value if the fUnderlineThickness metric is not available:
|
||||
// Divide by 14pt as it is the default size.
|
||||
: record.style().font_size / 14.0f *
|
||||
record.style().decoration_thickness_multiplier);
|
||||
|
||||
// Setup the decorations.
|
||||
switch (record.style().decoration_style) {
|
||||
case TextDecorationStyle::kSolid: {
|
||||
break;
|
||||
}
|
||||
case TextDecorationStyle::kDouble: {
|
||||
decoration_count = 2;
|
||||
break;
|
||||
}
|
||||
// Note: the intervals are scaled by the thickness of the line, so it is
|
||||
// possible to change spacing by changing the decoration_thickness
|
||||
// property of TextStyle.
|
||||
case TextDecorationStyle::kDotted: {
|
||||
// Divide by 14pt as it is the default size.
|
||||
const float scale = record.style().font_size / 14.0f;
|
||||
const SkScalar intervals[] = {1.0f * scale, 1.5f * scale, 1.0f * scale,
|
||||
1.5f * scale};
|
||||
size_t count = sizeof(intervals) / sizeof(intervals[0]);
|
||||
paint.setPathEffect(SkPathEffect::MakeCompose(
|
||||
SkDashPathEffect::Make(intervals, count, 0.0f),
|
||||
SkDiscretePathEffect::Make(0, 0)));
|
||||
break;
|
||||
}
|
||||
// Note: the intervals are scaled by the thickness of the line, so it is
|
||||
// possible to change spacing by changing the decoration_thickness
|
||||
// property of TextStyle.
|
||||
case TextDecorationStyle::kDashed: {
|
||||
// Divide by 14pt as it is the default size.
|
||||
const float scale = record.style().font_size / 14.0f;
|
||||
const SkScalar intervals[] = {4.0f * scale, 2.0f * scale, 4.0f * scale,
|
||||
2.0f * scale};
|
||||
size_t count = sizeof(intervals) / sizeof(intervals[0]);
|
||||
paint.setPathEffect(SkPathEffect::MakeCompose(
|
||||
SkDashPathEffect::Make(intervals, count, 0.0f),
|
||||
SkDiscretePathEffect::Make(0, 0)));
|
||||
break;
|
||||
}
|
||||
case TextDecorationStyle::kWavy: {
|
||||
int wave_count = 0;
|
||||
double x_start = 0;
|
||||
double wavelength = metrics.fUnderlineThickness *
|
||||
record.style().decoration_thickness_multiplier;
|
||||
path.moveTo(x, y);
|
||||
while (x_start + wavelength * 2 < width) {
|
||||
path.rQuadTo(wavelength, wave_count % 2 != 0 ? wavelength : -wavelength,
|
||||
wavelength * 2, 0);
|
||||
x_start += wavelength * 2;
|
||||
++wave_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the decorations.
|
||||
// Use a for loop for "kDouble" decoration style
|
||||
for (int i = 0; i < decoration_count; i++) {
|
||||
double y_offset =
|
||||
i * metrics.fUnderlineThickness * kDoubleDecorationSpacing;
|
||||
double y_offset_original = y_offset;
|
||||
// Underline
|
||||
if (record.style().decoration & 0x1) {
|
||||
y_offset +=
|
||||
(SkToBool(metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags::
|
||||
kUnderlinePositionIsValid_Flag))
|
||||
? metrics.fUnderlinePosition
|
||||
: metrics.fUnderlineThickness;
|
||||
if (record.style().decoration_style != TextDecorationStyle::kWavy) {
|
||||
canvas->drawLine(x, y + y_offset, x + width, y + y_offset, paint);
|
||||
} else {
|
||||
SkPath offsetPath = path;
|
||||
offsetPath.offset(0, y_offset);
|
||||
canvas->drawPath(offsetPath, paint);
|
||||
}
|
||||
y_offset = y_offset_original;
|
||||
}
|
||||
// Overline
|
||||
if (record.style().decoration & 0x2) {
|
||||
// We subtract fAscent here because for double overlines, we want the
|
||||
// second line to be above, not below the first.
|
||||
y_offset -= metrics.fAscent;
|
||||
if (record.style().decoration_style != TextDecorationStyle::kWavy) {
|
||||
canvas->drawLine(x, y - y_offset, x + width, y - y_offset, paint);
|
||||
} else {
|
||||
SkPath offsetPath = path;
|
||||
offsetPath.offset(0, -y_offset);
|
||||
canvas->drawPath(offsetPath, paint);
|
||||
}
|
||||
y_offset = y_offset_original;
|
||||
}
|
||||
// Strikethrough
|
||||
if (record.style().decoration & 0x4) {
|
||||
if (SkToBool(metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags::
|
||||
kStrikeoutThicknessIsValid_Flag))
|
||||
paint.setStrokeWidth(metrics.fStrikeoutThickness *
|
||||
record.style().decoration_thickness_multiplier);
|
||||
// Make sure the double line is "centered" vertically.
|
||||
y_offset += (decoration_count - 1.0) * metrics.fUnderlineThickness *
|
||||
kDoubleDecorationSpacing / -2.0;
|
||||
y_offset +=
|
||||
(SkToBool(metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags::
|
||||
kStrikeoutThicknessIsValid_Flag))
|
||||
? metrics.fStrikeoutPosition
|
||||
// Backup value if the strikeoutposition metric is not
|
||||
// available:
|
||||
: metrics.fXHeight / -2.0;
|
||||
if (record.style().decoration_style != TextDecorationStyle::kWavy) {
|
||||
canvas->drawLine(x, y + y_offset, x + width, y + y_offset, paint);
|
||||
} else {
|
||||
SkPath offsetPath = path;
|
||||
offsetPath.offset(0, y_offset);
|
||||
canvas->drawPath(offsetPath, paint);
|
||||
}
|
||||
y_offset = y_offset_original;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SkRect> Paragraph::GetRectsForRange(size_t start,
|
||||
size_t end) const {
|
||||
std::vector<SkRect> rects;
|
||||
end = fmax(start, end);
|
||||
start = fmin(start, end);
|
||||
FTL_DCHECK(end >= start && end >= 0 && start >= 0);
|
||||
if (end == start)
|
||||
end = start + 1;
|
||||
end = fmin(end, text_.size());
|
||||
while (start < end) {
|
||||
SkIPoint word_bounds = GetWordBoundary(start);
|
||||
word_bounds.fX = fmax(start, word_bounds.fX);
|
||||
word_bounds.fY = fmin(end, word_bounds.fY);
|
||||
start = fmax(word_bounds.fY, start + 1);
|
||||
SkRect left_limits = GetCoordinatesForGlyphPosition(word_bounds.fX);
|
||||
SkRect right_limits = GetCoordinatesForGlyphPosition(word_bounds.fY - 1);
|
||||
if (left_limits.top() < right_limits.top()) {
|
||||
rects.push_back(SkRect::MakeLTRB(
|
||||
0, right_limits.top(), right_limits.right(), right_limits.bottom()));
|
||||
} else {
|
||||
rects.push_back(SkRect::MakeLTRB(left_limits.left(), left_limits.top(),
|
||||
right_limits.right(),
|
||||
right_limits.bottom()));
|
||||
}
|
||||
}
|
||||
return rects;
|
||||
}
|
||||
|
||||
SkRect Paragraph::GetCoordinatesForGlyphPosition(size_t pos) const {
|
||||
size_t remainder = fmin(pos, text_.size());
|
||||
remainder++;
|
||||
size_t line = 1;
|
||||
for (line = 1; line < line_heights_.size() - 1; ++line) {
|
||||
if (remainder > glyph_position_x_[line].size() - 3) {
|
||||
remainder -= glyph_position_x_[line].size() - 3;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SkRect::MakeLTRB(glyph_position_x_[line][remainder],
|
||||
line_heights_[line - 1],
|
||||
remainder < glyph_position_x_[line].size() - 2
|
||||
? glyph_position_x_[line][remainder + 1]
|
||||
: line_widths_[line - 1],
|
||||
line_heights_[line]);
|
||||
}
|
||||
|
||||
size_t Paragraph::GetGlyphPositionAtCoordinate(
|
||||
double dx,
|
||||
double dy,
|
||||
bool using_glyph_center_as_boundary) const {
|
||||
size_t offset = 0;
|
||||
size_t y_index = 1;
|
||||
size_t prev_count = 0;
|
||||
for (y_index = 1; y_index < line_heights_.size() - 2; ++y_index) {
|
||||
if (dy < line_heights_[y_index]) {
|
||||
offset += prev_count;
|
||||
prev_count = glyph_position_x_[y_index - 1].size() - 3;
|
||||
break;
|
||||
} else {
|
||||
offset += prev_count;
|
||||
prev_count = glyph_position_x_[y_index].size() - 3;
|
||||
}
|
||||
}
|
||||
if (y_index == line_heights_.size() - 2)
|
||||
offset += prev_count;
|
||||
prev_count = 0;
|
||||
for (size_t x_index = 1; x_index < glyph_position_x_[y_index].size() - 1;
|
||||
++x_index) {
|
||||
if (dx < glyph_position_x_[y_index][x_index] -
|
||||
(using_glyph_center_as_boundary
|
||||
? (glyph_position_x_[y_index][x_index] -
|
||||
glyph_position_x_[y_index][x_index - 1]) /
|
||||
2.0f
|
||||
: 0)) {
|
||||
break;
|
||||
} else {
|
||||
offset += prev_count;
|
||||
prev_count = 1;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
SkIPoint Paragraph::GetWordBoundary(size_t offset) const {
|
||||
// TODO(garyq): Consider punctuation as separate words.
|
||||
if (text_.size() == 0)
|
||||
return SkIPoint::Make(0, 0);
|
||||
return SkIPoint::Make(
|
||||
minikin::getPrevWordBreakForCache(text_.data(), offset + 1, text_.size()),
|
||||
minikin::getNextWordBreakForCache(text_.data(), offset, text_.size()));
|
||||
}
|
||||
|
||||
int Paragraph::GetLineCount() const {
|
||||
return lines_;
|
||||
}
|
||||
|
||||
bool Paragraph::DidExceedMaxLines() const {
|
||||
if (lines_ > paragraph_style_.max_lines)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Paragraph::SetDirty(bool dirty) {
|
||||
needs_layout_ = dirty;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
252
engine/src/flutter/third_party/txt/src/paragraph.h
vendored
Normal file
252
engine/src/flutter/third_party/txt/src/paragraph.h
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_PARAGRAPH_H_
|
||||
#define LIB_TXT_SRC_PARAGRAPH_H_
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/ftl/macros.h"
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
#include "lib/txt/src/paint_record.h"
|
||||
#include "lib/txt/src/paragraph_style.h"
|
||||
#include "lib/txt/src/styled_runs.h"
|
||||
#include "minikin/LineBreaker.h"
|
||||
#include "third_party/gtest/include/gtest/gtest_prod.h"
|
||||
#include "third_party/skia/include/core/SkPoint.h"
|
||||
#include "third_party/skia/include/core/SkRect.h"
|
||||
#include "third_party/skia/include/core/SkTextBlob.h"
|
||||
|
||||
class SkCanvas;
|
||||
|
||||
namespace txt {
|
||||
|
||||
using GlyphID = uint32_t;
|
||||
|
||||
// Paragraph provides Layout, metrics, and painting capabilites for text. Once a
|
||||
// Paragraph is constructed with ParagraphBuilder::Build(), an example basic
|
||||
// workflow can be this:
|
||||
//
|
||||
// std::unique_ptr<Paragraph> paragraph = paragraph_builder.Build();
|
||||
// paragraph->Layout(<somewidthgoeshere>);
|
||||
// paragraph->Paint(<someSkCanvas>, <xpos>, <ypos>);
|
||||
class Paragraph {
|
||||
public:
|
||||
// Constructor. I is highly recommended to construct a paragrph with a
|
||||
// ParagraphBuilder.
|
||||
Paragraph();
|
||||
|
||||
~Paragraph();
|
||||
|
||||
// Minikin Layout doLayout() and LineBreaker addStyleRun() has an
|
||||
// O(N^2) (according to benchmarks) time complexity where N is the total
|
||||
// number of characters. However, this is not significant for reasonably sized
|
||||
// paragraphs. It is currently recommended to break up very long paragraphs
|
||||
// (10k+ characters) to ensure speedy layout.
|
||||
//
|
||||
// Layout calculates the positioning of all the glyphs. Must call this method
|
||||
// before Painting and getting any statistics from this class.
|
||||
void Layout(double width, bool force = false);
|
||||
|
||||
// Paints the Laid out text onto the supplied SkCanvas at (x, y) offset from
|
||||
// the origin. Only valid after Layout() is called.
|
||||
void Paint(SkCanvas* canvas, double x, double y);
|
||||
|
||||
// Getter for paragraph_style_.
|
||||
const ParagraphStyle& GetParagraphStyle() const;
|
||||
|
||||
// Returns the number of characters/unicode characters. AKA text_.size()
|
||||
size_t TextSize() const;
|
||||
|
||||
// Returns the height of the laid out paragraph. NOTE this is not a tight
|
||||
// bounding height of the glyphs, as some glyphs do not reach as low as they
|
||||
// can.
|
||||
double GetHeight() const;
|
||||
|
||||
// Returns the actual max width of the longest line after Layout().
|
||||
double GetLayoutWidth() const;
|
||||
|
||||
// Returns the width provided in the Layout() method. This is the maximum
|
||||
// width any line in the laid out paragraph can occupy. We expect that
|
||||
// GetMaxWidth() >= GetLayoutWidth().
|
||||
double GetMaxWidth() const;
|
||||
|
||||
// Distance from top of paragraph to the Alphabetic baseline of the first
|
||||
// line. Used for alphabetic fonts (A-Z, a-z, greek, etc.)
|
||||
double GetAlphabeticBaseline() const;
|
||||
|
||||
// Distance from top of paragraph to the Ideographic baseline of the first
|
||||
// line. Used for ideographic fonts (Chinese, Japanese, Korean, etc.)
|
||||
double GetIdeographicBaseline() const;
|
||||
|
||||
// Returns the total width covered by the paragraph without linebreaking.
|
||||
double GetMaxIntrinsicWidth() const;
|
||||
|
||||
// Currently, calculated similarly to as GetLayoutWidth(), however this is not
|
||||
// nessecarily 100% correct in all cases.
|
||||
//
|
||||
// Returns the actual max width of the longest line after Layout().
|
||||
double GetMinIntrinsicWidth() const;
|
||||
|
||||
// Returns a vector of bounding boxes that enclose all text between start and
|
||||
// end glyph indexes, including start and excluding end.
|
||||
std::vector<SkRect> GetRectsForRange(size_t start, size_t end) const;
|
||||
|
||||
// Returns the index of the glyph that corresponds to the provided coordinate,
|
||||
// with the top left corner as the origin, and +y direction as down.
|
||||
//
|
||||
// When using_glyph_center_as_boundary == true, coords to the + direction of
|
||||
// the center x-position of the glyph will be considered as the next glyph. A
|
||||
// typical use-case for this is when the cursor is meant to be on either side
|
||||
// of any given character. This allows the transition border to be middle of
|
||||
// each character.
|
||||
size_t GetGlyphPositionAtCoordinate(
|
||||
double dx,
|
||||
double dy,
|
||||
bool using_glyph_center_as_boundary = false) const;
|
||||
|
||||
// Returns a bounding box that encloses the glyph at the index pos.
|
||||
SkRect GetCoordinatesForGlyphPosition(size_t pos) const;
|
||||
|
||||
// Finds the first and last glyphs that define a word containing the glyph at
|
||||
// index offset.
|
||||
SkIPoint GetWordBoundary(size_t offset) const;
|
||||
|
||||
// Returns the number of lines the paragraph takes up. If the text exceeds the
|
||||
// amount width and maxlines provides, Layout() truncates the extra text from
|
||||
// the layout and this will return the max lines allowed.
|
||||
int GetLineCount() const;
|
||||
|
||||
// Checks if the layout extends past the maximum lines and had to be
|
||||
// truncated.
|
||||
bool DidExceedMaxLines() const;
|
||||
|
||||
// Sets the needs_layout_ to dirty. When Layout() is called, a new Layout will
|
||||
// be performed when this is set to true. Can also be used to prevent a new
|
||||
// Layout from being calculated by setting to false.
|
||||
void SetDirty(bool dirty = true);
|
||||
|
||||
private:
|
||||
friend class ParagraphBuilder;
|
||||
FRIEND_TEST(RenderTest, SimpleParagraph);
|
||||
FRIEND_TEST(RenderTest, SimpleRedParagraph);
|
||||
FRIEND_TEST(RenderTest, RainbowParagraph);
|
||||
FRIEND_TEST(RenderTest, DefaultStyleParagraph);
|
||||
FRIEND_TEST(RenderTest, BoldParagraph);
|
||||
FRIEND_TEST(RenderTest, LeftAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, RightAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, CenterAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, JustifyAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, DecorationsParagraph);
|
||||
FRIEND_TEST(RenderTest, ItalicsParagraph);
|
||||
FRIEND_TEST(RenderTest, ChineseParagraph);
|
||||
FRIEND_TEST(RenderTest, DISABLED_ArabicParagraph);
|
||||
FRIEND_TEST(RenderTest, SpacingParagraph);
|
||||
FRIEND_TEST(RenderTest, LongWordParagraph);
|
||||
FRIEND_TEST(RenderTest, KernScaleParagraph);
|
||||
FRIEND_TEST(RenderTest, NewlineParagraph);
|
||||
FRIEND_TEST(RenderTest, EmojiParagraph);
|
||||
FRIEND_TEST(RenderTest, HyphenBreakParagraph);
|
||||
FRIEND_TEST(RenderTest, RepeatLayoutParagraph);
|
||||
|
||||
// Starting data to layout.
|
||||
std::vector<uint16_t> text_;
|
||||
StyledRuns runs_;
|
||||
ParagraphStyle paragraph_style_;
|
||||
FontCollection* font_collection_;
|
||||
|
||||
minikin::LineBreaker breaker_;
|
||||
|
||||
// Stores the result of Layout().
|
||||
std::vector<PaintRecord> records_;
|
||||
|
||||
// TODO(garyq): Can we access this info without redundantly storing it here?
|
||||
std::vector<double> line_widths_;
|
||||
std::vector<double> line_heights_;
|
||||
// Holds the laid out x positions of each glyph, as well as padding to make
|
||||
// math on it simpler.
|
||||
std::vector<std::vector<double>> glyph_position_x_;
|
||||
|
||||
// Set of glyph IDs that correspond to whitespace.
|
||||
std::set<GlyphID> whitespace_set_;
|
||||
|
||||
// The max width of the paragraph as provided in the most recent Layout()
|
||||
// call.
|
||||
double width_ = -1.0f;
|
||||
size_t lines_ = 0;
|
||||
double max_intrinsic_width_ = 0;
|
||||
double min_intrinsic_width_ = 0;
|
||||
double alphabetic_baseline_ = FLT_MAX;
|
||||
double ideographic_baseline_ = FLT_MAX;
|
||||
|
||||
bool needs_layout_ = true;
|
||||
|
||||
struct WaveCoordinates {
|
||||
double x_start;
|
||||
double y_start;
|
||||
double x_end;
|
||||
double y_end;
|
||||
|
||||
WaveCoordinates(double x_s, double y_s, double x_e, double y_e)
|
||||
: x_start(x_s), y_start(y_s), x_end(x_e), y_end(y_e) {}
|
||||
};
|
||||
|
||||
// Passes in the text and Styled Runs. text_ and runs_ will later be passed
|
||||
// into breaker_ in InitBreaker(), which is called in Layout().
|
||||
void SetText(std::vector<uint16_t> text, StyledRuns runs);
|
||||
|
||||
// Sets up breaker_ with the contents of text_ and runs_. This is called every
|
||||
// Layout() call to allow for different widths to be used.
|
||||
void InitBreaker();
|
||||
|
||||
void SetParagraphStyle(const ParagraphStyle& style);
|
||||
|
||||
void SetFontCollection(FontCollection* font_collection);
|
||||
|
||||
// Pass the runs to breaker_.
|
||||
// NOTE: This is O(N^2) due to minikin breaking being O(N^2) where N = sum of
|
||||
// all text added using this method. This is insignificant with normal usage.
|
||||
void AddRunsToLineBreaker(
|
||||
std::unordered_map<std::string, std::shared_ptr<minikin::FontCollection>>&
|
||||
collection_map);
|
||||
|
||||
// Calculates the GlyphIDs of all whitespace characters present in the text
|
||||
// between start and end. THis is used to correctly add extra whitespace when
|
||||
// justifying.
|
||||
void FillWhitespaceSet(size_t start, size_t end, hb_font_t* hb_font);
|
||||
|
||||
// Calculates and amends the layout for one line to be justified.
|
||||
void JustifyLine(std::vector<const SkTextBlobBuilder::RunBuffer*>& buffers,
|
||||
std::vector<size_t>& buffer_sizes,
|
||||
int word_count,
|
||||
double& justify_spacing,
|
||||
double multiplier = 1);
|
||||
|
||||
// Creates and draws the decorations onto the canvas.
|
||||
void PaintDecorations(SkCanvas* canvas,
|
||||
double x,
|
||||
double y,
|
||||
size_t record_index);
|
||||
|
||||
void CalculateIntrinsicWidths();
|
||||
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(Paragraph);
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_PARAGRAPH_H_
|
||||
126
engine/src/flutter/third_party/txt/src/paragraph_builder.cc
vendored
Normal file
126
engine/src/flutter/third_party/txt/src/paragraph_builder.cc
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "lib/txt/src/paragraph_builder.h"
|
||||
#include "lib/txt/src/paragraph_style.h"
|
||||
#include "third_party/icu/source/common/unicode/unistr.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
ParagraphBuilder::ParagraphBuilder(ParagraphStyle style,
|
||||
FontCollection* font_collection)
|
||||
: font_collection_(font_collection) {
|
||||
SetParagraphStyle(style);
|
||||
}
|
||||
|
||||
ParagraphBuilder::ParagraphBuilder(ParagraphStyle style) {
|
||||
SetParagraphStyle(style);
|
||||
}
|
||||
|
||||
ParagraphBuilder::ParagraphBuilder() {}
|
||||
|
||||
void ParagraphBuilder::SetParagraphStyle(const ParagraphStyle& style) {
|
||||
paragraph_style_ = style;
|
||||
// Keep a default style to fall back to.
|
||||
TextStyle text_style;
|
||||
text_style.font_weight = paragraph_style_.font_weight;
|
||||
text_style.font_style = paragraph_style_.font_style;
|
||||
text_style.font_family = paragraph_style_.font_family;
|
||||
text_style.font_size = paragraph_style_.font_size;
|
||||
PushStyle(text_style);
|
||||
}
|
||||
|
||||
void ParagraphBuilder::SetFontCollection(FontCollection* font_collection) {
|
||||
font_collection_ = font_collection;
|
||||
}
|
||||
|
||||
ParagraphBuilder::~ParagraphBuilder() = default;
|
||||
|
||||
void ParagraphBuilder::PushStyle(const TextStyle& style) {
|
||||
const size_t text_index = text_.size();
|
||||
runs_.EndRunIfNeeded(text_index);
|
||||
const size_t style_index = runs_.AddStyle(style);
|
||||
runs_.StartRun(style_index, text_index);
|
||||
style_stack_.push_back(style_index);
|
||||
}
|
||||
|
||||
void ParagraphBuilder::Pop() {
|
||||
if (style_stack_.empty())
|
||||
return;
|
||||
const size_t text_index = text_.size();
|
||||
runs_.EndRunIfNeeded(text_index);
|
||||
style_stack_.pop_back();
|
||||
if (style_stack_.empty())
|
||||
return;
|
||||
const size_t style_index = style_stack_.back();
|
||||
runs_.StartRun(style_index, text_index);
|
||||
}
|
||||
|
||||
const TextStyle& ParagraphBuilder::PeekStyle() const {
|
||||
return runs_.PeekStyle();
|
||||
}
|
||||
|
||||
void ParagraphBuilder::AddText(const std::u16string& text) {
|
||||
text_.insert(text_.end(), text.begin(), text.end());
|
||||
}
|
||||
|
||||
void ParagraphBuilder::AddText(const std::string& text) {
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
AddText(u16_text);
|
||||
}
|
||||
|
||||
void ParagraphBuilder::AddText(const char* text) {
|
||||
auto icu_text = icu::UnicodeString::fromUTF8(text);
|
||||
std::u16string u16_text(icu_text.getBuffer(),
|
||||
icu_text.getBuffer() + icu_text.length());
|
||||
AddText(u16_text);
|
||||
}
|
||||
|
||||
void ParagraphBuilder::SplitNewlineRuns() {
|
||||
std::list<size_t> newline_positions;
|
||||
for (size_t i = 0; i < text_.size(); ++i) {
|
||||
if (text_[i] == '\n') {
|
||||
newline_positions.push_back(i);
|
||||
}
|
||||
}
|
||||
if (newline_positions.size() > 0)
|
||||
runs_.SplitNewlineRuns(newline_positions);
|
||||
}
|
||||
|
||||
std::unique_ptr<Paragraph> ParagraphBuilder::Build() {
|
||||
if (font_collection_ == nullptr) {
|
||||
// Will be deprecated when full compatibility with Flutter Engine is
|
||||
// complete.
|
||||
font_collection_ = &FontCollection::GetFontCollection("");
|
||||
}
|
||||
|
||||
runs_.EndRunIfNeeded(text_.size());
|
||||
|
||||
SplitNewlineRuns();
|
||||
|
||||
std::unique_ptr<Paragraph> paragraph = std::make_unique<Paragraph>();
|
||||
paragraph->SetText(std::move(text_), std::move(runs_));
|
||||
paragraph->SetParagraphStyle(paragraph_style_);
|
||||
paragraph->SetFontCollection(font_collection_);
|
||||
return paragraph;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
98
engine/src/flutter/third_party/txt/src/paragraph_builder.h
vendored
Normal file
98
engine/src/flutter/third_party/txt/src/paragraph_builder.h
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_PARAGRAPH_BUILDER_H_
|
||||
#define LIB_TXT_SRC_PARAGRAPH_BUILDER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "lib/ftl/macros.h"
|
||||
#include "lib/txt/src/font_collection.h"
|
||||
#include "lib/txt/src/paragraph.h"
|
||||
#include "lib/txt/src/paragraph_style.h"
|
||||
#include "lib/txt/src/styled_runs.h"
|
||||
#include "lib/txt/src/text_style.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
class ParagraphBuilder {
|
||||
public:
|
||||
explicit ParagraphBuilder(ParagraphStyle style);
|
||||
|
||||
ParagraphBuilder(ParagraphStyle style, FontCollection* font_collection);
|
||||
|
||||
ParagraphBuilder();
|
||||
|
||||
~ParagraphBuilder();
|
||||
|
||||
// Push a style to the stack. The corresponding text added with AddText will
|
||||
// use the top-most style.
|
||||
void PushStyle(const TextStyle& style);
|
||||
|
||||
// Remove a style from the stack. Useful to apply different styles to chunks
|
||||
// of text such as bolding.
|
||||
// Example:
|
||||
// builder.PushStyle(normal_style);
|
||||
// builder.AddText("Hello this is normal. ");
|
||||
//
|
||||
// builder.PushStyle(bold_style);
|
||||
// builder.AddText("And this is BOLD. ");
|
||||
//
|
||||
// builder.Pop();
|
||||
// builder.AddText(" Back to normal again.");
|
||||
void Pop();
|
||||
|
||||
// Returns the last TextStyle on the stack.
|
||||
const TextStyle& PeekStyle() const;
|
||||
|
||||
// Adds text to the builder. Forms the proper runs to use the upper-most style
|
||||
// on the style_stack_;
|
||||
void AddText(const std::u16string& text);
|
||||
|
||||
// Converts to u16string before adding.
|
||||
void AddText(const std::string& text);
|
||||
|
||||
// Converts to u16string before adding.
|
||||
void AddText(const char* text);
|
||||
|
||||
void SetParagraphStyle(const ParagraphStyle& style);
|
||||
|
||||
// It is recommended to initialize the ParagraphBuilder with a font collection
|
||||
// as default font collection fallback will be deprecated.
|
||||
void SetFontCollection(FontCollection* font_collection);
|
||||
|
||||
// Constructs a Paragraph object that can be used to layout and paint the text
|
||||
// to a SkCanvas.
|
||||
std::unique_ptr<Paragraph> Build();
|
||||
|
||||
private:
|
||||
std::vector<uint16_t> text_;
|
||||
std::vector<size_t> style_stack_;
|
||||
StyledRuns runs_;
|
||||
ParagraphStyle paragraph_style_;
|
||||
FontCollection* font_collection_ = nullptr;
|
||||
|
||||
// Break any newline '\n' characters into their own runs. This allows
|
||||
// Paragraph::Layout to cleanly discover and handle newlines.
|
||||
void SplitNewlineRuns();
|
||||
|
||||
FTL_DISALLOW_COPY_AND_ASSIGN(ParagraphBuilder);
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_PARAGRAPH_BUILDER_H_
|
||||
17
engine/src/flutter/third_party/txt/src/paragraph_style.cc
vendored
Normal file
17
engine/src/flutter/third_party/txt/src/paragraph_style.cc
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/paragraph_style.h"
|
||||
54
engine/src/flutter/third_party/txt/src/paragraph_style.h
vendored
Normal file
54
engine/src/flutter/third_party/txt/src/paragraph_style.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_PARAGRAPH_STYLE_H_
|
||||
#define LIB_TXT_SRC_PARAGRAPH_STYLE_H_
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#include "lib/txt/include/minikin/LineBreaker.h"
|
||||
#include "lib/txt/src/font_style.h"
|
||||
#include "lib/txt/src/font_weight.h"
|
||||
#include "lib/txt/src/text_align.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
class ParagraphStyle {
|
||||
public:
|
||||
FontWeight font_weight = FontWeight::w400;
|
||||
FontStyle font_style = FontStyle::normal;
|
||||
std::string font_family = "";
|
||||
double font_size = 14;
|
||||
|
||||
TextAlign text_align = TextAlign::left;
|
||||
size_t max_lines = UINT_MAX;
|
||||
double line_height = 1.0;
|
||||
std::string ellipsis = "...";
|
||||
// Default strategy is kBreakStrategy_Greedy. Sometimes,
|
||||
// kBreakStrategy_HighQuality will produce more desireable layouts (eg, very
|
||||
// long words are more likely to be reasonably placed).
|
||||
// kBreakStrategy_Balanced will balance between the two.
|
||||
minikin::BreakStrategy break_strategy =
|
||||
minikin::BreakStrategy::kBreakStrategy_Greedy;
|
||||
// TODO(garyq): Implement right to left.
|
||||
// Right to left (Arabic, Hebrew, etc).
|
||||
bool rtl = false;
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_PARAGRAPH_STYLE_H_
|
||||
110
engine/src/flutter/third_party/txt/src/styled_runs.cc
vendored
Normal file
110
engine/src/flutter/third_party/txt/src/styled_runs.cc
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/styled_runs.h"
|
||||
|
||||
#include "lib/ftl/logging.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
StyledRuns::StyledRuns() = default;
|
||||
|
||||
StyledRuns::~StyledRuns() = default;
|
||||
|
||||
StyledRuns::StyledRuns(StyledRuns&& other) {
|
||||
styles_.swap(other.styles_);
|
||||
runs_.swap(other.runs_);
|
||||
}
|
||||
|
||||
const StyledRuns& StyledRuns::operator=(StyledRuns&& other) {
|
||||
styles_.swap(other.styles_);
|
||||
runs_.swap(other.runs_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void StyledRuns::swap(StyledRuns& other) {
|
||||
styles_.swap(other.styles_);
|
||||
runs_.swap(other.runs_);
|
||||
}
|
||||
|
||||
size_t StyledRuns::AddStyle(const TextStyle& style) {
|
||||
const size_t style_index = styles_.size();
|
||||
styles_.push_back(style);
|
||||
return style_index;
|
||||
}
|
||||
|
||||
const TextStyle& StyledRuns::PeekStyle() const {
|
||||
return styles_.back();
|
||||
}
|
||||
|
||||
void StyledRuns::StartRun(size_t style_index, size_t start) {
|
||||
runs_.push_back(IndexedRun{style_index, start, start});
|
||||
}
|
||||
|
||||
void StyledRuns::EndRunIfNeeded(size_t end) {
|
||||
if (runs_.empty())
|
||||
return;
|
||||
IndexedRun& run = runs_.back();
|
||||
if (run.start == end) {
|
||||
// The run is empty. We can skip it.
|
||||
runs_.pop_back();
|
||||
} else {
|
||||
run.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
StyledRuns::Run StyledRuns::GetRun(size_t index) const {
|
||||
const IndexedRun& run = runs_[index];
|
||||
return Run{styles_[run.style_index], run.start, run.end};
|
||||
}
|
||||
|
||||
void StyledRuns::SplitNewlineRuns(std::list<size_t> newline_positions) {
|
||||
std::vector<IndexedRun> result;
|
||||
for (size_t i = 0; i < runs_.size(); ++i) {
|
||||
if (runs_[i].end <= newline_positions.front() ||
|
||||
newline_positions.empty()) {
|
||||
result.push_back(runs_[i]);
|
||||
} else {
|
||||
size_t start = runs_[i].start;
|
||||
size_t end = runs_[i].end;
|
||||
while (end > newline_positions.front() && !newline_positions.empty() &&
|
||||
start < end) {
|
||||
IndexedRun temp_run;
|
||||
temp_run.style_index = runs_[i].style_index;
|
||||
temp_run.start = start;
|
||||
temp_run.end = newline_positions.front();
|
||||
newline_positions.pop_front();
|
||||
result.push_back(temp_run);
|
||||
|
||||
temp_run.start = temp_run.end;
|
||||
temp_run.end = temp_run.end + 1;
|
||||
result.push_back(temp_run);
|
||||
|
||||
start = temp_run.end;
|
||||
}
|
||||
if (start < end) {
|
||||
IndexedRun temp_run;
|
||||
temp_run.style_index = runs_[i].style_index;
|
||||
temp_run.start = start;
|
||||
temp_run.end = end;
|
||||
result.push_back(temp_run);
|
||||
}
|
||||
}
|
||||
}
|
||||
runs_ = result;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
97
engine/src/flutter/third_party/txt/src/styled_runs.h
vendored
Normal file
97
engine/src/flutter/third_party/txt/src/styled_runs.h
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_STYLED_RUNS_H_
|
||||
#define LIB_TXT_SRC_STYLED_RUNS_H_
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/txt/src/text_style.h"
|
||||
#include "third_party/gtest/include/gtest/gtest_prod.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
// This holds and handles the start/end positions of discrete chunks of text
|
||||
// that use different styles (a 'run').
|
||||
class StyledRuns {
|
||||
public:
|
||||
struct Run {
|
||||
const TextStyle& style;
|
||||
size_t start;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
StyledRuns();
|
||||
|
||||
~StyledRuns();
|
||||
|
||||
StyledRuns(const StyledRuns& other) = delete;
|
||||
|
||||
StyledRuns(StyledRuns&& other);
|
||||
|
||||
const StyledRuns& operator=(StyledRuns&& other);
|
||||
|
||||
void swap(StyledRuns& other);
|
||||
|
||||
size_t AddStyle(const TextStyle& style);
|
||||
|
||||
// Returns the last TextStyle on the stack.
|
||||
const TextStyle& PeekStyle() const;
|
||||
|
||||
void StartRun(size_t style_index, size_t start);
|
||||
|
||||
void EndRunIfNeeded(size_t end);
|
||||
|
||||
size_t size() const { return runs_.size(); }
|
||||
|
||||
Run GetRun(size_t index) const;
|
||||
|
||||
// Break any newline '\n' characters into their own runs.
|
||||
void SplitNewlineRuns(std::list<size_t> newline_positions);
|
||||
|
||||
private:
|
||||
FRIEND_TEST(RenderTest, SimpleParagraph);
|
||||
FRIEND_TEST(RenderTest, SimpleRedParagraph);
|
||||
FRIEND_TEST(RenderTest, RainbowParagraph);
|
||||
FRIEND_TEST(RenderTest, DefaultStyleParagraph);
|
||||
FRIEND_TEST(RenderTest, BoldParagraph);
|
||||
FRIEND_TEST(RenderTest, LeftAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, RightAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, CenterAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, JustifyAlignParagraph);
|
||||
FRIEND_TEST(RenderTest, DecorationsParagraph);
|
||||
FRIEND_TEST(RenderTest, ItalicsParagraph);
|
||||
FRIEND_TEST(RenderTest, ChineseParagraph);
|
||||
FRIEND_TEST(RenderTest, DISABLED_ArabicParagraph);
|
||||
FRIEND_TEST(RenderTest, LongWordParagraph);
|
||||
FRIEND_TEST(RenderTest, KernParagraph);
|
||||
FRIEND_TEST(RenderTest, HyphenBreakParagraph);
|
||||
FRIEND_TEST(RenderTest, RepeatLayoutParagraph);
|
||||
|
||||
struct IndexedRun {
|
||||
size_t style_index = 0;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
};
|
||||
|
||||
std::vector<TextStyle> styles_;
|
||||
std::vector<IndexedRun> runs_;
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_STYLED_RUNS_H_
|
||||
31
engine/src/flutter/third_party/txt/src/text_align.h
vendored
Normal file
31
engine/src/flutter/third_party/txt/src/text_align.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_TEXT_ALIGN_H_
|
||||
#define LIB_TXT_SRC_TEXT_ALIGN_H_
|
||||
|
||||
namespace txt {
|
||||
|
||||
enum class TextAlign {
|
||||
left,
|
||||
right,
|
||||
center,
|
||||
justify,
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_TEXT_ALIGN_H_
|
||||
29
engine/src/flutter/third_party/txt/src/text_baseline.h
vendored
Normal file
29
engine/src/flutter/third_party/txt/src/text_baseline.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_TEXT_BASELINE_H_
|
||||
#define LIB_TXT_SRC_TEXT_BASELINE_H_
|
||||
|
||||
namespace txt {
|
||||
|
||||
enum TextBaseline {
|
||||
kAlphabetic,
|
||||
kIdeographic,
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_TEXT_BASELINE_H_
|
||||
22
engine/src/flutter/third_party/txt/src/text_decoration.cc
vendored
Normal file
22
engine/src/flutter/third_party/txt/src/text_decoration.cc
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/txt/src/text_decoration.h"
|
||||
|
||||
namespace txt {} // namespace txt
|
||||
35
engine/src/flutter/third_party/txt/src/text_decoration.h
vendored
Normal file
35
engine/src/flutter/third_party/txt/src/text_decoration.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_TEXT_DECORATION_H_
|
||||
#define LIB_TXT_SRC_TEXT_DECORATION_H_
|
||||
|
||||
namespace txt {
|
||||
|
||||
// Multiple decorations can be applied at once. Ex: Underline and overline is
|
||||
// (0x1 | 0x2)
|
||||
enum TextDecoration {
|
||||
kNone = 0x0,
|
||||
kUnderline = 0x1,
|
||||
kOverline = 0x2,
|
||||
kLineThrough = 0x4,
|
||||
};
|
||||
|
||||
enum TextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy };
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_TEXT_DECORATION_H_
|
||||
51
engine/src/flutter/third_party/txt/src/text_style.cc
vendored
Normal file
51
engine/src/flutter/third_party/txt/src/text_style.cc
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2017 Google, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "lib/txt/src/text_style.h"
|
||||
#include "lib/txt/src/font_style.h"
|
||||
#include "lib/txt/src/font_weight.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
bool TextStyle::equals(const TextStyle& other) const {
|
||||
if (color != other.color)
|
||||
return false;
|
||||
if (decoration != other.decoration)
|
||||
return false;
|
||||
if (decoration_color != other.decoration_color)
|
||||
return false;
|
||||
if (decoration_style != other.decoration_style)
|
||||
return false;
|
||||
if (decoration_thickness_multiplier != other.decoration_thickness_multiplier)
|
||||
return false;
|
||||
if (font_weight != other.font_weight)
|
||||
return false;
|
||||
if (font_style != other.font_style)
|
||||
return false;
|
||||
if (font_family != other.font_family)
|
||||
return false;
|
||||
if (letter_spacing != other.letter_spacing)
|
||||
return false;
|
||||
if (word_spacing != other.word_spacing)
|
||||
return false;
|
||||
if (height != other.height)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace txt
|
||||
54
engine/src/flutter/third_party/txt/src/text_style.h
vendored
Normal file
54
engine/src/flutter/third_party/txt/src/text_style.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIB_TXT_SRC_TEXT_STYLE_H_
|
||||
#define LIB_TXT_SRC_TEXT_STYLE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lib/txt/src/font_style.h"
|
||||
#include "lib/txt/src/font_weight.h"
|
||||
#include "lib/txt/src/text_baseline.h"
|
||||
#include "lib/txt/src/text_decoration.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
|
||||
namespace txt {
|
||||
|
||||
class TextStyle {
|
||||
public:
|
||||
SkColor color = SK_ColorBLACK;
|
||||
TextDecoration decoration = TextDecoration::kNone;
|
||||
// Does not make sense to draw a transparent object, so we use it as a default
|
||||
// value to indicate no decoration color was set.
|
||||
SkColor decoration_color = SK_ColorTRANSPARENT;
|
||||
TextDecorationStyle decoration_style = TextDecorationStyle::kSolid;
|
||||
// Thickness is applied as a multiplier to the default thickness of the font.
|
||||
double decoration_thickness_multiplier = 1.0;
|
||||
FontWeight font_weight = FontWeight::w400;
|
||||
FontStyle font_style = FontStyle::normal;
|
||||
TextBaseline text_baseline = TextBaseline::kAlphabetic;
|
||||
std::string font_family = "";
|
||||
double font_size = 14.0;
|
||||
double letter_spacing = 0.0;
|
||||
double word_spacing = 0.0;
|
||||
double height = 1.0;
|
||||
|
||||
bool equals(const TextStyle& other) const;
|
||||
};
|
||||
|
||||
} // namespace txt
|
||||
|
||||
#endif // LIB_TXT_SRC_TEXT_STYLE_H_
|
||||
33
engine/src/flutter/third_party/txt/tests/Android.bp
vendored
Normal file
33
engine/src/flutter/third_party/txt/tests/Android.bp
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
filegroup {
|
||||
name: "minikin-test-data",
|
||||
srcs: [
|
||||
"data/Bold.ttf",
|
||||
"data/BoldItalic.ttf",
|
||||
"data/ColorEmojiFont.ttf",
|
||||
"data/ColorTextMixedEmojiFont.ttf",
|
||||
"data/Emoji.ttf",
|
||||
"data/Italic.ttf",
|
||||
"data/Ja.ttf",
|
||||
"data/Ko.ttf",
|
||||
"data/MultiAxis.ttf",
|
||||
"data/NoCmapFormat14.ttf",
|
||||
"data/NoGlyphFont.ttf",
|
||||
"data/Regular.ttf",
|
||||
"data/TextEmojiFont.ttf",
|
||||
"data/UnicodeBMPOnly.ttf",
|
||||
"data/UnicodeBMPOnly2.ttf",
|
||||
"data/UnicodeUCS4.ttf",
|
||||
"data/VariationSelectorTest-Regular.ttf",
|
||||
"data/ZhHans.ttf",
|
||||
"data/ZhHant.ttf",
|
||||
"data/emoji.xml",
|
||||
"data/itemize.xml",
|
||||
],
|
||||
}
|
||||
|
||||
subdirs = [
|
||||
"perftests",
|
||||
"stresstest",
|
||||
"unittest",
|
||||
"util",
|
||||
]
|
||||
22
engine/src/flutter/third_party/txt/tests/BUILD.gn
vendored
Normal file
22
engine/src/flutter/third_party/txt/tests/BUILD.gn
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Copyright 2017 Google, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
group("tests") {
|
||||
testonly = true
|
||||
|
||||
deps = [
|
||||
"txt",
|
||||
"unittest",
|
||||
]
|
||||
}
|
||||
49
engine/src/flutter/third_party/txt/tests/perftests/Android.bp
vendored
Normal file
49
engine/src/flutter/third_party/txt/tests/perftests/Android.bp
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
cc_benchmark {
|
||||
name: "minikin_perftests",
|
||||
test_suites: ["device-tests"],
|
||||
cppflags: [
|
||||
"-Werror",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
],
|
||||
srcs: [
|
||||
"FontCollection.cpp",
|
||||
"FontFamily.cpp",
|
||||
"FontLanguage.cpp",
|
||||
"GraphemeBreak.cpp",
|
||||
"Hyphenator.cpp",
|
||||
"WordBreaker.cpp",
|
||||
"main.cpp",
|
||||
],
|
||||
|
||||
header_libs: ["libminikin-headers-for-tests"],
|
||||
|
||||
static_libs: [
|
||||
"libminikin-tests-util",
|
||||
"libminikin",
|
||||
"libxml2",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libharfbuzz_ng",
|
||||
"libicuuc",
|
||||
"liblog",
|
||||
"libskia",
|
||||
],
|
||||
}
|
||||
26
engine/src/flutter/third_party/txt/tests/perftests/AndroidTest.xml
vendored
Normal file
26
engine/src/flutter/third_party/txt/tests/perftests/AndroidTest.xml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<configuration description="Config for minikin_perftests">
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="minikin_perftests->/data/benchmarktest/minikin_perftests" />
|
||||
</target_preparer>
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
|
||||
<option name="native-benchmark-device-path" value="/data/benchmarktest" />
|
||||
<option name="benchmark-module-name" value="minikin_perftests" />
|
||||
</test>
|
||||
</configuration>
|
||||
101
engine/src/flutter/third_party/txt/tests/perftests/FontCollection.cpp
vendored
Normal file
101
engine/src/flutter/third_party/txt/tests/perftests/FontCollection.cpp
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <minikin/FontCollection.h>
|
||||
#include <FontTestUtils.h>
|
||||
#include <UnicodeUtils.h>
|
||||
#include <MinikinInternal.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const char* SYSTEM_FONT_PATH = "/system/fonts/";
|
||||
const char* SYSTEM_FONT_XML = "/system/etc/fonts.xml";
|
||||
|
||||
static void BM_FontCollection_construct(benchmark::State& state) {
|
||||
std::vector<std::shared_ptr<FontFamily>> families =
|
||||
getFontFamilies(SYSTEM_FONT_PATH, SYSTEM_FONT_XML);
|
||||
while (state.KeepRunning()) {
|
||||
std::make_shared<FontCollection>(families);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_FontCollection_construct);
|
||||
|
||||
static void BM_FontCollection_hasVariationSelector(benchmark::State& state) {
|
||||
std::shared_ptr<FontCollection> collection(
|
||||
getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
|
||||
|
||||
uint32_t baseCp = state.range(0);
|
||||
uint32_t vsCp = state.range(1);
|
||||
|
||||
char titleBuffer[64];
|
||||
snprintf(titleBuffer, 64, "hasVariationSelector U+%04X,U+%04X", baseCp, vsCp);
|
||||
state.SetLabel(titleBuffer);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
collection->hasVariationSelector(baseCp, vsCp);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Rewrite with BENCHMARK_CAPTURE for better test name.
|
||||
BENCHMARK(BM_FontCollection_hasVariationSelector)
|
||||
->ArgPair(0x2708, 0xFE0F)
|
||||
->ArgPair(0x2708, 0xFE0E)
|
||||
->ArgPair(0x3402, 0xE0100);
|
||||
|
||||
struct ItemizeTestCases {
|
||||
std::string itemizeText;
|
||||
std::string languageTag;
|
||||
std::string labelText;
|
||||
} ITEMIZE_TEST_CASES[] = {
|
||||
{ "'A' 'n' 'd' 'r' 'o' 'i' 'd'", "en", "English" },
|
||||
{ "U+4E16", "zh-Hans", "CJK Ideograph" },
|
||||
{ "U+4E16", "zh-Hans,zh-Hant,ja,en,es,pt,fr,de", "CJK Ideograph with many language fallback" },
|
||||
{ "U+3402 U+E0100", "ja", "CJK Ideograph with variation selector" },
|
||||
{ "'A' 'n' U+0E1A U+0E31 U+0645 U+062D U+0648", "en", "Mixture of English, Thai and Arabic" },
|
||||
{ "U+2708 U+FE0E", "en", "Emoji with variation selector" },
|
||||
{ "U+0031 U+FE0F U+20E3", "en", "KEYCAP" },
|
||||
};
|
||||
|
||||
static void BM_FontCollection_itemize(benchmark::State& state) {
|
||||
std::shared_ptr<FontCollection> collection(
|
||||
getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML));
|
||||
|
||||
size_t testIndex = state.range(0);
|
||||
state.SetLabel("Itemize: " + ITEMIZE_TEST_CASES[testIndex].labelText);
|
||||
|
||||
uint16_t buffer[64];
|
||||
size_t utf16_length = 0;
|
||||
ParseUnicode(
|
||||
buffer, 64, ITEMIZE_TEST_CASES[testIndex].itemizeText.c_str(), &utf16_length, nullptr);
|
||||
std::vector<FontCollection::Run> result;
|
||||
FontStyle style(FontStyle::registerLanguageList(ITEMIZE_TEST_CASES[testIndex].languageTag));
|
||||
|
||||
std::lock_guard<std::mutex> _l(gMinikinLock);
|
||||
while (state.KeepRunning()) {
|
||||
result.clear();
|
||||
collection->itemize(buffer, utf16_length, style, &result);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Rewrite with BENCHMARK_CAPTURE once it is available in Android.
|
||||
BENCHMARK(BM_FontCollection_itemize)
|
||||
->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(5)->Arg(6);
|
||||
|
||||
} // namespace minikin
|
||||
35
engine/src/flutter/third_party/txt/tests/perftests/FontFamily.cpp
vendored
Normal file
35
engine/src/flutter/third_party/txt/tests/perftests/FontFamily.cpp
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <minikin/FontFamily.h>
|
||||
#include "../util/MinikinFontForTest.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
static void BM_FontFamily_create(benchmark::State& state) {
|
||||
std::shared_ptr<MinikinFontForTest> minikinFont =
|
||||
std::make_shared<MinikinFontForTest>("/system/fonts/NotoSansCJK-Regular.ttc", 0);
|
||||
|
||||
while (state.KeepRunning()) {
|
||||
std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(
|
||||
std::vector<Font>({Font(minikinFont, FontStyle())}));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK(BM_FontFamily_create);
|
||||
|
||||
} // namespace minikin
|
||||
43
engine/src/flutter/third_party/txt/tests/perftests/FontLanguage.cpp
vendored
Normal file
43
engine/src/flutter/third_party/txt/tests/perftests/FontLanguage.cpp
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "FontLanguage.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
static void BM_FontLanguage_en_US(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
FontLanguage language("en-US", 5);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontLanguage_en_US);
|
||||
|
||||
static void BM_FontLanguage_en_Latn_US(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
FontLanguage language("en-Latn-US", 10);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontLanguage_en_Latn_US);
|
||||
|
||||
static void BM_FontLanguage_en_Latn_US_u_em_emoji(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
FontLanguage language("en-Latn-US-u-em-emoji", 21);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FontLanguage_en_Latn_US_u_em_emoji);
|
||||
|
||||
} // namespace minikin
|
||||
79
engine/src/flutter/third_party/txt/tests/perftests/GraphemeBreak.cpp
vendored
Normal file
79
engine/src/flutter/third_party/txt/tests/perftests/GraphemeBreak.cpp
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include "minikin/GraphemeBreak.h"
|
||||
#include "UnicodeUtils.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const char* ASCII_TEST_STR = "'L' 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' '.'";
|
||||
// U+261D: WHITE UP POINTING INDEX
|
||||
// U+1F3FD: EMOJI MODIFIER FITZPATRICK TYPE-4
|
||||
const char* EMOJI_TEST_STR = "U+261D U+1F3FD U+261D U+1F3FD U+261D U+1F3FD U+261D U+1F3FD";
|
||||
// U+1F1FA: REGIONAL INDICATOR SYMBOL LETTER U
|
||||
// U+1F1F8: REGIONAL INDICATOR SYMBOL LETTER S
|
||||
const char* FLAGS_TEST_STR = "U+1F1FA U+1F1F8 U+1F1FA U+1F1F8 U+1F1FA U+1F1F8";
|
||||
|
||||
// TODO: Migrate BENCHMARK_CAPTURE for parameterizing.
|
||||
static void BM_GraphemeBreak_Ascii(benchmark::State& state) {
|
||||
size_t result_size;
|
||||
uint16_t buffer[12];
|
||||
ParseUnicode(buffer, 12, ASCII_TEST_STR, &result_size, nullptr);
|
||||
LOG_ALWAYS_FATAL_IF(result_size != 12);
|
||||
const size_t testIndex = state.range(0);
|
||||
while (state.KeepRunning()) {
|
||||
GraphemeBreak::isGraphemeBreak(nullptr, buffer, 0, result_size, testIndex);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_GraphemeBreak_Ascii)
|
||||
->Arg(0) // Begining of the text.
|
||||
->Arg(1) // Middle of the text.
|
||||
->Arg(12); // End of the text.
|
||||
|
||||
static void BM_GraphemeBreak_Emoji(benchmark::State& state) {
|
||||
size_t result_size;
|
||||
uint16_t buffer[12];
|
||||
ParseUnicode(buffer, 12, EMOJI_TEST_STR, &result_size, nullptr);
|
||||
LOG_ALWAYS_FATAL_IF(result_size != 12);
|
||||
const size_t testIndex = state.range(0);
|
||||
while (state.KeepRunning()) {
|
||||
GraphemeBreak::isGraphemeBreak(nullptr, buffer, 0, result_size, testIndex);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_GraphemeBreak_Emoji)
|
||||
->Arg(1) // Middle of emoji modifier sequence.
|
||||
->Arg(2) // Middle of the surrogate pairs.
|
||||
->Arg(3); // After emoji modifier sequence. Here is boundary of grapheme cluster.
|
||||
|
||||
static void BM_GraphemeBreak_Emoji_Flags(benchmark::State& state) {
|
||||
size_t result_size;
|
||||
uint16_t buffer[12];
|
||||
ParseUnicode(buffer, 12, FLAGS_TEST_STR, &result_size, nullptr);
|
||||
LOG_ALWAYS_FATAL_IF(result_size != 12);
|
||||
const size_t testIndex = state.range(0);
|
||||
while (state.KeepRunning()) {
|
||||
GraphemeBreak::isGraphemeBreak(nullptr, buffer, 0, result_size, testIndex);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_GraphemeBreak_Emoji_Flags)
|
||||
->Arg(2) // Middle of flag sequence.
|
||||
->Arg(4) // After flag sequence. Here is boundary of grapheme cluster.
|
||||
->Arg(10); // Middle of 3rd flag sequence.
|
||||
|
||||
} // namespace minikin
|
||||
60
engine/src/flutter/third_party/txt/tests/perftests/Hyphenator.cpp
vendored
Normal file
60
engine/src/flutter/third_party/txt/tests/perftests/Hyphenator.cpp
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <minikin/Hyphenator.h>
|
||||
#include <FileUtils.h>
|
||||
#include <UnicodeUtils.h>
|
||||
|
||||
namespace minikin {
|
||||
|
||||
const char* enUsHyph = "/system/usr/hyphen-data/hyph-en-us.hyb";
|
||||
const int enUsMinPrefix = 2;
|
||||
const int enUsMinSuffix = 3;
|
||||
const icu::Locale& usLocale = icu::Locale::getUS();
|
||||
|
||||
static void BM_Hyphenator_short_word(benchmark::State& state) {
|
||||
Hyphenator* hyphenator = Hyphenator::loadBinary(
|
||||
readWholeFile(enUsHyph).data(), enUsMinPrefix, enUsMinSuffix);
|
||||
std::vector<uint16_t> word = utf8ToUtf16("hyphen");
|
||||
std::vector<HyphenationType> result;
|
||||
while (state.KeepRunning()) {
|
||||
hyphenator->hyphenate(&result, word.data(), word.size(), usLocale);
|
||||
}
|
||||
Hyphenator::loadBinary(nullptr, 2, 2);
|
||||
}
|
||||
|
||||
// TODO: Use BENCHMARK_CAPTURE for parametrise.
|
||||
BENCHMARK(BM_Hyphenator_short_word);
|
||||
|
||||
static void BM_Hyphenator_long_word(benchmark::State& state) {
|
||||
Hyphenator* hyphenator = Hyphenator::loadBinary(
|
||||
readWholeFile(enUsHyph).data(), enUsMinPrefix, enUsMinSuffix);
|
||||
std::vector<uint16_t> word = utf8ToUtf16(
|
||||
"Pneumonoultramicroscopicsilicovolcanoconiosis");
|
||||
std::vector<HyphenationType> result;
|
||||
while (state.KeepRunning()) {
|
||||
hyphenator->hyphenate(&result, word.data(), word.size(), usLocale);
|
||||
}
|
||||
Hyphenator::loadBinary(nullptr, 2, 2);
|
||||
}
|
||||
|
||||
// TODO: Use BENCHMARK_CAPTURE for parametrise.
|
||||
BENCHMARK(BM_Hyphenator_long_word);
|
||||
|
||||
// TODO: Add more tests for other languages.
|
||||
|
||||
} // namespace minikin
|
||||
39
engine/src/flutter/third_party/txt/tests/perftests/WordBreaker.cpp
vendored
Normal file
39
engine/src/flutter/third_party/txt/tests/perftests/WordBreaker.cpp
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "minikin/WordBreaker.h"
|
||||
#include "UnicodeUtils.h"
|
||||
|
||||
namespace minikin {
|
||||
|
||||
static void BM_WordBreaker_English(benchmark::State& state) {
|
||||
const char* kLoremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
|
||||
"eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||
|
||||
WordBreaker wb;
|
||||
wb.setLocale(icu::Locale::getEnglish());
|
||||
std::vector<uint16_t> text = utf8ToUtf16(kLoremIpsum);
|
||||
while (state.KeepRunning()) {
|
||||
wb.setText(text.data(), text.size());
|
||||
while (wb.next() != -1) {}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_WordBreaker_English);
|
||||
|
||||
// TODO: Add more tests for other languages.
|
||||
|
||||
} // namespace minikin
|
||||
3
engine/src/flutter/third_party/txt/tests/perftests/how_to_run.txt
vendored
Normal file
3
engine/src/flutter/third_party/txt/tests/perftests/how_to_run.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
mmm -j8 frameworks/minikin/tests/perftests &&
|
||||
adb sync data &&
|
||||
adb shell /data/benchmarktest/minikin_perftests/minikin_perftests
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user