lottie/render: fixed std::move copy elision warning
[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     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)
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     //IMP: stroker takes radius in 26.6 co-ordinate
121     ftWidth = SW_FT_Fixed(width * (1<<6));
122     //IMP: stroker takes meterlimit in 16.16 co-ordinate
123     ftMeterLimit = SW_FT_Fixed(meterLimit * (1<<16));
124
125     // map to freetype capstyle
126     switch (cap)
127       {
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       {
140          case JoinStyle::Bevel:
141            ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
142            break;
143          case JoinStyle::Round:
144            ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
145            break;
146          default:
147            ftJoin = SW_FT_STROKER_LINEJOIN_MITER;
148            break;
149       }
150 }
151
152
153 #define TO_FT_COORD(x) ((x) * 64) // to freetype 26.6 coordinate.
154
155 void FTOutline::moveTo(const VPointF &pt)
156 {
157     ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
158     ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
159     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
160     if (ft.n_points) {
161         ft.contours[ft.n_contours] = ft.n_points - 1;
162         ft.n_contours++;
163     }
164     ft.n_points++;
165     closed = false;
166 }
167
168 void FTOutline::lineTo(const VPointF &pt)
169 {
170     ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
171     ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
172     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
173     ft.n_points++;
174     closed = false;
175 }
176
177 void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2, const VPointF ep)
178 {
179     ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
180     ft.points[ft.n_points].y = TO_FT_COORD(cp1.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(cp2.x());
185     ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
186     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
187     ft.n_points++;
188
189     ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
190     ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
191     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
192     ft.n_points++;
193     closed = false;
194 }
195 void FTOutline::close()
196 {
197     int index;
198     if (ft.n_contours) {
199         index = ft.contours[ft.n_contours - 1] + 1;
200     } else {
201         index = 0;
202     }
203
204     // make sure atleast 1 point exists in the segment.
205     if (ft.n_points == index) {
206         closed = false;
207         return;
208     }
209
210     ft.points[ft.n_points].x = ft.points[index].x;
211     ft.points[ft.n_points].y = ft.points[index].y;
212     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
213     ft.n_points++;
214     closed = true;
215 }
216
217 void FTOutline::end()
218 {
219     if (ft.n_points) {
220         ft.contours[ft.n_contours] = ft.n_points - 1;
221         ft.n_contours++;
222     }
223 }
224
225 struct SpanInfo
226 {
227   VRle::Span *spans;
228   int          size;
229 };
230
231 static void
232 rleGenerationCb( int count, const SW_FT_Span*  spans,void *user)
233 {
234    VRle *rle = (VRle *) user;
235    VRle::Span *rleSpan = (VRle::Span *)spans;
236    rle->addSpan(rleSpan, count);
237 }
238
239 struct RleTask
240 {
241     RleTask() {
242         receiver = sender.get_future();
243     }
244     std::promise<VRle>       sender;
245     std::future<VRle>        receiver;
246     bool                     stroke;
247     VPath                    path;
248     FillRule                 fillRule;
249     CapStyle                 cap;
250     JoinStyle                join;
251     float                    width;
252     float                    meterLimit;
253     VRle operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
254 };
255
256 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
257 {
258     if (stroke) { //Stroke Task
259         outRef.convert(path);
260         outRef.convert(cap, join, width, meterLimit);
261
262         uint points,contors;
263
264         SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin, outRef.ftMeterLimit);
265         SW_FT_Stroker_ParseOutline(stroker, &outRef.ft, !outRef.ftClosed);
266         SW_FT_Stroker_GetCounts(stroker,&points, &contors);
267
268         FTOutline strokeOutline;
269         strokeOutline.grow(points, contors);
270
271         SW_FT_Stroker_Export(stroker, &strokeOutline.ft);
272
273         VRle rle;
274         SW_FT_Raster_Params params;
275
276         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ;
277         params.gray_spans = &rleGenerationCb;
278         params.user = &rle;
279         params.source = &strokeOutline;
280
281         sw_ft_grays_raster.raster_render(nullptr, &params);
282
283         return rle;
284     } else { //Fill Task
285         outRef.convert(path);
286         int fillRuleFlag = SW_FT_OUTLINE_NONE;
287         switch (fillRule) {
288         case FillRule::EvenOdd:
289             fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
290             break;
291         default:
292             fillRuleFlag = SW_FT_OUTLINE_NONE;
293             break;
294         }
295         outRef.ft.flags =  fillRuleFlag;
296         VRle rle;
297         SW_FT_Raster_Params params;
298
299         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ;
300         params.gray_spans = &rleGenerationCb;
301         params.user = &rle;
302         params.source = &outRef.ft;
303
304         sw_ft_grays_raster.raster_render(nullptr, &params);
305         return rle;
306     }
307 }
308
309 class RleTaskScheduler {
310     const unsigned _count{std::thread::hardware_concurrency()};
311     std::vector<std::thread> _threads;
312     std::vector<TaskQueue<RleTask>> _q{_count};
313     std::atomic<unsigned> _index{0};
314
315     void run(unsigned i) {
316         /*
317          * initalize  per thread objects.
318          */
319         FTOutline  outlineRef;
320         SW_FT_Stroker stroker;
321         SW_FT_Stroker_New(&stroker);
322
323         // Task Loop
324         while (true) {
325             RleTask *task = nullptr;
326
327             for (unsigned n = 0; n != _count * 32; ++n) {
328                 if (_q[(i + n) % _count].try_pop(task)) break;
329             }
330             if (!task && !_q[i].pop(task)) break;
331
332             task->sender.set_value((*task)(outlineRef, stroker));
333             delete task;
334         }
335
336         //cleanup
337         SW_FT_Stroker_Done(stroker);
338     }
339
340 public:
341     RleTaskScheduler() {
342         for (unsigned n = 0; n != _count; ++n) {
343             _threads.emplace_back([&, n] { run(n); });
344         }
345     }
346
347     ~RleTaskScheduler() {
348         for (auto& e : _q)
349             e.done();
350
351         for (auto& e : _threads)
352             e.join();
353     }
354
355     std::future<VRle> async(RleTask *task) {
356         auto receiver = std::move(task->receiver);
357         auto i = _index++;
358
359         for (unsigned n = 0; n != _count; ++n) {
360             if (_q[(i + n) % _count].try_push(task)) return receiver;
361         }
362
363         _q[i % _count].push(task);
364
365         return receiver;
366     }
367
368     std::future<VRle> strokeRle(const VPath &path,
369                                 CapStyle cap,
370                                 JoinStyle join,
371                                 float width,
372                                 float meterLimit) {
373         RleTask *task = new RleTask();
374         task->stroke = true;
375         task->path = path;
376         task->cap = cap;
377         task->join = join;
378         task->width = width;
379         task->meterLimit = meterLimit;
380         return async(task);
381     }
382
383     std::future<VRle> fillRle(const VPath &path, FillRule fillRule) {
384         RleTask *task = new RleTask();
385         task->path = path;
386         task->fillRule = fillRule;
387         task->stroke = false;
388         return async(task);
389     }
390 };
391
392 static RleTaskScheduler raster_scheduler;
393
394 VRaster::VRaster()
395 {
396 }
397
398 VRaster::~VRaster()
399 {
400 }
401
402 std::future<VRle>
403 VRaster::generateFillInfo(const VPath &path, FillRule fillRule)
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.fillRle(path, fillRule);
411 }
412
413 std::future<VRle>
414 VRaster::generateStrokeInfo(const VPath &path, CapStyle cap, JoinStyle join,
415                             float width, float meterLimit)
416 {
417     if (path.isEmpty()) {
418         std::promise<VRle> promise;
419         promise.set_value(VRle());
420         return promise.get_future();
421     }
422     return raster_scheduler.strokeRle(path, cap, join, width, meterLimit);
423 }
424
425 V_END_NAMESPACE