Skip to content

Commit

Permalink
Getting drop shadow, blur, and inner shadows working.
Browse files Browse the repository at this point in the history
  • Loading branch information
luigi-rosso committed Nov 27, 2019
1 parent 31aa641 commit dee0d83
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 19 deletions.
14 changes: 13 additions & 1 deletion flare_dart/lib/actor_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,19 @@ import "stream_reader.dart";

abstract class ActorComponent {
String _name = "Unnamed";
ActorNode parent;
ActorNode _parent;
ActorNode get parent => _parent;
set parent(ActorNode value) {
if (_parent == value) {
return;
}
ActorNode from = _parent;
_parent = value;
onParentChanged(from, value);
}

void onParentChanged(ActorNode from, ActorNode to) {}

ActorArtboard artboard;
int _parentIdx = 0;
int idx = 0;
Expand Down
29 changes: 29 additions & 0 deletions flare_dart/lib/actor_layer_effect_renderer.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import 'package:flare_dart/actor_drop_shadow.dart';
import 'package:flare_dart/actor_node.dart';
import 'package:flare_dart/math/aabb.dart';

import 'actor_artboard.dart';
import 'actor_blur.dart';
import 'actor_component.dart';
import 'actor_drawable.dart';
import 'actor_inner_shadow.dart';
import 'actor_mask.dart';

class ActorLayerEffectRendererMask {
Expand All @@ -15,6 +20,13 @@ class ActorLayerEffectRenderer extends ActorDrawable {
List<ActorDrawable> get drawables => _drawables;
final List<ActorLayerEffectRendererMask> _renderMasks = [];
List<ActorLayerEffectRendererMask> get renderMasks => _renderMasks;
ActorBlur _blur;
List<ActorDropShadow> _dropShadows;
List<ActorInnerShadow> _innerShadows;

ActorBlur get blur => _blur;
List<ActorDropShadow> get dropShadows => _dropShadows;
List<ActorInnerShadow> get innerShadows => _innerShadows;

bool addDrawable(ActorDrawable drawable) {
if (_drawables.contains(drawable)) {
Expand All @@ -31,6 +43,13 @@ class ActorLayerEffectRenderer extends ActorDrawable {
.sort((ActorDrawable a, ActorDrawable b) => a.drawOrder - b.drawOrder);
}

@override
void onParentChanged(ActorNode from, ActorNode to) {
from?.findLayerEffect();
to?.findLayerEffect();
findEffects();
}

@override
int get blendModeId {
return 0;
Expand All @@ -52,6 +71,15 @@ class ActorLayerEffectRenderer extends ActorDrawable {
return instanceNode;
}

void findEffects() {
var blurs = parent.children.whereType<ActorBlur>();
_blur = blurs.isNotEmpty ? blurs.first : null;
_dropShadows =
parent.children.whereType<ActorDropShadow>().toList(growable: false);
_innerShadows =
parent.children.whereType<ActorInnerShadow>().toList(growable: false);
}

@override
void completeResolve() {
super.completeResolve();
Expand All @@ -68,6 +96,7 @@ class ActorLayerEffectRenderer extends ActorDrawable {
});
sortDrawables();
computeMasks();
findEffects();
}

void computeMasks() {
Expand Down
30 changes: 28 additions & 2 deletions flare_dart/lib/actor_node.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:flare_dart/actor_layer_effect_renderer.dart';

import "actor_artboard.dart";
import "actor_component.dart";
import "actor_constraint.dart";
Expand Down Expand Up @@ -30,6 +32,7 @@ class ActorNode extends ActorComponent {
Vec2D _scale = Vec2D.fromValues(1.0, 1.0);
double _opacity = 1.0;
double _renderOpacity = 1.0;
ActorLayerEffectRenderer _layerEffect;

bool _overrideWorldTransform = false;
bool _isCollapsedVisibility = false;
Expand Down Expand Up @@ -164,6 +167,25 @@ class ActorNode extends ActorComponent {
return _renderOpacity;
}

double get childOpacity {
return _layerEffect == null ? _renderOpacity : 1;
}

// Helper that looks for layer effect, this is only called by
// ActorLayerEffectRenderer when the parent changes. This keeps it efficient
// so not every ActorNode has to look for layerEffects as most won't have it.
void findLayerEffect() {
var layerEffects = children?.whereType<ActorLayerEffectRenderer>();
var change = layerEffects != null && layerEffects.isNotEmpty
? layerEffects.first
: null;
if (_layerEffect != change) {
_layerEffect = change;
// Force update the opacity.
markTransformDirty();
}
}

bool get renderCollapsed {
return _renderCollapsed;
}
Expand Down Expand Up @@ -222,7 +244,7 @@ class ActorNode extends ActorComponent {

if (parent != null) {
_renderCollapsed = _isCollapsedVisibility || parent._renderCollapsed;
_renderOpacity *= parent._renderOpacity;
_renderOpacity *= parent.childOpacity;
if (!_overrideWorldTransform) {
Mat2D.multiply(_worldTransform, parent._worldTransform, _transform);
}
Expand Down Expand Up @@ -260,9 +282,13 @@ class ActorNode extends ActorComponent {
return node;
}

void removeChild(ActorComponent component) {
_children?.remove(component);
}

void addChild(ActorComponent component) {
if (component.parent != null) {
component.parent._children.remove(component);
component.parent.removeChild(component);
}
component.parent = this;
_children ??= <ActorComponent>[];
Expand Down
204 changes: 188 additions & 16 deletions flare_flutter/lib/flare.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:ui' as ui;

import 'package:flare_dart/actor_flags.dart';
import 'package:flare_dart/actor_image.dart';
import 'package:flare_dart/actor_mask.dart';
import 'package:flare_dart/math/aabb.dart';
import 'package:flutter/services.dart';

Expand Down Expand Up @@ -1308,12 +1309,134 @@ class FlutterActorLayerEffectRenderer extends ActorLayerEffectRenderer
with FlutterActorDrawable {
@override
void draw(ui.Canvas canvas) {
drawPass(canvas, Paint());
}

void drawPass(ui.Canvas canvas, Paint layerPaint) {
var aabb = artboard.artboardAABB();
Rect bounds = Rect.fromLTRB(aabb[0], aabb[1], aabb[2], aabb[3]);

double baseBlurX = 0;
double baseBlurY = 0;
Paint layerPaint = Paint();
Color layerColor = Colors.white.withOpacity(parent.renderOpacity);
layerPaint.color = layerColor;
if (blur?.isActive ?? false) {
baseBlurX = blur.blurX;
baseBlurY = blur.blurY;
layerPaint.imageFilter =
ui.ImageFilter.blur(sigmaX: baseBlurX, sigmaY: baseBlurY);
}

if (dropShadows.isNotEmpty) {
for (final dropShadow in dropShadows) {
if (!dropShadow.isActive) {
continue;
}
// DropShadow: To draw a shadow we just draw the shape (with
// drawPass) with a custom color and image (blur) filter before
// drawing the main shape.
canvas.save();
var color = dropShadow.color;
canvas.translate(dropShadow.offsetX, dropShadow.offsetY);
var shadowPaint = Paint()
..color = layerColor
..imageFilter = ui.ImageFilter.blur(
sigmaX: dropShadow.blurX + baseBlurX,
sigmaY: dropShadow.blurY + baseBlurY)
..colorFilter = ui.ColorFilter.mode(
ui.Color.fromRGBO(
(color[0] * 255.0).round(),
(color[1] * 255.0).round(),
(color[2] * 255.0).round(),
color[3]),
ui.BlendMode.srcIn)
..blendMode = ui.BlendMode.values[dropShadow.blendModeId];

drawPass(canvas, bounds, shadowPaint);
canvas.restore();
canvas.restore();
}
}
drawPass(canvas, bounds, layerPaint);
// Draw inner shadows on the main layer.
if (innerShadows.isNotEmpty) {
for (final innerShadow in innerShadows) {
if (!innerShadow.isActive) {
continue;
}
var blendMode = ui.BlendMode.values[innerShadow.blendModeId];
bool extraBlendPass = blendMode != ui.BlendMode.srcOver;
if (extraBlendPass) {
// if we have a custom blend mode, then we can't just srcATop with
// what's already been drawn. We need to draw the contents as a mask
// to then draw the shadow on top of with srcIn to only show the
// shadow and finally composite with the desired blend mode requested
// here.
var extraLayerPaint = Paint()..blendMode = blendMode;
drawPass(canvas, bounds, extraLayerPaint);
}

// because there's no way to compose image filters (use two filters in
// one) we have to use an extra layer to invert the alpha for the inner
// shadow before blurring.

var color = innerShadow.color;
var shadowPaint = Paint()
..color = layerColor
..blendMode =
extraBlendPass ? ui.BlendMode.srcIn : ui.BlendMode.srcATop
..imageFilter = ui.ImageFilter.blur(
sigmaX: innerShadow.blurX + baseBlurX,
sigmaY: innerShadow.blurY + baseBlurY)
..colorFilter = ui.ColorFilter.mode(
ui.Color.fromRGBO(
(color[0] * 255.0).round(),
(color[1] * 255.0).round(),
(color[2] * 255.0).round(),
color[3]),
ui.BlendMode.srcIn);

canvas.saveLayer(bounds, shadowPaint);
canvas.translate(innerShadow.offsetX, innerShadow.offsetY);

// Invert the alpha to compute inner part.
var invertPaint = Paint()
..colorFilter = const ui.ColorFilter.matrix([
1,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
-1,
255,
]);
drawPass(canvas, bounds, invertPaint);
// restore draw pass (inverted aint)
canvas.restore();
// restore save layer used to that blurs and colors the shadow
canvas.restore();

if (extraBlendPass) {
// Restore extra layer used to draw the contents to clip against (we
// clip by drawing with srcIn)
canvas.restore();
}
}
}
canvas.restore();
}

void drawPass(ui.Canvas canvas, Rect bounds, Paint layerPaint) {
canvas.saveLayer(bounds, layerPaint);
for (final drawable in drawables) {
if (drawable is FlutterActorDrawable) {
Expand All @@ -1322,20 +1445,71 @@ class FlutterActorLayerEffectRenderer extends ActorLayerEffectRenderer
}

for (final renderMask in renderMasks) {
if (!renderMask.mask.isActive) {
var mask = renderMask.mask;
if (!mask.isActive) {
continue;
}

// const mat = CanvasKit.SkColorFilter.MakeMatrix(new Float32Array([
// 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0,
// 0, 0, 0, 1, 0
// ]));
// maskPaint.setColorFilter(mat);
var maskPaint = Paint();
maskPaint.colorFilter = ui.ColorFilter.matrix(
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
switch (mask.maskType) {
case MaskType.invertedAlpha:
maskPaint.colorFilter = const ui.ColorFilter.matrix(
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1]);
break;
case MaskType.luminance:
maskPaint.colorFilter = const ui.ColorFilter.matrix([
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0.33,
0.59,
0.11,
0,
0
]);
break;
case MaskType.invertedLuminance:
maskPaint.colorFilter = const ui.ColorFilter.matrix([
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
-0.33,
-0.59,
-0.11,
0,
1
]);
break;
case MaskType.alpha:
default:
maskPaint.colorFilter = const ui.ColorFilter.matrix(
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
break;
}

maskPaint.blendMode = BlendMode.dstIn;
canvas.saveLayer(bounds, maskPaint);
Expand All @@ -1351,8 +1525,6 @@ class FlutterActorLayerEffectRenderer extends ActorLayerEffectRenderer
}
canvas.restore();
}

canvas.restore();
}

@override
Expand Down

0 comments on commit dee0d83

Please sign in to comment.