Skip to content

Commit

Permalink
Fix group opacity
Browse files Browse the repository at this point in the history
  • Loading branch information
dnfield committed Apr 9, 2019
1 parent 40ad262 commit 60b6696
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 64 deletions.
12 changes: 12 additions & 0 deletions example/assets/simple/group_composite_opacity.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion example/lib/main.dart
Expand Up @@ -92,7 +92,7 @@ class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
_dimension = 200.0;
_dimension = 203.0;
for (String assetName in assetNames) {
_painters.add(
SvgPicture.asset(assetName),
Expand Down
Binary file modified golden/dart.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added golden/simple/group_composite_opacity.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified golden/simple/group_opacity.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions lib/src/svg/parser_state.dart
Expand Up @@ -81,8 +81,12 @@ class _Elements {
viewBox,
<Drawable>[],
parserState._definitions,
parseStyle(parserState.attributes, parserState._definitions,
viewBox.viewBoxRect, null),
parseStyle(
parserState.attributes,
parserState._definitions,
viewBox.viewBoxRect,
null,
),
);
parserState.addGroup(parserState._currentStartElement, parserState._root);
return null;
Expand Down
22 changes: 12 additions & 10 deletions lib/src/svg/xml_parsers.dart
Expand Up @@ -175,11 +175,12 @@ DrawablePaint parseStroke(
DrawablePaint parentStroke,
) {
final String rawStroke = getAttribute(attributes, 'stroke');
final String rawOpacity = getAttribute(attributes, 'stroke-opacity');

final double opacity = rawOpacity == ''
? parentStroke?.color?.opacity ?? 1.0
: parseDouble(rawOpacity).clamp(0.0, 1.0);
final String rawStrokeOpacity = getAttribute(attributes, 'stroke-opacity', def: '1.0');
final String rawOpacity = getAttribute(attributes, 'opacity');
double opacity = parseDouble(rawStrokeOpacity).clamp(0.0, 1.0);
if (rawOpacity != '') {
opacity *= parseDouble(rawOpacity).clamp(0.0, 1.0);
}

if (rawStroke.startsWith('url')) {
return _getDefinitionPaint(
Expand Down Expand Up @@ -237,11 +238,12 @@ DrawablePaint parseFill(
DrawablePaint parentFill,
) {
final String rawFill = getAttribute(el, 'fill');
final String rawOpacity = getAttribute(el, 'fill-opacity');

final double opacity = rawOpacity == ''
? parentFill?.color?.opacity ?? 1.0
: parseDouble(rawOpacity).clamp(0.0, 1.0);
final String rawFillOpacity = getAttribute(el, 'fill-opacity', def: '1.0');
final String rawOpacity = getAttribute(el, 'opacity');
double opacity = parseDouble(rawFillOpacity).clamp(0.0, 1.0);
if (rawOpacity != '') {
opacity *= parseDouble(rawOpacity).clamp(0.0, 1.0);
}

if (rawFill.startsWith('url')) {
return _getDefinitionPaint(
Expand Down
90 changes: 40 additions & 50 deletions lib/src/vector_drawable.dart
Expand Up @@ -17,7 +17,9 @@ abstract class Drawable {

/// Draws the contents or children of this [Drawable] to the `canvas`, using
/// the `parentPaint` to optionally override the child's paint.
void draw(Canvas canvas, ColorFilter colorFilter);
///
/// The `bounds` specify the area to draw in.
void draw(Canvas canvas, ColorFilter colorFilter, Rect bounds);
}

/// A [Drawable] that can have a [DrawableStyle] applied to it.
Expand Down Expand Up @@ -89,11 +91,13 @@ class DrawableStyle {
/// The clip to apply, if any.
final List<Path> clipPath;

/// Controls inheriting opacity. Will be averaged with child opacity.
/// Controls group level opacity. Will be [BlendMode.dstIn] blended with any
/// children.
final double groupOpacity;

/// Creates a new [DrawableStyle] if `parent` is not null, filling in any null properties on
/// this with the properties from other (except [groupOpacity], which is averaged).
/// Creates a new [DrawableStyle] if `parent` is not null, filling in any null
/// properties on this with the properties from other (except [groupOpacity],
/// is not inherited).
static DrawableStyle mergeAndBlend(
DrawableStyle parent, {
DrawablePaint fill,
Expand All @@ -106,10 +110,9 @@ class DrawableStyle {
double groupOpacity,
List<Path> clipPath,
}) {
groupOpacity = mergeOpacity(groupOpacity, parent?.groupOpacity);
return DrawableStyle(
fill: DrawablePaint.merge(fill, parent?.fill, groupOpacity),
stroke: DrawablePaint.merge(stroke, parent?.stroke, groupOpacity),
fill: DrawablePaint.merge(fill, parent?.fill),
stroke: DrawablePaint.merge(stroke, parent?.stroke),
dashArray: dashArray ?? parent?.dashArray,
dashOffset: dashOffset ?? parent?.dashOffset,
// transforms aren't inherited because they're applied to canvas with save/restore
Expand Down Expand Up @@ -166,36 +169,31 @@ class DrawablePaint {
/// Will merge two DrawablePaints, preferring properties defined in `a` if they're not null.
///
/// If `a` is `identical` wiht [DrawablePaint.empty], `b` will be ignored.
factory DrawablePaint.merge(DrawablePaint a, DrawablePaint b,
[double groupOpacity]) {
factory DrawablePaint.merge(DrawablePaint a, DrawablePaint b) {
if (a == null && b == null) {
return null;
}

if (b == null && a != null) {
return a._withGroupOpacity(groupOpacity);
return a;
}

if (identical(a, DrawablePaint.empty) ||
identical(b, DrawablePaint.empty)) {
return (a ?? b)._withGroupOpacity(groupOpacity);
return a ?? b;
}

if (a == null) {
return b._withGroupOpacity(groupOpacity);
return b;
}

// If we got here, the styles should not be null.
assert(a.style == b.style,
'Cannot merge Paints with different PaintStyles; got:\na: $a\nb: $b.');

final Color mergedColor = a.color ?? b.color;

return DrawablePaint(
a.style ?? b.style,
color: mergedColor.withOpacity(mergedColor.opacity == 1.0
? groupOpacity ?? 1.0
: DrawableStyle.mergeOpacity(groupOpacity, mergedColor.opacity)),
color: a.color ?? b.color,
shader: a.shader ?? b.shader,
blendMode: a.blendMode ?? b.blendMode,
colorFilter: a.colorFilter ?? b.colorFilter,
Expand Down Expand Up @@ -264,23 +262,6 @@ class DrawablePaint {
/// The width of strokes for this paint.
final double strokeWidth;

DrawablePaint _withGroupOpacity(double groupOpacity) {
if (color == null || groupOpacity == null) {
return this;
}
return DrawablePaint.merge(
DrawablePaint(
style,
color: color.withOpacity(
color.opacity == 1.0
? groupOpacity ?? 1.0
: DrawableStyle.mergeOpacity(groupOpacity, color.opacity),
),
),
this,
);
}

/// Creates a [Paint] object from this [DrawablePaint].
@virtual
Paint toFlutterPaint([ColorFilter colorFilterOverride]) {
Expand Down Expand Up @@ -507,7 +488,7 @@ class DrawableText implements Drawable {
(fill?.width ?? 0.0) + (stroke?.width ?? 0.0) > 0.0;

@override
void draw(Canvas canvas, ColorFilter colorFilter) {
void draw(Canvas canvas, ColorFilter colorFilter, Rect bounds) {
if (!hasDrawableContent) {
return;
}
Expand Down Expand Up @@ -888,15 +869,15 @@ class DrawableRoot implements DrawableParent {
!viewport.viewBox.isEmpty;

@override
void draw(Canvas canvas, ColorFilter colorFilter) {
void draw(Canvas canvas, ColorFilter colorFilter, Rect bounds) {
if (!hasDrawableContent) {
return;
}
if (viewport.viewBoxOffset != Offset.zero) {
canvas.translate(viewport.viewBoxOffset.dx, viewport.viewBoxOffset.dy);
}
for (Drawable child in children) {
child.draw(canvas, colorFilter);
child.draw(canvas, colorFilter, viewport.viewBoxRect);
}
}

Expand Down Expand Up @@ -924,7 +905,7 @@ class DrawableRoot implements DrawableParent {
clipCanvasToViewBox(canvas);
}

draw(canvas, colorFilter);
draw(canvas, colorFilter, viewport.viewBoxRect);
canvas.restore();
return recorder.endRecording();
}
Expand All @@ -939,7 +920,6 @@ class DrawableRoot implements DrawableParent {
clipPath: newStyle.clipPath,
dashArray: newStyle.dashArray,
dashOffset: newStyle.dashOffset,
groupOpacity: newStyle.groupOpacity,
pathFillType: newStyle.pathFillType,
textStyle: newStyle.textStyle,
transform: newStyle.transform,
Expand Down Expand Up @@ -973,18 +953,30 @@ class DrawableGroup implements DrawableStyleable, DrawableParent {
bool get hasDrawableContent => children != null && children.isNotEmpty;

@override
void draw(Canvas canvas, ColorFilter colorFilter) {
void draw(Canvas canvas, ColorFilter colorFilter, Rect bounds) {
if (!hasDrawableContent) {
return;
}

final Function innerDraw = () {
final Function innerDraw = (Rect innerBounds) {
if (style.groupOpacity == 0) {
return;
}
if (style?.transform != null) {
canvas.save();
canvas.transform(style?.transform);
}
if (style.groupOpacity != null && style.groupOpacity != 1.0) {
canvas.saveLayer(
innerBounds,
Paint()..color = Color.fromRGBO(0, 0, 0, style.groupOpacity),
);
}
for (Drawable child in children) {
child.draw(canvas, colorFilter);
child.draw(canvas, colorFilter, bounds);
}
if (style.groupOpacity != null && style.groupOpacity != 1.0) {
canvas.restore();
}
if (style?.transform != null) {
canvas.restore();
Expand All @@ -995,20 +987,20 @@ class DrawableGroup implements DrawableStyleable, DrawableParent {
for (Path clipPath in style.clipPath) {
canvas.save();
canvas.clipPath(clipPath);

final Rect clipBounds = clipPath.getBounds();
if (children.length > 1) {
canvas.saveLayer(clipPath.getBounds(), Paint());
canvas.saveLayer(clipBounds, Paint());
}

innerDraw();
innerDraw(clipBounds);

if (children.length > 1) {
canvas.restore();
}
canvas.restore();
}
} else {
innerDraw();
innerDraw(bounds);
}
}

Expand All @@ -1022,7 +1014,6 @@ class DrawableGroup implements DrawableStyleable, DrawableParent {
clipPath: newStyle.clipPath,
dashArray: newStyle.dashArray,
dashOffset: newStyle.dashOffset,
groupOpacity: newStyle.groupOpacity,
pathFillType: newStyle.pathFillType,
textStyle: newStyle.textStyle,
transform: newStyle.transform,
Expand Down Expand Up @@ -1056,7 +1047,7 @@ class DrawableRasterImage implements Drawable {
final Size size;

@override
void draw(Canvas canvas, ColorFilter colorFilter) {
void draw(Canvas canvas, ColorFilter colorFilter, Rect bounds) {
final Size imageSize = Size(
image.width.toDouble(),
image.height.toDouble(),
Expand Down Expand Up @@ -1114,7 +1105,7 @@ class DrawableShape implements DrawableStyleable {
bool get hasDrawableContent => bounds.width + bounds.height > 0;

@override
void draw(Canvas canvas, ColorFilter colorFilter) {
void draw(Canvas canvas, ColorFilter colorFilter, Rect bounds) {
if (!hasDrawableContent || style == null) {
return;
}
Expand Down Expand Up @@ -1178,7 +1169,6 @@ class DrawableShape implements DrawableStyleable {
clipPath: newStyle.clipPath,
dashArray: newStyle.dashArray,
dashOffset: newStyle.dashOffset,
groupOpacity: newStyle.groupOpacity,
pathFillType: newStyle.pathFillType,
textStyle: newStyle.textStyle,
transform: newStyle.transform,
Expand Down
2 changes: 1 addition & 1 deletion tool/gen_golden.dart
Expand Up @@ -27,7 +27,7 @@ Future<Uint8List> getSvgPngBytes(String svgData) async {
svgRoot.clipCanvasToViewBox(canvas);

canvas.drawPaint(Paint()..color = const Color(0xFFFFFFFF));
svgRoot.draw(canvas, null);
svgRoot.draw(canvas, null, svgRoot.viewport.viewBoxRect);

final Picture pict = rec.endRecording();

Expand Down

0 comments on commit 60b6696

Please sign in to comment.