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