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