blob: f8ee71208639b607bf6bb3a801a180bc1485b0bc [file] [edit]
#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