diff --git a/isobus/include/isobus/isobus/isobus_virtual_terminal_objects.hpp b/isobus/include/isobus/isobus/isobus_virtual_terminal_objects.hpp index 47ac472da..6ae5f8f7f 100644 --- a/isobus/include/isobus/isobus/isobus_virtual_terminal_objects.hpp +++ b/isobus/include/isobus/isobus/isobus_virtual_terminal_objects.hpp @@ -4529,6 +4529,149 @@ namespace isobus std::uint8_t pointerType = 0; ///< The pointer type, defines how this should be rendered }; + /// @brief Defines an Scaled Graphic object. + /// The Scaled Graphic object, available in VT version 6 and later, displays a scaled representation of a + /// referenced graphic object. The VT shall scale the raw data from the referenced graphic object from the + /// actual width and height to the target width and height. See Table B.75 and Table B.76. Therefore the + /// target width attribute in the Picture Graphic object shall be ignored when referenced from the Scaled + /// Graphic object. + class ScaledGraphicObject : public VTObject + { + public: + /// @brief Enumerates this object's attributes that have Attribute IDs (AID) + enum class AttributeName : std::uint8_t + { + Width = 1, + Height = 2, + ScaleType = 3, + Options = 4, + + NumberOfAttributes = 5 + }; + + /// @brief Enumerates the option bits in the options bitfield for a Scaled graphics object + enum class Options + { + Flashing = 0, // Flashing. Flash style and rate determined by VT design. + }; + + /// @brief Enumerates the scaling value bits in the scale type attribute of a Scaled graphics object + enum class ScalingValue : std::uint8_t + { + NotScaled = 0, + ScaleToWidthMaintainAspectRatio = 1, + ScaleToHeightMaintainAspectRatio = 2, + ScaleToWidthAndHeight = 3, + ScaleToWidthAndHeightMaintainAspectRatio = 4, + }; + + /// @brief Enumerates the horizontal justification bits in the scale type attribute of a Scaled graphics object + enum class HorizontalJustification : std::uint8_t + { + PositionLeft = 0, + PositionMiddle = 1, + PositionRight = 2, + }; + + /// @brief Enumerates the vertical justification bits in the scale type attribute of a Scaled graphics object + enum class VerticalJustification : std::uint8_t + { + PositionTop = 0, + PositionMiddle = 1, + PositionBottom = 2, + }; + + /// @brief Bitmask of the horizontal justification bits in the scale type attribute of a Scaled graphics object + static constexpr std::uint8_t HORIZONTAL_JUSTIFICATION_MASK = 0x18; + + /// @brief Bitmask of the vertical justification bits in the scale type attribute of a Scaled graphics object + static constexpr std::uint8_t VERTICAL_JUSTIFICATION_MASK = 0x60; + + ScaledGraphicObject() = default; + ~ScaledGraphicObject() override = default; + + /// @brief Returns the VT object type of the underlying derived object + /// @returns The VT object type of the underlying derived objec + VirtualTerminalObjectType get_object_type() const override; + + /// @brief Returns the minimum binary serialized length of the associated object + /// @returns The minimum binary serialized length of the associated object + std::uint32_t get_minumum_object_length() const override; + + /// @brief Performs basic error checking on the object and returns if the object is valid + /// @param[in] objectPool The object pool to use when validating the object + /// @returns `true` if the object passed basic error checks + bool get_is_valid(const std::map> &objectPool) const override; + + /// @brief Sets an attribute and optionally returns an error code in the last parameter + /// @param[in] attributeID The ID of the attribute to change + /// @param[in] rawData The raw data to change the attribute to, as decoded in little endian format with unused + /// bytes/bits set to zero. + /// @param[in] objectPool The object pool to use when validating the objects affected by setting this attribute + /// @param[out] returnedError If this function returns false, this will be the error code. If the function + /// returns true, this value is undefined. + /// @returns True if the attribute was changed, otherwise false (check the returnedError in this case to know why). + bool set_attribute(std::uint8_t attributeID, std::uint32_t rawData, const std::map> &objectPool, AttributeError &returnedError) override; + + /// @brief Gets an attribute and returns the raw data in the last parameter + /// @param[in] attributeID The ID of the attribute to get + /// @param[out] returnedAttributeData The raw data of the attribute, as decoded in little endian format with unused + /// bytes/bits set to zero. You may need to cast this to the correct type. If this function + /// returns false, this value is undefined. + /// @returns True if the attribute was retrieved, otherwise false (the attribute ID was invalid) + bool get_attribute(std::uint8_t attributeID, std::uint32_t &returnedAttributeData) const override; + + /// @brief Returns the state of a single option in the object's option bitfield + /// @param[in] option The option to check the value of in the object's option bitfield + /// @returns The state of the associated option bit + bool get_option(Options option) const; + + /// @brief Sets a single option in the options bitfield to the specified value + /// @param[in] option The option to set + /// @param[in] value The new value of the option bit + void set_option(Options option, bool value); + + /// @brief Sets the options bitfield for this object to a new value + /// @param[in] options The new value for the options bitfield + void set_options(std::uint8_t options); + + /// @brief Returns the scale type of the scaled graphic object + /// @returns Scaling type value + std::uint8_t get_scale_type() const; + + /// @brief Sets the scaling type of the scaled graphic object + /// @param[in] scale_type The scale type to set + void set_scale_type(std::uint8_t scale_type); + + /// @brief Helper method the query the vertical and horizontal justification with a single call + /// @param[in] vjust reference parameter for the vertical justification + /// @param[in] hjust reference parameter for the horizontal justification + void get_justification(VerticalJustification &vjust, HorizontalJustification &hjust) const; + + /// @brief Returns the object ID of the referenced Graphics Data, Picture Graphic or object pointer + /// @returns The object ID of the referenced Graphics Data, Picture Graphic or object pointer or the null object ID + std::uint16_t get_graphic_id() const; + + /// @brief Sets the object ID of the referenced Graphics Data, Picture Graphic or object pointer + /// @param[in] newGraphicID The object ID of the referenced Graphics Data, Picture Graphic or object pointer or the null object ID + void set_graphic_id(std::uint16_t newGraphicID); + + private: + /// ScaleType bit-field + /// Bits 0-2 : scaling type + /// Bits 3-4 : horizontal justification + /// Bits 5-6 : vertical justification + /// Bit 7 : reserved + std::uint8_t scaleType = 0; + + /// Options bit-field + /// Bit 0 = Flashing + /// Bits 1–7 = reserved + std::uint8_t optionsBitfield = 0; + + std::uint16_t graphicID = NULL_OBJECT_ID; ///< Object ID of a referenced Graphics Data, Picture Graphic or object pointer or NULL_OBJECT_ID + }; + template /// @brief A specialized replacement for std::to_string /// @param object_id An ID of an IsoBus object diff --git a/isobus/src/isobus_virtual_terminal_objects.cpp b/isobus/src/isobus_virtual_terminal_objects.cpp index 70cafed88..481e17586 100644 --- a/isobus/src/isobus_virtual_terminal_objects.cpp +++ b/isobus/src/isobus_virtual_terminal_objects.cpp @@ -8619,4 +8619,165 @@ namespace isobus pointerType = type; } + VirtualTerminalObjectType ScaledGraphicObject::get_object_type() const + { + return VirtualTerminalObjectType::ScaledGraphic; + } + + bool ScaledGraphicObject::get_attribute(std::uint8_t attributeID, std::uint32_t &returnedAttributeData) const + { + switch (static_cast(attributeID)) + { + case AttributeName::Width: + returnedAttributeData = width; + return true; + + case AttributeName::Height: + returnedAttributeData = height; + return true; + + case AttributeName::ScaleType: + returnedAttributeData = scaleType; + return true; + + case AttributeName::Options: + returnedAttributeData = optionsBitfield; + return true; + + default: + return false; + } + } + + bool ScaledGraphicObject::get_option(Options option) const + { + return (0 != ((1 << static_cast(option)) & optionsBitfield)); + } + + void ScaledGraphicObject::set_option(Options option, bool value) + { + if (value) + { + optionsBitfield |= (1 << static_cast(option)); + } + else + { + optionsBitfield &= ~(1 << static_cast(option)); + } + } + + void ScaledGraphicObject::set_options(std::uint8_t options) + { + optionsBitfield = options; + } + + void ScaledGraphicObject::set_scale_type(std::uint8_t scale_type) + { + scaleType = scale_type; + } + + void ScaledGraphicObject::get_justification(VerticalJustification &vjust, HorizontalJustification &hjust) const + { + hjust = static_cast((scaleType & HORIZONTAL_JUSTIFICATION_MASK) >> 3); + vjust = static_cast((scaleType & VERTICAL_JUSTIFICATION_MASK) >> 5); + } + + std::uint16_t ScaledGraphicObject::get_graphic_id() const + { + return graphicID; + } + + void ScaledGraphicObject::set_graphic_id(std::uint16_t newGraphicID) + { + graphicID = newGraphicID; + } + + std::uint8_t ScaledGraphicObject::get_scale_type() const + { + return scaleType; + } + + bool ScaledGraphicObject::set_attribute(std::uint8_t attributeID, std::uint32_t rawData, const std::map> &, AttributeError &returnedError) + { + switch (static_cast(attributeID)) + { + case AttributeName::Width: + set_width(static_cast(rawData)); + return true; + + case AttributeName::Height: + set_height(static_cast(rawData)); + return true; + + case AttributeName::ScaleType: + if (((rawData & 0x07) >= 5) || + ((rawData & VERTICAL_JUSTIFICATION_MASK) == VERTICAL_JUSTIFICATION_MASK) || + ((rawData & HORIZONTAL_JUSTIFICATION_MASK) == HORIZONTAL_JUSTIFICATION_MASK) || + ((rawData & 0x80) == 0x80)) + { + returnedError = AttributeError::InvalidValue; + return false; + } + scaleType = static_cast(rawData); + return true; + + case AttributeName::Options: + optionsBitfield = static_cast(rawData & 0xFF); + return true; + default: + returnedError = AttributeError::InvalidAttributeID; + return false; + } + } + + bool ScaledGraphicObject::get_is_valid(const std::map> &objectPool) const + { + if (((scaleType & 0x07) >= 5) || + ((scaleType & VERTICAL_JUSTIFICATION_MASK) == VERTICAL_JUSTIFICATION_MASK) || + ((scaleType & HORIZONTAL_JUSTIFICATION_MASK) == HORIZONTAL_JUSTIFICATION_MASK) || + ((scaleType & 0x80) == 0x80)) + { + return false; + } + + if (NULL_OBJECT_ID != graphicID) + { + // Object identifier of a graphic object, or an Object Pointer object. + // Graphic objects include: + // —   Graphic Data object; + // —   Picture Graphic object. + // An Object Pointer object shall point only to one of the above listed graphic objects, + // another Object Pointer object, or the NULL object. + auto object = get_object_by_id(graphicID, objectPool); + + if (object && (VirtualTerminalObjectType::ObjectPointer == object->get_object_type())) + { + auto objectPointer = std::static_pointer_cast(object); + if (NULL_OBJECT_ID != objectPointer->get_value()) + { + object = get_object_by_id(objectPointer->get_value(), objectPool); + if (object && + (VirtualTerminalObjectType::PictureGraphic != object->get_object_type()) && + (VirtualTerminalObjectType::GraphicData != object->get_object_type())) + { + return false; + } + } + } + + if (object && + (VirtualTerminalObjectType::PictureGraphic != object->get_object_type()) && + (VirtualTerminalObjectType::GraphicData != object->get_object_type())) + { + return false; + } + } + return true; + } + + std::uint32_t ScaledGraphicObject::get_minumum_object_length() const + { + return 12; + } + } // namespace isobus diff --git a/isobus/src/isobus_virtual_terminal_working_set_base.cpp b/isobus/src/isobus_virtual_terminal_working_set_base.cpp index e9ae772c7..a7856f4ac 100644 --- a/isobus/src/isobus_virtual_terminal_working_set_base.cpp +++ b/isobus/src/isobus_virtual_terminal_working_set_base.cpp @@ -2355,6 +2355,37 @@ namespace isobus } break; + case VirtualTerminalObjectType::ScaledGraphic: + { + auto tempObject = std::make_shared(); + + if (iopLength >= tempObject->get_minumum_object_length()) + { + tempObject->set_id(decodedID); + tempObject->set_width((static_cast(iopData[3]) | (static_cast(iopData[4]) << 8))); + tempObject->set_height((static_cast(iopData[5]) | (static_cast(iopData[6]) << 8))); + tempObject->set_scale_type(iopData[7]); + tempObject->set_options(iopData[8]); + tempObject->set_graphic_id((static_cast(iopData[9]) | (static_cast(iopData[10]) << 8))); + const std::uint8_t numberOfMacrosToFollow = iopData[11]; + iopData += 12; + iopLength -= 12; + + // Next, parse macro list + retVal = parse_object_macro_reference(tempObject, numberOfMacrosToFollow, iopData, iopLength); + } + else + { + LOG_ERROR("[WS]: Not enough IOP data to parse scaled graphics object"); + } + + if (retVal) + { + retVal = add_or_replace_object(tempObject); + } + } + break; + default: { LOG_ERROR("[WS]: Unsupported Object (Type: %d)", decodedType);