Create Renderer when the Visual is created
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / npatch / npatch-visual.cpp
1 /*
2  * Copyright (c) 2021 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/devel-api/rendering/renderer-devel.h>
23 #include <dali/devel-api/adaptor-framework/image-loading.h>
24 #include <dali/integration-api/debug.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
28 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
29 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
30 #include <dali-toolkit/internal/visuals/npatch-loader.h>
31 #include <dali-toolkit/internal/visuals/rendering-addon.h>
32 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
33 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
34 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
35 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
36 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
37 #include <dali-toolkit/public-api/visuals/visual-properties.h>
38
39 namespace Dali
40 {
41
42 namespace Toolkit
43 {
44
45 namespace Internal
46 {
47
48 namespace
49 {
50
51 /**
52  * @brief Creates the geometry formed from the vertices and indices
53  *
54  * @param[in]  vertices             The vertices to generate the geometry from
55  * @param[in]  indices              The indices to generate the geometry from
56  * @return The geometry formed from the vertices and indices
57  */
58 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned short >& indices )
59 {
60   Property::Map vertexFormat;
61   vertexFormat[ "aPosition" ] = Property::VECTOR2;
62   VertexBuffer vertexBuffer = VertexBuffer::New( vertexFormat );
63   if( vertices.Size() > 0 )
64   {
65     vertexBuffer.SetData( &vertices[ 0 ], vertices.Size() );
66   }
67
68   // Create the geometry object
69   Geometry geometry = Geometry::New();
70   geometry.AddVertexBuffer( vertexBuffer );
71   if( indices.Size() > 0 )
72   {
73     geometry.SetIndexBuffer( &indices[ 0 ], indices.Size() );
74   }
75
76
77   return geometry;
78 }
79
80 /**
81  * @brief Adds the indices to form a quad composed off two triangles where the indices are organised in a grid
82  *
83  * @param[out] indices     The indices to add to
84  * @param[in]  rowIdx      The row index to start the quad
85  * @param[in]  nextRowIdx  The index to the next row
86  */
87 void AddQuadIndices( Vector< unsigned short >& indices, unsigned int rowIdx, unsigned int nextRowIdx )
88 {
89   indices.PushBack( rowIdx );
90   indices.PushBack( nextRowIdx + 1 );
91   indices.PushBack( rowIdx + 1 );
92
93   indices.PushBack( rowIdx );
94   indices.PushBack( nextRowIdx );
95   indices.PushBack( nextRowIdx + 1 );
96 }
97
98 void AddVertex( Vector< Vector2 >& vertices, unsigned int x, unsigned int y )
99 {
100   vertices.PushBack( Vector2( x, y ) );
101 }
102
103 void RegisterStretchProperties( Renderer& renderer, const char * uniformName, const NPatchUtility::StretchRanges& stretchPixels, uint16_t imageExtent)
104 {
105   uint16_t prevEnd = 0;
106   uint16_t prevFix = 0;
107   uint16_t prevStretch = 0;
108   unsigned int i = 1;
109   for( NPatchUtility::StretchRanges::ConstIterator it = stretchPixels.Begin(); it != stretchPixels.End(); ++it, ++i )
110   {
111     uint16_t start = it->GetX();
112     uint16_t end = it->GetY();
113
114     uint16_t fix = prevFix + start - prevEnd;
115     uint16_t stretch = prevStretch + end - start;
116
117     std::stringstream uniform;
118     uniform << uniformName << "[" << i << "]";
119     renderer.RegisterProperty( uniform.str(), Vector2( fix, stretch ) );
120
121     prevEnd = end;
122     prevFix = fix;
123     prevStretch = stretch;
124   }
125
126   {
127     prevFix += imageExtent - prevEnd;
128     std::stringstream uniform;
129     uniform << uniformName << "[" << i << "]";
130     renderer.RegisterProperty( uniform.str(), Vector2( prevFix, prevStretch ) );
131   }
132 }
133
134 } //unnamed namespace
135
136 /////////////////NPatchVisual////////////////
137
138 NPatchVisualPtr NPatchVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties)
139 {
140   NPatchVisualPtr nPatchVisual(new NPatchVisual(factoryCache, shaderFactory));
141   nPatchVisual->mImageUrl = imageUrl;
142   nPatchVisual->SetProperties( properties );
143   nPatchVisual->Initialize();
144   return nPatchVisual;
145 }
146
147 NPatchVisualPtr NPatchVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl)
148 {
149   NPatchVisualPtr nPatchVisual(new NPatchVisual(factoryCache, shaderFactory));
150   nPatchVisual->mImageUrl = imageUrl;
151   nPatchVisual->Initialize();
152   return nPatchVisual;
153 }
154
155 void NPatchVisual::LoadImages()
156 {
157   TextureManager& textureManager = mFactoryCache.GetTextureManager();
158   bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
159
160   if( mId == NPatchData::INVALID_NPATCH_DATA_ID && mImageUrl.IsLocalResource() )
161   {
162     bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
163     mId = mLoader.Load( textureManager, this, mImageUrl.GetUrl(), mBorder, preMultiplyOnLoad, synchronousLoading );
164
165     const NPatchData* data;
166     if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
167     {
168       EnablePreMultipliedAlpha( data->IsPreMultiplied() );
169     }
170   }
171
172   if( !mAuxiliaryPixelBuffer && mAuxiliaryUrl.IsValid() && mAuxiliaryUrl.IsLocalResource() )
173   {
174     // Load the auxiliary image
175     auto preMultiplyOnLoading = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
176     mAuxiliaryPixelBuffer = textureManager.LoadPixelBuffer( mAuxiliaryUrl, Dali::ImageDimensions(), FittingMode::DEFAULT,
177                                                             SamplingMode::BOX_THEN_LINEAR, synchronousLoading,
178                                                             this, true, preMultiplyOnLoading );
179   }
180 }
181
182 void NPatchVisual::GetNaturalSize( Vector2& naturalSize )
183 {
184   naturalSize.x = 0u;
185   naturalSize.y = 0u;
186
187   // load now if not already loaded
188   const NPatchData* data;
189   if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() != NPatchData::LoadingState::LOADING )
190   {
191     naturalSize.x = data->GetCroppedWidth();
192     naturalSize.y = data->GetCroppedHeight();
193   }
194   else
195   {
196     if( mImageUrl.IsValid() )
197     {
198       ImageDimensions dimensions = Dali::GetOriginalImageSize( mImageUrl.GetUrl() );
199       if( dimensions != ImageDimensions( 0, 0 ) )
200       {
201         naturalSize.x = dimensions.GetWidth();
202         naturalSize.y = dimensions.GetHeight();
203       }
204     }
205   }
206
207   if( mAuxiliaryPixelBuffer )
208   {
209     naturalSize.x = std::max( naturalSize.x, float(mAuxiliaryPixelBuffer.GetWidth()) );
210     naturalSize.y = std::max( naturalSize.y, float(mAuxiliaryPixelBuffer.GetHeight()) );
211   }
212 }
213
214 void NPatchVisual::DoSetProperties( const Property::Map& propertyMap )
215 {
216   // URL is already passed in via constructor
217
218   Property::Value* borderOnlyValue = propertyMap.Find( Toolkit::ImageVisual::Property::BORDER_ONLY, BORDER_ONLY );
219   if( borderOnlyValue )
220   {
221     borderOnlyValue->Get( mBorderOnly );
222   }
223
224   Property::Value* borderValue = propertyMap.Find( Toolkit::ImageVisual::Property::BORDER, BORDER );
225   if( borderValue && ! borderValue->Get( mBorder ) ) // If value exists and is rect, just set mBorder
226   {
227     // Not a rect so try vector4
228     Vector4 border;
229     if( borderValue->Get( border ) )
230     {
231       mBorder.left = static_cast< int >( border.x );
232       mBorder.right = static_cast< int >( border.y );
233       mBorder.bottom = static_cast< int >( border.z );
234       mBorder.top = static_cast< int >( border.w );
235     }
236   }
237
238   Property::Value* auxImage = propertyMap.Find( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, AUXILIARY_IMAGE_NAME );
239   if( auxImage )
240   {
241     std::string url;
242     if( auxImage->Get( url ) )
243     {
244       mAuxiliaryUrl = url;
245     }
246   }
247
248   Property::Value* auxImageAlpha = propertyMap.Find( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, AUXILIARY_IMAGE_ALPHA_NAME );
249   if( auxImageAlpha )
250   {
251     auxImageAlpha->Get( mAuxiliaryImageAlpha );
252   }
253
254   Property::Value* synchronousLoading = propertyMap.Find( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, SYNCHRONOUS_LOADING );
255   if( synchronousLoading )
256   {
257     bool sync = false;
258     synchronousLoading->Get( sync );
259     if( sync )
260     {
261       mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
262     }
263     else
264     {
265       mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
266     }
267   }
268
269   Property::Value* releasePolicy = propertyMap.Find( Toolkit::ImageVisual::Property::RELEASE_POLICY, RELEASE_POLICY_NAME );
270   if( releasePolicy )
271   {
272     releasePolicy->Get( mReleasePolicy );
273   }
274 }
275
276 void NPatchVisual::DoSetOnScene( Actor& actor )
277 {
278   // load when first go on stage
279   LoadImages();
280
281   const NPatchData* data;
282   if( mLoader.GetNPatchData( mId, data ) )
283   {
284     Geometry geometry = CreateGeometry();
285     Shader shader = CreateShader();
286
287     mImpl->mRenderer.SetGeometry(geometry);
288     mImpl->mRenderer.SetShader(shader);
289
290     mPlacementActor = actor;
291     if( data->GetLoadingState() != NPatchData::LoadingState::LOADING )
292     {
293       if( RenderingAddOn::Get().IsValid() )
294       {
295         RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->GetRenderingMap() );
296       }
297
298       ApplyTextureAndUniforms();
299       actor.AddRenderer( mImpl->mRenderer );
300       mPlacementActor.Reset();
301
302       // npatch loaded and ready to display
303       ResourceReady( Toolkit::Visual::ResourceStatus::READY );
304     }
305   }
306 }
307
308 void NPatchVisual::DoSetOffScene( Actor& actor )
309 {
310   if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
311   {
312     mLoader.Remove(mId, this);
313     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
314     mId = NPatchData::INVALID_NPATCH_DATA_ID;
315   }
316
317   actor.RemoveRenderer( mImpl->mRenderer );
318   mPlacementActor.Reset();
319 }
320
321 void NPatchVisual::OnSetTransform()
322 {
323   if( mImpl->mRenderer )
324   {
325     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
326   }
327 }
328
329 void NPatchVisual::DoCreatePropertyMap( Property::Map& map ) const
330 {
331   map.Clear();
332   bool sync = IsSynchronousLoadingRequired();
333   map.Insert( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync );
334   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::N_PATCH );
335   map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
336   map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly );
337   map.Insert( Toolkit::ImageVisual::Property::BORDER, mBorder );
338   map.Insert( Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy );
339
340   if( mAuxiliaryUrl.IsValid() )
341   {
342     map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl() );
343     map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha );
344   }
345 }
346
347 void NPatchVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
348 {
349   if( mAuxiliaryUrl.IsValid() )
350   {
351     map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl() );
352     map.Insert( Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha );
353   }
354 }
355
356 NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory)
357 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::N_PATCH),
358   mPlacementActor(),
359   mLoader(factoryCache.GetNPatchLoader()),
360   mImageVisualShaderFactory(shaderFactory),
361   mImageUrl(),
362   mAuxiliaryUrl(),
363   mId(NPatchData::INVALID_NPATCH_DATA_ID),
364   mBorderOnly(false),
365   mBorder(),
366   mAuxiliaryImageAlpha(0.0f),
367   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED)
368 {
369   EnablePreMultipliedAlpha( mFactoryCache.GetPreMultiplyOnLoad() );
370 }
371
372 NPatchVisual::~NPatchVisual()
373 {
374   if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && ( mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER ))
375   {
376     mLoader.Remove(mId, this);
377     mId = NPatchData::INVALID_NPATCH_DATA_ID;
378   }
379 }
380
381 void NPatchVisual::OnInitialize()
382 {
383   // Get basic geometry and shader
384   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
385   Shader   shader   = mImageVisualShaderFactory.GetShader(mFactoryCache, false, true, false);
386
387   mImpl->mRenderer = Renderer::New(geometry, shader);
388
389   //Register transform properties
390   mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
391 }
392
393 Geometry NPatchVisual::CreateGeometry()
394 {
395   Geometry geometry;
396   const NPatchData* data;
397   if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
398   {
399     if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
400     {
401       if( DALI_UNLIKELY( mBorderOnly ) )
402       {
403         geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY );
404       }
405       else
406       {
407         if( data->GetRenderingMap() )
408         {
409           uint32_t elementCount[2];
410           geometry = RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), Uint16Pair(3, 3), elementCount );
411           if( mImpl->mRenderer )
412           {
413             RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
414           }
415         }
416         else
417         {
418           geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY );
419         }
420       }
421     }
422     else if( data->GetStretchPixelsX().Size() > 0 || data->GetStretchPixelsY().Size() > 0)
423     {
424       Uint16Pair gridSize( 2 * data->GetStretchPixelsX().Size() + 1,  2 * data->GetStretchPixelsY().Size() + 1 );
425       if( !data->GetRenderingMap() )
426       {
427         geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize );
428       }
429       else
430       {
431         uint32_t elementCount[2];
432         geometry = !mBorderOnly ?
433                    RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), gridSize, elementCount ) : CreateBorderGeometry(gridSize );
434         if( mImpl->mRenderer )
435         {
436           RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
437         }
438       }
439     }
440   }
441   else
442   {
443     // no N patch data so use default geometry
444     geometry = GetNinePatchGeometry( VisualFactoryCache::NINE_PATCH_GEOMETRY );
445   }
446   return geometry;
447 }
448
449 Shader NPatchVisual::CreateShader()
450 {
451   Shader shader;
452   const NPatchData* data;
453   // 0 is either no data (load failed?) or no stretch regions on image
454   // for both cases we use the default shader
455   NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
456   NPatchUtility::StretchRanges::SizeType yStretchCount = 0;
457
458   auto fragmentShader = mAuxiliaryPixelBuffer ? SHADER_NPATCH_VISUAL_MASK_SHADER_FRAG
459                                               : SHADER_NPATCH_VISUAL_SHADER_FRAG;
460   auto shaderType = mAuxiliaryPixelBuffer ? VisualFactoryCache::NINE_PATCH_MASK_SHADER
461                                           : VisualFactoryCache::NINE_PATCH_SHADER;
462
463   // ask loader for the regions
464   if( mLoader.GetNPatchData( mId, data ) )
465   {
466     xStretchCount = data->GetStretchPixelsX().Count();
467     yStretchCount = data->GetStretchPixelsY().Count();
468   }
469
470   if( DALI_LIKELY( !mImpl->mCustomShader ) )
471   {
472     if( DALI_LIKELY( ( xStretchCount == 1 && yStretchCount == 1 ) ||
473                      ( xStretchCount == 0 && yStretchCount == 0 ) ) )
474     {
475       shader = mFactoryCache.GetShader( shaderType );
476       if( DALI_UNLIKELY( !shader ) )
477       {
478         shader = Shader::New( SHADER_NPATCH_VISUAL_3X3_SHADER_VERT, fragmentShader );
479         // Only cache vanilla 9 patch shaders
480         mFactoryCache.SaveShader( shaderType, shader );
481       }
482     }
483     else if( xStretchCount > 0 || yStretchCount > 0)
484     {
485       std::stringstream vertexShader;
486       vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n"
487                    << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n"
488                    << SHADER_NPATCH_VISUAL_SHADER_VERT;
489
490       shader = Shader::New( vertexShader.str(), fragmentShader );
491     }
492   }
493   else
494   {
495     Dali::Shader::Hint::Value hints = Dali::Shader::Hint::NONE;
496
497     if( !mImpl->mCustomShader->mFragmentShader.empty() )
498     {
499       fragmentShader = mImpl->mCustomShader->mFragmentShader.c_str();
500     }
501     hints = mImpl->mCustomShader->mHints;
502
503     /* Apply Custom Vertex Shader only if image is 9-patch */
504     if( ( xStretchCount == 1 && yStretchCount == 1 ) ||
505         ( xStretchCount == 0 && yStretchCount == 0 ) )
506     {
507       const char* vertexShader = SHADER_NPATCH_VISUAL_3X3_SHADER_VERT.data();
508
509       if( !mImpl->mCustomShader->mVertexShader.empty() )
510       {
511         vertexShader = mImpl->mCustomShader->mVertexShader.c_str();
512       }
513       shader = Shader::New( vertexShader, fragmentShader, hints );
514     }
515     else if( xStretchCount > 0 || yStretchCount > 0)
516     {
517       std::stringstream vertexShader;
518       vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n"
519                    << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n"
520                    << SHADER_NPATCH_VISUAL_SHADER_VERT;
521
522       shader = Shader::New( vertexShader.str(), fragmentShader, hints );
523     }
524   }
525
526   return shader;
527 }
528
529 void NPatchVisual::ApplyTextureAndUniforms()
530 {
531   const NPatchData* data;
532   TextureSet textureSet;
533
534   if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
535   {
536     textureSet = data->GetTextures();
537
538     if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
539     {
540       //special case for 9 patch
541       Uint16Pair stretchX = data->GetStretchPixelsX()[ 0 ];
542       Uint16Pair stretchY = data->GetStretchPixelsY()[ 0 ];
543
544       uint16_t stretchWidth = ( stretchX.GetY() >= stretchX.GetX() ) ? stretchX.GetY() - stretchX.GetX() : 0;
545       uint16_t stretchHeight = ( stretchY.GetY() >= stretchY.GetX() ) ? stretchY.GetY() - stretchY.GetX() : 0;
546
547       mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO );
548       mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
549       mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->GetCroppedWidth() - stretchWidth, data->GetCroppedHeight() - stretchHeight ) );
550       mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
551     }
552     else
553     {
554       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO );
555       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO );
556
557       RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->GetStretchPixelsX(), data->GetCroppedWidth() );
558       RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->GetStretchPixelsY(), data->GetCroppedHeight() );
559     }
560   }
561   else
562   {
563     DALI_LOG_ERROR("The N patch image '%s' is not a valid N patch image\n", mImageUrl.GetUrl().c_str() );
564     textureSet = TextureSet::New();
565
566     Texture croppedImage = mFactoryCache.GetBrokenVisualImage();
567     textureSet.SetTexture( 0u, croppedImage );
568     mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO );
569     mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2::ZERO );
570     mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2::ZERO );
571     mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( croppedImage.GetWidth(), croppedImage.GetHeight() ) );
572   }
573
574   if( mAuxiliaryPixelBuffer )
575   {
576     // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
577     // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
578     // on GL interpolation alone.
579     if( mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
580         mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight() )
581     {
582       mAuxiliaryPixelBuffer.Resize( data->GetCroppedWidth(), data->GetCroppedHeight() );
583     }
584
585     // Note, this resets mAuxiliaryPixelBuffer handle
586     auto auxiliaryPixelData = Devel::PixelBuffer::Convert( mAuxiliaryPixelBuffer );
587
588     auto texture = Texture::New( TextureType::TEXTURE_2D,
589                                  auxiliaryPixelData.GetPixelFormat(), auxiliaryPixelData.GetWidth(),
590                                  auxiliaryPixelData.GetHeight() );
591     texture.Upload( auxiliaryPixelData );
592     textureSet.SetTexture( 1, texture );
593     mImpl->mRenderer.RegisterProperty( DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA,
594                                        AUXILIARY_IMAGE_ALPHA_NAME, mAuxiliaryImageAlpha );
595   }
596   mImpl->mRenderer.SetTextures( textureSet );
597
598   // Register transform properties
599   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
600 }
601
602 Geometry NPatchVisual::GetNinePatchGeometry( VisualFactoryCache::GeometryType subType )
603 {
604   Geometry geometry = mFactoryCache.GetGeometry( subType );
605   if( !geometry )
606   {
607     if( DALI_LIKELY( VisualFactoryCache::NINE_PATCH_GEOMETRY == subType ) )
608     {
609       geometry = CreateGridGeometry( Uint16Pair( 3, 3 ) );
610     }
611     else if( VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY == subType )
612     {
613       geometry = CreateBorderGeometry( Uint16Pair( 3, 3 ) );
614     }
615     mFactoryCache.SaveGeometry( subType, geometry );
616   }
617   return geometry;
618 }
619
620 Geometry NPatchVisual::CreateGridGeometry( Uint16Pair gridSize )
621 {
622   uint16_t gridWidth = gridSize.GetWidth();
623   uint16_t gridHeight = gridSize.GetHeight();
624
625   // Create vertices
626   Vector< Vector2 > vertices;
627   vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
628
629   for( int y = 0; y < gridHeight + 1; ++y )
630   {
631     for( int x = 0; x < gridWidth + 1; ++x )
632     {
633       AddVertex( vertices, x, y );
634     }
635   }
636
637   // Create indices
638   Vector< unsigned short > indices;
639   indices.Reserve( gridWidth * gridHeight * 6 );
640
641   unsigned int rowIdx     = 0;
642   unsigned int nextRowIdx = gridWidth + 1;
643   for( int y = 0; y < gridHeight; ++y, ++nextRowIdx, ++rowIdx )
644   {
645     for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
646     {
647       AddQuadIndices( indices, rowIdx, nextRowIdx );
648     }
649   }
650
651   return GenerateGeometry( vertices, indices );
652 }
653
654 Geometry NPatchVisual::CreateBorderGeometry( Uint16Pair gridSize )
655 {
656   uint16_t gridWidth = gridSize.GetWidth();
657   uint16_t gridHeight = gridSize.GetHeight();
658
659   // Create vertices
660   Vector< Vector2 > vertices;
661   vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
662
663   //top
664   int y = 0;
665   for(; y < 2; ++y)
666   {
667     for( int x = 0; x < gridWidth + 1; ++x )
668     {
669       AddVertex( vertices, x, y );
670     }
671   }
672
673   for(; y < gridHeight - 1; ++y)
674   {
675     //left
676     AddVertex( vertices, 0, y );
677     AddVertex( vertices, 1, y );
678
679     //right
680     AddVertex( vertices, gridWidth - 1, y );
681     AddVertex( vertices, gridWidth, y );
682   }
683
684   //bottom
685   for(; y < gridHeight + 1; ++y)
686   {
687     for( int x = 0; x < gridWidth + 1; ++x )
688     {
689       AddVertex( vertices, x, y );
690     }
691   }
692
693   // Create indices
694   Vector< unsigned short > indices;
695   indices.Reserve( gridWidth * gridHeight * 6 );
696
697   //top
698   unsigned int rowIdx     = 0 ;
699   unsigned int nextRowIdx = gridWidth + 1;
700   for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
701   {
702     AddQuadIndices( indices, rowIdx, nextRowIdx );
703   }
704
705   if(gridHeight > 2)
706   {
707     rowIdx     = gridWidth + 1;
708     nextRowIdx = ( gridWidth + 1 ) * 2;
709
710     unsigned increment = gridWidth - 1;
711     if(gridHeight > 3)
712     {
713       increment = 2;
714       //second row left
715       AddQuadIndices( indices, rowIdx, nextRowIdx );
716
717       rowIdx     = gridWidth * 2;
718       nextRowIdx = ( gridWidth + 1 ) * 2 + 2;
719       //second row right
720       AddQuadIndices( indices, rowIdx, nextRowIdx );
721
722       //left and right
723       rowIdx     = nextRowIdx - 2;
724       nextRowIdx = rowIdx + 4;
725       for(int y = 2; y < 2*(gridHeight - 3); ++y, rowIdx += 2, nextRowIdx += 2)
726       {
727         AddQuadIndices( indices, rowIdx, nextRowIdx );
728       }
729     }
730
731     //second row left
732     AddQuadIndices( indices, rowIdx, nextRowIdx );
733
734     rowIdx     += increment;
735     nextRowIdx += gridWidth - 1;
736     //second row right
737     AddQuadIndices( indices, rowIdx, nextRowIdx );
738   }
739
740   //bottom
741   rowIdx     = nextRowIdx - gridWidth + 1;
742   nextRowIdx = rowIdx + gridWidth + 1;
743   for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
744   {
745     AddQuadIndices( indices, rowIdx, nextRowIdx );
746   }
747
748   return GenerateGeometry( vertices, indices );
749 }
750
751 void NPatchVisual::SetResource()
752 {
753   const NPatchData* data;
754   if( mImpl->mRenderer && mLoader.GetNPatchData( mId, data ) )
755   {
756     Geometry geometry = CreateGeometry();
757     Shader shader = CreateShader();
758
759     mImpl->mRenderer.SetGeometry( geometry );
760     mImpl->mRenderer.SetShader( shader );
761
762     Actor actor = mPlacementActor.GetHandle();
763     if( actor )
764     {
765       ApplyTextureAndUniforms();
766       actor.AddRenderer( mImpl->mRenderer );
767       mPlacementActor.Reset();
768
769       // npatch loaded and ready to display
770       ResourceReady( Toolkit::Visual::ResourceStatus::READY );
771     }
772   }
773 }
774
775 void NPatchVisual::UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied )
776 {
777   EnablePreMultipliedAlpha( preMultiplied );
778   if(!loadSuccess)
779   {
780     // Image loaded and ready to display
781     ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
782   }
783
784   if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
785   {
786     SetResource();
787   }
788 }
789
790 void NPatchVisual::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
791 {
792   if( loadSuccess && url.GetUrl() == mAuxiliaryUrl.GetUrl() )
793   {
794     mAuxiliaryPixelBuffer = pixelBuffer;
795     SetResource();
796   }
797   else
798   {
799     // Image loaded and ready to display
800     ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
801   }
802 }
803
804 } // namespace Internal
805
806 } // namespace Toolkit
807
808 } // namespace Dali