291 lines
13 KiB
291 lines
13 KiB
/*
|
|
* 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 "minikin/Layout.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "minikin/LayoutCache.h"
|
|
|
|
#include "FontTestUtils.h"
|
|
#include "LocaleListCache.h"
|
|
#include "UnicodeUtils.h"
|
|
|
|
namespace minikin {
|
|
|
|
class TestableLayoutCache : public LayoutCache {
|
|
public:
|
|
TestableLayoutCache(uint32_t maxEntries) : LayoutCache(maxEntries) {}
|
|
using LayoutCache::getCacheSize;
|
|
};
|
|
|
|
class LayoutCapture {
|
|
public:
|
|
LayoutCapture() {}
|
|
|
|
void operator()(const LayoutPiece& layout, const MinikinPaint& /* dir */) { mLayout = &layout; }
|
|
|
|
const LayoutPiece* get() const { return mLayout; }
|
|
|
|
private:
|
|
const LayoutPiece* mLayout;
|
|
};
|
|
|
|
TEST(LayoutCacheTest, cacheHitTest) {
|
|
auto text = utf8ToUtf16("android");
|
|
Range range(0, text.size());
|
|
MinikinPaint paint(buildFontCollection("Ascii.ttf"));
|
|
|
|
TestableLayoutCache layoutCache(10);
|
|
|
|
LayoutCapture layout1;
|
|
layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
|
|
EndHyphenEdit::NO_EDIT, layout1);
|
|
|
|
LayoutCapture layout2;
|
|
layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
|
|
EndHyphenEdit::NO_EDIT, layout2);
|
|
|
|
EXPECT_EQ(layout1.get(), layout2.get());
|
|
}
|
|
|
|
TEST(LayoutCacheTest, cacheMissTest) {
|
|
auto text1 = utf8ToUtf16("android");
|
|
auto text2 = utf8ToUtf16("ANDROID");
|
|
MinikinPaint paint(buildFontCollection("Ascii.ttf"));
|
|
|
|
TestableLayoutCache layoutCache(10);
|
|
|
|
LayoutCapture layout1;
|
|
LayoutCapture layout2;
|
|
|
|
{
|
|
SCOPED_TRACE("Different text");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
layoutCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different range");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
layoutCache.getOrCreate(text1, Range(1, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different text");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
layoutCache.getOrCreate(text2, Range(0, text2.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different direction");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, true /* RTL */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different start hyphenation");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::INSERT_HYPHEN, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different end hyphen");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::INSERT_HYPHEN, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different collection");
|
|
MinikinPaint paint1(buildFontCollection("Ascii.ttf"));
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(buildFontCollection("Emoji.ttf"));
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different size");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.size = 10.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.size = 20.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different scale X");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.scaleX = 1.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.scaleX = 2.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different skew X");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.skewX = 1.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.skewX = 2.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different letter spacing");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.letterSpacing = 0.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.letterSpacing = 1.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different word spacing");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.wordSpacing = 0.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.wordSpacing = 1.0f;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different paint flags");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.fontFlags = 0;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.fontFlags = LinearMetrics_Flag;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different locale list ID");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.localeListId = LocaleListCache::getId("en-US");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.localeListId = LocaleListCache::getId("ja-JP");
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different family variant");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.familyVariant = FamilyVariant::DEFAULT;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.familyVariant = FamilyVariant::COMPACT;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
{
|
|
SCOPED_TRACE("Different font feature settings");
|
|
auto collection = buildFontCollection("Ascii.ttf");
|
|
MinikinPaint paint1(collection);
|
|
paint1.fontFeatureSettings = "";
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint1, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout1);
|
|
MinikinPaint paint2(collection);
|
|
paint2.fontFeatureSettings = "'liga' on";
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint2, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
EXPECT_NE(layout1.get(), layout2.get());
|
|
}
|
|
}
|
|
|
|
TEST(LayoutCacheTest, cacheOverflowTest) {
|
|
auto text = utf8ToUtf16("android");
|
|
Range range(0, text.size());
|
|
MinikinPaint paint(buildFontCollection("Ascii.ttf"));
|
|
|
|
TestableLayoutCache layoutCache(5);
|
|
|
|
LayoutCapture layout1;
|
|
layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
|
|
EndHyphenEdit::NO_EDIT, layout1);
|
|
|
|
for (char c = 'a'; c <= 'z'; c++) {
|
|
auto text1 = utf8ToUtf16(std::string(10, c));
|
|
LayoutCapture layout2;
|
|
layoutCache.getOrCreate(text1, Range(0, text1.size()), paint, false /* LTR */,
|
|
StartHyphenEdit::NO_EDIT, EndHyphenEdit::NO_EDIT, layout2);
|
|
}
|
|
|
|
LayoutCapture layout3;
|
|
layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
|
|
EndHyphenEdit::NO_EDIT, layout3);
|
|
EXPECT_NE(layout1.get(), layout3.get());
|
|
}
|
|
|
|
TEST(LayoutCacheTest, cacheLengthLimitTest) {
|
|
auto text = utf8ToUtf16(std::string(130, 'a'));
|
|
Range range(0, text.size());
|
|
MinikinPaint paint(buildFontCollection("Ascii.ttf"));
|
|
|
|
TestableLayoutCache layoutCache(140);
|
|
|
|
LayoutCapture layout;
|
|
layoutCache.getOrCreate(text, range, paint, false /* LTR */, StartHyphenEdit::NO_EDIT,
|
|
EndHyphenEdit::NO_EDIT, layout);
|
|
|
|
EXPECT_EQ(layoutCache.getCacheSize(), 0u);
|
|
}
|
|
|
|
} // namespace minikin
|