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