提交 a12d43dc 编写于 作者: H Hixie

Turn on wavy underlines. The waves aren't very pretty yet (they are too short...

Turn on wavy underlines. The waves aren't very pretty yet (they are too short somehow), I'll fix that in a subsequent CL.

I abstracted out the wavy underline code so that it doesn't duplicate the code for horizontal and vertical lines.

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1201503003.
上级 8f20fb6e
......@@ -796,10 +796,25 @@ static void adjustStepToDecorationLength(float& step, float& controlPointDistanc
controlPointDistance += adjustment;
}
struct CurveAlongX {
static inline float x(const FloatPoint& p) { return p.x(); }
static inline float y(const FloatPoint& p) { return p.y(); }
static inline FloatPoint p(float x, float y) { return FloatPoint(x, y); }
static inline void setX(FloatPoint& p, double x) { p.setX(x); }
};
struct CurveAlongY {
static inline float x(const FloatPoint& p) { return p.y(); }
static inline float y(const FloatPoint& p) { return p.x(); }
static inline FloatPoint p(float x, float y) { return FloatPoint(y, x); }
static inline void setX(FloatPoint& p, double x) { p.setY(x); }
};
/*
* Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis.
* The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
* form a diamond shape:
* Draw one cubic Bezier curve and repeat the same pattern along the
* the decoration's axis. The start point (p1), controlPoint1,
* controlPoint2 and end point (p2) of the Bezier curve form a diamond
* shape, as follows (the four points marked +):
*
* step
* |-----------|
......@@ -822,84 +837,62 @@ static void adjustStepToDecorationLength(float& step, float& controlPointDistanc
*
* |-----------|
* step
*
* strokeWavyTextDecorationInternal() takes two points, p1 and p2.
* These must be axis-aligned. If they are horizontally-aligned,
* specialize it with CurveAlongX; if they are vertically aligned,
* specialize it with CurveAlongY. The function is written as if it
* was doing everything along the X axis; CurveAlongY just flips the
* coordinates around.
*/
static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint p1, FloatPoint p2, float strokeThickness)
template <class Curve> static void strokeWavyTextDecorationInternal(GraphicsContext* context, FloatPoint p1, FloatPoint p2, float strokeThickness)
{
ASSERT(Curve::y(p1) == Curve::y(p2)); // verify that this is indeed axis-aligned
context->adjustLineToPixelBoundaries(p1, p2, strokeThickness, context->strokeStyle());
Path path;
path.moveTo(p1);
// Distance between decoration's axis and Bezier curve's control points.
// The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
// the actual curve passes approximately at half of that distance, that is 3 pixels.
// The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
// as strockThickness increases to make the curve looks better.
float controlPointDistance = 3 * std::max<float>(2, strokeThickness);
// Increment used to form the diamond shape between start point (p1), control
// points and end point (p2) along the axis of the decoration. Makes the
// curve wider as strockThickness increases to make the curve looks better.
float step = 2 * std::max<float>(2, strokeThickness);
bool isVerticalLine = (p1.x() == p2.x());
if (isVerticalLine) {
ASSERT(p1.x() == p2.x());
float xAxis = p1.x();
float y1;
float y2;
if (p1.y() < p2.y()) {
y1 = p1.y();
y2 = p2.y();
} else {
y1 = p2.y();
y2 = p1.y();
}
float controlPointDistance = 2 * strokeThickness;
float step = controlPointDistance;
adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
float yAxis = Curve::y(p1);
float x1;
float x2;
for (float y = y1; y + 2 * step <= y2;) {
controlPoint1.setY(y + step);
controlPoint2.setY(y + step);
y += 2 * step;
path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
}
if (Curve::x(p1) < Curve::x(p2)) {
x1 = Curve::x(p1);
x2 = Curve::x(p2);
} else {
ASSERT(p1.y() == p2.y());
float yAxis = p1.y();
float x1;
float x2;
if (p1.x() < p2.x()) {
x1 = p1.x();
x2 = p2.x();
} else {
x1 = p2.x();
x2 = p1.x();
}
x1 = Curve::x(p2);
x2 = Curve::x(p1);
}
adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
FloatPoint controlPoint1(0, yAxis + controlPointDistance);
FloatPoint controlPoint2(0, yAxis - controlPointDistance);
adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
for (float x = x1; x + 2 * step <= x2;) {
controlPoint1.setX(x + step);
controlPoint2.setX(x + step);
x += 2 * step;
path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
}
FloatPoint controlPoint1 = Curve::p(0, yAxis + controlPointDistance);
FloatPoint controlPoint2 = Curve::p(0, yAxis - controlPointDistance);
for (float x = x1; x + 2 * step <= x2;) {
Curve::setX(controlPoint1, x + step);
Curve::setX(controlPoint2, x + step);
x += 2 * step;
path.addBezierCurveTo(controlPoint1, controlPoint2, Curve::p(x, yAxis));
}
context->setShouldAntialias(true);
context->strokePath(path);
}
static void strokeWavyTextDecoration(GraphicsContext* context, FloatPoint p1, FloatPoint p2, float strokeThickness)
{
if (p1.y() == p2.y()) // horizontal line
strokeWavyTextDecorationInternal<CurveAlongX>(context, p1, p2, strokeThickness);
else // vertical line
strokeWavyTextDecorationInternal<CurveAlongY>(context, p1, p2, strokeThickness);
}
static bool shouldSetDecorationAntialias(TextDecorationStyle decorationStyle)
{
return decorationStyle == TextDecorationStyleDotted || decorationStyle == TextDecorationStyleDashed;
......@@ -962,7 +955,7 @@ void InlineTextBox::paintDecoration(GraphicsContext* context, const FloatPoint&
// Set the thick of the line to be 10% (or something else ?)of the computed font size and not less than 1px.
// Update Underline thickness, in case we have Faulty Font Metrics calculating underline thickness by old method.
float textDecorationThickness = styleToUse->fontMetrics().underlineThickness();
float textDecorationThickness = styleToUse->fontMetrics().underlineThickness(); // TODO(ianh): Make this author-controllable
int fontHeightInt = (int)(styleToUse->fontMetrics().floatHeight() + 0.5);
if ((textDecorationThickness == 0.f) || (textDecorationThickness >= (fontHeightInt >> 1)))
textDecorationThickness = std::max(1.f, styleToUse->computedFontSize() / 10.f);
......
......@@ -36,7 +36,7 @@ CSSAttributeCaseSensitivity status=experimental
CSSTouchActionDelay status=test
CSSViewport status=experimental
CSS3Text status=experimental
CSS3TextDecorations status=experimental
CSS3TextDecorations status=stable
CustomSchemeHandler depends_on=NavigatorContentUtils, status=experimental
Database status=stable
DeviceLight status=experimental
......
......@@ -44,7 +44,11 @@ HAL: This mission is too important for me to allow you to jeopardize it.''';
final TextStyle daveStyle = new TextStyle(color: Indigo[400]);
final TextStyle halStyle = new TextStyle(color: Red[400], fontFamily: "monospace");
final TextStyle boldStyle = const TextStyle(fontWeight: bold);
final TextStyle underlineStyle = const TextStyle(decoration: underline);
final TextStyle underlineStyle = const TextStyle(
decoration: underline,
decorationColor: const Color(0xFF000000),
decorationStyle: TextDecorationStyle.wavy
);
Component toStyledText(String name, String text) {
TextStyle lineStyle = (name == "Dave") ? daveStyle : halStyle;
......
......@@ -39,9 +39,9 @@ enum TextDecoration {
lineThrough
}
const underline = const <TextDecoration> [TextDecoration.underline];
const overline = const <TextDecoration> [TextDecoration.overline];
const lineThrough = const <TextDecoration> [TextDecoration.lineThrough];
const underline = const <TextDecoration>[TextDecoration.underline];
const overline = const <TextDecoration>[TextDecoration.overline];
const lineThrough = const <TextDecoration>[TextDecoration.lineThrough];
enum TextDecorationStyle {
solid,
......@@ -58,7 +58,7 @@ class TextStyle {
this.fontSize,
this.fontWeight,
this.textAlign,
List<TextDecoration> this.decoration,
this.decoration,
this.decorationColor,
this.decorationStyle
});
......@@ -68,21 +68,10 @@ class TextStyle {
final double fontSize; // in pixels
final FontWeight fontWeight;
final TextAlign textAlign;
final List<TextDecoration> decoration;
final List<TextDecoration> decoration; // TODO(ianh): Switch this to a Set<> once Dart supports constant Sets
final Color decorationColor;
final TextDecorationStyle decorationStyle;
String _decorationToString() {
assert(decoration != null);
const toCSS = const {
TextDecoration.none: 'none',
TextDecoration.underline: 'underline',
TextDecoration.overline: 'overline',
TextDecoration.lineThrough: 'lineThrough'
};
return decoration.map((d) => toCSS[d]).join(' ');
}
TextStyle copyWith({
Color color,
double fontSize,
......@@ -104,41 +93,44 @@ class TextStyle {
);
}
bool operator ==(other) {
if (identical(this, other))
return true;
return other is TextStyle &&
color == other.color &&
fontFamily == other.fontFamily &&
fontSize == other.fontSize &&
fontWeight == other.fontWeight &&
textAlign == other.textAlign &&
decoration == other.decoration &&
decorationColor == other.decorationColor &&
decorationStyle == other.decorationStyle;
static String _colorToCSSString(Color color) {
return 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})';
}
int get hashCode {
// Use Quiver: https://github.com/domokit/mojo/issues/236
int value = 373;
value = 37 * value + color.hashCode;
value = 37 * value + fontFamily.hashCode;
value = 37 * value + fontSize.hashCode;
value = 37 * value + fontWeight.hashCode;
value = 37 * value + textAlign.hashCode;
value = 37 * value + decoration.hashCode;
value = 37 * value + decorationColor.hashCode;
value = 37 * value + decorationStyle.hashCode;
return value;
static String _fontFamilyToCSSString(String fontFamily) {
// TODO(hansmuller): escape the fontFamily string.
return fontFamily;
}
static String _decorationToCSSString(List<TextDecoration> decoration) {
assert(decoration != null);
const toCSS = const <TextDecoration, String>{
TextDecoration.none: 'none',
TextDecoration.underline: 'underline',
TextDecoration.overline: 'overline',
TextDecoration.lineThrough: 'lineThrough'
};
return decoration.map((d) => toCSS[d]).join(' ');
}
static String _decorationStyleToCSSString(TextDecorationStyle decorationStyle) {
assert(decorationStyle != null);
const toCSS = const <TextDecorationStyle, String>{
TextDecorationStyle.solid: 'solid',
TextDecorationStyle.double: 'double',
TextDecorationStyle.dotted: 'dotted',
TextDecorationStyle.dashed: 'dashed',
TextDecorationStyle.wavy: 'wavy'
};
return toCSS[decorationStyle];
}
void applyToCSSStyle(CSSStyleDeclaration cssStyle) {
if (color != null) {
cssStyle['color'] = 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})';
cssStyle['color'] = _colorToCSSString(color);
}
// TODO(hansmuller): escape the fontFamily string.
if (fontFamily != null) {
cssStyle['font-family'] = fontFamily;
cssStyle['font-family'] = _fontFamilyToCSSString(fontFamily);
}
if (fontSize != null) {
cssStyle['font-size'] = "${fontSize}px";
......@@ -164,9 +156,40 @@ class TextStyle {
}[textAlign];
}
if (decoration != null) {
cssStyle['text-decoration'] = _decorationToString();
cssStyle['text-decoration'] = _decorationToCSSString(decoration);
if (decorationColor != null)
cssStyle['text-decoration-color'] = _colorToCSSString(decorationColor);
if (decorationStyle != null)
cssStyle['text-decoration-style'] = _decorationStyleToCSSString(decorationStyle);
}
// TODO(hansmuller): add support for decoration color and style.
}
bool operator ==(other) {
if (identical(this, other))
return true;
return other is TextStyle &&
color == other.color &&
fontFamily == other.fontFamily &&
fontSize == other.fontSize &&
fontWeight == other.fontWeight &&
textAlign == other.textAlign &&
decoration == other.decoration &&
decorationColor == other.decorationColor &&
decorationStyle == other.decorationStyle;
}
int get hashCode {
// Use Quiver: https://github.com/domokit/mojo/issues/236
int value = 373;
value = 37 * value + color.hashCode;
value = 37 * value + fontFamily.hashCode;
value = 37 * value + fontSize.hashCode;
value = 37 * value + fontWeight.hashCode;
value = 37 * value + textAlign.hashCode;
value = 37 * value + decoration.hashCode;
value = 37 * value + decorationColor.hashCode;
value = 37 * value + decorationStyle.hashCode;
return value;
}
String toString([String prefix = '']) {
......@@ -183,7 +206,7 @@ class TextStyle {
if (textAlign != null)
result.add('${prefix}textAlign: $textAlign');
if (decoration != null)
result.add('${prefix}decoration: ${_decorationToString()}');
result.add('${prefix}decoration: $decoration');
if (decorationColor != null)
result.add('${prefix}decorationColor: $decorationColor');
if (decorationStyle != null)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册