#include "utils/serializing_factory.hpp" #include "rive/decoders/bitmap_decoder.hpp" #include "rive/core/binary_reader.hpp" #include "rive/artboard.hpp" #include #include #include #include #include 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 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(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 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(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(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 m_bytes; uint64_t m_id; }; rcp SerializingFactory::makeRenderBuffer(RenderBufferType type, RenderBufferFlags flags, size_t size) { return make_rcp(&m_writer, m_renderBufferId++, type, flags, size); } rcp 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(id); } rcp 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(id); } rcp SerializingFactory::makeRenderPath(RawPath& rawPath, FillRule fillRule) { return make_rcp(&m_writer, m_renderPathId++, rawPath, fillRule); } rcp SerializingFactory::makeEmptyRenderPath() { return make_rcp(&m_writer, m_renderPathId++); } rcp SerializingFactory::makeRenderPaint() { return make_rcp(&m_writer, m_renderPaintId++); } rcp SerializingFactory::decodeImage(Span 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(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(path)->id()); m_writer->writeVarUint( static_cast(paint)->id()); } void clipPath(RenderPath* path) override { m_writer->writeVarUint((uint32_t)SerializeOp::clipPath); m_writer->writeVarUint(static_cast(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(image)->id()); m_writer->writeVarUint((uint32_t)blendMode); m_writer->writeFloat(opacity); } virtual void drawImageMesh(const RenderImage* image, ImageSampler samplerOptions, rcp positions, rcp uvs, rcp indices, uint32_t vertexCount, uint32_t indexCount, BlendMode blendMode, float opacity) override { m_writer->writeVarUint((uint32_t)SerializeOp::drawImageMesh); m_writer->writeVarUint( static_cast(image)->id()); m_writer->writeVarUint((uint32_t)blendMode); m_writer->writeFloat(opacity); m_writer->writeVarUint( static_cast(positions.get())->id()); m_writer->writeVarUint( static_cast(uvs.get())->id()); m_writer->writeVarUint( static_cast(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 SerializingFactory::makeRenderer() { return rivestd::make_unique(&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& fileA, std::vector& 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 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 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; }