Skip to content

Commit

Permalink
Add an object generator.
Browse files Browse the repository at this point in the history
I guess I never PRed this...

Adds ability to override built-in objects to custom ones so you can do things like add shadows:
```
RiveAnimation.asset(
  'assets/shadowtext.riv',
  objectGenerator: (coreTypeKey) {
    switch (coreTypeKey) {
      case ShapeBase.typeKey:
        return ShadowRiveShape();
    }
    return null;
  },
```

![image](https://github.com/rive-app/rive/assets/454182/cae261b1-8005-4c49-9714-3bb1dca50cde)

```
class ShadowRiveShape extends Shape {
  Paint? _shadowPaint;
  @OverRide
  void onAdded() {
    if (name == 'ShadowShape') {
      // This was named "ShadowShape" we use that as a simple way to determine
      // that we want to add a drop shadow at runtime.
      _shadowPaint = Paint()
        ..imageFilter = ImageFilter.blur(
          // Make it a 10x10 black blur
          sigmaX: 10,
          sigmaY: 10,
          tileMode: TileMode.clamp,
        );
    }
    super.onAdded();
  }

  @OverRide
  K? clone<K extends Core<CoreContext>>() {
    var shape = ShadowRiveShape();
    shape.copy(this);
    return shape as K;
  }

  @OverRide
  void draw(Canvas canvas) {
    var shadow = _shadowPaint;
    if (shadow != null) {
      // We draw a blurred version of this shape before regular drawing. Make
      // sure to respect clipping rules for the shadow too.
      bool clipped = clip(canvas);
      var path = pathComposer.fillPath;
      canvas.save();
      if (!fillInWorld) {
        canvas.transform(worldTransform.mat4);
      }
      // Offset the shadow to the bottom right (change to whatever you want).
      canvas.translate(10, 10);
      canvas.drawPath(path, shadow);

      canvas.restore();

      if (clipped) {
        canvas.restore();
      }
    }

    // Draw the regular shape.
    super.draw(canvas);
  }
}
```

Diffs=
99a2215da Add an object generator. (#6805)
7cb7eb812 Upgrade rive_wasm to the new premake system (#6789)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
  • Loading branch information
luigi-rosso and luigi-rosso committed Mar 8, 2024
1 parent bee93ab commit 8487b7f
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
74e649ee25a9d65fd428f32d3dbd144e54c38aa8
99a2215daeffdfe537e2a900113a9d59ee7b9d3f
40 changes: 29 additions & 11 deletions lib/src/rive_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,21 @@ import 'package:rive_common/rive_text.dart';

import 'package:rive_common/utilities.dart';

Core<CoreContext>? _readRuntimeObject(
BinaryReader reader, HashMap<int, CoreFieldType> propertyToField) {
typedef Core<CoreContext>? ObjectGenerator(int coreTypeKey);

Core<CoreContext>? _readRuntimeObject(BinaryReader reader,
HashMap<int, CoreFieldType> propertyToField, ObjectGenerator? generator) {
int coreObjectKey = reader.readVarUint();
Core<CoreContext>? instance;
switch (coreObjectKey) {
case ArtboardBase.typeKey:
instance = RuntimeArtboard();
break;
case NestedArtboardBase.typeKey:
instance = RuntimeNestedArtboard();
break;
Core<CoreContext>? instance = generator?.call(coreObjectKey);
if (instance == null) {
switch (coreObjectKey) {
case ArtboardBase.typeKey:
instance = RuntimeArtboard();
break;
case NestedArtboardBase.typeKey:
instance = RuntimeNestedArtboard();
break;
}
}
var object = instance ?? RiveCoreContext.makeCoreInstance(coreObjectKey);

Expand Down Expand Up @@ -153,6 +157,7 @@ class RiveFile {
RiveFile._(
BinaryReader reader,
this.header,
ObjectGenerator? generator,
this._assetLoader,
) {
/// Property fields table of contents
Expand All @@ -162,7 +167,7 @@ class RiveFile {
var artboardLookup = HashMap<int, Artboard>();
var importStack = ImportStack();
while (!reader.isEOF) {
final object = _readRuntimeObject(reader, propertyToField);
final object = _readRuntimeObject(reader, propertyToField, generator);
if (object == null) {
// See if there's an artboard on the stack, need to track the null
// object as it'll still hold an id.
Expand Down Expand Up @@ -320,18 +325,23 @@ class RiveFile {
/// Loading assets documentation: https://help.rive.app/runtimes/loading-assets
/// {@endtemplate}
///
/// Provide an [objectGenerator] if you want to override any built-in Rive
/// types.
///
/// Will throw [RiveFormatErrorException] if data is malformed. Will throw
/// [RiveUnsupportedVersionException] if the version is not supported.
factory RiveFile.import(
ByteData bytes, {
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
FileAssetLoader? assetLoader,
ObjectGenerator? objectGenerator,
bool loadCdnAssets = true,
}) {
var reader = BinaryReader(bytes);
return RiveFile._(
reader,
RuntimeHeader.read(reader),
objectGenerator,
FallbackAssetLoader(
[
if (assetLoader != null) assetLoader,
Expand All @@ -355,6 +365,7 @@ class RiveFile {
ByteData bytes, {
FileAssetLoader? assetLoader,
bool loadCdnAssets = true,
ObjectGenerator? objectGenerator,
}) async {
if (!_initializedText) {
/// If the file looks like needs the text runtime, let's load it.
Expand All @@ -367,6 +378,7 @@ class RiveFile {
bytes,
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
objectGenerator: objectGenerator,
);
}

Expand All @@ -384,6 +396,7 @@ class RiveFile {
AssetBundle? bundle,
FileAssetLoader? assetLoader,
bool loadCdnAssets = true,
ObjectGenerator? objectGenerator,
}) async {
final bytes = await (bundle ?? rootBundle).load(
bundleKey,
Expand All @@ -393,6 +406,7 @@ class RiveFile {
bytes,
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
objectGenerator: objectGenerator,
);
}

Expand All @@ -410,13 +424,15 @@ class RiveFile {
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
FileAssetLoader? assetLoader,
bool loadCdnAssets = true,
ObjectGenerator? objectGenerator,
}) async {
final res = await http.get(Uri.parse(url), headers: headers);
final bytes = ByteData.view(res.bodyBytes.buffer);
return _initTextAndImport(
bytes,
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
objectGenerator: objectGenerator,
);
}

Expand All @@ -430,12 +446,14 @@ class RiveFile {
String path, {
FileAssetLoader? assetLoader,
bool loadCdnAssets = true,
ObjectGenerator? objectGenerator,
}) async {
final bytes = await localFileBytes(path);
return _initTextAndImport(
ByteData.view(bytes!.buffer),
assetLoader: assetLoader,
loadCdnAssets: loadCdnAssets,
objectGenerator: objectGenerator,
);
}

Expand Down
12 changes: 12 additions & 0 deletions lib/src/widgets/rive_animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ class RiveAnimation extends StatefulWidget {
/// {@macro Rive.behavior}
final RiveHitTestBehavior behavior;

/// Rive object generator to override built-in types and methods to, for
/// example, interject custom rendering functionality interleaved with Rive
/// rendering.
final ObjectGenerator? objectGenerator;

/// Creates a new [RiveAnimation] from an asset bundle.
///
/// *Example:*
Expand All @@ -86,6 +91,7 @@ class RiveAnimation extends StatefulWidget {
this.controllers = const [],
this.onInit,
this.behavior = RiveHitTestBehavior.opaque,
this.objectGenerator,
Key? key,
}) : name = asset,
file = null,
Expand Down Expand Up @@ -114,6 +120,7 @@ class RiveAnimation extends StatefulWidget {
this.onInit,
this.headers,
this.behavior = RiveHitTestBehavior.opaque,
this.objectGenerator,
Key? key,
}) : name = url,
file = null,
Expand All @@ -140,6 +147,7 @@ class RiveAnimation extends StatefulWidget {
this.controllers = const [],
this.onInit,
this.behavior = RiveHitTestBehavior.opaque,
this.objectGenerator,
Key? key,
}) : name = path,
file = null,
Expand Down Expand Up @@ -172,6 +180,7 @@ class RiveAnimation extends StatefulWidget {
this.behavior = RiveHitTestBehavior.opaque,
}) : name = null,
headers = null,
objectGenerator = null,
src = _Source.direct,
super(key: key);

Expand Down Expand Up @@ -209,15 +218,18 @@ class RiveAnimationState extends State<RiveAnimation> {
case _Source.asset:
return RiveFile.asset(
widget.name!,
objectGenerator: widget.objectGenerator,
);
case _Source.network:
return RiveFile.network(
widget.name!,
headers: widget.headers,
objectGenerator: widget.objectGenerator,
);
case _Source.file:
return RiveFile.file(
widget.name!,
objectGenerator: widget.objectGenerator,
);
case _Source.direct:
return Future.value(
Expand Down

0 comments on commit 8487b7f

Please sign in to comment.