sw_engine shape: ++ comment for later optimization
[platform/core/graphics/tizenvg.git] / src / lib / sw_engine / tvgSwShape.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *               http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  */
17 #ifndef _TVG_SW_SHAPE_H_
18 #define _TVG_SW_SHAPE_H_
19
20 #include "tvgSwCommon.h"
21
22 /************************************************************************/
23 /* Internal Class Implementation                                        */
24 /************************************************************************/
25
26 static void _growOutlineContour(SwOutline& outline, uint32_t n)
27 {
28     if (n == 0) {
29         free(outline.cntrs);
30         outline.cntrs = nullptr;
31         outline.cntrsCnt = 0;
32         outline.reservedCntrsCnt = 0;
33         return;
34     }
35     if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return;
36
37     //cout << "Grow Cntrs: " << outline.reservedCntrsCnt << " -> " << outline.cntrsCnt + n << endl;;
38     outline.reservedCntrsCnt = n;
39     outline.cntrs = static_cast<uint32_t*>(realloc(outline.cntrs, n * sizeof(uint32_t)));
40     assert(outline.cntrs);
41 }
42
43
44 static void _growOutlinePoint(SwOutline& outline, uint32_t n)
45 {
46     if (n == 0) {
47         free(outline.pts);
48         outline.pts = nullptr;
49         free(outline.types);
50         outline.types = nullptr;
51         outline.reservedPtsCnt = 0;
52         outline.ptsCnt = 0;
53         return;
54     }
55
56     if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
57
58     //cout << "Grow Pts: " << outline.reservedPtsCnt << " -> " << outline.ptsCnt + n << endl;
59     outline.reservedPtsCnt = n;
60     outline.pts = static_cast<SwPoint*>(realloc(outline.pts, n * sizeof(SwPoint)));
61     assert(outline.pts);
62     outline.types = static_cast<uint8_t*>(realloc(outline.types, n * sizeof(uint8_t)));
63     assert(outline.types);
64 }
65
66
67 static void _freeOutline(SwOutline* outline)
68 {
69     if (!outline) return;
70
71     if (outline->cntrs) free(outline->cntrs);
72     if (outline->pts) free(outline->pts);
73     if (outline->types) free(outline->types);
74     free(outline);
75 }
76
77
78 static void _outlineEnd(SwOutline& outline)
79 {
80     _growOutlineContour(outline, 1);
81     if (outline.ptsCnt > 0) {
82         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
83         ++outline.cntrsCnt;
84     }
85 }
86
87
88 static void _outlineMoveTo(SwOutline& outline, const Point* to)
89 {
90     assert(to);
91
92     _growOutlinePoint(outline, 1);
93
94     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
95     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
96
97     if (outline.ptsCnt > 0) {
98         _growOutlineContour(outline, 1);
99         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
100         ++outline.cntrsCnt;
101     }
102
103     ++outline.ptsCnt;
104 }
105
106
107 static void _outlineLineTo(SwOutline& outline, const Point* to)
108 {
109     assert(to);
110
111     _growOutlinePoint(outline, 1);
112
113     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
114     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
115
116     ++outline.ptsCnt;
117 }
118
119
120 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to)
121 {
122     assert(ctrl1 && ctrl2 && to);
123
124     _growOutlinePoint(outline, 3);
125
126     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl1);
127     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
128     ++outline.ptsCnt;
129
130     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl2);
131     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
132     ++outline.ptsCnt;
133
134     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
135     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
136     ++outline.ptsCnt;
137 }
138
139
140 static void _outlineClose(SwOutline& outline)
141 {
142     uint32_t i = 0;
143
144     if (outline.cntrsCnt > 0) {
145         i = outline.cntrs[outline.cntrsCnt - 1] + 1;
146     } else {
147         i = 0;   //First Path
148     }
149
150     //Make sure there is at least one point in the current path
151     if (outline.ptsCnt == i) {
152         outline.opened = true;
153         return;
154     }
155
156     //Close the path
157     _growOutlinePoint(outline, 1);
158
159     outline.pts[outline.ptsCnt] = outline.pts[i];
160     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
161     ++outline.ptsCnt;
162
163     outline.opened = false;
164 }
165
166
167 static void _initBBox(SwBBox& bbox)
168 {
169     bbox.min.x = bbox.min.y = 0;
170     bbox.max.x = bbox.max.y = 0;
171 }
172
173
174 static bool _updateBBox(SwOutline* outline, SwBBox& bbox)
175 {
176     if (!outline) return false;
177
178     auto pt = outline->pts;
179     assert(pt);
180
181     if (outline->ptsCnt <= 0) {
182         _initBBox(bbox);
183         return false;
184     }
185
186     auto xMin = pt->x;
187     auto xMax = pt->x;
188     auto yMin = pt->y;
189     auto yMax = pt->y;
190
191     ++pt;
192
193     for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
194         assert(pt);
195         if (xMin > pt->x) xMin = pt->x;
196         if (xMax < pt->x) xMax = pt->x;
197         if (yMin > pt->y) yMin = pt->y;
198         if (yMax < pt->y) yMax = pt->y;
199     }
200     bbox.min.x = xMin >> 6;
201     bbox.max.x = (xMax + 63) >> 6;
202     bbox.min.y = yMin >> 6;
203     bbox.max.y = (yMax + 63) >> 6;
204
205     if (xMax - xMin < 1 || yMax - yMin < 1) return false;
206
207     return true;
208 }
209
210
211 static bool _checkValid(SwShape& sdata, const SwSize& clip)
212 {
213     assert(sdata.outline);
214
215     if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) return false;
216
217     //Check boundary
218     if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) ||
219         (sdata.bbox.min.x + sdata.bbox.max.x < 0) ||
220         (sdata.bbox.min.y + sdata.bbox.max.y < 0)) return false;
221
222     return true;
223 }
224
225
226 /************************************************************************/
227 /* External Class Implementation                                        */
228 /************************************************************************/
229
230 void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform)
231 {
232     auto outline = sdata.outline;
233     assert(outline);
234
235     for(uint32_t i = 0; i < outline->ptsCnt; ++i) {
236         auto dx = static_cast<float>(outline->pts[i].x >> 6);
237         auto dy = static_cast<float>(outline->pts[i].y >> 6);
238         auto tx = dx * transform.e11 + dy * transform.e12 + transform.e13;
239         auto ty = dx * transform.e21 + dy * transform.e22 + transform.e23;
240         auto pt = Point{tx + transform.e31, ty + transform.e32};
241         outline->pts[i] = TO_SWPOINT(&pt);
242     }
243 }
244
245
246 bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
247 {
248     /* OPTIMIZE ME: We may avoid this bounding box calculation in this stage
249        if this shape has stroke and stroke bbox can be used here... */
250     if (!_updateBBox(sdata.outline, sdata.bbox)) goto end;
251     if (!_checkValid(sdata, clip)) goto end;
252
253     sdata.rle = rleRender(sdata.outline, sdata.bbox, clip);
254
255 end:
256     if (sdata.rle) return true;
257     return false;
258 }
259
260
261 void shapeDelOutline(SwShape& sdata)
262 {
263     auto outline = sdata.outline;
264     _freeOutline(outline);
265     sdata.outline = nullptr;
266 }
267
268
269 void shapeReset(SwShape& sdata)
270 {
271     shapeDelOutline(sdata);
272     rleFree(sdata.rle);
273     sdata.rle = nullptr;
274     _initBBox(sdata.bbox);
275 }
276
277
278 bool shapeGenOutline(const Shape& shape, SwShape& sdata)
279 {
280     const PathCommand* cmds = nullptr;
281     auto cmdCnt = shape.pathCommands(&cmds);
282
283     const Point* pts = nullptr;
284     auto ptsCnt = shape.pathCoords(&pts);
285
286     //No actual shape data
287     if (cmdCnt == 0 || ptsCnt == 0) return false;
288
289     //smart reservation
290     auto outlinePtsCnt = 0;
291     auto outlineCntrsCnt = 0;
292
293     for (uint32_t i = 0; i < cmdCnt; ++i) {
294         switch(*(cmds + i)) {
295             case PathCommand::Close: {
296                 ++outlinePtsCnt;
297                 break;
298             }
299             case PathCommand::MoveTo: {
300                 ++outlineCntrsCnt;
301                 ++outlinePtsCnt;
302                 break;
303             }
304             case PathCommand::LineTo: {
305                 ++outlinePtsCnt;
306                 break;
307             }
308             case PathCommand::CubicTo: {
309                 outlinePtsCnt += 3;
310                 break;
311             }
312         }
313     }
314
315     ++outlinePtsCnt;    //for close
316     ++outlineCntrsCnt;  //for end
317
318     auto outline = sdata.outline;
319     if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
320     assert(outline);
321     outline->opened = true;
322
323     _growOutlinePoint(*outline, outlinePtsCnt);
324     _growOutlineContour(*outline, outlineCntrsCnt);
325
326     //Generate Outlines
327     while (cmdCnt-- > 0) {
328         switch(*cmds) {
329             case PathCommand::Close: {
330                 _outlineClose(*outline);
331                 break;
332             }
333             case PathCommand::MoveTo: {
334                 _outlineMoveTo(*outline, pts);
335                 ++pts;
336                 break;
337             }
338             case PathCommand::LineTo: {
339                 _outlineLineTo(*outline, pts);
340                 ++pts;
341                 break;
342             }
343             case PathCommand::CubicTo: {
344                 _outlineCubicTo(*outline, pts, pts + 1, pts + 2);
345                 pts += 3;
346                 break;
347             }
348         }
349         ++cmds;
350     }
351
352     _outlineEnd(*outline);
353
354     //FIXME:
355     //outline->flags = SwOutline::FillRule::Winding;
356
357     sdata.outline = outline;
358
359     return true;
360 }
361
362
363 void shapeFree(SwShape* sdata)
364 {
365     assert(sdata);
366
367     shapeDelOutline(*sdata);
368     rleFree(sdata->rle);
369
370     if (sdata->stroke) {
371         rleFree(sdata->strokeRle);
372         strokeFree(sdata->stroke);
373     }
374
375     free(sdata);
376 }
377
378
379 void shapeResetStroke(const Shape& shape, SwShape& sdata)
380 {
381     if (!sdata.stroke) sdata.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
382     auto stroke = sdata.stroke;
383     assert(stroke);
384     strokeReset(*stroke, shape.strokeWidth(), shape.strokeCap(), shape.strokeJoin());
385     rleFree(sdata.strokeRle);
386     sdata.strokeRle = nullptr;
387 }
388
389
390 bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
391 {
392     if (!sdata.outline) {
393         if (!shapeGenOutline(shape, sdata)) return false;
394     }
395
396     if (!_checkValid(sdata, clip)) return false;
397
398     if (!strokeParseOutline(*sdata.stroke, *sdata.outline)) return false;
399
400     auto outline = strokeExportOutline(*sdata.stroke);
401     if (!outline) return false;
402
403     SwBBox bbox;
404     _updateBBox(outline, bbox);
405
406     sdata.strokeRle = rleRender(outline, bbox, clip);
407
408     _freeOutline(outline);
409
410     return true;
411 }
412
413 #endif /* _TVG_SW_SHAPE_H_ */