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