summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorSeigo Nonaka <nona@google.com>2017-11-14 18:58:00 -0800
committerSeigo Nonaka <nona@google.com>2017-11-20 14:00:34 -0800
commitad63f0eb8e968bab5bf999b916f718a4437ca65b (patch)
treeccb97597ca10b395d0a10aee82baeb5196bbe9d0 /include
parentd35cb3d85ebe4cc2e45d924c905da062322ce958 (diff)
downloadminikin-ad63f0eb8e968bab5bf999b916f718a4437ca65b.tar.gz
Decouple the text measurement from the LineBreaker
This lays the groundwork for precomputed layout. By extracting measureText call from LineBreaker to JNI, JNI will be able to fill the precomputed layout result if possible. Locally verified this doesn't regresses the performance. Here is a raw performance test result on marlin. StaticLayoutPerfTest (median): createRandom: 6,714,256 -> 6,644,276 (-1.04%) createRandom Balanced: 6,675,482 -> 6,562,508 (-1.69%) TextViewOnMeasurePerfTest (median): measure_AtMost: 59,225,636 -> 60,388,310 (+1.96%) measure_Exactly: 59,145,734 -> 58,801,274 (-0.58%) measure_Unspecified: 99,063,403 -> 99,739,018 (+0.68%) Bug: 65024629 Test: bit CtsTextTestCases:* Test: bit CtsWidgetTestCases:* Test: bit CtsGraphicsTestCases:* Test: minikin_tests Change-Id: Ic26c2a8d526c05f3a38ee6a200221543fbb33713
Diffstat (limited to 'include')
-rw-r--r--include/minikin/AndroidLineBreakerHelper.h197
-rw-r--r--include/minikin/LineBreaker.h56
2 files changed, 237 insertions, 16 deletions
diff --git a/include/minikin/AndroidLineBreakerHelper.h b/include/minikin/AndroidLineBreakerHelper.h
new file mode 100644
index 0000000..f722684
--- /dev/null
+++ b/include/minikin/AndroidLineBreakerHelper.h
@@ -0,0 +1,197 @@
+/*
+ * 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_ANDROID_LINE_BREAKER_HELPERS_H
+#define MINIKIN_ANDROID_LINE_BREAKER_HELPERS_H
+
+#include "minikin/LineBreaker.h"
+
+namespace minikin {
+namespace android {
+
+class LineWidth : public LineBreaker::LineWidthDelegate {
+ public:
+ LineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
+ const std::vector<float>& indents, const std::vector<float>& leftPaddings,
+ const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset)
+ : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
+ mIndents(indents), mLeftPaddings(leftPaddings),
+ mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {}
+
+ float getLineWidth(size_t lineNo) override {
+ const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
+ ? mFirstWidth : mRestWidth;
+ return width - get(mIndents, lineNo);
+ }
+
+ float getMinLineWidth() override {
+ // A simpler algorithm would have been simply looping until the larger of
+ // mFirstLineCount and mIndents.size()-mOffset, but that does unnecessary calculations
+ // when mFirstLineCount is large. Instead, we measure the first line, all the lines that
+ // have an indent, and the first line after firstWidth ends and restWidth starts.
+ float minWidth = std::min(getLineWidth(0), getLineWidth(mFirstLineCount));
+ for (size_t lineNo = 1; lineNo + mOffset < mIndents.size(); lineNo++) {
+ minWidth = std::min(minWidth, getLineWidth(lineNo));
+ }
+ return minWidth;
+ }
+
+ float getLeftPadding(size_t lineNo) override {
+ return get(mLeftPaddings, lineNo);
+ }
+
+ float getRightPadding(size_t lineNo) override {
+ return get(mRightPaddings, lineNo);
+ }
+
+ private:
+ float get(const std::vector<float>& vec, size_t lineNo) {
+ if (vec.empty()) {
+ return 0;
+ }
+ const size_t index = lineNo + mOffset;
+ if (index < vec.size()) {
+ return vec[index];
+ } else {
+ return vec.back();
+ }
+ }
+
+ const float mFirstWidth;
+ const int32_t mFirstLineCount;
+ const float mRestWidth;
+ const std::vector<float>& mIndents;
+ const std::vector<float>& mLeftPaddings;
+ const std::vector<float>& mRightPaddings;
+ const int32_t mOffset;
+};
+
+class StyleRun : public Run {
+ public:
+ StyleRun(const Range& range, MinikinPaint&& paint,
+ std::shared_ptr<FontCollection>&& collection, bool isRtl)
+ : Run(range), mPaint(std::move(paint)),
+ mCollection(std::move(collection)), mIsRtl(isRtl) {}
+
+ bool canHyphenate() const override { return true; }
+ uint32_t getLocaleListId() const override { return mPaint.localeListId; }
+ bool isRtl() const override { return mIsRtl; }
+
+ void getMetrics(const U16StringPiece& text, float* advances,
+ MinikinExtent* extents,
+ LayoutOverhang* overhangs) const override {
+ Bidi bidiFlag = mIsRtl ? Bidi::FORCE_RTL : Bidi::FORCE_LTR;
+ Layout::measureText(text, mRange, bidiFlag, mPaint, mCollection, advances, extents,
+ overhangs);
+ }
+
+ const MinikinPaint* getPaint() const override {
+ return &mPaint;
+ }
+
+ float measureHyphenPiece(const U16StringPiece& text, const Range& range,
+ StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
+ float* advances, LayoutOverhang* overhangs) const override {
+ Bidi bidiFlag = mIsRtl ? Bidi::FORCE_RTL : Bidi::FORCE_LTR;
+ return Layout::measureText(
+ text, range, bidiFlag, mPaint, startHyphen, endHyphen, mCollection,
+ advances, nullptr /* extent */, overhangs);
+ }
+
+ private:
+ MinikinPaint mPaint;
+ std::shared_ptr<FontCollection> mCollection;
+ const bool mIsRtl;
+};
+
+class Replacement : public Run {
+ public:
+ Replacement(const Range& range, float width, uint32_t localeListId)
+ : Run(range), mWidth(width), mLocaleListId(localeListId) {}
+
+ bool isRtl() const { return false; }
+ bool canHyphenate() const { return false; }
+ uint32_t getLocaleListId() const { return mLocaleListId; }
+
+ void getMetrics(const U16StringPiece& /* unused */, float* advances,
+ MinikinExtent* /* unused */, LayoutOverhang* /* unused */) const override {
+ advances[mRange.getStart()] = mWidth;
+ // TODO: Get the extents information from the caller.
+ }
+
+ private:
+ const float mWidth;
+ const uint32_t mLocaleListId;
+};
+
+class StaticLayoutNative {
+ public:
+ StaticLayoutNative(
+ BreakStrategy strategy, HyphenationFrequency frequency,
+ bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings,
+ std::vector<float>&& rightPaddings)
+ : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified),
+ mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
+ mRightPaddings(std::move(rightPaddings)) {}
+
+ void addStyleRun(int32_t start, int32_t end, MinikinPaint&& paint,
+ std::shared_ptr<FontCollection> collection, bool isRtl) {
+ mRuns.emplace_back(std::make_unique<StyleRun>(
+ Range(start, end), std::move(paint), std::move(collection), isRtl));
+ }
+
+ void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
+ mRuns.emplace_back(
+ std::make_unique<Replacement>(Range(start, end), width, localeListId));
+ }
+
+ // Only valid while this instance is alive.
+ inline std::unique_ptr<LineBreaker::LineWidthDelegate> buildLineWidthDelegate(
+ float firstWidth, int32_t firstLineCount, float restWidth,
+ int32_t indentsAndPaddingsOffset) {
+ return std::make_unique<LineWidth>(firstWidth, firstLineCount, restWidth, mIndents,
+ mLeftPaddings, mRightPaddings, indentsAndPaddingsOffset);
+ }
+
+ void addRuns(LineBreaker* lineBreaker) {
+ for (const auto& run : mRuns) {
+ lineBreaker->addRun(*run);
+ }
+ }
+
+ void clearRuns() {
+ mRuns.clear();
+ }
+
+ inline BreakStrategy getStrategy() const { return mStrategy; }
+ inline HyphenationFrequency getFrequency() const { return mFrequency; }
+ inline bool isJustified() const { return mIsJustified; }
+
+ private:
+ const BreakStrategy mStrategy;
+ const HyphenationFrequency mFrequency;
+ const bool mIsJustified;
+ const std::vector<float> mIndents;
+ const std::vector<float> mLeftPaddings;
+ const std::vector<float> mRightPaddings;
+
+ std::vector<std::unique_ptr<Run>> mRuns;
+};
+
+} // namespace android
+} // namespace minikin
+
+#endif // MINIKIN_ANDROID_LINE_BREAKER_HELPERS_H
diff --git a/include/minikin/LineBreaker.h b/include/minikin/LineBreaker.h
index bc35188..f9f4c7f 100644
--- a/include/minikin/LineBreaker.h
+++ b/include/minikin/LineBreaker.h
@@ -48,6 +48,43 @@ enum HyphenationFrequency {
class Hyphenator;
class WordBreaker;
+class Run {
+ public:
+ Run(const Range& range) : mRange(range) {}
+ virtual ~Run() {}
+
+ // Returns true if this run is RTL. Otherwise returns false.
+ virtual bool isRtl() const = 0;
+
+ // Returns true if this run is a target of hyphenation. Otherwise return false.
+ virtual bool canHyphenate() const = 0;
+
+ // Returns the locale list ID for this run.
+ virtual uint32_t getLocaleListId() const = 0;
+
+ // Fills the each character's advances, extents and overhangs.
+ virtual void getMetrics(const U16StringPiece& text, float* advances, MinikinExtent* extents,
+ LayoutOverhang* overhangs) const = 0;
+
+ // Following two methods are only called when the implementation returns true for
+ // canHyphenate method.
+
+ // Returns the paint pointer used for this run. Do not return null if you returns true for
+ // canHyphenate method.
+ virtual const MinikinPaint* getPaint() const { return nullptr; }
+
+ // Measure the hyphenation piece and fills the each character's advances and overhangs.
+ virtual float measureHyphenPiece(const U16StringPiece&, const Range&, StartHyphenEdit,
+ EndHyphenEdit, float*, LayoutOverhang*) const {
+ return 0.0;
+ }
+
+ inline const Range& getRange() const { return mRange; }
+
+ protected:
+ const Range mRange;
+};
+
class TabStops {
public:
void set(const int* stops, size_t nStops, int tabWidth) {
@@ -135,13 +172,7 @@ class LineBreaker {
mHyphenationFrequency = frequency;
}
- inline void addStyleRun(MinikinPaint* paint,
- const std::shared_ptr<FontCollection>& typeface,
- const Range& range, bool isRtl) {
- addStyleRunInternal(paint, typeface, range, isRtl, paint->localeListId);
- }
-
- void addReplacement(const Range& range, float width, uint32_t localeListId);
+ void addRun(const Run& run);
size_t computeBreaks();
@@ -203,21 +234,14 @@ class LineBreaker {
uint32_t mCurrentLocaleListId;
uint64_t mCurrentLocaleId = 0;
- void addStyleRunInternal(const MinikinPaint* paint,
- const std::shared_ptr<FontCollection>& typeface,
- const Range& range,
- bool isRtl,
- uint32_t localeListId);
-
// Hyphenates a string potentially containing non-breaking spaces.
std::vector<HyphenationType> hyphenate(const U16StringPiece& string);
- void addHyphenationCandidates(const MinikinPaint& paint,
- const std::shared_ptr<FontCollection>& typeface,
+ void addHyphenationCandidates(const Run& run,
const Range& contextRange,
const Range& wordRange, ParaWidth lastBreakWidth,
ParaWidth PostBreak, size_t postSpaceCount,
- float hyphenPenalty, Bidi bidiFlags);
+ float hyphenPenalty);
void addWordBreak(size_t offset, ParaWidth preBreak, ParaWidth postBreak,
float firstOverhang, float secondOverhang,