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