common shape: revise scale/rotate approach.
[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 inline SwPoint TO_SWPOINT(const Point* pt)
27 {
28     return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
29 }
30
31
32 static void _growOutlineContour(SwOutline& outline, size_t n)
33 {
34     if (n == 0) {
35         free(outline.cntrs);
36         outline.cntrs = nullptr;
37         outline.cntrsCnt = 0;
38         outline.reservedCntrsCnt = 0;
39         return;
40     }
41     if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return;
42
43     //cout << "Grow Cntrs: " << outline.reservedCntrsCnt << " -> " << outline.cntrsCnt + n << endl;;
44     outline.reservedCntrsCnt = n;
45     outline.cntrs = static_cast<size_t*>(realloc(outline.cntrs, n * sizeof(size_t)));
46     assert(outline.cntrs);
47 }
48
49
50 static void _growOutlinePoint(SwOutline& outline, size_t n)
51 {
52     if (n == 0) {
53         free(outline.pts);
54         outline.pts = nullptr;
55         free(outline.types);
56         outline.types = nullptr;
57         outline.reservedPtsCnt = 0;
58         outline.ptsCnt = 0;
59         return;
60     }
61
62     if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
63
64     //cout << "Grow Pts: " << outline.reservedPtsCnt << " -> " << outline.ptsCnt + n << endl;
65     outline.reservedPtsCnt = n;
66     outline.pts = static_cast<SwPoint*>(realloc(outline.pts, n * sizeof(SwPoint)));
67     assert(outline.pts);
68     outline.types = static_cast<uint8_t*>(realloc(outline.types, n * sizeof(uint8_t)));
69     assert(outline.types);
70 }
71
72
73 static void _outlineEnd(SwOutline& outline)
74 {
75     _growOutlineContour(outline, 1);
76     if (outline.ptsCnt > 0) {
77         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
78         ++outline.cntrsCnt;
79     }
80 }
81
82
83 static void _outlineMoveTo(SwOutline& outline, const Point* to)
84 {
85     assert(to);
86
87     _growOutlinePoint(outline, 1);
88
89     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
90     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
91
92     if (outline.ptsCnt > 0) {
93         _growOutlineContour(outline, 1);
94         outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
95         ++outline.cntrsCnt;
96     }
97
98     ++outline.ptsCnt;
99 }
100
101
102 static void _outlineLineTo(SwOutline& outline, const Point* to)
103 {
104     assert(to);
105
106     _growOutlinePoint(outline, 1);
107
108     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
109     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
110
111     ++outline.ptsCnt;
112 }
113
114
115 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to)
116 {
117     assert(ctrl1 && ctrl2 && to);
118
119     _growOutlinePoint(outline, 3);
120
121     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl1);
122     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
123     ++outline.ptsCnt;
124
125     outline.pts[outline.ptsCnt] = TO_SWPOINT(ctrl2);
126     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
127     ++outline.ptsCnt;
128
129     outline.pts[outline.ptsCnt] = TO_SWPOINT(to);
130     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
131     ++outline.ptsCnt;
132 }
133
134
135 static bool _outlineClose(SwOutline& outline)
136 {
137     size_t i = 0;
138
139     if (outline.cntrsCnt > 0) {
140         i = outline.cntrs[outline.cntrsCnt - 1] + 1;
141     } else {
142         i = 0;   //First Path
143     }
144
145     //Make sure there is at least one point in the current path
146     if (outline.ptsCnt == i) return false;
147
148     //Close the path
149     _growOutlinePoint(outline, 1);
150
151     outline.pts[outline.ptsCnt] = outline.pts[i];
152     outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
153     ++outline.ptsCnt;
154
155     return true;
156 }
157
158
159 static void _initBBox(SwShape& sdata)
160 {
161     sdata.bbox.min.x = sdata.bbox.min.y = 0;
162     sdata.bbox.max.x = sdata.bbox.max.y = 0;
163 }
164
165
166 static bool _updateBBox(SwShape& sdata)
167 {
168     auto outline = sdata.outline;
169     assert(outline);
170
171     auto pt = outline->pts;
172     assert(pt);
173
174     if (outline->ptsCnt <= 0) {
175         _initBBox(sdata);
176         return false;
177     }
178
179     auto xMin = pt->x;
180     auto xMax = pt->x;
181     auto yMin = pt->y;
182     auto yMax = pt->y;
183
184     ++pt;
185
186     for(size_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
187         assert(pt);
188         if (xMin > pt->x) xMin = pt->x;
189         if (xMax < pt->x) xMax = pt->x;
190         if (yMin > pt->y) yMin = pt->y;
191         if (yMax < pt->y) yMax = pt->y;
192     }
193     sdata.bbox.min.x = xMin >> 6;
194     sdata.bbox.max.x = (xMax + 63) >> 6;
195     sdata.bbox.min.y = yMin >> 6;
196     sdata.bbox.max.y = (yMax + 63) >> 6;
197
198     if (xMax - xMin < 1 || yMax - yMin < 1) return false;
199
200     return true;
201 }
202
203
204 void _deleteRle(SwShape& sdata)
205 {
206     if (sdata.rle) {
207         if (sdata.rle->spans) free(sdata.rle->spans);
208         free(sdata.rle);
209     }
210     sdata.rle = nullptr;
211 }
212
213
214 /************************************************************************/
215 /* External Class Implementation                                        */
216 /************************************************************************/
217
218 bool shapeTransformOutline(const Shape& shape, SwShape& sdata)
219 {
220     constexpr auto PI = 3.141592f;
221
222     auto degree = shape.rotate();
223     auto scale = shape.scale();
224     bool rotateOn = false;
225     bool scaleOn = false;
226
227     if (fabsf(degree) > FLT_EPSILON) rotateOn = true;
228     if (fabsf(scale - 1) > FLT_EPSILON) scaleOn = true;
229
230     if (!rotateOn && !scaleOn) return true;
231
232     auto outline = sdata.outline;
233     assert(outline);
234
235     float x, y, w, h;
236     shape.bounds(x, y, w, h);
237
238     auto cx = x + w * 0.5f;
239     auto cy = y + h * 0.5f;
240
241     float radian, cosVal, sinVal;
242     if (rotateOn) {
243         radian = degree / 180.0f * PI;
244         cosVal = cosf(radian);
245         sinVal = sinf(radian);
246     }
247
248     for(size_t i = 0; i < outline->ptsCnt; ++i) {
249         auto dx = static_cast<float>(outline->pts[i].x >> 6) - cx;
250         auto dy = static_cast<float>(outline->pts[i].y >> 6) - cy;
251         if (rotateOn) {
252             auto tx = (cosVal * dx - sinVal * dy);
253             auto ty = (sinVal * dx + cosVal * dy);
254             dx = tx;
255             dy = ty;
256         }
257         if (scaleOn) {
258             dx *= scale;
259             dy *= scale;
260         }
261         auto pt = Point{dx + cx, dy + cy};
262         outline->pts[i] = TO_SWPOINT(&pt);
263     }
264
265     return true;
266 }
267
268
269 bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
270 {
271     if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) goto end;
272     if (!_updateBBox(sdata)) goto end;
273
274     //Check boundary
275     if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) ||
276         (sdata.bbox.min.x + sdata.bbox.max.x < 0) ||
277         (sdata.bbox.min.y + sdata.bbox.max.y < 0)) goto end;
278
279     sdata.rle = rleRender(sdata, clip);
280
281 end:
282     if (sdata.rle) return true;
283     return false;
284 }
285
286
287 void shapeDelOutline(SwShape& sdata)
288 {
289     if (!sdata.outline) return;
290
291     SwOutline* outline = sdata.outline;
292     if (outline->cntrs) free(outline->cntrs);
293     if (outline->pts) free(outline->pts);
294     if (outline->types) free(outline->types);
295     free(outline);
296
297     sdata.outline = nullptr;
298 }
299
300
301 void shapeReset(SwShape& sdata)
302 {
303     shapeDelOutline(sdata);
304     _deleteRle(sdata);
305     _initBBox(sdata);
306 }
307
308
309 bool shapeGenOutline(const Shape& shape, SwShape& sdata)
310 {
311     const PathCommand* cmds = nullptr;
312     auto cmdCnt = shape.pathCommands(&cmds);
313
314     const Point* pts = nullptr;
315     auto ptsCnt = shape.pathCoords(&pts);
316
317     //No actual shape data
318     if (cmdCnt == 0 || ptsCnt == 0) return false;
319
320     //smart reservation
321     auto outlinePtsCnt = 0;
322     auto outlineCntrsCnt = 0;
323
324     for (size_t i = 0; i < cmdCnt; ++i) {
325         switch(*(cmds + i)) {
326             case PathCommand::Close: {
327                 ++outlinePtsCnt;
328                 break;
329             }
330             case PathCommand::MoveTo: {
331                 ++outlineCntrsCnt;
332                 ++outlinePtsCnt;
333                 break;
334             }
335             case PathCommand::LineTo: {
336                 ++outlinePtsCnt;
337                 break;
338             }
339             case PathCommand::CubicTo: {
340                 outlinePtsCnt += 3;
341                 break;
342             }
343         }
344     }
345
346     ++outlinePtsCnt;    //for close
347     ++outlineCntrsCnt;  //for end
348
349     SwOutline* outline = sdata.outline;
350
351     if (!outline) {
352         outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
353         assert(outline);
354     } else {
355         cout << "Outline was already allocated? How?" << endl;
356     }
357
358     _growOutlinePoint(*outline, outlinePtsCnt);
359     _growOutlineContour(*outline, outlineCntrsCnt);
360
361     //Generate Outlines
362     while (cmdCnt-- > 0) {
363         switch(*cmds) {
364             case PathCommand::Close: {
365                 _outlineClose(*outline);
366                 break;
367             }
368             case PathCommand::MoveTo: {
369                 _outlineMoveTo(*outline, pts);
370                 ++pts;
371                 break;
372             }
373             case PathCommand::LineTo: {
374                 _outlineLineTo(*outline, pts);
375                 ++pts;
376                 break;
377             }
378             case PathCommand::CubicTo: {
379                 _outlineCubicTo(*outline, pts, pts + 1, pts + 2);
380                 pts += 3;
381                 break;
382             }
383         }
384         ++cmds;
385     }
386
387     _outlineEnd(*outline);
388
389     //FIXME:
390     //outline->flags = SwOutline::FillRule::Winding;
391
392     sdata.outline = outline;
393
394     return true;
395 }
396
397
398 #endif /* _TVG_SW_SHAPE_H_ */