Skip to content

Commit

Permalink
Enable line join styles and miter limit. (flutter#3777)
Browse files Browse the repository at this point in the history
* Add support for join types and miter limits.

* Fix miter limit data type.

* Adding some clearer documentation to painting.dart

* Clarifying more documentation.

* Code review changes

* Fixed debug printing of stroke width and miter limit

* Adding missing periods at the ends of sentences.
  • Loading branch information
gspencergoog authored and Hixie committed Jun 16, 2017
1 parent 80f039f commit 5403f65
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 16 deletions.
87 changes: 78 additions & 9 deletions lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,25 @@ enum StrokeCap {
square,
}

/// Styles to use for line joins.
///
/// This only affects line joins for polygons drawn by [Canvas.drawPath] and
/// rectangles, not points drawn as lines with [Canvas.drawPoints].
///
/// See [Paint.strokeJoin].
// These enum values must be kept in sync with SkPaint::Join.
enum StrokeJoin {
/// Joins between line segments form sharp corners.
miter,

/// Joins between line segments are semi-circular.
round,

/// Joins between line segments connect the corners of the butt ends of the
/// line segments to give a beveled appearance.
bevel,
}

/// Strategies for painting shapes and paths on a canvas.
///
/// See [Paint.style].
Expand Down Expand Up @@ -321,7 +340,7 @@ class Paint {
// * _data is binary data in four-byte fields, each of which is either a
// uint32_t or a float. The default value for each field is encoded as
// zero to make initialization trivial. Most values already have a default
// value of zero, but some, such a color, have a non-zero default value.
// value of zero, but some, such as color, have a non-zero default value.
// To encode or decode these values, XOR the value with the default value.
//
// * _objects is a list of unencodable objects, typically wrappers for native
Expand All @@ -337,23 +356,27 @@ class Paint {
static const int _kStyleIndex = 3;
static const int _kStrokeWidthIndex = 4;
static const int _kStrokeCapIndex = 5;
static const int _kFilterQualityIndex = 6;
static const int _kColorFilterIndex = 7;
static const int _kColorFilterColorIndex = 8;
static const int _kColorFilterBlendModeIndex = 9;
static const int _kStrokeJoinIndex = 6;
static const int _kStrokeMiterLimitIndex = 7;
static const int _kFilterQualityIndex = 8;
static const int _kColorFilterIndex = 9;
static const int _kColorFilterColorIndex = 10;
static const int _kColorFilterBlendModeIndex = 11;

static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2;
static const int _kColorOffset = _kColorIndex << 2;
static const int _kBlendModeOffset = _kBlendModeIndex << 2;
static const int _kStyleOffset = _kStyleIndex << 2;
static const int _kStrokeWidthOffset = _kStrokeWidthIndex << 2;
static const int _kStrokeCapOffset = _kStrokeCapIndex << 2;
static const int _kStrokeJoinOffset = _kStrokeJoinIndex << 2;
static const int _kStrokeMiterLimitOffset = _kStrokeMiterLimitIndex << 2;
static const int _kFilterQualityOffset = _kFilterQualityIndex << 2;
static const int _kColorFilterOffset = _kColorFilterIndex << 2;
static const int _kColorFilterColorOffset = _kColorFilterColorIndex << 2;
static const int _kColorFilterBlendModeOffset = _kColorFilterBlendModeIndex << 2;
// If you add more fields, remember to update _kDataByteCount.
static const int _kDataByteCount = 40;
static const int _kDataByteCount = 48;

// Binary format must match the deserialization code in paint.cc.
List<dynamic> _objects;
Expand All @@ -375,6 +398,7 @@ class Paint {
_data.setInt32(_kIsAntiAliasOffset, encoded, _kFakeHostEndian);
}

// Must be kept in sync with the default in paint.cc.
static const int _kColorDefault = 0xFF000000;

/// The color to use when stroking or filling a shape.
Expand All @@ -399,6 +423,7 @@ class Paint {
_data.setInt32(_kColorOffset, encoded, _kFakeHostEndian);
}

// Must be kept in sync with the default in paint.cc.
static final int _kBlendModeDefault = BlendMode.srcOver.index;

/// A blend mode to apply when a shape is drawn or a layer is composited.
Expand Down Expand Up @@ -461,6 +486,44 @@ class Paint {
_data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian);
}

/// The kind of finish to place on the joins between segments.
///
/// This applies to paths drawn when [style] is set to [PaintingStyle.stroke],
/// It does not apply to points drawn as lines with [Canvas.drawPoints].
///
/// Defaults to [StrokeJoin.miter], i.e. sharp corners. See also
/// [strokeMiterLimit] to control when miters are replaced by bevels.
StrokeJoin get strokeJoin {
return StrokeJoin.values[_data.getInt32(_kStrokeJoinOffset, _kFakeHostEndian)];
}
set strokeJoin(StrokeJoin value) {
assert(value != null);
final int encoded = value.index;
_data.setInt32(_kStrokeJoinOffset, encoded, _kFakeHostEndian);
}

// Must be kept in sync with the default in paint.cc.
static final double _kStrokeMiterLimitDefault = 4.0;

/// The limit for miters to be drawn on segments when the join is set to
/// [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If
/// this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn
/// instead. This may cause some 'popping' of the corners of a path if the
/// angle between line segments is animated.
///
/// This limit is expressed as a limit on the length of the miter.
///
/// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel]
/// join to be used all the time.
double get strokeMiterLimit {
return _data.getFloat32(_kStrokeMiterLimitOffset, _kFakeHostEndian);
}
set strokeMiterLimit(double value) {
assert(value != null);
final double encoded = value - _kStrokeMiterLimitDefault;
_data.setFloat32(_kStrokeMiterLimitOffset, encoded, _kFakeHostEndian);
}

/// A mask filter (for example, a blur) to apply to a shape after it has been
/// drawn but before it has been composited into the image.
///
Expand Down Expand Up @@ -547,25 +610,31 @@ class Paint {
if (style == PaintingStyle.stroke) {
result.write('$style');
if (strokeWidth != 0.0)
result.write(' $strokeWidth');
result.write(' ${strokeWidth.toStringAsFixed(1)}');
else
result.write(' hairline');
if (strokeCap != StrokeCap.butt)
result.write(' $strokeCap');
if (strokeJoin == StrokeJoin.miter) {
if (strokeMiterLimit != _kStrokeMiterLimitDefault)
result.write(' $strokeJoin up to ${strokeMiterLimit.toStringAsFixed(1)}');
} else {
result.write(' $strokeJoin');
}
semicolon = '; ';
}
if (isAntiAlias != true) {
result.write('${semicolon}antialias off');
semicolon = '; ';
}
if (color != const Color(0xFF000000)) {
if (color != const Color(_kColorDefault)) {
if (color != null)
result.write('$semicolon$color');
else
result.write('${semicolon}no color');
semicolon = '; ';
}
if (blendMode != BlendMode.srcOver) {
if (blendMode.index != _kBlendModeDefault) {
result.write('$semicolon$blendMode');
semicolon = '; ';
}
Expand Down
35 changes: 28 additions & 7 deletions lib/ui/painting/paint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,29 @@ constexpr int kBlendModeIndex = 2;
constexpr int kStyleIndex = 3;
constexpr int kStrokeWidthIndex = 4;
constexpr int kStrokeCapIndex = 5;
constexpr int kFilterQualityIndex = 6;
constexpr int kColorFilterIndex = 7;
constexpr int kColorFilterColorIndex = 8;
constexpr int kColorFilterBlendModeIndex = 9;
constexpr size_t kDataByteCount = 40;
constexpr int kStrokeJoinIndex = 6;
constexpr int kStrokeMiterLimitIndex = 7;
constexpr int kFilterQualityIndex = 8;
constexpr int kColorFilterIndex = 9;
constexpr int kColorFilterColorIndex = 10;
constexpr int kColorFilterBlendModeIndex = 11;
constexpr size_t kDataByteCount = 48;

constexpr int kMaskFilterIndex = 0;
constexpr int kShaderIndex = 1;
constexpr int kObjectCount = 2; // Must be one larger than the largest index

// Must be kept in sync with the default in painting.dart.
constexpr uint32_t kColorDefault = 0xFF000000;

// Must be kept in sync with the default in painting.dart.
constexpr uint32_t kBlendModeDefault =
static_cast<uint32_t>(SkBlendMode::kSrcOver);

// Must be kept in sync with the default in painting.dart, and also with the
// default SkPaintDefaults_MiterLimit in Skia (which is not in a public header).
constexpr double kStrokeMiterLimitDefault = 4.0;

Paint DartConverter<Paint>::FromArguments(Dart_NativeArguments args,
int index,
Dart_Handle& exception) {
Expand Down Expand Up @@ -78,14 +91,14 @@ Paint DartConverter<Paint>::FromArguments(Dart_NativeArguments args,

uint32_t encoded_color = uint_data[kColorIndex];
if (encoded_color) {
SkColor color = encoded_color ^ 0xFF000000;
SkColor color = encoded_color ^ kColorDefault;
paint.setColor(color);
}

uint32_t encoded_blend_mode = uint_data[kBlendModeIndex];
if (encoded_blend_mode) {
uint32_t blend_mode =
encoded_blend_mode ^ static_cast<uint32_t>(SkBlendMode::kSrcOver);
encoded_blend_mode ^ kBlendModeDefault;
paint.setBlendMode(static_cast<SkBlendMode>(blend_mode));
}

Expand All @@ -101,6 +114,14 @@ Paint DartConverter<Paint>::FromArguments(Dart_NativeArguments args,
if (stroke_cap)
paint.setStrokeCap(static_cast<SkPaint::Cap>(stroke_cap));

uint32_t stroke_join = uint_data[kStrokeJoinIndex];
if (stroke_join)
paint.setStrokeJoin(static_cast<SkPaint::Join>(stroke_join));

float stroke_miter_limit = float_data[kStrokeMiterLimitIndex];
if (stroke_miter_limit != 0.0)
paint.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault);

uint32_t filter_quality = uint_data[kFilterQualityIndex];
if (filter_quality)
paint.setFilterQuality(static_cast<SkFilterQuality>(filter_quality));
Expand Down

0 comments on commit 5403f65

Please sign in to comment.