Cache NPatch textures
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-scroller.cpp
1 /*
2  * Copyright (c) 2016 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 // CLASS HEADER
19 #include <dali-toolkit/internal/text/text-scroller.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/common/stage.h>
23 #include <dali/public-api/images/frame-buffer-image.h>
24 #include <dali/public-api/render-tasks/render-task-list.h>
25 #include <dali/public-api/rendering/geometry.h>
26 #include <dali/public-api/rendering/renderer.h>
27 #include <dali/public-api/rendering/sampler.h>
28 #include <dali/public-api/rendering/shader.h>
29 #include <dali/devel-api/images/texture-set-image.h>
30 #include <dali/integration-api/debug.h>
31
32 // INTERNAL INCLUDES
33 #include <dali-toolkit/internal/text/text-scroller-interface.h>
34 #include <dali-toolkit/internal/text/text-scroller-data.h>
35
36 namespace Dali
37 {
38
39 namespace Toolkit
40 {
41
42 namespace Text
43 {
44 extern const int MINIMUM_SCROLL_SPEED;
45 } // namespace
46
47 namespace
48 {
49
50 #if defined ( DEBUG_ENABLED )
51   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_SCROLLING");
52 #endif
53
54 const char* VERTEX_SHADER_SCROLL = DALI_COMPOSE_SHADER(
55   attribute mediump vec2 aPosition;\n
56   varying highp vec2 vTexCoord;\n
57   varying highp float vRatio;\n
58   uniform mediump mat4 uMvpMatrix;\n
59   uniform mediump vec3 uSize;\n
60   uniform mediump float uDelta;\n
61   uniform mediump vec2 uTextureSize;
62   uniform mediump float uGap;\n
63   uniform mediump float uRtl;\n
64   \n
65   void main()\n
66   {\n
67     {\n
68       mediump vec4 vertexPosition = vec4(aPosition*uSize.xy, 0.0, 1.0);\n
69       float smallTextPadding = max(uSize.x - uTextureSize.x, 0. );\n
70       float gap = max( uGap, smallTextPadding );\n
71       vTexCoord.x = ( uDelta + ( uRtl * ( uTextureSize.x - uSize.x ) )  + ( aPosition.x * uSize.x ) )/ ( uTextureSize.x+gap );\n
72       vTexCoord.y = aPosition.y;\n
73       vRatio = uTextureSize.x / ( uTextureSize.x + gap );\n
74       gl_Position = uMvpMatrix * vertexPosition;\n
75     }\n
76   }\n
77 );
78
79 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
80   varying mediump vec2 vTexCoord;\n
81   varying highp float vRatio;\n
82   uniform sampler2D sTexture;\n
83   \n
84   void main()\n
85   {\n
86     mediump vec2 texCoord;\n
87     texCoord.y = vTexCoord.y;\n
88     texCoord.x = fract( vTexCoord.x ) / vRatio;\n
89     if ( texCoord.x > 1.0 )\n
90       discard;\n
91     \n
92     gl_FragColor = texture2D( sTexture, texCoord );\n
93   }\n
94 );
95
96 /**
97  * @brief Create and set up a camera for the render task to use
98  *
99  * @param[in] sizeOfTarget size of the source camera to look at
100  * @param[out] offscreenCamera custom camera
101  */
102 void CreateCameraActor( const Size& sizeOfTarget, CameraActor& offscreenCamera )
103 {
104   offscreenCamera = CameraActor::New();
105   offscreenCamera.SetOrthographicProjection( sizeOfTarget );
106   offscreenCamera.SetInvertYAxis( true );
107 }
108
109 /**
110  * @brief Create a render task
111  *
112  * @param[in] sourceActor actor to be used as source
113  * @param[in] cameraActor camera looking at source
114  * @param[in] offscreenTarget resulting image from render task
115  * @param[out] renderTask render task that has been setup
116  */
117 void CreateRenderTask( Actor sourceActor, CameraActor cameraActor , FrameBufferImage offscreenTarget, RenderTask& renderTask )
118 {
119   Stage stage = Stage::GetCurrent();
120   RenderTaskList taskList = stage.GetRenderTaskList();
121   renderTask = taskList.CreateTask();
122   renderTask.SetSourceActor( sourceActor );
123   renderTask.SetExclusive( true );
124   renderTask.SetInputEnabled( false );
125   renderTask.SetClearEnabled( true );
126   renderTask.SetCameraActor( cameraActor );
127   renderTask.SetTargetFrameBuffer( offscreenTarget );
128   renderTask.SetClearColor( Color::TRANSPARENT );
129   renderTask.SetCullMode( false );
130 }
131
132 /**
133  * @brief Create quad geometry for the mesh
134  *
135  * @param[out] geometry quad geometry that can be used for a mesh
136  */
137 void CreateGeometry( Geometry& geometry )
138 {
139   struct QuadVertex { Vector2 position;  };
140
141   QuadVertex quadVertexData[4] =
142   {
143       { Vector2( 0.0f, 0.0f) },
144       { Vector2( 1.0f, 0.0f) },
145       { Vector2( 0.0f, 1.0f) },
146       { Vector2( 1.0f, 1.0f) },
147   };
148
149   const unsigned short indices[6] =
150   {
151      3,1,0,0,2,3
152   };
153
154   Property::Map quadVertexFormat;
155   quadVertexFormat["aPosition"] = Property::VECTOR2;
156   PropertyBuffer quadVertices = PropertyBuffer::New( quadVertexFormat );
157   quadVertices.SetData(quadVertexData, 4 );
158
159   geometry = Geometry::New();
160   geometry.AddVertexBuffer( quadVertices );
161   geometry.SetIndexBuffer( indices, sizeof(indices)/sizeof(indices[0]) );
162 }
163
164
165 /**
166  * @brief Create a renderer
167  *
168  * @param[in] frameBufferImage texture to be used
169  * @param[out] renderer mesh renderer using the supplied texture
170  */
171 void CreateRenderer( FrameBufferImage frameBufferImage, Dali::Renderer& renderer )
172 {
173   Shader shader = Shader::New( VERTEX_SHADER_SCROLL , FRAGMENT_SHADER, Shader::Hint::NONE );
174
175   Sampler sampler = Sampler::New();
176   sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST );
177
178   TextureSet textureSet = TextureSet::New();
179   TextureSetImage( textureSet, 0u, frameBufferImage );
180   textureSet.SetSampler( 0u, sampler );
181
182   Geometry meshGeometry;
183   CreateGeometry( meshGeometry );
184
185   renderer = Renderer::New( meshGeometry, shader );
186   renderer.SetTextures( textureSet );
187 }
188
189 } // namespace
190
191 namespace Text
192 {
193
194 TextScrollerPtr TextScroller::New( ScrollerInterface& scrollerInterface )
195 {
196   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::New\n" );
197
198   TextScrollerPtr textScroller( new TextScroller( scrollerInterface) );
199   return textScroller;
200 }
201
202 Actor TextScroller::GetSourceCamera() const
203 {
204   return mOffscreenCameraActor;
205 }
206
207 Actor TextScroller::GetScrollingText() const
208 {
209   return mScrollingTextActor;
210 }
211
212 TextScroller::TextScroller( ScrollerInterface& scrollerInterface )
213 : mScrollerInterface( scrollerInterface ),
214   mScrollDeltaIndex( Property::INVALID_INDEX )
215 {
216   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller Default Constructor\n" );
217 }
218
219 TextScroller::~TextScroller()
220 {
221   CleanUp();
222 }
223
224 void TextScroller::StartScrolling( Actor sourceActor,
225                                    const ScrollerData& data )
226 {
227   DALI_LOG_INFO( gLogFilter,
228                  Debug::Verbose,
229                  "TextScroller::StartScrolling controlSize[%f,%f] offscreenSize[%f,%f] direction[%d] alignmentOffset[%f]\n",
230                  data.mControlSize.x, data.mControlSize.y,
231                  data.mOffscreenSize.x, data.mOffscreenSize.y,
232                  data.mAutoScrollDirectionRTL,
233                  data.mAlignmentOffset );
234
235   FrameBufferImage offscreenRenderTargetForText = FrameBufferImage::New( data.mOffscreenSize.width, data.mOffscreenSize.height, Pixel::RGBA8888 );
236   Renderer renderer;
237
238   CreateCameraActor( data.mOffscreenSize, mOffscreenCameraActor );
239   CreateRenderer( offscreenRenderTargetForText, renderer );
240   CreateRenderTask( sourceActor, mOffscreenCameraActor, offscreenRenderTargetForText, mRenderTask );
241
242   // Reposition camera to match alignment of target, RTL text has direction=true
243   if( data.mAutoScrollDirectionRTL )
244   {
245     mOffscreenCameraActor.SetX( data.mAlignmentOffset + data.mOffscreenSize.width * 0.5f );
246   }
247   else
248   {
249     mOffscreenCameraActor.SetX( data.mOffscreenSize.width * 0.5f );
250   }
251
252   mOffscreenCameraActor.SetY( data.mOffscreenSize.height * 0.5f );
253
254   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetParameters mWrapGap[%f]\n", data.mWrapGap )
255
256   mScrollingTextActor = Actor::New();
257   mScrollingTextActor.AddRenderer( renderer );
258   mScrollingTextActor.RegisterProperty( "uTextureSize", data.mOffscreenSize );
259   mScrollingTextActor.RegisterProperty( "uRtl", ( data.mAutoScrollDirectionRTL ? 1.f : 0.f ) );
260   mScrollingTextActor.RegisterProperty( "uGap", data.mWrapGap );
261   mScrollingTextActor.SetSize( data.mControlSize.width, std::min( data.mOffscreenSize.height, data.mControlSize.height ) );
262   mScrollDeltaIndex = mScrollingTextActor.RegisterProperty( "uDelta", 0.0f );
263
264   float scrollAmount = std::max( data.mOffscreenSize.width + data.mWrapGap, data.mControlSize.width );
265   float scrollSpeed = std::max( MINIMUM_SCROLL_SPEED, data.mScrollSpeed );
266   float scrollDuration =  scrollAmount / scrollSpeed;
267
268   if( data.mAutoScrollDirectionRTL )
269   {
270      scrollAmount = -scrollAmount; // reverse direction of scrollung
271   }
272
273   mScrollAnimation = Animation::New( scrollDuration );
274   mScrollAnimation.AnimateTo( Property( mScrollingTextActor, mScrollDeltaIndex ), scrollAmount );
275   mScrollAnimation.SetEndAction( Animation::Discard );
276   mScrollAnimation.SetLoopCount( data.mLoopCount );
277   mScrollAnimation.FinishedSignal().Connect( this, &TextScroller::AutoScrollAnimationFinished );
278   mScrollAnimation.Play();
279 }
280
281 void TextScroller::StopScrolling()
282 {
283   if( mScrollAnimation &&
284       ( mScrollAnimation.GetState() == Animation::PLAYING ) )
285   {
286     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::SetLoopCount Single loop forced\n" );
287     mScrollAnimation.SetLoopCount( 1 ); // As animation already playing this allows the current animation to finish instead of trying to stop mid-way
288   }
289 }
290
291 void TextScroller::AutoScrollAnimationFinished( Dali::Animation& animation )
292 {
293   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextScroller::AutoScrollAnimationFinished\n" );
294   CleanUp();
295   mScrollerInterface.ScrollingFinished();
296 }
297
298 void TextScroller::CleanUp()
299 {
300   if ( Stage::IsInstalled() )
301   {
302     Stage stage = Stage::GetCurrent();
303     RenderTaskList taskList = stage.GetRenderTaskList();
304     UnparentAndReset( mScrollingTextActor );
305     UnparentAndReset( mOffscreenCameraActor );
306     taskList.RemoveTask( mRenderTask );
307   }
308 }
309
310 } // namespace Text
311
312 } // namespace Toolkit
313
314 } // namespace Dali