use the dimensions of the image at runtime, rather than of the ImageA…

…sset

ok just "fixing" how we export .riv files, and no-longer rely on width and height form image assets, but take them from the image instead.

its not "huge" difference, but basically it means that if we replace an existing image with an image of different dimensions we end up drawing the image from the centre of where the original image was, rather than the top left, which is a bit mroe forgiving

example:

got a few images in here, (they're all like 5k x 3k pixels, so all quite large)

<img width="1086" alt="CleanShot 2023-07-03 at 16 28 34@2x" src="https://github.com/rive-app/rive/assets/1216025/625e0d34-ac0c-4eb6-ad75-cb839aca92ac">

before this change this would look like:

<img width="1135" alt="CleanShot 2023-07-03 at 16 28 46@2x" src="https://github.com/rive-app/rive/assets/1216025/8ba848da-5938-4897-a664-eaae39c86806">

with this change we get

<img width="1113" alt="CleanShot 2023-07-03 at 16 29 17@2x" src="https://github.com/rive-app/rive/assets/1216025/c5f30eb6-21bd-419e-802d-9c98c00399e7">

(the mesh is kinda interesting here)

*note* i'm not touching the cpp runtime just yet, so this & other changes still need to make it down there

Diffs=
235908221 use the dimensions of the image at runtime, rather than of the ImageA… (#5519)

Co-authored-by: Maxwell Talbot <talbot.maxwell@gmail.com>
This commit is contained in:
mjtalbot
2023-07-20 13:59:39 +00:00
parent 591b275519
commit b4ec7a7ab4
5 changed files with 49 additions and 105 deletions

View File

@ -1 +1 @@
05c8632e88e89ba40c0fa006265d28d651150c0e
2359082217a57031dfaff5e7138fd974e9025aad

View File

@ -12,59 +12,4 @@ abstract class DrawableAssetBase extends FileAsset {
@override
Set<int> get coreTypes =>
{DrawableAssetBase.typeKey, FileAssetBase.typeKey, AssetBase.typeKey};
/// --------------------------------------------------------------------------
/// Height field with key 207.
static const double heightInitialValue = 0;
double _height = heightInitialValue;
static const int heightPropertyKey = 207;
/// Height of the original asset uploaded
double get height => _height;
/// Change the [_height] field value.
/// [heightChanged] will be invoked only if the field's value has changed.
set height(double value) {
if (_height == value) {
return;
}
double from = _height;
_height = value;
if (hasValidated) {
heightChanged(from, value);
}
}
void heightChanged(double from, double to);
/// --------------------------------------------------------------------------
/// Width field with key 208.
static const double widthInitialValue = 0;
double _width = widthInitialValue;
static const int widthPropertyKey = 208;
/// Width of the original asset uploaded
double get width => _width;
/// Change the [_width] field value.
/// [widthChanged] will be invoked only if the field's value has changed.
set width(double value) {
if (_width == value) {
return;
}
double from = _width;
_width = value;
if (hasValidated) {
widthChanged(from, value);
}
}
void widthChanged(double from, double to);
@override
void copy(covariant DrawableAssetBase source) {
super.copy(source);
_height = source._height;
_width = source._width;
}
}

View File

@ -19,7 +19,6 @@ import 'package:rive/src/generated/animation/state_machine_component_base.dart';
import 'package:rive/src/generated/animation/transition_condition_base.dart';
import 'package:rive/src/generated/animation/transition_value_condition_base.dart';
import 'package:rive/src/generated/assets/asset_base.dart';
import 'package:rive/src/generated/assets/drawable_asset_base.dart';
import 'package:rive/src/generated/assets/file_asset_base.dart';
import 'package:rive/src/generated/component_base.dart';
import 'package:rive/src/generated/constraints/constraint_base.dart';
@ -1514,16 +1513,6 @@ class RiveCoreContext {
object.cdnBaseUrl = value;
}
break;
case DrawableAssetBase.heightPropertyKey:
if (object is DrawableAssetBase && value is double) {
object.height = value;
}
break;
case DrawableAssetBase.widthPropertyKey:
if (object is DrawableAssetBase && value is double) {
object.width = value;
}
break;
case FileAssetContentsBase.bytesPropertyKey:
if (object is FileAssetContentsBase && value is Uint8List) {
object.bytes = value;
@ -1750,8 +1739,6 @@ class RiveCoreContext {
case TextBase.heightPropertyKey:
case TextBase.originXPropertyKey:
case TextBase.originYPropertyKey:
case DrawableAssetBase.heightPropertyKey:
case DrawableAssetBase.widthPropertyKey:
return doubleType;
case TransformComponentConstraintBase.offsetPropertyKey:
case TransformComponentConstraintBase.doesCopyPropertyKey:
@ -2218,10 +2205,6 @@ class RiveCoreContext {
return (object as TextBase).originX;
case TextBase.originYPropertyKey:
return (object as TextBase).originY;
case DrawableAssetBase.heightPropertyKey:
return (object as DrawableAssetBase).height;
case DrawableAssetBase.widthPropertyKey:
return (object as DrawableAssetBase).width;
}
return 0.0;
}
@ -3350,16 +3333,6 @@ class RiveCoreContext {
object.originY = value;
}
break;
case DrawableAssetBase.heightPropertyKey:
if (object is DrawableAssetBase) {
object.height = value;
}
break;
case DrawableAssetBase.widthPropertyKey:
if (object is DrawableAssetBase) {
object.width = value;
}
break;
}
}

View File

@ -19,6 +19,9 @@ class Image extends ImageBase
Mesh? get mesh => _mesh;
bool get hasMesh => _mesh != null;
double get width => image?.width.toDouble() ?? 0.0;
double get height => image?.width.toDouble() ?? 0.0;
AABB get localBounds {
if (hasMesh && _mesh!.draws) {
return _mesh!.bounds;
@ -26,8 +29,8 @@ class Image extends ImageBase
if (asset == null) {
return AABB.empty();
}
final halfWidth = asset!.width / 2;
final halfHeight = asset!.height / 2;
final halfWidth = width / 2;
final halfHeight = height / 2;
return AABB.fromValues(-halfWidth, -halfHeight, halfWidth, halfHeight);
}
@ -47,9 +50,6 @@ class Image extends ImageBase
..filterQuality = ui.FilterQuality.high
..blendMode = blendMode;
final width = asset!.width;
final height = asset!.height;
canvas.save();
canvas.transform(renderTransform.mat4);
if (_mesh == null || !_mesh!.draws) {

View File

@ -22,8 +22,7 @@ void main() {
registerFallbackValue(Stream.value(<int>[]));
});
group("Test loading rive file with embedded asset.", () {
testWidgets('Default load does not hit any url',
(WidgetTester tester) async {
test('Default load does not hit any url', () async {
final mockHttpClient = getMockHttpClient();
await HttpOverrides.runZoned(() async {
@ -36,8 +35,7 @@ void main() {
verifyNever(() => mockHttpClient.openUrl(any(), any()));
});
testWidgets('Disabling embedded assets also does not hit a url',
(WidgetTester tester) async {
test('Disabling embedded assets also does not hit a url', () async {
final mockHttpClient = getMockHttpClient();
await HttpOverrides.runZoned(() async {
@ -53,8 +51,7 @@ void main() {
verifyNever(() => mockHttpClient.openUrl(any(), any()));
});
testWidgets('Disabling cdn also does not hit a url',
(WidgetTester tester) async {
test('Disabling cdn also does not hit a url', () async {
final mockHttpClient = getMockHttpClient();
await HttpOverrides.runZoned(() async {
@ -79,8 +76,8 @@ void main() {
verifyNever(() => mockHttpClient.openUrl(any(), any()));
// by default we try to check for assets
});
testWidgets('test importing rive file, make sure we get a good callback',
(WidgetTester tester) async {
test('test importing rive file, make sure we get a good callback',
() async {
// lets just return an image
final riveBytes = loadFile('assets/sample_image.riv');
final imageBytes = loadFile('assets/file.png');
@ -106,6 +103,37 @@ void main() {
expect(fileAsset.assetId, 42981);
expect(fileAsset.id, -1);
});
test('Make sure the image gets the dimensions once the image is loaded',
() async {
// lets just return an image
final riveBytes = loadFile('assets/sample_image.riv');
final imageBytes = loadFile('assets/file.png');
final completer = Completer();
final file = RiveFile.import(
riveBytes,
loadEmbeddedAssets: false,
assetLoader: CallbackAssetLoader(
(asset) async {
await asset.decode(Uint8List.sublistView(
imageBytes,
));
completer.complete(null);
return true;
},
),
);
final image = file.artboards.first
.component("CleanShot 2023-06-08 at 08.51.19@2x.png");
print(image);
expect(image.width, 0);
expect(image.height, 0);
await completer.future;
expect(image.width, 256);
expect(image.height, 256);
});
});
group("Test loading rive file with cdn asset.", () {
late MockHttpClient mockHttpClient;
@ -115,7 +143,7 @@ void main() {
prepMockRequest(mockHttpClient, Uint8List.sublistView(imageBytes));
});
testWidgets('Default load will his the cdn', (WidgetTester tester) async {
test('Default load will his the cdn', () async {
await HttpOverrides.runZoned(() async {
final riveBytes = loadFile('assets/cdn_image.riv');
RiveFile.import(
@ -131,8 +159,7 @@ void main() {
)).called(1);
});
testWidgets('Disabling embedded assets also hits a url',
(WidgetTester tester) async {
test('Disabling embedded assets also hits a url', () async {
await HttpOverrides.runZoned(() async {
final riveBytes = loadFile('assets/cdn_image.riv');
runZonedGuarded(() {
@ -149,8 +176,7 @@ void main() {
verify(() => mockHttpClient.openUrl(any(), any())).called(1);
});
testWidgets('Disabling cdn will mean no url hit',
(WidgetTester tester) async {
test('Disabling cdn will mean no url hit', () async {
await HttpOverrides.runZoned(() async {
final riveBytes = loadFile('assets/cdn_image.riv');
@ -165,9 +191,9 @@ void main() {
verifyNever(() => mockHttpClient.openUrl(any(), any()));
// by default we try to check for assets
});
testWidgets(
test(
'If we provide a callback, we are hit first, and success means no cdn hit',
(WidgetTester tester) async {
() async {
// lets just return an image
final imageBytes = loadFile('assets/file.png');
final parameters = [];
@ -192,9 +218,9 @@ void main() {
verifyNever(() => mockHttpClient.openUrl(any(), any()));
});
testWidgets(
test(
'If we provide a callback, we are hit first, a failure means we hit cdn',
(WidgetTester tester) async {
() async {
// lets just return an image
final parameters = [];
await HttpOverrides.runZoned(() async {