2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "clipped-image.h"
22 #include <dali-toolkit/dali-toolkit.h>
25 #include "generated/clipped-image-frag.h"
26 #include "generated/clipped-image-vert.h"
28 namespace ClippedImage
31 using namespace Dali::Toolkit;
35 const char* const DELTA_PROPERTY_NAME("uDelta"); ///< Name of uniform used to mix the Circle and Quad geometries.
38 * @brief Creates the shader required for the clipped image
39 * @return A reference to a static handle to a shader object (only created when first called).
41 Shader& CreateShader()
43 // Only need to create it once
44 // The issue with using a static is that the shader will only get destroyed at application destruction.
45 // This is OK for a simple use cases such as this example, but for more polished applications, the shader creation/destruction needs to
46 // be managed by the application writer.
51 shader = Shader::New(SHADER_CLIPPED_IMAGE_VERT, SHADER_CLIPPED_IMAGE_FRAG);
58 * @brief Creates the geometry required for the clipped image
59 * @return A reference to a static handle to a geometry object (only created when first called).
61 Geometry& CreateGeometry()
63 // Only need to create it once
64 // The issue with using a static is that the geometry will only get destroyed at application destruction.
65 // This is OK for a simple use cases such as this example, but for more polished applications, the geometry creation/destruction needs to
66 // be managed by the application writer.
67 static Geometry geometry;
71 const int vertexCount = 34; // Needs to be 4n plus 2 where n is a positive integer above 4
73 // Create the circle geometry
75 // Radius is bound to actor's dimensions so this should not be increased.
76 // If a bigger circle is required then the actor size should be increased.
77 const float radius = 0.5f;
78 const Vector2 center = Vector2::ZERO;
80 // Create a buffer for vertex data
81 Vector2 circleBuffer[vertexCount];
84 // Center vertex for triangle fan
85 circleBuffer[idx++] = center;
87 // Outer vertices of the circle
88 const int outerVertexCount = vertexCount - 1;
90 for(int i = 0; i < outerVertexCount; ++i)
92 const float percent = (i / static_cast<float>(outerVertexCount - 1));
93 const float rad = percent * 2.0f * Math::PI;
97 outer.x = center.x + radius * cos(rad);
98 outer.y = center.y + radius * sin(rad);
100 circleBuffer[idx++] = outer;
103 Property::Map circleVertexFormat;
104 circleVertexFormat["aPositionCircle"] = Property::VECTOR2;
105 VertexBuffer circleVertices = VertexBuffer::New(circleVertexFormat);
106 circleVertices.SetData(circleBuffer, vertexCount);
108 // Create the Quad Geometry
109 Vector2 quadBuffer[vertexCount];
111 quadBuffer[idx++] = center;
113 const size_t vertsPerSide = (vertexCount - 2) / 4;
114 Vector2 outer(0.5f, 0.0f);
115 quadBuffer[idx++] = outer;
116 float incrementPerBuffer = 1.0f / static_cast<float>(vertsPerSide);
118 for(size_t i = 0; i < vertsPerSide && outer.y < 0.5f; ++i)
120 outer.y += incrementPerBuffer;
121 quadBuffer[idx++] = outer;
124 for(size_t i = 0; i < vertsPerSide && outer.x > -0.5f; ++i)
126 outer.x -= incrementPerBuffer;
127 quadBuffer[idx++] = outer;
130 for(size_t i = 0; i < vertsPerSide && outer.y > -0.5f; ++i)
132 outer.y -= incrementPerBuffer;
133 quadBuffer[idx++] = outer;
136 for(size_t i = 0; i < vertsPerSide && outer.x < 0.5f; ++i)
138 outer.x += incrementPerBuffer;
139 quadBuffer[idx++] = outer;
142 for(size_t i = 0; i < vertsPerSide && outer.y < 0.0f; ++i)
144 outer.y += incrementPerBuffer;
145 quadBuffer[idx++] = outer;
148 Property::Map vertexFormat;
149 vertexFormat["aPositionQuad"] = Property::VECTOR2;
150 VertexBuffer quadVertices2 = VertexBuffer::New(vertexFormat);
151 quadVertices2.SetData(quadBuffer, vertexCount);
153 // Create the geometry object itself
154 geometry = Geometry::New();
155 geometry.AddVertexBuffer(circleVertices);
156 geometry.AddVertexBuffer(quadVertices2);
157 geometry.SetType(Geometry::TRIANGLE_FAN);
163 } // unnamed namespace
165 const float CIRCLE_GEOMETRY = 0.0f;
166 const float QUAD_GEOMETRY = 1.0f;
168 Dali::Toolkit::Control Create(const std::string& imagePath, Property::Index& propertyIndex)
170 // Create a control which whose geometry will be morphed between a circle and a quad
171 Control clippedImage = Control::New();
172 clippedImage.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_CHILDREN);
174 // Create the required renderer and add to the clipped image control
175 Renderer renderer = Renderer::New(CreateGeometry(), CreateShader());
176 renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
177 clippedImage.AddRenderer(renderer);
179 // Register the property on the clipped image control which will allow animations between a circle and a quad
180 propertyIndex = clippedImage.RegisterProperty(DELTA_PROPERTY_NAME, 0.0f);
182 // Add the actual image to the control
183 Control image = ImageView::New(imagePath);
184 image.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
185 image.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
186 image.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
187 clippedImage.Add(image);
192 } // namespace ClippedImage