| /* | |
| * Copyright 2026 Rive | |
| */ | |
| #ifndef _RIVE_SERIALIZE_OPS_HPP_ | |
| #define _RIVE_SERIALIZE_OPS_HPP_ | |
| #include "rive/core/binary_reader.hpp" | |
| #include "rive/core/binary_writer.hpp" | |
| #include "rive/math/raw_path.hpp" | |
| #include <vector> | |
| namespace rive | |
| { | |
| // Wire opcodes shared by SerializingFactory and replaySerializedCommands so | |
| // the two ends of the .sriv format cannot drift apart. | |
| enum class SerializeOp : uint32_t | |
| { | |
| 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, | |
| paintModulatedImage = 31, | |
| // Offscreen canvases (cache-as-bitmap). Content records inline between the | |
| // begin and end brackets; the canvas id shares the image id space so a | |
| // later drawImage of that id composites the canvas. | |
| makeRenderCanvas = 32, // id, width, height | |
| canvasContentBegin = 33, // id, clearColor | |
| canvasContentEnd = 34, // id | |
| modulateColor = 35, // color, replace | |
| // RenderPaint::additiveness. Its own op rather than a field on an existing | |
| // one so a stream with no additive content is byte for byte what it was | |
| // before additiveness existed. | |
| additiveness = 36, // paint id, value | |
| // drawImage / drawImageMesh carrying a non zero additiveness, which images | |
| // take per draw instead of off a paint. Same payload as the plain op plus | |
| // a trailing float, and only emitted when the value is non zero, so an | |
| // ordinary image draw still records as drawImage. | |
| drawImageAdditive = 37, | |
| drawImageMeshAdditive = 38, | |
| // Instanced image meshes | |
| makeImageMeshInstances = 39, // id, count | |
| setImageMeshInstancesData = 40, // id, instances[count] | |
| // image, positions, uvs, indices, instances | |
| drawImageMeshInstanced = 41, | |
| // Layer masking: multiply the current target by a factor derived from the | |
| // named image. Recorded inside the masked layer's canvas bracket, as the | |
| // last op before it closes. | |
| applyLayerMask = 42, // imageId, mode | |
| }; | |
| inline 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); | |
| } | |
| } | |
| inline RawPath deserializeRawPath(BinaryReader& reader) | |
| { | |
| RawPath path; | |
| size_t verbCount = static_cast<size_t>(reader.readVarUint64()); | |
| std::vector<PathVerb> verbs(verbCount); | |
| for (size_t i = 0; i < verbCount; ++i) | |
| verbs[i] = static_cast<PathVerb>(reader.readVarUint64()); | |
| size_t pointCount = static_cast<size_t>(reader.readVarUint64()); | |
| std::vector<Vec2D> pts(pointCount); | |
| for (size_t i = 0; i < pointCount; ++i) | |
| { | |
| pts[i].x = reader.readFloat32(); | |
| pts[i].y = reader.readFloat32(); | |
| } | |
| size_t p = 0; | |
| // A truncated stream can promise more points than it delivers. | |
| auto have = [&](size_t n) { return p + n <= pts.size(); }; | |
| for (PathVerb v : verbs) | |
| { | |
| switch (v) | |
| { | |
| case PathVerb::move: | |
| if (!have(1)) | |
| return path; | |
| path.move(pts[p++]); | |
| break; | |
| case PathVerb::line: | |
| if (!have(1)) | |
| return path; | |
| path.line(pts[p++]); | |
| break; | |
| case PathVerb::quad: | |
| if (!have(2)) | |
| return path; | |
| path.quad(pts[p], pts[p + 1]); | |
| p += 2; | |
| break; | |
| case PathVerb::cubic: | |
| if (!have(3)) | |
| return path; | |
| path.cubic(pts[p], pts[p + 1], pts[p + 2]); | |
| p += 3; | |
| break; | |
| case PathVerb::close: | |
| path.close(); | |
| break; | |
| } | |
| } | |
| return path; | |
| } | |
| } // namespace rive | |
| #endif |