From 0036da164e3b25f1ac29c840c1fe15b03dc6677f Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Thu, 25 Feb 2016 17:07:42 +0900 Subject: [PATCH] Use color font if skin tone is specified. If skin tone is specified, the base emoji should be emoji style even if it is text presentation default emoji. This patch also removes wrong test case which expects default emoji presentation but it is controlled by family order in /etc/fonts.xml and there is no special logic for default presentation in minikin. Thus the default presentation unit test should not be in minikin. Bug: 27342346 Change-Id: I74a2b2feab4d559535049e368cfd833063cce81c --- libs/minikin/FontCollection.cpp | 10 ++- libs/minikin/GraphemeBreak.cpp | 43 +---------- libs/minikin/MinikinInternal.cpp | 42 +++++++++++ libs/minikin/MinikinInternal.h | 6 ++ tests/FontCollectionItemizeTest.cpp | 110 ++++++++++------------------ tests/data/ColorEmojiFont.ttf | Bin 948 -> 1020 bytes tests/data/ColorEmojiFont.ttx | 16 +++- tests/data/TextEmojiFont.ttf | Bin 940 -> 964 bytes tests/data/TextEmojiFont.ttx | 6 ++ tests/data/emoji.xml | 6 +- 10 files changed, 116 insertions(+), 123 deletions(-) diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp index dd905a3442f..4541af84757 100644 --- a/libs/minikin/FontCollection.cpp +++ b/libs/minikin/FontCollection.cpp @@ -379,11 +379,13 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty langListId, variant); if (utf16Pos == 0 || family != lastFamily) { size_t start = utf16Pos; - // Workaround for Emoji keycap until we implement per-cluster font - // selection: if keycap is found in a different font that also - // supports previous char, attach previous char to the new run. + // Workaround for Emoji keycap and emoji modifier until we implement per-cluster + // font selection: if a keycap or an emoji modifier is found in a different font + // that also supports previous char, attach previous char to the new run. // Bug 7557244. - if (ch == KEYCAP && utf16Pos != 0 && family && family->getCoverage()->get(prevCh)) { + if (utf16Pos != 0 && + (ch == KEYCAP || (isEmojiModifier(ch) && isEmojiBase(prevCh))) && + family && family->getCoverage()->get(prevCh)) { const size_t prevChLength = U16_LENGTH(prevCh); run->end -= prevChLength; if (run->start == run->end) { diff --git a/libs/minikin/GraphemeBreak.cpp b/libs/minikin/GraphemeBreak.cpp index 41410917d4e..ef323d5d489 100644 --- a/libs/minikin/GraphemeBreak.cpp +++ b/libs/minikin/GraphemeBreak.cpp @@ -19,6 +19,7 @@ #include #include +#include "MinikinInternal.h" namespace android { @@ -77,48 +78,6 @@ bool isZwjEmoji(uint32_t c) { || c == 0x1F5E8); // LEFT SPEECH BUBBLE } -// Based on Modifiers from http://www.unicode.org/L2/L2016/16011-data-file.txt -bool isEmojiModifier(uint32_t c) { - return (0x1F3FB <= c && c <= 0x1F3FF); -} - -// Based on Emoji_Modifier_Base from -// http://www.unicode.org/Public/emoji/3.0/emoji-data.txt -bool isEmojiBase(uint32_t c) { - if (0x261D <= c && c <= 0x270D) { - return (c == 0x261D || c == 0x26F9 || (0x270A <= c && c <= 0x270D)); - } else if (0x1F385 <= c && c <= 0x1F93E) { - return (c == 0x1F385 - || (0x1F3C3 <= c || c <= 0x1F3C4) - || (0x1F3CA <= c || c <= 0x1F3CB) - || (0x1F442 <= c || c <= 0x1F443) - || (0x1F446 <= c || c <= 0x1F450) - || (0x1F466 <= c || c <= 0x1F469) - || c == 0x1F46E - || (0x1F470 <= c || c <= 0x1F478) - || c == 0x1F47C - || (0x1F481 <= c || c <= 0x1F483) - || (0x1F485 <= c || c <= 0x1F487) - || c == 0x1F4AA - || c == 0x1F575 - || c == 0x1F57A - || c == 0x1F590 - || (0x1F595 <= c || c <= 0x1F596) - || (0x1F645 <= c || c <= 0x1F647) - || (0x1F64B <= c || c <= 0x1F64F) - || c == 0x1F6A3 - || (0x1F6B4 <= c || c <= 0x1F6B6) - || c == 0x1F6C0 - || (0x1F918 <= c || c <= 0x1F91E) - || c == 0x1F926 - || c == 0x1F930 - || (0x1F933 <= c || c <= 0x1F939) - || (0x1F93B <= c || c <= 0x1F93E)); - } else { - return false; - } -} - bool GraphemeBreak::isGraphemeBreak(const uint16_t* buf, size_t start, size_t count, size_t offset) { // This implementation closely follows Unicode Standard Annex #29 on diff --git a/libs/minikin/MinikinInternal.cpp b/libs/minikin/MinikinInternal.cpp index c2aa01ac198..7fa7ce9d4db 100644 --- a/libs/minikin/MinikinInternal.cpp +++ b/libs/minikin/MinikinInternal.cpp @@ -30,4 +30,46 @@ void assertMinikinLocked() { #endif } +// Based on Modifiers from http://www.unicode.org/L2/L2016/16011-data-file.txt +bool isEmojiModifier(uint32_t c) { + return (0x1F3FB <= c && c <= 0x1F3FF); +} + +// Based on Emoji_Modifier_Base from +// http://www.unicode.org/Public/emoji/3.0/emoji-data.txt +bool isEmojiBase(uint32_t c) { + if (0x261D <= c && c <= 0x270D) { + return (c == 0x261D || c == 0x26F9 || (0x270A <= c && c <= 0x270D)); + } else if (0x1F385 <= c && c <= 0x1F93E) { + return (c == 0x1F385 + || (0x1F3C3 <= c || c <= 0x1F3C4) + || (0x1F3CA <= c || c <= 0x1F3CB) + || (0x1F442 <= c || c <= 0x1F443) + || (0x1F446 <= c || c <= 0x1F450) + || (0x1F466 <= c || c <= 0x1F469) + || c == 0x1F46E + || (0x1F470 <= c || c <= 0x1F478) + || c == 0x1F47C + || (0x1F481 <= c || c <= 0x1F483) + || (0x1F485 <= c || c <= 0x1F487) + || c == 0x1F4AA + || c == 0x1F575 + || c == 0x1F57A + || c == 0x1F590 + || (0x1F595 <= c || c <= 0x1F596) + || (0x1F645 <= c || c <= 0x1F647) + || (0x1F64B <= c || c <= 0x1F64F) + || c == 0x1F6A3 + || (0x1F6B4 <= c || c <= 0x1F6B6) + || c == 0x1F6C0 + || (0x1F918 <= c || c <= 0x1F91E) + || c == 0x1F926 + || c == 0x1F930 + || (0x1F933 <= c || c <= 0x1F939) + || (0x1F93B <= c || c <= 0x1F93E)); + } else { + return false; + } +} + } diff --git a/libs/minikin/MinikinInternal.h b/libs/minikin/MinikinInternal.h index 34a95bb3d12..3d68691b35d 100644 --- a/libs/minikin/MinikinInternal.h +++ b/libs/minikin/MinikinInternal.h @@ -32,6 +32,12 @@ extern Mutex gMinikinLock; // Aborts if gMinikinLock is not acquired. Do nothing on the release build. void assertMinikinLocked(); +// Returns true if c is emoji modifier base. +bool isEmojiBase(uint32_t c); + +// Returns true if c is emoji modifier. +bool isEmojiModifier(uint32_t c); + } #endif // MINIKIN_INTERNAL_H diff --git a/tests/FontCollectionItemizeTest.cpp b/tests/FontCollectionItemizeTest.cpp index 446efc6b565..031f3f98f3a 100644 --- a/tests/FontCollectionItemizeTest.cpp +++ b/tests/FontCollectionItemizeTest.cpp @@ -1119,78 +1119,6 @@ TEST_F(FontCollectionItemizeTest, itemize_LanguageAndCoverage) { } } -TEST_F(FontCollectionItemizeTest, itemize_emojiSelection) { - std::unique_ptr collection = getFontCollection(kTestFontDir, kEmojiXmlFile); - std::vector runs; - - const FontStyle kDefaultFontStyle; - - // U+00A9 is a text default emoji which is only available in TextEmojiFont.ttf. - // TextEmojiFont.ttf should be selected. - itemize(collection.get(), "U+00A9", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); - - // U+00AE is a text default emoji which is only available in ColorEmojiFont.ttf. - // ColorEmojiFont.ttf should be selected. - itemize(collection.get(), "U+00AE", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); - - // U+203C is a text default emoji which is available in both TextEmojiFont.ttf and - // ColorEmojiFont.ttf. TextEmojiFont.ttf should be selected. - itemize(collection.get(), "U+203C", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - // TODO: use text font for text default emoji. - // EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); - - // U+2049 is a text default emoji which is not available in either TextEmojiFont.ttf or - // ColorEmojiFont.ttf. No font should be selected. - itemize(collection.get(), "U+2049", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0])); - - // U+231A is a emoji default emoji which is available only in TextEmojiFont.ttf. - // TextEmojiFont.ttf should be selected. - itemize(collection.get(), "U+231A", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); - - // U+231B is a emoji default emoji which is available only in ColorEmojiFont.ttf. - // ColorEmojiFont.ttf should be selected. - itemize(collection.get(), "U+231B", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); - - // U+23E9 is a emoji default emoji which is available in both TextEmojiFont.ttf and - // ColorEmojiFont.ttf. ColorEmojiFont should be selected. - itemize(collection.get(), "U+23E9", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); - - // U+23EA is a emoji default emoji which is not avaialble in either TextEmojiFont.ttf and - // ColorEmojiFont.ttf. No font should b e selected. - itemize(collection.get(), "U+23EA", kDefaultFontStyle, &runs); - ASSERT_EQ(1U, runs.size()); - EXPECT_EQ(0, runs[0].start); - EXPECT_EQ(1, runs[0].end); - EXPECT_TRUE(runs[0].fakedFont.font == NULL || kNoGlyphFont == getFontPath(runs[0])); -} - TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0E) { std::unique_ptr collection = getFontCollection(kTestFontDir, kEmojiXmlFile); std::vector runs; @@ -1355,3 +1283,41 @@ TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_withFE0F) { EXPECT_EQ(kMixedEmojiFont, getFontPath(runs[0])); } +TEST_F(FontCollectionItemizeTest, itemize_emojiSelection_with_skinTone) { + std::unique_ptr collection = getFontCollection(kTestFontDir, kEmojiXmlFile); + std::vector runs; + + const FontStyle kDefaultFontStyle; + + // TextEmoji font is selected since it is listed before ColorEmoji font. + itemize(collection.get(), "U+261D", kDefaultFontStyle, &runs); + ASSERT_EQ(1U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(1, runs[0].end); + EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); + + // If skin tone is specified, it should be colored. + itemize(collection.get(), "U+261D U+1F3FD", kDefaultFontStyle, &runs); + ASSERT_EQ(1U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(3, runs[0].end); + EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); + + // Still color font is selected if an emoji variation selector is specified. + itemize(collection.get(), "U+261D U+FE0F U+1F3FD", kDefaultFontStyle, &runs); + ASSERT_EQ(1U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(4, runs[0].end); + EXPECT_EQ(kColorEmojiFont, getFontPath(runs[0])); + + // Text font should be selected if a text variation selector is specified and skin tone is + // rendered by itself. + itemize(collection.get(), "U+261D U+FE0E U+1F3FD", kDefaultFontStyle, &runs); + ASSERT_EQ(2U, runs.size()); + EXPECT_EQ(0, runs[0].start); + EXPECT_EQ(2, runs[0].end); + EXPECT_EQ(kTextEmojiFont, getFontPath(runs[0])); + EXPECT_EQ(2, runs[1].start); + EXPECT_EQ(4, runs[1].end); + EXPECT_EQ(kColorEmojiFont, getFontPath(runs[1])); +} diff --git a/tests/data/ColorEmojiFont.ttf b/tests/data/ColorEmojiFont.ttf index dd72eea6d58a44e8a320acf90732f8ee9c421c44..e0b34a1d059542c21d71b0fccba3ce9465fd1861 100644 GIT binary patch delta 375 zcmdnO{)fGufsuiMfs3Jmftf+UKUm)=V@0|TQ5P&6SqH?e?0BxnnezXZrHNYANE zV_*Za7?=!z#EOj6#1s}e+iO7f8Xz{y01B`yVLA$AKLKKujNFn679}P;&3=Hf*dL0xq0gbhRU|FOb3~mg*3?U2*lZBY-C+jn5ZJx-a%m@ISLrQc2 delta 343 zcmeyvzJfsuiMfs3Jmftf+UKUm)=V|AY?0|TQ5P&6SqH?hFQ+uIPxUjXF$q~}zo zF|Yzz3`{IQq9-FYF@<^EJ4YaU4G^1U00mfZy+zF1Cv zaw5YehV?-H8xTJ?u_Ca5;SBdypa2I@Kp`(NH}(Irw~{~$CIH2l3i69f{_kKgVqjpN z0+I(i7U*EZa^CQGew(ihjLa{9;tZEWr=JDWt)deqUa)24n#{)-!=(0eat@<$GDw0E z=shkV&BMUL-~gsM7?^-* hltG3;nL&%ekimk%p22PM1SZeP_DpV@Z!#$}0sxJCPN)C? diff --git a/tests/data/ColorEmojiFont.ttx b/tests/data/ColorEmojiFont.ttx index b077339e6a3..58b3d83b9b3 100644 --- a/tests/data/ColorEmojiFont.ttx +++ b/tests/data/ColorEmojiFont.ttx @@ -27,6 +27,8 @@ + + @@ -153,17 +155,21 @@ + + - + - + + + @@ -206,6 +212,12 @@ + + + + + + diff --git a/tests/data/TextEmojiFont.ttf b/tests/data/TextEmojiFont.ttf index 21013a0fa73cebdf661eb0daa2054890c3d1f7eb..21b6801841bb109baef85ca0761bfb88a4e019a9 100644 GIT binary patch delta 286 zcmZ3(euRC3LVZuCrXm9a;{pZ-hLH4}$}|SH|Nj{nn0SEvSsAH`DJ*g}bAaqMKx~!) z6ku7xbQH*b0>mmAxg{0MN1611`a6JpiJbi8M21TYe}MciAbxIQMFGQE?yW#R2avCj zmzbN{R-bkNX#NTy|4%`Fv0=&o9SlYc49rVF3K$rHBoI%^-+wut-{va=Bl8QO2*V}O z>1V<8m*m$IFW542Pv&Es=ot%E%*?q0!pZtfTATZsWElZvs!iYk delta 276 zcmX@YzJ`5*LcNQpw+;gX;{pZ-2A}kt$}|R6Ad7*C1xWN{q$Z{?uY2bOWUm2YvkagB z%MzxeK=u{aMsbD_JcoC?-1IQQ4$xlvXxWup?$bSRk=O$JZFr4Au3gmMD`3iZ7 zxv6dSX$OGjPXO}&6yz5hmi*trV8p<{+yhd;zz8IPSow++Z#=)vR|ZDr7eEn)OQO@y zg6US#nG-MAGICAkW1Q#+5@BRuX5a$SJPa%h4q%#tfeFZ7sbHflrTkLu*Z==OQ6%2~ ii2KLyyuKrMlb?Zc@(V`g$$?B^lk=IZHm_!qWCQ^0Q%7Y0 diff --git a/tests/data/TextEmojiFont.ttx b/tests/data/TextEmojiFont.ttx index 35e7d9837b2..83cc9bce528 100644 --- a/tests/data/TextEmojiFont.ttx +++ b/tests/data/TextEmojiFont.ttx @@ -27,6 +27,7 @@ + @@ -153,6 +154,7 @@ + @@ -163,6 +165,7 @@ + @@ -206,6 +209,9 @@ + + + diff --git a/tests/data/emoji.xml b/tests/data/emoji.xml index a908b4634a9..796a0f1ea03 100644 --- a/tests/data/emoji.xml +++ b/tests/data/emoji.xml @@ -19,12 +19,12 @@ NoGlyphFont.ttf - - ColorEmojiFont.ttf - TextEmojiFont.ttf + + ColorEmojiFont.ttf + ColorTextMixedEmojiFont.ttf