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