Updated demos to use DALi clang-format
[platform/core/uifw/dali-demo.git] / examples / contact-cards / clipped-image.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
18 // HEADER
19 #include "clipped-image.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali-toolkit/dali-toolkit.h>
23
24 namespace ClippedImage
25 {
26 using namespace Dali;
27 using namespace Dali::Toolkit;
28
29 namespace
30 {
31 const char* const DELTA_PROPERTY_NAME("uDelta"); ///< Name of uniform used to mix the Circle and Quad geometries.
32
33 /**
34  * @brief This vertex-shader mixes in the quad and circle geometry depending on the value of uDelta.
35  *
36  * uDelta is used to mix in the Circle and the Quad positions.
37  * If uDelta is 0.0f, then the circle position is adopted and if it is 1.0f, then the quad position is adopted.
38  */
39 // clang-format off
40 const char * VERTEX_SHADER = DALI_COMPOSE_SHADER(
41   attribute mediump vec2  aPositionCircle;\n
42   attribute mediump vec2  aPositionQuad;\n
43   uniform   mediump float uDelta;
44   uniform mediump mat4    uMvpMatrix;\n
45   uniform mediump vec3    uSize;\n
46   \n
47   void main()\n
48   {\n
49     mediump vec4 vertexPosition = vec4(mix(aPositionCircle,aPositionQuad,uDelta), 0.0, 1.0);\n
50     vertexPosition.xyz *= uSize;\n
51     gl_Position = uMvpMatrix * vertexPosition;\n
52   }\n
53 );
54
55 /**
56  * @brief This fragment-shader does not output anything. It's for a control which is just going to clip as specified in the vertex shader.
57  */
58 const char * FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
59   void main()\n
60   {\n
61     gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n
62   }\n
63 );
64 // clang-format on
65
66 /**
67  * @brief Creates the shader required for the clipped image
68  * @return A reference to a static handle to a shader object (only created when first called).
69  */
70 Shader& CreateShader()
71 {
72   // Only need to create it once
73   // The issue with using a static is that the shader will only get destroyed at application destruction.
74   // This is OK for a simple use cases such as this example, but for more polished applications, the shader creation/destruction needs to
75   // be managed by the application writer.
76   static Shader shader;
77
78   if(!shader)
79   {
80     shader = Shader::New(VERTEX_SHADER, FRAGMENT_SHADER);
81   }
82
83   return shader;
84 }
85
86 /**
87  * @brief Creates the geometry required for the clipped image
88  * @return A reference to a static handle to a geometry object (only created when first called).
89  */
90 Geometry& CreateGeometry()
91 {
92   // Only need to create it once
93   // The issue with using a static is that the geometry will only get destroyed at application destruction.
94   // This is OK for a simple use cases such as this example, but for more polished applications, the geometry creation/destruction needs to
95   // be managed by the application writer.
96   static Geometry geometry;
97
98   if(!geometry)
99   {
100     const int vertexCount = 34; // Needs to be 4n plus 2 where n is a positive integer above 4
101
102     // Create the circle geometry
103
104     // Radius is bound to actor's dimensions so this should not be increased.
105     // If a bigger circle is required then the actor size should be increased.
106     const float   radius = 0.5f;
107     const Vector2 center = Vector2::ZERO;
108
109     // Create a buffer for vertex data
110     Vector2 circleBuffer[vertexCount];
111     int     idx = 0;
112
113     // Center vertex for triangle fan
114     circleBuffer[idx++] = center;
115
116     // Outer vertices of the circle
117     const int outerVertexCount = vertexCount - 1;
118
119     for(int i = 0; i < outerVertexCount; ++i)
120     {
121       const float percent = (i / static_cast<float>(outerVertexCount - 1));
122       const float rad     = percent * 2.0f * Math::PI;
123
124       // Vertex position
125       Vector2 outer;
126       outer.x = center.x + radius * cos(rad);
127       outer.y = center.y + radius * sin(rad);
128
129       circleBuffer[idx++] = outer;
130     }
131
132     Property::Map circleVertexFormat;
133     circleVertexFormat["aPositionCircle"] = Property::VECTOR2;
134     VertexBuffer circleVertices           = VertexBuffer::New(circleVertexFormat);
135     circleVertices.SetData(circleBuffer, vertexCount);
136
137     // Create the Quad Geometry
138     Vector2 quadBuffer[vertexCount];
139     idx               = 0;
140     quadBuffer[idx++] = center;
141
142     const size_t vertsPerSide = (vertexCount - 2) / 4;
143     Vector2      outer(0.5f, 0.0f);
144     quadBuffer[idx++]        = outer;
145     float incrementPerBuffer = 1.0f / static_cast<float>(vertsPerSide);
146
147     for(size_t i = 0; i < vertsPerSide && outer.y < 0.5f; ++i)
148     {
149       outer.y += incrementPerBuffer;
150       quadBuffer[idx++] = outer;
151     }
152
153     for(size_t i = 0; i < vertsPerSide && outer.x > -0.5f; ++i)
154     {
155       outer.x -= incrementPerBuffer;
156       quadBuffer[idx++] = outer;
157     }
158
159     for(size_t i = 0; i < vertsPerSide && outer.y > -0.5f; ++i)
160     {
161       outer.y -= incrementPerBuffer;
162       quadBuffer[idx++] = outer;
163     }
164
165     for(size_t i = 0; i < vertsPerSide && outer.x < 0.5f; ++i)
166     {
167       outer.x += incrementPerBuffer;
168       quadBuffer[idx++] = outer;
169     }
170
171     for(size_t i = 0; i < vertsPerSide && outer.y < 0.0f; ++i)
172     {
173       outer.y += incrementPerBuffer;
174       quadBuffer[idx++] = outer;
175     }
176
177     Property::Map vertexFormat;
178     vertexFormat["aPositionQuad"] = Property::VECTOR2;
179     VertexBuffer quadVertices2    = VertexBuffer::New(vertexFormat);
180     quadVertices2.SetData(quadBuffer, vertexCount);
181
182     // Create the geometry object itself
183     geometry = Geometry::New();
184     geometry.AddVertexBuffer(circleVertices);
185     geometry.AddVertexBuffer(quadVertices2);
186     geometry.SetType(Geometry::TRIANGLE_FAN);
187   }
188
189   return geometry;
190 }
191
192 } // unnamed namespace
193
194 const float CIRCLE_GEOMETRY = 0.0f;
195 const float QUAD_GEOMETRY   = 1.0f;
196
197 Dali::Toolkit::Control Create(const std::string& imagePath, Property::Index& propertyIndex)
198 {
199   // Create a control which whose geometry will be morphed between a circle and a quad
200   Control clippedImage = Control::New();
201   clippedImage.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_CHILDREN);
202
203   // Create the required renderer and add to the clipped image control
204   Renderer renderer = Renderer::New(CreateGeometry(), CreateShader());
205   renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
206   clippedImage.AddRenderer(renderer);
207
208   // Register the property on the clipped image control which will allow animations between a circle and a quad
209   propertyIndex = clippedImage.RegisterProperty(DELTA_PROPERTY_NAME, 0.0f);
210
211   // Add the actual image to the control
212   Control image = ImageView::New(imagePath);
213   image.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
214   image.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
215   image.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
216   clippedImage.Add(image);
217
218   return clippedImage;
219 }
220
221 } // namespace ClippedImage