[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:
Florin Malita 2020-01-28 13:21:47 -05:00 committed by Skia Commit-Bot
parent faa34c536b
commit ad9110026b
8 changed files with 9504 additions and 61 deletions

View File

@ -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;

View File

@ -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

View File

@ -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
};

View File

@ -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 {

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}
}
}

File diff suppressed because it is too large Load Diff