lottie/vector: extend freetype stroker to provide the contour open flag for each...
[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) {
21             delete[] ft.contours;
22             delete[] ft.contours_flag;
23         }
24     }
25     void reset();
26     void grow(int, int);
27     void convert(const VPath &path);
28     void convert(CapStyle, JoinStyle, float, float);
29     void moveTo(const VPointF &pt);
30     void lineTo(const VPointF &pt);
31     void cubicTo(const VPointF &ctr1, const VPointF &ctr2, const VPointF end);
32     void close();
33     void end();
34     void transform(const VMatrix &m);
35     SW_FT_Outline          ft;
36     int                    mPointSize{0};
37     int                    mSegmentSize{0};
38     int                    mTagSize{0};
39     bool                   closed{false};
40     SW_FT_Stroker_LineCap  ftCap;
41     SW_FT_Stroker_LineJoin ftJoin;
42     SW_FT_Fixed            ftWidth;
43     SW_FT_Fixed            ftMeterLimit;
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
56     int point_size = (points + segments);
57     int segment_size = (sizeof(short) * segments);
58     int tag_size = (sizeof(char) * (points + segments));
59
60     if (point_size > mPointSize) {
61         if (mPointSize) delete [] ft.points;
62         ft.points = new SW_FT_Vector[point_size];
63         mPointSize = point_size;
64     }
65
66     if (segment_size > mSegmentSize) {
67         if (mSegmentSize) {
68             delete [] ft.contours;
69             delete [] ft.contours_flag;
70         }
71         ft.contours = new short[segment_size];
72         ft.contours_flag = new char[segment_size];
73         mSegmentSize = segment_size;
74     }
75
76     if (tag_size > mTagSize) {
77         if (mTagSize) delete [] ft.tags;
78         ft.tags = new char[tag_size];
79         mTagSize = tag_size;
80     }
81 }
82
83 void FTOutline::convert(const VPath &path)
84 {
85     const std::vector<VPath::Element> &elements = path.elements();
86     const std::vector<VPointF> &       points = path.points();
87
88     grow(points.size(), path.segments());
89
90     int index = 0;
91     for (auto element : elements) {
92         switch (element) {
93         case VPath::Element::MoveTo:
94             moveTo(points[index]);
95             index++;
96             break;
97         case VPath::Element::LineTo:
98             lineTo(points[index]);
99             index++;
100             break;
101         case VPath::Element::CubicTo:
102             cubicTo(points[index], points[index + 1], points[index + 2]);
103             index = index + 3;
104             break;
105         case VPath::Element::Close:
106             close();
107             break;
108         default:
109             break;
110         }
111     }
112     end();
113 }
114
115 void FTOutline::convert(CapStyle cap, JoinStyle join, float width,
116                         float meterLimit)
117 {
118     // map strokeWidth to freetype. It uses as the radius of the pen not the
119     // diameter
120     width = width / 2.0;
121     // convert to freetype co-ordinate
122     // IMP: stroker takes radius in 26.6 co-ordinate
123     ftWidth = SW_FT_Fixed(width * (1 << 6));
124     // IMP: stroker takes meterlimit in 16.16 co-ordinate
125     ftMeterLimit = SW_FT_Fixed(meterLimit * (1 << 16));
126
127     // map to freetype capstyle
128     switch (cap) {
129     case CapStyle::Square:
130         ftCap = SW_FT_STROKER_LINECAP_SQUARE;
131         break;
132     case CapStyle::Round:
133         ftCap = SW_FT_STROKER_LINECAP_ROUND;
134         break;
135     default:
136         ftCap = SW_FT_STROKER_LINECAP_BUTT;
137         break;
138     }
139     switch (join) {
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 #define TO_FT_COORD(x) ((x)*64)  // to freetype 26.6 coordinate.
153
154 void FTOutline::moveTo(const VPointF &pt)
155 {
156     ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
157     ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
158     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
159     if (ft.n_points) {
160         ft.contours[ft.n_contours] = ft.n_points - 1;
161         ft.n_contours++;
162     }
163     // mark the current contour as open
164     // will be updated if ther is a close tag at the end.
165     ft.contours_flag[ft.n_contours] = 1;
166
167     ft.n_points++;
168 }
169
170 void FTOutline::lineTo(const VPointF &pt)
171 {
172     ft.points[ft.n_points].x = TO_FT_COORD(pt.x());
173     ft.points[ft.n_points].y = TO_FT_COORD(pt.y());
174     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
175     ft.n_points++;
176 }
177
178 void FTOutline::cubicTo(const VPointF &cp1, const VPointF &cp2,
179                         const VPointF ep)
180 {
181     ft.points[ft.n_points].x = TO_FT_COORD(cp1.x());
182     ft.points[ft.n_points].y = TO_FT_COORD(cp1.y());
183     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
184     ft.n_points++;
185
186     ft.points[ft.n_points].x = TO_FT_COORD(cp2.x());
187     ft.points[ft.n_points].y = TO_FT_COORD(cp2.y());
188     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_CUBIC;
189     ft.n_points++;
190
191     ft.points[ft.n_points].x = TO_FT_COORD(ep.x());
192     ft.points[ft.n_points].y = TO_FT_COORD(ep.y());
193     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
194     ft.n_points++;
195 }
196 void FTOutline::close()
197 {
198     // mark the contour as a close path.
199     ft.contours_flag[ft.n_contours] = 0;
200
201     int index;
202     if (ft.n_contours) {
203         index = ft.contours[ft.n_contours - 1] + 1;
204     } else {
205         index = 0;
206     }
207
208     // make sure atleast 1 point exists in the segment.
209     if (ft.n_points == index) {
210         closed = false;
211         return;
212     }
213
214     ft.points[ft.n_points].x = ft.points[index].x;
215     ft.points[ft.n_points].y = ft.points[index].y;
216     ft.tags[ft.n_points] = SW_FT_CURVE_TAG_ON;
217     ft.n_points++;
218 }
219
220 void FTOutline::end()
221 {
222     if (ft.n_points) {
223         ft.contours[ft.n_contours] = ft.n_points - 1;
224         ft.n_contours++;
225     }
226 }
227
228 struct SpanInfo {
229     VRle::Span *spans;
230     int         size;
231 };
232
233 static void rleGenerationCb(int count, const SW_FT_Span *spans, void *user)
234 {
235     VRle *      rle = (VRle *)user;
236     VRle::Span *rleSpan = (VRle::Span *)spans;
237     rle->addSpan(rleSpan, count);
238 }
239
240 struct RleTask {
241     RleTask() { receiver = sender.get_future(); }
242     std::promise<VRle> sender;
243     std::future<VRle>  receiver;
244     bool               stroke;
245     VPath              path;
246     VRle               rle;
247     FillRule           fillRule;
248     CapStyle           cap;
249     JoinStyle          join;
250     float              width;
251     float              meterLimit;
252     VRle               operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
253 };
254
255 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
256 {
257     rle.reset();
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,
265                           outRef.ftMeterLimit);
266         SW_FT_Stroker_ParseOutline(stroker, &outRef.ft);
267         SW_FT_Stroker_GetCounts(stroker, &points, &contors);
268
269         outRef.grow(points, contors);
270
271         SW_FT_Stroker_Export(stroker, &outRef.ft);
272
273         SW_FT_Raster_Params params;
274
275         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
276         params.gray_spans = &rleGenerationCb;
277         params.user = &rle;
278         params.source = &outRef;
279
280         sw_ft_grays_raster.raster_render(nullptr, &params);
281
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         SW_FT_Raster_Params params;
295
296         params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
297         params.gray_spans = &rleGenerationCb;
298         params.user = &rle;
299         params.source = &outRef.ft;
300
301         sw_ft_grays_raster.raster_render(nullptr, &params);
302     }
303     return std::move(rle);
304 }
305
306 class RleTaskScheduler {
307     const unsigned                  _count{std::thread::hardware_concurrency()};
308     std::vector<std::thread>        _threads;
309     std::vector<TaskQueue<RleTask>> _q{_count};
310     std::atomic<unsigned>           _index{0};
311
312     void run(unsigned i)
313     {
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     {
341         for (unsigned n = 0; n != _count; ++n) {
342             _threads.emplace_back([&, n] { run(n); });
343         }
344     }
345
346     ~RleTaskScheduler()
347     {
348         for (auto &e : _q) e.done();
349
350         for (auto &e : _threads) e.join();
351     }
352
353     std::future<VRle> async(RleTask *task)
354     {
355         auto receiver = std::move(task->receiver);
356         auto i = _index++;
357
358         for (unsigned n = 0; n != _count; ++n) {
359             if (_q[(i + n) % _count].try_push(task)) return receiver;
360         }
361
362         _q[i % _count].push(task);
363
364         return receiver;
365     }
366
367     std::future<VRle> strokeRle(VPath &&path, VRle &&rle, CapStyle cap, JoinStyle join,
368                                 float width, float meterLimit)
369     {
370         RleTask *task = new RleTask();
371         task->stroke = true;
372         task->path = std::move(path);
373         task->rle = std::move(rle);
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(VPath &&path, VRle &&rle, FillRule fillRule)
382     {
383         RleTask *task = new RleTask();
384         task->path = std::move(path);
385         task->rle = std::move(rle);
386         task->fillRule = fillRule;
387         task->stroke = false;
388         return async(task);
389     }
390 };
391
392 static RleTaskScheduler raster_scheduler;
393
394 std::future<VRle> VRaster::generateFillInfo(VPath &&path, VRle &&rle,
395                                             FillRule     fillRule)
396 {
397     if (path.empty()) {
398         std::promise<VRle> promise;
399         promise.set_value(VRle());
400         return promise.get_future();
401     }
402     return raster_scheduler.fillRle(std::move(path), std::move(rle), fillRule);
403 }
404
405 std::future<VRle> VRaster::generateStrokeInfo(VPath &&path, VRle &&rle, CapStyle cap,
406                                               JoinStyle join, float width,
407                                               float meterLimit)
408 {
409     if (path.empty()) {
410         std::promise<VRle> promise;
411         promise.set_value(VRle());
412         return promise.get_future();
413     }
414     return raster_scheduler.strokeRle(std::move(path), std::move(rle), cap, join, width, meterLimit);
415 }
416
417 V_END_NAMESPACE