lottie/vector: move VPath to raster/drawable methods
[platform/core/uifw/lottie-player.git] / src / vector / vraster.cpp
1 #include "vraster.h"
2 #include <cstring>
3 #include <thread>
4 #include "v_ft_raster.h"
5 #include "v_ft_stroker.h"
6 #include "vdebug.h"
7 #include "vmatrix.h"
8 #include "vpath.h"
9 #include "vtaskqueue.h"
10
11 V_BEGIN_NAMESPACE
12
13 struct FTOutline {
14 public:
15     ~FTOutline()
16     {
17         if (mPointSize) delete[] ft.points;
18         if (mTagSize) delete[] ft.tags;
19         if (mSegmentSize) delete[] ft.contours;
20     }
21     void reset();
22     void grow(int, int);
23     void convert(const VPath &path);
24     void convert(CapStyle, JoinStyle, float, float);
25     void moveTo(const VPointF &pt);
26     void lineTo(const VPointF &pt);
27     void cubicTo(const VPointF &ctr1, const VPointF &ctr2, const VPointF end);
28     void close();
29     void end();
30     void transform(const VMatrix &m);
31     SW_FT_Outline          ft;
32     int                    mPointSize{0};
33     int                    mSegmentSize{0};
34     int                    mTagSize{0};
35     bool                   closed{false};
36     SW_FT_Stroker_LineCap  ftCap;
37     SW_FT_Stroker_LineJoin ftJoin;
38     SW_FT_Fixed            ftWidth;
39     SW_FT_Fixed            ftMeterLimit;
40     SW_FT_Bool             ftClosed;
41 };
42
43 void FTOutline::reset()
44 {
45     ft.n_points = ft.n_contours = 0;
46     ft.flags = 0x0;
47 }
48
49 void FTOutline::grow(int points, int segments)
50 {
51     reset();
52
53     int point_size = (points + segments);
54     int segment_size = (sizeof(short) * segments);
55     int tag_size = (sizeof(char) * (points + segments));
56
57     if (point_size > mPointSize) {
58         if (mPointSize) delete [] ft.points;
59         ft.points = new SW_FT_Vector[point_size];
60         mPointSize = point_size;
61     }
62
63     if (segment_size > mSegmentSize) {
64         if (mSegmentSize) delete [] ft.contours;
65         ft.contours = new short[segment_size];
66         mSegmentSize = segment_size;
67     }
68
69     if (tag_size > mTagSize) {
70         if (mTagSize) delete [] ft.tags;
71         ft.tags = new char[tag_size];
72         mTagSize = tag_size;
73     }
74 }
75
76 void FTOutline::convert(const VPath &path)
77 {
78     const std::vector<VPath::Element> &elements = path.elements();
79     const std::vector<VPointF> &       points = path.points();
80
81     grow(points.size(), path.segments());
82
83     int index = 0;
84     for (auto element : elements) {
85         switch (element) {
86         case VPath::Element::MoveTo:
87             moveTo(points[index]);
88             index++;
89             break;
90         case VPath::Element::LineTo:
91             lineTo(points[index]);
92             index++;
93             break;
94         case VPath::Element::CubicTo:
95             cubicTo(points[index], points[index + 1], points[index + 2]);
96             index = index + 3;
97             break;
98         case VPath::Element::Close:
99             close();
100             break;
101         default:
102             break;
103         }
104     }
105     end();
106 }
107
108 void FTOutline::convert(CapStyle cap, JoinStyle join, float width,
109                         float meterLimit)
110 {
111     ftClosed = (SW_FT_Bool)closed;
112
113     // map strokeWidth to freetype. It uses as the radius of the pen not the
114     // diameter
115     width = width / 2.0;
116     // convert to freetype co-ordinate
117     // IMP: stroker takes radius in 26.6 co-ordinate
118     ftWidth = SW_FT_Fixed(width * (1 << 6));
119     // IMP: stroker takes meterlimit in 16.16 co-ordinate
120     ftMeterLimit = SW_FT_Fixed(meterLimit * (1 << 16));
121
122     // map to freetype capstyle
123     switch (cap) {
124     case CapStyle::Square:
125         ftCap = SW_FT_STROKER_LINECAP_SQUARE;
126         break;
127     case CapStyle::Round:
128         ftCap = SW_FT_STROKER_LINECAP_ROUND;
129         break;
130     default:
131         ftCap = SW_FT_STROKER_LINECAP_BUTT;
132         break;
133     }
134     switch (join) {
135     case JoinStyle::Bevel:
136         ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
137         break;
138     case JoinStyle::Round:
139         ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
140         break;
141     default:
142         ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
143         break;
144     }
145 }
146
147 #define TO_FT_COORD(x) ((x)*64)  // to freetype 26.6 coordinate.
148
149 void FTOutline::moveTo(const VPointF &pt)
150 {
151     ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
152     ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
153     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
154     if (ft.n_points) {
155         ft.contours[ft.n_contours] = ft.n_points - 1;
156         ft.n_contours++;
157     }
158     ft.n_points++;
159     closed = false;
160 }
161
162 void FTOutline::lineTo(const VPointF &pt)
163 {
164     ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
165     ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
166     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
167     ft.n_points++;
168     closed = false;
169 }
170
171 void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2,
172                         const VPointF ep)
173 {
174     ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
175     ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
176     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
177     ft.n_points++;
178
179     ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
180     ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
181     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
182     ft.n_points++;
183
184     ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
185     ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
186     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
187     ft.n_points++;
188     closed = false;
189 }
190 void FTOutline::close()
191 {
192     int index;
193     if (ft.n_contours) {
194         index = ft.contours[ft.n_contours - 1] + 1;
195     } else {
196         index = 0;
197     }
198
199     // make sure atleast 1 point exists in the segment.
200     if (ft.n_points == index) {
201         closed = false;
202         return;
203     }
204
205     ft.points[ft.n_points].x = ft.points[index].x;
206     ft.points[ft.n_points].y = ft.points[index].y;
207     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
208     ft.n_points++;
209     closed = true;
210 }
211
212 void FTOutline::end()
213 {
214     if (ft.n_points) {
215         ft.contours[ft.n_contours] = ft.n_points - 1;
216         ft.n_contours++;
217     }
218 }
219
220 struct SpanInfo {
221     VRle::Span *spans;
222     int         size;
223 };
224
225 static void rleGenerationCb(int count, const SW_FT_Span *spans, void *user)
226 {
227     VRle *      rle = (VRle *)user;
228     VRle::Span *rleSpan = (VRle::Span *)spans;
229     rle->addSpan(rleSpan, count);
230 }
231
232 struct RleTask {
233     RleTask() { receiver = sender.get_future(); }
234     std::promise<VRle> sender;
235     std::future<VRle>  receiver;
236     bool               stroke;
237     VPath              path;
238     VRle               rle;
239     FillRule           fillRule;
240     CapStyle           cap;
241     JoinStyle          join;
242     float              width;
243     float              meterLimit;
244     VRle               operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
245 };
246
247 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
248 {
249     rle.reset();
250     if (stroke) {  // Stroke Task
251         outRef.convert(path);
252         outRef.convert(cap, join, width, meterLimit);
253
254         uint points, contors;
255
256         SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
257                           outRef.ftMeterLimit);
258         SW_FT_Stroker_ParseOutline(stroker, &outRef.ft, !outRef.ftClosed);
259         SW_FT_Stroker_GetCounts(stroker, &points, &contors);
260
261         outRef.grow(points, contors);
262
263         SW_FT_Stroker_Export(stroker, &outRef.ft);
264
265         SW_FT_Raster_Params params;
266
267         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
268         params.gray_spans = &rleGenerationCb;
269         params.user = &rle;
270         params.source = &outRef;
271
272         sw_ft_grays_raster.raster_render(nullptr, &params);
273
274     } else {  // Fill Task
275         outRef.convert(path);
276         int fillRuleFlag = SW_FT_OUTLINE_NONE;
277         switch (fillRule) {
278         case FillRule::EvenOdd:
279             fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
280             break;
281         default:
282             fillRuleFlag = SW_FT_OUTLINE_NONE;
283             break;
284         }
285         outRef.ft.flags = fillRuleFlag;
286         SW_FT_Raster_Params params;
287
288         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
289         params.gray_spans = &rleGenerationCb;
290         params.user = &rle;
291         params.source = &outRef.ft;
292
293         sw_ft_grays_raster.raster_render(nullptr, &params);
294     }
295     return std::move(rle);
296 }
297
298 class RleTaskScheduler {
299     const unsigned                  _count{std::thread::hardware_concurrency()};
300     std::vector<std::thread>        _threads;
301     std::vector<TaskQueue<RleTask>> _q{_count};
302     std::atomic<unsigned>           _index{0};
303
304     void run(unsigned i)
305     {
306         /*
307          * initalize  per thread objects.
308          */
309         FTOutline     outlineRef;
310         SW_FT_Stroker stroker;
311         SW_FT_Stroker_New(&stroker);
312
313         // Task Loop
314         while (true) {
315             RleTask *task = nullptr;
316
317             for (unsigned n = 0; n != _count * 32; ++n) {
318                 if (_q[(i + n) % _count].try_pop(task)) break;
319             }
320             if (!task && !_q[i].pop(task)) break;
321
322             task->sender.set_value((*task)(outlineRef, stroker));
323             delete task;
324         }
325
326         // cleanup
327         SW_FT_Stroker_Done(stroker);
328     }
329
330 public:
331     RleTaskScheduler()
332     {
333         for (unsigned n = 0; n != _count; ++n) {
334             _threads.emplace_back([&, n] { run(n); });
335         }
336     }
337
338     ~RleTaskScheduler()
339     {
340         for (auto &e : _q) e.done();
341
342         for (auto &e : _threads) e.join();
343     }
344
345     std::future<VRle> async(RleTask *task)
346     {
347         auto receiver = std::move(task->receiver);
348         auto i = _index++;
349
350         for (unsigned n = 0; n != _count; ++n) {
351             if (_q[(i + n) % _count].try_push(task)) return receiver;
352         }
353
354         _q[i % _count].push(task);
355
356         return receiver;
357     }
358
359     std::future<VRle> strokeRle(const VPath &&path, VRle &&rle, CapStyle cap, JoinStyle join,
360                                 float width, float meterLimit)
361     {
362         RleTask *task = new RleTask();
363         task->stroke = true;
364         task->path = std::move(path);
365         task->rle = std::move(rle);
366         task->cap = cap;
367         task->join = join;
368         task->width = width;
369         task->meterLimit = meterLimit;
370         return async(task);
371     }
372
373     std::future<VRle> fillRle(const VPath &&path, VRle &&rle, FillRule fillRule)
374     {
375         RleTask *task = new RleTask();
376         task->path = std::move(path);
377         task->rle = std::move(rle);
378         task->fillRule = fillRule;
379         task->stroke = false;
380         return async(task);
381     }
382 };
383
384 static RleTaskScheduler raster_scheduler;
385
386 VRaster::VRaster() {}
387
388 VRaster::~VRaster() {}
389
390 std::future<VRle> VRaster::generateFillInfo(const VPath &&path, VRle &&rle,
391                                             FillRule     fillRule)
392 {
393     if (path.isEmpty()) {
394         std::promise<VRle> promise;
395         promise.set_value(VRle());
396         return promise.get_future();
397     }
398     return raster_scheduler.fillRle(std::move(path), std::move(rle), fillRule);
399 }
400
401 std::future<VRle> VRaster::generateStrokeInfo(const VPath &&path, VRle &&rle, CapStyle cap,
402                                               JoinStyle join, float width,
403                                               float meterLimit)
404 {
405     if (path.isEmpty()) {
406         std::promise<VRle> promise;
407         promise.set_value(VRle());
408         return promise.get_future();
409     }
410     return raster_scheduler.strokeRle(std::move(path), std::move(rle), cap, join, width, meterLimit);
411 }
412
413 V_END_NAMESPACE