| #ifndef _RIVE_TESTS_RIV_BYTES_HPP_ |
| #define _RIVE_TESTS_RIV_BYTES_HPP_ |
| |
| #include "rive/core/vector_binary_writer.hpp" |
| #include "rive/file.hpp" |
| #include "rive/generated/artboard_base.hpp" |
| #include "rive/generated/backboard_base.hpp" |
| #include "rive/generated/component_base.hpp" |
| #include "rive/generated/layout_component_base.hpp" |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| // A minimal .riv assembled by hand, so an object or a property can be placed |
| // where no exporter would put it -- which is how the import paths that only |
| // a malformed or forward-dated file reaches get exercised. |
| class RivBytes |
| { |
| public: |
| RivBytes() : m_writer(&m_objects) {} |
| |
| void object(uint16_t typeKey) { m_writer.writeVarUint((uint64_t)typeKey); } |
| void end() { m_writer.writeVarUint((uint64_t)0); } |
| |
| // ToC field ids: 0 uint (bools ride as uints), 1 string, 2 double. |
| void propUint(uint16_t key, uint64_t value) |
| { |
| m_toc[key] = 0; |
| m_writer.writeVarUint((uint64_t)key); |
| m_writer.writeVarUint(value); |
| } |
| void propBool(uint16_t key, bool value) |
| { |
| m_toc[key] = 0; |
| m_writer.writeVarUint((uint64_t)key); |
| m_writer.write((uint8_t)(value ? 1 : 0)); |
| } |
| void propString(uint16_t key, const std::string& value) |
| { |
| m_toc[key] = 1; |
| m_writer.writeVarUint((uint64_t)key); |
| m_writer.write(value); |
| } |
| void propFloat(uint16_t key, float value) |
| { |
| m_toc[key] = 2; |
| m_writer.writeVarUint((uint64_t)key); |
| m_writer.write(value); |
| } |
| // A key the file's ToC does not describe. |
| void propUintOutsideToc(uint16_t key, uint64_t value) |
| { |
| m_writer.writeVarUint((uint64_t)key); |
| m_writer.writeVarUint(value); |
| } |
| |
| std::vector<uint8_t> bytes() const |
| { |
| std::vector<uint8_t> out; |
| rive::VectorBinaryWriter writer(&out); |
| writer.write((const uint8_t*)"RIVE", 4); |
| writer.writeVarUint((uint64_t)rive::File::majorVersion); |
| writer.writeVarUint((uint64_t)rive::File::minorVersion); |
| writer.writeVarUint((uint64_t)0); // file id |
| for (const auto& entry : m_toc) |
| { |
| writer.writeVarUint((uint64_t)entry.first); |
| } |
| writer.writeVarUint((uint64_t)0); |
| // The reader refreshes its packed int every 8 bits: one uint32 per |
| // four keys (RuntimeHeader::read). |
| uint32_t packed = 0; |
| int bit = 0; |
| for (const auto& entry : m_toc) |
| { |
| packed |= entry.second << bit; |
| bit += 2; |
| if (bit == 8) |
| { |
| writer.write(packed); |
| packed = 0; |
| bit = 0; |
| } |
| } |
| if (bit != 0) |
| { |
| writer.write(packed); |
| } |
| out.insert(out.end(), m_objects.begin(), m_objects.end()); |
| return out; |
| } |
| |
| private: |
| std::vector<uint8_t> m_objects; |
| rive::VectorBinaryWriter m_writer; |
| std::map<uint16_t, uint32_t> m_toc; |
| }; |
| |
| // One 100x100 artboard named "A", with no Backboard of its own -- for streams |
| // that need to place something between the two. |
| inline void writeArtboardObject(RivBytes& riv) |
| { |
| riv.object(rive::ArtboardBase::typeKey); |
| riv.propString(rive::ComponentBase::namePropertyKey, "A"); |
| riv.propFloat(rive::LayoutComponentBase::widthPropertyKey, 100.0f); |
| riv.propFloat(rive::LayoutComponentBase::heightPropertyKey, 100.0f); |
| riv.end(); |
| } |
| |
| // The smallest loadable file: a Backboard and one 100x100 artboard named "A". |
| inline void writeArtboard(RivBytes& riv) |
| { |
| riv.object(rive::BackboardBase::typeKey); |
| riv.end(); |
| writeArtboardObject(riv); |
| } |
| |
| #endif |