1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef MINIKIN_LAYOUT_SPLITTER_H
18 #define MINIKIN_LAYOUT_SPLITTER_H
19 
20 #define LOG_TAG "Minikin"
21 
22 #include "minikin/Layout.h"
23 
24 #include <memory>
25 
26 #include <unicode/ubidi.h>
27 
28 #include "minikin/Macros.h"
29 #include "minikin/U16StringPiece.h"
30 
31 #include "LayoutUtils.h"
32 
33 namespace minikin {
34 
35 // LayoutSplitter split the input text into recycle-able pieces.
36 //
37 // LayoutSplitter basically splits the text before and after space characters.
38 //
39 // Here is an example of how the LayoutSplitter split the text into layout pieces.
40 // Input:
41 //   Text          : T h i s _ i s _ a n _ e x a m p l e _ t e x t .
42 //   Range         :            |-------------------|
43 //
44 // Output:
45 //   Context Range :          |---|-|---|-|-------------|
46 //   Piece Range   :            |-|-|---|-|---------|
47 //
48 // Input:
49 //   Text          : T h i s _ i s _ a n _ e x a m p l e _ t e x t .
50 //   Range         :                          |-------|
51 //
52 // Output:
53 //   Context Range :                      |-------------|
54 //   Piece Range   :                          |-------|
55 class LayoutSplitter {
56 public:
LayoutSplitter(const U16StringPiece & textBuf,const Range & range,bool isRtl)57     LayoutSplitter(const U16StringPiece& textBuf, const Range& range, bool isRtl)
58             : mTextBuf(textBuf), mRange(range), mIsRtl(isRtl) {}
59 
60     class iterator {
61     public:
62         bool operator==(const iterator& o) const { return mPos == o.mPos && mParent == o.mParent; }
63 
64         bool operator!=(const iterator& o) const { return !(*this == o); }
65 
66         std::pair<Range, Range> operator*() const {
67             return std::make_pair(mContextRange, mPieceRange);
68         }
69 
70         iterator& operator++() {
71             const U16StringPiece& textBuf = mParent->mTextBuf;
72             const Range& range = mParent->mRange;
73             if (mParent->mIsRtl) {
74                 mPos = mPieceRange.getStart();
75                 mContextRange.setStart(getPrevWordBreakForCache(textBuf, mPos));
76                 mContextRange.setEnd(mPos);
77                 mPieceRange.setStart(std::max(mContextRange.getStart(), range.getStart()));
78                 mPieceRange.setEnd(mPos);
79             } else {
80                 mPos = mPieceRange.getEnd();
81                 mContextRange.setStart(mPos);
82                 mContextRange.setEnd(getNextWordBreakForCache(textBuf, mPos));
83                 mPieceRange.setStart(mPos);
84                 mPieceRange.setEnd(std::min(mContextRange.getEnd(), range.getEnd()));
85             }
86             return *this;
87         }
88 
89     private:
90         friend class LayoutSplitter;
91 
iterator(const LayoutSplitter * parent,uint32_t pos)92         iterator(const LayoutSplitter* parent, uint32_t pos) : mParent(parent), mPos(pos) {
93             const U16StringPiece& textBuf = mParent->mTextBuf;
94             const Range& range = mParent->mRange;
95             if (parent->mIsRtl) {
96                 mContextRange.setStart(getPrevWordBreakForCache(textBuf, pos));
97                 mContextRange.setEnd(getNextWordBreakForCache(textBuf, pos == 0 ? 0 : pos - 1));
98                 mPieceRange.setStart(std::max(mContextRange.getStart(), range.getStart()));
99                 mPieceRange.setEnd(pos);
100             } else {
101                 mContextRange.setStart(
102                         getPrevWordBreakForCache(textBuf, pos == range.getEnd() ? pos : pos + 1));
103                 mContextRange.setEnd(getNextWordBreakForCache(textBuf, pos));
104                 mPieceRange.setStart(pos);
105                 mPieceRange.setEnd(std::min(mContextRange.getEnd(), range.getEnd()));
106             }
107         }
108 
109         const LayoutSplitter* mParent;
110         uint32_t mPos;
111         Range mContextRange;
112         Range mPieceRange;
113     };
114 
begin()115     iterator begin() const { return iterator(this, mIsRtl ? mRange.getEnd() : mRange.getStart()); }
end()116     iterator end() const { return iterator(this, mIsRtl ? mRange.getStart() : mRange.getEnd()); }
117 
118 private:
119     U16StringPiece mTextBuf;
120     Range mRange;  // The range in the original buffer. Used for range check.
121     bool mIsRtl;   // The paragraph direction.
122 
123     MINIKIN_PREVENT_COPY_AND_ASSIGN(LayoutSplitter);
124 };
125 
126 }  // namespace minikin
127 
128 #endif  // MINIKIN_LAYOUT_SPLITTER_H
129