mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-06-23 23:46:42 +08:00
Mesh deform for flutter runtime.
This commit is contained in:
@ -3,6 +3,9 @@ import 'dart:collection';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
|
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
|
||||||
|
|
||||||
|
export 'dart:typed_data';
|
||||||
|
export 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
export 'package:rive/src/animation_list.dart';
|
export 'package:rive/src/animation_list.dart';
|
||||||
export 'package:rive/src/asset_list.dart';
|
export 'package:rive/src/asset_list.dart';
|
||||||
export 'package:rive/src/blend_animations.dart';
|
export 'package:rive/src/blend_animations.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/assets/file_asset.dart';
|
import 'package:rive/src/rive_core/assets/file_asset.dart';
|
||||||
import 'package:rive/src/rive_core/assets/file_asset_contents.dart';
|
import 'package:rive/src/rive_core/assets/file_asset_contents.dart';
|
||||||
@ -21,7 +19,7 @@ class FileAssetImporter extends ImportStackObject {
|
|||||||
|
|
||||||
void loadContents(FileAssetContents contents) {
|
void loadContents(FileAssetContents contents) {
|
||||||
_loadedContents = true;
|
_loadedContents = true;
|
||||||
fileAsset.decode(contents.bytes as Uint8List);
|
fileAsset.decode(contents.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -13,27 +13,27 @@ abstract class FileAssetContentsBase<T extends CoreContext> extends Core<T> {
|
|||||||
|
|
||||||
/// --------------------------------------------------------------------------
|
/// --------------------------------------------------------------------------
|
||||||
/// Bytes field with key 212.
|
/// Bytes field with key 212.
|
||||||
static const List<int> bytesInitialValue = [];
|
static final Uint8List bytesInitialValue = Uint8List(0);
|
||||||
List<int> _bytes = bytesInitialValue;
|
Uint8List _bytes = bytesInitialValue;
|
||||||
static const int bytesPropertyKey = 212;
|
static const int bytesPropertyKey = 212;
|
||||||
|
|
||||||
/// Byte data of the file.
|
/// Byte data of the file.
|
||||||
List<int> get bytes => _bytes;
|
Uint8List get bytes => _bytes;
|
||||||
|
|
||||||
/// Change the [_bytes] field value.
|
/// Change the [_bytes] field value.
|
||||||
/// [bytesChanged] will be invoked only if the field's value has changed.
|
/// [bytesChanged] will be invoked only if the field's value has changed.
|
||||||
set bytes(List<int> value) {
|
set bytes(Uint8List value) {
|
||||||
if (_bytes == value) {
|
if (listEquals(_bytes, value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<int> from = _bytes;
|
Uint8List from = _bytes;
|
||||||
_bytes = value;
|
_bytes = value;
|
||||||
if (hasValidated) {
|
if (hasValidated) {
|
||||||
bytesChanged(from, value);
|
bytesChanged(from, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bytesChanged(List<int> from, List<int> to);
|
void bytesChanged(Uint8List from, Uint8List to);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void copy(covariant FileAssetContentsBase source) {
|
void copy(covariant FileAssetContentsBase source) {
|
||||||
|
@ -27,7 +27,7 @@ import 'package:rive/src/generated/nested_animation_base.dart';
|
|||||||
import 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
|
import 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
|
||||||
import 'package:rive/src/generated/shapes/parametric_path_base.dart';
|
import 'package:rive/src/generated/shapes/parametric_path_base.dart';
|
||||||
import 'package:rive/src/generated/shapes/path_base.dart';
|
import 'package:rive/src/generated/shapes/path_base.dart';
|
||||||
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
import 'package:rive/src/generated/transform_component_base.dart';
|
import 'package:rive/src/generated/transform_component_base.dart';
|
||||||
import 'package:rive/src/generated/world_transform_component_base.dart';
|
import 'package:rive/src/generated/world_transform_component_base.dart';
|
||||||
import 'package:rive/src/rive_core/animation/animation.dart';
|
import 'package:rive/src/rive_core/animation/animation.dart';
|
||||||
@ -82,11 +82,14 @@ import 'package:rive/src/rive_core/draw_target.dart';
|
|||||||
import 'package:rive/src/rive_core/nested_artboard.dart';
|
import 'package:rive/src/rive_core/nested_artboard.dart';
|
||||||
import 'package:rive/src/rive_core/node.dart';
|
import 'package:rive/src/rive_core/node.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/clipping_shape.dart';
|
import 'package:rive/src/rive_core/shapes/clipping_shape.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/contour_mesh_vertex.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/cubic_asymmetric_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_asymmetric_vertex.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/cubic_detached_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_detached_vertex.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/cubic_mirrored_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_mirrored_vertex.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/ellipse.dart';
|
import 'package:rive/src/rive_core/shapes/ellipse.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/image.dart';
|
import 'package:rive/src/rive_core/shapes/image.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh_vertex.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/fill.dart';
|
import 'package:rive/src/rive_core/shapes/paint/fill.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/gradient_stop.dart';
|
import 'package:rive/src/rive_core/shapes/paint/gradient_stop.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/linear_gradient.dart';
|
import 'package:rive/src/rive_core/shapes/paint/linear_gradient.dart';
|
||||||
@ -100,6 +103,8 @@ import 'package:rive/src/rive_core/shapes/rectangle.dart';
|
|||||||
import 'package:rive/src/rive_core/shapes/shape.dart';
|
import 'package:rive/src/rive_core/shapes/shape.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/star.dart';
|
import 'package:rive/src/rive_core/shapes/star.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/straight_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/straight_vertex.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/text.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/text_run.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/triangle.dart';
|
import 'package:rive/src/rive_core/shapes/triangle.dart';
|
||||||
|
|
||||||
// ignore: avoid_classes_with_only_static_members
|
// ignore: avoid_classes_with_only_static_members
|
||||||
@ -198,6 +203,8 @@ class RiveCoreContext {
|
|||||||
return TrimPath();
|
return TrimPath();
|
||||||
case FillBase.typeKey:
|
case FillBase.typeKey:
|
||||||
return Fill();
|
return Fill();
|
||||||
|
case MeshVertexBase.typeKey:
|
||||||
|
return MeshVertex();
|
||||||
case ShapeBase.typeKey:
|
case ShapeBase.typeKey:
|
||||||
return Shape();
|
return Shape();
|
||||||
case WeightBase.typeKey:
|
case WeightBase.typeKey:
|
||||||
@ -208,8 +215,14 @@ class RiveCoreContext {
|
|||||||
return CubicWeight();
|
return CubicWeight();
|
||||||
case CubicAsymmetricVertexBase.typeKey:
|
case CubicAsymmetricVertexBase.typeKey:
|
||||||
return CubicAsymmetricVertex();
|
return CubicAsymmetricVertex();
|
||||||
|
case MeshBase.typeKey:
|
||||||
|
return Mesh();
|
||||||
|
case TextRunBase.typeKey:
|
||||||
|
return TextRun();
|
||||||
case PointsPathBase.typeKey:
|
case PointsPathBase.typeKey:
|
||||||
return PointsPath();
|
return PointsPath();
|
||||||
|
case ContourMeshVertexBase.typeKey:
|
||||||
|
return ContourMeshVertex();
|
||||||
case RectangleBase.typeKey:
|
case RectangleBase.typeKey:
|
||||||
return Rectangle();
|
return Rectangle();
|
||||||
case CubicMirroredVertexBase.typeKey:
|
case CubicMirroredVertexBase.typeKey:
|
||||||
@ -226,6 +239,8 @@ class RiveCoreContext {
|
|||||||
return Star();
|
return Star();
|
||||||
case ImageBase.typeKey:
|
case ImageBase.typeKey:
|
||||||
return Image();
|
return Image();
|
||||||
|
case TextBase.typeKey:
|
||||||
|
return Text();
|
||||||
case CubicDetachedVertexBase.typeKey:
|
case CubicDetachedVertexBase.typeKey:
|
||||||
return CubicDetachedVertex();
|
return CubicDetachedVertex();
|
||||||
case DrawRulesBase.typeKey:
|
case DrawRulesBase.typeKey:
|
||||||
@ -730,21 +745,31 @@ class RiveCoreContext {
|
|||||||
object.fillRule = value;
|
object.fillRule = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PathBase.pathFlagsPropertyKey:
|
case VertexBase.xPropertyKey:
|
||||||
if (object is PathBase && value is int) {
|
if (object is VertexBase && value is double) {
|
||||||
object.pathFlags = value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PathVertexBase.xPropertyKey:
|
|
||||||
if (object is PathVertexBase && value is double) {
|
|
||||||
object.x = value;
|
object.x = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PathVertexBase.yPropertyKey:
|
case VertexBase.yPropertyKey:
|
||||||
if (object is PathVertexBase && value is double) {
|
if (object is VertexBase && value is double) {
|
||||||
object.y = value;
|
object.y = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MeshVertexBase.uPropertyKey:
|
||||||
|
if (object is MeshVertexBase && value is double) {
|
||||||
|
object.u = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MeshVertexBase.vPropertyKey:
|
||||||
|
if (object is MeshVertexBase && value is double) {
|
||||||
|
object.v = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PathBase.pathFlagsPropertyKey:
|
||||||
|
if (object is PathBase && value is int) {
|
||||||
|
object.pathFlags = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case WeightBase.valuesPropertyKey:
|
case WeightBase.valuesPropertyKey:
|
||||||
if (object is WeightBase && value is int) {
|
if (object is WeightBase && value is int) {
|
||||||
object.values = value;
|
object.values = value;
|
||||||
@ -795,6 +820,21 @@ class RiveCoreContext {
|
|||||||
object.outDistance = value;
|
object.outDistance = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MeshBase.triangleIndexBytesPropertyKey:
|
||||||
|
if (object is MeshBase && value is Uint8List) {
|
||||||
|
object.triangleIndexBytes = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TextRunBase.pointSizePropertyKey:
|
||||||
|
if (object is TextRunBase && value is double) {
|
||||||
|
object.pointSize = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TextRunBase.textLengthPropertyKey:
|
||||||
|
if (object is TextRunBase && value is int) {
|
||||||
|
object.textLength = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case PointsPathBase.isClosedPropertyKey:
|
case PointsPathBase.isClosedPropertyKey:
|
||||||
if (object is PointsPathBase && value is bool) {
|
if (object is PointsPathBase && value is bool) {
|
||||||
object.isClosed = value;
|
object.isClosed = value;
|
||||||
@ -890,6 +930,11 @@ class RiveCoreContext {
|
|||||||
object.assetId = value;
|
object.assetId = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TextBase.valuePropertyKey:
|
||||||
|
if (object is TextBase && value is String) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case CubicDetachedVertexBase.inRotationPropertyKey:
|
case CubicDetachedVertexBase.inRotationPropertyKey:
|
||||||
if (object is CubicDetachedVertexBase && value is double) {
|
if (object is CubicDetachedVertexBase && value is double) {
|
||||||
object.inRotation = value;
|
object.inRotation = value;
|
||||||
@ -1051,7 +1096,7 @@ class RiveCoreContext {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FileAssetContentsBase.bytesPropertyKey:
|
case FileAssetContentsBase.bytesPropertyKey:
|
||||||
if (object is FileAssetContentsBase && value is List<int>) {
|
if (object is FileAssetContentsBase && value is Uint8List) {
|
||||||
object.bytes = value;
|
object.bytes = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1069,6 +1114,7 @@ class RiveCoreContext {
|
|||||||
case ComponentBase.namePropertyKey:
|
case ComponentBase.namePropertyKey:
|
||||||
case AnimationBase.namePropertyKey:
|
case AnimationBase.namePropertyKey:
|
||||||
case StateMachineComponentBase.namePropertyKey:
|
case StateMachineComponentBase.namePropertyKey:
|
||||||
|
case TextBase.valuePropertyKey:
|
||||||
case AssetBase.namePropertyKey:
|
case AssetBase.namePropertyKey:
|
||||||
return stringType;
|
return stringType;
|
||||||
case ComponentBase.parentIdPropertyKey:
|
case ComponentBase.parentIdPropertyKey:
|
||||||
@ -1117,6 +1163,7 @@ class RiveCoreContext {
|
|||||||
case CubicWeightBase.inIndicesPropertyKey:
|
case CubicWeightBase.inIndicesPropertyKey:
|
||||||
case CubicWeightBase.outValuesPropertyKey:
|
case CubicWeightBase.outValuesPropertyKey:
|
||||||
case CubicWeightBase.outIndicesPropertyKey:
|
case CubicWeightBase.outIndicesPropertyKey:
|
||||||
|
case TextRunBase.textLengthPropertyKey:
|
||||||
case ClippingShapeBase.sourceIdPropertyKey:
|
case ClippingShapeBase.sourceIdPropertyKey:
|
||||||
case ClippingShapeBase.fillRulePropertyKey:
|
case ClippingShapeBase.fillRulePropertyKey:
|
||||||
case PolygonBase.pointsPropertyKey:
|
case PolygonBase.pointsPropertyKey:
|
||||||
@ -1161,12 +1208,15 @@ class RiveCoreContext {
|
|||||||
case TrimPathBase.startPropertyKey:
|
case TrimPathBase.startPropertyKey:
|
||||||
case TrimPathBase.endPropertyKey:
|
case TrimPathBase.endPropertyKey:
|
||||||
case TrimPathBase.offsetPropertyKey:
|
case TrimPathBase.offsetPropertyKey:
|
||||||
case PathVertexBase.xPropertyKey:
|
case VertexBase.xPropertyKey:
|
||||||
case PathVertexBase.yPropertyKey:
|
case VertexBase.yPropertyKey:
|
||||||
|
case MeshVertexBase.uPropertyKey:
|
||||||
|
case MeshVertexBase.vPropertyKey:
|
||||||
case StraightVertexBase.radiusPropertyKey:
|
case StraightVertexBase.radiusPropertyKey:
|
||||||
case CubicAsymmetricVertexBase.rotationPropertyKey:
|
case CubicAsymmetricVertexBase.rotationPropertyKey:
|
||||||
case CubicAsymmetricVertexBase.inDistancePropertyKey:
|
case CubicAsymmetricVertexBase.inDistancePropertyKey:
|
||||||
case CubicAsymmetricVertexBase.outDistancePropertyKey:
|
case CubicAsymmetricVertexBase.outDistancePropertyKey:
|
||||||
|
case TextRunBase.pointSizePropertyKey:
|
||||||
case ParametricPathBase.widthPropertyKey:
|
case ParametricPathBase.widthPropertyKey:
|
||||||
case ParametricPathBase.heightPropertyKey:
|
case ParametricPathBase.heightPropertyKey:
|
||||||
case ParametricPathBase.originXPropertyKey:
|
case ParametricPathBase.originXPropertyKey:
|
||||||
@ -1230,6 +1280,7 @@ class RiveCoreContext {
|
|||||||
case SolidColorBase.colorValuePropertyKey:
|
case SolidColorBase.colorValuePropertyKey:
|
||||||
case GradientStopBase.colorValuePropertyKey:
|
case GradientStopBase.colorValuePropertyKey:
|
||||||
return colorType;
|
return colorType;
|
||||||
|
case MeshBase.triangleIndexBytesPropertyKey:
|
||||||
case FileAssetContentsBase.bytesPropertyKey:
|
case FileAssetContentsBase.bytesPropertyKey:
|
||||||
return bytesType;
|
return bytesType;
|
||||||
default:
|
default:
|
||||||
@ -1245,6 +1296,8 @@ class RiveCoreContext {
|
|||||||
return (object as AnimationBase).name;
|
return (object as AnimationBase).name;
|
||||||
case StateMachineComponentBase.namePropertyKey:
|
case StateMachineComponentBase.namePropertyKey:
|
||||||
return (object as StateMachineComponentBase).name;
|
return (object as StateMachineComponentBase).name;
|
||||||
|
case TextBase.valuePropertyKey:
|
||||||
|
return (object as TextBase).value;
|
||||||
case AssetBase.namePropertyKey:
|
case AssetBase.namePropertyKey:
|
||||||
return (object as AssetBase).name;
|
return (object as AssetBase).name;
|
||||||
}
|
}
|
||||||
@ -1345,6 +1398,8 @@ class RiveCoreContext {
|
|||||||
return (object as CubicWeightBase).outValues;
|
return (object as CubicWeightBase).outValues;
|
||||||
case CubicWeightBase.outIndicesPropertyKey:
|
case CubicWeightBase.outIndicesPropertyKey:
|
||||||
return (object as CubicWeightBase).outIndices;
|
return (object as CubicWeightBase).outIndices;
|
||||||
|
case TextRunBase.textLengthPropertyKey:
|
||||||
|
return (object as TextRunBase).textLength;
|
||||||
case ClippingShapeBase.sourceIdPropertyKey:
|
case ClippingShapeBase.sourceIdPropertyKey:
|
||||||
return (object as ClippingShapeBase).sourceId;
|
return (object as ClippingShapeBase).sourceId;
|
||||||
case ClippingShapeBase.fillRulePropertyKey:
|
case ClippingShapeBase.fillRulePropertyKey:
|
||||||
@ -1437,10 +1492,14 @@ class RiveCoreContext {
|
|||||||
return (object as TrimPathBase).end;
|
return (object as TrimPathBase).end;
|
||||||
case TrimPathBase.offsetPropertyKey:
|
case TrimPathBase.offsetPropertyKey:
|
||||||
return (object as TrimPathBase).offset;
|
return (object as TrimPathBase).offset;
|
||||||
case PathVertexBase.xPropertyKey:
|
case VertexBase.xPropertyKey:
|
||||||
return (object as PathVertexBase).x;
|
return (object as VertexBase).x;
|
||||||
case PathVertexBase.yPropertyKey:
|
case VertexBase.yPropertyKey:
|
||||||
return (object as PathVertexBase).y;
|
return (object as VertexBase).y;
|
||||||
|
case MeshVertexBase.uPropertyKey:
|
||||||
|
return (object as MeshVertexBase).u;
|
||||||
|
case MeshVertexBase.vPropertyKey:
|
||||||
|
return (object as MeshVertexBase).v;
|
||||||
case StraightVertexBase.radiusPropertyKey:
|
case StraightVertexBase.radiusPropertyKey:
|
||||||
return (object as StraightVertexBase).radius;
|
return (object as StraightVertexBase).radius;
|
||||||
case CubicAsymmetricVertexBase.rotationPropertyKey:
|
case CubicAsymmetricVertexBase.rotationPropertyKey:
|
||||||
@ -1449,6 +1508,8 @@ class RiveCoreContext {
|
|||||||
return (object as CubicAsymmetricVertexBase).inDistance;
|
return (object as CubicAsymmetricVertexBase).inDistance;
|
||||||
case CubicAsymmetricVertexBase.outDistancePropertyKey:
|
case CubicAsymmetricVertexBase.outDistancePropertyKey:
|
||||||
return (object as CubicAsymmetricVertexBase).outDistance;
|
return (object as CubicAsymmetricVertexBase).outDistance;
|
||||||
|
case TextRunBase.pointSizePropertyKey:
|
||||||
|
return (object as TextRunBase).pointSize;
|
||||||
case ParametricPathBase.widthPropertyKey:
|
case ParametricPathBase.widthPropertyKey:
|
||||||
return (object as ParametricPathBase).width;
|
return (object as ParametricPathBase).width;
|
||||||
case ParametricPathBase.heightPropertyKey:
|
case ParametricPathBase.heightPropertyKey:
|
||||||
@ -1585,12 +1646,14 @@ class RiveCoreContext {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<int> getBytes(Core object, int propertyKey) {
|
static Uint8List getBytes(Core object, int propertyKey) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
|
case MeshBase.triangleIndexBytesPropertyKey:
|
||||||
|
return (object as MeshBase).triangleIndexBytes;
|
||||||
case FileAssetContentsBase.bytesPropertyKey:
|
case FileAssetContentsBase.bytesPropertyKey:
|
||||||
return (object as FileAssetContentsBase).bytes;
|
return (object as FileAssetContentsBase).bytes;
|
||||||
}
|
}
|
||||||
return [];
|
return Uint8List(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setString(Core object, int propertyKey, String value) {
|
static void setString(Core object, int propertyKey, String value) {
|
||||||
@ -1610,6 +1673,11 @@ class RiveCoreContext {
|
|||||||
object.name = value;
|
object.name = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TextBase.valuePropertyKey:
|
||||||
|
if (object is TextBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case AssetBase.namePropertyKey:
|
case AssetBase.namePropertyKey:
|
||||||
if (object is AssetBase) {
|
if (object is AssetBase) {
|
||||||
object.name = value;
|
object.name = value;
|
||||||
@ -1850,6 +1918,11 @@ class RiveCoreContext {
|
|||||||
object.outIndices = value;
|
object.outIndices = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TextRunBase.textLengthPropertyKey:
|
||||||
|
if (object is TextRunBase) {
|
||||||
|
object.textLength = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ClippingShapeBase.sourceIdPropertyKey:
|
case ClippingShapeBase.sourceIdPropertyKey:
|
||||||
if (object is ClippingShapeBase) {
|
if (object is ClippingShapeBase) {
|
||||||
object.sourceId = value;
|
object.sourceId = value;
|
||||||
@ -2070,16 +2143,26 @@ class RiveCoreContext {
|
|||||||
object.offset = value;
|
object.offset = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PathVertexBase.xPropertyKey:
|
case VertexBase.xPropertyKey:
|
||||||
if (object is PathVertexBase) {
|
if (object is VertexBase) {
|
||||||
object.x = value;
|
object.x = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PathVertexBase.yPropertyKey:
|
case VertexBase.yPropertyKey:
|
||||||
if (object is PathVertexBase) {
|
if (object is VertexBase) {
|
||||||
object.y = value;
|
object.y = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MeshVertexBase.uPropertyKey:
|
||||||
|
if (object is MeshVertexBase) {
|
||||||
|
object.u = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MeshVertexBase.vPropertyKey:
|
||||||
|
if (object is MeshVertexBase) {
|
||||||
|
object.v = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case StraightVertexBase.radiusPropertyKey:
|
case StraightVertexBase.radiusPropertyKey:
|
||||||
if (object is StraightVertexBase) {
|
if (object is StraightVertexBase) {
|
||||||
object.radius = value;
|
object.radius = value;
|
||||||
@ -2100,6 +2183,11 @@ class RiveCoreContext {
|
|||||||
object.outDistance = value;
|
object.outDistance = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TextRunBase.pointSizePropertyKey:
|
||||||
|
if (object is TextRunBase) {
|
||||||
|
object.pointSize = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ParametricPathBase.widthPropertyKey:
|
case ParametricPathBase.widthPropertyKey:
|
||||||
if (object is ParametricPathBase) {
|
if (object is ParametricPathBase) {
|
||||||
object.width = value;
|
object.width = value;
|
||||||
@ -2413,8 +2501,13 @@ class RiveCoreContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setBytes(Core object, int propertyKey, List<int> value) {
|
static void setBytes(Core object, int propertyKey, Uint8List value) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
|
case MeshBase.triangleIndexBytesPropertyKey:
|
||||||
|
if (object is MeshBase) {
|
||||||
|
object.triangleIndexBytes = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case FileAssetContentsBase.bytesPropertyKey:
|
case FileAssetContentsBase.bytesPropertyKey:
|
||||||
if (object is FileAssetContentsBase) {
|
if (object is FileAssetContentsBase) {
|
||||||
object.bytes = value;
|
object.bytes = value;
|
||||||
|
22
lib/src/generated/shapes/contour_mesh_vertex_base.dart
Normal file
22
lib/src/generated/shapes/contour_mesh_vertex_base.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/shapes/contour_mesh_vertex_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh_vertex.dart';
|
||||||
|
|
||||||
|
abstract class ContourMeshVertexBase extends MeshVertex {
|
||||||
|
static const int typeKey = 111;
|
||||||
|
@override
|
||||||
|
int get coreType => ContourMeshVertexBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
ContourMeshVertexBase.typeKey,
|
||||||
|
MeshVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
|
ContainerComponentBase.typeKey,
|
||||||
|
ComponentBase.typeKey
|
||||||
|
};
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/cubic_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_vertex.dart';
|
||||||
|
|
||||||
abstract class CubicAsymmetricVertexBase extends CubicVertex {
|
abstract class CubicAsymmetricVertexBase extends CubicVertex {
|
||||||
@ -16,6 +17,7 @@ abstract class CubicAsymmetricVertexBase extends CubicVertex {
|
|||||||
CubicAsymmetricVertexBase.typeKey,
|
CubicAsymmetricVertexBase.typeKey,
|
||||||
CubicVertexBase.typeKey,
|
CubicVertexBase.typeKey,
|
||||||
PathVertexBase.typeKey,
|
PathVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
ContainerComponentBase.typeKey,
|
ContainerComponentBase.typeKey,
|
||||||
ComponentBase.typeKey
|
ComponentBase.typeKey
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/cubic_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_vertex.dart';
|
||||||
|
|
||||||
abstract class CubicDetachedVertexBase extends CubicVertex {
|
abstract class CubicDetachedVertexBase extends CubicVertex {
|
||||||
@ -16,6 +17,7 @@ abstract class CubicDetachedVertexBase extends CubicVertex {
|
|||||||
CubicDetachedVertexBase.typeKey,
|
CubicDetachedVertexBase.typeKey,
|
||||||
CubicVertexBase.typeKey,
|
CubicVertexBase.typeKey,
|
||||||
PathVertexBase.typeKey,
|
PathVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
ContainerComponentBase.typeKey,
|
ContainerComponentBase.typeKey,
|
||||||
ComponentBase.typeKey
|
ComponentBase.typeKey
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/cubic_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_vertex.dart';
|
||||||
|
|
||||||
abstract class CubicMirroredVertexBase extends CubicVertex {
|
abstract class CubicMirroredVertexBase extends CubicVertex {
|
||||||
@ -16,6 +17,7 @@ abstract class CubicMirroredVertexBase extends CubicVertex {
|
|||||||
CubicMirroredVertexBase.typeKey,
|
CubicMirroredVertexBase.typeKey,
|
||||||
CubicVertexBase.typeKey,
|
CubicVertexBase.typeKey,
|
||||||
PathVertexBase.typeKey,
|
PathVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
ContainerComponentBase.typeKey,
|
ContainerComponentBase.typeKey,
|
||||||
ComponentBase.typeKey
|
ComponentBase.typeKey
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
import 'package:rive/src/rive_core/bones/cubic_weight.dart';
|
import 'package:rive/src/rive_core/bones/cubic_weight.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ abstract class CubicVertexBase extends PathVertex<CubicWeight> {
|
|||||||
Set<int> get coreTypes => {
|
Set<int> get coreTypes => {
|
||||||
CubicVertexBase.typeKey,
|
CubicVertexBase.typeKey,
|
||||||
PathVertexBase.typeKey,
|
PathVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
ContainerComponentBase.typeKey,
|
ContainerComponentBase.typeKey,
|
||||||
ComponentBase.typeKey
|
ComponentBase.typeKey
|
||||||
};
|
};
|
||||||
|
67
lib/src/generated/shapes/forced_edge_base.dart
Normal file
67
lib/src/generated/shapes/forced_edge_base.dart
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/// Core automatically generated lib/src/generated/shapes/forced_edge_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
|
|
||||||
|
abstract class ForcedEdgeBase extends Component {
|
||||||
|
static const int typeKey = 112;
|
||||||
|
@override
|
||||||
|
int get coreType => ForcedEdgeBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {ForcedEdgeBase.typeKey, ComponentBase.typeKey};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// FromId field with key 219.
|
||||||
|
static const int fromIdInitialValue = 0;
|
||||||
|
int _fromId = fromIdInitialValue;
|
||||||
|
static const int fromIdPropertyKey = 219;
|
||||||
|
|
||||||
|
/// Identifier used to track MeshVertex the force edge extends from.
|
||||||
|
int get fromId => _fromId;
|
||||||
|
|
||||||
|
/// Change the [_fromId] field value.
|
||||||
|
/// [fromIdChanged] will be invoked only if the field's value has changed.
|
||||||
|
set fromId(int value) {
|
||||||
|
if (_fromId == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _fromId;
|
||||||
|
_fromId = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
fromIdChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fromIdChanged(int from, int to);
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// ToId field with key 220.
|
||||||
|
static const int toIdInitialValue = 0;
|
||||||
|
int _toId = toIdInitialValue;
|
||||||
|
static const int toIdPropertyKey = 220;
|
||||||
|
|
||||||
|
/// Identifier used to track MeshVertex the force edge extends to.
|
||||||
|
int get toId => _toId;
|
||||||
|
|
||||||
|
/// Change the [_toId] field value.
|
||||||
|
/// [toIdChanged] will be invoked only if the field's value has changed.
|
||||||
|
set toId(int value) {
|
||||||
|
if (_toId == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _toId;
|
||||||
|
_toId = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
toIdChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void toIdChanged(int from, int to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void copy(covariant ForcedEdgeBase source) {
|
||||||
|
super.copy(source);
|
||||||
|
_fromId = source._fromId;
|
||||||
|
_toId = source._toId;
|
||||||
|
}
|
||||||
|
}
|
47
lib/src/generated/shapes/mesh_base.dart
Normal file
47
lib/src/generated/shapes/mesh_base.dart
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/// Core automatically generated lib/src/generated/shapes/mesh_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/container_component.dart';
|
||||||
|
|
||||||
|
abstract class MeshBase extends ContainerComponent {
|
||||||
|
static const int typeKey = 109;
|
||||||
|
@override
|
||||||
|
int get coreType => MeshBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes =>
|
||||||
|
{MeshBase.typeKey, ContainerComponentBase.typeKey, ComponentBase.typeKey};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// TriangleIndexBytes field with key 223.
|
||||||
|
static final Uint8List triangleIndexBytesInitialValue = Uint8List(0);
|
||||||
|
Uint8List _triangleIndexBytes = triangleIndexBytesInitialValue;
|
||||||
|
static const int triangleIndexBytesPropertyKey = 223;
|
||||||
|
|
||||||
|
/// Byte data for the triangle indices.
|
||||||
|
Uint8List get triangleIndexBytes => _triangleIndexBytes;
|
||||||
|
|
||||||
|
/// Change the [_triangleIndexBytes] field value.
|
||||||
|
/// [triangleIndexBytesChanged] will be invoked only if the field's value has
|
||||||
|
/// changed.
|
||||||
|
set triangleIndexBytes(Uint8List value) {
|
||||||
|
if (listEquals(_triangleIndexBytes, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Uint8List from = _triangleIndexBytes;
|
||||||
|
_triangleIndexBytes = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
triangleIndexBytesChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void triangleIndexBytesChanged(Uint8List from, Uint8List to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void copy(covariant MeshBase source) {
|
||||||
|
super.copy(source);
|
||||||
|
_triangleIndexBytes = source._triangleIndexBytes;
|
||||||
|
}
|
||||||
|
}
|
74
lib/src/generated/shapes/mesh_vertex_base.dart
Normal file
74
lib/src/generated/shapes/mesh_vertex_base.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/// Core automatically generated lib/src/generated/shapes/mesh_vertex_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/vertex.dart';
|
||||||
|
|
||||||
|
abstract class MeshVertexBase extends Vertex {
|
||||||
|
static const int typeKey = 108;
|
||||||
|
@override
|
||||||
|
int get coreType => MeshVertexBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
MeshVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
|
ContainerComponentBase.typeKey,
|
||||||
|
ComponentBase.typeKey
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// U field with key 215.
|
||||||
|
static const double uInitialValue = 0;
|
||||||
|
double _u = uInitialValue;
|
||||||
|
static const int uPropertyKey = 215;
|
||||||
|
|
||||||
|
/// U value for the texture coordinate of the vertex.
|
||||||
|
double get u => _u;
|
||||||
|
|
||||||
|
/// Change the [_u] field value.
|
||||||
|
/// [uChanged] will be invoked only if the field's value has changed.
|
||||||
|
set u(double value) {
|
||||||
|
if (_u == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double from = _u;
|
||||||
|
_u = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
uChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uChanged(double from, double to);
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// V field with key 216.
|
||||||
|
static const double vInitialValue = 0;
|
||||||
|
double _v = vInitialValue;
|
||||||
|
static const int vPropertyKey = 216;
|
||||||
|
|
||||||
|
/// V value for the texture coordinate of the vertex.
|
||||||
|
double get v => _v;
|
||||||
|
|
||||||
|
/// Change the [_v] field value.
|
||||||
|
/// [vChanged] will be invoked only if the field's value has changed.
|
||||||
|
set v(double value) {
|
||||||
|
if (_v == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double from = _v;
|
||||||
|
_v = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
vChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vChanged(double from, double to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void copy(covariant MeshVertexBase source) {
|
||||||
|
super.copy(source);
|
||||||
|
_u = source._u;
|
||||||
|
_v = source._v;
|
||||||
|
}
|
||||||
|
}
|
@ -3,71 +3,18 @@
|
|||||||
|
|
||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
import 'package:rive/src/rive_core/container_component.dart';
|
import 'package:rive/src/rive_core/bones/weight.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/vertex.dart';
|
||||||
|
|
||||||
abstract class PathVertexBase extends ContainerComponent {
|
abstract class PathVertexBase<T extends Weight> extends Vertex<T> {
|
||||||
static const int typeKey = 14;
|
static const int typeKey = 14;
|
||||||
@override
|
@override
|
||||||
int get coreType => PathVertexBase.typeKey;
|
int get coreType => PathVertexBase.typeKey;
|
||||||
@override
|
@override
|
||||||
Set<int> get coreTypes => {
|
Set<int> get coreTypes => {
|
||||||
PathVertexBase.typeKey,
|
PathVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
ContainerComponentBase.typeKey,
|
ContainerComponentBase.typeKey,
|
||||||
ComponentBase.typeKey
|
ComponentBase.typeKey
|
||||||
};
|
};
|
||||||
|
|
||||||
/// --------------------------------------------------------------------------
|
|
||||||
/// X field with key 24.
|
|
||||||
static const double xInitialValue = 0;
|
|
||||||
double _x = xInitialValue;
|
|
||||||
static const int xPropertyKey = 24;
|
|
||||||
|
|
||||||
/// X value for the translation of the vertex.
|
|
||||||
double get x => _x;
|
|
||||||
|
|
||||||
/// Change the [_x] field value.
|
|
||||||
/// [xChanged] will be invoked only if the field's value has changed.
|
|
||||||
set x(double value) {
|
|
||||||
if (_x == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
double from = _x;
|
|
||||||
_x = value;
|
|
||||||
if (hasValidated) {
|
|
||||||
xChanged(from, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void xChanged(double from, double to);
|
|
||||||
|
|
||||||
/// --------------------------------------------------------------------------
|
|
||||||
/// Y field with key 25.
|
|
||||||
static const double yInitialValue = 0;
|
|
||||||
double _y = yInitialValue;
|
|
||||||
static const int yPropertyKey = 25;
|
|
||||||
|
|
||||||
/// Y value for the translation of the vertex.
|
|
||||||
double get y => _y;
|
|
||||||
|
|
||||||
/// Change the [_y] field value.
|
|
||||||
/// [yChanged] will be invoked only if the field's value has changed.
|
|
||||||
set y(double value) {
|
|
||||||
if (_y == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
double from = _y;
|
|
||||||
_y = value;
|
|
||||||
if (hasValidated) {
|
|
||||||
yChanged(from, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void yChanged(double from, double to);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void copy(covariant PathVertexBase source) {
|
|
||||||
super.copy(source);
|
|
||||||
_x = source._x;
|
|
||||||
_y = source._y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
import 'package:rive/src/rive_core/bones/weight.dart';
|
import 'package:rive/src/rive_core/bones/weight.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ abstract class StraightVertexBase extends PathVertex<Weight> {
|
|||||||
Set<int> get coreTypes => {
|
Set<int> get coreTypes => {
|
||||||
StraightVertexBase.typeKey,
|
StraightVertexBase.typeKey,
|
||||||
PathVertexBase.typeKey,
|
PathVertexBase.typeKey,
|
||||||
|
VertexBase.typeKey,
|
||||||
ContainerComponentBase.typeKey,
|
ContainerComponentBase.typeKey,
|
||||||
ComponentBase.typeKey
|
ComponentBase.typeKey
|
||||||
};
|
};
|
||||||
|
53
lib/src/generated/shapes/text_base.dart
Normal file
53
lib/src/generated/shapes/text_base.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/// Core automatically generated lib/src/generated/shapes/text_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/transform_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/world_transform_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/node.dart';
|
||||||
|
|
||||||
|
abstract class TextBase extends Node {
|
||||||
|
static const int typeKey = 110;
|
||||||
|
@override
|
||||||
|
int get coreType => TextBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
TextBase.typeKey,
|
||||||
|
NodeBase.typeKey,
|
||||||
|
TransformComponentBase.typeKey,
|
||||||
|
WorldTransformComponentBase.typeKey,
|
||||||
|
ContainerComponentBase.typeKey,
|
||||||
|
ComponentBase.typeKey
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// Value field with key 218.
|
||||||
|
static const String valueInitialValue = '';
|
||||||
|
String _value = valueInitialValue;
|
||||||
|
static const int valuePropertyKey = 218;
|
||||||
|
|
||||||
|
/// The value stored in the Text object.
|
||||||
|
String get value => _value;
|
||||||
|
|
||||||
|
/// Change the [_value] field value.
|
||||||
|
/// [valueChanged] will be invoked only if the field's value has changed.
|
||||||
|
set value(String value) {
|
||||||
|
if (_value == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String from = _value;
|
||||||
|
_value = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
valueChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueChanged(String from, String to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void copy(covariant TextBase source) {
|
||||||
|
super.copy(source);
|
||||||
|
_value = source._value;
|
||||||
|
}
|
||||||
|
}
|
80
lib/src/generated/shapes/text_run_base.dart
Normal file
80
lib/src/generated/shapes/text_run_base.dart
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/// Core automatically generated lib/src/generated/shapes/text_run_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/node_base.dart';
|
||||||
|
import 'package:rive/src/generated/transform_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/world_transform_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/drawable.dart';
|
||||||
|
|
||||||
|
abstract class TextRunBase extends Drawable {
|
||||||
|
static const int typeKey = 113;
|
||||||
|
@override
|
||||||
|
int get coreType => TextRunBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
TextRunBase.typeKey,
|
||||||
|
DrawableBase.typeKey,
|
||||||
|
NodeBase.typeKey,
|
||||||
|
TransformComponentBase.typeKey,
|
||||||
|
WorldTransformComponentBase.typeKey,
|
||||||
|
ContainerComponentBase.typeKey,
|
||||||
|
ComponentBase.typeKey
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// PointSize field with key 221.
|
||||||
|
static const double pointSizeInitialValue = 16;
|
||||||
|
double _pointSize = pointSizeInitialValue;
|
||||||
|
static const int pointSizePropertyKey = 221;
|
||||||
|
|
||||||
|
/// The point size for text styled by this run.
|
||||||
|
double get pointSize => _pointSize;
|
||||||
|
|
||||||
|
/// Change the [_pointSize] field value.
|
||||||
|
/// [pointSizeChanged] will be invoked only if the field's value has changed.
|
||||||
|
set pointSize(double value) {
|
||||||
|
if (_pointSize == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double from = _pointSize;
|
||||||
|
_pointSize = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
pointSizeChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointSizeChanged(double from, double to);
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// TextLength field with key 222.
|
||||||
|
static const int textLengthInitialValue = 0;
|
||||||
|
int _textLength = textLengthInitialValue;
|
||||||
|
static const int textLengthPropertyKey = 222;
|
||||||
|
|
||||||
|
/// The length of the text styled by this run.
|
||||||
|
int get textLength => _textLength;
|
||||||
|
|
||||||
|
/// Change the [_textLength] field value.
|
||||||
|
/// [textLengthChanged] will be invoked only if the field's value has changed.
|
||||||
|
set textLength(int value) {
|
||||||
|
if (_textLength == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _textLength;
|
||||||
|
_textLength = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
textLengthChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void textLengthChanged(int from, int to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void copy(covariant TextRunBase source) {
|
||||||
|
super.copy(source);
|
||||||
|
_pointSize = source._pointSize;
|
||||||
|
_textLength = source._textLength;
|
||||||
|
}
|
||||||
|
}
|
73
lib/src/generated/shapes/vertex_base.dart
Normal file
73
lib/src/generated/shapes/vertex_base.dart
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/// Core automatically generated lib/src/generated/shapes/vertex_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/container_component.dart';
|
||||||
|
|
||||||
|
abstract class VertexBase extends ContainerComponent {
|
||||||
|
static const int typeKey = 107;
|
||||||
|
@override
|
||||||
|
int get coreType => VertexBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
VertexBase.typeKey,
|
||||||
|
ContainerComponentBase.typeKey,
|
||||||
|
ComponentBase.typeKey
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// X field with key 24.
|
||||||
|
static const double xInitialValue = 0;
|
||||||
|
double _x = xInitialValue;
|
||||||
|
static const int xPropertyKey = 24;
|
||||||
|
|
||||||
|
/// X value for the translation of the vertex.
|
||||||
|
double get x => _x;
|
||||||
|
|
||||||
|
/// Change the [_x] field value.
|
||||||
|
/// [xChanged] will be invoked only if the field's value has changed.
|
||||||
|
set x(double value) {
|
||||||
|
if (_x == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double from = _x;
|
||||||
|
_x = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
xChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void xChanged(double from, double to);
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// Y field with key 25.
|
||||||
|
static const double yInitialValue = 0;
|
||||||
|
double _y = yInitialValue;
|
||||||
|
static const int yPropertyKey = 25;
|
||||||
|
|
||||||
|
/// Y value for the translation of the vertex.
|
||||||
|
double get y => _y;
|
||||||
|
|
||||||
|
/// Change the [_y] field value.
|
||||||
|
/// [yChanged] will be invoked only if the field's value has changed.
|
||||||
|
set y(double value) {
|
||||||
|
if (_y == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double from = _y;
|
||||||
|
_y = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
yChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void yChanged(double from, double to);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void copy(covariant VertexBase source) {
|
||||||
|
super.copy(source);
|
||||||
|
_x = source._x;
|
||||||
|
_y = source._y;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/generated/animation/blend_animation_base.dart';
|
import 'package:rive/src/generated/animation/blend_animation_base.dart';
|
||||||
import 'package:rive/src/generated/artboard_base.dart';
|
|
||||||
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||||
import 'package:rive/src/rive_core/artboard.dart';
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
import 'package:rive/src/rive_core/animation/blend_state.dart';
|
import 'package:rive/src/rive_core/animation/blend_state.dart';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/generated/animation/cubic_interpolator_base.dart';
|
import 'package:rive/src/generated/animation/cubic_interpolator_base.dart';
|
||||||
import 'package:rive/src/rive_core/animation/interpolator.dart';
|
import 'package:rive/src/rive_core/animation/interpolator.dart';
|
||||||
|
@ -7,7 +7,5 @@ abstract class TransitionValueCondition extends TransitionValueConditionBase {
|
|||||||
TransitionConditionOp get op => TransitionConditionOp.values[opValue];
|
TransitionConditionOp get op => TransitionConditionOp.values[opValue];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void opValueChanged(int from, int to) {
|
void opValueChanged(int from, int to) {}
|
||||||
// TODO: implement opValueChanged
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:rive/src/generated/assets/asset_base.dart';
|
import 'package:rive/src/generated/assets/asset_base.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/assets/asset_base.dart';
|
export 'package:rive/src/generated/assets/asset_base.dart';
|
||||||
|
|
||||||
class Asset extends AssetBase {
|
class Asset extends AssetBase {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/generated/assets/file_asset_base.dart';
|
import 'package:rive/src/generated/assets/file_asset_base.dart';
|
||||||
import 'package:rive/src/rive_core/backboard.dart';
|
import 'package:rive/src/rive_core/backboard.dart';
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rive/src/generated/assets/image_asset_base.dart';
|
import 'package:rive/src/generated/assets/image_asset_base.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/image.dart';
|
import 'package:rive/src/rive_core/shapes/image.dart';
|
||||||
|
|
||||||
@ -11,6 +12,11 @@ class ImageAsset extends ImageAssetBase {
|
|||||||
ui.Image? _image;
|
ui.Image? _image;
|
||||||
ui.Image? get image => _image;
|
ui.Image? get image => _image;
|
||||||
|
|
||||||
|
ImageAsset();
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
ImageAsset.fromTestImage(this._image);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> decode(Uint8List bytes) {
|
Future<void> decode(Uint8List bytes) {
|
||||||
var completer = Completer<void>();
|
var completer = Completer<void>();
|
||||||
|
@ -6,7 +6,7 @@ import 'package:rive/src/rive_core/bones/skinnable.dart';
|
|||||||
import 'package:rive/src/rive_core/bones/tendon.dart';
|
import 'package:rive/src/rive_core/bones/tendon.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/vertex.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/bones/skin_base.dart';
|
export 'package:rive/src/generated/bones/skin_base.dart';
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ class Skin extends SkinBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void deform(List<PathVertex> vertices) {
|
void deform(List<Vertex> vertices) {
|
||||||
for (final vertex in vertices) {
|
for (final vertex in vertices) {
|
||||||
vertex.deform(_worldTransform, _boneTransforms);
|
vertex.deform(_worldTransform, _boneTransforms);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'package:rive/src/rive_core/bones/skin.dart';
|
import 'package:rive/src/rive_core/bones/skin.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
|
|
||||||
|
import 'package:rive/src/rive_core/shapes/vertex.dart';
|
||||||
|
|
||||||
/// An abstraction to give a common interface to any container component that
|
/// An abstraction to give a common interface to any container component that
|
||||||
/// can contain a skin to bind bones to.
|
/// can contain a skin to bind bones to.
|
||||||
abstract class Skinnable {
|
abstract class Skinnable<T extends Vertex> {
|
||||||
// _skin is null when this object isn't connected to bones.
|
// _skin is null when this object isn't connected to bones.
|
||||||
Skin? _skin;
|
Skin? _skin;
|
||||||
Skin? get skin => _skin;
|
Skin? get skin => _skin;
|
||||||
@ -28,3 +30,7 @@ abstract class Skinnable {
|
|||||||
|
|
||||||
void markSkinDirty();
|
void markSkinDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class SkinnableProvider<T extends Vertex> {
|
||||||
|
Skinnable<T>? get skinnable;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/rive_core/artboard.dart';
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
|
@ -14,6 +14,9 @@ class ComponentDirt {
|
|||||||
/// Path is dirty and needs to be rebuilt.
|
/// Path is dirty and needs to be rebuilt.
|
||||||
static const int path = 1 << 4;
|
static const int path = 1 << 4;
|
||||||
|
|
||||||
|
/// Text shape is dirty, the shaper needs to re-run.
|
||||||
|
static const int textShape = 1 << 4;
|
||||||
|
|
||||||
/// Vertices have changed, re-order cached lists.
|
/// Vertices have changed, re-order cached lists.
|
||||||
static const int vertices = 1 << 5;
|
static const int vertices = 1 << 5;
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/generated/container_component_base.dart';
|
import 'package:rive/src/generated/container_component_base.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
@ -9,6 +8,9 @@ typedef bool DescentCallback(Component component);
|
|||||||
|
|
||||||
abstract class ContainerComponent extends ContainerComponentBase {
|
abstract class ContainerComponent extends ContainerComponentBase {
|
||||||
final ContainerChildren children = ContainerChildren();
|
final ContainerChildren children = ContainerChildren();
|
||||||
|
|
||||||
|
/// Adds the child to the children list and re-wires the parent reference of
|
||||||
|
/// the child to the parent. Effectively detach and append.
|
||||||
void appendChild(Component child) {
|
void appendChild(Component child) {
|
||||||
child.parent = this;
|
child.parent = this;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
// Just a way to get around the protected notifyListeners so we can use trigger
|
// Just a way to get around the protected notifyListeners so we can use trigger
|
||||||
// multiple events from a single object.
|
// multiple events from a single object.
|
||||||
|
4
lib/src/rive_core/shapes/contour_mesh_vertex.dart
Normal file
4
lib/src/rive_core/shapes/contour_mesh_vertex.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import 'package:rive/src/generated/shapes/contour_mesh_vertex_base.dart';
|
||||||
|
export 'package:rive/src/generated/shapes/contour_mesh_vertex_base.dart';
|
||||||
|
|
||||||
|
class ContourMeshVertex extends ContourMeshVertexBase {}
|
@ -4,13 +4,27 @@ import 'package:rive/src/core/core.dart';
|
|||||||
import 'package:rive/src/generated/shapes/image_base.dart';
|
import 'package:rive/src/generated/shapes/image_base.dart';
|
||||||
import 'package:rive/src/rive_core/assets/file_asset.dart';
|
import 'package:rive/src/rive_core/assets/file_asset.dart';
|
||||||
import 'package:rive/src/rive_core/assets/image_asset.dart';
|
import 'package:rive/src/rive_core/assets/image_asset.dart';
|
||||||
|
import 'package:rive/src/rive_core/bones/skinnable.dart';
|
||||||
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
import 'package:rive/src/rive_core/math/aabb.dart';
|
import 'package:rive/src/rive_core/math/aabb.dart';
|
||||||
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh_vertex.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/shapes/image_base.dart';
|
export 'package:rive/src/generated/shapes/image_base.dart';
|
||||||
|
|
||||||
class Image extends ImageBase with FileAssetReferencer<ImageAsset> {
|
class Image extends ImageBase
|
||||||
|
with FileAssetReferencer<ImageAsset>, SkinnableProvider<MeshVertex> {
|
||||||
|
ui.Image? get image => asset?.image;
|
||||||
|
Mesh? _mesh;
|
||||||
|
Mesh? get mesh => _mesh;
|
||||||
|
bool get hasMesh => _mesh != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AABB get localBounds {
|
AABB get localBounds {
|
||||||
|
if (hasMesh && _mesh!.draws) {
|
||||||
|
return _mesh!.bounds;
|
||||||
|
}
|
||||||
if (asset == null) {
|
if (asset == null) {
|
||||||
return AABB.empty();
|
return AABB.empty();
|
||||||
}
|
}
|
||||||
@ -36,8 +50,34 @@ class Image extends ImageBase with FileAssetReferencer<ImageAsset> {
|
|||||||
final height = asset!.height;
|
final height = asset!.height;
|
||||||
|
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.transform(worldTransform.mat4);
|
canvas.transform(renderTransform.mat4);
|
||||||
canvas.drawImage(uiImage, ui.Offset(-width / 2, -height / 2), paint);
|
if (_mesh == null || !_mesh!.draws) {
|
||||||
|
canvas.drawImage(uiImage, ui.Offset(-width / 2, -height / 2), paint);
|
||||||
|
} else {
|
||||||
|
paint.shader = ui.ImageShader(
|
||||||
|
uiImage,
|
||||||
|
ui.TileMode.clamp,
|
||||||
|
ui.TileMode.clamp,
|
||||||
|
Float64List.fromList(<double>[
|
||||||
|
1 / width,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1 / height,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]));
|
||||||
|
_mesh!.draw(canvas, paint);
|
||||||
|
}
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
|
|
||||||
if (clipped) {
|
if (clipped) {
|
||||||
@ -62,4 +102,25 @@ class Image extends ImageBase with FileAssetReferencer<ImageAsset> {
|
|||||||
super.copy(source);
|
super.copy(source);
|
||||||
asset = source.asset;
|
asset = source.asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void childAdded(Component child) {
|
||||||
|
super.childAdded(child);
|
||||||
|
if (child is Mesh) {
|
||||||
|
_mesh = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void childRemoved(Component child) {
|
||||||
|
super.childRemoved(child);
|
||||||
|
if (child is Mesh && _mesh == child) {
|
||||||
|
_mesh = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Skinnable<MeshVertex>? get skinnable => _mesh;
|
||||||
|
|
||||||
|
Mat2D get renderTransform => _mesh?.worldTransform ?? worldTransform;
|
||||||
}
|
}
|
||||||
|
147
lib/src/rive_core/shapes/mesh.dart
Normal file
147
lib/src/rive_core/shapes/mesh.dart
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/shapes/mesh_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/bones/skinnable.dart';
|
||||||
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
|
import 'package:rive/src/rive_core/drawable.dart';
|
||||||
|
import 'package:rive/src/rive_core/math/aabb.dart';
|
||||||
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/contour_mesh_vertex.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/image.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh_vertex.dart';
|
||||||
|
import 'package:rive/src/rive_core/transform_component.dart';
|
||||||
|
import 'package:rive/src/utilities/binary_buffer/binary_reader.dart';
|
||||||
|
|
||||||
|
export 'package:rive/src/generated/shapes/mesh_base.dart';
|
||||||
|
|
||||||
|
class Mesh extends MeshBase with Skinnable<MeshVertex> {
|
||||||
|
// When bound to bones pathTransform should be the identity as it'll already
|
||||||
|
// be in world space.
|
||||||
|
|
||||||
|
final List<MeshVertex> _vertices = [];
|
||||||
|
@override
|
||||||
|
List<MeshVertex> get vertices => _vertices;
|
||||||
|
|
||||||
|
ui.Vertices? _uiVertices;
|
||||||
|
Uint16List _triangleIndices = Uint16List(0);
|
||||||
|
Uint16List get triangleIndices => _triangleIndices;
|
||||||
|
|
||||||
|
int get contourVertexCount {
|
||||||
|
int i = 0;
|
||||||
|
while (i < _vertices.length && _vertices[i] is ContourMeshVertex) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isValid => _uiVertices != null;
|
||||||
|
|
||||||
|
bool get draws {
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformComponent get transformComponent => parent as Image;
|
||||||
|
Drawable get drawable => parent as Drawable;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool validate() => super.validate() && parent is TransformComponent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void childAdded(Component child) {
|
||||||
|
super.childAdded(child);
|
||||||
|
if (child is MeshVertex && !_vertices.contains(child)) {
|
||||||
|
_vertices.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markDrawableDirty() => addDirt(ComponentDirt.vertices);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void childRemoved(Component child) {
|
||||||
|
super.childRemoved(child);
|
||||||
|
if (child is MeshVertex) {
|
||||||
|
_vertices.remove(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void buildDependencies() {
|
||||||
|
super.buildDependencies();
|
||||||
|
parent?.addDependent(this);
|
||||||
|
skin?.addDependent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AABB bounds = AABB.empty();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(int dirt) {
|
||||||
|
if (dirt & ComponentDirt.vertices != 0) {
|
||||||
|
skin?.deform(_vertices);
|
||||||
|
|
||||||
|
bounds = AABB.empty();
|
||||||
|
var vertices = <ui.Offset>[];
|
||||||
|
var uv = <ui.Offset>[];
|
||||||
|
for (final vertex in _vertices) {
|
||||||
|
var point = vertex.renderTranslation;
|
||||||
|
|
||||||
|
vertices.add(ui.Offset(point[0], point[1]));
|
||||||
|
bounds.expandToPoint(point);
|
||||||
|
uv.add(ui.Offset(vertex.u, vertex.v));
|
||||||
|
}
|
||||||
|
if (_triangleIndices.isEmpty) {
|
||||||
|
_uiVertices = null;
|
||||||
|
} else {
|
||||||
|
_uiVertices = ui.Vertices(
|
||||||
|
ui.VertexMode.triangles,
|
||||||
|
vertices,
|
||||||
|
textureCoordinates: uv,
|
||||||
|
indices: _triangleIndices,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(ui.Canvas canvas, ui.Paint paint) {
|
||||||
|
assert(_uiVertices != null);
|
||||||
|
canvas.drawVertices(_uiVertices!, ui.BlendMode.srcOver, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _deserializeTriangleIndices() {
|
||||||
|
var reader = BinaryReader.fromList(triangleIndexBytes);
|
||||||
|
List<int> triangles = [];
|
||||||
|
while (!reader.isEOF) {
|
||||||
|
triangles.add(reader.readVarUint());
|
||||||
|
}
|
||||||
|
_triangleIndices = Uint16List.fromList(triangles);
|
||||||
|
|
||||||
|
markDrawableDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onAdded() {
|
||||||
|
super.onAdded();
|
||||||
|
|
||||||
|
_deserializeTriangleIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void markSkinDirty() => addDirt(ComponentDirt.vertices);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool addDirt(int value, {bool recurse = false}) {
|
||||||
|
if (value == ComponentDirt.vertices) {
|
||||||
|
// throw 'why';
|
||||||
|
}
|
||||||
|
return super.addDirt(value, recurse: recurse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Mat2D get worldTransform =>
|
||||||
|
skin != null ? Mat2D.identity : transformComponent.worldTransform;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void triangleIndexBytesChanged(List<int> from, List<int> to) =>
|
||||||
|
_deserializeTriangleIndices();
|
||||||
|
}
|
26
lib/src/rive_core/shapes/mesh_vertex.dart
Normal file
26
lib/src/rive_core/shapes/mesh_vertex.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:rive/src/generated/shapes/mesh_vertex_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/node.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/mesh.dart';
|
||||||
|
import 'package:rive/src/rive_core/transform_component.dart';
|
||||||
|
|
||||||
|
export 'package:rive/src/generated/shapes/mesh_vertex_base.dart';
|
||||||
|
|
||||||
|
class MeshVertex extends MeshVertexBase {
|
||||||
|
Mesh? get mesh => parent as Mesh?;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransformComponent get transformComponent =>
|
||||||
|
mesh?.transformComponent ?? Node();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool validate() => super.validate() && parent is Mesh;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void markGeometryDirty() => mesh?.markDrawableDirty();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void uChanged(double from, double to) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void vChanged(double from, double to) {}
|
||||||
|
}
|
@ -1,77 +1,12 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
import 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
||||||
import 'package:rive/src/rive_core/bones/weight.dart';
|
import 'package:rive/src/rive_core/bones/weight.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
|
||||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
|
||||||
import 'package:rive/src/rive_core/shapes/path.dart';
|
import 'package:rive/src/rive_core/shapes/path.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
export 'package:rive/src/generated/shapes/path_vertex_base.dart';
|
||||||
|
|
||||||
abstract class PathVertex<T extends Weight> extends PathVertexBase {
|
abstract class PathVertex<T extends Weight> extends PathVertexBase<T> {
|
||||||
T? _weight;
|
|
||||||
T? get weight => _weight;
|
|
||||||
|
|
||||||
Path? get path => parent as Path?;
|
Path? get path => parent as Path?;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void markGeometryDirty() => path?.markPathDirty();
|
||||||
|
|
||||||
final Vec2D _renderTranslation = Vec2D();
|
|
||||||
Vec2D get translation => Vec2D.fromValues(x, y);
|
|
||||||
Vec2D get renderTranslation => _renderTranslation;
|
|
||||||
|
|
||||||
set translation(Vec2D value) {
|
|
||||||
x = value[0];
|
|
||||||
y = value[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onAddedDirty() {
|
|
||||||
super.onAddedDirty();
|
|
||||||
_renderTranslation[0] = x;
|
|
||||||
_renderTranslation[1] = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void xChanged(double from, double to) {
|
|
||||||
_renderTranslation[0] = to;
|
|
||||||
|
|
||||||
path?.markPathDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void yChanged(double from, double to) {
|
|
||||||
_renderTranslation[1] = to;
|
|
||||||
|
|
||||||
path?.markPathDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return translation.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void childAdded(Component component) {
|
|
||||||
super.childAdded(component);
|
|
||||||
if (component is T) {
|
|
||||||
_weight = component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void childRemoved(Component component) {
|
|
||||||
super.childRemoved(component);
|
|
||||||
if (_weight == component) {
|
|
||||||
_weight = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deform only gets called when we are weighted.
|
|
||||||
void deform(Mat2D world, Float32List boneTransforms) {
|
|
||||||
Weight.deform(x, y, weight!.indices, weight!.values, world, boneTransforms,
|
|
||||||
_weight!.translation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
|||||||
|
|
||||||
export 'package:rive/src/generated/shapes/points_path_base.dart';
|
export 'package:rive/src/generated/shapes/points_path_base.dart';
|
||||||
|
|
||||||
class PointsPath extends PointsPathBase with Skinnable {
|
class PointsPath extends PointsPathBase with Skinnable<PathVertex> {
|
||||||
final List<PathVertex> _vertices = [];
|
final List<PathVertex> _vertices = [];
|
||||||
|
|
||||||
PointsPath() {
|
PointsPath() {
|
||||||
|
81
lib/src/rive_core/shapes/text.dart
Normal file
81
lib/src/rive_core/shapes/text.dart
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/shapes/text_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/text_run.dart';
|
||||||
|
import 'package:rive/src/rive_core/src/text/paragraph.dart' as rive;
|
||||||
|
|
||||||
|
export 'package:rive/src/generated/shapes/text_base.dart';
|
||||||
|
|
||||||
|
class Text extends TextBase with rive.Paragraph {
|
||||||
|
List<TextRun> _textRuns = [];
|
||||||
|
List<int> _textCodes = [];
|
||||||
|
Text() {
|
||||||
|
frame = const Rect.fromLTWH(0, 0, 200, 200);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
List<TextRun> get textRuns => _textRuns;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<int> get textCodes => _textCodes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void removeRun(TextRun run, int index) {
|
||||||
|
run.remove();
|
||||||
|
sortChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool validate() => super.validate() && areTextRunsValid;
|
||||||
|
@override
|
||||||
|
void onAddedDirty() {
|
||||||
|
super.onAddedDirty();
|
||||||
|
_textCodes = value.codeUnits.toList(growable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void textCodesChanged() {
|
||||||
|
value = String.fromCharCodes(textCodes);
|
||||||
|
markTextShapeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void modifyRuns(int start, int end, rive.TextRunMutator mutator) {
|
||||||
|
super.modifyRuns(start, end, mutator);
|
||||||
|
markTextShapeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void sortChildren() {
|
||||||
|
// Cache the runs.
|
||||||
|
_textRuns = children.whereType<TextRun>().toList();
|
||||||
|
markTextShapeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void valueChanged(String from, String to) {
|
||||||
|
_textCodes = value.codeUnits.toList(growable: true);
|
||||||
|
super.textCodesChanged();
|
||||||
|
markTextShapeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void markTextShapeDirty() {
|
||||||
|
addDirt(ComponentDirt.textShape);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(int dirt) {
|
||||||
|
super.update(dirt);
|
||||||
|
if ((dirt & ComponentDirt.textShape) != 0) {
|
||||||
|
for (final textRun in _textRuns) {
|
||||||
|
textRun.clearGlyphRuns();
|
||||||
|
}
|
||||||
|
visitGlyphs((glyphRun, start, end, originX, originY) {
|
||||||
|
var textRun = glyphRun.textRun as TextRun;
|
||||||
|
textRun.addGlyphRun(glyphRun, start, end, originX, originY);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
lib/src/rive_core/shapes/text_run.dart
Normal file
85
lib/src/rive_core/shapes/text_run.dart
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/shapes/text_run_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/text.dart';
|
||||||
|
import 'package:rive/src/rive_core/src/text/raw_path.dart' as rive;
|
||||||
|
import 'package:rive/src/rive_core/src/text/rive_font.dart';
|
||||||
|
import 'package:rive/src/rive_core/src/text/shape_text.dart' as rive;
|
||||||
|
|
||||||
|
export 'package:rive/src/generated/shapes/text_run_base.dart';
|
||||||
|
|
||||||
|
class _DrawableGlyphRun {
|
||||||
|
final rive.GlyphRun run;
|
||||||
|
final int start;
|
||||||
|
final int end;
|
||||||
|
final double ox;
|
||||||
|
final double oy;
|
||||||
|
|
||||||
|
_DrawableGlyphRun(this.run, this.start, this.end, this.ox, this.oy);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextRun extends TextRunBase implements rive.TextRun {
|
||||||
|
@override
|
||||||
|
RiveFont font = RiveFont.builtIn;
|
||||||
|
final List<_DrawableGlyphRun> _glyphRuns = [];
|
||||||
|
|
||||||
|
void clearGlyphRuns() => _glyphRuns.clear();
|
||||||
|
|
||||||
|
void addGlyphRun(
|
||||||
|
rive.GlyphRun glyphRun, int start, int end, double x, double y) {
|
||||||
|
_glyphRuns.add(_DrawableGlyphRun(glyphRun, start, end, x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object? attr;
|
||||||
|
@override
|
||||||
|
bool validate() {
|
||||||
|
return super.validate() && parent is Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text get text => parent as Text;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void draw(Canvas canvas) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.transform(text.worldTransform.mat4);
|
||||||
|
for (final drawableRun in _glyphRuns) {
|
||||||
|
_visitRawPaths(drawableRun.run, drawableRun.start, drawableRun.end,
|
||||||
|
drawableRun.ox, drawableRun.oy, (rive.RawPath rp) {
|
||||||
|
canvas.drawPath(_toPath(rp), Paint()..color = const Color(0xFFFFFFFF));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void pointSizeChanged(double from, double to) {
|
||||||
|
text.markTextShapeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void textLengthChanged(int from, int to) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
rive.TextRun cloneRun() {
|
||||||
|
var cloned = super.clone();
|
||||||
|
assert(cloned is TextRun);
|
||||||
|
return cloned as TextRun;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should generalize all of this...
|
||||||
|
Path _toPath(rive.RawPath rp) {
|
||||||
|
Path p = Path();
|
||||||
|
rp.sinker(p.moveTo, p.lineTo, p.quadraticBezierTo, p.cubicTo, p.close);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _visitRawPaths(rive.GlyphRun run, int start, int end, double x, double y,
|
||||||
|
Function(rive.RawPath) visitor) {
|
||||||
|
for (int i = start; i < end; ++i) {
|
||||||
|
final rive.RawPath? p = run.font.getRawPath(run.glyphs[i]);
|
||||||
|
if (p != null) {
|
||||||
|
visitor(p.scalexy(run.pointSize, run.pointSize, x + run.xpos[i], y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
lib/src/rive_core/shapes/vertex.dart
Normal file
63
lib/src/rive_core/shapes/vertex.dart
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/bones/weight.dart';
|
||||||
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
|
|
||||||
|
export 'package:rive/src/generated/shapes/vertex_base.dart';
|
||||||
|
|
||||||
|
abstract class Vertex<T extends Weight> extends VertexBase {
|
||||||
|
T? _weight;
|
||||||
|
T? get weight => _weight;
|
||||||
|
|
||||||
|
Vec2D get translation => Vec2D.fromValues(x, y);
|
||||||
|
Vec2D get renderTranslation => weight?.translation ?? translation;
|
||||||
|
|
||||||
|
set translation(Vec2D value) {
|
||||||
|
x = value[0];
|
||||||
|
y = value[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void xChanged(double from, double to) {
|
||||||
|
markGeometryDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void markGeometryDirty();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void yChanged(double from, double to) {
|
||||||
|
markGeometryDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return translation.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void childAdded(Component component) {
|
||||||
|
super.childAdded(component);
|
||||||
|
if (component is T) {
|
||||||
|
_weight = component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void childRemoved(Component component) {
|
||||||
|
super.childRemoved(component);
|
||||||
|
if (_weight == component) {
|
||||||
|
_weight = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deform only gets called when we are weighted.
|
||||||
|
void deform(Mat2D world, Float32List boneTransforms) {
|
||||||
|
Weight.deform(x, y, weight!.indices, weight!.values, world, boneTransforms,
|
||||||
|
_weight!.translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(int dirt) {}
|
||||||
|
}
|
2
lib/src/rive_core/src/text/built_in_font.dart
Normal file
2
lib/src/rive_core/src/text/built_in_font.dart
Normal file
File diff suppressed because one or more lines are too long
684
lib/src/rive_core/src/text/paragraph.dart
Normal file
684
lib/src/rive_core/src/text/paragraph.dart
Normal file
@ -0,0 +1,684 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'raw_path.dart';
|
||||||
|
import 'shape_text.dart';
|
||||||
|
|
||||||
|
bool contains(Rect r, double x, double y) {
|
||||||
|
return r.left <= x && x <= r.right && r.top <= y && y <= r.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect outset(Rect r, double dx, double dy) {
|
||||||
|
return Rect.fromLTRB(r.left - dx, r.top - dy, r.right + dx, r.bottom + dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
double pin(double min, double max, double value) {
|
||||||
|
return math.min(math.max(value, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO NOT MODIFY the run
|
||||||
|
typedef TextRunVisitor = void Function(TextRun);
|
||||||
|
|
||||||
|
// Returns either the original, or a new (cloned+modified) run.
|
||||||
|
// DO NOT MODIFY run.textLength
|
||||||
|
typedef TextRunMutator = TextRun Function(TextRun);
|
||||||
|
|
||||||
|
// Given a shaped run and lineIndex (e.g. for drawing)
|
||||||
|
// DO NOT MODIFY the GlyphRun
|
||||||
|
typedef GlyphRunVisitor = void Function(
|
||||||
|
GlyphRun, int start, int end, double originX, double originY);
|
||||||
|
|
||||||
|
enum LinePref {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
class VBar {
|
||||||
|
double x, top, bottom;
|
||||||
|
|
||||||
|
VBar(this.x, this.top, this.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Paragraph {
|
||||||
|
Rect _frame = const Rect.fromLTRB(0, 0, 0, 0);
|
||||||
|
List<int> get textCodes;
|
||||||
|
List<TextRun> get textRuns;
|
||||||
|
// cached output
|
||||||
|
List<GlyphRun> _runs = [];
|
||||||
|
List<GlyphLine> _lines = [];
|
||||||
|
bool _dirty = true;
|
||||||
|
|
||||||
|
bool get areTextRunsValid {
|
||||||
|
int len = 0;
|
||||||
|
textRuns.forEach((run) => len += run.textLength);
|
||||||
|
return len == textCodes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _validateTextRuns() {
|
||||||
|
assert(areTextRunsValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get text => String.fromCharCodes(textCodes);
|
||||||
|
|
||||||
|
void _validateShaping() {
|
||||||
|
if (textCodes.isEmpty) {
|
||||||
|
assert(_lines.length == 1);
|
||||||
|
assert(_runs.length == 1);
|
||||||
|
// ignore: prefer_is_empty
|
||||||
|
assert(_runs[0].glyphs.length == 0);
|
||||||
|
assert(_runs[0].xpos.length == 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphLine line = _lines[0];
|
||||||
|
line.validate();
|
||||||
|
for (int i = 1; i < _lines.length; ++i) {
|
||||||
|
final GlyphLine nextLine = _lines[i];
|
||||||
|
nextLine.validate();
|
||||||
|
assert(line.textEnd == nextLine.textStart);
|
||||||
|
line = nextLine;
|
||||||
|
}
|
||||||
|
assert(line.textEnd == textCodes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _makeAligned() {
|
||||||
|
double Y = _frame.top;
|
||||||
|
for (final line in _lines) {
|
||||||
|
double asc = 0;
|
||||||
|
double des = 0;
|
||||||
|
for (int i = line.startRun; i <= line.wsRun; ++i) {
|
||||||
|
final run = _runs[i];
|
||||||
|
asc = math.min(asc, run.font.ascent * run.pointSize);
|
||||||
|
des = math.max(des, run.font.descent * run.pointSize);
|
||||||
|
}
|
||||||
|
line.top = Y;
|
||||||
|
Y -= asc;
|
||||||
|
line.baseline = Y;
|
||||||
|
Y += des;
|
||||||
|
line.bottom = Y;
|
||||||
|
}
|
||||||
|
// TODO: good place to perform left/center/right alignment
|
||||||
|
}
|
||||||
|
|
||||||
|
void _ensureShapedLines() {
|
||||||
|
if (_dirty) {
|
||||||
|
_validateTextRuns();
|
||||||
|
if (textCodes.isEmpty) {
|
||||||
|
return;
|
||||||
|
// _runs = [
|
||||||
|
// GlyphRun(_defaultRun.font, _defaultRun.size, null, 0,
|
||||||
|
// Uint16List.fromList([]), Float32List.fromList([0]))
|
||||||
|
// ];
|
||||||
|
// _lines = [GlyphLine(_runs, 0, 0, 0, 0, 0, 0, 0)];
|
||||||
|
} else {
|
||||||
|
final breaks = GlyphRun.findWordBreaks(textCodes);
|
||||||
|
_runs = GlyphRun.shapeText(textCodes, textRuns);
|
||||||
|
_lines = GlyphLine.lineBreak(textCodes, breaks, _runs, _frame.width);
|
||||||
|
_makeAligned();
|
||||||
|
}
|
||||||
|
_validateShaping();
|
||||||
|
_dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void trunsChanged() {
|
||||||
|
_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void textCodesChanged() {
|
||||||
|
_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _layoutChanged() {
|
||||||
|
_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isEmpty {
|
||||||
|
return textLength == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isNotEmpty {
|
||||||
|
return textLength != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get textLength {
|
||||||
|
return textCodes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect get bounds {
|
||||||
|
_ensureShapedLines();
|
||||||
|
if (_lines.isEmpty) {
|
||||||
|
return Rect.zero;
|
||||||
|
} else {
|
||||||
|
return Rect.fromLTRB(
|
||||||
|
_frame.left, _frame.top, _frame.right, _lines.last.bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect get frame {
|
||||||
|
return _frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
set frame(Rect rect) {
|
||||||
|
if (rect == _frame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_frame.width != rect.width) {
|
||||||
|
_layoutChanged();
|
||||||
|
}
|
||||||
|
_frame = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GlyphLine> get lines {
|
||||||
|
_ensureShapedLines();
|
||||||
|
return _lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
double offsetToFrameX(GlyphLine line, int textOffset) {
|
||||||
|
return _frame.left + line.offsetToX(textOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetToLineIndex(int textOffset, LinePref pref) {
|
||||||
|
assert(textOffset >= 0 && textOffset <= textLength);
|
||||||
|
_ensureShapedLines();
|
||||||
|
for (int i = 0; i < _lines.length; ++i) {
|
||||||
|
final line = _lines[i];
|
||||||
|
assert(line.textStart <= textOffset);
|
||||||
|
if (textOffset <= line.textEnd) {
|
||||||
|
if (pref == LinePref.start && textOffset == line.textEnd) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _lines.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphLine offsetToLine(int textOffset, LinePref pref) {
|
||||||
|
int index = offsetToLineIndex(textOffset, pref);
|
||||||
|
return _lines[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// [ index, offset ]
|
||||||
|
List<int> _findRunIndexOffset(int offset) {
|
||||||
|
assert(offset >= 0 && offset <= textCodes.length);
|
||||||
|
|
||||||
|
int prevLenths = 0;
|
||||||
|
for (int i = 0; i < textRuns.length; ++i) {
|
||||||
|
final int len = textRuns[i].textLength;
|
||||||
|
if (offset < len) {
|
||||||
|
return [i, prevLenths];
|
||||||
|
}
|
||||||
|
prevLenths += len;
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
offset -= len;
|
||||||
|
}
|
||||||
|
return [textRuns.length - 1, textCodes.length]; // last run
|
||||||
|
}
|
||||||
|
|
||||||
|
int insert(int offset, String str) {
|
||||||
|
if (str.isEmpty) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
assert(textRuns.isNotEmpty);
|
||||||
|
final pair = _findRunIndexOffset(offset);
|
||||||
|
final int index = pair[0];
|
||||||
|
textRuns[index].textLength += str.length;
|
||||||
|
trunsChanged();
|
||||||
|
final units = str.codeUnits;
|
||||||
|
textCodes.insertAll(offset, units);
|
||||||
|
textCodesChanged();
|
||||||
|
return units.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete(int start, int end) {
|
||||||
|
assert(start <= end);
|
||||||
|
assert(start >= 0);
|
||||||
|
assert(end <= textLength);
|
||||||
|
if (start == end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
textCodes.removeRange(start, end);
|
||||||
|
textCodesChanged();
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0;; ++i) {
|
||||||
|
final run = textRuns[i];
|
||||||
|
if (start < run.textLength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
start -= run.textLength;
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
end -= run.textLength;
|
||||||
|
}
|
||||||
|
TextRun run = textRuns[i];
|
||||||
|
assert(start >= 0 && start < run.textLength);
|
||||||
|
if (start > 0) {
|
||||||
|
// trim leading run
|
||||||
|
final int amount = math.min(end, run.textLength) - start;
|
||||||
|
assert(amount > 0 && amount < run.textLength);
|
||||||
|
run.textLength -= amount;
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
start += amount;
|
||||||
|
assert(start <= end);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
end = end - start; // now end is just a length
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
// remove whole runs
|
||||||
|
while (end > 0 && end >= textRuns[i].textLength) {
|
||||||
|
var run = textRuns[i];
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
end -= run.textLength;
|
||||||
|
_removeRunAt(i);
|
||||||
|
}
|
||||||
|
if (end > 0) {
|
||||||
|
textRuns[i].textLength -= end;
|
||||||
|
}
|
||||||
|
trunsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRun(int start, int end, TextRun newRun) {
|
||||||
|
modifyRuns(start, end, (TextRun orig) {
|
||||||
|
return newRun;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRun(covariant TextRun run, int index) {
|
||||||
|
textRuns.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertRun(covariant TextRun run, covariant TextRun? before,
|
||||||
|
covariant TextRun? after, int index) {
|
||||||
|
textRuns.insert(index, run);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _removeRunAt(int index) {
|
||||||
|
var run = textRuns[index];
|
||||||
|
removeRun(run, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _insertRun(int index, TextRun run) {
|
||||||
|
TextRun? before, after;
|
||||||
|
if (index > 0 && index <= textRuns.length) {
|
||||||
|
before = textRuns[index - 1];
|
||||||
|
}
|
||||||
|
if (index < textRuns.length) {
|
||||||
|
after = textRuns[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
insertRun(run, before, after, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets everything to just the [text] with the [run].
|
||||||
|
void setAll(String text, TextRun run) {
|
||||||
|
run.textLength = text.length;
|
||||||
|
while (textRuns.isNotEmpty) {
|
||||||
|
_removeRunAt(0);
|
||||||
|
}
|
||||||
|
_insertRun(0, run);
|
||||||
|
textCodes.clear();
|
||||||
|
textCodes.insertAll(0, text.codeUnits);
|
||||||
|
_validateTextRuns();
|
||||||
|
_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lambda returns a new TextRun, or null to indicated that the run
|
||||||
|
// assed in need not be changed.
|
||||||
|
void modifyRuns(int start, int end, TextRunMutator mutator) {
|
||||||
|
assert(start >= 0 && start <= end);
|
||||||
|
assert(end <= textCodes.length);
|
||||||
|
int modLength = end - start;
|
||||||
|
if (modLength == 0) {
|
||||||
|
return; // snothing to modify
|
||||||
|
}
|
||||||
|
|
||||||
|
final first = _findRunIndexOffset(start);
|
||||||
|
int trunIndex = first[0];
|
||||||
|
int globalOffset = first[1];
|
||||||
|
assert(globalOffset < textCodes.length);
|
||||||
|
|
||||||
|
// Handle splitting the first run
|
||||||
|
if (start > globalOffset) {
|
||||||
|
TextRun origRun = textRuns[trunIndex];
|
||||||
|
TextRun newRun = mutator(origRun);
|
||||||
|
if (origRun != newRun) {
|
||||||
|
final int skipLength = start - globalOffset;
|
||||||
|
newRun.textLength = origRun.textLength - skipLength;
|
||||||
|
origRun.textLength = skipLength;
|
||||||
|
_insertRun(++trunIndex, newRun);
|
||||||
|
trunsChanged();
|
||||||
|
if (newRun.textLength > modLength) {
|
||||||
|
// oops, need to trim and readd oldRun afterwards
|
||||||
|
origRun = origRun.cloneRun();
|
||||||
|
origRun.textLength = newRun.textLength - modLength;
|
||||||
|
newRun.textLength = modLength;
|
||||||
|
_insertRun(++trunIndex, origRun);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modLength -= newRun.textLength;
|
||||||
|
}
|
||||||
|
trunIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace whole runs?
|
||||||
|
while (modLength > 0 && modLength >= textRuns[trunIndex].textLength) {
|
||||||
|
TextRun origRun = textRuns[trunIndex];
|
||||||
|
modLength -= origRun.textLength;
|
||||||
|
TextRun newRun = mutator(origRun);
|
||||||
|
if (origRun != newRun) {
|
||||||
|
removeRun(origRun, trunIndex);
|
||||||
|
_insertRun(trunIndex, newRun);
|
||||||
|
trunsChanged();
|
||||||
|
}
|
||||||
|
trunIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim the last run?
|
||||||
|
if (modLength > 0) {
|
||||||
|
TextRun origRun = textRuns[trunIndex];
|
||||||
|
assert(modLength < origRun.textLength);
|
||||||
|
TextRun newRun = mutator(origRun);
|
||||||
|
if (origRun != newRun) {
|
||||||
|
newRun.textLength = modLength;
|
||||||
|
origRun.textLength -= modLength;
|
||||||
|
_insertRun(trunIndex, newRun);
|
||||||
|
trunsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HOW CAN WE COMPARE RUNS?
|
||||||
|
if (false) {
|
||||||
|
// consolidate equal runs...
|
||||||
|
for (int i = 1; i < textRuns.length; ++i) {
|
||||||
|
if (textRuns[i - 1] == textRuns[i]) {
|
||||||
|
textRuns[i - 1].textLength += textRuns[i].textLength;
|
||||||
|
var run = textRuns[i - 1];
|
||||||
|
_removeRunAt(i);
|
||||||
|
i -= 1; // to undo the loop's ++i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitGlyphs(GlyphRunVisitor visitor) {
|
||||||
|
_ensureShapedLines();
|
||||||
|
|
||||||
|
for (final GlyphLine line in _lines) {
|
||||||
|
int start = line.startIndex;
|
||||||
|
for (int i = line.startRun; i <= line.wsRun; ++i) {
|
||||||
|
final run = _runs[i];
|
||||||
|
int wsEnd = run.glyphs.length;
|
||||||
|
if (i == line.wsRun) {
|
||||||
|
// last run on this line
|
||||||
|
wsEnd = line.wsIndex;
|
||||||
|
}
|
||||||
|
visitor(run, start, wsEnd, _frame.left - line.startX, line.baseline);
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GlyphRun> rawGlyphRuns() {
|
||||||
|
_ensureShapedLines();
|
||||||
|
return _runs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitText(TextRunVisitor visitor) {
|
||||||
|
textRuns.forEach(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect offsetsToRect(GlyphLine line, int start, int end) {
|
||||||
|
assert(start >= line.textStart);
|
||||||
|
assert(end <= line.textEnd);
|
||||||
|
final double left = offsetToFrameX(line, start);
|
||||||
|
final double right = offsetToFrameX(line, end);
|
||||||
|
return Rect.fromLTRB(left, line.top, right, line.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
int xyToOffset(double x, double y) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
x = pin(_frame.left, _frame.right, x);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
y = pin(_lines.first.top, _lines.last.bottom, y);
|
||||||
|
|
||||||
|
// move x into xpos coordinates
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
x -= _frame.left;
|
||||||
|
|
||||||
|
for (final GlyphLine line in _lines) {
|
||||||
|
if (y <= line.bottom) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
x += line.startX; // now we're relative to the xpos in this line
|
||||||
|
GlyphRun run = line.runs.last;
|
||||||
|
for (final GlyphRun r in line.runs) {
|
||||||
|
if (x < r.right) {
|
||||||
|
run = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int index = run.xpos.length - 1;
|
||||||
|
for (int i = 1; i < run.xpos.length; ++i) {
|
||||||
|
if (x < run.xpos[i]) {
|
||||||
|
final mid = (run.xpos[i] + run.xpos[i - 1]) * 0.5;
|
||||||
|
index = x < mid ? i - 1 : i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return run.textStart + index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VBar getVBar(int textOffset, LinePref pref) {
|
||||||
|
final line = offsetToLine(textOffset, pref);
|
||||||
|
final x = offsetToFrameX(line, textOffset);
|
||||||
|
return VBar(x, line.top, line.bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
enum CursorType {
|
||||||
|
line,
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Cursor {
|
||||||
|
Paragraph para;
|
||||||
|
// these are logical, not necessarily sorted
|
||||||
|
int _start = 0;
|
||||||
|
int _end = 0;
|
||||||
|
|
||||||
|
Cursor(this.para);
|
||||||
|
|
||||||
|
CursorType get type {
|
||||||
|
return _start == _end ? CursorType.line : CursorType.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get text =>
|
||||||
|
String.fromCharCodes(para.textCodes.sublist(fromIndex, toIndex));
|
||||||
|
|
||||||
|
int get start {
|
||||||
|
return _start;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get end {
|
||||||
|
return _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get fromIndex {
|
||||||
|
return math.min(_start, _end);
|
||||||
|
}
|
||||||
|
|
||||||
|
int get toIndex {
|
||||||
|
return math.max(_start, _end);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isCollapsed => _start == _end;
|
||||||
|
|
||||||
|
int _legalize(int index) {
|
||||||
|
return math.max(math.min(index, para.textLength), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIndex(int index) {
|
||||||
|
setRange(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRange(int start, int end) {
|
||||||
|
_start = _legalize(start);
|
||||||
|
_end = _legalize(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(String str) {
|
||||||
|
final int from = fromIndex;
|
||||||
|
para.delete(from, toIndex);
|
||||||
|
_start = _end = from + para.insert(from, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete() {
|
||||||
|
if (_start == _end) {
|
||||||
|
if (_start > 0) {
|
||||||
|
para.delete(_start - 1, _start);
|
||||||
|
_start = _end = _start - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
para.delete(fromIndex, toIndex);
|
||||||
|
_start = _end = fromIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrowLeft(bool extend) {
|
||||||
|
if (extend) {
|
||||||
|
_end = _legalize(_end - 1);
|
||||||
|
} else {
|
||||||
|
if (_start == _end) {
|
||||||
|
_start = _end = _legalize(_end - 1);
|
||||||
|
}
|
||||||
|
_start = _end = fromIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// todo: need to not pre-swap _start and _end
|
||||||
|
|
||||||
|
void arrowRight(bool extend) {
|
||||||
|
if (extend) {
|
||||||
|
_end = _legalize(_end + 1);
|
||||||
|
} else {
|
||||||
|
if (_start == _end) {
|
||||||
|
_start = _end = _legalize(_end + 1);
|
||||||
|
} else {
|
||||||
|
_start = _end = toIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrowUp(bool extend) {
|
||||||
|
LinePref pref = LinePref.end; // store this somehow?
|
||||||
|
|
||||||
|
int index = para.offsetToLineIndex(_end, pref);
|
||||||
|
if (index == 0) {
|
||||||
|
_end = 0;
|
||||||
|
} else {
|
||||||
|
final double currX = para.offsetToFrameX(para._lines[index], _end);
|
||||||
|
{
|
||||||
|
int i = para.xyToOffset(currX, para._lines[index].baseline);
|
||||||
|
assert(i == _end);
|
||||||
|
}
|
||||||
|
index -= 1;
|
||||||
|
_end = para.xyToOffset(currX, para._lines[index].baseline);
|
||||||
|
}
|
||||||
|
if (!extend) {
|
||||||
|
_start = _end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrowDown(bool extend) {
|
||||||
|
LinePref pref = LinePref.end; // store this somehow?
|
||||||
|
|
||||||
|
int index = para.offsetToLineIndex(_end, pref);
|
||||||
|
if (index == para._lines.length - 1) {
|
||||||
|
_end = para.textLength;
|
||||||
|
} else {
|
||||||
|
final double currX = para.offsetToFrameX(para._lines[index], _end);
|
||||||
|
index += 1;
|
||||||
|
_end = para.xyToOffset(currX, para._lines[index].baseline);
|
||||||
|
}
|
||||||
|
if (!extend) {
|
||||||
|
_start = _end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointerDown(double x, double y) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
x = pin(para.frame.left, para.frame.right, x);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
y = pin(para.frame.top, para.frame.bottom, y);
|
||||||
|
_start = _end = para.xyToOffset(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pointerMove(double x, double y) {
|
||||||
|
final newEnd = para.xyToOffset(x, y);
|
||||||
|
final changed = _end != newEnd;
|
||||||
|
_end = newEnd;
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointerUp(double x, double y) {}
|
||||||
|
|
||||||
|
// PointerTracker pointerTracker(double x, double y) {
|
||||||
|
// final t = _CursorPointerTracker(this);
|
||||||
|
// t.down(x, y);
|
||||||
|
// return t;
|
||||||
|
// }
|
||||||
|
|
||||||
|
void modifyRuns(TextRunMutator mutator) {
|
||||||
|
para.modifyRuns(fromIndex, toIndex, mutator);
|
||||||
|
}
|
||||||
|
|
||||||
|
VBar getVBar(LinePref pref) {
|
||||||
|
return para.getVBar(_end, pref);
|
||||||
|
}
|
||||||
|
|
||||||
|
RawPath getRawPath([LinePref pref = LinePref.end]) {
|
||||||
|
final b = RawPathBuilder();
|
||||||
|
if (type == CursorType.line) {
|
||||||
|
final bar = getVBar(pref);
|
||||||
|
const width = 1.0;
|
||||||
|
b.addLTRB(bar.x, bar.top, bar.x + width, bar.bottom);
|
||||||
|
} else {
|
||||||
|
final int from = fromIndex;
|
||||||
|
final int to = toIndex;
|
||||||
|
int index = para.offsetToLineIndex(from, LinePref.start);
|
||||||
|
int last = para.offsetToLineIndex(to, LinePref.end);
|
||||||
|
if (index == last) {
|
||||||
|
GlyphLine line = para.lines[index];
|
||||||
|
b.addRect(para.offsetsToRect(line, from, to));
|
||||||
|
} else {
|
||||||
|
GlyphLine line = para.lines[index];
|
||||||
|
b.addRect(para.offsetsToRect(line, from, line.textEnd));
|
||||||
|
while (++index < last) {
|
||||||
|
line = para.lines[index];
|
||||||
|
b.addRect(para.offsetsToRect(line, line.textStart, line.textEnd));
|
||||||
|
}
|
||||||
|
line = para.lines[last];
|
||||||
|
b.addRect(para.offsetsToRect(line, line.textStart, to));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleParagraph extends Paragraph {
|
||||||
|
final List<int> _textCodes = [];
|
||||||
|
final List<TextRun> _truns = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<int> get textCodes => _textCodes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<TextRun> get textRuns => _truns;
|
||||||
|
}
|
149
lib/src/rive_core/src/text/raw_path.dart
Normal file
149
lib/src/rive_core/src/text/raw_path.dart
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const int kMove_PathVerb = 0;
|
||||||
|
const int kLine_PathVerb = 1;
|
||||||
|
const int kQuad_PathVerb = 2;
|
||||||
|
const int kConic_PathVerb = 3;
|
||||||
|
const int kCubic_PathVerb = 4;
|
||||||
|
const int kClose_PathVerb = 5;
|
||||||
|
|
||||||
|
class IPathSink {
|
||||||
|
void moveTo(double x, double y) {}
|
||||||
|
void lineTo(double x, double y) {}
|
||||||
|
void quadTo(double x, double y, double x1, double y1) {}
|
||||||
|
void cubicTo(
|
||||||
|
double x, double y, double x1, double y1, double x2, double y2) {}
|
||||||
|
void close() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawPath {
|
||||||
|
Float32List pts;
|
||||||
|
Uint8List verbs;
|
||||||
|
|
||||||
|
RawPath(this.pts, this.verbs);
|
||||||
|
|
||||||
|
void sinker(
|
||||||
|
Function(double, double) moveTo,
|
||||||
|
Function(double, double) lineTo,
|
||||||
|
Function(double, double, double, double) quadTo,
|
||||||
|
Function(double, double, double, double, double, double) cubicTo,
|
||||||
|
Function close) {
|
||||||
|
int i = 0;
|
||||||
|
for (final int verb in verbs) {
|
||||||
|
switch (verb) {
|
||||||
|
case kMove_PathVerb:
|
||||||
|
moveTo(pts[i + 0], pts[i + 1]);
|
||||||
|
i += 2;
|
||||||
|
break;
|
||||||
|
case kLine_PathVerb:
|
||||||
|
lineTo(pts[i + 0], pts[i + 1]);
|
||||||
|
i += 2;
|
||||||
|
break;
|
||||||
|
case kQuad_PathVerb:
|
||||||
|
quadTo(pts[i + 0], pts[i + 1], pts[i + 2], pts[i + 3]);
|
||||||
|
i += 4;
|
||||||
|
break;
|
||||||
|
case kConic_PathVerb: // not supported
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
case kCubic_PathVerb:
|
||||||
|
cubicTo(pts[i + 0], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4],
|
||||||
|
pts[i + 5]);
|
||||||
|
i += 6;
|
||||||
|
break;
|
||||||
|
case kClose_PathVerb:
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw 'unknown verb $verb';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(i == pts.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
RawPath scalexy(double sx, double sy, double tx, double ty) {
|
||||||
|
final newp = Float32List(pts.length);
|
||||||
|
for (int i = 0; i < pts.length; i += 2) {
|
||||||
|
newp[i + 0] = pts[i + 0] * sx + tx;
|
||||||
|
newp[i + 1] = pts[i + 1] * sy + ty;
|
||||||
|
}
|
||||||
|
// we share verbs to save memory -- so don't modify them!
|
||||||
|
return RawPath(Float32List.fromList(newp), verbs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawPathBuilder {
|
||||||
|
List<double> pts = [];
|
||||||
|
List<int> vbs = [];
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
pts = [];
|
||||||
|
vbs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
double get lastX {
|
||||||
|
return pts[pts.length - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
double get lastY {
|
||||||
|
return pts[pts.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveTo(double x, double y) {
|
||||||
|
pts.add(x);
|
||||||
|
pts.add(y);
|
||||||
|
vbs.add(kMove_PathVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lineTo(double x, double y) {
|
||||||
|
pts.add(x);
|
||||||
|
pts.add(y);
|
||||||
|
vbs.add(kLine_PathVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void quadTo(double x, double y, double x1, double y1) {
|
||||||
|
pts.add(x);
|
||||||
|
pts.add(y);
|
||||||
|
pts.add(x1);
|
||||||
|
pts.add(y1);
|
||||||
|
vbs.add(kQuad_PathVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cubicTo(double x, double y, double x1, double y1, double x2, double y2) {
|
||||||
|
pts.add(x);
|
||||||
|
pts.add(y);
|
||||||
|
pts.add(x1);
|
||||||
|
pts.add(y1);
|
||||||
|
pts.add(x2);
|
||||||
|
pts.add(y2);
|
||||||
|
vbs.add(kCubic_PathVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
vbs.add(kClose_PathVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLine(double x0, double y0, double x1, double y1) {
|
||||||
|
moveTo(x0, y0);
|
||||||
|
lineTo(x1, y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLTRB(double l, double t, double r, double b) {
|
||||||
|
moveTo(l, t);
|
||||||
|
lineTo(r, t);
|
||||||
|
lineTo(r, b);
|
||||||
|
lineTo(l, b);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRect(Rect r) {
|
||||||
|
addLTRB(r.left, r.top, r.right, r.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
RawPath detach() {
|
||||||
|
final rp = RawPath(Float32List.fromList(pts), Uint8List.fromList(vbs));
|
||||||
|
reset();
|
||||||
|
return rp;
|
||||||
|
}
|
||||||
|
}
|
255
lib/src/rive_core/src/text/rive_font.dart
Normal file
255
lib/src/rive_core/src/text/rive_font.dart
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:rive/src/rive_core/src/text/built_in_font.dart';
|
||||||
|
|
||||||
|
import 'raw_path.dart';
|
||||||
|
|
||||||
|
// for diagnostic/debugging purposes
|
||||||
|
const bool enableFontDump = false;
|
||||||
|
|
||||||
|
class Reader {
|
||||||
|
ByteData data;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
Reader(this.data);
|
||||||
|
|
||||||
|
int u8() {
|
||||||
|
offset += 1;
|
||||||
|
return data.getUint8(offset - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int s16() {
|
||||||
|
offset += 2;
|
||||||
|
return data.getInt16(offset - 2, Endian.little);
|
||||||
|
}
|
||||||
|
|
||||||
|
int u16() {
|
||||||
|
offset += 2;
|
||||||
|
return data.getUint16(offset - 2, Endian.little);
|
||||||
|
}
|
||||||
|
|
||||||
|
int u32() {
|
||||||
|
offset += 4;
|
||||||
|
return data.getUint32(offset - 4, Endian.little);
|
||||||
|
}
|
||||||
|
|
||||||
|
void skipPad16() {
|
||||||
|
offset += offset & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List u8list(int count) {
|
||||||
|
offset += count;
|
||||||
|
return Uint8List.sublistView(data, offset - count, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int available() {
|
||||||
|
return data.lengthInBytes - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eof() {
|
||||||
|
return offset == data.lengthInBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Tag(int a, int b, int c, int d) {
|
||||||
|
return (a << 24) | (b << 16) | (c << 8) | d;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fromTag(int tag) {
|
||||||
|
final int a = (tag >> 24) & 0xFF;
|
||||||
|
final int b = (tag >> 16) & 0xFF;
|
||||||
|
final int c = (tag >> 8) & 0xFF;
|
||||||
|
final int d = (tag >> 0) & 0xFF;
|
||||||
|
return '' +
|
||||||
|
String.fromCharCode(a) +
|
||||||
|
String.fromCharCode(b) +
|
||||||
|
String.fromCharCode(c) +
|
||||||
|
String.fromCharCode(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int kCMap_TableTag = 1668112752; // cmap
|
||||||
|
const int kAdvances_TableTag = 1751213174; // hadv
|
||||||
|
const int kInfo_TableTag = 1768842863; // info
|
||||||
|
const int kPaths_TableTag = 1885434984; // path
|
||||||
|
const int kOffsets_TableTag = 1886348902; // poff
|
||||||
|
|
||||||
|
ByteData find_table(List<int> dir, int tag, ByteData data) {
|
||||||
|
for (int i = 0; i < dir.length; i += 3) {
|
||||||
|
if (dir[i] == tag) {
|
||||||
|
return ByteData.view(data.buffer, dir[i + 1], dir[i + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw 'missing ${fromTag(tag)} table';
|
||||||
|
}
|
||||||
|
|
||||||
|
class RiveFont {
|
||||||
|
final double _ascent = -0.9; // TODO: get from file
|
||||||
|
final double _descent = 0.2; // TODO: get from file
|
||||||
|
|
||||||
|
final _cmap = <int, int>{};
|
||||||
|
final _advances = <double>[];
|
||||||
|
final _rawpaths = <RawPath?>[];
|
||||||
|
|
||||||
|
RiveFont() {
|
||||||
|
_advances.add(0);
|
||||||
|
_rawpaths.add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get ascent {
|
||||||
|
return _ascent;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get descent {
|
||||||
|
return _descent;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get height {
|
||||||
|
return _descent - _ascent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int charToGlyph(int charCode) {
|
||||||
|
int? glyph = _cmap[charCode];
|
||||||
|
return glyph ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getAdvance(int glyph) {
|
||||||
|
return _advances[glyph];
|
||||||
|
}
|
||||||
|
|
||||||
|
Path? getPath(int glyph) {
|
||||||
|
final rp = getRawPath(glyph);
|
||||||
|
if (rp != null) {
|
||||||
|
Path p = Path();
|
||||||
|
rp.sinker(p.moveTo, p.lineTo, p.quadraticBezierTo, p.cubicTo, p.close);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawPath? getRawPath(int glyph) {
|
||||||
|
return _rawpaths[glyph];
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint16List textToGlyphs(List<int> chars, int start, int end) {
|
||||||
|
final int n = end - start;
|
||||||
|
Uint16List glyphs = Uint16List(n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
glyphs[i] = charToGlyph(chars[i + start]);
|
||||||
|
}
|
||||||
|
return glyphs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Float32List getAdvances(Uint16List glyphs) {
|
||||||
|
Float32List advances = Float32List(glyphs.length);
|
||||||
|
for (int i = 0; i < glyphs.length; ++i) {
|
||||||
|
advances[i] = getAdvance(glyphs[i]);
|
||||||
|
}
|
||||||
|
return advances;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _build_cmap(int glyphCount, ByteData cmapD) {
|
||||||
|
final reader = Reader(cmapD);
|
||||||
|
final int count = reader.u16();
|
||||||
|
if (enableFontDump) {
|
||||||
|
print('cmap has $count entries');
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
final int charCode = reader.u16();
|
||||||
|
final int glyphID = reader.u16();
|
||||||
|
assert(glyphID < glyphCount);
|
||||||
|
_cmap.putIfAbsent(charCode, () => glyphID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _build_advances(int glyphCount, double scale, ByteData advD) {
|
||||||
|
assert(advD.lengthInBytes == glyphCount * 2);
|
||||||
|
final advances = Reader(advD);
|
||||||
|
for (int i = 0; i < glyphCount; ++i) {
|
||||||
|
_advances.add(advances.u16() * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RawPath _build_rawpath(double scale, ByteData pathD) {
|
||||||
|
final reader = Reader(pathD);
|
||||||
|
|
||||||
|
final int verbCount = reader.u16();
|
||||||
|
assert(verbCount > 0);
|
||||||
|
final int pointCount = reader.u16();
|
||||||
|
final Float32List pts = Float32List(pointCount * 2);
|
||||||
|
|
||||||
|
final verbs = reader.u8list(verbCount);
|
||||||
|
reader.skipPad16();
|
||||||
|
for (int i = 0; i < pointCount * 2; ++i) {
|
||||||
|
pts[i] = reader.s16() * scale;
|
||||||
|
}
|
||||||
|
assert(reader.eof());
|
||||||
|
return RawPath(pts, verbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _build_rawpaths(
|
||||||
|
int glyphCount, double scale, ByteData offD, ByteData pathD) {
|
||||||
|
final offsets = Reader(offD);
|
||||||
|
int start = offsets.u32();
|
||||||
|
for (int i = 0; i < glyphCount; ++i) {
|
||||||
|
int end = offsets.u32();
|
||||||
|
assert(start <= end);
|
||||||
|
|
||||||
|
RawPath? path;
|
||||||
|
if (start < end) {
|
||||||
|
path = _build_rawpath(scale, ByteData.sublistView(pathD, start, end));
|
||||||
|
}
|
||||||
|
_rawpaths.add(path);
|
||||||
|
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RiveFont.fromBinary(ByteData data) {
|
||||||
|
final Reader reader = Reader(data);
|
||||||
|
|
||||||
|
{
|
||||||
|
int signature = reader.u32();
|
||||||
|
int version = reader.u32();
|
||||||
|
assert(signature == 0x23581321);
|
||||||
|
assert(version == 1);
|
||||||
|
}
|
||||||
|
final int tableCount = reader.u32();
|
||||||
|
if (enableFontDump) {
|
||||||
|
print('tables $tableCount');
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<int> dir = [];
|
||||||
|
for (int i = 0; i < tableCount; ++i) {
|
||||||
|
int tag = reader.u32();
|
||||||
|
int off = reader.u32();
|
||||||
|
int len = reader.u32();
|
||||||
|
if (enableFontDump) {
|
||||||
|
print('tag: ${fromTag(tag)} offset:$off length:$len');
|
||||||
|
}
|
||||||
|
dir.add(tag); // tag
|
||||||
|
dir.add(off); // offset
|
||||||
|
dir.add(len); // length
|
||||||
|
}
|
||||||
|
|
||||||
|
final infoReader = Reader(find_table(dir, kInfo_TableTag, data));
|
||||||
|
final int glyphCount = infoReader.u16();
|
||||||
|
final int upem = infoReader.u16();
|
||||||
|
final double scale = 1.0 / upem;
|
||||||
|
if (enableFontDump) {
|
||||||
|
print('glyphs $glyphCount, upem $upem');
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_cmap(glyphCount, find_table(dir, kCMap_TableTag, data));
|
||||||
|
|
||||||
|
_build_advances(
|
||||||
|
glyphCount, scale, find_table(dir, kAdvances_TableTag, data));
|
||||||
|
|
||||||
|
_build_rawpaths(glyphCount, scale, find_table(dir, kOffsets_TableTag, data),
|
||||||
|
find_table(dir, kPaths_TableTag, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static final builtIn = RiveFont.fromBinary(
|
||||||
|
ByteData.view(const Base64Decoder().convert(builtInFontBase64).buffer));
|
||||||
|
}
|
252
lib/src/rive_core/src/text/shape_text.dart
Normal file
252
lib/src/rive_core/src/text/shape_text.dart
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'rive_font.dart';
|
||||||
|
|
||||||
|
abstract class TextRun {
|
||||||
|
RiveFont get font;
|
||||||
|
double get pointSize;
|
||||||
|
int get textLength;
|
||||||
|
set textLength(int value);
|
||||||
|
|
||||||
|
TextRun cloneRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlyphRun {
|
||||||
|
TextRun textRun; // the run this glyph run was generated from
|
||||||
|
RiveFont font; // possibly different from TextRun (substitution)
|
||||||
|
double pointSize; // possibly different from TextRun
|
||||||
|
|
||||||
|
int textOffset;
|
||||||
|
Uint16List glyphs;
|
||||||
|
Float32List xpos; // #glyphs + 1
|
||||||
|
|
||||||
|
GlyphRun(
|
||||||
|
this.textRun,
|
||||||
|
this.font,
|
||||||
|
this.pointSize,
|
||||||
|
this.textOffset,
|
||||||
|
this.glyphs,
|
||||||
|
this.xpos,
|
||||||
|
) {
|
||||||
|
assert(glyphs.length + 1 == xpos.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get left {
|
||||||
|
return xpos.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get right {
|
||||||
|
return xpos.last;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get width {
|
||||||
|
return right - left;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get textStart {
|
||||||
|
return textOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get textEnd {
|
||||||
|
return textOffset + glyphs.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ws(int code) {
|
||||||
|
return code <= 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<GlyphRun> shapeText(List<int> chars, List<TextRun> textRuns) {
|
||||||
|
final glyphRuns = <GlyphRun>[];
|
||||||
|
int offset = 0;
|
||||||
|
double x = 0;
|
||||||
|
|
||||||
|
for (final TextRun run in textRuns) {
|
||||||
|
if (run.textLength > 0) {
|
||||||
|
final glyphs =
|
||||||
|
run.font.textToGlyphs(chars, offset, offset + run.textLength);
|
||||||
|
final advances = run.font.getAdvances(glyphs);
|
||||||
|
final int n = glyphs.length;
|
||||||
|
final xpos = Float32List(n + 1);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
xpos[i] = x;
|
||||||
|
x += advances[i] * run.pointSize;
|
||||||
|
}
|
||||||
|
xpos[n] = x;
|
||||||
|
glyphRuns
|
||||||
|
.add(GlyphRun(run, run.font, run.pointSize, offset, glyphs, xpos));
|
||||||
|
offset += run.textLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return glyphRuns;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<int> findWordBreaks(List<int> chars) {
|
||||||
|
List<int> breaks = [];
|
||||||
|
for (int i = 0; i < chars.length;) {
|
||||||
|
// skip ws
|
||||||
|
while (i < chars.length && ws(chars[i])) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
breaks.add(i); // word start
|
||||||
|
// skip non-ws
|
||||||
|
while (i < chars.length && !ws(chars[i])) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
breaks.add(i); // word end
|
||||||
|
}
|
||||||
|
assert(breaks.last == chars.length);
|
||||||
|
return breaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offsetToIndex(int offset) {
|
||||||
|
assert(textOffset <= offset);
|
||||||
|
assert(offset - textOffset <= glyphs.length);
|
||||||
|
return offset - textOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlyphLine {
|
||||||
|
List<GlyphRun> runs;
|
||||||
|
int startRun;
|
||||||
|
int startIndex;
|
||||||
|
int endRun;
|
||||||
|
int endIndex;
|
||||||
|
int wsRun;
|
||||||
|
int wsIndex;
|
||||||
|
double startX;
|
||||||
|
double top = 0, baseline = 0, bottom = 0;
|
||||||
|
|
||||||
|
void validate() {
|
||||||
|
void val(int r, int i) {
|
||||||
|
final run = runs[r];
|
||||||
|
assert(i >= 0 && i <= run.glyphs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
val(startRun, startIndex);
|
||||||
|
val(endRun, endIndex);
|
||||||
|
val(wsRun, wsIndex);
|
||||||
|
assert(startRun < endRun || (startRun == endRun && startIndex <= endIndex));
|
||||||
|
assert(endRun < wsRun || (endRun == wsRun && endIndex <= wsIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphLine(this.runs, this.startRun, this.startIndex, this.endRun,
|
||||||
|
this.endIndex, this.wsRun, this.wsIndex, this.startX);
|
||||||
|
|
||||||
|
int get textStart {
|
||||||
|
return runs[startRun].textStart + startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get textEnd {
|
||||||
|
return runs[wsRun].textStart + wsIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
double offsetToX(int textOffset) {
|
||||||
|
assert(textOffset <= runs[endRun].textEnd);
|
||||||
|
for (int i = startRun; i <= endRun; ++i) {
|
||||||
|
final run = runs[i];
|
||||||
|
if (textOffset <= run.textEnd) {
|
||||||
|
return run.xpos[textOffset - run.textStart] - startX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _offsetToRunIndex(
|
||||||
|
List<GlyphRun> runs, int offset, int charLength) {
|
||||||
|
assert(offset >= 0);
|
||||||
|
for (int i = 1; i < runs.length; ++i) {
|
||||||
|
final run = runs[i];
|
||||||
|
if (run.textOffset >= offset) {
|
||||||
|
return i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runs.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<GlyphLine> lineBreak(
|
||||||
|
List<int> chars, List<int> breaks, List<GlyphRun> runs, double width) {
|
||||||
|
List<GlyphLine> lines = [];
|
||||||
|
int startRun = 0;
|
||||||
|
int startIndex = 0;
|
||||||
|
double xlimit = width;
|
||||||
|
|
||||||
|
int prevRun = 0;
|
||||||
|
int prevIndex = 0;
|
||||||
|
|
||||||
|
int wordStart = breaks[0];
|
||||||
|
int wordEnd = breaks[1];
|
||||||
|
int nextBreakIndex = 2;
|
||||||
|
int lineStartTextOffset = wordStart;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
assert(wordStart <= wordEnd); // == means trailing spaces?
|
||||||
|
|
||||||
|
int endRun = _offsetToRunIndex(runs, wordEnd, chars.length);
|
||||||
|
int endIndex = runs[endRun].offsetToIndex(wordEnd);
|
||||||
|
double pos = runs[endRun].xpos[endIndex];
|
||||||
|
bool bumpBreakIndex = true;
|
||||||
|
if (pos > xlimit) {
|
||||||
|
int wsRun = _offsetToRunIndex(runs, wordStart, chars.length);
|
||||||
|
int wsIndex = runs[wsRun].offsetToIndex(wordStart);
|
||||||
|
|
||||||
|
bumpBreakIndex = false;
|
||||||
|
// does just one word not fit?
|
||||||
|
if (lineStartTextOffset == wordStart) {
|
||||||
|
// walk backwards a letter at a time until we fit, stopping at
|
||||||
|
// 1 letter.
|
||||||
|
int wend = wordEnd;
|
||||||
|
while (pos > xlimit && wend - 1 > wordStart) {
|
||||||
|
wend -= 1;
|
||||||
|
prevRun = _offsetToRunIndex(runs, wend, chars.length);
|
||||||
|
prevIndex = runs[prevRun].offsetToIndex(wend);
|
||||||
|
pos = runs[prevRun].xpos[prevIndex];
|
||||||
|
}
|
||||||
|
assert(wend < wordEnd || wend == wordEnd && wordStart + 1 == wordEnd);
|
||||||
|
if (wend == wordEnd) {
|
||||||
|
bumpBreakIndex = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now reset our "whitespace" marker to just be prev, since
|
||||||
|
// by defintion we have no extra whitespace on this line
|
||||||
|
wsRun = prevRun;
|
||||||
|
wsIndex = prevIndex;
|
||||||
|
wordStart = wend;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulid the line
|
||||||
|
final lineStartX = runs[startRun].xpos[startIndex];
|
||||||
|
lines.add(GlyphLine(runs, startRun, startIndex, prevRun, prevIndex,
|
||||||
|
wsRun, wsIndex, lineStartX));
|
||||||
|
|
||||||
|
// update for the next line
|
||||||
|
xlimit = runs[wsRun].xpos[wsIndex] + width;
|
||||||
|
startRun = prevRun = wsRun;
|
||||||
|
startIndex = prevIndex = wsIndex;
|
||||||
|
lineStartTextOffset = wordStart;
|
||||||
|
} else {
|
||||||
|
// we didn't go too far, so remember this word-end boundary
|
||||||
|
prevRun = endRun;
|
||||||
|
prevIndex = endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bumpBreakIndex) {
|
||||||
|
if (nextBreakIndex < breaks.length) {
|
||||||
|
wordStart = breaks[nextBreakIndex++];
|
||||||
|
wordEnd = breaks[nextBreakIndex++];
|
||||||
|
} else {
|
||||||
|
break; // bust out of the loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scoop up the last line (if present)
|
||||||
|
final int tailRun = runs.length - 1;
|
||||||
|
final int tailIndex = runs[tailRun].glyphs.length;
|
||||||
|
if (startRun != tailRun || startIndex != tailIndex) {
|
||||||
|
final double startX = runs[startRun].xpos[startIndex];
|
||||||
|
lines.add(GlyphLine(runs, startRun, startIndex, tailRun, tailIndex,
|
||||||
|
tailRun, tailIndex, startX));
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:rive/rive.dart';
|
import 'package:rive/rive.dart';
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
|
Reference in New Issue
Block a user