Initial implemention NPatchRenderer only for 9 patch case.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / renderers / npatch / npatch-renderer.cpp
1 /*
2  * Copyright (c) 2015 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 "npatch-renderer.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/platform-abstraction.h>
23 #include <dali/public-api/images/buffer-image.h>
24 #include <dali/public-api/images/resource-image.h>
25
26 // INTERNAL IINCLUDES
27 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
28 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
29 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
30 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
31
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Internal
40 {
41
42 namespace
43 {
44
45 const char * const IMAGE_URL_NAME("image-url");
46 const char * const BORDER_ONLY("border-only");
47
48 std::string TEXTURE_UNIFORM_NAME = "sTexture";
49
50 const char* VERTEX_SHADER_3X3 = DALI_COMPOSE_SHADER(
51     attribute mediump vec2 aPosition;\n
52     varying mediump vec2 vTexCoord;\n
53     uniform mediump mat4 uModelMatrix;\n
54     uniform mediump mat4 uMvpMatrix;\n
55     uniform mediump vec3 uSize;\n
56     uniform mediump vec2 uFixed[ 3 ];\n
57     uniform mediump vec2 uStretchTotal;\n
58     \n
59     void main()\n
60     {\n
61       mediump vec2 scale        = vec2( length( uModelMatrix[ 0 ].xyz ), length( uModelMatrix[ 1 ].xyz ) );\n
62       mediump vec2 size         = uSize.xy * scale;\n
63       \n
64       mediump vec2 fixedFactor  = vec2( uFixed[ int( ( aPosition.x + 1.0 ) * 0.5 ) ].x, uFixed[ int( ( aPosition.y  + 1.0 ) * 0.5 ) ].y );\n
65       mediump vec2 stretch      = floor( aPosition * 0.5 );\n
66       mediump vec2 fixedTotal   = uFixed[ 2 ];\n
67       \n
68       mediump vec4 vertexPosition = vec4( fixedFactor + ( size - fixedTotal ) * stretch, 0.0, 1.0 );\n
69       vertexPosition.xy -= size * vec2( 0.5, 0.5 );\n
70       vertexPosition.xy =  vertexPosition.xy / scale;\n
71       \n
72       vertexPosition = uMvpMatrix * vertexPosition;\n
73       \n
74       vTexCoord = ( fixedFactor + stretch * uStretchTotal ) / ( fixedTotal + uStretchTotal );\n
75       \n
76       gl_Position = vertexPosition;\n
77     }\n
78 );
79
80 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
81   varying mediump vec2 vTexCoord;\n
82   uniform sampler2D sTexture;\n
83   uniform lowp vec4 uColor;\n
84   \n
85   void main()\n
86   {\n
87     gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
88   }\n
89 );
90
91 /**
92  * @brief Creates the geometry formed from the vertices and indices
93  *
94  * @param[in]  vertices             The vertices to generate the geometry from
95  * @param[in]  indices              The indices to generate the geometry from
96  * @return The geometry formed from the vertices and indices
97  */
98 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices )
99 {
100   Property::Map vertexFormat;
101   vertexFormat[ "aPosition" ] = Property::VECTOR2;
102   PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
103   if( vertices.Size() > 0 )
104   {
105     vertexPropertyBuffer.SetData( &vertices[ 0 ] );
106   }
107
108   Property::Map indexFormat;
109   indexFormat[ "indices" ] = Property::INTEGER;
110   PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
111   if( indices.Size() > 0 )
112   {
113     indexPropertyBuffer.SetData( &indices[ 0 ] );
114   }
115
116   // Create the geometry object
117   Geometry geometry = Geometry::New();
118   geometry.AddVertexBuffer( vertexPropertyBuffer );
119   geometry.SetIndexBuffer( indexPropertyBuffer );
120
121   return geometry;
122 }
123
124 /**
125  * @brief Adds the indices to form a quad composed off two triangles where the indices are organised in a grid
126  *
127  * @param[out] indices     The indices to add to
128  * @param[in]  rowIdx      The row index to start the quad
129  * @param[in]  nextRowIdx  The index to the next row
130  */
131 void AddQuadIndices( Vector< unsigned int >& indices, unsigned int rowIdx, unsigned int nextRowIdx )
132 {
133   indices.PushBack( rowIdx );
134   indices.PushBack( nextRowIdx + 1 );
135   indices.PushBack( rowIdx + 1 );
136
137   indices.PushBack( rowIdx );
138   indices.PushBack( nextRowIdx );
139   indices.PushBack( nextRowIdx + 1 );
140 }
141
142 void AddVertex( Vector< Vector2 >& vertices, unsigned int x, unsigned int y )
143 {
144   vertices.PushBack( Vector2( x, y ) );
145 }
146
147 } //unnamed namespace
148
149 /////////////////NPatchRenderer////////////////
150
151 NPatchRenderer::NPatchRenderer()
152 : ControlRenderer(),
153   mBorderOnly( false )
154 {
155 }
156
157 NPatchRenderer::~NPatchRenderer()
158 {
159 }
160
161 void NPatchRenderer::Initialize( RendererFactoryCache& factoryCache, const Property::Map& propertyMap )
162 {
163   Initialize(factoryCache);
164
165   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
166   if( imageURLValue )
167   {
168     //Read the border-only property first since InitialiseFromImage relies on mBorderOnly to be properly set
169     Property::Value* borderOnlyValue = propertyMap.Find( BORDER_ONLY );
170     if( borderOnlyValue )
171     {
172       borderOnlyValue->Get( mBorderOnly );
173     }
174
175     if( imageURLValue->Get( mImageUrl ) )
176     {
177       NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
178       InitialiseFromImage( nPatch );
179     }
180     else
181     {
182       CreateErrorImage();
183       DALI_LOG_ERROR( "The property '%s' is not a string\n", IMAGE_URL_NAME );
184     }
185   }
186 }
187
188 void NPatchRenderer::SetClipRect( const Rect<int>& clipRect )
189 {
190   ControlRenderer::SetClipRect( clipRect );
191   //ToDo: renderer responds to the clipRect change
192 }
193
194 void NPatchRenderer::SetOffset( const Vector2& offset )
195 {
196   //ToDo: renderer applies the offset
197 }
198
199 void NPatchRenderer::DoSetOnStage( Actor& actor )
200 {
201   if( !mCroppedImage )
202   {
203     if( !mImageUrl.empty() )
204     {
205       NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
206       InitialiseFromImage( nPatch );
207     }
208     else if( mImage )
209     {
210       InitialiseFromImage( mImage );
211     }
212   }
213
214   if( mCroppedImage )
215   {
216     ApplyImageToSampler();
217   }
218 }
219
220 void NPatchRenderer::DoSetOffStage( Actor& actor )
221 {
222   mCroppedImage.Reset();
223 }
224
225 void NPatchRenderer::Initialize( RendererFactoryCache& factoryCache )
226 {
227   mNinePatchGeometry = factoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY );
228   if( !(mNinePatchGeometry) )
229   {
230     mNinePatchGeometry = CreateGeometry( Uint16Pair( 3, 3 ) );
231     factoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY, mNinePatchGeometry );
232   }
233
234   mNinePatchBorderGeometry = factoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY );
235   if( !(mNinePatchBorderGeometry) )
236   {
237     mNinePatchBorderGeometry = CreateGeometryBorder( Uint16Pair( 3, 3 ) );
238     factoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY, mNinePatchBorderGeometry );
239   }
240
241   mNinePatchShader = factoryCache.GetShader( RendererFactoryCache::NINE_PATCH_SHADER );
242   if( !mNinePatchShader )
243   {
244     mNinePatchShader = Shader::New( VERTEX_SHADER_3X3, FRAGMENT_SHADER );
245     factoryCache.SaveShader( RendererFactoryCache::NINE_PATCH_SHADER, mNinePatchShader );
246   }
247
248   mImpl->mGeometry = mNinePatchGeometry;
249   mImpl->mShader = mNinePatchShader;
250
251   mImageUrl.clear();
252 }
253
254 void NPatchRenderer::SetImage( const std::string& imageUrl, bool borderOnly )
255 {
256   mBorderOnly = borderOnly;
257   mImage.Reset();
258   if( mImageUrl == imageUrl )
259   {
260     return;
261   }
262
263   mImageUrl = imageUrl;
264   NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
265   InitialiseFromImage( nPatch );
266
267   if( mCroppedImage && mImpl->mIsOnStage )
268   {
269     ApplyImageToSampler();
270   }
271 }
272
273 void NPatchRenderer::SetImage( NinePatchImage image, bool borderOnly )
274 {
275   mBorderOnly = borderOnly;
276   mImageUrl.empty();
277   if( mImage == image )
278   {
279     return;
280   }
281
282   mImage = image;
283   InitialiseFromImage( mImage );
284
285   if( mCroppedImage && mImpl->mIsOnStage )
286   {
287     ApplyImageToSampler();
288   }
289 }
290
291 void NPatchRenderer::InitialiseFromImage( NinePatchImage nPatch )
292 {
293   mCroppedImage = nPatch.CreateCroppedBufferImage();
294   if( !mCroppedImage )
295   {
296     DALI_LOG_ERROR("'%s' specify a valid 9 patch image\n", mImageUrl.c_str() );
297     CreateErrorImage();
298     return;
299   }
300
301   mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() );
302
303   mStretchPixelsX = nPatch.GetStretchPixelsX();
304   mStretchPixelsY = nPatch.GetStretchPixelsY();
305
306   if( mStretchPixelsX.Size() > 0 && mStretchPixelsY.Size() > 0 )
307   {
308     //only 9 patch supported for now
309     mImpl->mGeometry = !mBorderOnly ? mNinePatchGeometry : mNinePatchBorderGeometry;
310     mImpl->mShader = mNinePatchShader;
311   }
312 }
313
314 void NPatchRenderer::CreateErrorImage()
315 {
316   mImageSize = ImageDimensions( 1, 1 );
317
318   BufferImage bufferImage = BufferImage::New( mImageSize.GetWidth(), mImageSize.GetHeight(), Pixel::RGBA8888 );
319   mCroppedImage = bufferImage;
320   PixelBuffer* pixbuf = bufferImage.GetBuffer();
321
322   for( size_t i = 0; i < mImageSize.GetWidth() * mImageSize.GetHeight() * 4u; )
323   {
324     pixbuf[ i++ ] = 0;
325     pixbuf[ i++ ] = 0;
326     pixbuf[ i++ ] = 0;
327     pixbuf[ i++ ] = 255;
328   }
329
330   mStretchPixelsX.Clear();
331   mStretchPixelsX.PushBack( Uint16Pair( 0, mImageSize.GetWidth() ) );
332   mStretchPixelsY.Clear();
333   mStretchPixelsY.PushBack( Uint16Pair( 0, mImageSize.GetHeight() ) );
334
335   mImpl->mGeometry = mNinePatchGeometry;
336   mImpl->mShader = mNinePatchShader;
337 }
338
339 void NPatchRenderer::ApplyImageToSampler()
340 {
341   Material material = mImpl->mRenderer.GetMaterial();
342   if( material )
343   {
344     Sampler sampler;
345     for( std::size_t i = 0; i < material.GetNumberOfSamplers(); ++i )
346     {
347       sampler = material.GetSamplerAt( i );
348       if( sampler.GetUniformName() == TEXTURE_UNIFORM_NAME )
349       {
350         sampler.SetImage( mCroppedImage );
351         break;
352       }
353     }
354     if( !sampler )
355     {
356       sampler = Sampler::New( mCroppedImage, TEXTURE_UNIFORM_NAME );
357       material.AddSampler( sampler );
358     }
359
360     if( mStretchPixelsX.Size() > 0 && mStretchPixelsY.Size() > 0 )
361     {
362       //only 9 patch supported for now
363       Uint16Pair stretchX = mStretchPixelsX[ 0 ];
364       Uint16Pair stretchY = mStretchPixelsY[ 0 ];
365
366       uint16_t stretchWidth = stretchX.GetY() - stretchX.GetX();
367       uint16_t stretchHeight = stretchY.GetY() - stretchY.GetX();
368
369       sampler.RegisterProperty( "uFixed[0]", Vector2::ZERO );
370       sampler.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
371       sampler.RegisterProperty( "uFixed[2]", Vector2( mImageSize.GetWidth() - stretchWidth, mImageSize.GetHeight() - stretchHeight ) );
372       sampler.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
373     }
374   }
375 }
376
377 Geometry NPatchRenderer::CreateGeometry( Uint16Pair gridSize )
378 {
379   uint16_t gridWidth = gridSize.GetWidth();
380   uint16_t gridHeight = gridSize.GetHeight();
381
382   // Create vertices
383   Vector< Vector2 > vertices;
384   vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
385
386   for( int y = 0; y < gridHeight + 1; ++y )
387   {
388     for( int x = 0; x < gridWidth + 1; ++x )
389     {
390       AddVertex( vertices, x, y );
391     }
392   }
393
394   // Create indices
395   //TODO: compare performance with triangle strip when Geometry supports it
396   Vector< unsigned int > indices;
397   indices.Reserve( gridWidth * gridHeight * 6 );
398
399   unsigned int rowIdx     = 0;
400   unsigned int nextRowIdx = gridWidth + 1;
401   for( int y = 0; y < gridHeight; ++y, ++nextRowIdx, ++rowIdx )
402   {
403     for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
404     {
405       AddQuadIndices( indices, rowIdx, nextRowIdx );
406     }
407   }
408
409   return GenerateGeometry( vertices, indices );
410 }
411
412 Geometry NPatchRenderer::CreateGeometryBorder( Uint16Pair gridSize )
413 {
414   uint16_t gridWidth = gridSize.GetWidth();
415   uint16_t gridHeight = gridSize.GetHeight();
416
417   // Create vertices
418   Vector< Vector2 > vertices;
419   vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
420
421   //top
422   int y = 0;
423   for(; y < 2; ++y)
424   {
425     for( int x = 0; x < gridWidth + 1; ++x )
426     {
427       AddVertex( vertices, x, y );
428     }
429   }
430
431   for(; y < gridHeight - 1; ++y)
432   {
433     //left
434     AddVertex( vertices, 0, y );
435     AddVertex( vertices, 1, y );
436
437     //right
438     AddVertex( vertices, gridWidth - 1, y );
439     AddVertex( vertices, gridWidth, y );
440   }
441
442   //bottom
443   for(; y < gridHeight + 1; ++y)
444   {
445     for( int x = 0; x < gridWidth + 1; ++x )
446     {
447       AddVertex( vertices, x, y );
448     }
449   }
450
451   // Create indices
452   //TODO: compare performance with triangle strip when Geometry supports it
453   Vector< unsigned int > indices;
454   indices.Reserve( gridWidth * gridHeight * 6 );
455
456   //top
457   unsigned int rowIdx     = 0 ;
458   unsigned int nextRowIdx = gridWidth + 1;
459   for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
460   {
461     AddQuadIndices( indices, rowIdx, nextRowIdx );
462   }
463
464   if(gridHeight > 2)
465   {
466     rowIdx     = gridWidth + 1;
467     nextRowIdx = ( gridWidth + 1 ) * 2;
468
469     unsigned increment = gridWidth - 1;
470     if(gridHeight > 3)
471     {
472       increment = 2;
473       //second row left
474       AddQuadIndices( indices, rowIdx, nextRowIdx );
475
476       rowIdx     = gridWidth * 2;
477       nextRowIdx = ( gridWidth + 1 ) * 2 + 2;
478       //second row right
479       AddQuadIndices( indices, rowIdx, nextRowIdx );
480
481       //left and right
482       rowIdx     = nextRowIdx - 2;
483       nextRowIdx = rowIdx + 4;
484       for(int y = 2; y < 2*(gridHeight - 3); ++y, rowIdx += 2, nextRowIdx += 2)
485       {
486         AddQuadIndices( indices, rowIdx, nextRowIdx );
487       }
488     }
489
490     //second row left
491     AddQuadIndices( indices, rowIdx, nextRowIdx );
492
493     rowIdx     += increment;
494     nextRowIdx += gridWidth - 1;
495     //second row right
496     AddQuadIndices( indices, rowIdx, nextRowIdx );
497   }
498
499   //bottom
500   rowIdx     = nextRowIdx - gridWidth + 1;
501   nextRowIdx = rowIdx + gridWidth + 1;
502   for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
503   {
504     AddQuadIndices( indices, rowIdx, nextRowIdx );
505   }
506
507   return GenerateGeometry( vertices, indices );
508 }
509
510 } // namespace Internal
511
512 } // namespace Toolkit
513
514 } // namespace Dali