/* * Copyright (C) 2018 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 #include "minikin/BoundsCache.h" #include "FontTestUtils.h" #include "LocaleListCache.h" #include "UnicodeUtils.h" namespace minikin { class TestableBoundsCache : public BoundsCache { public: TestableBoundsCache(uint32_t maxEntries) : BoundsCache(maxEntries) {} }; class BoundsCapture { public: BoundsCapture() {} void operator()(const MinikinRect& rect, float advance) { mRect = rect; mAdvance = advance; } const MinikinRect& rect() const { return mRect; } float advance() const { return mAdvance; } private: MinikinRect mRect; float mAdvance; }; TEST(BoundsCacheTest, cacheHitTest) { auto text = utf8ToUtf16("android"); Range range(0, text.size()); MinikinPaint paint(buildFontCollection("Ascii.ttf")); TestableBoundsCache boundsCache(10); BoundsCapture bounds1; boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); BoundsCapture bounds2; boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); EXPECT_EQ(bounds1.rect(), bounds2.rect()); EXPECT_EQ(bounds1.advance(), bounds2.advance()); } TEST(BoundsCacheTest, cacheMissTest) { auto text1 = utf8ToUtf16("android"); auto text2 = utf8ToUtf16("αβγδζ"); MinikinPaint paint(buildFontCollection("Ascii.ttf")); TestableBoundsCache boundsCache(10); BoundsCapture bounds1; BoundsCapture bounds2; { SCOPED_TRACE("Different text"); boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); boundsCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); EXPECT_NE(bounds1.rect(), bounds2.rect()); EXPECT_NE(bounds1.advance(), bounds2.advance()); } { SCOPED_TRACE("Different range"); boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); boundsCache.getOrCreate(text1, Range(1, text1.size()), paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); EXPECT_NE(bounds1.rect(), bounds2.rect()); EXPECT_NE(bounds1.advance(), bounds2.advance()); } { SCOPED_TRACE("Different collection"); MinikinPaint paint1(buildFontCollection("Ascii.ttf")); paint1.size = 10.0f; paint1.scaleX = 1.0f; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); MinikinPaint paint2(buildFontCollection("Emoji.ttf")); paint2.size = 10.0f; paint2.scaleX = 1.0f; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); EXPECT_NE(bounds1.rect(), bounds2.rect()); EXPECT_NE(bounds1.advance(), bounds2.advance()); } { SCOPED_TRACE("Different size"); auto collection = buildFontCollection("Ascii.ttf"); MinikinPaint paint1(collection); paint1.size = 10.0f; paint1.scaleX = 1.0f; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); MinikinPaint paint2(collection); paint2.size = 20.0f; paint2.scaleX = 1.0f; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); EXPECT_NE(bounds1.rect(), bounds2.rect()); EXPECT_NE(bounds1.advance(), bounds2.advance()); } { SCOPED_TRACE("Different letter spacing"); auto collection = buildFontCollection("Ascii.ttf"); MinikinPaint paint1(collection); paint1.letterSpacing = 0.0f; paint1.size = 10.0f; paint1.scaleX = 1.0f; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); MinikinPaint paint2(collection); paint2.letterSpacing = 1.0f; paint2.size = 10.0f; paint2.scaleX = 1.0f; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); EXPECT_NE(bounds1.rect(), bounds2.rect()); EXPECT_NE(bounds1.advance(), bounds2.advance()); } } TEST(BoundsCacheTest, cacheOverflowTest) { auto text = utf8ToUtf16("android"); Range range(0, text.size()); MinikinPaint paint(buildFontCollection("Ascii.ttf")); TestableBoundsCache boundsCache(5); BoundsCapture bounds1; boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds1); for (char c = 'a'; c <= 'z'; c++) { auto text1 = utf8ToUtf16(std::string(10, c)); BoundsCapture bounds2; boundsCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds2); } BoundsCapture bounds3; boundsCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, bounds3); EXPECT_EQ(bounds1.rect(), bounds3.rect()); EXPECT_EQ(bounds1.advance(), bounds3.advance()); } } // namespace minikin