mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 13:11:19 +01:00
fix(scripting): search first parent transform component to build script node feature: modulate opacity (#11427) 128d9d61e0 * feature: modulate opacity * fix: clang-format * fix: rust renderer has a no-op modulateOpacity * fix: no-op modulateOpacity for canvas android * feature: modulate opacity on android canvas * fix: rcp ref * fix: missing override * fix: gms * fix: make flutter_renderer match cg one * fix: josh pr feedback * fix: remove CG transparency layer * fix: save modulated gradient up-front * fix: store only one gradient ref * fix: remove specific constructor * fix: use GradDataArray! * fix: expose currentModulatedOpacity * fix: cg_factory modulated opacity value * fix: modulate negative opacity test * fix: verify double modulate negative also clamps Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com> Co-authored-by: hernan <hernan@rive.app>
1408 lines
43 KiB
C++
1408 lines
43 KiB
C++
#include "utils/serializing_factory.hpp"
|
|
#include "rive/decoders/bitmap_decoder.hpp"
|
|
#include "rive/core/binary_reader.hpp"
|
|
#include "rive/artboard.hpp"
|
|
#include <cstring>
|
|
#include <stdlib.h>
|
|
#include <filesystem>
|
|
#include <inttypes.h>
|
|
#include <unordered_map>
|
|
|
|
using namespace rive;
|
|
|
|
// Threshold for floating point tests.
|
|
static const float epsilon = 0.001f;
|
|
|
|
enum class SerializeOp : unsigned char
|
|
{
|
|
makeRenderBuffer = 0,
|
|
makeLinearGradient = 1,
|
|
makeRadialGradient = 2,
|
|
makeRenderPath = 3,
|
|
makeRenderPaint = 5,
|
|
decodeImage = 6,
|
|
save = 7,
|
|
restore = 8,
|
|
transform = 9,
|
|
drawPath = 10,
|
|
clipPath = 11,
|
|
drawImage = 12,
|
|
drawImageMesh = 13,
|
|
|
|
// RenderBuffer
|
|
setVertexBufferData = 14,
|
|
setIndexBufferData = 15,
|
|
|
|
// RenderPath
|
|
addRawPath = 16,
|
|
rewind = 17,
|
|
fillRule = 18,
|
|
|
|
// RenderPaint
|
|
style = 20,
|
|
color = 21,
|
|
thickness = 22,
|
|
join = 23,
|
|
cap = 24,
|
|
feather = 25,
|
|
blendMode = 26,
|
|
shader = 27,
|
|
|
|
frame = 28,
|
|
frameSize = 29,
|
|
modulateOpacity = 30,
|
|
|
|
};
|
|
|
|
static const char* opToName(SerializeOp op)
|
|
{
|
|
switch (op)
|
|
{
|
|
case SerializeOp::makeRenderBuffer:
|
|
return "makeRenderBuffer";
|
|
case SerializeOp::makeLinearGradient:
|
|
return "makeLinearGradient";
|
|
case SerializeOp::makeRadialGradient:
|
|
return "makeRadialGradient";
|
|
case SerializeOp::makeRenderPath:
|
|
return "makeRenderPath";
|
|
case SerializeOp::makeRenderPaint:
|
|
return "makeRenderPaint";
|
|
case SerializeOp::decodeImage:
|
|
return "decodeImage";
|
|
case SerializeOp::save:
|
|
return "save";
|
|
case SerializeOp::restore:
|
|
return "restore";
|
|
case SerializeOp::transform:
|
|
return "transform";
|
|
case SerializeOp::drawPath:
|
|
return "drawPath";
|
|
case SerializeOp::clipPath:
|
|
return "clipPath";
|
|
case SerializeOp::drawImage:
|
|
return "drawImage";
|
|
case SerializeOp::drawImageMesh:
|
|
return "drawImageMesh";
|
|
|
|
// RenderBuffer
|
|
case SerializeOp::setVertexBufferData:
|
|
return "setVertexBufferData";
|
|
case SerializeOp::setIndexBufferData:
|
|
return "setIndexBufferData";
|
|
|
|
// RenderPath
|
|
case SerializeOp::addRawPath:
|
|
return "addRawPath";
|
|
case SerializeOp::rewind:
|
|
return "rewind";
|
|
case SerializeOp::fillRule:
|
|
return "fillRule";
|
|
|
|
// RenderPaint
|
|
case SerializeOp::style:
|
|
return "style";
|
|
case SerializeOp::color:
|
|
return "color";
|
|
case SerializeOp::thickness:
|
|
return "thickness";
|
|
case SerializeOp::join:
|
|
return "join";
|
|
case SerializeOp::cap:
|
|
return "cap";
|
|
case SerializeOp::feather:
|
|
return "feather";
|
|
case SerializeOp::blendMode:
|
|
return "blendMode";
|
|
case SerializeOp::shader:
|
|
return "shader";
|
|
|
|
case SerializeOp::frame:
|
|
return "frame";
|
|
case SerializeOp::frameSize:
|
|
return "frameSize";
|
|
case SerializeOp::modulateOpacity:
|
|
return "modulateOpacity";
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
class SerializingRenderImage : public RenderImage
|
|
{
|
|
public:
|
|
SerializingRenderImage(uint64_t id, uint32_t width, uint32_t height) :
|
|
m_id(id)
|
|
{
|
|
m_Width = width;
|
|
m_Height = height;
|
|
}
|
|
|
|
uint64_t id() const { return m_id; }
|
|
|
|
private:
|
|
uint64_t m_id;
|
|
};
|
|
|
|
static void serializeRawPath(BinaryWriter* writer, const RawPath& path)
|
|
{
|
|
auto verbs = path.verbs();
|
|
auto points = path.points();
|
|
writer->writeVarUint((uint64_t)verbs.size());
|
|
for (auto verb : verbs)
|
|
{
|
|
writer->writeVarUint((uint64_t)verb);
|
|
}
|
|
writer->writeVarUint((uint64_t)points.size());
|
|
for (auto point : points)
|
|
{
|
|
writer->writeFloat(point.x);
|
|
writer->writeFloat(point.y);
|
|
}
|
|
}
|
|
|
|
class SerializingRenderShader : public RenderShader
|
|
{
|
|
public:
|
|
SerializingRenderShader(uint64_t id) : m_id(id) {}
|
|
uint64_t id() const { return m_id; }
|
|
|
|
private:
|
|
uint64_t m_id;
|
|
};
|
|
|
|
class SerializingRenderPaint : public RenderPaint
|
|
{
|
|
public:
|
|
SerializingRenderPaint(BinaryWriter* writer, uint64_t id) :
|
|
m_id(id), m_writer(writer)
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::makeRenderPaint);
|
|
m_writer->writeVarUint(m_id);
|
|
}
|
|
|
|
void color(unsigned int value) override
|
|
{
|
|
if (m_color == value)
|
|
{
|
|
return;
|
|
}
|
|
m_color = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::color);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint(m_color);
|
|
}
|
|
|
|
void style(RenderPaintStyle value) override
|
|
{
|
|
if (m_stroked == (value == RenderPaintStyle::stroke))
|
|
{
|
|
return;
|
|
}
|
|
m_stroked = value == RenderPaintStyle::stroke;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::style);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint((uint32_t)(m_stroked ? 0 : 1));
|
|
}
|
|
|
|
void thickness(float value) override
|
|
{
|
|
if (m_thickness == value)
|
|
{
|
|
return;
|
|
}
|
|
m_thickness = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::thickness);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeFloat(m_thickness);
|
|
}
|
|
|
|
void join(StrokeJoin value) override
|
|
{
|
|
if (m_join == value)
|
|
{
|
|
return;
|
|
}
|
|
m_join = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::join);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint((uint32_t)m_join);
|
|
}
|
|
|
|
void cap(StrokeCap value) override
|
|
{
|
|
if (m_cap == value)
|
|
{
|
|
return;
|
|
}
|
|
m_cap = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::cap);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint((uint32_t)m_cap);
|
|
}
|
|
|
|
void blendMode(BlendMode value) override
|
|
{
|
|
if (m_blendMode == value)
|
|
{
|
|
return;
|
|
}
|
|
m_blendMode = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::blendMode);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint((uint32_t)m_blendMode);
|
|
}
|
|
|
|
void shader(rcp<RenderShader> shader) override
|
|
{
|
|
if (m_shader == shader)
|
|
{
|
|
return;
|
|
}
|
|
m_shader = shader;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::shader);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint(
|
|
shader == nullptr
|
|
? 0
|
|
: static_cast<SerializingRenderShader*>(shader.get())->id());
|
|
}
|
|
void invalidateStroke() override {}
|
|
void feather(float value) override
|
|
{
|
|
if (m_feather == value)
|
|
{
|
|
return;
|
|
}
|
|
m_feather = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::feather);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeFloat(m_feather);
|
|
}
|
|
|
|
uint64_t id() const { return m_id; }
|
|
|
|
private:
|
|
uint64_t m_id;
|
|
BinaryWriter* m_writer;
|
|
rcp<RenderShader> m_shader;
|
|
|
|
unsigned int m_color = 0xFF000000;
|
|
float m_thickness = 1;
|
|
StrokeJoin m_join = StrokeJoin::miter;
|
|
StrokeCap m_cap = StrokeCap::butt;
|
|
float m_feather = 0;
|
|
BlendMode m_blendMode = BlendMode::srcOver;
|
|
bool m_stroked = false;
|
|
};
|
|
|
|
class SerializingRenderPath : public RenderPath
|
|
{
|
|
public:
|
|
void rewind() override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::rewind);
|
|
m_writer->writeVarUint(m_id);
|
|
}
|
|
|
|
void fillRule(FillRule value) override
|
|
{
|
|
if (m_fillRule == value)
|
|
{
|
|
return;
|
|
}
|
|
m_fillRule = value;
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::fillRule);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint((uint64_t)value);
|
|
}
|
|
|
|
void addPath(CommandPath* path, const Mat2D& transform) override
|
|
{
|
|
RIVE_UNREACHABLE();
|
|
}
|
|
void addRenderPath(RenderPath* path, const Mat2D& transform) override
|
|
{
|
|
RIVE_UNREACHABLE();
|
|
}
|
|
|
|
void moveTo(float x, float y) override { RIVE_UNREACHABLE(); }
|
|
void lineTo(float x, float y) override { RIVE_UNREACHABLE(); }
|
|
void cubicTo(float ox, float oy, float ix, float iy, float x, float y)
|
|
override
|
|
{
|
|
RIVE_UNREACHABLE();
|
|
}
|
|
void close() override { RIVE_UNREACHABLE(); }
|
|
|
|
void addRawPath(const RawPath& path) override
|
|
{
|
|
m_rawPath.addPath(path, nullptr);
|
|
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::addRawPath);
|
|
m_writer->writeVarUint(m_id);
|
|
serializeRawPath(m_writer, path);
|
|
}
|
|
|
|
SerializingRenderPath(BinaryWriter* writer, uint64_t id) :
|
|
m_id(id), m_writer(writer)
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::makeRenderPath);
|
|
m_writer->writeVarUint(m_id);
|
|
}
|
|
|
|
SerializingRenderPath(BinaryWriter* writer,
|
|
uint64_t id,
|
|
const RawPath& path,
|
|
FillRule fillRule) :
|
|
m_id(id), m_fillRule(fillRule), m_rawPath(path), m_writer(writer)
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::makeRenderPath);
|
|
m_writer->writeVarUint(m_id);
|
|
this->fillRule(fillRule);
|
|
addRawPath(path);
|
|
}
|
|
|
|
uint64_t id() const { return m_id; }
|
|
|
|
private:
|
|
uint64_t m_id;
|
|
FillRule m_fillRule = FillRule::nonZero;
|
|
RawPath m_rawPath;
|
|
BinaryWriter* m_writer;
|
|
};
|
|
|
|
class SerializingRenderBuffer : public RenderBuffer
|
|
{
|
|
public:
|
|
SerializingRenderBuffer(BinaryWriter* writer,
|
|
uint64_t id,
|
|
RenderBufferType type,
|
|
RenderBufferFlags flags,
|
|
size_t sizeInBytes) :
|
|
RenderBuffer(type, flags, sizeInBytes),
|
|
m_writer(writer),
|
|
m_bytes(sizeInBytes),
|
|
m_id(id)
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::makeRenderBuffer);
|
|
m_writer->writeVarUint(m_id);
|
|
m_writer->writeVarUint((uint64_t)sizeInBytes);
|
|
m_writer->writeVarUint((uint32_t)type);
|
|
m_writer->writeVarUint((uint32_t)flags);
|
|
}
|
|
|
|
uint64_t id() const { return m_id; }
|
|
void* onMap() override { return m_bytes.data(); }
|
|
void onUnmap() override
|
|
{
|
|
switch (type())
|
|
{
|
|
case RenderBufferType::index:
|
|
m_writer->writeVarUint(
|
|
(uint32_t)SerializeOp::setIndexBufferData);
|
|
break;
|
|
case RenderBufferType::vertex:
|
|
m_writer->writeVarUint(
|
|
(uint32_t)SerializeOp::setVertexBufferData);
|
|
break;
|
|
default:
|
|
RIVE_UNREACHABLE();
|
|
}
|
|
m_writer->writeVarUint(m_id);
|
|
switch (type())
|
|
{
|
|
case RenderBufferType::vertex:
|
|
{
|
|
auto count = m_bytes.size() / sizeof(float);
|
|
auto floatBuffer = reinterpret_cast<float*>(m_bytes.data());
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
m_writer->writeFloat(floatBuffer[i]);
|
|
}
|
|
break;
|
|
}
|
|
case RenderBufferType::index:
|
|
{
|
|
auto count = m_bytes.size() / sizeof(uint16_t);
|
|
auto shortBuffer = reinterpret_cast<uint16_t*>(m_bytes.data());
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
m_writer->writeVarUint((uint32_t)shortBuffer[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
BinaryWriter* m_writer;
|
|
std::vector<uint8_t> m_bytes;
|
|
uint64_t m_id;
|
|
};
|
|
|
|
rcp<RenderBuffer> SerializingFactory::makeRenderBuffer(RenderBufferType type,
|
|
RenderBufferFlags flags,
|
|
size_t size)
|
|
{
|
|
return make_rcp<SerializingRenderBuffer>(&m_writer,
|
|
m_renderBufferId++,
|
|
type,
|
|
flags,
|
|
size);
|
|
}
|
|
|
|
rcp<RenderShader> SerializingFactory::makeLinearGradient(
|
|
float sx,
|
|
float sy,
|
|
float ex,
|
|
float ey,
|
|
const ColorInt colors[], // [count]
|
|
const float stops[], // [count]
|
|
size_t count)
|
|
{
|
|
auto id = m_renderShaderId++;
|
|
m_writer.writeVarUint((uint32_t)SerializeOp::makeLinearGradient);
|
|
m_writer.writeVarUint(id);
|
|
m_writer.writeVarUint((uint64_t)count);
|
|
for (auto i = 0; i < count; i++)
|
|
{
|
|
m_writer.writeVarUint(colors[i]);
|
|
m_writer.writeFloat(stops[i]);
|
|
}
|
|
m_writer.writeFloat(sx);
|
|
m_writer.writeFloat(sy);
|
|
m_writer.writeFloat(ex);
|
|
m_writer.writeFloat(ey);
|
|
|
|
return make_rcp<SerializingRenderShader>(id);
|
|
}
|
|
|
|
rcp<RenderShader> SerializingFactory::makeRadialGradient(
|
|
float cx,
|
|
float cy,
|
|
float radius,
|
|
const ColorInt colors[], // [count]
|
|
const float stops[], // [count]
|
|
size_t count)
|
|
{
|
|
auto id = m_renderShaderId++;
|
|
m_writer.writeVarUint((uint32_t)SerializeOp::makeRadialGradient);
|
|
m_writer.writeVarUint(id);
|
|
m_writer.writeVarUint((uint64_t)count);
|
|
for (auto i = 0; i < count; i++)
|
|
{
|
|
m_writer.writeVarUint(colors[i]);
|
|
m_writer.writeFloat(stops[i]);
|
|
}
|
|
m_writer.writeFloat(cx);
|
|
m_writer.writeFloat(cy);
|
|
m_writer.writeFloat(radius);
|
|
|
|
return make_rcp<SerializingRenderShader>(id);
|
|
}
|
|
|
|
rcp<RenderPath> SerializingFactory::makeRenderPath(RawPath& rawPath,
|
|
FillRule fillRule)
|
|
{
|
|
return make_rcp<SerializingRenderPath>(&m_writer,
|
|
m_renderPathId++,
|
|
rawPath,
|
|
fillRule);
|
|
}
|
|
|
|
rcp<RenderPath> SerializingFactory::makeEmptyRenderPath()
|
|
{
|
|
return make_rcp<SerializingRenderPath>(&m_writer, m_renderPathId++);
|
|
}
|
|
|
|
rcp<RenderPaint> SerializingFactory::makeRenderPaint()
|
|
{
|
|
return make_rcp<SerializingRenderPaint>(&m_writer, m_renderPaintId++);
|
|
}
|
|
|
|
rcp<RenderImage> SerializingFactory::decodeImage(Span<const uint8_t> data)
|
|
{
|
|
auto id = m_renderImageId++;
|
|
m_writer.writeVarUint((uint32_t)SerializeOp::decodeImage);
|
|
m_writer.writeVarUint(id);
|
|
m_writer.writeVarUint((uint64_t)data.size());
|
|
m_writer.write(data.data(), data.size());
|
|
|
|
auto bitmap = Bitmap::decode(data.data(), data.size());
|
|
if (!bitmap)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return make_rcp<SerializingRenderImage>(id,
|
|
bitmap->width(),
|
|
bitmap->height());
|
|
}
|
|
|
|
class SerializingRenderer : public Renderer
|
|
{
|
|
public:
|
|
SerializingRenderer(BinaryWriter* writer) : m_writer(writer) {}
|
|
|
|
void save() override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::save);
|
|
}
|
|
void restore() override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::restore);
|
|
}
|
|
void transform(const Mat2D& mat) override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::transform);
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
m_writer->writeFloat(mat[i]);
|
|
}
|
|
}
|
|
|
|
void modulateOpacity(float opacity) override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::modulateOpacity);
|
|
m_writer->writeFloat(opacity);
|
|
}
|
|
|
|
void drawPath(RenderPath* path, RenderPaint* paint) override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::drawPath);
|
|
m_writer->writeVarUint(static_cast<SerializingRenderPath*>(path)->id());
|
|
m_writer->writeVarUint(
|
|
static_cast<SerializingRenderPaint*>(paint)->id());
|
|
}
|
|
|
|
void clipPath(RenderPath* path) override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::clipPath);
|
|
m_writer->writeVarUint(static_cast<SerializingRenderPath*>(path)->id());
|
|
}
|
|
|
|
virtual void drawImage(const RenderImage* image,
|
|
ImageSampler samplerOptions,
|
|
BlendMode blendMode,
|
|
float opacity) override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::drawImage);
|
|
m_writer->writeVarUint(
|
|
static_cast<const SerializingRenderImage*>(image)->id());
|
|
m_writer->writeVarUint((uint32_t)blendMode);
|
|
m_writer->writeFloat(opacity);
|
|
}
|
|
|
|
virtual void drawImageMesh(const RenderImage* image,
|
|
ImageSampler samplerOptions,
|
|
rcp<RenderBuffer> positions,
|
|
rcp<RenderBuffer> uvs,
|
|
rcp<RenderBuffer> indices,
|
|
uint32_t vertexCount,
|
|
uint32_t indexCount,
|
|
BlendMode blendMode,
|
|
float opacity) override
|
|
{
|
|
m_writer->writeVarUint((uint32_t)SerializeOp::drawImageMesh);
|
|
m_writer->writeVarUint(
|
|
static_cast<const SerializingRenderImage*>(image)->id());
|
|
m_writer->writeVarUint((uint32_t)blendMode);
|
|
m_writer->writeFloat(opacity);
|
|
m_writer->writeVarUint(
|
|
static_cast<SerializingRenderBuffer*>(positions.get())->id());
|
|
m_writer->writeVarUint(
|
|
static_cast<SerializingRenderBuffer*>(uvs.get())->id());
|
|
m_writer->writeVarUint(
|
|
static_cast<SerializingRenderBuffer*>(indices.get())->id());
|
|
}
|
|
|
|
private:
|
|
BinaryWriter* m_writer;
|
|
};
|
|
|
|
SerializingFactory::SerializingFactory() : m_writer(&m_buffer)
|
|
{
|
|
m_writer.write((const uint8_t*)"SRIV", 4);
|
|
m_writer.writeVarUint((uint32_t)1);
|
|
}
|
|
|
|
std::unique_ptr<Renderer> SerializingFactory::makeRenderer()
|
|
{
|
|
return rivestd::make_unique<SerializingRenderer>(&m_writer);
|
|
}
|
|
|
|
void SerializingFactory::addFrame()
|
|
{
|
|
Artboard::incFrameId();
|
|
m_writer.writeVarUint((uint32_t)SerializeOp::frame);
|
|
}
|
|
|
|
void SerializingFactory::frameSize(uint32_t width, uint32_t height)
|
|
{
|
|
m_writer.writeVarUint((uint32_t)SerializeOp::frameSize);
|
|
m_writer.writeVarUint(width);
|
|
m_writer.writeVarUint(height);
|
|
}
|
|
|
|
void SerializingFactory::save(const char* filename)
|
|
{
|
|
FILE* fp = fopen(filename, "wb");
|
|
fwrite(m_buffer.data(), 1, m_buffer.size(), fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
void SerializingFactory::saveTarnished(const char* filename)
|
|
{
|
|
auto path = std::string("silvers/tarnished/");
|
|
if (!std::filesystem::exists(path))
|
|
{
|
|
if (!std::filesystem::create_directories(path))
|
|
{
|
|
fprintf(stderr, "failed to create directory %s\n", path.c_str());
|
|
}
|
|
}
|
|
auto fullFileName = path + std::string(filename) + std::string(".sriv");
|
|
save(fullFileName.c_str());
|
|
}
|
|
|
|
static bool varUintMatches(uint64_t op,
|
|
std::string name,
|
|
BinaryReader& readerA,
|
|
BinaryReader& readerB,
|
|
uint64_t* value = nullptr)
|
|
{
|
|
auto valueA = readerA.readVarUint64();
|
|
auto valueB = readerB.readVarUint64();
|
|
if (valueA != valueB)
|
|
{
|
|
fprintf(stderr,
|
|
"%s for %s doesn't match %" PRIu64 " != %" PRIu64 "\n",
|
|
name.c_str(),
|
|
opToName((SerializeOp)op),
|
|
valueA,
|
|
valueB);
|
|
return false;
|
|
}
|
|
if (value != nullptr)
|
|
{
|
|
*value = valueA;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool bytesMatches(uint64_t op,
|
|
std::string name,
|
|
uint64_t size,
|
|
BinaryReader& readerA,
|
|
BinaryReader& readerB)
|
|
{
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
auto valueA = readerA.readByte();
|
|
auto valueB = readerB.readByte();
|
|
if (valueA != valueB)
|
|
{
|
|
fprintf(stderr,
|
|
"%s [%i] for %s doesn't match %hhu != %hhu\n",
|
|
name.c_str(),
|
|
i,
|
|
opToName((SerializeOp)op),
|
|
valueA,
|
|
valueB);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool floatMatches(uint64_t op,
|
|
std::string name,
|
|
BinaryReader& readerA,
|
|
BinaryReader& readerB,
|
|
float* value = nullptr)
|
|
{
|
|
auto valueA = readerA.readFloat32();
|
|
auto valueB = readerB.readFloat32();
|
|
if (std::abs(valueA - valueB) > epsilon)
|
|
{
|
|
fprintf(stderr,
|
|
"%s for %s doesn't match %f != %f\n",
|
|
name.c_str(),
|
|
opToName((SerializeOp)op),
|
|
valueA,
|
|
valueB);
|
|
return false;
|
|
}
|
|
if (value != nullptr)
|
|
{
|
|
*value = valueA;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool shortMatches(uint64_t op,
|
|
std::string name,
|
|
BinaryReader& readerA,
|
|
BinaryReader& readerB,
|
|
uint16_t* value = nullptr)
|
|
{
|
|
auto valueA = readerA.readUint16();
|
|
auto valueB = readerB.readUint16();
|
|
if (valueA != valueB)
|
|
{
|
|
fprintf(stderr,
|
|
"%s for %s doesn't match %i != %i\n",
|
|
name.c_str(),
|
|
opToName((SerializeOp)op),
|
|
valueA,
|
|
valueB);
|
|
return false;
|
|
}
|
|
if (value != nullptr)
|
|
{
|
|
*value = valueA;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool vec2DMatches(uint64_t op,
|
|
std::string name,
|
|
BinaryReader& readerA,
|
|
BinaryReader& readerB,
|
|
Vec2D* value = nullptr)
|
|
{
|
|
auto ax = readerA.readFloat32();
|
|
auto ay = readerA.readFloat32();
|
|
|
|
auto bx = readerB.readFloat32();
|
|
auto by = readerB.readFloat32();
|
|
|
|
// if (ax != bx || ay != by)
|
|
if (rive::Vec2D::distance(Vec2D(ax, ay), Vec2D(bx, by)) > epsilon)
|
|
{
|
|
fprintf(stderr,
|
|
"%s for %s doesn't match (%f, %f) != (%f, %f)\n",
|
|
name.c_str(),
|
|
opToName((SerializeOp)op),
|
|
ax,
|
|
ay,
|
|
bx,
|
|
by);
|
|
return false;
|
|
}
|
|
if (value != nullptr)
|
|
{
|
|
*value = Vec2D(ax, ay);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool gradientMatches(uint64_t op,
|
|
std::string name,
|
|
BinaryReader& readerA,
|
|
BinaryReader& readerB,
|
|
Vec2D* value = nullptr)
|
|
{
|
|
if (!varUintMatches(op,
|
|
std::string("make_") + name + std::string("_id"),
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
uint64_t count = 0;
|
|
if (!varUintMatches(op,
|
|
std::string("make_") + name + std::string("_count"),
|
|
readerA,
|
|
readerB,
|
|
&count))
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
std::string colorName = std::string("make_") + name +
|
|
std::string("_color_") + std::to_string(i);
|
|
if (!varUintMatches(op, colorName, readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
std::string stopName = std::string("make_") + name +
|
|
std::string("_stop_") + std::to_string(i);
|
|
if (!floatMatches(op, stopName, readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool advancedMatch(std::vector<uint8_t>& fileA, std::vector<uint8_t>& fileB)
|
|
{
|
|
BinaryReader readerA(fileA);
|
|
BinaryReader readerB(fileB);
|
|
if (readerA.readByte() != 'S' || readerA.readByte() != 'R' ||
|
|
readerA.readByte() != 'I' || readerA.readByte() != 'V')
|
|
{
|
|
fprintf(stderr, "advancedMatch: invalid header A\n");
|
|
return false;
|
|
}
|
|
if (readerB.readByte() != 'S' || readerB.readByte() != 'R' ||
|
|
readerB.readByte() != 'I' || readerB.readByte() != 'V')
|
|
{
|
|
fprintf(stderr, "advancedMatch: invalid header B\n");
|
|
return false;
|
|
}
|
|
|
|
if (readerA.readVarUint64() != 1 || readerB.readVarUint64() != 1)
|
|
{
|
|
fprintf(stderr, "advancedMatch: invalid version\n");
|
|
return false;
|
|
}
|
|
std::unordered_map<uint64_t, uint64_t> renderBufferSizes;
|
|
while (!readerA.reachedEnd())
|
|
{
|
|
if (readerB.reachedEnd())
|
|
{
|
|
fprintf(stderr, "generated file is shorter.\n");
|
|
return false;
|
|
}
|
|
auto opA = readerA.readVarUint64();
|
|
auto opB = readerB.readVarUint64();
|
|
if (opA != opB)
|
|
{
|
|
fprintf(stderr,
|
|
"expected %s but got %s\n",
|
|
opToName((SerializeOp)opA),
|
|
opToName((SerializeOp)opB));
|
|
}
|
|
switch ((SerializeOp)opA)
|
|
{
|
|
case SerializeOp::makeRenderBuffer:
|
|
{
|
|
uint64_t id = 0;
|
|
uint64_t size = 0;
|
|
if (!varUintMatches(opA,
|
|
"make_renderbuffer_id",
|
|
readerA,
|
|
readerB,
|
|
&id))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"make_renderbuffer_size",
|
|
readerA,
|
|
readerB,
|
|
&size))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"make_renderbuffer_flags",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
renderBufferSizes[id] = size;
|
|
break;
|
|
}
|
|
case SerializeOp::makeLinearGradient:
|
|
if (!gradientMatches(opA, "lineargradient", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!vec2DMatches(opA,
|
|
"make_lineargradient_start",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!vec2DMatches(opA,
|
|
"make_lineargradient_end",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::makeRadialGradient:
|
|
if (!gradientMatches(opA, "lineargradient", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!vec2DMatches(opA,
|
|
"make_lineargradient_center",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!floatMatches(opA,
|
|
"make_lineargradient_radius",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::makeRenderPath:
|
|
if (!varUintMatches(opA,
|
|
"make_renderpath_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::makeRenderPaint:
|
|
if (!varUintMatches(opA,
|
|
"make_renderpaint_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::decodeImage:
|
|
{
|
|
if (!varUintMatches(opA, "decodeimage_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
uint64_t size = 0;
|
|
if (!varUintMatches(opA,
|
|
"decodeimage_size",
|
|
readerA,
|
|
readerB,
|
|
&size))
|
|
{
|
|
return false;
|
|
}
|
|
if (!bytesMatches(opA,
|
|
"decodeimage_bytes",
|
|
size,
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case SerializeOp::save:
|
|
break;
|
|
case SerializeOp::restore:
|
|
break;
|
|
case SerializeOp::transform:
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
if (!floatMatches(opA,
|
|
std::string("transform[") +
|
|
std::to_string(i) + std::string("]"),
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case SerializeOp::drawPath:
|
|
if (!varUintMatches(opA, "drawpath_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "drawpath_paint_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::clipPath:
|
|
if (!varUintMatches(opA, "clippath_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::drawImage:
|
|
if (!varUintMatches(opA, "drawimage_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"drawimage_blendmode",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!floatMatches(opA, "drawimage_opacity", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::drawImageMesh:
|
|
if (!varUintMatches(opA, "drawimagemesh_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"drawimagemesh_blendmode",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!floatMatches(opA,
|
|
"drawimagemesh_opacity",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"drawimagemesh_vertex_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"drawimagemesh_uv_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"drawimagemesh_index_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case SerializeOp::setVertexBufferData:
|
|
{
|
|
uint64_t id = 0;
|
|
if (!varUintMatches(opA,
|
|
"setvertexbufferdata_id",
|
|
readerA,
|
|
readerB,
|
|
&id))
|
|
{
|
|
return false;
|
|
}
|
|
if (renderBufferSizes.find(id) == renderBufferSizes.end())
|
|
{
|
|
fprintf(stderr,
|
|
"could not find render buffer with id: %" PRIu64
|
|
"\n",
|
|
id);
|
|
return false;
|
|
}
|
|
uint64_t size = renderBufferSizes[id];
|
|
uint64_t floatCount = size / sizeof(float);
|
|
for (int i = 0; i < floatCount; i++)
|
|
{
|
|
if (!floatMatches(opA,
|
|
std::string("setvertexbufferdata_[") +
|
|
std::to_string(i) + std::string("]"),
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SerializeOp::setIndexBufferData:
|
|
{
|
|
uint64_t id = 0;
|
|
if (!varUintMatches(opA,
|
|
"setindexbufferdata_id",
|
|
readerA,
|
|
readerB,
|
|
&id))
|
|
{
|
|
return false;
|
|
}
|
|
if (renderBufferSizes.find(id) == renderBufferSizes.end())
|
|
{
|
|
fprintf(stderr,
|
|
"could not find render buffer with id: %" PRIu64
|
|
"\n",
|
|
id);
|
|
return false;
|
|
}
|
|
uint64_t size = renderBufferSizes[id];
|
|
uint64_t shortCount = size / sizeof(uint16_t);
|
|
for (int i = 0; i < shortCount; i++)
|
|
{
|
|
if (!shortMatches(opA,
|
|
std::string("setindexbufferdata_[") +
|
|
std::to_string(i) + std::string("]"),
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// RenderPath
|
|
case SerializeOp::addRawPath:
|
|
{
|
|
uint64_t verbCount = 0;
|
|
uint64_t pointCount = 0;
|
|
if (!varUintMatches(opA, "addrawpath_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"addrawpath_verb_count",
|
|
readerA,
|
|
readerB,
|
|
&verbCount))
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < verbCount; i++)
|
|
{
|
|
if (!varUintMatches(opA,
|
|
std::string("addrawpath_verb[") +
|
|
std::to_string(i) +
|
|
std::string("]"),
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (!varUintMatches(opA,
|
|
"addrawpath_point_count",
|
|
readerA,
|
|
readerB,
|
|
&pointCount))
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < pointCount; i++)
|
|
{
|
|
if (!vec2DMatches(opA,
|
|
std::string("addrawpath_point[") +
|
|
std::to_string(i) + std::string("]"),
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SerializeOp::rewind:
|
|
if (!varUintMatches(opA, "rewind_path_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::fillRule:
|
|
if (!varUintMatches(opA, "fillrule_path_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "fillrule_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// RenderPaint
|
|
case SerializeOp::style:
|
|
if (!varUintMatches(opA, "style_paint_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "style_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::color:
|
|
if (!varUintMatches(opA, "color_paint_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "color_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::thickness:
|
|
if (!varUintMatches(opA,
|
|
"thickness_paint_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!floatMatches(opA, "thickness_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::join:
|
|
if (!varUintMatches(opA, "join_paint_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "join_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::cap:
|
|
if (!varUintMatches(opA, "cap_paint_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "cap_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::feather:
|
|
if (!varUintMatches(opA, "feather_paint_id", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!floatMatches(opA, "feather_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::blendMode:
|
|
if (!varUintMatches(opA,
|
|
"blendmode_paint_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "blendmode_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::shader:
|
|
if (!varUintMatches(opA,
|
|
"setgradient_paint_id",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "setgradient_value", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case SerializeOp::frame:
|
|
break;
|
|
case SerializeOp::frameSize:
|
|
if (!varUintMatches(opA, "framesize_width", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
if (!varUintMatches(opA, "framesize_height", readerA, readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case SerializeOp::modulateOpacity:
|
|
if (!floatMatches(opA,
|
|
"modulateopacity_value",
|
|
readerA,
|
|
readerB))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!readerB.reachedEnd())
|
|
{
|
|
fprintf(stderr, "generated file is longer, otherwise matches.\n");
|
|
return false;
|
|
}
|
|
// for (size_t i = 0; i < length; i++)
|
|
// {
|
|
// if (fileA[i] != fileB[i])
|
|
// {
|
|
// fprintf(stderr,
|
|
// "SerializingFactory::matches - %s buffer did not match "
|
|
// "generated one.\n",
|
|
// filename);
|
|
|
|
// return false;
|
|
// }
|
|
// }
|
|
return true;
|
|
}
|
|
|
|
bool SerializingFactory::matches(const char* filename)
|
|
{
|
|
auto fullFileName =
|
|
std::string("silvers/") + std::string(filename) + std::string(".sriv");
|
|
const char* rebaseline = getenv("REBASELINE_SILVERS");
|
|
if (rebaseline != nullptr)
|
|
{
|
|
save(fullFileName.c_str());
|
|
return true;
|
|
}
|
|
|
|
FILE* fp = fopen(fullFileName.c_str(), "rb");
|
|
if (fp == nullptr)
|
|
{
|
|
fprintf(stderr,
|
|
"SerializingFactory::matches - %s is missing\n",
|
|
fullFileName.c_str());
|
|
return false;
|
|
}
|
|
fseek(fp, 0, SEEK_END);
|
|
const size_t length = ftell(fp);
|
|
if (m_buffer.size() != length)
|
|
{
|
|
fprintf(stderr,
|
|
"SerializingFactory::matches - %s size differs from generated "
|
|
"one %zu != %zu.\n",
|
|
filename,
|
|
m_buffer.size(),
|
|
length);
|
|
fclose(fp);
|
|
saveTarnished(filename);
|
|
return false;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
std::vector<uint8_t> existing(length);
|
|
if (fread(existing.data(), 1, length, fp) != length)
|
|
{
|
|
fprintf(stderr,
|
|
"SerializingFactory::matches - %s could not be read.\n",
|
|
filename);
|
|
return false;
|
|
}
|
|
fclose(fp);
|
|
|
|
if (!advancedMatch(existing, m_buffer))
|
|
{
|
|
saveTarnished(filename);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|