lottie/optimization: Refactored to reduced number of allocation per task.
[platform/core/uifw/lottie-player.git] / src / vector / vraster.cpp
1 #include"vraster.h"
2 #include"v_ft_raster.h"
3 #include"v_ft_stroker.h"
4 #include"vpath.h"
5 #include"vmatrix.h"
6 #include<cstring>
7 #include"vdebug.h"
8 #include"vtaskqueue.h"
9 #include<thread>
10
11 V_BEGIN_NAMESPACE
12
13 struct FTOutline
14 {
15 public:
16     FTOutline():mMemory(nullptr){}
17     ~FTOutline(){delete[] mMemory;}
18     void releaseMemory() {
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     int                      ftWidth;
42     int                      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)
56         return;
57
58     // release old memory
59     releaseMemory();
60
61     //update book keeping
62     mPointSize = points;
63     mSegmentSize = segments;
64
65     int point_size = (points + segments);
66     int contour_size = ((sizeof(short) * segments) / sizeof(SW_FT_Vector)) + 1;
67     int tag_size = ((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,
113                         float width, 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 diameter
118     width = width/2.0;
119     // convert to freetype co-ordinate
120     ftWidth = int(width * 64);
121     ftMeterLimit = int(meterLimit * 64);
122
123     // map to freetype capstyle
124     switch (cap)
125       {
126          case CapStyle::Square:
127            ftCap = SW_FT_STROKER_LINECAP_SQUARE;
128            break;
129          case CapStyle::Round:
130            ftCap = SW_FT_STROKER_LINECAP_ROUND;
131            break;
132          default:
133            ftCap = SW_FT_STROKER_LINECAP_BUTT;
134            break;
135       }
136     switch (join)
137       {
138          case JoinStyle::Bevel:
139            ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
140            break;
141          case JoinStyle::Round:
142            ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
143            break;
144          default:
145            ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
146            break;
147       }
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, const VPointF ep)
176 {
177     ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
178     ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
179     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
180     ft.n_points++;
181
182     ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
183     ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
184     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
185     ft.n_points++;
186
187     ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
188     ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
189     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
190     ft.n_points++;
191     closed = false;
192 }
193 void FTOutline::close()
194 {
195     int index;
196     if (ft.n_contours) {
197         index = ft.contours[ft.n_contours - 1] + 1;
198     } else {
199         index = 0;
200     }
201
202     // make sure atleast 1 point exists in the segment.
203     if (ft.n_points == index) {
204         closed = false;
205         return;
206     }
207
208     ft.points[ft.n_points].x = ft.points[index].x;
209     ft.points[ft.n_points].y = ft.points[index].y;
210     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
211     ft.n_points++;
212     closed = true;
213 }
214
215 void FTOutline::end()
216 {
217     if (ft.n_points) {
218         ft.contours[ft.n_contours] = ft.n_points - 1;
219         ft.n_contours++;
220     }
221 }
222
223 struct SpanInfo
224 {
225   VRle::Span *spans;
226   int          size;
227 };
228
229 static void
230 rleGenerationCb( int count, const SW_FT_Span*  spans,void *user)
231 {
232    VRle *rle = (VRle *) user;
233    VRle::Span *rleSpan = (VRle::Span *)spans;
234    rle->addSpan(rleSpan, count);
235 }
236
237 struct RleTask
238 {
239     RleTask() {
240         receiver = sender.get_future();
241     }
242     std::promise<VRle>       sender;
243     std::future<VRle>        receiver;
244     bool                     stroke;
245     VPath                    path;
246     FillRule                 fillRule;
247     CapStyle                 cap;
248     JoinStyle                join;
249     float                    width;
250     float                    meterLimit;
251     VRle operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
252 };
253
254 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
255 {
256     if (stroke) { //Stroke Task
257         outRef.convert(path);
258         outRef.convert(cap, join, width, meterLimit);
259
260         uint points,contors;
261
262         SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin, outRef.ftMeterLimit);
263         SW_FT_Stroker_ParseOutline(stroker, &outRef.ft, !outRef.ftClosed);
264         SW_FT_Stroker_GetCounts(stroker,&points, &contors);
265
266         FTOutline strokeOutline;
267         strokeOutline.grow(points, contors);
268
269         SW_FT_Stroker_Export(stroker, &strokeOutline.ft);
270
271         VRle rle;
272         SW_FT_Raster_Params params;
273
274         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ;
275         params.gray_spans = &rleGenerationCb;
276         params.user = &rle;
277         params.source = &strokeOutline;
278
279         sw_ft_grays_raster.raster_render(nullptr, &params);
280
281         return rle;
282     } else { //Fill Task
283         outRef.convert(path);
284         int fillRuleFlag = SW_FT_OUTLINE_NONE;
285         switch (fillRule) {
286         case FillRule::EvenOdd:
287             fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
288             break;
289         default:
290             fillRuleFlag = SW_FT_OUTLINE_NONE;
291             break;
292         }
293         outRef.ft.flags =  fillRuleFlag;
294         VRle rle;
295         SW_FT_Raster_Params params;
296
297         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ;
298         params.gray_spans = &rleGenerationCb;
299         params.user = &rle;
300         params.source = &outRef.ft;
301
302         sw_ft_grays_raster.raster_render(nullptr, &params);
303         return rle;
304     }
305 }
306
307 class RleTaskScheduler {
308     const unsigned _count{std::thread::hardware_concurrency()};
309     std::vector<std::thread> _threads;
310     std::vector<TaskQueue<RleTask>> _q{_count};
311     std::atomic<unsigned> _index{0};
312
313     void run(unsigned i) {
314         /*
315          * initalize  per thread objects.
316          */
317         FTOutline  outlineRef;
318         SW_FT_Stroker stroker;
319         SW_FT_Stroker_New(&stroker);
320
321         // Task Loop
322         while (true) {
323             RleTask *task = nullptr;
324
325             for (unsigned n = 0; n != _count * 32; ++n) {
326                 if (_q[(i + n) % _count].try_pop(task)) break;
327             }
328             if (!task && !_q[i].pop(task)) break;
329
330             task->sender.set_value((*task)(outlineRef, stroker));
331             delete task;
332         }
333
334         //cleanup
335         SW_FT_Stroker_Done(stroker);
336     }
337
338 public:
339     RleTaskScheduler() {
340         for (unsigned n = 0; n != _count; ++n) {
341             _threads.emplace_back([&, n] { run(n); });
342         }
343     }
344
345     ~RleTaskScheduler() {
346         for (auto& e : _q)
347             e.done();
348
349         for (auto& e : _threads)
350             e.join();
351     }
352
353     std::future<VRle> async(RleTask *task) {
354         auto receiver = std::move(task->receiver);
355         auto i = _index++;
356
357         for (unsigned n = 0; n != _count; ++n) {
358             if (_q[(i + n) % _count].try_push(task)) return std::move(receiver);
359         }
360
361         _q[i % _count].push(task);
362
363         return std::move(receiver);
364     }
365
366     std::future<VRle> strokeRle(const VPath &path,
367                                 CapStyle cap,
368                                 JoinStyle join,
369                                 float width,
370                                 float meterLimit) {
371         RleTask *task = new RleTask();
372         task->stroke = true;
373         task->path = path;
374         task->cap = cap;
375         task->join = join;
376         task->width = width;
377         task->meterLimit = meterLimit;
378         return async(task);
379     }
380
381     std::future<VRle> fillRle(const VPath &path, FillRule fillRule) {
382         RleTask *task = new RleTask();
383         task->path = path;
384         task->fillRule = fillRule;
385         task->stroke = false;
386         return async(task);
387     }
388 };
389
390 static RleTaskScheduler raster_scheduler;
391
392 VRaster::VRaster()
393 {
394 }
395
396 VRaster::~VRaster()
397 {
398 }
399
400 std::future<VRle>
401 VRaster::generateFillInfo(const VPath &path, FillRule fillRule)
402 {
403     if (path.isEmpty()) {
404         std::promise<VRle> promise;
405         promise.set_value(VRle());
406         return promise.get_future();
407     }
408     return std::move(raster_scheduler.fillRle(path, fillRule));
409 }
410
411 std::future<VRle>
412 VRaster::generateStrokeInfo(const VPath &path, CapStyle cap, JoinStyle join,
413                             float width, float meterLimit)
414 {
415     if (path.isEmpty()) {
416         std::promise<VRle> promise;
417         promise.set_value(VRle());
418         return promise.get_future();
419     }
420     return std::move(raster_scheduler.strokeRle(path, cap, join, width, meterLimit));
421 }
422
423 V_END_NAMESPACE