diff --git a/infra/bots/recipe_modules/build/default.py b/infra/bots/recipe_modules/build/default.py index 326d8be150..39b9b77e18 100644 --- a/infra/bots/recipe_modules/build/default.py +++ b/infra/bots/recipe_modules/build/default.py @@ -281,6 +281,7 @@ def compile_fn(api, checkout_root, out_dir): 'skia_use_expat': 'false', 'skia_use_freetype': 'false', 'skia_use_harfbuzz': 'false', + 'skia_use_icu': 'false', 'skia_use_libjpeg_turbo_decode': 'false', 'skia_use_libjpeg_turbo_encode': 'false', 'skia_use_libpng_decode': 'false', diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-NoDEPS.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-NoDEPS.json index 68de71b0b7..4043c52334 100644 --- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-NoDEPS.json +++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-NoDEPS.json @@ -52,7 +52,7 @@ "[START_DIR]/cache/work/skia/bin/gn", "gen", "[START_DIR]/cache/work/skia/out/Build-Debian10-Clang-x86_64-Release-NoDEPS/Release", - "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cc_wrapper=\"[START_DIR]/ccache_linux/bin/ccache\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DPLACEHOLDER_clang_linux_version=42\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\", \"-L[START_DIR]/clang_linux/lib\"] is_debug=false is_official_build=true skia_enable_fontmgr_empty=true skia_enable_gpu=true skia_enable_pdf=false skia_use_expat=false skia_use_freetype=false skia_use_harfbuzz=false skia_use_libjpeg_turbo_decode=false skia_use_libjpeg_turbo_encode=false skia_use_libpng_decode=false skia_use_libpng_encode=false skia_use_libwebp_decode=false skia_use_libwebp_encode=false skia_use_vulkan=false skia_use_zlib=false target_cpu=\"x86_64\" werror=true" + "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cc_wrapper=\"[START_DIR]/ccache_linux/bin/ccache\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DPLACEHOLDER_clang_linux_version=42\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\", \"-L[START_DIR]/clang_linux/lib\"] is_debug=false is_official_build=true skia_enable_fontmgr_empty=true skia_enable_gpu=true skia_enable_pdf=false skia_use_expat=false skia_use_freetype=false skia_use_harfbuzz=false skia_use_icu=false skia_use_libjpeg_turbo_decode=false skia_use_libjpeg_turbo_encode=false skia_use_libpng_decode=false skia_use_libpng_encode=false skia_use_libwebp_decode=false skia_use_libwebp_encode=false skia_use_vulkan=false skia_use_zlib=false target_cpu=\"x86_64\" werror=true" ], "cwd": "[START_DIR]/cache/work/skia", "env": { diff --git a/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json b/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json index 6827e80b8b..646d0be5f1 100644 --- a/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json +++ b/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json @@ -139,7 +139,7 @@ "[START_DIR]\\skia\\bin\\gn", "gen", "[START_DIR]\\skia\\out\\Build-Win10-Clang-x86_64-Release-NoDEPS\\Release_x64", - "--args=cc=\"clang\" clang_win=\"[START_DIR]\\clang_win\" cxx=\"clang++\" extra_cflags=[\"-DPLACEHOLDER_clang_win_version=42\"] is_debug=false is_official_build=true skia_enable_fontmgr_empty=true skia_enable_gpu=true skia_enable_pdf=false skia_use_expat=false skia_use_freetype=false skia_use_harfbuzz=false skia_use_libjpeg_turbo_decode=false skia_use_libjpeg_turbo_encode=false skia_use_libpng_decode=false skia_use_libpng_encode=false skia_use_libwebp_decode=false skia_use_libwebp_encode=false skia_use_vulkan=false skia_use_zlib=false target_cpu=\"x86_64\" werror=true win_sdk=\"[START_DIR]\\win_toolchain/win_sdk\" win_vc=\"[START_DIR]\\win_toolchain/VC\"" + "--args=cc=\"clang\" clang_win=\"[START_DIR]\\clang_win\" cxx=\"clang++\" extra_cflags=[\"-DPLACEHOLDER_clang_win_version=42\"] is_debug=false is_official_build=true skia_enable_fontmgr_empty=true skia_enable_gpu=true skia_enable_pdf=false skia_use_expat=false skia_use_freetype=false skia_use_harfbuzz=false skia_use_icu=false skia_use_libjpeg_turbo_decode=false skia_use_libjpeg_turbo_encode=false skia_use_libpng_decode=false skia_use_libpng_encode=false skia_use_libwebp_decode=false skia_use_libwebp_encode=false skia_use_vulkan=false skia_use_zlib=false target_cpu=\"x86_64\" werror=true win_sdk=\"[START_DIR]\\win_toolchain/win_sdk\" win_vc=\"[START_DIR]\\win_toolchain/VC\"" ], "cwd": "[START_DIR]\\skia", "env": { diff --git a/modules/skottie/BUILD.gn b/modules/skottie/BUILD.gn index 0f67f3f2b0..0f986e81d5 100644 --- a/modules/skottie/BUILD.gn +++ b/modules/skottie/BUILD.gn @@ -23,6 +23,7 @@ if (skia_enable_skottie) { "../skresources", "../sksg", "../skshaper", + "../skunicode", ] } diff --git a/modules/skottie/include/SkottieProperty.h b/modules/skottie/include/SkottieProperty.h index 5b084bd823..e1f5bf3970 100644 --- a/modules/skottie/include/SkottieProperty.h +++ b/modules/skottie/include/SkottieProperty.h @@ -39,24 +39,25 @@ enum class TextPaintOrder : uint8_t { struct TextPropertyValue { sk_sp fTypeface; SkString fText; - float fTextSize = 0, - fMinTextSize = 0, // when auto-sizing - fMaxTextSize = std::numeric_limits::max(), // when auto-sizing - fStrokeWidth = 0, - fLineHeight = 0, - fLineShift = 0, - fAscent = 0; - SkTextUtils::Align fHAlign = SkTextUtils::kLeft_Align; - Shaper::VAlign fVAlign = Shaper::VAlign::kTop; - Shaper::ResizePolicy fResize = Shaper::ResizePolicy::kNone; - Shaper::LinebreakPolicy fLineBreak = Shaper::LinebreakPolicy::kExplicit; - Shaper::Direction fDirection = Shaper::Direction::kLTR; - SkRect fBox = SkRect::MakeEmpty(); - SkColor fFillColor = SK_ColorTRANSPARENT, - fStrokeColor = SK_ColorTRANSPARENT; - TextPaintOrder fPaintOrder = TextPaintOrder::kFillStroke; - bool fHasFill = false, - fHasStroke = false; + float fTextSize = 0, + fMinTextSize = 0, // when auto-sizing + fMaxTextSize = std::numeric_limits::max(), // when auto-sizing + fStrokeWidth = 0, + fLineHeight = 0, + fLineShift = 0, + fAscent = 0; + SkTextUtils::Align fHAlign = SkTextUtils::kLeft_Align; + Shaper::VAlign fVAlign = Shaper::VAlign::kTop; + Shaper::ResizePolicy fResize = Shaper::ResizePolicy::kNone; + Shaper::LinebreakPolicy fLineBreak = Shaper::LinebreakPolicy::kExplicit; + Shaper::Direction fDirection = Shaper::Direction::kLTR; + Shaper::Capitalization fCapitalization = Shaper::Capitalization::kNone; + SkRect fBox = SkRect::MakeEmpty(); + SkColor fFillColor = SK_ColorTRANSPARENT, + fStrokeColor = SK_ColorTRANSPARENT; + TextPaintOrder fPaintOrder = TextPaintOrder::kFillStroke; + bool fHasFill = false, + fHasStroke = false; bool operator==(const TextPropertyValue& other) const; bool operator!=(const TextPropertyValue& other) const; diff --git a/modules/skottie/src/SkottieProperty.cpp b/modules/skottie/src/SkottieProperty.cpp index c44c7e974b..a9ee6e08c5 100644 --- a/modules/skottie/src/SkottieProperty.cpp +++ b/modules/skottie/src/SkottieProperty.cpp @@ -27,6 +27,7 @@ bool TextPropertyValue::operator==(const TextPropertyValue& other) const { && fResize == other.fResize && fLineBreak == other.fLineBreak && fDirection == other.fDirection + && fCapitalization == other.fCapitalization && fBox == other.fBox && fFillColor == other.fFillColor && fStrokeColor == other.fStrokeColor diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp index 28a00b73e8..cbc79021eb 100644 --- a/modules/skottie/src/SkottieTest.cpp +++ b/modules/skottie/src/SkottieTest.cpp @@ -326,6 +326,7 @@ DEF_TEST(Skottie_Properties, reporter) { Shaper::ResizePolicy::kNone, Shaper::LinebreakPolicy::kExplicit, Shaper::Direction::kLTR, + Shaper::Capitalization::kNone, SkRect::MakeEmpty(), SK_ColorTRANSPARENT, SK_ColorTRANSPARENT, @@ -478,6 +479,7 @@ DEF_TEST(Skottie_Shaper_HAlign, reporter) { Shaper::ResizePolicy::kNone, Shaper::LinebreakPolicy::kExplicit, Shaper::Direction::kLTR, + Shaper::Capitalization::kNone, Shaper::Flags::kNone }; @@ -548,6 +550,7 @@ DEF_TEST(Skottie_Shaper_VAlign, reporter) { Shaper::ResizePolicy::kNone, Shaper::LinebreakPolicy::kParagraph, Shaper::Direction::kLTR, + Shaper::Capitalization::kNone, Shaper::Flags::kNone }; @@ -588,6 +591,7 @@ DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) { Shaper::ResizePolicy::kNone, Shaper::LinebreakPolicy::kParagraph, Shaper::Direction::kLTR, + Shaper::Capitalization::kNone, Shaper::Flags::kNone }; @@ -680,6 +684,7 @@ DEF_TEST(Skottie_Shaper_ExplicitFontMgr, reporter) { Shaper::ResizePolicy::kNone, Shaper::LinebreakPolicy::kParagraph, Shaper::Direction::kLTR, + Shaper::Capitalization::kNone, Shaper::Flags::kNone }; diff --git a/modules/skottie/src/text/SkottieShaper.cpp b/modules/skottie/src/text/SkottieShaper.cpp index ba87af37c5..4468cf9ce3 100644 --- a/modules/skottie/src/text/SkottieShaper.cpp +++ b/modules/skottie/src/text/SkottieShaper.cpp @@ -13,6 +13,7 @@ #include "include/private/SkTPin.h" #include "include/private/SkTemplates.h" #include "modules/skshaper/include/SkShaper.h" +#include "modules/skunicode/include/SkUnicode.h" #include "src/core/SkTLazy.h" #include "src/core/SkTextBlobPriv.h" #include "src/utils/SkUTF.h" @@ -446,18 +447,47 @@ Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc return best_result; } + +// Applies capitalization rules. +class AdjustedText { +public: + AdjustedText(const SkString& txt, const Shaper::TextDesc& desc) + : fText(txt) { + switch (desc.fCapitalization) { + case Shaper::Capitalization::kNone: + break; + case Shaper::Capitalization::kUpperCase: +#ifdef SK_UNICODE_AVAILABLE + if (auto skuni = SkUnicode::Make()) { + *fText.writable() = skuni->toUpper(*fText); + } +#endif + break; + } + } + + operator const SkString&() const { return *fText; } + +private: + SkTCopyOnFirstWrite fText; +}; + } // namespace -Shaper::Result Shaper::Shape(const SkString& txt, const TextDesc& desc, const SkPoint& point, +Shaper::Result Shaper::Shape(const SkString& orig_txt, const TextDesc& desc, const SkPoint& point, const sk_sp& fontmgr) { + const AdjustedText txt(orig_txt, desc); + return (desc.fResize == ResizePolicy::kScaleToFit || desc.fResize == ResizePolicy::kDownscaleToFit) // makes no sense in point mode ? Result() : ShapeImpl(txt, desc, SkRect::MakeEmpty().makeOffset(point.x(), point.y()), fontmgr); } -Shaper::Result Shaper::Shape(const SkString& txt, const TextDesc& desc, const SkRect& box, +Shaper::Result Shaper::Shape(const SkString& orig_txt, const TextDesc& desc, const SkRect& box, const sk_sp& fontmgr) { + const AdjustedText txt(orig_txt, desc); + switch(desc.fResize) { case ResizePolicy::kNone: return ShapeImpl(txt, desc, box, fontmgr); diff --git a/modules/skottie/src/text/SkottieShaper.h b/modules/skottie/src/text/SkottieShaper.h index 20c678a5d2..cd9fbe72fa 100644 --- a/modules/skottie/src/text/SkottieShaper.h +++ b/modules/skottie/src/text/SkottieShaper.h @@ -86,6 +86,11 @@ public: // Initial text direction. enum class Direction : uint8_t { kLTR, kRTL }; + enum class Capitalization { + kNone, + kUpperCase, + }; + enum Flags : uint32_t { kNone = 0x00, @@ -110,6 +115,7 @@ public: ResizePolicy fResize; LinebreakPolicy fLinebreak; Direction fDirection; + Capitalization fCapitalization; uint32_t fFlags; }; diff --git a/modules/skottie/src/text/TextAdapter.cpp b/modules/skottie/src/text/TextAdapter.cpp index 197d6e1232..048f8d060a 100644 --- a/modules/skottie/src/text/TextAdapter.cpp +++ b/modules/skottie/src/text/TextAdapter.cpp @@ -279,6 +279,7 @@ void TextAdapter::reshape() { fText->fResize, fText->fLineBreak, fText->fDirection, + fText->fCapitalization, this->shaperFlags(), }; const auto shape_result = Shaper::Shape(fText->fText, text_desc, fText->fBox, fFontMgr); diff --git a/modules/skottie/src/text/TextValue.cpp b/modules/skottie/src/text/TextValue.cpp index 46118fec12..f92514e620 100644 --- a/modules/skottie/src/text/TextValue.cpp +++ b/modules/skottie/src/text/TextValue.cpp @@ -46,7 +46,7 @@ bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder, SkTextUtils::kCenter_Align // 'j': 2 }; v->fHAlign = gAlignMap[std::min(ParseDefault((*jtxt)["j"], 0), - SK_ARRAY_COUNT(gAlignMap))]; + SK_ARRAY_COUNT(gAlignMap) - 1)]; // Optional text box size. if (const skjson::ArrayValue* jsz = (*jtxt)["sz"]) { @@ -72,7 +72,7 @@ bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder, // TODO: remove "sk_rs" support after migrating clients. v->fResize = gResizeMap[std::min(std::max(ParseDefault((*jtxt)[ "rs"], 0), ParseDefault((*jtxt)["sk_rs"], 0)), - SK_ARRAY_COUNT(gResizeMap))]; + SK_ARRAY_COUNT(gResizeMap) - 1)]; // Optional min/max font size (used when aute-resizing) v->fMinTextSize = ParseDefault((*jtxt)["mf"], 0.0f); @@ -93,6 +93,14 @@ bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder, : Shaper::LinebreakPolicy::kParagraph; // 'm': 1 -> paragraph text } + // Optional capitalization. + static constexpr Shaper::Capitalization gCapMap[] = { + Shaper::Capitalization::kNone, // 'ca': 0 + Shaper::Capitalization::kUpperCase, // 'ca': 1 + }; + v->fCapitalization = gCapMap[std::min(ParseDefault((*jtxt)["ca"], 0), + SK_ARRAY_COUNT(gCapMap) - 1)]; + // In point mode, the text is baseline-aligned. v->fVAlign = v->fBox.isEmpty() ? Shaper::VAlign::kTopBaseline : Shaper::VAlign::kTop; diff --git a/modules/skunicode/include/SkUnicode.h b/modules/skunicode/include/SkUnicode.h index d3a32e1b27..280bb913a3 100644 --- a/modules/skunicode/include/SkUnicode.h +++ b/modules/skunicode/include/SkUnicode.h @@ -116,6 +116,7 @@ class SKUNICODE_API SkUnicode { virtual bool isWhitespace(SkUnichar utf8) = 0; virtual bool isSpace(SkUnichar utf8) = 0; virtual SkString convertUtf16ToUtf8(const std::u16string& utf16) = 0; + virtual SkString toUpper(const SkString&) = 0; // Methods used in SkShaper and SkText virtual std::unique_ptr makeBidiIterator diff --git a/modules/skunicode/src/SkUnicode_icu.cpp b/modules/skunicode/src/SkUnicode_icu.cpp index 4d5b960da1..7b33f2ac4c 100644 --- a/modules/skunicode/src/SkUnicode_icu.cpp +++ b/modules/skunicode/src/SkUnicode_icu.cpp @@ -498,6 +498,37 @@ public: } } + SkString toUpper(const SkString& str) override { + // Convert to UTF16 since that's what ICU wants. + std::unique_ptr str16; + const auto str16len = utf8ToUtf16(str.c_str(), str.size(), &str16); + if (str16len <= 0) { + return SkString(); + } + + UErrorCode icu_err = U_ZERO_ERROR; + const auto upper16len = sk_u_strToUpper(nullptr, 0, (UChar*)(str16.get()), str16len, + nullptr, &icu_err); + if (icu_err != U_BUFFER_OVERFLOW_ERROR || upper16len <= 0) { + return SkString(); + } + + SkAutoSTArray<128, uint16_t> upper16(upper16len); + icu_err = U_ZERO_ERROR; + sk_u_strToUpper((UChar*)(upper16.get()), SkToS32(upper16.size()), + (UChar*)(str16.get()), str16len, + nullptr, &icu_err); + SkASSERT(!U_FAILURE(icu_err)); + + // ... and back to utf8 'cause that's what we want. + std::unique_ptr upper8; + auto upper8len = utf16ToUtf8(upper16.data(), upper16.size(), &upper8); + + return upper8len >= 0 + ? SkString(upper8.get(), upper8len) + : SkString(); + } + bool getBidiRegions(const char utf8[], int utf8Units, TextDirection dir, diff --git a/modules/skunicode/src/SkUnicode_icu.h b/modules/skunicode/src/SkUnicode_icu.h index ece8abd5f2..6eb58c3075 100644 --- a/modules/skunicode/src/SkUnicode_icu.h +++ b/modules/skunicode/src/SkUnicode_icu.h @@ -22,6 +22,7 @@ SKICU_FUNC(u_iscntrl) \ SKICU_FUNC(u_isspace) \ SKICU_FUNC(u_isWhitespace) \ + SKICU_FUNC(u_strToUpper) \ SKICU_FUNC(ubidi_close) \ SKICU_FUNC(ubidi_getLength) \ SKICU_FUNC(ubidi_getLevelAt) \ diff --git a/resources/skottie/skottie-text-allcaps.json b/resources/skottie/skottie-text-allcaps.json new file mode 100644 index 0000000000..df377628bd --- /dev/null +++ b/resources/skottie/skottie-text-allcaps.json @@ -0,0 +1 @@ +{"assets":[],"ddd":0,"fonts":{"list":[{"ascent":71.5988159179688,"fClass":"","fFamily":"Arial","fName":"ArialMT","fPath":"","fStyle":"Regular","fWeight":"","origin":0}]},"fr":60,"h":500,"ip":0,"layers":[{"ao":0,"bm":0,"ddd":0,"ind":1,"ip":0,"ks":{"a":{"a":0,"ix":1,"k":[0,0,0],"l":2},"o":{"a":0,"ix":11,"k":100},"p":{"a":0,"ix":2,"k":[245,226.5,0],"l":2},"r":{"a":0,"ix":10,"k":0},"s":{"a":0,"ix":6,"k":[100,100,100],"l":2}},"nm":"Foo Bar - allcaps","op":600,"sr":1,"st":0,"t":{"a":[],"d":{"k":[{"s":{"ca":0,"f":"ArialMT","fc":[0.706,0,1],"j":2,"lh":75.6,"ls":0,"mc":100,"mf":10,"ps":[-164,-141.5],"rs":1,"s":63,"sz":[354,335],"t":"Hello there, κεφαλαίο κείμενο!","tr":0,"vj":1,"xf":100},"t":0},{"s":{"ca":1,"f":"ArialMT","fc":[0.706,0,1],"j":2,"lh":75.6,"ls":0,"mc":100,"mf":10,"ps":[-164,-141.5],"rs":1,"s":63,"sz":[354,335],"t":"Hello there, κεφαλαίο κείμενο!","tr":0,"vj":1,"xf":100},"t":150},{"s":{"ca":0,"f":"ArialMT","fc":[0.706,0,1],"j":2,"lh":75.6,"ls":0,"mc":100,"mf":10,"ps":[-164,-141.5],"rs":1,"s":63,"sz":[354,335],"t":"Hello there, κεφαλαίο κείμενο!","tr":0,"vj":1,"xf":100},"t":300},{"s":{"ca":1,"f":"ArialMT","fc":[0.706,0,1],"j":2,"lh":75.6,"ls":0,"mc":100,"mf":10,"ps":[-164,-141.5],"rs":1,"s":63,"sz":[354,335],"t":"Hello there, κεφαλαίο κείμενο!","tr":0,"vj":1,"xf":100},"t":450}]},"m":{"a":{"a":0,"ix":2,"k":[0,0]},"g":1},"p":{}},"ty":5}],"markers":[],"nm":"allcaps","op":600,"v":"5.7.11","w":500}