2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "npatch-visual.h"
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/common/stage.h>
24 #include <dali/devel-api/rendering/renderer-devel.h>
25 #include <dali/integration-api/debug.h>
28 #include <dali-toolkit/devel-api/utility/npatch-helper.h>
29 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
30 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
31 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
32 #include <dali-toolkit/internal/visuals/npatch-loader.h>
33 #include <dali-toolkit/internal/visuals/rendering-addon.h>
34 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
35 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
36 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
37 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
38 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
39 #include <dali-toolkit/public-api/visuals/visual-properties.h>
49 const int CUSTOM_PROPERTY_COUNT(5); // fixed(3),stretch,aux
52 /////////////////NPatchVisual////////////////
54 NPatchVisualPtr NPatchVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties)
56 NPatchVisualPtr nPatchVisual(new NPatchVisual(factoryCache, shaderFactory));
57 nPatchVisual->mImageUrl = imageUrl;
58 nPatchVisual->SetProperties(properties);
59 nPatchVisual->Initialize();
63 NPatchVisualPtr NPatchVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl)
65 NPatchVisualPtr nPatchVisual(new NPatchVisual(factoryCache, shaderFactory));
66 nPatchVisual->mImageUrl = imageUrl;
67 nPatchVisual->Initialize();
71 void NPatchVisual::LoadImages()
73 TextureManager& textureManager = mFactoryCache.GetTextureManager();
74 bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
76 if(mId == NPatchData::INVALID_NPATCH_DATA_ID && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()))
78 bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
79 mId = mLoader.Load(textureManager, this, mImageUrl, mBorder, preMultiplyOnLoad, synchronousLoading);
81 const NPatchData* data;
82 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
84 EnablePreMultipliedAlpha(data->IsPreMultiplied());
88 if(!mAuxiliaryPixelBuffer && mAuxiliaryUrl.IsValid() && (mAuxiliaryUrl.IsLocalResource() || mAuxiliaryUrl.IsBufferResource()))
90 // Load the auxiliary image
91 auto preMultiplyOnLoading = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
92 mAuxiliaryPixelBuffer = textureManager.LoadPixelBuffer(mAuxiliaryUrl, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, this, true, preMultiplyOnLoading);
94 // If synchronousLoading is true, we can check the auxiliaryResource's statue now.
95 if(synchronousLoading)
97 mAuxiliaryResourceStatus = mAuxiliaryPixelBuffer ? Toolkit::Visual::ResourceStatus::READY : Toolkit::Visual::ResourceStatus::FAILED;
102 void NPatchVisual::GetNaturalSize(Vector2& naturalSize)
107 // load now if not already loaded
108 const NPatchData* data;
109 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
111 naturalSize.x = data->GetCroppedWidth();
112 naturalSize.y = data->GetCroppedHeight();
116 if(mImageUrl.IsValid())
118 ImageDimensions dimensions = Dali::GetOriginalImageSize(mImageUrl.GetUrl());
119 if(dimensions != ImageDimensions(0, 0))
121 naturalSize.x = dimensions.GetWidth();
122 naturalSize.y = dimensions.GetHeight();
127 if(mAuxiliaryPixelBuffer)
129 naturalSize.x = std::max(naturalSize.x, float(mAuxiliaryPixelBuffer.GetWidth()));
130 naturalSize.y = std::max(naturalSize.y, float(mAuxiliaryPixelBuffer.GetHeight()));
134 void NPatchVisual::DoSetProperties(const Property::Map& propertyMap)
136 // URL is already passed in via constructor
138 Property::Value* borderOnlyValue = propertyMap.Find(Toolkit::ImageVisual::Property::BORDER_ONLY, BORDER_ONLY);
141 borderOnlyValue->Get(mBorderOnly);
144 Property::Value* borderValue = propertyMap.Find(Toolkit::ImageVisual::Property::BORDER, BORDER);
145 if(borderValue && !borderValue->Get(mBorder)) // If value exists and is rect, just set mBorder
147 // Not a rect so try vector4
149 if(borderValue->Get(border))
151 mBorder.left = static_cast<int>(border.x);
152 mBorder.right = static_cast<int>(border.y);
153 mBorder.bottom = static_cast<int>(border.z);
154 mBorder.top = static_cast<int>(border.w);
158 Property::Value* auxImage = propertyMap.Find(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, AUXILIARY_IMAGE_NAME);
162 if(auxImage->Get(url))
168 Property::Value* auxImageAlpha = propertyMap.Find(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, AUXILIARY_IMAGE_ALPHA_NAME);
171 auxImageAlpha->Get(mAuxiliaryImageAlpha);
174 Property::Value* synchronousLoading = propertyMap.Find(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, SYNCHRONOUS_LOADING);
175 if(synchronousLoading)
178 synchronousLoading->Get(sync);
181 mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
185 mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
189 Property::Value* releasePolicy = propertyMap.Find(Toolkit::ImageVisual::Property::RELEASE_POLICY, RELEASE_POLICY_NAME);
192 releasePolicy->Get(mReleasePolicy);
196 void NPatchVisual::DoSetOnScene(Actor& actor)
198 // load when first go on stage
201 const NPatchData* data;
202 if(mLoader.GetNPatchData(mId, data))
204 Geometry geometry = CreateGeometry();
205 Shader shader = CreateShader();
207 mImpl->mRenderer.SetGeometry(geometry);
208 mImpl->mRenderer.SetShader(shader);
210 mPlacementActor = actor;
211 // If all reasources are already loaded, apply textures and uniforms now
212 // else, will be completed uploaded at LoadComplete function asynchronously.
213 if(data->GetLoadingState() != NPatchData::LoadingState::LOADING &&
214 (!mAuxiliaryUrl.IsValid() || mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::PREPARING))
216 if(RenderingAddOn::Get().IsValid())
218 RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
221 ApplyTextureAndUniforms();
222 actor.AddRenderer(mImpl->mRenderer);
223 mPlacementActor.Reset();
225 // npatch loaded and ready to display
226 if(data->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE ||
227 (mAuxiliaryUrl.IsValid() && mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::READY))
229 ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
233 ResourceReady(Toolkit::Visual::ResourceStatus::READY);
239 void NPatchVisual::DoSetOffScene(Actor& actor)
241 if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
243 mLoader.Remove(mId, this);
244 mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
245 mId = NPatchData::INVALID_NPATCH_DATA_ID;
248 actor.RemoveRenderer(mImpl->mRenderer);
249 mPlacementActor.Reset();
252 void NPatchVisual::OnSetTransform()
256 mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
260 bool NPatchVisual::IsResourceReady() const
262 return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
263 mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
266 void NPatchVisual::DoCreatePropertyMap(Property::Map& map) const
269 bool sync = IsSynchronousLoadingRequired();
270 map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
271 map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::N_PATCH);
272 map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
273 map.Insert(Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly);
274 map.Insert(Toolkit::ImageVisual::Property::BORDER, mBorder);
275 map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
277 if(mAuxiliaryUrl.IsValid())
279 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl());
280 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha);
284 void NPatchVisual::DoCreateInstancePropertyMap(Property::Map& map) const
286 if(mAuxiliaryUrl.IsValid())
288 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl());
289 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha);
293 NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory)
294 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::N_PATCH),
296 mLoader(factoryCache.GetNPatchLoader()),
297 mImageVisualShaderFactory(shaderFactory),
300 mId(NPatchData::INVALID_NPATCH_DATA_ID),
301 mAuxiliaryResourceStatus(Toolkit::Visual::ResourceStatus::PREPARING),
304 mAuxiliaryImageAlpha(0.0f),
305 mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED)
307 EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad());
310 NPatchVisual::~NPatchVisual()
312 if(Stage::IsInstalled() && (mId != NPatchData::INVALID_NPATCH_DATA_ID) && (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER))
314 mLoader.Remove(mId, this);
315 mId = NPatchData::INVALID_NPATCH_DATA_ID;
319 void NPatchVisual::OnInitialize()
321 // Get basic geometry and shader
322 Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
323 Shader shader = mImageVisualShaderFactory.GetShader(
325 ImageVisualShaderFeature::FeatureBuilder());
327 mImpl->mRenderer = VisualRenderer::New(geometry, shader);
328 mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
330 //Register transform properties
331 mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
334 Geometry NPatchVisual::CreateGeometry()
337 const NPatchData* data;
338 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
340 if(data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1)
342 if(DALI_UNLIKELY(mBorderOnly))
344 geometry = GetNinePatchGeometry(VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY);
348 if(data->GetRenderingMap())
350 uint32_t elementCount[2];
351 geometry = RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), Uint16Pair(3, 3), elementCount);
354 RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
359 geometry = GetNinePatchGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY);
363 else if(data->GetStretchPixelsX().Size() > 0 || data->GetStretchPixelsY().Size() > 0)
365 Uint16Pair gridSize(2 * data->GetStretchPixelsX().Size() + 1, 2 * data->GetStretchPixelsY().Size() + 1);
366 if(!data->GetRenderingMap())
368 geometry = !mBorderOnly ? NPatchHelper::CreateGridGeometry(gridSize) : NPatchHelper::CreateBorderGeometry(gridSize);
372 uint32_t elementCount[2];
373 geometry = !mBorderOnly ? RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), gridSize, elementCount) : NPatchHelper::CreateBorderGeometry(gridSize);
376 RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
383 // no N patch data so use default geometry
384 geometry = GetNinePatchGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY);
389 Shader NPatchVisual::CreateShader()
392 const NPatchData* data;
393 // 0 is either no data (load failed?) or no stretch regions on image
394 // for both cases we use the default shader
395 NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
396 NPatchUtility::StretchRanges::SizeType yStretchCount = 0;
398 auto fragmentShader = mAuxiliaryPixelBuffer ? SHADER_NPATCH_VISUAL_MASK_SHADER_FRAG
399 : SHADER_NPATCH_VISUAL_SHADER_FRAG;
400 auto shaderType = mAuxiliaryPixelBuffer ? VisualFactoryCache::NINE_PATCH_MASK_SHADER
401 : VisualFactoryCache::NINE_PATCH_SHADER;
403 // ask loader for the regions
404 if(mLoader.GetNPatchData(mId, data))
406 xStretchCount = data->GetStretchPixelsX().Count();
407 yStretchCount = data->GetStretchPixelsY().Count();
410 if(DALI_LIKELY(!mImpl->mCustomShader))
412 if(DALI_LIKELY((xStretchCount == 1 && yStretchCount == 1) ||
413 (xStretchCount == 0 && yStretchCount == 0)))
415 shader = mFactoryCache.GetShader(shaderType);
416 if(DALI_UNLIKELY(!shader))
418 shader = Shader::New(SHADER_NPATCH_VISUAL_3X3_SHADER_VERT, fragmentShader);
419 // Only cache vanilla 9 patch shaders
420 mFactoryCache.SaveShader(shaderType, shader);
423 else if(xStretchCount > 0 || yStretchCount > 0)
425 std::stringstream vertexShader;
426 vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n"
427 << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n"
428 << SHADER_NPATCH_VISUAL_SHADER_VERT;
430 shader = Shader::New(vertexShader.str(), fragmentShader);
435 Dali::Shader::Hint::Value hints = Dali::Shader::Hint::NONE;
437 if(!mImpl->mCustomShader->mFragmentShader.empty())
439 fragmentShader = mImpl->mCustomShader->mFragmentShader.c_str();
441 hints = mImpl->mCustomShader->mHints;
443 /* Apply Custom Vertex Shader only if image is 9-patch */
444 if((xStretchCount == 1 && yStretchCount == 1) ||
445 (xStretchCount == 0 && yStretchCount == 0))
447 const char* vertexShader = SHADER_NPATCH_VISUAL_3X3_SHADER_VERT.data();
449 if(!mImpl->mCustomShader->mVertexShader.empty())
451 vertexShader = mImpl->mCustomShader->mVertexShader.c_str();
453 shader = Shader::New(vertexShader, fragmentShader, hints);
455 else if(xStretchCount > 0 || yStretchCount > 0)
457 std::stringstream vertexShader;
458 vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n"
459 << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n"
460 << SHADER_NPATCH_VISUAL_SHADER_VERT;
462 shader = Shader::New(vertexShader.str(), fragmentShader, hints);
469 void NPatchVisual::ApplyTextureAndUniforms()
471 const NPatchData* data;
472 TextureSet textureSet;
474 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
476 textureSet = data->GetTextures();
477 NPatchHelper::ApplyTextureAndUniforms(mImpl->mRenderer, data);
479 if(mAuxiliaryPixelBuffer)
481 // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
482 // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
483 // on GL interpolation alone.
484 if(mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
485 mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight())
487 mAuxiliaryPixelBuffer.Resize(data->GetCroppedWidth(), data->GetCroppedHeight());
490 // Note, this resets mAuxiliaryPixelBuffer handle
491 auto auxiliaryPixelData = Devel::PixelBuffer::Convert(mAuxiliaryPixelBuffer);
493 auto texture = Texture::New(TextureType::TEXTURE_2D,
494 auxiliaryPixelData.GetPixelFormat(),
495 auxiliaryPixelData.GetWidth(),
496 auxiliaryPixelData.GetHeight());
497 texture.Upload(auxiliaryPixelData);
499 // TODO : This code exist due to the texture cache manager hold TextureSet, not Texture.
500 // If we call textureSet.SetTexture(1, texture) directly, the cached TextureSet also be changed.
501 // We should make pass utc-Dali-VisualFactory.cpp UtcDaliNPatchVisualAuxiliaryImage02().
502 TextureSet tempTextureSet = TextureSet::New();
503 tempTextureSet.SetTexture(0, textureSet.GetTexture(0));
504 tempTextureSet.SetTexture(1, texture);
505 textureSet = tempTextureSet;
507 mImpl->mRenderer.RegisterProperty(DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA,
508 AUXILIARY_IMAGE_ALPHA_NAME,
509 mAuxiliaryImageAlpha);
511 mImpl->mRenderer.SetTextures(textureSet);
515 DALI_LOG_ERROR("The N patch image '%s' is not a valid N patch image\n", mImageUrl.GetUrl().c_str());
516 textureSet = TextureSet::New();
518 Actor actor = mPlacementActor.GetHandle();
519 Vector2 imageSize = Vector2::ZERO;
522 imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
524 mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize, false);
527 // Register transform properties
528 mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
531 Geometry NPatchVisual::GetNinePatchGeometry(VisualFactoryCache::GeometryType subType)
533 Geometry geometry = mFactoryCache.GetGeometry(subType);
536 if(DALI_LIKELY(VisualFactoryCache::NINE_PATCH_GEOMETRY == subType))
538 geometry = NPatchHelper::CreateGridGeometry(Uint16Pair(3, 3));
540 else if(VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY == subType)
542 geometry = NPatchHelper::CreateBorderGeometry(Uint16Pair(3, 3));
544 mFactoryCache.SaveGeometry(subType, geometry);
549 void NPatchVisual::SetResource()
551 Geometry geometry = CreateGeometry();
552 Shader shader = CreateShader();
554 mImpl->mRenderer.SetGeometry(geometry);
555 mImpl->mRenderer.SetShader(shader);
557 Actor actor = mPlacementActor.GetHandle();
560 ApplyTextureAndUniforms();
561 actor.AddRenderer(mImpl->mRenderer);
562 mPlacementActor.Reset();
566 void NPatchVisual::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
568 if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE) // For the Url.
572 EnablePreMultipliedAlpha(textureInformation.preMultiplied);
575 else // For the AuxiliaryUrl : ReturnType::PIXEL_BUFFER
577 if(loadSuccess && textureInformation.url == mAuxiliaryUrl.GetUrl())
579 mAuxiliaryPixelBuffer = textureInformation.pixelBuffer;
580 mAuxiliaryResourceStatus = Toolkit::Visual::ResourceStatus::READY;
584 mAuxiliaryResourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
587 // If auxiliaryUrl didn't set || auxiliaryUrl load done.
588 if(!mAuxiliaryUrl.IsValid() || mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::PREPARING)
590 const NPatchData* data;
591 // and.. If Url loading done.
592 if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
595 // npatch loaded and ready to display
596 if(data->GetLoadingState() != NPatchData::LoadingState::LOAD_COMPLETE ||
597 (mAuxiliaryUrl.IsValid() && mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::READY))
599 ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
603 ResourceReady(Toolkit::Visual::ResourceStatus::READY);
609 } // namespace Internal
611 } // namespace Toolkit