lottie/vector: optimize RleTask structure packing.
[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     std::promise<VRle> sender;
242     VPath              path;
243     VRle               rle;
244     float              width;
245     float              meterLimit;
246     VRect              clip;
247     FillRule           fillRule;
248     CapStyle           cap;
249     JoinStyle          join;
250     bool               stroke;
251     VRle               operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
252     void               render(FTOutline &outRef);
253 };
254
255 void RleTask::render(FTOutline &outRef)
256 {
257     SW_FT_Raster_Params params;
258
259     params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
260     params.gray_spans = &rleGenerationCb;
261     params.user = &rle;
262     params.source = &outRef.ft;
263
264     if (!clip.empty()) {
265         params.flags |= SW_FT_RASTER_FLAG_CLIP;
266
267         params.clip_box.xMin =  clip.left();
268         params.clip_box.yMin =  clip.top();
269         params.clip_box.xMax =  clip.right();
270         params.clip_box.yMax =  clip.bottom();
271     }
272     // compute rle
273     sw_ft_grays_raster.raster_render(nullptr, &params);
274     // update bounding box.
275     rle.boundingRect();
276 }
277
278 VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
279 {
280     rle.reset();
281     if (stroke) {  // Stroke Task
282         outRef.convert(path);
283         outRef.convert(cap, join, width, meterLimit);
284
285         uint points, contors;
286
287         SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
288                           outRef.ftMeterLimit);
289         SW_FT_Stroker_ParseOutline(stroker, &outRef.ft);
290         SW_FT_Stroker_GetCounts(stroker, &points, &contors);
291
292         outRef.grow(points, contors);
293
294         SW_FT_Stroker_Export(stroker, &outRef.ft);
295
296     } else {  // Fill Task
297         outRef.convert(path);
298         int fillRuleFlag = SW_FT_OUTLINE_NONE;
299         switch (fillRule) {
300         case FillRule::EvenOdd:
301             fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
302             break;
303         default:
304             fillRuleFlag = SW_FT_OUTLINE_NONE;
305             break;
306         }
307         outRef.ft.flags = fillRuleFlag;
308     }
309
310     render(outRef);
311
312     return std::move(rle);
313 }
314
315 class RleTaskScheduler {
316     const unsigned                  _count{std::thread::hardware_concurrency()};
317     std::vector<std::thread>        _threads;
318     std::vector<TaskQueue<RleTask>> _q{_count};
319     std::atomic<unsigned>           _index{0};
320
321     void run(unsigned i)
322     {
323         /*
324          * initalize  per thread objects.
325          */
326         FTOutline     outlineRef;
327         SW_FT_Stroker stroker;
328         SW_FT_Stroker_New(&stroker);
329
330         // Task Loop
331         while (true) {
332             RleTask *task = nullptr;
333
334             for (unsigned n = 0; n != _count * 32; ++n) {
335                 if (_q[(i + n) % _count].try_pop(task)) break;
336             }
337             if (!task && !_q[i].pop(task)) break;
338
339             task->sender.set_value((*task)(outlineRef, stroker));
340             delete task;
341         }
342
343         // cleanup
344         SW_FT_Stroker_Done(stroker);
345     }
346
347 public:
348     RleTaskScheduler()
349     {
350         for (unsigned n = 0; n != _count; ++n) {
351             _threads.emplace_back([&, n] { run(n); });
352         }
353     }
354
355     ~RleTaskScheduler()
356     {
357         for (auto &e : _q) e.done();
358
359         for (auto &e : _threads) e.join();
360     }
361
362     std::future<VRle> async(RleTask *task)
363     {
364         auto receiver = std::move(task->sender.get_future());
365         auto i = _index++;
366
367         for (unsigned n = 0; n != _count; ++n) {
368             if (_q[(i + n) % _count].try_push(task)) return receiver;
369         }
370
371         _q[i % _count].push(task);
372
373         return receiver;
374     }
375
376     std::future<VRle> strokeRle(VPath &&path, VRle &&rle, CapStyle cap, JoinStyle join,
377                                 float width, float meterLimit, const VRect &clip)
378     {
379         RleTask *task = new RleTask();
380         task->stroke = true;
381         task->path = std::move(path);
382         task->rle = std::move(rle);
383         task->cap = cap;
384         task->join = join;
385         task->width = width;
386         task->meterLimit = meterLimit;
387         task->clip = clip;
388         return async(task);
389     }
390
391     std::future<VRle> fillRle(VPath &&path, VRle &&rle, FillRule fillRule, const VRect &clip)
392     {
393         RleTask *task = new RleTask();
394         task->path = std::move(path);
395         task->rle = std::move(rle);
396         task->fillRule = fillRule;
397         task->clip = clip;
398         task->stroke = false;
399         return async(task);
400     }
401 };
402
403 static RleTaskScheduler raster_scheduler;
404
405 std::future<VRle> VRaster::generateFillInfo(VPath &&path, VRle &&rle,
406                                             FillRule     fillRule, const VRect &clip)
407 {
408     if (path.empty()) {
409         std::promise<VRle> promise;
410         promise.set_value(VRle());
411         return promise.get_future();
412     }
413     return raster_scheduler.fillRle(std::move(path), std::move(rle), fillRule, clip);
414 }
415
416 std::future<VRle> VRaster::generateStrokeInfo(VPath &&path, VRle &&rle, CapStyle cap,
417                                               JoinStyle join, float width,
418                                               float meterLimit, const VRect &clip)
419 {
420     if (path.empty()) {
421         std::promise<VRle> promise;
422         promise.set_value(VRle());
423         return promise.get_future();
424     }
425     return raster_scheduler.strokeRle(std::move(path), std::move(rle), cap, join, width, meterLimit, clip);
426 }
427
428 V_END_NAMESPACE