[skottie] Separate text resize options
The sk_vj text property (Skottie extension) is currently mixing vertical alignment and resizing semantics into a single enum. This precludes certain valid combinations. Split the resize options into a separate enum (ResizePolicy), and ensure support for all combinations. Before: "sk_vj": 0 -> Shaper::VAlign::kVisualTop "sk_vj": 1 -> Shaper::VAlign::kVisualCenter "sk_vj": 2 -> Shaper::VAlign::kVisualBottom "sk_vj": 3 -> Shaper::VAlign::kVisualResizeToFit "sk_vj": 4 -> Shaper::VAlign::kVisualDownscaleToFit After: "sk_vj": 0 -> Shaper::VAlign::kVisualTop "sk_vj": 1 -> Shaper::VAlign::kVisualCenter "sk_vj": 2 -> Shaper::VAlign::kVisualBottom "sk_rs": 0 -> Shaper::ResizePolicy::kNone "sk_rs": 1 -> Shaper::ResizePolicy::kScaleToFit "sk_rs": 2 -> Shaper::ResizePolicy::kDownscaleToFit Bug: skia:9809, skia:9810 Change-Id: I631ae1fa31a9bc9c6958bb480354138591d504ff Reviewed-on: https://skia-review.googlesource.com/c/skia/+/267040 Reviewed-by: Ben Wagner <bungeman@google.com> Reviewed-by: Isabel Ren <isabelren@google.com> Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
faa34c536b
commit
ad9110026b
@ -32,19 +32,20 @@ using ColorPropertyValue = SkColor;
|
||||
using OpacityPropertyValue = float;
|
||||
|
||||
struct TextPropertyValue {
|
||||
sk_sp<SkTypeface> fTypeface;
|
||||
SkString fText;
|
||||
float fTextSize = 0,
|
||||
fStrokeWidth = 0,
|
||||
fLineHeight = 0,
|
||||
fAscent = 0;
|
||||
SkTextUtils::Align fHAlign = SkTextUtils::kLeft_Align;
|
||||
Shaper::VAlign fVAlign = Shaper::VAlign::kTop;
|
||||
SkRect fBox = SkRect::MakeEmpty();
|
||||
SkColor fFillColor = SK_ColorTRANSPARENT,
|
||||
fStrokeColor = SK_ColorTRANSPARENT;
|
||||
bool fHasFill = false,
|
||||
fHasStroke = false;
|
||||
sk_sp<SkTypeface> fTypeface;
|
||||
SkString fText;
|
||||
float fTextSize = 0,
|
||||
fStrokeWidth = 0,
|
||||
fLineHeight = 0,
|
||||
fAscent = 0;
|
||||
SkTextUtils::Align fHAlign = SkTextUtils::kLeft_Align;
|
||||
Shaper::VAlign fVAlign = Shaper::VAlign::kTop;
|
||||
Shaper::ResizePolicy fResize = Shaper::ResizePolicy::kNone;
|
||||
SkRect fBox = SkRect::MakeEmpty();
|
||||
SkColor fFillColor = SK_ColorTRANSPARENT,
|
||||
fStrokeColor = SK_ColorTRANSPARENT;
|
||||
bool fHasFill = false,
|
||||
fHasStroke = false;
|
||||
|
||||
bool operator==(const TextPropertyValue& other) const;
|
||||
bool operator!=(const TextPropertyValue& other) const;
|
||||
|
@ -22,6 +22,7 @@ bool TextPropertyValue::operator==(const TextPropertyValue& other) const {
|
||||
&& fLineHeight == other.fLineHeight
|
||||
&& fHAlign == other.fHAlign
|
||||
&& fVAlign == other.fVAlign
|
||||
&& fResize == other.fResize
|
||||
&& fBox == other.fBox
|
||||
&& fFillColor == other.fFillColor
|
||||
&& fStrokeColor == other.fStrokeColor
|
||||
|
@ -295,6 +295,7 @@ DEF_TEST(Skottie_Properties, reporter) {
|
||||
0,
|
||||
SkTextUtils::kLeft_Align,
|
||||
Shaper::VAlign::kTopBaseline,
|
||||
Shaper::ResizePolicy::kNone,
|
||||
SkRect::MakeEmpty(),
|
||||
SK_ColorTRANSPARENT,
|
||||
SK_ColorTRANSPARENT,
|
||||
@ -440,6 +441,7 @@ DEF_TEST(Skottie_Shaper_HAlign, reporter) {
|
||||
0,
|
||||
talign.align,
|
||||
skottie::Shaper::VAlign::kTopBaseline,
|
||||
skottie::Shaper::ResizePolicy::kNone,
|
||||
Shaper::Flags::kNone
|
||||
};
|
||||
|
||||
@ -505,6 +507,7 @@ DEF_TEST(Skottie_Shaper_VAlign, reporter) {
|
||||
0,
|
||||
SkTextUtils::Align::kCenter_Align,
|
||||
talign.align,
|
||||
skottie::Shaper::ResizePolicy::kNone,
|
||||
Shaper::Flags::kNone
|
||||
};
|
||||
|
||||
@ -541,6 +544,7 @@ DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) {
|
||||
0,
|
||||
SkTextUtils::Align::kCenter_Align,
|
||||
Shaper::VAlign::kTop,
|
||||
skottie::Shaper::ResizePolicy::kNone,
|
||||
Shaper::Flags::kNone
|
||||
};
|
||||
|
||||
@ -635,6 +639,7 @@ DEF_TEST(Skottie_Shaper_ExplicitFontMgr, reporter) {
|
||||
0,
|
||||
SkTextUtils::Align::kCenter_Align,
|
||||
Shaper::VAlign::kTop,
|
||||
Shaper::ResizePolicy::kNone,
|
||||
Shaper::Flags::kNone
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "include/core/SkTextBlob.h"
|
||||
#include "include/private/SkTemplates.h"
|
||||
#include "modules/skshaper/include/SkShaper.h"
|
||||
#include "src/core/SkTLazy.h"
|
||||
#include "src/core/SkTextBlobPriv.h"
|
||||
#include "src/utils/SkUTF.h"
|
||||
|
||||
@ -172,7 +173,8 @@ public:
|
||||
return box;
|
||||
};
|
||||
|
||||
SkASSERT(!shaped_size || fDesc.fVAlign == Shaper::VAlign::kVisualCenter);
|
||||
// Only compute the extent box when needed.
|
||||
SkTLazy<SkRect> ebox;
|
||||
|
||||
// Perform additional adjustments based on VAlign.
|
||||
float v_offset = 0;
|
||||
@ -184,24 +186,26 @@ public:
|
||||
// Default behavior.
|
||||
break;
|
||||
case Shaper::VAlign::kVisualTop:
|
||||
v_offset = fBox.fTop - extent_box().fTop;
|
||||
ebox.init(extent_box());
|
||||
v_offset = fBox.fTop - ebox->fTop;
|
||||
break;
|
||||
case Shaper::VAlign::kVisualCenter:
|
||||
ebox.init(extent_box());
|
||||
v_offset = fBox.centerY() - ebox->centerY();
|
||||
break;
|
||||
case Shaper::VAlign::kVisualCenter: {
|
||||
const auto ebox = extent_box();
|
||||
v_offset = fBox.centerY() - ebox.centerY();
|
||||
if (shaped_size) {
|
||||
*shaped_size = SkSize::Make(ebox.width(), ebox.height());
|
||||
}
|
||||
} break;
|
||||
case Shaper::VAlign::kVisualBottom:
|
||||
v_offset = fBox.fBottom - extent_box().fBottom;
|
||||
break;
|
||||
case Shaper::VAlign::kVisualResizeToFit:
|
||||
case Shaper::VAlign::kVisualDownscaleToFit:
|
||||
SkASSERT(false);
|
||||
ebox.init(extent_box());
|
||||
v_offset = fBox.fBottom - ebox->fBottom;
|
||||
break;
|
||||
}
|
||||
|
||||
if (shaped_size) {
|
||||
if (!ebox.isValid()) {
|
||||
ebox.init(extent_box());
|
||||
}
|
||||
*shaped_size = SkSize::Make(ebox->width(), ebox->height());
|
||||
}
|
||||
|
||||
if (v_offset) {
|
||||
for (auto& fragment : fResult.fFragments) {
|
||||
fragment.fPos.fY += v_offset;
|
||||
@ -324,8 +328,6 @@ private:
|
||||
Shaper::Result ShapeImpl(const SkString& txt, const Shaper::TextDesc& desc,
|
||||
const SkRect& box, const sk_sp<SkFontMgr>& fontmgr,
|
||||
SkSize* shaped_size = nullptr) {
|
||||
SkASSERT(desc.fVAlign != Shaper::VAlign::kVisualResizeToFit);
|
||||
|
||||
const auto& is_line_break = [](SkUnichar uch) {
|
||||
// TODO: other explicit breaks?
|
||||
return uch == '\r';
|
||||
@ -349,8 +351,6 @@ Shaper::Result ShapeImpl(const SkString& txt, const Shaper::TextDesc& desc,
|
||||
|
||||
Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc,
|
||||
const SkRect& box, const sk_sp<SkFontMgr>& fontmgr) {
|
||||
SkASSERT(orig_desc.fVAlign == Shaper::VAlign::kVisualResizeToFit);
|
||||
|
||||
Shaper::Result best_result;
|
||||
|
||||
if (box.isEmpty() || orig_desc.fTextSize <= 0) {
|
||||
@ -358,7 +358,6 @@ Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc
|
||||
}
|
||||
|
||||
auto desc = orig_desc;
|
||||
desc.fVAlign = Shaper::VAlign::kVisualCenter;
|
||||
|
||||
float in_scale = 0, // maximum scale that fits inside
|
||||
out_scale = std::numeric_limits<float>::max(), // minimum scale that doesn't fit
|
||||
@ -402,35 +401,30 @@ Shaper::Result ShapeToFit(const SkString& txt, const Shaper::TextDesc& orig_desc
|
||||
|
||||
Shaper::Result Shaper::Shape(const SkString& txt, const TextDesc& desc, const SkPoint& point,
|
||||
const sk_sp<SkFontMgr>& fontmgr) {
|
||||
return (desc.fVAlign == VAlign::kVisualResizeToFit ||
|
||||
desc.fVAlign == VAlign::kVisualDownscaleToFit) // makes no sense in point mode
|
||||
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,
|
||||
const sk_sp<SkFontMgr>& fontmgr) {
|
||||
if (desc.fVAlign == VAlign::kVisualResizeToFit) {
|
||||
switch(desc.fResize) {
|
||||
case ResizePolicy::kNone:
|
||||
return ShapeImpl(txt, desc, box, fontmgr);
|
||||
case ResizePolicy::kScaleToFit:
|
||||
return ShapeToFit(txt, desc, box, fontmgr);
|
||||
}
|
||||
|
||||
if (desc.fVAlign == VAlign::kVisualDownscaleToFit) {
|
||||
auto adjusted_desc = desc;
|
||||
adjusted_desc.fVAlign = VAlign::kVisualCenter;
|
||||
|
||||
case ResizePolicy::kDownscaleToFit: {
|
||||
SkSize size;
|
||||
auto result = ShapeImpl(txt, adjusted_desc, box, fontmgr, &size);
|
||||
auto result = ShapeImpl(txt, desc, box, fontmgr, &size);
|
||||
|
||||
if (size.width() <= box.width() && size.height() <= box.height()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
adjusted_desc.fVAlign = VAlign::kVisualResizeToFit;
|
||||
|
||||
return ShapeToFit(txt, adjusted_desc, box, fontmgr);
|
||||
return (size.width() <= box.width() && size.height() <= box.height())
|
||||
? result
|
||||
: ShapeToFit(txt, desc, box, fontmgr);
|
||||
}
|
||||
}
|
||||
|
||||
return ShapeImpl(txt, desc, box, fontmgr);
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
SkRect Shaper::Result::computeVisualBounds() const {
|
||||
|
@ -61,12 +61,17 @@ public:
|
||||
kVisualCenter,
|
||||
// extent box bottom -> text box bottom
|
||||
kVisualBottom,
|
||||
};
|
||||
|
||||
enum class ResizePolicy : uint8_t {
|
||||
// Use the specified text size.
|
||||
kNone,
|
||||
// Resize the text such that the extent box fits (snuggly) in the text box,
|
||||
// both horizontally and vertically.
|
||||
kVisualResizeToFit,
|
||||
// Same kVisualResizeToFit if the text doesn't fit at the specified font size.
|
||||
// Otherwise, same as kVisualCenter.
|
||||
kVisualDownscaleToFit,
|
||||
kScaleToFit,
|
||||
// Same kScaleToFit if the text doesn't fit at the specified font size.
|
||||
// Otherwise, same as kNone.
|
||||
kDownscaleToFit,
|
||||
};
|
||||
|
||||
enum Flags : uint32_t {
|
||||
@ -84,6 +89,7 @@ public:
|
||||
fAscent;
|
||||
SkTextUtils::Align fHAlign;
|
||||
VAlign fVAlign;
|
||||
ResizePolicy fResize;
|
||||
uint32_t fFlags;
|
||||
};
|
||||
|
||||
|
@ -188,6 +188,7 @@ void TextAdapter::reshape() {
|
||||
fText->fAscent,
|
||||
fText->fHAlign,
|
||||
fText->fVAlign,
|
||||
fText->fResize,
|
||||
fAnimators.empty() ? Shaper::Flags::kNone : Shaper::Flags::kFragmentGlyphs,
|
||||
};
|
||||
const auto shape_result = Shaper::Shape(fText->fText, text_desc, fText->fBox, fFontMgr);
|
||||
|
@ -66,25 +66,48 @@ bool ValueTraits<TextValue>::FromJSON(const skjson::Value& jv,
|
||||
}
|
||||
}
|
||||
|
||||
// Skia resizing extension "sk_rs":
|
||||
static constexpr Shaper::ResizePolicy gResizeMap[] = {
|
||||
Shaper::ResizePolicy::kNone, // 'sk_rs': 0
|
||||
Shaper::ResizePolicy::kScaleToFit, // 'sk_rs': 1
|
||||
Shaper::ResizePolicy::kDownscaleToFit, // 'sk_rs': 2
|
||||
};
|
||||
v->fResize = gResizeMap[SkTMin<size_t>(ParseDefault<size_t>((*jtxt)["sk_rs"], 0),
|
||||
SK_ARRAY_COUNT(gResizeMap))];
|
||||
|
||||
// In point mode, the text is baseline-aligned.
|
||||
v->fVAlign = v->fBox.isEmpty() ? Shaper::VAlign::kTopBaseline
|
||||
: Shaper::VAlign::kTop;
|
||||
|
||||
// Skia vertical alignment extension "sk_vj":
|
||||
static constexpr Shaper::VAlign gVAlignMap[] = {
|
||||
Shaper::VAlign::kVisualTop, // 'sk_vj': 0
|
||||
Shaper::VAlign::kVisualCenter, // 'sk_vj': 1
|
||||
Shaper::VAlign::kVisualBottom, // 'sk_vj': 2
|
||||
Shaper::VAlign::kVisualResizeToFit, // 'sk_vj': 3
|
||||
Shaper::VAlign::kVisualDownscaleToFit, // 'sk_vj': 4
|
||||
Shaper::VAlign::kVisualTop, // 'sk_vj': 0
|
||||
Shaper::VAlign::kVisualCenter, // 'sk_vj': 1
|
||||
Shaper::VAlign::kVisualBottom, // 'sk_vj': 2
|
||||
};
|
||||
size_t sk_vj;
|
||||
if (Parse((*jtxt)["sk_vj"], &sk_vj)) {
|
||||
if (sk_vj < SK_ARRAY_COUNT(gVAlignMap)) {
|
||||
v->fVAlign = gVAlignMap[sk_vj];
|
||||
} else {
|
||||
abuilder->log(Logger::Level::kWarning, nullptr,
|
||||
"Ignoring unknown 'sk_vj' value: %zu", sk_vj);
|
||||
// Legacy sk_vj values.
|
||||
// TODO: remove after clients update.
|
||||
switch (sk_vj) {
|
||||
case 3:
|
||||
// 'sk_vj': 3 -> kVisualCenter/kScaleToFit
|
||||
v->fVAlign = Shaper::VAlign::kVisualCenter;
|
||||
v->fResize = Shaper::ResizePolicy::kScaleToFit;
|
||||
break;
|
||||
case 4:
|
||||
// 'sk_vj': 4 -> kVisualCenter/kDownscaleToFit
|
||||
v->fVAlign = Shaper::VAlign::kVisualCenter;
|
||||
v->fResize = Shaper::ResizePolicy::kDownscaleToFit;
|
||||
break;
|
||||
default:
|
||||
abuilder->log(Logger::Level::kWarning, nullptr,
|
||||
"Ignoring unknown 'sk_vj' value: %zu", sk_vj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
9412
resources/skottie/skottie-text-valign-scaletofit.json
Normal file
9412
resources/skottie/skottie-text-valign-scaletofit.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user