Skips in underline decorations
Bug: skia:10166 Change-Id: Ib1d71f69d8647840f71c8599ca3517cb575bf945 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/287620 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
parent
041232a789
commit
18db52f2ee
@ -50,6 +50,8 @@ constexpr TextDecoration AllTextDecorations[] = {
|
||||
|
||||
enum TextDecorationStyle { kSolid, kDouble, kDotted, kDashed, kWavy };
|
||||
|
||||
enum TextDecorationMode { kGaps, kThrough };
|
||||
|
||||
enum StyleType {
|
||||
kNone,
|
||||
kAllAttributes,
|
||||
@ -64,12 +66,14 @@ enum StyleType {
|
||||
|
||||
struct Decoration {
|
||||
TextDecoration fType;
|
||||
TextDecorationMode fMode;
|
||||
SkColor fColor;
|
||||
TextDecorationStyle fStyle;
|
||||
SkScalar fThicknessMultiplier;
|
||||
|
||||
bool operator==(const Decoration& other) const {
|
||||
return this->fType == other.fType &&
|
||||
this->fMode == other.fMode &&
|
||||
this->fColor == other.fColor &&
|
||||
this->fStyle == other.fStyle &&
|
||||
this->fThicknessMultiplier == other.fThicknessMultiplier;
|
||||
@ -181,12 +185,14 @@ public:
|
||||
// Decorations
|
||||
Decoration getDecoration() const { return fDecoration; }
|
||||
TextDecoration getDecorationType() const { return fDecoration.fType; }
|
||||
TextDecorationMode getDecorationMode() const { return fDecoration.fMode; }
|
||||
SkColor getDecorationColor() const { return fDecoration.fColor; }
|
||||
TextDecorationStyle getDecorationStyle() const { return fDecoration.fStyle; }
|
||||
SkScalar getDecorationThicknessMultiplier() const {
|
||||
return fDecoration.fThicknessMultiplier;
|
||||
}
|
||||
void setDecoration(TextDecoration decoration) { fDecoration.fType = decoration; }
|
||||
void setDecorationMode(TextDecorationMode mode) { fDecoration.fMode = mode; }
|
||||
void setDecorationStyle(TextDecorationStyle style) { fDecoration.fStyle = style; }
|
||||
void setDecorationColor(SkColor color) { fDecoration.fColor = color; }
|
||||
void setDecorationThicknessMultiplier(SkScalar m) { fDecoration.fThicknessMultiplier = m; }
|
||||
|
@ -19,6 +19,8 @@ skparagraph_public = [
|
||||
]
|
||||
|
||||
skparagraph_sources = [
|
||||
"$_src/Decorations.cpp",
|
||||
"$_src/Decorations.h",
|
||||
"$_src/FontCollection.cpp",
|
||||
"$_src/Iterators.h",
|
||||
"$_src/OneLineShaper.cpp",
|
||||
|
238
modules/skparagraph/src/Decorations.cpp
Normal file
238
modules/skparagraph/src/Decorations.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
#include "include/effects/SkDashPathEffect.h"
|
||||
#include "include/effects/SkDiscretePathEffect.h"
|
||||
#include "modules/skparagraph/src/Decorations.h"
|
||||
|
||||
namespace skia {
|
||||
namespace textlayout {
|
||||
|
||||
static const float kDoubleDecorationSpacing = 3.0f;
|
||||
void Decorations::paint(SkCanvas* canvas, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline, SkScalar shift) {
|
||||
if (textStyle.getDecorationType() == TextDecoration::kNoDecoration) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get thickness and position
|
||||
calculateThickness(textStyle, context.run->font().refTypeface());
|
||||
|
||||
for (auto decoration : AllTextDecorations) {
|
||||
if ((textStyle.getDecorationType() & decoration) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
calculatePosition(decoration, context.run->correctAscent());
|
||||
|
||||
calculatePaint(textStyle);
|
||||
|
||||
auto width = context.clip.width();
|
||||
SkScalar x = context.clip.left();
|
||||
SkScalar y = context.clip.top() + fPosition;
|
||||
|
||||
bool drawGaps = textStyle.getDecorationMode() == TextDecorationMode::kGaps &&
|
||||
textStyle.getDecorationType() == TextDecoration::kUnderline;
|
||||
|
||||
switch (textStyle.getDecorationStyle()) {
|
||||
case TextDecorationStyle::kWavy: {
|
||||
calculateWaves(textStyle, context.clip);
|
||||
fPath.offset(x, y);
|
||||
canvas->drawPath(fPath, fPaint);
|
||||
break;
|
||||
}
|
||||
case TextDecorationStyle::kDouble: {
|
||||
SkScalar bottom = y + kDoubleDecorationSpacing;
|
||||
if (drawGaps) {
|
||||
SkScalar left = x - context.fTextShift;
|
||||
canvas->translate(context.fTextShift, 0);
|
||||
calculateGaps(context, left, left + width, y, y + fThickness, baseline, fThickness);
|
||||
canvas->drawPath(fPath, fPaint);
|
||||
calculateGaps(context, left, left + width, bottom, bottom + fThickness, baseline, fThickness);
|
||||
canvas->drawPath(fPath, fPaint);
|
||||
} else {
|
||||
canvas->drawLine(x, y, x + width, y, fPaint);
|
||||
canvas->drawLine(x, bottom, x + width, bottom, fPaint);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TextDecorationStyle::kDashed:
|
||||
case TextDecorationStyle::kDotted:
|
||||
if (drawGaps) {
|
||||
SkScalar left = x - context.fTextShift;
|
||||
canvas->translate(context.fTextShift, 0);
|
||||
calculateGaps(context, left, left + width, y, y + fThickness, baseline, 0);
|
||||
canvas->drawPath(fPath, fPaint);
|
||||
} else {
|
||||
canvas->drawLine(x, y, x + width, y, fPaint);
|
||||
}
|
||||
break;
|
||||
case TextDecorationStyle::kSolid:
|
||||
if (drawGaps) {
|
||||
SkScalar left = x - context.fTextShift;
|
||||
canvas->translate(context.fTextShift, 0);
|
||||
calculateGaps(context, left, left + width, y, y + fThickness, baseline, fThickness);
|
||||
canvas->drawPath(fPath, fPaint);
|
||||
} else {
|
||||
canvas->drawLine(x, y, x + width, y, fPaint);
|
||||
}
|
||||
break;
|
||||
default:break;
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
void Decorations::calculateGaps(const TextLine::ClipContext& context, SkScalar x0, SkScalar x1, SkScalar y0, SkScalar y1, SkScalar baseline, SkScalar halo) {
|
||||
|
||||
fPath.reset();
|
||||
|
||||
// Create a special textblob for decorations
|
||||
SkTextBlobBuilder builder;
|
||||
context.run->copyTo(builder,
|
||||
SkToU32(context.pos),
|
||||
context.size,
|
||||
SkVector::Make(0, baseline));
|
||||
auto blob = builder.make();
|
||||
|
||||
const SkScalar bounds[2] = {y0, y1};
|
||||
auto count = blob->getIntercepts(bounds, nullptr, &fPaint);
|
||||
SkTArray<SkScalar> intersections(count);
|
||||
intersections.resize(count);
|
||||
blob->getIntercepts(bounds, intersections.data(), &fPaint);
|
||||
|
||||
auto start = x0;
|
||||
fPath.moveTo({x0, y0});
|
||||
for (int i = 0; i < intersections.count(); i += 2) {
|
||||
auto end = intersections[i] - halo;
|
||||
if (end - start >= halo) {
|
||||
start = intersections[i + 1] + halo;
|
||||
fPath.lineTo(end, y0).moveTo(start, y0);
|
||||
}
|
||||
}
|
||||
if (!intersections.empty() && (x1 - start > halo)) {
|
||||
fPath.lineTo(x1, y0);
|
||||
}
|
||||
}
|
||||
|
||||
// This is how flutter calculates the thickness
|
||||
void Decorations::calculateThickness(TextStyle textStyle, sk_sp<SkTypeface> typeface) {
|
||||
|
||||
textStyle.setTypeface(typeface);
|
||||
textStyle.getFontMetrics(&fFontMetrics);
|
||||
|
||||
fThickness = textStyle.getFontSize() / 14.0f;
|
||||
|
||||
if ((fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kUnderlineThicknessIsValid_Flag) &&
|
||||
fFontMetrics.fUnderlineThickness > 0) {
|
||||
fThickness = fFontMetrics.fUnderlineThickness;
|
||||
}
|
||||
|
||||
if (textStyle.getDecorationType() == TextDecoration::kLineThrough) {
|
||||
if ((fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag) &&
|
||||
fFontMetrics.fStrikeoutThickness > 0) {
|
||||
fThickness = fFontMetrics.fStrikeoutThickness;
|
||||
}
|
||||
}
|
||||
fThickness *= textStyle.getDecorationThicknessMultiplier();
|
||||
}
|
||||
|
||||
// This is how flutter calculates the positioning
|
||||
void Decorations::calculatePosition(TextDecoration decoration, SkScalar ascent) {
|
||||
switch (decoration) {
|
||||
case TextDecoration::kUnderline:
|
||||
if ((fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kUnderlinePositionIsValid_Flag) &&
|
||||
fFontMetrics.fUnderlinePosition > 0) {
|
||||
fPosition = fFontMetrics.fUnderlinePosition;
|
||||
} else {
|
||||
fPosition = fThickness;
|
||||
}
|
||||
fPosition -= ascent;
|
||||
break;
|
||||
case TextDecoration::kOverline:
|
||||
fPosition = 0;
|
||||
break;
|
||||
case TextDecoration::kLineThrough: {
|
||||
fPosition = (fFontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag)
|
||||
? fFontMetrics.fStrikeoutPosition
|
||||
: fFontMetrics.fXHeight / -2;
|
||||
fPosition -= ascent;
|
||||
break;
|
||||
}
|
||||
default:SkASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Decorations::calculatePaint(const TextStyle& textStyle) {
|
||||
|
||||
fPaint.reset();
|
||||
|
||||
fPaint.setStyle(SkPaint::kStroke_Style);
|
||||
if (textStyle.getDecorationColor() == SK_ColorTRANSPARENT) {
|
||||
fPaint.setColor(textStyle.getColor());
|
||||
} else {
|
||||
fPaint.setColor(textStyle.getDecorationColor());
|
||||
}
|
||||
fPaint.setAntiAlias(true);
|
||||
fPaint.setStrokeWidth(fThickness);
|
||||
|
||||
SkScalar scaleFactor = textStyle.getFontSize() / 14.f;
|
||||
switch (textStyle.getDecorationStyle()) {
|
||||
// Note: the intervals are scaled by the thickness of the line, so it is
|
||||
// possible to change spacing by changing the decoration_thickness
|
||||
// property of TextStyle.
|
||||
case TextDecorationStyle::kDotted: {
|
||||
const SkScalar intervals[] = {1.0f * scaleFactor, 1.5f * scaleFactor,
|
||||
1.0f * scaleFactor, 1.5f * scaleFactor};
|
||||
size_t count = sizeof(intervals) / sizeof(intervals[0]);
|
||||
fPaint.setPathEffect(SkPathEffect::MakeCompose(
|
||||
SkDashPathEffect::Make(intervals, (int32_t)count, 0.0f),
|
||||
SkDiscretePathEffect::Make(0, 0)));
|
||||
break;
|
||||
}
|
||||
// Note: the intervals are scaled by the thickness of the line, so it is
|
||||
// possible to change spacing by changing the decoration_thickness
|
||||
// property of TextStyle.
|
||||
case TextDecorationStyle::kDashed: {
|
||||
const SkScalar intervals[] = {4.0f * scaleFactor, 2.0f * scaleFactor,
|
||||
4.0f * scaleFactor, 2.0f * scaleFactor};
|
||||
size_t count = sizeof(intervals) / sizeof(intervals[0]);
|
||||
fPaint.setPathEffect(SkPathEffect::MakeCompose(
|
||||
SkDashPathEffect::Make(intervals, (int32_t)count, 0.0f),
|
||||
SkDiscretePathEffect::Make(0, 0)));
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void Decorations::calculateWaves(const TextStyle& textStyle, SkRect clip) {
|
||||
|
||||
fPath.reset();
|
||||
int wave_count = 0;
|
||||
SkScalar x_start = 0;
|
||||
SkScalar quarterWave = fThickness;
|
||||
fPath.moveTo(0, 0);
|
||||
while (x_start + quarterWave * 2 < clip.width()) {
|
||||
fPath.rQuadTo(quarterWave,
|
||||
wave_count % 2 != 0 ? quarterWave : -quarterWave,
|
||||
quarterWave * 2,
|
||||
0);
|
||||
x_start += quarterWave * 2;
|
||||
++wave_count;
|
||||
}
|
||||
|
||||
// The rest of the wave
|
||||
auto remaining = clip.width() - x_start;
|
||||
if (remaining > 0) {
|
||||
double x1 = remaining / 2;
|
||||
double y1 = remaining / 2 * (wave_count % 2 == 0 ? -1 : 1);
|
||||
double x2 = remaining;
|
||||
double y2 = (remaining - remaining * remaining / (quarterWave * 2)) *
|
||||
(wave_count % 2 == 0 ? -1 : 1);
|
||||
fPath.rQuadTo(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
34
modules/skparagraph/src/Decorations.h
Normal file
34
modules/skparagraph/src/Decorations.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2020 Google LLC.
|
||||
#ifndef Decorations_DEFINED
|
||||
#define Decorations_DEFINED
|
||||
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "modules/skparagraph/include/TextStyle.h"
|
||||
#include "modules/skparagraph/src/TextLine.h"
|
||||
|
||||
namespace skia {
|
||||
namespace textlayout {
|
||||
|
||||
class Decorations {
|
||||
public:
|
||||
void paint(SkCanvas* canvas, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline, SkScalar shift);
|
||||
|
||||
private:
|
||||
|
||||
void calculateThickness(TextStyle textStyle, sk_sp<SkTypeface> typeface);
|
||||
void calculatePosition(TextDecoration decoration, SkScalar ascent);
|
||||
void calculatePaint(const TextStyle& textStyle);
|
||||
void calculateWaves(const TextStyle& textStyle, SkRect clip);
|
||||
void calculateGaps(const TextLine::ClipContext& context, SkScalar x0, SkScalar x1, SkScalar y0, SkScalar y1, SkScalar baseline, SkScalar halo);
|
||||
|
||||
SkScalar fThickness;
|
||||
SkScalar fPosition;
|
||||
|
||||
SkFontMetrics fFontMetrics;
|
||||
SkPaint fPaint;
|
||||
SkPath fPath;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
@ -184,6 +184,7 @@ public:
|
||||
void resetJustificationShifts() {
|
||||
fJustificationShifts.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ParagraphImpl;
|
||||
friend class TextLine;
|
||||
@ -212,7 +213,6 @@ private:
|
||||
SkSTArray<128, SkPoint, true> fOffsets;
|
||||
SkSTArray<128, uint32_t, true> fClusterIndexes;
|
||||
SkSTArray<128, SkRect, true> fBounds;
|
||||
|
||||
SkSTArray<128, SkScalar, true> fShifts; // For formatting (letter/word spacing)
|
||||
bool fSpaced;
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "modules/skparagraph/src/TextLine.h"
|
||||
#include <unicode/brkiter.h>
|
||||
#include <unicode/ubidi.h>
|
||||
#include "modules/skparagraph/src/Decorations.h"
|
||||
#include "modules/skparagraph/src/ParagraphImpl.h"
|
||||
|
||||
#include "include/core/SkMaskFilter.h"
|
||||
@ -388,170 +389,12 @@ void TextLine::paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyl
|
||||
}
|
||||
}
|
||||
|
||||
static const float kDoubleDecorationSpacing = 3.0f;
|
||||
void TextLine::paintDecorations(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
|
||||
if (style.getDecorationType() == TextDecoration::kNoDecoration) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
Decorations decorations;
|
||||
SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
|
||||
decorations.paint(canvas, style, context, correctedBaseline, this->fShift);
|
||||
|
||||
|
||||
SkPaint paint;
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
if (style.getDecorationColor() == SK_ColorTRANSPARENT) {
|
||||
paint.setColor(style.getColor());
|
||||
} else {
|
||||
paint.setColor(style.getDecorationColor());
|
||||
}
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
SkFontMetrics fontMetrics;
|
||||
TextStyle combined = style;
|
||||
combined.setTypeface(context.run->fFont.refTypeface());
|
||||
combined.getFontMetrics(&fontMetrics);
|
||||
SkScalar thickness;
|
||||
if ((fontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kUnderlineThicknessIsValid_Flag) &&
|
||||
fontMetrics.fUnderlineThickness > 0) {
|
||||
thickness = fontMetrics.fUnderlineThickness;
|
||||
} else {
|
||||
thickness = style.getFontSize() / 14.0f;
|
||||
}
|
||||
|
||||
paint.setStrokeWidth(thickness * style.getDecorationThicknessMultiplier());
|
||||
|
||||
for (auto decoration : AllTextDecorations) {
|
||||
if ((style.getDecorationType() & decoration) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SkScalar position = 0;
|
||||
switch (decoration) {
|
||||
case TextDecoration::kUnderline:
|
||||
if ((fontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kUnderlinePositionIsValid_Flag) &&
|
||||
fontMetrics.fUnderlinePosition > 0) {
|
||||
position = fontMetrics.fUnderlinePosition;
|
||||
} else {
|
||||
position = thickness;
|
||||
}
|
||||
position += - context.run->correctAscent();
|
||||
break;
|
||||
case TextDecoration::kOverline:
|
||||
position = 0;
|
||||
break;
|
||||
case TextDecoration::kLineThrough: {
|
||||
if ((fontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag) &&
|
||||
fontMetrics.fStrikeoutThickness > 0) {
|
||||
paint.setStrokeWidth(fontMetrics.fStrikeoutThickness * style.getDecorationThicknessMultiplier());
|
||||
}
|
||||
position = (fontMetrics.fFlags & SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag)
|
||||
? fontMetrics.fStrikeoutPosition
|
||||
: fontMetrics.fXHeight / -2;
|
||||
position += - context.run->correctAscent();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SkASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
auto width = context.clip.width();
|
||||
SkScalar x = context.clip.left();
|
||||
SkScalar y = context.clip.top() + position;
|
||||
|
||||
// Decoration paint (for now) and/or path
|
||||
SkPath path;
|
||||
this->computeDecorationPaint(paint, context.clip, style, thickness, path);
|
||||
|
||||
switch (style.getDecorationStyle()) {
|
||||
case TextDecorationStyle::kWavy:
|
||||
path.offset(x, y);
|
||||
canvas->drawPath(path, paint);
|
||||
break;
|
||||
case TextDecorationStyle::kDouble: {
|
||||
canvas->drawLine(x, y, x + width, y, paint);
|
||||
SkScalar bottom = y + kDoubleDecorationSpacing;
|
||||
canvas->drawLine(x, bottom, x + width, bottom, paint);
|
||||
break;
|
||||
}
|
||||
case TextDecorationStyle::kDashed:
|
||||
case TextDecorationStyle::kDotted:
|
||||
case TextDecorationStyle::kSolid:
|
||||
canvas->drawLine(x, y, x + width, y, paint);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
void TextLine::computeDecorationPaint(SkPaint& paint,
|
||||
SkRect clip,
|
||||
const TextStyle& style,
|
||||
SkScalar thickness,
|
||||
SkPath& path) const {
|
||||
SkScalar scaleFactor = style.getFontSize() / 14.f;
|
||||
|
||||
switch (style.getDecorationStyle()) {
|
||||
case TextDecorationStyle::kSolid:
|
||||
break;
|
||||
|
||||
case TextDecorationStyle::kDouble:
|
||||
break;
|
||||
|
||||
// Note: the intervals are scaled by the thickness of the line, so it is
|
||||
// possible to change spacing by changing the decoration_thickness
|
||||
// property of TextStyle.
|
||||
case TextDecorationStyle::kDotted: {
|
||||
const SkScalar intervals[] = {1.0f * scaleFactor, 1.5f * scaleFactor,
|
||||
1.0f * scaleFactor, 1.5f * scaleFactor};
|
||||
size_t count = sizeof(intervals) / sizeof(intervals[0]);
|
||||
paint.setPathEffect(SkPathEffect::MakeCompose(
|
||||
SkDashPathEffect::Make(intervals, (int32_t)count, 0.0f),
|
||||
SkDiscretePathEffect::Make(0, 0)));
|
||||
break;
|
||||
}
|
||||
// Note: the intervals are scaled by the thickness of the line, so it is
|
||||
// possible to change spacing by changing the decoration_thickness
|
||||
// property of TextStyle.
|
||||
case TextDecorationStyle::kDashed: {
|
||||
const SkScalar intervals[] = {4.0f * scaleFactor, 2.0f * scaleFactor,
|
||||
4.0f * scaleFactor, 2.0f * scaleFactor};
|
||||
size_t count = sizeof(intervals) / sizeof(intervals[0]);
|
||||
paint.setPathEffect(SkPathEffect::MakeCompose(
|
||||
SkDashPathEffect::Make(intervals, (int32_t)count, 0.0f),
|
||||
SkDiscretePathEffect::Make(0, 0)));
|
||||
break;
|
||||
}
|
||||
case TextDecorationStyle::kWavy: {
|
||||
int wave_count = 0;
|
||||
SkScalar x_start = 0;
|
||||
SkScalar quarterWave = thickness * style.getDecorationThicknessMultiplier();
|
||||
path.moveTo(0, 0);
|
||||
while (x_start + quarterWave * 2 < clip.width()) {
|
||||
path.rQuadTo(quarterWave,
|
||||
wave_count % 2 != 0 ? quarterWave : -quarterWave,
|
||||
quarterWave * 2,
|
||||
0);
|
||||
x_start += quarterWave * 2;
|
||||
++wave_count;
|
||||
}
|
||||
|
||||
// The rest of the wave
|
||||
auto remaining = clip.width() - x_start;
|
||||
if (remaining > 0) {
|
||||
double x1 = remaining / 2;
|
||||
double y1 = remaining / 2 * (wave_count % 2 == 0 ? -1 : 1);
|
||||
double x2 = remaining;
|
||||
double y2 = (remaining - remaining * remaining / (quarterWave * 2)) *
|
||||
(wave_count % 2 == 0 ? -1 : 1);
|
||||
path.rQuadTo(x1, y1, x2, y2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextLine::justify(SkScalar maxWidth) {
|
||||
|
@ -116,13 +116,6 @@ private:
|
||||
void paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
void paintDecorations(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
|
||||
|
||||
void computeDecorationPaint(SkPaint& paint, SkRect clip, const TextStyle& style, SkScalar thickness,
|
||||
SkPath& path) const;
|
||||
|
||||
bool contains(const Cluster* cluster) const {
|
||||
return fTextRange.contains(cluster->textRange());
|
||||
}
|
||||
|
||||
void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift);
|
||||
|
||||
ParagraphImpl* fMaster;
|
||||
|
@ -15,6 +15,7 @@ TextStyle::TextStyle() : fFontStyle() {
|
||||
// value to indicate no decoration color was set.
|
||||
fDecoration.fColor = SK_ColorTRANSPARENT;
|
||||
fDecoration.fStyle = TextDecorationStyle::kSolid;
|
||||
fDecoration.fMode = TextDecorationMode::kGaps;
|
||||
// Thickness is applied as a multiplier to the default thickness of the font.
|
||||
fDecoration.fThicknessMultiplier = 1.0;
|
||||
fFontSize = 14.0;
|
||||
|
@ -2538,6 +2538,68 @@ private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
class ParagraphView38 : public ParagraphView_Base {
|
||||
protected:
|
||||
SkString name() override { return SkString("Paragraph38"); }
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
|
||||
auto fontCollection = sk_make_sp<FontCollection>();
|
||||
fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
|
||||
fontCollection->enableFontFallback();
|
||||
|
||||
ParagraphStyle paragraph_style;
|
||||
paragraph_style.setTextAlign(TextAlign::kLeft);
|
||||
ParagraphBuilderImpl builder(paragraph_style, fontCollection);
|
||||
TextStyle text_style;
|
||||
text_style.setColor(SK_ColorDKGRAY);
|
||||
text_style.setFontFamilies({SkString("Roboto")});
|
||||
text_style.setFontSize(40);
|
||||
text_style.setDecoration(TextDecoration::kUnderline);
|
||||
|
||||
text_style.setDecorationMode(TextDecorationMode::kThrough);
|
||||
text_style.setDecorationStyle(TextDecorationStyle::kDouble);
|
||||
text_style.setDecorationColor(SK_ColorBLUE);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Double underline: {opopo}\n");
|
||||
|
||||
text_style.setDecorationMode(TextDecorationMode::kGaps);
|
||||
text_style.setDecorationStyle(TextDecorationStyle::kDouble);
|
||||
text_style.setDecorationColor(SK_ColorBLUE);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Double underline: {opopo}\n");
|
||||
|
||||
text_style.setDecorationStyle(TextDecorationStyle::kDotted);
|
||||
text_style.setDecorationColor(SK_ColorRED);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Dotted underline: {ijiji}\n");
|
||||
|
||||
text_style.setDecorationStyle(TextDecorationStyle::kSolid);
|
||||
text_style.setDecorationColor(SK_ColorGREEN);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Solid underline: {rqrqr}\n");
|
||||
|
||||
text_style.setDecorationStyle(TextDecorationStyle::kDashed);
|
||||
text_style.setDecorationColor(SK_ColorMAGENTA);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Dashed underline: {zyzyz}\n");
|
||||
|
||||
text_style.setDecorationStyle(TextDecorationStyle::kWavy);
|
||||
text_style.setDecorationColor(SK_ColorCYAN);
|
||||
builder.pushStyle(text_style);
|
||||
builder.addText("Wavy underline: {does not skip}\n");
|
||||
|
||||
auto paragraph = builder.Build();
|
||||
paragraph->layout(width());
|
||||
paragraph->paint(canvas, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Sample INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
DEF_SAMPLE(return new ParagraphView1();)
|
||||
DEF_SAMPLE(return new ParagraphView2();)
|
||||
@ -2556,7 +2618,7 @@ DEF_SAMPLE(return new ParagraphView15();)
|
||||
DEF_SAMPLE(return new ParagraphView16();)
|
||||
DEF_SAMPLE(return new ParagraphView17();)
|
||||
DEF_SAMPLE(return new ParagraphView18();)
|
||||
DEF_SAMPLE(return new ParagraphView19();)
|
||||
//DEF_SAMPLE(return new ParagraphView19();)
|
||||
DEF_SAMPLE(return new ParagraphView20();)
|
||||
DEF_SAMPLE(return new ParagraphView21();)
|
||||
DEF_SAMPLE(return new ParagraphView22();)
|
||||
@ -2575,3 +2637,4 @@ DEF_SAMPLE(return new ParagraphView34();)
|
||||
DEF_SAMPLE(return new ParagraphView35();)
|
||||
DEF_SAMPLE(return new ParagraphView36();)
|
||||
DEF_SAMPLE(return new ParagraphView37();)
|
||||
DEF_SAMPLE(return new ParagraphView38();)
|
||||
|
Loading…
Reference in New Issue
Block a user