From ea010e2500fd0680336223f3413c68422c735cbb Mon Sep 17 00:00:00 2001 From: Roman Laitarenko Date: Thu, 17 Oct 2024 16:00:46 +0300 Subject: [PATCH] Fix GlyphsRasterizationMode crashing Snapshotter (#738) --- CHANGELOG.md | 1 + .../pigeons/SnapshotterMessenger.kt | 55 +++++++++++-------- .../snapshotter/snapshotter_test.dart | 2 +- example/lib/main.dart | 6 +- example/lib/offline_map_example.dart | 6 +- example/lib/snapshotter_example.dart | 3 +- .../Generated/SnapshotterMessenger.swift | 55 +++++++++++-------- lib/src/snapshotter/snapshotter.dart | 3 +- .../snapshotter/snapshotter_messenger.dart | 52 ++++++++++-------- 9 files changed, 108 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9061cf84..e3d1dc0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ onCameraChangeListener(CameraChangedEventData data) { * Print to console native Maps SDK logs in debug configuration. Logs are proxied only in debug configuration and can be disabled completely by passing environment flag `MAPBOX_LOG_DEBUG` with false value. * Fix rare crash in `Snapshotter`. The crash could happen when creating/destroying multiple instances of `Snapshotter` in succession. +* Fix crash in Snapshotter when GlyphsRasterizationMode is specified in MapSnapshotOptions. # 2.3.0 diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/SnapshotterMessenger.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/SnapshotterMessenger.kt index bc11a690..a0f2b2f0 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/SnapshotterMessenger.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/SnapshotterMessenger.kt @@ -82,61 +82,66 @@ private open class SnapshotterMessengerPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { + GlyphsRasterizationMode.ofRaw(it.toInt()) + } + } + 130.toByte() -> { return (readValue(buffer) as? List)?.let { PointDecoder.fromList(it) } } - 130.toByte() -> { + 131.toByte() -> { return (readValue(buffer) as? List)?.let { MbxEdgeInsets.fromList(it) } } - 131.toByte() -> { + 132.toByte() -> { return (readValue(buffer) as? List)?.let { ScreenCoordinate.fromList(it) } } - 132.toByte() -> { + 133.toByte() -> { return (readValue(buffer) as? List)?.let { CameraOptions.fromList(it) } } - 133.toByte() -> { + 134.toByte() -> { return (readValue(buffer) as? List)?.let { Size.fromList(it) } } - 134.toByte() -> { + 135.toByte() -> { return (readValue(buffer) as? List)?.let { CoordinateBounds.fromList(it) } } - 135.toByte() -> { + 136.toByte() -> { return (readValue(buffer) as? List)?.let { CameraState.fromList(it) } } - 136.toByte() -> { + 137.toByte() -> { return (readValue(buffer) as? List)?.let { MbxImage.fromList(it) } } - 137.toByte() -> { + 138.toByte() -> { return (readValue(buffer) as? List)?.let { GlyphsRasterizationOptions.fromList(it) } } - 138.toByte() -> { + 139.toByte() -> { return (readValue(buffer) as? List)?.let { TileCoverOptions.fromList(it) } } - 139.toByte() -> { + 140.toByte() -> { return (readValue(buffer) as? List)?.let { CanonicalTileID.fromList(it) } } - 140.toByte() -> { + 141.toByte() -> { return (readValue(buffer) as? List)?.let { MapSnapshotOptions.fromList(it) } @@ -146,52 +151,56 @@ private open class SnapshotterMessengerPigeonCodec : StandardMessageCodec() { } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is Point -> { + is GlyphsRasterizationMode -> { stream.write(129) + writeValue(stream, value.raw) + } + is Point -> { + stream.write(130) writeValue(stream, value.toList()) } is MbxEdgeInsets -> { - stream.write(130) + stream.write(131) writeValue(stream, value.toList()) } is ScreenCoordinate -> { - stream.write(131) + stream.write(132) writeValue(stream, value.toList()) } is CameraOptions -> { - stream.write(132) + stream.write(133) writeValue(stream, value.toList()) } is Size -> { - stream.write(133) + stream.write(134) writeValue(stream, value.toList()) } is CoordinateBounds -> { - stream.write(134) + stream.write(135) writeValue(stream, value.toList()) } is CameraState -> { - stream.write(135) + stream.write(136) writeValue(stream, value.toList()) } is MbxImage -> { - stream.write(136) + stream.write(137) writeValue(stream, value.toList()) } is GlyphsRasterizationOptions -> { - stream.write(137) + stream.write(138) writeValue(stream, value.toList()) } is TileCoverOptions -> { - stream.write(138) + stream.write(139) writeValue(stream, value.toList()) } is CanonicalTileID -> { - stream.write(139) + stream.write(140) writeValue(stream, value.toList()) } is MapSnapshotOptions -> { - stream.write(140) + stream.write(141) writeValue(stream, value.toList()) } else -> super.writeValue(stream, value) diff --git a/example/integration_test/snapshotter/snapshotter_test.dart b/example/integration_test/snapshotter/snapshotter_test.dart index 99d49220..6d644605 100644 --- a/example/integration_test/snapshotter/snapshotter_test.dart +++ b/example/integration_test/snapshotter/snapshotter_test.dart @@ -48,7 +48,7 @@ void main() { final snapshotter = await Snapshotter.create( options: options, onStyleLoadedListener: styleLoaded.complete, - onStyleDataLoadedListener: styleDataLoaded.complete, + onStyleDataLoadedListener: (e) { if(!styleDataLoaded.isCompleted) styleDataLoaded.complete(e); }, ); await snapshotter.style.setStyleURI(MapboxStyles.LIGHT); diff --git a/example/lib/main.dart b/example/lib/main.dart index a4fe0777..91af025f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -87,7 +87,11 @@ class MapsDemo extends StatelessWidget { leading: example.leading, title: Text(example.title), subtitle: (example.subtitle?.isNotEmpty == true) - ? Text(example.subtitle!, maxLines: 2, overflow: TextOverflow.ellipsis,) + ? Text( + example.subtitle!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ) : null, onTap: () => _pushPage(context, _allPages[index]), ); diff --git a/example/lib/offline_map_example.dart b/example/lib/offline_map_example.dart index c5690bd1..7500752b 100644 --- a/example/lib/offline_map_example.dart +++ b/example/lib/offline_map_example.dart @@ -12,7 +12,8 @@ class OfflineMapExample extends StatefulWidget implements Example { @override final String title = 'Offline Map'; @override - final String subtitle = "Shows how to use OfflineManager and TileStore to download regions for offline use."; + final String subtitle = + "Shows how to use OfflineManager and TileStore to download regions for offline use."; @override State createState() => OfflineMapExampleState(); @@ -88,7 +89,8 @@ class OfflineMapExampleState extends State { acceptExpired: true, networkRestriction: NetworkRestriction.NONE); - _tileStore?.loadTileRegion(_tileRegionId, tileRegionLoadOptions, (progress) { + _tileStore?.loadTileRegion(_tileRegionId, tileRegionLoadOptions, + (progress) { final percentage = progress.completedResourceCount / progress.requiredResourceCount; if (!_tileRegionLoadProgress.isClosed) { diff --git a/example/lib/snapshotter_example.dart b/example/lib/snapshotter_example.dart index ae2ec38f..6f79afbe 100644 --- a/example/lib/snapshotter_example.dart +++ b/example/lib/snapshotter_example.dart @@ -9,7 +9,8 @@ class SnapshotterExample extends StatefulWidget implements Example { @override final String title = 'Create a static map snapshot'; @override - final String subtitle = "Create a static, non-interactive image of a map style with specified camera position."; + final String subtitle = + "Create a static, non-interactive image of a map style with specified camera position."; @override State createState() => SnapshotterExampleState(); diff --git a/ios/Classes/Generated/SnapshotterMessenger.swift b/ios/Classes/Generated/SnapshotterMessenger.swift index fd0d94d6..07ddd104 100644 --- a/ios/Classes/Generated/SnapshotterMessenger.swift +++ b/ios/Classes/Generated/SnapshotterMessenger.swift @@ -116,28 +116,34 @@ private class SnapshotterMessengerPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 129: - return Point.fromList(self.readValue() as! [Any?]) + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return GlyphsRasterizationMode(rawValue: enumResultAsInt) + } + return nil case 130: - return MbxEdgeInsets.fromList(self.readValue() as! [Any?]) + return Point.fromList(self.readValue() as! [Any?]) case 131: - return ScreenCoordinate.fromList(self.readValue() as! [Any?]) + return MbxEdgeInsets.fromList(self.readValue() as! [Any?]) case 132: - return CameraOptions.fromList(self.readValue() as! [Any?]) + return ScreenCoordinate.fromList(self.readValue() as! [Any?]) case 133: - return Size.fromList(self.readValue() as! [Any?]) + return CameraOptions.fromList(self.readValue() as! [Any?]) case 134: - return CoordinateBounds.fromList(self.readValue() as! [Any?]) + return Size.fromList(self.readValue() as! [Any?]) case 135: - return CameraState.fromList(self.readValue() as! [Any?]) + return CoordinateBounds.fromList(self.readValue() as! [Any?]) case 136: - return MbxImage.fromList(self.readValue() as! [Any?]) + return CameraState.fromList(self.readValue() as! [Any?]) case 137: - return GlyphsRasterizationOptions.fromList(self.readValue() as! [Any?]) + return MbxImage.fromList(self.readValue() as! [Any?]) case 138: - return TileCoverOptions.fromList(self.readValue() as! [Any?]) + return GlyphsRasterizationOptions.fromList(self.readValue() as! [Any?]) case 139: - return CanonicalTileID.fromList(self.readValue() as! [Any?]) + return TileCoverOptions.fromList(self.readValue() as! [Any?]) case 140: + return CanonicalTileID.fromList(self.readValue() as! [Any?]) + case 141: return MapSnapshotOptions.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -147,41 +153,44 @@ private class SnapshotterMessengerPigeonCodecReader: FlutterStandardReader { private class SnapshotterMessengerPigeonCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { - if let value = value as? Point { + if let value = value as? GlyphsRasterizationMode { super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? Point { + super.writeByte(130) super.writeValue(value.toList()) } else if let value = value as? MbxEdgeInsets { - super.writeByte(130) + super.writeByte(131) super.writeValue(value.toList()) } else if let value = value as? ScreenCoordinate { - super.writeByte(131) + super.writeByte(132) super.writeValue(value.toList()) } else if let value = value as? CameraOptions { - super.writeByte(132) + super.writeByte(133) super.writeValue(value.toList()) } else if let value = value as? Size { - super.writeByte(133) + super.writeByte(134) super.writeValue(value.toList()) } else if let value = value as? CoordinateBounds { - super.writeByte(134) + super.writeByte(135) super.writeValue(value.toList()) } else if let value = value as? CameraState { - super.writeByte(135) + super.writeByte(136) super.writeValue(value.toList()) } else if let value = value as? MbxImage { - super.writeByte(136) + super.writeByte(137) super.writeValue(value.toList()) } else if let value = value as? GlyphsRasterizationOptions { - super.writeByte(137) + super.writeByte(138) super.writeValue(value.toList()) } else if let value = value as? TileCoverOptions { - super.writeByte(138) + super.writeByte(139) super.writeValue(value.toList()) } else if let value = value as? CanonicalTileID { - super.writeByte(139) + super.writeByte(140) super.writeValue(value.toList()) } else if let value = value as? MapSnapshotOptions { - super.writeByte(140) + super.writeByte(141) super.writeValue(value.toList()) } else { super.writeValue(value) diff --git a/lib/src/snapshotter/snapshotter.dart b/lib/src/snapshotter/snapshotter.dart index 605db3d0..5d5f4656 100644 --- a/lib/src/snapshotter/snapshotter.dart +++ b/lib/src/snapshotter/snapshotter.dart @@ -79,7 +79,8 @@ final class Snapshotter { snapshotter._mapEvents.eventTypes.map((e) => e.index).toList(), options); - Snapshotter._finalizer.attach(snapshotter, snapshotter._suffix, detach: snapshotter); + Snapshotter._finalizer + .attach(snapshotter, snapshotter._suffix, detach: snapshotter); return snapshotter; } diff --git a/lib/src/snapshotter/snapshotter_messenger.dart b/lib/src/snapshotter/snapshotter_messenger.dart index f1f5e8cf..588f76b4 100644 --- a/lib/src/snapshotter/snapshotter_messenger.dart +++ b/lib/src/snapshotter/snapshotter_messenger.dart @@ -59,41 +59,44 @@ class SnapshotterMessenger_PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is Point) { + } else if (value is GlyphsRasterizationMode) { buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is Point) { + buffer.putUint8(130); writeValue(buffer, value.encode()); } else if (value is MbxEdgeInsets) { - buffer.putUint8(130); + buffer.putUint8(131); writeValue(buffer, value.encode()); } else if (value is ScreenCoordinate) { - buffer.putUint8(131); + buffer.putUint8(132); writeValue(buffer, value.encode()); } else if (value is CameraOptions) { - buffer.putUint8(132); + buffer.putUint8(133); writeValue(buffer, value.encode()); } else if (value is Size) { - buffer.putUint8(133); + buffer.putUint8(134); writeValue(buffer, value.encode()); } else if (value is CoordinateBounds) { - buffer.putUint8(134); + buffer.putUint8(135); writeValue(buffer, value.encode()); } else if (value is CameraState) { - buffer.putUint8(135); + buffer.putUint8(136); writeValue(buffer, value.encode()); } else if (value is MbxImage) { - buffer.putUint8(136); + buffer.putUint8(137); writeValue(buffer, value.encode()); } else if (value is GlyphsRasterizationOptions) { - buffer.putUint8(137); + buffer.putUint8(138); writeValue(buffer, value.encode()); } else if (value is TileCoverOptions) { - buffer.putUint8(138); + buffer.putUint8(139); writeValue(buffer, value.encode()); } else if (value is CanonicalTileID) { - buffer.putUint8(139); + buffer.putUint8(140); writeValue(buffer, value.encode()); } else if (value is MapSnapshotOptions) { - buffer.putUint8(140); + buffer.putUint8(141); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -104,28 +107,31 @@ class SnapshotterMessenger_PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - return Point.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : GlyphsRasterizationMode.values[value]; case 130: - return MbxEdgeInsets.decode(readValue(buffer)!); + return Point.decode(readValue(buffer)!); case 131: - return ScreenCoordinate.decode(readValue(buffer)!); + return MbxEdgeInsets.decode(readValue(buffer)!); case 132: - return CameraOptions.decode(readValue(buffer)!); + return ScreenCoordinate.decode(readValue(buffer)!); case 133: - return Size.decode(readValue(buffer)!); + return CameraOptions.decode(readValue(buffer)!); case 134: - return CoordinateBounds.decode(readValue(buffer)!); + return Size.decode(readValue(buffer)!); case 135: - return CameraState.decode(readValue(buffer)!); + return CoordinateBounds.decode(readValue(buffer)!); case 136: - return MbxImage.decode(readValue(buffer)!); + return CameraState.decode(readValue(buffer)!); case 137: - return GlyphsRasterizationOptions.decode(readValue(buffer)!); + return MbxImage.decode(readValue(buffer)!); case 138: - return TileCoverOptions.decode(readValue(buffer)!); + return GlyphsRasterizationOptions.decode(readValue(buffer)!); case 139: - return CanonicalTileID.decode(readValue(buffer)!); + return TileCoverOptions.decode(readValue(buffer)!); case 140: + return CanonicalTileID.decode(readValue(buffer)!); + case 141: return MapSnapshotOptions.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer);