diff --git a/engine/src/flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java b/engine/src/flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java index e962fa6b3c0..485af3e0af2 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java @@ -33,7 +33,7 @@ import java.util.Map.Entry; *
  • BigIntegers (see below) *
  • Floats, Doubles *
  • Strings - *
  • byte[], int[], long[], double[] + *
  • byte[], int[], long[], float[], double[] *
  • Lists of supported values *
  • Maps with supported keys and values * @@ -49,6 +49,7 @@ import java.util.Map.Entry; *
  • byte[]: Uint8List *
  • int[]: Int32List *
  • long[]: Int64List + *
  • float[]: Float32List *
  • double[]: Float64List *
  • List: List *
  • Map: Map @@ -104,6 +105,7 @@ public class StandardMessageCodec implements MessageCodec { private static final byte DOUBLE_ARRAY = 11; private static final byte LIST = 12; private static final byte MAP = 13; + private static final byte FLOAT_ARRAY = 14; /** * Writes an int representing a size to the specified stream. Uses an expanding code of 1 to 5 @@ -173,6 +175,11 @@ public class StandardMessageCodec implements MessageCodec { } } + /** Writes the specified double as 4 bytes to the specified stream */ + protected static final void writeFloat(ByteArrayOutputStream stream, float value) { + writeInt(stream, Float.floatToIntBits(value)); + } + /** Writes the specified double as 8 bytes to the specified stream. */ protected static final void writeDouble(ByteArrayOutputStream stream, double value) { writeLong(stream, Double.doubleToLongBits(value)); @@ -272,6 +279,14 @@ public class StandardMessageCodec implements MessageCodec { writeValue(stream, entry.getKey()); writeValue(stream, entry.getValue()); } + } else if (value instanceof float[]) { + stream.write(FLOAT_ARRAY); + final float[] array = (float[]) value; + writeSize(stream, array.length); + writeAlignment(stream, 4); + for (final float f : array) { + writeFloat(stream, f); + } } else { throw new IllegalArgumentException("Unsupported value: " + value); } @@ -412,6 +427,16 @@ public class StandardMessageCodec implements MessageCodec { result = map; break; } + case FLOAT_ARRAY: + { + final int length = readSize(buffer); + final float[] array = new float[length]; + readAlignment(buffer, 4); + buffer.asFloatBuffer().get(array); + result = array; + buffer.position(buffer.position() + 4 * length); + break; + } default: throw new IllegalArgumentException("Message corrupted"); } diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/common/StandardMessageCodecTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/common/StandardMessageCodecTest.java index 5a3223d59f6..7234630ae1b 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/common/StandardMessageCodecTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/common/StandardMessageCodecTest.java @@ -1,5 +1,6 @@ package io.flutter.plugin.common; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import java.nio.ByteBuffer; @@ -28,6 +29,7 @@ public class StandardMessageCodecTest { private static final byte DOUBLE_ARRAY = 11; private static final byte LIST = 12; private static final byte MAP = 13; + private static final byte FLOAT_ARRAY = 14; @Test public void itEncodesNullLiterals() { @@ -93,4 +95,17 @@ public class StandardMessageCodecTest { expected.flip(); assertEquals(expected, message); } + + @Test + public void itEncodesFloatArrays() { + StandardMessageCodec codec = new StandardMessageCodec(); + + float[] expectedValues = new float[] {1.0f, 2.2f, 5.3f}; + + ByteBuffer message = codec.encodeMessage(expectedValues); + message.flip(); + + float[] values = (float[]) codec.decodeMessage(message); + assertArrayEquals(expectedValues, values, 0.01f); + } } diff --git a/engine/src/flutter/shell/platform/common/client_wrapper/encodable_value_unittests.cc b/engine/src/flutter/shell/platform/common/client_wrapper/encodable_value_unittests.cc index 3fcf83aa2ab..2a4ec5c671f 100644 --- a/engine/src/flutter/shell/platform/common/client_wrapper/encodable_value_unittests.cc +++ b/engine/src/flutter/shell/platform/common/client_wrapper/encodable_value_unittests.cc @@ -217,6 +217,9 @@ TEST(EncodableValueTest, Comparison) { EncodableValue( EncodableMap{{EncodableValue(), EncodableValue(1.0)}, {EncodableValue("key"), EncodableValue("value")}}), + // FloatList + EncodableValue(std::vector{0, 1}), + EncodableValue(std::vector{0, 100}), }; for (size_t i = 0; i < values.size(); ++i) { diff --git a/engine/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h b/engine/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h index a4c7ac4058f..870cfc80f83 100644 --- a/engine/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h +++ b/engine/src/flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h @@ -110,6 +110,7 @@ using EncodableValueVariant = std::variant, EncodableList, EncodableMap, + std::vector, CustomEncodableValue>; } // namespace internal @@ -158,6 +159,7 @@ using EncodableValueVariant = std::variant -> Float64List // EncodableList -> List // EncodableMap -> Map +// std::vector -> Float32List class EncodableValue : public internal::EncodableValueVariant { public: // Rely on std::variant for most of the constructors/operators. diff --git a/engine/src/flutter/shell/platform/common/client_wrapper/standard_codec.cc b/engine/src/flutter/shell/platform/common/client_wrapper/standard_codec.cc index 3e86879b477..a7725088ddd 100644 --- a/engine/src/flutter/shell/platform/common/client_wrapper/standard_codec.cc +++ b/engine/src/flutter/shell/platform/common/client_wrapper/standard_codec.cc @@ -41,6 +41,7 @@ enum class EncodedType { kFloat64List, kList, kMap, + kFloat32List, }; // Returns the encoded type that should be written when serializing |value|. @@ -70,6 +71,8 @@ EncodedType EncodedTypeForValue(const EncodableValue& value) { return EncodedType::kList; case 11: return EncodedType::kMap; + case 12: + return EncodedType::kFloat32List; } assert(false); return EncodedType::kNull; @@ -150,7 +153,11 @@ void StandardCodecSerializer::WriteValue(const EncodableValue& value, } break; } - case 12: + case 12: { + WriteVector(std::get>(value), stream); + break; + } + case 13: std::cerr << "Unhandled custom type in StandardCodecSerializer::WriteValue. " << "Custom types require codec extensions." << std::endl; @@ -210,6 +217,9 @@ EncodableValue StandardCodecSerializer::ReadValueOfType( } return EncodableValue(map_value); } + case EncodedType::kFloat32List: { + return ReadVector(stream); + } } std::cerr << "Unknown type in StandardCodecSerializer::ReadValueOfType: " << static_cast(type) << std::endl; diff --git a/engine/src/flutter/shell/platform/common/client_wrapper/standard_message_codec_unittests.cc b/engine/src/flutter/shell/platform/common/client_wrapper/standard_message_codec_unittests.cc index f1fa75e7c8e..a516fe24fff 100644 --- a/engine/src/flutter/shell/platform/common/client_wrapper/standard_message_codec_unittests.cc +++ b/engine/src/flutter/shell/platform/common/client_wrapper/standard_message_codec_unittests.cc @@ -175,6 +175,13 @@ TEST(StandardMessageCodec, CanEncodeAndDecodeInt64Array) { CheckEncodeDecode(value, bytes); } +TEST(StandradMessageCodec, CanEncodeAndDecodeFloat32Array) { + std::vector bytes = {0x0e, 0x02, 0x00, 0x00, 0xd8, 0x0f, + 0x49, 0x40, 0x00, 0x00, 0x7a, 0x44}; + EncodableValue value(std::vector{3.1415920257568359375f, 1000.0f}); + CheckEncodeDecode(value, bytes); +} + TEST(StandardMessageCodec, CanEncodeAndDecodeFloat64Array) { std::vector bytes = {0x0b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40, diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h b/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h index 0919ad02359..3ba5d4e9c25 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h @@ -282,6 +282,7 @@ typedef NS_ENUM(NSInteger, FlutterStandardDataType) { FlutterStandardDataTypeUInt8, FlutterStandardDataTypeInt32, FlutterStandardDataTypeInt64, + FlutterStandardDataTypeFloat32, FlutterStandardDataTypeFloat64, }; @@ -319,6 +320,14 @@ FLUTTER_DARWIN_EXPORT */ + (instancetype)typedDataWithInt64:(NSData*)data; +/** + * Creates a `FlutterStandardTypedData` which interprets the specified data + * as 32-bit floats. + * + * @param data the byte data. The length must be divisible by 8. + */ ++ (instancetype)typedDataWithFloat32:(NSData*)data; + /** * Creates a `FlutterStandardTypedData` which interprets the specified data * as 64-bit floats. diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index 9019f5bff95..9135a59addd 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -162,6 +162,10 @@ using namespace flutter; return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeInt64]; } ++ (instancetype)typedDataWithFloat32:(NSData*)data { + return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeFloat32]; +} + + (instancetype)typedDataWithFloat64:(NSData*)data { return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeFloat64]; } @@ -443,6 +447,7 @@ using namespace flutter; case FlutterStandardFieldUInt8Data: case FlutterStandardFieldInt32Data: case FlutterStandardFieldInt64Data: + case FlutterStandardFieldFloat32Data: case FlutterStandardFieldFloat64Data: return [self readTypedDataOfType:FlutterStandardDataTypeForField(field)]; case FlutterStandardFieldList: { diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h index 4cd8710843f..88d7f277476 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h @@ -16,22 +16,47 @@ typedef NS_ENUM(NSInteger, FlutterStandardField) { FlutterStandardFieldIntHex, FlutterStandardFieldFloat64, FlutterStandardFieldString, - // The following must match the corresponding order from `FlutterStandardDataType`. FlutterStandardFieldUInt8Data, FlutterStandardFieldInt32Data, FlutterStandardFieldInt64Data, FlutterStandardFieldFloat64Data, FlutterStandardFieldList, FlutterStandardFieldMap, + FlutterStandardFieldFloat32Data, }; namespace flutter { FlutterStandardField FlutterStandardFieldForDataType(FlutterStandardDataType type) { - return (FlutterStandardField)(type + FlutterStandardFieldUInt8Data); + switch (type) { + case FlutterStandardDataTypeUInt8: + return FlutterStandardFieldUInt8Data; + case FlutterStandardDataTypeInt32: + return FlutterStandardFieldInt32Data; + case FlutterStandardDataTypeInt64: + return FlutterStandardFieldInt64Data; + case FlutterStandardDataTypeFloat32: + return FlutterStandardFieldFloat32Data; + case FlutterStandardDataTypeFloat64: + return FlutterStandardFieldFloat64Data; + } } FlutterStandardDataType FlutterStandardDataTypeForField(FlutterStandardField field) { - return (FlutterStandardDataType)(field - FlutterStandardFieldUInt8Data); + switch (field) { + case FlutterStandardFieldUInt8Data: + return FlutterStandardDataTypeUInt8; + case FlutterStandardFieldInt32Data: + return FlutterStandardDataTypeInt32; + case FlutterStandardFieldInt64Data: + return FlutterStandardDataTypeInt64; + case FlutterStandardFieldFloat32Data: + return FlutterStandardDataTypeFloat32; + case FlutterStandardFieldFloat64Data: + return FlutterStandardDataTypeFloat64; + default: + return FlutterStandardDataTypeUInt8; + } } + UInt8 elementSizeForFlutterStandardDataType(FlutterStandardDataType type) { switch (type) { case FlutterStandardDataTypeUInt8: @@ -40,6 +65,8 @@ UInt8 elementSizeForFlutterStandardDataType(FlutterStandardDataType type) { return 4; case FlutterStandardDataTypeInt64: return 8; + case FlutterStandardDataTypeFloat32: + return 4; case FlutterStandardDataTypeFloat64: return 8; } diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm b/engine/src/flutter/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm index 34ab6f1c004..fbfd2a86e1e 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm @@ -176,6 +176,13 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64Array) { checkEncodeDecode(value); } +TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat32Array) { + uint8_t bytes[8] = {0xd8, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x7a, 0x44}; + NSData* data = [NSData dataWithBytes:bytes length:8]; + FlutterStandardTypedData* value = [FlutterStandardTypedData typedDataWithFloat32:data]; + checkEncodeDecode(value); +} + TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64Array) { uint8_t bytes[16] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff, 0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};