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_PIECES_H
18 #define MINIKIN_LAYOUT_PIECES_H
19 
20 #include <unordered_map>
21 
22 #include "minikin/LayoutCache.h"
23 #include "minikin/LayoutCore.h"
24 #include "minikin/MinikinPaint.h"
25 
26 namespace minikin {
27 
28 struct LayoutPieces {
29     const static uint32_t kNoPaintId = static_cast<uint32_t>(-1);
30 
31     struct Key {
KeyLayoutPieces::Key32         Key(const Range& range, HyphenEdit hyphenEdit, bool dir, uint32_t paintId)
33                 : range(range),
34                   hyphenEdit(hyphenEdit),
35                   dir(dir),
36                   paintId(paintId),
37                   hash(calcHash()) {}
38 
39         Range range;
40         HyphenEdit hyphenEdit;
41         bool dir;
42         uint32_t paintId;
43         uint32_t hash;
44 
calcHashLayoutPieces::Key45         uint32_t calcHash() const {
46             return Hasher()
47                     .update(range.getStart())
48                     .update(range.getEnd())
49                     .update(hyphenEdit)
50                     .update(dir)
51                     .update(paintId)
52                     .hash();
53         }
54 
55         bool operator==(const Key& o) const {
56             return range == o.range && hyphenEdit == o.hyphenEdit && dir == o.dir &&
57                    paintId == o.paintId;
58         }
59 
getMemoryUsageLayoutPieces::Key60         uint32_t getMemoryUsage() const {
61             return sizeof(Range) + sizeof(HyphenEdit) + sizeof(bool) + sizeof(uint32_t);
62         }
63     };
64 
65     struct KeyHasher {
operatorLayoutPieces::KeyHasher66         std::size_t operator()(const Key& key) const { return key.hash; }
67     };
68 
69     struct PaintHasher {
operatorLayoutPieces::PaintHasher70         std::size_t operator()(const MinikinPaint& paint) const { return paint.hash(); }
71     };
72 
LayoutPiecesLayoutPieces73     LayoutPieces() : nextPaintId(0) {}
~LayoutPiecesLayoutPieces74     ~LayoutPieces() {}
75 
76     uint32_t nextPaintId;
77     std::unordered_map<MinikinPaint, uint32_t, PaintHasher> paintMap;
78     std::unordered_map<Key, LayoutSlot, KeyHasher> offsetMap;
79 
insertLayoutPieces80     void insert(const Range& range, HyphenEdit edit, const LayoutPiece& layout, bool dir,
81                 const MinikinPaint& paint, const MinikinRect& rect) {
82         uint32_t paintId = findPaintId(paint);
83         if (paintId == kNoPaintId) {
84             paintId = nextPaintId++;
85             paintMap.insert(std::make_pair(paint, paintId));
86         }
87         offsetMap.emplace(std::piecewise_construct,
88                           std::forward_as_tuple(range, edit, dir, paintId),
89                           std::forward_as_tuple(layout, rect));
90     }
91 
92     template <typename F>
getOrCreateLayoutPieces93     void getOrCreate(const U16StringPiece& textBuf, const Range& range, const Range& context,
94                      const MinikinPaint& paint, bool dir, StartHyphenEdit startEdit,
95                      EndHyphenEdit endEdit, uint32_t paintId, bool boundsCalculation, F& f) const {
96         const HyphenEdit edit = packHyphenEdit(startEdit, endEdit);
97         auto it = offsetMap.find(Key(range, edit, dir, paintId));
98         if (it != offsetMap.end()) {
99             const LayoutPiece& piece = it->second.mLayout;
100             const MinikinRect& bounds = it->second.mBounds;
101             if (boundsCalculation && !bounds.isValid()) {
102                 f(piece, paint, LayoutPiece::calculateBounds(piece, paint));
103             } else {
104                 f(piece, paint, bounds);
105             }
106             return;
107         }
108 
109         LayoutCache::getInstance().getOrCreate(textBuf.substr(context), range - context.getStart(),
110                                                paint, dir, startEdit, endEdit, boundsCalculation,
111                                                f);
112     }
113 
findPaintIdLayoutPieces114     uint32_t findPaintId(const MinikinPaint& paint) const {
115         auto paintIt = paintMap.find(paint);
116         return paintIt == paintMap.end() ? kNoPaintId : paintIt->second;
117     }
118 
getMemoryUsageLayoutPieces119     uint32_t getMemoryUsage() const {
120         uint32_t result = 0;
121         for (const auto& i : offsetMap) {
122             result += i.first.getMemoryUsage() + i.second.mLayout.getMemoryUsage();
123         }
124         result += (sizeof(MinikinPaint) + sizeof(uint32_t)) * paintMap.size();
125         return result;
126     }
127 };
128 
129 }  // namespace minikin
130 
131 #endif  // MINIKIN_LAYOUT_PIECES_H
132