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