df0e8f5ada4a3d8af16adacfcf46c9ff27ef83b9
[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 const char * const RENDERER_TYPE("rendererType");
45 const char * const RENDERER_TYPE_VALUE("nPatch");
46
47 const char * const IMAGE_URL_NAME("imageUrl");
48 const char * const BORDER_ONLY("borderOnly");
49
50 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
51   attribute mediump vec2 aPosition;\n
52   varying mediump vec2 vTexCoord;\n
53   uniform mediump mat4 uMvpMatrix;\n
54   uniform mediump vec3 uSize;\n
55   uniform mediump vec2 uNinePatchFactorsX[ FACTOR_SIZE_X ];\n
56   uniform mediump vec2 uNinePatchFactorsY[ FACTOR_SIZE_Y ];\n
57   \n
58   void main()\n
59   {\n
60     mediump vec2 fixedFactor  = vec2( uNinePatchFactorsX[ int( ( aPosition.x + 1.0 ) * 0.5 ) ].x, uNinePatchFactorsY[ int( ( aPosition.y + 1.0 ) * 0.5 ) ].x );\n
61     mediump vec2 stretch      = vec2( uNinePatchFactorsX[ int( ( aPosition.x       ) * 0.5 ) ].y, uNinePatchFactorsY[ int( ( aPosition.y       ) * 0.5 ) ].y );\n
62     \n
63     mediump vec2 fixedTotal   = vec2( uNinePatchFactorsX[ FACTOR_SIZE_X - 1 ].x, uNinePatchFactorsY[ FACTOR_SIZE_Y - 1 ].x );\n
64     mediump vec2 stretchTotal = vec2( uNinePatchFactorsX[ FACTOR_SIZE_X - 1 ].y, uNinePatchFactorsY[ FACTOR_SIZE_Y - 1 ].y );\n
65     \n
66     mediump vec4 vertexPosition = vec4( ( fixedFactor + ( uSize.xy - fixedTotal ) * stretch / stretchTotal ), 0.0, 1.0 );\n
67     vertexPosition.xy -= uSize.xy * vec2( 0.5, 0.5 );\n
68     vertexPosition = uMvpMatrix * vertexPosition;\n
69     \n
70     vTexCoord = ( fixedFactor + stretch ) / ( fixedTotal + stretchTotal );\n
71     \n
72     gl_Position = vertexPosition;\n
73   }\n
74 );
75
76 const char* VERTEX_SHADER_3X3 = DALI_COMPOSE_SHADER(
77     attribute mediump vec2 aPosition;\n
78     varying mediump vec2 vTexCoord;\n
79     uniform mediump mat4 uModelMatrix;\n
80     uniform mediump mat4 uMvpMatrix;\n
81     uniform mediump vec3 uSize;\n
82     uniform mediump vec2 uFixed[ 3 ];\n
83     uniform mediump vec2 uStretchTotal;\n
84     \n
85     void main()\n
86     {\n
87       mediump vec2 scale        = vec2( length( uModelMatrix[ 0 ].xyz ), length( uModelMatrix[ 1 ].xyz ) );\n
88       mediump vec2 size         = uSize.xy * scale;\n
89       \n
90       mediump vec2 fixedFactor  = vec2( uFixed[ int( ( aPosition.x + 1.0 ) * 0.5 ) ].x, uFixed[ int( ( aPosition.y  + 1.0 ) * 0.5 ) ].y );\n
91       mediump vec2 stretch      = floor( aPosition * 0.5 );\n
92       mediump vec2 fixedTotal   = uFixed[ 2 ];\n
93       \n
94       mediump vec4 vertexPosition = vec4( fixedFactor + ( size - fixedTotal ) * stretch, 0.0, 1.0 );\n
95       vertexPosition.xy -= size * vec2( 0.5, 0.5 );\n
96       vertexPosition.xy =  vertexPosition.xy / scale;\n
97       \n
98       vertexPosition = uMvpMatrix * vertexPosition;\n
99       \n
100       vTexCoord = ( fixedFactor + stretch * uStretchTotal ) / ( fixedTotal + uStretchTotal );\n
101       \n
102       gl_Position = vertexPosition;\n
103     }\n
104 );
105
106 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
107   varying mediump vec2 vTexCoord;\n
108   uniform sampler2D sTexture;\n
109   uniform lowp vec4 uColor;\n
110   \n
111   void main()\n
112   {\n
113     gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
114   }\n
115 );
116
117 /**
118  * @brief Creates the geometry formed from the vertices and indices
119  *
120  * @param[in]  vertices             The vertices to generate the geometry from
121  * @param[in]  indices              The indices to generate the geometry from
122  * @return The geometry formed from the vertices and indices
123  */
124 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices )
125 {
126   Property::Map vertexFormat;
127   vertexFormat[ "aPosition" ] = Property::VECTOR2;
128   PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat );
129   if( vertices.Size() > 0 )
130   {
131     vertexPropertyBuffer.SetData( &vertices[ 0 ], vertices.Size() );
132   }
133
134   Property::Map indexFormat;
135   indexFormat[ "indices" ] = Property::INTEGER;
136   PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat );
137   if( indices.Size() > 0 )
138   {
139     indexPropertyBuffer.SetData( &indices[ 0 ], indices.Size() );
140   }
141
142   // Create the geometry object
143   Geometry geometry = Geometry::New();
144   geometry.AddVertexBuffer( vertexPropertyBuffer );
145   geometry.SetIndexBuffer( indexPropertyBuffer );
146
147   return geometry;
148 }
149
150 /**
151  * @brief Adds the indices to form a quad composed off two triangles where the indices are organised in a grid
152  *
153  * @param[out] indices     The indices to add to
154  * @param[in]  rowIdx      The row index to start the quad
155  * @param[in]  nextRowIdx  The index to the next row
156  */
157 void AddQuadIndices( Vector< unsigned int >& indices, unsigned int rowIdx, unsigned int nextRowIdx )
158 {
159   indices.PushBack( rowIdx );
160   indices.PushBack( nextRowIdx + 1 );
161   indices.PushBack( rowIdx + 1 );
162
163   indices.PushBack( rowIdx );
164   indices.PushBack( nextRowIdx );
165   indices.PushBack( nextRowIdx + 1 );
166 }
167
168 void AddVertex( Vector< Vector2 >& vertices, unsigned int x, unsigned int y )
169 {
170   vertices.PushBack( Vector2( x, y ) );
171 }
172
173 void RegisterStretchProperties( TextureSet& textureSet, const char * uniformName, const NinePatchImage::StretchRanges& stretchPixels, uint16_t imageExtent)
174 {
175   uint16_t prevEnd = 0;
176   uint16_t prevFix = 0;
177   uint16_t prevStretch = 0;
178   unsigned int i = 1;
179   for( NinePatchImage::StretchRanges::ConstIterator it = stretchPixels.Begin(); it != stretchPixels.End(); ++it, ++i )
180   {
181     uint16_t start = it->GetX();
182     uint16_t end = it->GetY();
183
184     uint16_t fix = prevFix + start - prevEnd;
185     uint16_t stretch = prevStretch + end - start;
186
187     std::stringstream uniform;
188     uniform << uniformName << "[" << i << "]";
189     textureSet.RegisterProperty( uniform.str(), Vector2( fix, stretch ) );
190
191     prevEnd = end;
192     prevFix = fix;
193     prevStretch = stretch;
194   }
195
196   {
197     prevFix += imageExtent - prevEnd;
198     std::stringstream uniform;
199     uniform << uniformName << "[" << i << "]";
200     textureSet.RegisterProperty( uniform.str(), Vector2( prevFix, prevStretch ) );
201   }
202 }
203
204 } //unnamed namespace
205
206 /////////////////NPatchRenderer////////////////
207
208 NPatchRenderer::NPatchRenderer( RendererFactoryCache& factoryCache )
209 : ControlRenderer( factoryCache ),
210   mBorderOnly( false )
211 {
212 }
213
214 NPatchRenderer::~NPatchRenderer()
215 {
216 }
217
218 void NPatchRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
219 {
220   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
221   if( imageURLValue )
222   {
223     //Read the borderOnly property first since InitialiseFromImage relies on mBorderOnly to be properly set
224     Property::Value* borderOnlyValue = propertyMap.Find( BORDER_ONLY );
225     if( borderOnlyValue )
226     {
227       borderOnlyValue->Get( mBorderOnly );
228     }
229
230     if( imageURLValue->Get( mImageUrl ) )
231     {
232       NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
233       InitializeFromImage( nPatch );
234     }
235     else
236     {
237       InitializeFromBrokenImage();
238       DALI_LOG_ERROR( "The property '%s' is not a string\n", IMAGE_URL_NAME );
239     }
240   }
241 }
242
243 void NPatchRenderer::GetNaturalSize( Vector2& naturalSize ) const
244 {
245   if( mImage )
246   {
247     naturalSize.x = mImage.GetWidth();
248     naturalSize.y = mImage.GetHeight();
249   }
250   else if( !mImageUrl.empty() )
251   {
252     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
253     naturalSize.x = dimentions.GetWidth();
254     naturalSize.y = dimentions.GetHeight();
255   }
256   else
257   {
258     naturalSize = Vector2::ZERO;
259   }
260 }
261
262 void NPatchRenderer::SetClipRect( const Rect<int>& clipRect )
263 {
264   ControlRenderer::SetClipRect( clipRect );
265   //ToDo: renderer responds to the clipRect change
266 }
267
268 void NPatchRenderer::SetOffset( const Vector2& offset )
269 {
270   //ToDo: renderer applies the offset
271 }
272
273 Geometry NPatchRenderer::CreateGeometry()
274 {
275   Geometry geometry;
276   if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 )
277   {
278     if( !mBorderOnly )
279     {
280       geometry = mFactoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY );
281       if( !geometry )
282       {
283         geometry = CreateGeometry( Uint16Pair( 3, 3 ) );
284         mFactoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_GEOMETRY, geometry );
285       }
286     }
287     else
288     {
289       geometry = mFactoryCache.GetGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY );
290       if( !geometry )
291       {
292         geometry = CreateGeometryBorder( Uint16Pair( 3, 3 ) );
293         mFactoryCache.SaveGeometry( RendererFactoryCache::NINE_PATCH_BORDER_GEOMETRY, geometry );
294       }
295     }
296   }
297   else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0)
298   {
299     Uint16Pair gridSize( 2 * mStretchPixelsX.Size() + 1,  2 * mStretchPixelsY.Size() + 1 );
300     geometry = !mBorderOnly ? CreateGeometry( gridSize ) : CreateGeometryBorder( gridSize );
301   }
302
303   return geometry;
304 }
305
306 Shader NPatchRenderer::CreateShader()
307 {
308   Shader shader;
309   if( !mImpl->mCustomShader )
310   {
311     if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 )
312     {
313       shader = mFactoryCache.GetShader( RendererFactoryCache::NINE_PATCH_SHADER );
314       if( !shader )
315       {
316         shader = Shader::New( VERTEX_SHADER_3X3, FRAGMENT_SHADER );
317         mFactoryCache.SaveShader( RendererFactoryCache::NINE_PATCH_SHADER, shader );
318       }
319     }
320     else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0)
321     {
322       std::stringstream vertexShader;
323       vertexShader << "#define FACTOR_SIZE_X " << mStretchPixelsX.Size() + 2 << "\n"
324                    << "#define FACTOR_SIZE_Y " << mStretchPixelsY.Size() + 2 << "\n"
325                    << VERTEX_SHADER;
326
327       shader = Shader::New( vertexShader.str(), FRAGMENT_SHADER );
328     }
329   }
330   else
331   {
332     const char* fragmentShader = FRAGMENT_SHADER;
333     Dali::Shader::ShaderHints hints = Dali::Shader::HINT_NONE;
334
335     if( !mImpl->mCustomShader->mFragmentShader.empty() )
336     {
337       fragmentShader = mImpl->mCustomShader->mFragmentShader.c_str();
338     }
339     hints = mImpl->mCustomShader->mHints;
340
341     if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 )
342     {
343       shader = Shader::New( VERTEX_SHADER_3X3, fragmentShader, hints );
344     }
345     else if( mStretchPixelsX.Size() > 0 || mStretchPixelsY.Size() > 0)
346     {
347       std::stringstream vertexShader;
348       vertexShader << "#define FACTOR_SIZE_X " << mStretchPixelsX.Size() + 2 << "\n"
349                    << "#define FACTOR_SIZE_Y " << mStretchPixelsY.Size() + 2 << "\n"
350                    << VERTEX_SHADER;
351
352       shader = Shader::New( vertexShader.str(), fragmentShader, hints );
353     }
354   }
355
356   return shader;
357 }
358
359 void NPatchRenderer::InitializeRenderer()
360 {
361   Geometry geometry = CreateGeometry();
362   Shader shader = CreateShader();
363
364   if( !geometry || !shader )
365   {
366     DALI_LOG_ERROR("The 9 patch image '%s' doesn't have any valid stretch borders and so is not a valid 9 patch image\n", mImageUrl.c_str() );
367     InitializeFromBrokenImage();
368   }
369
370   TextureSet textureSet = TextureSet::New();
371   mImpl->mRenderer = Renderer::New( geometry, shader );
372   mImpl->mRenderer.SetTextures( textureSet );
373 }
374
375
376 void NPatchRenderer::DoSetOnStage( Actor& actor )
377 {
378   if( !mCroppedImage )
379   {
380     if( !mImageUrl.empty() )
381     {
382       NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
383       InitializeFromImage( nPatch );
384     }
385     else if( mImage )
386     {
387       InitializeFromImage( mImage );
388     }
389   }
390
391   //initialize the renderer after initializing from the image since we need to know the grid size from the image before creating the geometry
392   InitializeRenderer();
393
394   if( mCroppedImage )
395   {
396     ApplyImageToSampler();
397   }
398 }
399
400 void NPatchRenderer::DoSetOffStage( Actor& actor )
401 {
402   mCroppedImage.Reset();
403   actor.RemoveRenderer( mImpl->mRenderer );
404   mImpl->mRenderer.Reset();
405 }
406
407 void NPatchRenderer::DoCreatePropertyMap( Property::Map& map ) const
408 {
409   map.Clear();
410   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
411   if( !mImageUrl.empty() )
412   {
413     map.Insert( IMAGE_URL_NAME, mImageUrl );
414   }
415   else if( mImage )
416   {
417     map.Insert( IMAGE_URL_NAME, mImage.GetUrl() );
418   }
419   map.Insert( BORDER_ONLY, mBorderOnly );
420 }
421
422 void NPatchRenderer::ChangeRenderer( bool oldBorderOnly, size_t oldGridX, size_t oldGridY )
423 {
424   //check to see if the border style has changed
425
426   bool borderOnlyChanged = oldBorderOnly != mBorderOnly;
427   bool gridChanged = oldGridX != mStretchPixelsX.Size() || oldGridY != mStretchPixelsY.Size();
428
429   if( borderOnlyChanged || gridChanged )
430   {
431     Geometry geometry = CreateGeometry();
432     if( geometry )
433     {
434       mImpl->mRenderer.SetGeometry( geometry );
435     }
436     else
437     {
438       InitializeFromBrokenImage();
439     }
440   }
441
442   if( gridChanged )
443   {
444     Shader shader = CreateShader();
445     TextureSet textureSet;
446     if( shader )
447     {
448       textureSet = mImpl->mRenderer.GetTextures();
449       if( !textureSet )
450       {
451         InitializeFromBrokenImage();
452       }
453       mImpl->mRenderer.SetShader( shader );
454     }
455   }
456 }
457
458 void NPatchRenderer::SetImage( const std::string& imageUrl, bool borderOnly )
459 {
460   bool oldBorderOnly = mBorderOnly;
461   size_t oldGridX = mStretchPixelsX.Size();
462   size_t oldGridY = mStretchPixelsY.Size();
463
464   mBorderOnly = borderOnly;
465   mImage.Reset();
466   if( mImageUrl == imageUrl )
467   {
468     return;
469   }
470
471   mImageUrl = imageUrl;
472   if( mImpl->mRenderer )
473   {
474     NinePatchImage nPatch = NinePatchImage::New( mImageUrl );
475     InitializeFromImage( nPatch );
476
477     ChangeRenderer( oldBorderOnly, oldGridX, oldGridY );
478
479     if( mCroppedImage )
480     {
481       ApplyImageToSampler();
482     }
483   }
484 }
485
486 void NPatchRenderer::SetImage( NinePatchImage image, bool borderOnly )
487 {
488   bool oldBorderOnly = mBorderOnly;
489   size_t oldGridX = mStretchPixelsX.Size();
490   size_t oldGridY = mStretchPixelsY.Size();
491
492   mBorderOnly = borderOnly;
493   mImageUrl.empty();
494   if( mImage == image )
495   {
496     return;
497   }
498
499   mImage = image;
500   if( mImpl->mRenderer )
501   {
502     InitializeFromImage( mImage );
503     ChangeRenderer( oldBorderOnly, oldGridX, oldGridY );
504
505     if( mCroppedImage )
506     {
507       ApplyImageToSampler();
508     }
509   }
510 }
511
512 void NPatchRenderer::InitializeFromImage( NinePatchImage nPatch )
513 {
514   mCroppedImage = nPatch.CreateCroppedBufferImage();
515   if( !mCroppedImage )
516   {
517     DALI_LOG_ERROR("'%s' specify a valid 9 patch image\n", mImageUrl.c_str() );
518     InitializeFromBrokenImage();
519     return;
520   }
521
522   mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() );
523
524   mStretchPixelsX = nPatch.GetStretchPixelsX();
525   mStretchPixelsY = nPatch.GetStretchPixelsY();
526 }
527
528 void NPatchRenderer::InitializeFromBrokenImage()
529 {
530   mCroppedImage = RendererFactory::GetBrokenRendererImage();
531   mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() );
532
533   mStretchPixelsX.Clear();
534   mStretchPixelsX.PushBack( Uint16Pair( 0, mImageSize.GetWidth() ) );
535   mStretchPixelsY.Clear();
536   mStretchPixelsY.PushBack( Uint16Pair( 0, mImageSize.GetHeight() ) );
537 }
538
539 void NPatchRenderer::ApplyImageToSampler()
540 {
541   TextureSet textureSet = mImpl->mRenderer.GetTextures();
542   if( textureSet )
543   {
544     textureSet.SetImage( 0u, mCroppedImage );
545
546     if( mStretchPixelsX.Size() == 1 && mStretchPixelsY.Size() == 1 )
547     {
548       //special case for 9 patch
549       Uint16Pair stretchX = mStretchPixelsX[ 0 ];
550       Uint16Pair stretchY = mStretchPixelsY[ 0 ];
551
552       uint16_t stretchWidth = stretchX.GetY() - stretchX.GetX();
553       uint16_t stretchHeight = stretchY.GetY() - stretchY.GetX();
554
555       textureSet.RegisterProperty( "uFixed[0]", Vector2::ZERO );
556       textureSet.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
557       textureSet.RegisterProperty( "uFixed[2]", Vector2( mImageSize.GetWidth() - stretchWidth, mImageSize.GetHeight() - stretchHeight ) );
558       textureSet.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
559     }
560     else
561     {
562       textureSet.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO );
563       textureSet.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO );
564
565       RegisterStretchProperties( textureSet, "uNinePatchFactorsX", mStretchPixelsX, mImageSize.GetWidth() );
566       RegisterStretchProperties( textureSet, "uNinePatchFactorsY", mStretchPixelsY, mImageSize.GetHeight() );
567     }
568   }
569 }
570
571 Geometry NPatchRenderer::CreateGeometry( Uint16Pair gridSize )
572 {
573   uint16_t gridWidth = gridSize.GetWidth();
574   uint16_t gridHeight = gridSize.GetHeight();
575
576   // Create vertices
577   Vector< Vector2 > vertices;
578   vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
579
580   for( int y = 0; y < gridHeight + 1; ++y )
581   {
582     for( int x = 0; x < gridWidth + 1; ++x )
583     {
584       AddVertex( vertices, x, y );
585     }
586   }
587
588   // Create indices
589   //TODO: compare performance with triangle strip when Geometry supports it
590   Vector< unsigned int > indices;
591   indices.Reserve( gridWidth * gridHeight * 6 );
592
593   unsigned int rowIdx     = 0;
594   unsigned int nextRowIdx = gridWidth + 1;
595   for( int y = 0; y < gridHeight; ++y, ++nextRowIdx, ++rowIdx )
596   {
597     for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
598     {
599       AddQuadIndices( indices, rowIdx, nextRowIdx );
600     }
601   }
602
603   return GenerateGeometry( vertices, indices );
604 }
605
606 Geometry NPatchRenderer::CreateGeometryBorder( Uint16Pair gridSize )
607 {
608   uint16_t gridWidth = gridSize.GetWidth();
609   uint16_t gridHeight = gridSize.GetHeight();
610
611   // Create vertices
612   Vector< Vector2 > vertices;
613   vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
614
615   //top
616   int y = 0;
617   for(; y < 2; ++y)
618   {
619     for( int x = 0; x < gridWidth + 1; ++x )
620     {
621       AddVertex( vertices, x, y );
622     }
623   }
624
625   for(; y < gridHeight - 1; ++y)
626   {
627     //left
628     AddVertex( vertices, 0, y );
629     AddVertex( vertices, 1, y );
630
631     //right
632     AddVertex( vertices, gridWidth - 1, y );
633     AddVertex( vertices, gridWidth, y );
634   }
635
636   //bottom
637   for(; y < gridHeight + 1; ++y)
638   {
639     for( int x = 0; x < gridWidth + 1; ++x )
640     {
641       AddVertex( vertices, x, y );
642     }
643   }
644
645   // Create indices
646   //TODO: compare performance with triangle strip when Geometry supports it
647   Vector< unsigned int > indices;
648   indices.Reserve( gridWidth * gridHeight * 6 );
649
650   //top
651   unsigned int rowIdx     = 0 ;
652   unsigned int nextRowIdx = gridWidth + 1;
653   for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
654   {
655     AddQuadIndices( indices, rowIdx, nextRowIdx );
656   }
657
658   if(gridHeight > 2)
659   {
660     rowIdx     = gridWidth + 1;
661     nextRowIdx = ( gridWidth + 1 ) * 2;
662
663     unsigned increment = gridWidth - 1;
664     if(gridHeight > 3)
665     {
666       increment = 2;
667       //second row left
668       AddQuadIndices( indices, rowIdx, nextRowIdx );
669
670       rowIdx     = gridWidth * 2;
671       nextRowIdx = ( gridWidth + 1 ) * 2 + 2;
672       //second row right
673       AddQuadIndices( indices, rowIdx, nextRowIdx );
674
675       //left and right
676       rowIdx     = nextRowIdx - 2;
677       nextRowIdx = rowIdx + 4;
678       for(int y = 2; y < 2*(gridHeight - 3); ++y, rowIdx += 2, nextRowIdx += 2)
679       {
680         AddQuadIndices( indices, rowIdx, nextRowIdx );
681       }
682     }
683
684     //second row left
685     AddQuadIndices( indices, rowIdx, nextRowIdx );
686
687     rowIdx     += increment;
688     nextRowIdx += gridWidth - 1;
689     //second row right
690     AddQuadIndices( indices, rowIdx, nextRowIdx );
691   }
692
693   //bottom
694   rowIdx     = nextRowIdx - gridWidth + 1;
695   nextRowIdx = rowIdx + gridWidth + 1;
696   for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
697   {
698     AddQuadIndices( indices, rowIdx, nextRowIdx );
699   }
700
701   return GenerateGeometry( vertices, indices );
702 }
703
704 } // namespace Internal
705
706 } // namespace Toolkit
707
708 } // namespace Dali