2 * Copyright (c) 2021 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/rendering/renderer-devel.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/devel-api/common/stage.h>
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/devel-api/utility/npatch-helper.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>
48 /////////////////NPatchVisual////////////////
50 NPatchVisualPtr NPatchVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties)
52 NPatchVisualPtr nPatchVisual(new NPatchVisual(factoryCache, shaderFactory));
53 nPatchVisual->mImageUrl = imageUrl;
54 nPatchVisual->SetProperties(properties);
55 nPatchVisual->Initialize();
59 NPatchVisualPtr NPatchVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl)
61 NPatchVisualPtr nPatchVisual(new NPatchVisual(factoryCache, shaderFactory));
62 nPatchVisual->mImageUrl = imageUrl;
63 nPatchVisual->Initialize();
67 void NPatchVisual::LoadImages()
69 TextureManager& textureManager = mFactoryCache.GetTextureManager();
70 bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
72 if(mId == NPatchData::INVALID_NPATCH_DATA_ID && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()))
74 bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
75 mId = mLoader.Load(textureManager, this, mImageUrl, mBorder, preMultiplyOnLoad, synchronousLoading);
77 const NPatchData* data;
78 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
80 EnablePreMultipliedAlpha(data->IsPreMultiplied());
84 if(!mAuxiliaryPixelBuffer && mAuxiliaryUrl.IsValid() && (mAuxiliaryUrl.IsLocalResource() || mAuxiliaryUrl.IsBufferResource()))
86 // Load the auxiliary image
87 auto preMultiplyOnLoading = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
88 mAuxiliaryPixelBuffer = textureManager.LoadPixelBuffer(mAuxiliaryUrl, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, this, true, preMultiplyOnLoading);
92 void NPatchVisual::GetNaturalSize(Vector2& naturalSize)
97 // load now if not already loaded
98 const NPatchData* data;
99 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
101 naturalSize.x = data->GetCroppedWidth();
102 naturalSize.y = data->GetCroppedHeight();
106 if(mImageUrl.IsValid())
108 ImageDimensions dimensions = Dali::GetOriginalImageSize(mImageUrl.GetUrl());
109 if(dimensions != ImageDimensions(0, 0))
111 naturalSize.x = dimensions.GetWidth();
112 naturalSize.y = dimensions.GetHeight();
117 if(mAuxiliaryPixelBuffer)
119 naturalSize.x = std::max(naturalSize.x, float(mAuxiliaryPixelBuffer.GetWidth()));
120 naturalSize.y = std::max(naturalSize.y, float(mAuxiliaryPixelBuffer.GetHeight()));
124 void NPatchVisual::DoSetProperties(const Property::Map& propertyMap)
126 // URL is already passed in via constructor
128 Property::Value* borderOnlyValue = propertyMap.Find(Toolkit::ImageVisual::Property::BORDER_ONLY, BORDER_ONLY);
131 borderOnlyValue->Get(mBorderOnly);
134 Property::Value* borderValue = propertyMap.Find(Toolkit::ImageVisual::Property::BORDER, BORDER);
135 if(borderValue && !borderValue->Get(mBorder)) // If value exists and is rect, just set mBorder
137 // Not a rect so try vector4
139 if(borderValue->Get(border))
141 mBorder.left = static_cast<int>(border.x);
142 mBorder.right = static_cast<int>(border.y);
143 mBorder.bottom = static_cast<int>(border.z);
144 mBorder.top = static_cast<int>(border.w);
148 Property::Value* auxImage = propertyMap.Find(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, AUXILIARY_IMAGE_NAME);
152 if(auxImage->Get(url))
158 Property::Value* auxImageAlpha = propertyMap.Find(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, AUXILIARY_IMAGE_ALPHA_NAME);
161 auxImageAlpha->Get(mAuxiliaryImageAlpha);
164 Property::Value* synchronousLoading = propertyMap.Find(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, SYNCHRONOUS_LOADING);
165 if(synchronousLoading)
168 synchronousLoading->Get(sync);
171 mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
175 mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
179 Property::Value* releasePolicy = propertyMap.Find(Toolkit::ImageVisual::Property::RELEASE_POLICY, RELEASE_POLICY_NAME);
182 releasePolicy->Get(mReleasePolicy);
186 void NPatchVisual::DoSetOnScene(Actor& actor)
188 // load when first go on stage
191 const NPatchData* data;
192 if(mLoader.GetNPatchData(mId, data))
194 Geometry geometry = CreateGeometry();
195 Shader shader = CreateShader();
197 mImpl->mRenderer.SetGeometry(geometry);
198 mImpl->mRenderer.SetShader(shader);
200 mPlacementActor = actor;
201 if(data->GetLoadingState() != NPatchData::LoadingState::LOADING)
203 if(RenderingAddOn::Get().IsValid())
205 RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
208 ApplyTextureAndUniforms();
209 actor.AddRenderer(mImpl->mRenderer);
210 mPlacementActor.Reset();
212 // npatch loaded and ready to display
213 ResourceReady(Toolkit::Visual::ResourceStatus::READY);
218 void NPatchVisual::DoSetOffScene(Actor& actor)
220 if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
222 mLoader.Remove(mId, this);
223 mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
224 mId = NPatchData::INVALID_NPATCH_DATA_ID;
227 actor.RemoveRenderer(mImpl->mRenderer);
228 mPlacementActor.Reset();
231 void NPatchVisual::OnSetTransform()
235 mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
239 void NPatchVisual::DoCreatePropertyMap(Property::Map& map) const
242 bool sync = IsSynchronousLoadingRequired();
243 map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
244 map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::N_PATCH);
245 map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
246 map.Insert(Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly);
247 map.Insert(Toolkit::ImageVisual::Property::BORDER, mBorder);
248 map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
250 if(mAuxiliaryUrl.IsValid())
252 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl());
253 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha);
257 void NPatchVisual::DoCreateInstancePropertyMap(Property::Map& map) const
259 if(mAuxiliaryUrl.IsValid())
261 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE, mAuxiliaryUrl.GetUrl());
262 map.Insert(Toolkit::DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA, mAuxiliaryImageAlpha);
266 NPatchVisual::NPatchVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory)
267 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::N_PATCH),
269 mLoader(factoryCache.GetNPatchLoader()),
270 mImageVisualShaderFactory(shaderFactory),
273 mId(NPatchData::INVALID_NPATCH_DATA_ID),
276 mAuxiliaryImageAlpha(0.0f),
277 mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED)
279 EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad());
282 NPatchVisual::~NPatchVisual()
284 if(Stage::IsInstalled() && (mId != NPatchData::INVALID_NPATCH_DATA_ID) && (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER))
286 mLoader.Remove(mId, this);
287 mId = NPatchData::INVALID_NPATCH_DATA_ID;
291 void NPatchVisual::OnInitialize()
293 // Get basic geometry and shader
294 Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
295 Shader shader = mImageVisualShaderFactory.GetShader(
297 ImageVisualShaderFeature::FeatureBuilder()
300 mImpl->mRenderer = Renderer::New(geometry, shader);
302 //Register transform properties
303 mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
306 Geometry NPatchVisual::CreateGeometry()
309 const NPatchData* data;
310 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
312 if(data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1)
314 if(DALI_UNLIKELY(mBorderOnly))
316 geometry = GetNinePatchGeometry(VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY);
320 if(data->GetRenderingMap())
322 uint32_t elementCount[2];
323 geometry = RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), Uint16Pair(3, 3), elementCount);
326 RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
331 geometry = GetNinePatchGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY);
335 else if(data->GetStretchPixelsX().Size() > 0 || data->GetStretchPixelsY().Size() > 0)
337 Uint16Pair gridSize(2 * data->GetStretchPixelsX().Size() + 1, 2 * data->GetStretchPixelsY().Size() + 1);
338 if(!data->GetRenderingMap())
340 geometry = !mBorderOnly ? NPatchHelper::CreateGridGeometry(gridSize) : NPatchHelper::CreateBorderGeometry(gridSize);
344 uint32_t elementCount[2];
345 geometry = !mBorderOnly ? RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), gridSize, elementCount) : NPatchHelper::CreateBorderGeometry(gridSize);
348 RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
355 // no N patch data so use default geometry
356 geometry = GetNinePatchGeometry(VisualFactoryCache::NINE_PATCH_GEOMETRY);
361 Shader NPatchVisual::CreateShader()
364 const NPatchData* data;
365 // 0 is either no data (load failed?) or no stretch regions on image
366 // for both cases we use the default shader
367 NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
368 NPatchUtility::StretchRanges::SizeType yStretchCount = 0;
370 auto fragmentShader = mAuxiliaryPixelBuffer ? SHADER_NPATCH_VISUAL_MASK_SHADER_FRAG
371 : SHADER_NPATCH_VISUAL_SHADER_FRAG;
372 auto shaderType = mAuxiliaryPixelBuffer ? VisualFactoryCache::NINE_PATCH_MASK_SHADER
373 : VisualFactoryCache::NINE_PATCH_SHADER;
375 // ask loader for the regions
376 if(mLoader.GetNPatchData(mId, data))
378 xStretchCount = data->GetStretchPixelsX().Count();
379 yStretchCount = data->GetStretchPixelsY().Count();
382 if(DALI_LIKELY(!mImpl->mCustomShader))
384 if(DALI_LIKELY((xStretchCount == 1 && yStretchCount == 1) ||
385 (xStretchCount == 0 && yStretchCount == 0)))
387 shader = mFactoryCache.GetShader(shaderType);
388 if(DALI_UNLIKELY(!shader))
390 shader = Shader::New(SHADER_NPATCH_VISUAL_3X3_SHADER_VERT, fragmentShader);
391 // Only cache vanilla 9 patch shaders
392 mFactoryCache.SaveShader(shaderType, shader);
395 else if(xStretchCount > 0 || yStretchCount > 0)
397 std::stringstream vertexShader;
398 vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n"
399 << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n"
400 << SHADER_NPATCH_VISUAL_SHADER_VERT;
402 shader = Shader::New(vertexShader.str(), fragmentShader);
407 Dali::Shader::Hint::Value hints = Dali::Shader::Hint::NONE;
409 if(!mImpl->mCustomShader->mFragmentShader.empty())
411 fragmentShader = mImpl->mCustomShader->mFragmentShader.c_str();
413 hints = mImpl->mCustomShader->mHints;
415 /* Apply Custom Vertex Shader only if image is 9-patch */
416 if((xStretchCount == 1 && yStretchCount == 1) ||
417 (xStretchCount == 0 && yStretchCount == 0))
419 const char* vertexShader = SHADER_NPATCH_VISUAL_3X3_SHADER_VERT.data();
421 if(!mImpl->mCustomShader->mVertexShader.empty())
423 vertexShader = mImpl->mCustomShader->mVertexShader.c_str();
425 shader = Shader::New(vertexShader, fragmentShader, hints);
427 else if(xStretchCount > 0 || yStretchCount > 0)
429 std::stringstream vertexShader;
430 vertexShader << "#define FACTOR_SIZE_X " << xStretchCount + 2 << "\n"
431 << "#define FACTOR_SIZE_Y " << yStretchCount + 2 << "\n"
432 << SHADER_NPATCH_VISUAL_SHADER_VERT;
434 shader = Shader::New(vertexShader.str(), fragmentShader, hints);
441 void NPatchVisual::ApplyTextureAndUniforms()
443 const NPatchData* data;
444 TextureSet textureSet;
446 if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
448 textureSet = data->GetTextures();
449 NPatchHelper::ApplyTextureAndUniforms(mImpl->mRenderer, data);
453 DALI_LOG_ERROR("The N patch image '%s' is not a valid N patch image\n", mImageUrl.GetUrl().c_str());
454 textureSet = TextureSet::New();
456 Actor actor = mPlacementActor.GetHandle();
457 Vector2 imageSize = Vector2::ZERO;
460 imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
462 mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
463 Texture croppedImage = mImpl->mRenderer.GetTextures().GetTexture(0);
464 textureSet.SetTexture(0u, croppedImage);
465 mImpl->mRenderer.RegisterProperty("uFixed[0]", Vector2::ZERO);
466 mImpl->mRenderer.RegisterProperty("uFixed[1]", Vector2::ZERO);
467 mImpl->mRenderer.RegisterProperty("uFixed[2]", Vector2::ZERO);
468 mImpl->mRenderer.RegisterProperty("uStretchTotal", Vector2(croppedImage.GetWidth(), croppedImage.GetHeight()));
471 if(mAuxiliaryPixelBuffer)
473 // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
474 // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
475 // on GL interpolation alone.
476 if(mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
477 mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight())
479 mAuxiliaryPixelBuffer.Resize(data->GetCroppedWidth(), data->GetCroppedHeight());
482 // Note, this resets mAuxiliaryPixelBuffer handle
483 auto auxiliaryPixelData = Devel::PixelBuffer::Convert(mAuxiliaryPixelBuffer);
485 auto texture = Texture::New(TextureType::TEXTURE_2D,
486 auxiliaryPixelData.GetPixelFormat(),
487 auxiliaryPixelData.GetWidth(),
488 auxiliaryPixelData.GetHeight());
489 texture.Upload(auxiliaryPixelData);
490 textureSet.SetTexture(1, texture);
491 mImpl->mRenderer.RegisterProperty(DevelImageVisual::Property::AUXILIARY_IMAGE_ALPHA,
492 AUXILIARY_IMAGE_ALPHA_NAME,
493 mAuxiliaryImageAlpha);
495 mImpl->mRenderer.SetTextures(textureSet);
497 // Register transform properties
498 mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
501 Geometry NPatchVisual::GetNinePatchGeometry(VisualFactoryCache::GeometryType subType)
503 Geometry geometry = mFactoryCache.GetGeometry(subType);
506 if(DALI_LIKELY(VisualFactoryCache::NINE_PATCH_GEOMETRY == subType))
508 geometry = NPatchHelper::CreateGridGeometry(Uint16Pair(3, 3));
510 else if(VisualFactoryCache::NINE_PATCH_BORDER_GEOMETRY == subType)
512 geometry = NPatchHelper::CreateBorderGeometry(Uint16Pair(3, 3));
514 mFactoryCache.SaveGeometry(subType, geometry);
519 void NPatchVisual::SetResource()
521 const NPatchData* data;
522 if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data))
524 Geometry geometry = CreateGeometry();
525 Shader shader = CreateShader();
527 mImpl->mRenderer.SetGeometry(geometry);
528 mImpl->mRenderer.SetShader(shader);
530 Actor actor = mPlacementActor.GetHandle();
533 ApplyTextureAndUniforms();
534 actor.AddRenderer(mImpl->mRenderer);
535 mPlacementActor.Reset();
537 // npatch loaded and ready to display
538 ResourceReady(Toolkit::Visual::ResourceStatus::READY);
543 void NPatchVisual::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
545 if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE)
547 EnablePreMultipliedAlpha(textureInformation.preMultiplied);
550 // Image loaded and ready to display
551 ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
554 if(mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid())
559 else // for the ReturnType::PIXEL_BUFFER
561 if(loadSuccess && textureInformation.url == mAuxiliaryUrl.GetUrl())
563 mAuxiliaryPixelBuffer = textureInformation.pixelBuffer;
568 // Image loaded and ready to display
569 ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
574 } // namespace Internal
576 } // namespace Toolkit