@shopify/react-native-skia 1.10.2 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cpp/api/JsiSkApi.h +3 -0
- package/cpp/api/recorder/ColorFilters.h +133 -0
- package/cpp/api/recorder/Command.h +58 -0
- package/cpp/api/recorder/Convertor.h +1213 -0
- package/cpp/api/recorder/DataTypes.h +232 -0
- package/cpp/api/recorder/DrawingCtx.h +187 -0
- package/cpp/api/recorder/Drawings.h +950 -0
- package/cpp/api/recorder/ImageFilters.h +292 -0
- package/cpp/api/recorder/ImageFit.h +108 -0
- package/cpp/api/recorder/JsiRecorder.h +314 -0
- package/cpp/api/recorder/Paint.h +191 -0
- package/cpp/api/recorder/PathEffects.h +194 -0
- package/cpp/api/recorder/RNRecorder.h +637 -0
- package/cpp/api/recorder/Shaders.h +406 -0
- package/cpp/rnskia/dom/nodes/JsiAtlasNode.h +3 -2
- package/cpp/rnskia/dom/nodes/JsiImageNode.h +3 -2
- package/jestSetup.js +8 -0
- package/jestSetup.mjs +8 -0
- package/lib/commonjs/renderer/components/image/Image.d.ts +1 -1
- package/lib/commonjs/renderer/components/image/Image.js +8 -2
- package/lib/commonjs/renderer/components/image/Image.js.map +1 -1
- package/lib/commonjs/skia/types/Recorder.d.ts +52 -0
- package/lib/commonjs/skia/types/Recorder.js +6 -0
- package/lib/commonjs/skia/types/Recorder.js.map +1 -0
- package/lib/commonjs/skia/types/Skia.d.ts +2 -0
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/index.d.ts +1 -0
- package/lib/commonjs/skia/types/index.js +11 -0
- package/lib/commonjs/skia/types/index.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkia.js +3 -0
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/commonjs/sksg/Container.d.ts +6 -1
- package/lib/commonjs/sksg/Container.js +59 -2
- package/lib/commonjs/sksg/Container.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/DrawingContext.js +1 -0
- package/lib/commonjs/sksg/Recorder/DrawingContext.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js +189 -0
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
- package/lib/commonjs/sksg/Recorder/Recorder.d.ts +2 -2
- package/lib/commonjs/sksg/Recorder/Recorder.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/Visitor.d.ts +2 -2
- package/lib/commonjs/sksg/Recorder/Visitor.js +2 -2
- package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
- package/lib/module/renderer/components/image/Image.d.ts +1 -1
- package/lib/module/renderer/components/image/Image.js +8 -2
- package/lib/module/renderer/components/image/Image.js.map +1 -1
- package/lib/module/skia/types/Recorder.d.ts +52 -0
- package/lib/module/skia/types/Recorder.js +2 -0
- package/lib/module/skia/types/Recorder.js.map +1 -0
- package/lib/module/skia/types/Skia.d.ts +2 -0
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/index.d.ts +1 -0
- package/lib/module/skia/types/index.js +1 -0
- package/lib/module/skia/types/index.js.map +1 -1
- package/lib/module/skia/web/JsiSkia.js +3 -0
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/sksg/Container.d.ts +6 -1
- package/lib/module/sksg/Container.js +59 -2
- package/lib/module/sksg/Container.js.map +1 -1
- package/lib/module/sksg/Recorder/DrawingContext.js +1 -0
- package/lib/module/sksg/Recorder/DrawingContext.js.map +1 -1
- package/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
- package/lib/module/sksg/Recorder/ReanimatedRecorder.js +182 -0
- package/lib/module/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
- package/lib/module/sksg/Recorder/Recorder.d.ts +2 -2
- package/lib/module/sksg/Recorder/Recorder.js.map +1 -1
- package/lib/module/sksg/Recorder/Visitor.d.ts +2 -2
- package/lib/module/sksg/Recorder/Visitor.js +2 -2
- package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
- package/lib/typescript/lib/commonjs/renderer/components/image/Image.d.ts +4 -1
- package/lib/typescript/lib/commonjs/skia/types/Recorder.d.ts +1 -0
- package/lib/typescript/lib/commonjs/skia/web/JsiSkia.d.ts +1 -0
- package/lib/typescript/lib/commonjs/sksg/Container.d.ts +5 -1
- package/lib/typescript/lib/commonjs/sksg/Reconciler.d.ts +6 -0
- package/lib/typescript/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +47 -0
- package/lib/typescript/lib/module/mock/index.d.ts +4 -1
- package/lib/typescript/lib/module/renderer/components/image/Image.d.ts +4 -1
- package/lib/typescript/lib/module/skia/Skia.web.d.ts +1 -0
- package/lib/typescript/lib/module/skia/types/Recorder.d.ts +1 -0
- package/lib/typescript/lib/module/skia/types/index.d.ts +1 -0
- package/lib/typescript/lib/module/skia/web/JsiSkia.d.ts +1 -0
- package/lib/typescript/lib/module/sksg/Container.d.ts +5 -1
- package/lib/typescript/lib/module/sksg/Reconciler.d.ts +6 -0
- package/lib/typescript/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +46 -0
- package/lib/typescript/src/renderer/components/image/Image.d.ts +1 -1
- package/lib/typescript/src/skia/types/Recorder.d.ts +52 -0
- package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
- package/lib/typescript/src/skia/types/index.d.ts +1 -0
- package/lib/typescript/src/sksg/Container.d.ts +6 -1
- package/lib/typescript/src/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
- package/lib/typescript/src/sksg/Recorder/Recorder.d.ts +2 -2
- package/lib/typescript/src/sksg/Recorder/Visitor.d.ts +2 -2
- package/package.json +3 -2
- package/src/renderer/components/image/Image.tsx +2 -2
- package/src/skia/types/Recorder.ts +91 -0
- package/src/skia/types/Skia.ts +2 -0
- package/src/skia/types/index.ts +1 -0
- package/src/skia/web/JsiSkia.ts +3 -0
- package/src/sksg/Container.ts +63 -4
- package/src/sksg/Recorder/DrawingContext.ts +1 -0
- package/src/sksg/Recorder/ReanimatedRecorder.ts +271 -0
- package/src/sksg/Recorder/Recorder.ts +2 -2
- package/src/sksg/Recorder/Visitor.ts +17 -12
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <optional>
|
|
4
|
+
|
|
5
|
+
#include "Command.h"
|
|
6
|
+
#include "Convertor.h"
|
|
7
|
+
#include "DrawingCtx.h"
|
|
8
|
+
#include "ImageFit.h"
|
|
9
|
+
|
|
10
|
+
namespace RNSkia {
|
|
11
|
+
|
|
12
|
+
struct CircleCmdProps {
|
|
13
|
+
std::optional<float> cx;
|
|
14
|
+
std::optional<float> cy;
|
|
15
|
+
std::optional<SkPoint> c;
|
|
16
|
+
float r;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
class CircleCmd : public Command {
|
|
20
|
+
private:
|
|
21
|
+
CircleCmdProps props;
|
|
22
|
+
|
|
23
|
+
public:
|
|
24
|
+
CircleCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
25
|
+
Variables &variables)
|
|
26
|
+
: Command(CommandType::DrawCircle) {
|
|
27
|
+
convertProperty(runtime, object, "cx", props.cx, variables);
|
|
28
|
+
convertProperty(runtime, object, "cy", props.cy, variables);
|
|
29
|
+
convertProperty(runtime, object, "c", props.c, variables);
|
|
30
|
+
convertProperty(runtime, object, "r", props.r, variables);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void draw(DrawingCtx *ctx) {
|
|
34
|
+
auto paint = ctx->getPaint();
|
|
35
|
+
if (props.c.has_value()) {
|
|
36
|
+
ctx->canvas->drawCircle(props.c.value(), props.r, paint);
|
|
37
|
+
} else {
|
|
38
|
+
auto cx = props.cx.value_or(0);
|
|
39
|
+
auto cy = props.cy.value_or(0);
|
|
40
|
+
auto r = props.r;
|
|
41
|
+
ctx->canvas->drawCircle(cx, cy, r, paint);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
struct RectCmdProps {
|
|
47
|
+
float x = 0;
|
|
48
|
+
float y = 0;
|
|
49
|
+
std::optional<float> width;
|
|
50
|
+
std::optional<float> height;
|
|
51
|
+
std::optional<SkRect> rect;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
class RectCmd : public Command {
|
|
55
|
+
private:
|
|
56
|
+
RectCmdProps props;
|
|
57
|
+
|
|
58
|
+
public:
|
|
59
|
+
RectCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
60
|
+
Variables &variables)
|
|
61
|
+
: Command(CommandType::DrawRect) {
|
|
62
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
63
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
64
|
+
convertProperty(runtime, object, "width", props.width, variables);
|
|
65
|
+
convertProperty(runtime, object, "height", props.height, variables);
|
|
66
|
+
convertProperty(runtime, object, "rect", props.rect, variables);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void draw(DrawingCtx *ctx) {
|
|
70
|
+
auto [x, y, width, height, rect] = props;
|
|
71
|
+
if (rect.has_value()) {
|
|
72
|
+
ctx->canvas->drawRect(rect.value(), ctx->getPaint());
|
|
73
|
+
} else {
|
|
74
|
+
auto rct = SkRect::MakeXYWH(x, y, width.value(), height.value());
|
|
75
|
+
ctx->canvas->drawRect(rct, ctx->getPaint());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
struct PathCmdProps {
|
|
81
|
+
SkPath path;
|
|
82
|
+
float start;
|
|
83
|
+
float end;
|
|
84
|
+
std::optional<StrokeOpts> stroke;
|
|
85
|
+
std::optional<SkPathFillType> fillType;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
class PathCmd : public Command {
|
|
89
|
+
private:
|
|
90
|
+
PathCmdProps props;
|
|
91
|
+
|
|
92
|
+
public:
|
|
93
|
+
PathCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
94
|
+
Variables &variables)
|
|
95
|
+
: Command(CommandType::DrawPath) {
|
|
96
|
+
convertProperty(runtime, object, "path", props.path, variables);
|
|
97
|
+
convertProperty(runtime, object, "start", props.start, variables);
|
|
98
|
+
convertProperty(runtime, object, "end", props.end, variables);
|
|
99
|
+
convertProperty(runtime, object, "stroke", props.stroke, variables);
|
|
100
|
+
convertProperty(runtime, object, "fillType", props.fillType, variables);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void draw(DrawingCtx *ctx) {
|
|
104
|
+
// Saturate start and end values (clamp between 0 and 1)
|
|
105
|
+
float start = std::clamp(props.start, 0.0f, 1.0f);
|
|
106
|
+
float end = std::clamp(props.end, 0.0f, 1.0f);
|
|
107
|
+
|
|
108
|
+
// Check conditions that require path mutation
|
|
109
|
+
bool hasStartOffset = start != 0.0f;
|
|
110
|
+
bool hasEndOffset = end != 1.0f;
|
|
111
|
+
bool hasStrokeOptions = props.stroke.has_value();
|
|
112
|
+
bool hasFillType = props.fillType.has_value();
|
|
113
|
+
bool willMutatePath =
|
|
114
|
+
hasStartOffset || hasEndOffset || hasStrokeOptions || hasFillType;
|
|
115
|
+
|
|
116
|
+
std::shared_ptr<const SkPath> pathToUse;
|
|
117
|
+
|
|
118
|
+
if (willMutatePath) {
|
|
119
|
+
// Create a filtered path for modifications
|
|
120
|
+
SkPath filteredPath(props.path);
|
|
121
|
+
|
|
122
|
+
// Handle path trimming
|
|
123
|
+
if (hasStartOffset || hasEndOffset) {
|
|
124
|
+
auto pe =
|
|
125
|
+
SkTrimPathEffect::Make(start, end, SkTrimPathEffect::Mode::kNormal);
|
|
126
|
+
if (pe != nullptr) {
|
|
127
|
+
SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
|
|
128
|
+
if (!pe->filterPath(&filteredPath, filteredPath, &rec, nullptr)) {
|
|
129
|
+
throw std::runtime_error(
|
|
130
|
+
"Failed trimming path with parameters start: " +
|
|
131
|
+
std::to_string(start) + ", end: " + std::to_string(end));
|
|
132
|
+
}
|
|
133
|
+
filteredPath.swap(filteredPath);
|
|
134
|
+
} else {
|
|
135
|
+
throw std::runtime_error(
|
|
136
|
+
"Failed trimming path with parameters start: " +
|
|
137
|
+
std::to_string(start) + ", end: " + std::to_string(end));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Set fill type
|
|
142
|
+
auto p = std::make_shared<SkPath>(filteredPath);
|
|
143
|
+
if (props.fillType.has_value()) {
|
|
144
|
+
p->setFillType(props.fillType.value());
|
|
145
|
+
}
|
|
146
|
+
// Handle stroke options
|
|
147
|
+
if (hasStrokeOptions) {
|
|
148
|
+
const auto &stroke = props.stroke.value();
|
|
149
|
+
SkPaint strokePaint;
|
|
150
|
+
|
|
151
|
+
if (stroke.cap.has_value()) {
|
|
152
|
+
strokePaint.setStrokeCap(stroke.cap.value());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (stroke.join.has_value()) {
|
|
156
|
+
strokePaint.setStrokeJoin(stroke.join.value());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (stroke.width.has_value()) {
|
|
160
|
+
strokePaint.setStrokeWidth(stroke.width.value());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (stroke.miter_limit.has_value()) {
|
|
164
|
+
strokePaint.setStrokeMiter(stroke.miter_limit.value());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
float precision = stroke.precision.value_or(1.0f);
|
|
168
|
+
|
|
169
|
+
auto strokedPath = std::make_shared<SkPath>();
|
|
170
|
+
if (!skpathutils::FillPathWithPaint(*p, strokePaint, strokedPath.get(),
|
|
171
|
+
nullptr, precision)) {
|
|
172
|
+
throw std::runtime_error("Failed to apply stroke to path");
|
|
173
|
+
}
|
|
174
|
+
pathToUse = std::const_pointer_cast<const SkPath>(strokedPath);
|
|
175
|
+
} else {
|
|
176
|
+
pathToUse = std::const_pointer_cast<const SkPath>(p);
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
// Use the original path directly
|
|
180
|
+
pathToUse = std::make_shared<const SkPath>(props.path);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!pathToUse) {
|
|
184
|
+
throw std::runtime_error(
|
|
185
|
+
"Path node could not resolve path props correctly.");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Draw the final path
|
|
189
|
+
ctx->canvas->drawPath(*pathToUse, ctx->getPaint());
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
struct LineCmdProps {
|
|
194
|
+
SkPoint p1;
|
|
195
|
+
SkPoint p2;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
class LineCmd : public Command {
|
|
199
|
+
private:
|
|
200
|
+
LineCmdProps props;
|
|
201
|
+
|
|
202
|
+
public:
|
|
203
|
+
LineCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
204
|
+
Variables &variables)
|
|
205
|
+
: Command(CommandType::DrawLine) {
|
|
206
|
+
convertProperty(runtime, object, "p1", props.p1, variables);
|
|
207
|
+
convertProperty(runtime, object, "p2", props.p2, variables);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
void draw(DrawingCtx *ctx) {
|
|
211
|
+
ctx->canvas->drawLine(props.p1.x(), props.p1.y(), props.p2.x(),
|
|
212
|
+
props.p2.y(), ctx->getPaint());
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
struct TextPathProps {
|
|
217
|
+
std::optional<SkFont> font;
|
|
218
|
+
std::string text;
|
|
219
|
+
SkPath path;
|
|
220
|
+
float initialOffset;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
class TextPathCmd : public Command {
|
|
224
|
+
private:
|
|
225
|
+
TextPathProps props;
|
|
226
|
+
|
|
227
|
+
public:
|
|
228
|
+
TextPathCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
229
|
+
Variables &variables)
|
|
230
|
+
: Command(CommandType::DrawTextPath) {
|
|
231
|
+
convertProperty(runtime, object, "font", props.font, variables);
|
|
232
|
+
convertProperty(runtime, object, "text", props.text, variables);
|
|
233
|
+
convertProperty(runtime, object, "path", props.path, variables);
|
|
234
|
+
convertProperty(runtime, object, "initialOffset", props.initialOffset,
|
|
235
|
+
variables);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
void draw(DrawingCtx *ctx) {
|
|
239
|
+
auto [font, text, path, initialOffset] = props;
|
|
240
|
+
if (font.has_value()) {
|
|
241
|
+
// Get glyphs
|
|
242
|
+
auto numGlyphIds =
|
|
243
|
+
font->countText(text.c_str(), text.length(), SkTextEncoding::kUTF8);
|
|
244
|
+
|
|
245
|
+
std::vector<SkGlyphID> glyphIds;
|
|
246
|
+
glyphIds.reserve(numGlyphIds);
|
|
247
|
+
auto ids = font->textToGlyphs(
|
|
248
|
+
text.c_str(), text.length(), SkTextEncoding::kUTF8,
|
|
249
|
+
static_cast<SkGlyphID *>(glyphIds.data()), numGlyphIds);
|
|
250
|
+
|
|
251
|
+
// Get glyph widths
|
|
252
|
+
int glyphsSize = static_cast<int>(ids);
|
|
253
|
+
std::vector<SkScalar> widthPtrs;
|
|
254
|
+
widthPtrs.resize(glyphsSize);
|
|
255
|
+
font->getWidthsBounds(glyphIds.data(), numGlyphIds,
|
|
256
|
+
static_cast<SkScalar *>(widthPtrs.data()), nullptr,
|
|
257
|
+
nullptr); // TODO: Should we use paint somehow here?
|
|
258
|
+
|
|
259
|
+
std::vector<SkRSXform> rsx;
|
|
260
|
+
SkContourMeasureIter meas(path, false, 1);
|
|
261
|
+
|
|
262
|
+
auto cont = meas.next();
|
|
263
|
+
auto dist = initialOffset;
|
|
264
|
+
|
|
265
|
+
for (size_t i = 0; i < text.length() && cont != nullptr; ++i) {
|
|
266
|
+
auto width = widthPtrs[i];
|
|
267
|
+
dist += width / 2;
|
|
268
|
+
if (dist > cont->length()) {
|
|
269
|
+
// jump to next contour
|
|
270
|
+
cont = meas.next();
|
|
271
|
+
if (cont == nullptr) {
|
|
272
|
+
// We have come to the end of the path - terminate the string
|
|
273
|
+
// right here.
|
|
274
|
+
text = text.substr(0, i);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
dist = width / 2;
|
|
278
|
+
}
|
|
279
|
+
// Gives us the (x, y) coordinates as well as the cos/sin of the
|
|
280
|
+
// tangent line at that position.
|
|
281
|
+
SkPoint pos;
|
|
282
|
+
SkVector tan;
|
|
283
|
+
if (!cont->getPosTan(dist, &pos, &tan)) {
|
|
284
|
+
throw std::runtime_error(
|
|
285
|
+
"Could not calculate distance when resolving text path");
|
|
286
|
+
}
|
|
287
|
+
auto px = pos.x();
|
|
288
|
+
auto py = pos.y();
|
|
289
|
+
auto tx = tan.x();
|
|
290
|
+
auto ty = tan.y();
|
|
291
|
+
|
|
292
|
+
auto adjustedX = px - (width / 2) * tx;
|
|
293
|
+
auto adjustedY = py - (width / 2) * ty;
|
|
294
|
+
|
|
295
|
+
rsx.push_back(SkRSXform::Make(tx, ty, adjustedX, adjustedY));
|
|
296
|
+
dist += width / 2;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
auto blob = SkTextBlob::MakeFromRSXform(text.c_str(), text.length(),
|
|
300
|
+
rsx.data(), *font);
|
|
301
|
+
ctx->canvas->drawTextBlob(blob, 0, 0, ctx->getPaint());
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
struct TextCmdProps {
|
|
307
|
+
std::optional<SkFont> font;
|
|
308
|
+
std::string text;
|
|
309
|
+
float x;
|
|
310
|
+
float y;
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
class TextCmd : public Command {
|
|
314
|
+
private:
|
|
315
|
+
TextCmdProps props;
|
|
316
|
+
|
|
317
|
+
public:
|
|
318
|
+
TextCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
319
|
+
Variables &variables)
|
|
320
|
+
: Command(CommandType::DrawText) {
|
|
321
|
+
convertProperty(runtime, object, "font", props.font, variables);
|
|
322
|
+
convertProperty(runtime, object, "text", props.text, variables);
|
|
323
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
324
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
void draw(DrawingCtx *ctx) {
|
|
328
|
+
auto [font, text, x, y] = props;
|
|
329
|
+
auto paint = ctx->getPaint();
|
|
330
|
+
if (font.has_value()) {
|
|
331
|
+
ctx->canvas->drawSimpleText(text.c_str(), text.length(),
|
|
332
|
+
SkTextEncoding::kUTF8, x, y, font.value(),
|
|
333
|
+
paint);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// Add to Drawings.h after existing command structures
|
|
339
|
+
struct BoxShadowCmdProps {
|
|
340
|
+
float dx = 0;
|
|
341
|
+
float dy = 0;
|
|
342
|
+
float spread = 0;
|
|
343
|
+
float blur = 0;
|
|
344
|
+
std::optional<SkColor> color;
|
|
345
|
+
std::optional<bool> inner;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
struct BoxCmdProps {
|
|
349
|
+
std::variant<SkRect, SkRRect> box;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
class BoxCmd : public Command {
|
|
353
|
+
private:
|
|
354
|
+
BoxCmdProps props;
|
|
355
|
+
std::vector<BoxShadowCmdProps> shadows;
|
|
356
|
+
|
|
357
|
+
// Helper function to inflate RRect (deflate is just negative inflation)
|
|
358
|
+
SkRRect inflate(const SkRRect &box, float dx, float dy, float tx = 0,
|
|
359
|
+
float ty = 0) {
|
|
360
|
+
const auto &rect = box.rect();
|
|
361
|
+
SkRect newRect =
|
|
362
|
+
SkRect::MakeXYWH(rect.x() - dx + tx, rect.y() - dy + ty,
|
|
363
|
+
rect.width() + 2 * dx, rect.height() + 2 * dy);
|
|
364
|
+
|
|
365
|
+
SkRRect result;
|
|
366
|
+
result.setRectXY(newRect, box.radii()[0].fX + dx, box.radii()[0].fY + dy);
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
SkRRect deflate(const SkRRect &box, float dx, float dy, float tx = 0,
|
|
371
|
+
float ty = 0) {
|
|
372
|
+
return inflate(box, -dx, -dy, tx, ty);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
public:
|
|
376
|
+
BoxCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
377
|
+
const jsi::Array &shadowsArray, Variables &variables)
|
|
378
|
+
: Command(CommandType::DrawBox) {
|
|
379
|
+
|
|
380
|
+
convertProperty(runtime, object, "box", props.box, variables);
|
|
381
|
+
size_t shadowCount = shadowsArray.size(runtime);
|
|
382
|
+
shadows.reserve(shadowCount);
|
|
383
|
+
|
|
384
|
+
for (size_t i = 0; i < shadowCount; i++) {
|
|
385
|
+
auto shadowObj =
|
|
386
|
+
shadowsArray.getValueAtIndex(runtime, i).asObject(runtime);
|
|
387
|
+
BoxShadowCmdProps shadow;
|
|
388
|
+
|
|
389
|
+
convertProperty(runtime, shadowObj, "dx", shadow.dx, variables);
|
|
390
|
+
convertProperty(runtime, shadowObj, "dy", shadow.dy, variables);
|
|
391
|
+
convertProperty(runtime, shadowObj, "spread", shadow.spread, variables);
|
|
392
|
+
convertProperty(runtime, shadowObj, "blur", shadow.blur, variables);
|
|
393
|
+
convertProperty(runtime, shadowObj, "color", shadow.color, variables);
|
|
394
|
+
convertProperty(runtime, shadowObj, "inner", shadow.inner, variables);
|
|
395
|
+
|
|
396
|
+
shadows.push_back(shadow);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
void draw(DrawingCtx *ctx) {
|
|
401
|
+
|
|
402
|
+
// Get current paint properties
|
|
403
|
+
auto paint = ctx->getPaint();
|
|
404
|
+
float opacity = paint.getAlphaf();
|
|
405
|
+
|
|
406
|
+
// Convert box to RRect if needed
|
|
407
|
+
SkRRect box;
|
|
408
|
+
if (std::holds_alternative<SkRect>(props.box)) {
|
|
409
|
+
auto rect = std::get<SkRect>(props.box);
|
|
410
|
+
box.setRectXY(rect, 0, 0);
|
|
411
|
+
} else {
|
|
412
|
+
box = std::get<SkRRect>(props.box);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Draw outer shadows first
|
|
416
|
+
for (const auto &shadow : shadows) {
|
|
417
|
+
if (!shadow.inner.value_or(false)) {
|
|
418
|
+
SkPaint shadowPaint;
|
|
419
|
+
shadowPaint.setAntiAlias(true);
|
|
420
|
+
shadowPaint.setColor(shadow.color.value_or(SK_ColorBLACK));
|
|
421
|
+
shadowPaint.setAlphaf(opacity);
|
|
422
|
+
shadowPaint.setMaskFilter(SkMaskFilter::MakeBlur(
|
|
423
|
+
SkBlurStyle::kNormal_SkBlurStyle, shadow.blur, true));
|
|
424
|
+
|
|
425
|
+
auto shadowBox =
|
|
426
|
+
inflate(box, shadow.spread, shadow.spread, shadow.dx, shadow.dy);
|
|
427
|
+
ctx->canvas->drawRRect(shadowBox, shadowPaint);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Draw main box
|
|
432
|
+
ctx->canvas->drawRRect(box, paint);
|
|
433
|
+
|
|
434
|
+
// Draw inner shadows
|
|
435
|
+
for (const auto &shadow : shadows) {
|
|
436
|
+
if (shadow.inner.value_or(false)) {
|
|
437
|
+
ctx->canvas->save();
|
|
438
|
+
|
|
439
|
+
// Clip to box bounds
|
|
440
|
+
ctx->canvas->clipRRect(box, SkClipOp::kIntersect, true);
|
|
441
|
+
|
|
442
|
+
SkPaint shadowPaint;
|
|
443
|
+
shadowPaint.setAntiAlias(true);
|
|
444
|
+
shadowPaint.setColor(shadow.color.value_or(SK_ColorBLACK));
|
|
445
|
+
shadowPaint.setAlphaf(opacity);
|
|
446
|
+
shadowPaint.setMaskFilter(SkMaskFilter::MakeBlur(
|
|
447
|
+
SkBlurStyle::kNormal_SkBlurStyle, shadow.blur, true));
|
|
448
|
+
|
|
449
|
+
// Calculate shadow bounds
|
|
450
|
+
float delta = 10 + std::max(std::abs(shadow.dx), std::abs(shadow.dy));
|
|
451
|
+
auto inner =
|
|
452
|
+
deflate(box, shadow.spread, shadow.spread, shadow.dx, shadow.dy);
|
|
453
|
+
auto outer = inflate(box, delta, delta);
|
|
454
|
+
|
|
455
|
+
ctx->canvas->drawDRRect(outer, inner, shadowPaint);
|
|
456
|
+
ctx->canvas->restore();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
struct ImageCmdProps {
|
|
463
|
+
float x = 0;
|
|
464
|
+
float y = 0;
|
|
465
|
+
std::optional<float> width;
|
|
466
|
+
std::optional<float> height;
|
|
467
|
+
std::optional<SkRect> rect;
|
|
468
|
+
std::string fit;
|
|
469
|
+
std::optional<sk_sp<SkImage>> image;
|
|
470
|
+
std::optional<SkSamplingOptions> sampling;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
class ImageCmd : public Command {
|
|
474
|
+
private:
|
|
475
|
+
ImageCmdProps props;
|
|
476
|
+
|
|
477
|
+
public:
|
|
478
|
+
ImageCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
479
|
+
Variables &variables)
|
|
480
|
+
: Command(CommandType::DrawImage) {
|
|
481
|
+
convertProperty(runtime, object, "rect", props.rect, variables);
|
|
482
|
+
convertProperty(runtime, object, "image", props.image, variables);
|
|
483
|
+
convertProperty(runtime, object, "sampling", props.sampling, variables);
|
|
484
|
+
|
|
485
|
+
convertProperty(runtime, object, "fit", props.fit, variables);
|
|
486
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
487
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
488
|
+
convertProperty(runtime, object, "width", props.width, variables);
|
|
489
|
+
convertProperty(runtime, object, "height", props.height, variables);
|
|
490
|
+
convertProperty(runtime, object, "rect", props.rect, variables);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
void draw(DrawingCtx *ctx) {
|
|
494
|
+
auto [x, y, width, height, rect, fit, image, sampling] = props;
|
|
495
|
+
if (image.has_value()) {
|
|
496
|
+
auto img = image.value();
|
|
497
|
+
auto hasRect =
|
|
498
|
+
rect.has_value() || (width.has_value() && height.has_value());
|
|
499
|
+
if (hasRect) {
|
|
500
|
+
auto src = SkRect::MakeXYWH(0, 0, img->width(), img->height());
|
|
501
|
+
auto dst = rect.has_value()
|
|
502
|
+
? rect.value()
|
|
503
|
+
: SkRect::MakeXYWH(x, y, width.value(), height.value());
|
|
504
|
+
auto rects = RNSkiaImage::fitRects(fit, src, dst);
|
|
505
|
+
ctx->canvas->drawImageRect(
|
|
506
|
+
img, rects.src, rects.dst,
|
|
507
|
+
sampling.value_or(SkSamplingOptions(SkFilterMode::kLinear)),
|
|
508
|
+
&(ctx->getPaint()), SkCanvas::kStrict_SrcRectConstraint);
|
|
509
|
+
} else {
|
|
510
|
+
throw std::runtime_error(
|
|
511
|
+
"Image node could not resolve image dimension props.");
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
struct PointsCmdProps {
|
|
518
|
+
std::vector<SkPoint> points;
|
|
519
|
+
SkCanvas::PointMode mode;
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
class PointsCmd : public Command {
|
|
523
|
+
private:
|
|
524
|
+
PointsCmdProps props;
|
|
525
|
+
|
|
526
|
+
public:
|
|
527
|
+
PointsCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
528
|
+
Variables &variables)
|
|
529
|
+
: Command(CommandType::DrawPoints) {
|
|
530
|
+
convertProperty(runtime, object, "points", props.points, variables);
|
|
531
|
+
convertProperty(runtime, object, "mode", props.mode, variables);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
void draw(DrawingCtx *ctx) {
|
|
535
|
+
ctx->canvas->drawPoints(props.mode, props.points.size(),
|
|
536
|
+
props.points.data(), ctx->getPaint());
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
struct RRectCmdProps {
|
|
541
|
+
std::optional<SkRRect> rect;
|
|
542
|
+
float x = 0;
|
|
543
|
+
float y = 0;
|
|
544
|
+
std::optional<float> width;
|
|
545
|
+
std::optional<float> height;
|
|
546
|
+
std::optional<Radius> r;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
class RRectCmd : public Command {
|
|
550
|
+
private:
|
|
551
|
+
RRectCmdProps props;
|
|
552
|
+
|
|
553
|
+
public:
|
|
554
|
+
RRectCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
555
|
+
Variables &variables)
|
|
556
|
+
: Command(CommandType::DrawRRect) {
|
|
557
|
+
convertProperty(runtime, object, "rect", props.rect, variables);
|
|
558
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
559
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
560
|
+
convertProperty(runtime, object, "width", props.width, variables);
|
|
561
|
+
convertProperty(runtime, object, "height", props.height, variables);
|
|
562
|
+
convertProperty(runtime, object, "r", props.r, variables);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
void draw(DrawingCtx *ctx) {
|
|
566
|
+
auto [rect, x, y, width, height, r] = props;
|
|
567
|
+
if (rect.has_value()) {
|
|
568
|
+
ctx->canvas->drawRRect(rect.value(), ctx->getPaint());
|
|
569
|
+
} else {
|
|
570
|
+
if (!width.has_value() || !height.has_value() || !r.has_value()) {
|
|
571
|
+
throw std::runtime_error("Invalid properties for rounded rect");
|
|
572
|
+
}
|
|
573
|
+
auto rct = SkRRect::MakeRectXY(
|
|
574
|
+
SkRect::MakeXYWH(x, y, width.value(), height.value()), r.value().rX,
|
|
575
|
+
r.value().rY);
|
|
576
|
+
ctx->canvas->drawRRect(rct, ctx->getPaint());
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
struct OvalCmdProps {
|
|
582
|
+
std::optional<SkRect> rect;
|
|
583
|
+
float x = 0;
|
|
584
|
+
float y = 0;
|
|
585
|
+
std::optional<float> width;
|
|
586
|
+
std::optional<float> height;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
class OvalCmd : public Command {
|
|
590
|
+
private:
|
|
591
|
+
OvalCmdProps props;
|
|
592
|
+
|
|
593
|
+
public:
|
|
594
|
+
OvalCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
595
|
+
Variables &variables)
|
|
596
|
+
: Command(CommandType::DrawOval) {
|
|
597
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
598
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
599
|
+
convertProperty(runtime, object, "width", props.width, variables);
|
|
600
|
+
convertProperty(runtime, object, "height", props.height, variables);
|
|
601
|
+
convertProperty(runtime, object, "rect", props.rect, variables);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
void draw(DrawingCtx *ctx) {
|
|
605
|
+
auto [rect, x, y, width, height] = props;
|
|
606
|
+
if (rect.has_value()) {
|
|
607
|
+
ctx->canvas->drawOval(rect.value(), ctx->getPaint());
|
|
608
|
+
} else {
|
|
609
|
+
if (!width.has_value() || !height.has_value()) {
|
|
610
|
+
throw std::runtime_error("Invalid properties received for Oval");
|
|
611
|
+
}
|
|
612
|
+
auto rct = SkRect::MakeXYWH(x, y, width.value(), height.value());
|
|
613
|
+
ctx->canvas->drawOval(rct, ctx->getPaint());
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
struct PatchCmdProps {
|
|
619
|
+
Patch patch;
|
|
620
|
+
std::optional<std::vector<SkColor>> colors;
|
|
621
|
+
std::optional<std::vector<SkPoint>> texture;
|
|
622
|
+
std::optional<SkBlendMode> blendMode;
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
class PatchCmd : public Command {
|
|
626
|
+
private:
|
|
627
|
+
PatchCmdProps props;
|
|
628
|
+
|
|
629
|
+
public:
|
|
630
|
+
PatchCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
631
|
+
Variables &variables)
|
|
632
|
+
: Command(CommandType::DrawPatch) {
|
|
633
|
+
convertProperty(runtime, object, "patch", props.patch, variables);
|
|
634
|
+
convertProperty(runtime, object, "colors", props.colors, variables);
|
|
635
|
+
convertProperty(runtime, object, "texture", props.texture, variables);
|
|
636
|
+
convertProperty(runtime, object, "blendMode", props.blendMode, variables);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
void draw(DrawingCtx *ctx) {
|
|
640
|
+
// Determine default blend mode based on presence of colors
|
|
641
|
+
SkBlendMode defaultBlendMode = props.colors.has_value()
|
|
642
|
+
? SkBlendMode::kDstOver
|
|
643
|
+
: SkBlendMode::kSrcOver;
|
|
644
|
+
|
|
645
|
+
ctx->canvas->drawPatch(
|
|
646
|
+
props.patch.data(),
|
|
647
|
+
props.colors.has_value() ? props.colors.value().data() : nullptr,
|
|
648
|
+
props.texture.has_value() ? props.texture.value().data() : nullptr,
|
|
649
|
+
props.blendMode.value_or(defaultBlendMode), ctx->getPaint());
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
struct VerticesCmdProps {
|
|
654
|
+
std::vector<SkPoint> vertices;
|
|
655
|
+
std::optional<std::vector<SkColor>> colors;
|
|
656
|
+
std::optional<std::vector<SkPoint>> textures;
|
|
657
|
+
SkVertices::VertexMode mode;
|
|
658
|
+
std::optional<SkBlendMode> blendMode;
|
|
659
|
+
std::optional<std::vector<uint16_t>> indices;
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
class VerticesCmd : public Command {
|
|
663
|
+
private:
|
|
664
|
+
VerticesCmdProps props;
|
|
665
|
+
|
|
666
|
+
public:
|
|
667
|
+
VerticesCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
668
|
+
Variables &variables)
|
|
669
|
+
: Command(CommandType::DrawVertices) {
|
|
670
|
+
convertProperty(runtime, object, "vertices", props.vertices, variables);
|
|
671
|
+
convertProperty(runtime, object, "colors", props.colors, variables);
|
|
672
|
+
convertProperty(runtime, object, "textures", props.textures, variables);
|
|
673
|
+
convertProperty(runtime, object, "mode", props.mode, variables);
|
|
674
|
+
convertProperty(runtime, object, "blendMode", props.blendMode, variables);
|
|
675
|
+
convertProperty(runtime, object, "indices", props.indices, variables);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
void draw(DrawingCtx *ctx) {
|
|
679
|
+
// Create vertices using MakeCopy
|
|
680
|
+
auto vertices = SkVertices::MakeCopy(
|
|
681
|
+
props.mode, static_cast<int>(props.vertices.size()),
|
|
682
|
+
props.vertices.data(),
|
|
683
|
+
props.textures.has_value() ? props.textures.value().data() : nullptr,
|
|
684
|
+
props.colors.has_value() ? props.colors.value().data() : nullptr,
|
|
685
|
+
props.indices.has_value()
|
|
686
|
+
? static_cast<int>(props.indices.value().size())
|
|
687
|
+
: 0,
|
|
688
|
+
props.indices.has_value() ? props.indices.value().data() : nullptr);
|
|
689
|
+
|
|
690
|
+
// Determine blend mode - use DstOver if colors are provided, SrcOver
|
|
691
|
+
// otherwise
|
|
692
|
+
const auto defaultBlendMode = props.colors.has_value()
|
|
693
|
+
? SkBlendMode::kDstOver
|
|
694
|
+
: SkBlendMode::kSrcOver;
|
|
695
|
+
|
|
696
|
+
// Draw the vertices with the determined blend mode
|
|
697
|
+
ctx->canvas->drawVertices(
|
|
698
|
+
vertices, props.blendMode.value_or(defaultBlendMode), ctx->getPaint());
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
struct DiffRectCmdProps {
|
|
703
|
+
SkRRect outer;
|
|
704
|
+
SkRRect inner;
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
class DiffRectCmd : public Command {
|
|
708
|
+
private:
|
|
709
|
+
DiffRectCmdProps props;
|
|
710
|
+
|
|
711
|
+
public:
|
|
712
|
+
DiffRectCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
713
|
+
Variables &variables)
|
|
714
|
+
: Command(CommandType::DrawDiffRect) {
|
|
715
|
+
convertProperty(runtime, object, "outer", props.outer, variables);
|
|
716
|
+
convertProperty(runtime, object, "inner", props.inner, variables);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
void draw(DrawingCtx *ctx) {
|
|
720
|
+
ctx->canvas->drawDRRect(props.outer, props.inner, ctx->getPaint());
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
struct TextBlobCmdProps {
|
|
725
|
+
sk_sp<SkTextBlob> blob;
|
|
726
|
+
float x;
|
|
727
|
+
float y;
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
class TextBlobCmd : public Command {
|
|
731
|
+
private:
|
|
732
|
+
TextBlobCmdProps props;
|
|
733
|
+
|
|
734
|
+
public:
|
|
735
|
+
TextBlobCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
736
|
+
Variables &variables)
|
|
737
|
+
: Command(CommandType::DrawTextBlob) {
|
|
738
|
+
convertProperty(runtime, object, "blob", props.blob, variables);
|
|
739
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
740
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
void draw(DrawingCtx *ctx) {
|
|
744
|
+
ctx->canvas->drawTextBlob(props.blob, props.x, props.y, ctx->getPaint());
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
struct GlyphData {
|
|
749
|
+
std::vector<SkGlyphID> glyphIds;
|
|
750
|
+
std::vector<SkPoint> positions;
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
struct GlyphsCmdProps {
|
|
754
|
+
std::optional<SkFont> font;
|
|
755
|
+
float x;
|
|
756
|
+
float y;
|
|
757
|
+
// GlyphData glyphs;
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
class GlyphsCmd : public Command {
|
|
761
|
+
private:
|
|
762
|
+
GlyphsCmdProps props;
|
|
763
|
+
|
|
764
|
+
public:
|
|
765
|
+
GlyphsCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
766
|
+
Variables &variables)
|
|
767
|
+
: Command(CommandType::DrawGlyphs) {
|
|
768
|
+
convertProperty(runtime, object, "font", props.font, variables);
|
|
769
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
770
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
771
|
+
// convertProperty(runtime, object, "glyphs", props.glyphs, variables);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
void draw(DrawingCtx *ctx) {
|
|
775
|
+
if (props.font.has_value()) {
|
|
776
|
+
// std::vector<uint16_t> glyphIds;
|
|
777
|
+
// std::vector<SkPoint> positions;
|
|
778
|
+
// for (const auto &[id, pos] : props.glyphs) {
|
|
779
|
+
// glyphIds.push_back(id);
|
|
780
|
+
// positions.push_back(pos);
|
|
781
|
+
// }
|
|
782
|
+
// ctx->canvas->drawGlyphs(
|
|
783
|
+
// static_cast<int>(props.glyphs.glyphIds.size()),
|
|
784
|
+
// props.glyphs.glyphIds.data(), props.glyphs.positions.data(),
|
|
785
|
+
// SkPoint::Make(props.x, props.y), props.font.value(),
|
|
786
|
+
// ctx->getPaint());
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
struct PictureCmdProps {
|
|
792
|
+
sk_sp<SkPicture> picture;
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
class PictureCmd : public Command {
|
|
796
|
+
private:
|
|
797
|
+
PictureCmdProps props;
|
|
798
|
+
|
|
799
|
+
public:
|
|
800
|
+
PictureCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
801
|
+
Variables &variables)
|
|
802
|
+
: Command(CommandType::DrawPicture) {
|
|
803
|
+
convertProperty(runtime, object, "picture", props.picture, variables);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
void draw(DrawingCtx *ctx) { ctx->canvas->drawPicture(props.picture); }
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
struct ImageSVGCmdProps {
|
|
810
|
+
sk_sp<SkSVGDOM> svg;
|
|
811
|
+
std::optional<float> x;
|
|
812
|
+
std::optional<float> y;
|
|
813
|
+
std::optional<float> width;
|
|
814
|
+
std::optional<float> height;
|
|
815
|
+
std::optional<SkRect> rect;
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
class ImageSVGCmd : public Command {
|
|
819
|
+
private:
|
|
820
|
+
ImageSVGCmdProps props;
|
|
821
|
+
|
|
822
|
+
public:
|
|
823
|
+
ImageSVGCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
824
|
+
Variables &variables)
|
|
825
|
+
: Command(CommandType::DrawImageSVG) {
|
|
826
|
+
// Convert SVG property - expect a host object of JsiSkSVG type
|
|
827
|
+
auto svgValue = object.getProperty(runtime, "svg");
|
|
828
|
+
if (svgValue.isObject() &&
|
|
829
|
+
svgValue.asObject(runtime).isHostObject(runtime)) {
|
|
830
|
+
auto ptr = std::dynamic_pointer_cast<JsiSkSVG>(
|
|
831
|
+
svgValue.asObject(runtime).asHostObject(runtime));
|
|
832
|
+
if (ptr != nullptr) {
|
|
833
|
+
props.svg = ptr->getObject();
|
|
834
|
+
} else {
|
|
835
|
+
throw std::runtime_error(
|
|
836
|
+
"Expected SkSvgDom object for the svg property.");
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Convert other properties
|
|
841
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
842
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
843
|
+
convertProperty(runtime, object, "width", props.width, variables);
|
|
844
|
+
convertProperty(runtime, object, "height", props.height, variables);
|
|
845
|
+
convertProperty(runtime, object, "rect", props.rect, variables);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
void draw(DrawingCtx *ctx) {
|
|
849
|
+
if (props.svg != nullptr) {
|
|
850
|
+
ctx->canvas->save();
|
|
851
|
+
|
|
852
|
+
if (props.rect.has_value()) {
|
|
853
|
+
// If rect is provided, use it for translation and container size
|
|
854
|
+
auto rect = props.rect.value();
|
|
855
|
+
ctx->canvas->translate(rect.x(), rect.y());
|
|
856
|
+
props.svg->setContainerSize(SkSize::Make(rect.width(), rect.height()));
|
|
857
|
+
} else {
|
|
858
|
+
// Otherwise use individual x, y, width, height properties
|
|
859
|
+
float x = props.x.value_or(-1);
|
|
860
|
+
float y = props.y.value_or(-1);
|
|
861
|
+
float width = props.width.value_or(-1);
|
|
862
|
+
float height = props.height.value_or(-1);
|
|
863
|
+
|
|
864
|
+
if (x != -1 && y != -1) {
|
|
865
|
+
ctx->canvas->translate(x, y);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (width != -1 && height != -1) {
|
|
869
|
+
props.svg->setContainerSize(SkSize::Make(width, height));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Render the SVG
|
|
874
|
+
props.svg->render(ctx->canvas);
|
|
875
|
+
ctx->canvas->restore();
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
struct ParagraphCmdProps {
|
|
881
|
+
para::Paragraph *paragraph;
|
|
882
|
+
float x;
|
|
883
|
+
float y;
|
|
884
|
+
float width;
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
class ParagraphCmd : public Command {
|
|
888
|
+
private:
|
|
889
|
+
ParagraphCmdProps props;
|
|
890
|
+
|
|
891
|
+
public:
|
|
892
|
+
ParagraphCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
893
|
+
Variables &variables)
|
|
894
|
+
: Command(CommandType::DrawParagraph) {
|
|
895
|
+
convertProperty(runtime, object, "paragraph", props.paragraph, variables);
|
|
896
|
+
convertProperty(runtime, object, "x", props.x, variables);
|
|
897
|
+
convertProperty(runtime, object, "y", props.y, variables);
|
|
898
|
+
convertProperty(runtime, object, "width", props.width, variables);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
void draw(DrawingCtx *ctx) {
|
|
902
|
+
if (props.paragraph) {
|
|
903
|
+
props.paragraph->layout(props.width);
|
|
904
|
+
props.paragraph->paint(ctx->canvas, props.x, props.y);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
|
|
909
|
+
struct AtlasCmdProps {
|
|
910
|
+
sk_sp<SkImage> image;
|
|
911
|
+
std::vector<SkRect> sprites;
|
|
912
|
+
std::vector<SkRSXform> transforms;
|
|
913
|
+
std::optional<std::vector<SkColor>> colors;
|
|
914
|
+
std::optional<SkBlendMode> blendMode;
|
|
915
|
+
std::optional<SkSamplingOptions> sampling;
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
class AtlasCmd : public Command {
|
|
919
|
+
private:
|
|
920
|
+
AtlasCmdProps props;
|
|
921
|
+
|
|
922
|
+
public:
|
|
923
|
+
AtlasCmd(jsi::Runtime &runtime, const jsi::Object &object,
|
|
924
|
+
Variables &variables)
|
|
925
|
+
: Command(CommandType::DrawAtlas) {
|
|
926
|
+
convertProperty(runtime, object, "image", props.image, variables);
|
|
927
|
+
convertProperty(runtime, object, "sprites", props.sprites, variables);
|
|
928
|
+
convertProperty(runtime, object, "transforms", props.transforms, variables);
|
|
929
|
+
convertProperty(runtime, object, "colors", props.colors, variables);
|
|
930
|
+
convertProperty(runtime, object, "blendMode", props.blendMode, variables);
|
|
931
|
+
convertProperty(runtime, object, "sampling", props.sampling, variables);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
void draw(DrawingCtx *ctx) {
|
|
935
|
+
if (props.image) {
|
|
936
|
+
auto colors =
|
|
937
|
+
props.colors.has_value() ? props.colors.value().data() : nullptr;
|
|
938
|
+
auto blendMode = props.blendMode.value_or(SkBlendMode::kDstOver);
|
|
939
|
+
auto sampling =
|
|
940
|
+
props.sampling.value_or(SkSamplingOptions(SkFilterMode::kLinear));
|
|
941
|
+
|
|
942
|
+
ctx->canvas->drawAtlas(props.image.get(), props.transforms.data(),
|
|
943
|
+
props.sprites.data(), colors,
|
|
944
|
+
props.transforms.size(), blendMode, sampling,
|
|
945
|
+
nullptr, &(ctx->getPaint()));
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
} // namespace RNSkia
|