• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "minikin/Layout.h"
18 
19 #include <gtest/gtest.h>
20 
21 #include "minikin/LayoutCache.h"
22 
23 #include "FontTestUtils.h"
24 #include "LocaleListCache.h"
25 #include "UnicodeUtils.h"
26 
27 namespace minikin {
28 
29 class TestableLayoutCache : public LayoutCache {
30 public:
TestableLayoutCache(uint32_t maxEntries)31     TestableLayoutCache(uint32_t maxEntries) : LayoutCache(maxEntries) {}
32 };
33 
34 class LayoutCapture {
35 public:
LayoutCapture()36     LayoutCapture() {}
37 
operator ()(const Layout & layout)38     void operator()(const Layout& layout) { mLayout = &layout; }
39 
get() const40     const Layout* get() const { return mLayout; }
41 
42 private:
43     const Layout* mLayout;
44 };
45 
TEST(LayoutCacheTest,cacheHitTest)46 TEST(LayoutCacheTest, cacheHitTest) {
47     auto text = utf8ToUtf16("android");
48     Range range(0, text.size());
49     MinikinPaint paint(buildFontCollection("Ascii.ttf"));
50 
51     TestableLayoutCache layoutCache(10);
52 
53     LayoutCapture layout1;
54     layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
55                             EndHyphenEdit::NO_EDIT, layout1);
56 
57     LayoutCapture layout2;
58     layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
59                             EndHyphenEdit::NO_EDIT, layout2);
60 
61     EXPECT_EQ(layout1.get(), layout2.get());
62 }
63 
TEST(LayoutCacheTest,cacheMissTest)64 TEST(LayoutCacheTest, cacheMissTest) {
65     auto text1 = utf8ToUtf16("android");
66     auto text2 = utf8ToUtf16("ANDROID");
67     MinikinPaint paint(buildFontCollection("Ascii.ttf"));
68 
69     TestableLayoutCache layoutCache(10);
70 
71     LayoutCapture layout1;
72     LayoutCapture layout2;
73 
74     {
75         SCOPED_TRACE("Different text");
76         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
77                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
78         layoutCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */,
79                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
80         EXPECT_NE(layout1.get(), layout2.get());
81     }
82     {
83         SCOPED_TRACE("Different range");
84         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
85                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
86         layoutCache.getOrCreate(text1, Range(1, text1.size()), paint, false /* LTR */,
87                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
88         EXPECT_NE(layout1.get(), layout2.get());
89     }
90     {
91         SCOPED_TRACE("Different text");
92         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
93                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
94         layoutCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */,
95                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
96         EXPECT_NE(layout1.get(), layout2.get());
97     }
98     {
99         SCOPED_TRACE("Different direction");
100         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
101                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
102         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, true /* RTL */,
103                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
104         EXPECT_NE(layout1.get(), layout2.get());
105     }
106     {
107         SCOPED_TRACE("Different start hyphenation");
108         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
109                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
110         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
111                                 StartHyphenEdit::INSERT_HYPHEN, EndHyphenEdit::NO_EDIT, layout2);
112         EXPECT_NE(layout1.get(), layout2.get());
113     }
114     {
115         SCOPED_TRACE("Different end hyphen");
116         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
117                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
118         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
119                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::INSERT_HYPHEN, layout2);
120         EXPECT_NE(layout1.get(), layout2.get());
121     }
122     {
123         SCOPED_TRACE("Different collection");
124         MinikinPaint paint1(buildFontCollection("Ascii.ttf"));
125         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
126                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
127         MinikinPaint paint2(buildFontCollection("Emoji.ttf"));
128         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
129                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
130         EXPECT_NE(layout1.get(), layout2.get());
131     }
132     {
133         SCOPED_TRACE("Different size");
134         auto collection = buildFontCollection("Ascii.ttf");
135         MinikinPaint paint1(collection);
136         paint1.size = 10.0f;
137         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
138                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
139         MinikinPaint paint2(collection);
140         paint2.size = 20.0f;
141         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
142                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
143         EXPECT_NE(layout1.get(), layout2.get());
144     }
145     {
146         SCOPED_TRACE("Different scale X");
147         auto collection = buildFontCollection("Ascii.ttf");
148         MinikinPaint paint1(collection);
149         paint1.scaleX = 1.0f;
150         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
151                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
152         MinikinPaint paint2(collection);
153         paint2.scaleX = 2.0f;
154         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
155                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
156         EXPECT_NE(layout1.get(), layout2.get());
157     }
158     {
159         SCOPED_TRACE("Different skew X");
160         auto collection = buildFontCollection("Ascii.ttf");
161         MinikinPaint paint1(collection);
162         paint1.skewX = 1.0f;
163         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
164                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
165         MinikinPaint paint2(collection);
166         paint2.skewX = 2.0f;
167         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
168                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
169         EXPECT_NE(layout1.get(), layout2.get());
170     }
171     {
172         SCOPED_TRACE("Different letter spacing");
173         auto collection = buildFontCollection("Ascii.ttf");
174         MinikinPaint paint1(collection);
175         paint1.letterSpacing = 0.0f;
176         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
177                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
178         MinikinPaint paint2(collection);
179         paint2.letterSpacing = 1.0f;
180         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
181                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
182         EXPECT_NE(layout1.get(), layout2.get());
183     }
184     {
185         SCOPED_TRACE("Different word spacing");
186         auto collection = buildFontCollection("Ascii.ttf");
187         MinikinPaint paint1(collection);
188         paint1.wordSpacing = 0.0f;
189         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
190                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
191         MinikinPaint paint2(collection);
192         paint2.wordSpacing = 1.0f;
193         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
194                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
195         EXPECT_NE(layout1.get(), layout2.get());
196     }
197     {
198         SCOPED_TRACE("Different paint flags");
199         auto collection = buildFontCollection("Ascii.ttf");
200         MinikinPaint paint1(collection);
201         paint1.paintFlags = 0;
202         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
203                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
204         MinikinPaint paint2(collection);
205         paint2.paintFlags = LinearTextFlag;
206         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
207                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
208         EXPECT_NE(layout1.get(), layout2.get());
209     }
210     {
211         SCOPED_TRACE("Different locale list ID");
212         auto collection = buildFontCollection("Ascii.ttf");
213         MinikinPaint paint1(collection);
214         paint1.localeListId = LocaleListCache::getId("en-US");
215         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
216                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
217         MinikinPaint paint2(collection);
218         paint2.localeListId = LocaleListCache::getId("ja-JP");
219         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
220                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
221         EXPECT_NE(layout1.get(), layout2.get());
222     }
223     {
224         SCOPED_TRACE("Different family variant");
225         auto collection = buildFontCollection("Ascii.ttf");
226         MinikinPaint paint1(collection);
227         paint1.familyVariant = FontFamily::Variant::DEFAULT;
228         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
229                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
230         MinikinPaint paint2(collection);
231         paint2.familyVariant = FontFamily::Variant::COMPACT;
232         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
233                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
234         EXPECT_NE(layout1.get(), layout2.get());
235     }
236     {
237         SCOPED_TRACE("Different font feature settings");
238         auto collection = buildFontCollection("Ascii.ttf");
239         MinikinPaint paint1(collection);
240         paint1.fontFeatureSettings = "";
241         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
242                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
243         MinikinPaint paint2(collection);
244         paint2.fontFeatureSettings = "'liga' on";
245         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
246                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
247         EXPECT_NE(layout1.get(), layout2.get());
248     }
249 }
250 
TEST(LayoutCacheTest,cacheOverflowTest)251 TEST(LayoutCacheTest, cacheOverflowTest) {
252     auto text = utf8ToUtf16("android");
253     Range range(0, text.size());
254     MinikinPaint paint(buildFontCollection("Ascii.ttf"));
255 
256     TestableLayoutCache layoutCache(5);
257 
258     LayoutCapture layout1;
259     layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
260                             EndHyphenEdit::NO_EDIT, layout1);
261 
262     for (char c = 'a'; c <= 'z'; c++) {
263         auto text1 = utf8ToUtf16(std::string(c, 10));
264         LayoutCapture layout2;
265         layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
266                                 StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
267     }
268 
269     LayoutCapture layout3;
270     layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
271                             EndHyphenEdit::NO_EDIT, layout3);
272     EXPECT_NE(layout1.get(), layout3.get());
273 }
274 
275 }  // namespace minikin
276