[dali_2.2.19] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / material-definition.cpp
index 7245652..f69ff72 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-// INTERNAL INCLUDES
-#include "dali-scene3d/public-api/loader/material-definition.h"
+// CLASS HEADER
+#include <dali-scene3d/public-api/loader/material-definition.h>
 
 // EXTERNAL INCLUDES
-#include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
+#include <dali-toolkit/devel-api/builder/base64-encoding.h>
+#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
 
 namespace Dali
 {
@@ -63,7 +65,54 @@ constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
   WrapMode::CLAMP_TO_EDGE,
   WrapMode::MIRRORED_REPEAT};
 
-const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+const SamplerFlags::Type GetSingleValueSampler()
+{
+  static const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+  return SINGLE_VALUE_SAMPLER;
+}
+
+static constexpr std::string_view EMBEDDED_DATA_PREFIX               = "data:";
+static constexpr std::string_view EMBEDDED_DATA_IMAGE_MEDIA_TYPE     = "image/";
+static constexpr std::string_view EMBEDDED_DATA_BASE64_ENCODING_TYPE = "base64,";
+
+Dali::PixelData LoadImageResource(const std::string& resourcePath,
+                                  TextureDefinition& textureDefinition,
+                                  FittingMode::Type  fittingMode,
+                                  bool               orientationCorrection)
+{
+  Dali::PixelData pixelData;
+  if(!textureDefinition.mTextureBuffer.empty())
+  {
+    Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(textureDefinition.mTextureBuffer.data(), textureDefinition.mTextureBuffer.size(), textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
+    if(pixelBuffer)
+    {
+      pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
+    }
+  }
+  else if(textureDefinition.mImageUri.find(EMBEDDED_DATA_PREFIX.data()) == 0 && textureDefinition.mImageUri.find(EMBEDDED_DATA_IMAGE_MEDIA_TYPE.data(), EMBEDDED_DATA_PREFIX.length()) == EMBEDDED_DATA_PREFIX.length())
+  {
+    uint32_t position = textureDefinition.mImageUri.find(EMBEDDED_DATA_BASE64_ENCODING_TYPE.data(), EMBEDDED_DATA_PREFIX.length() + EMBEDDED_DATA_IMAGE_MEDIA_TYPE.length());
+    if(position != std::string::npos)
+    {
+      position += EMBEDDED_DATA_BASE64_ENCODING_TYPE.length();
+      std::string_view     data = std::string_view(textureDefinition.mImageUri).substr(position);
+      std::vector<uint8_t> buffer;
+      Dali::Toolkit::DecodeBase64FromString(data, buffer);
+      uint32_t bufferSize = buffer.size();
+
+      Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(reinterpret_cast<uint8_t*>(buffer.data()), bufferSize, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
+      if(pixelBuffer)
+      {
+        pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
+      }
+    }
+  }
+  else
+  {
+    pixelData = SyncImageLoader::Load(resourcePath + textureDefinition.mImageUri, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
+  }
+  return pixelData;
+}
 } // namespace
 
 SamplerFlags::Type SamplerFlags::Encode(FilterMode::Type minFilter, FilterMode::Type magFilter, WrapMode::Type wrapS, WrapMode::Type wrapT)
@@ -108,8 +157,25 @@ TextureDefinition::TextureDefinition(const std::string& imageUri, SamplerFlags::
 {
 }
 
+TextureDefinition::TextureDefinition(std::string&& imageUri, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode)
+: mImageUri(std::move(imageUri)),
+  mSamplerFlags(samplerFlags),
+  mMinImageDimensions(minImageDimensions),
+  mSamplingMode(samplingMode)
+{
+}
+
+TextureDefinition::TextureDefinition(std::vector<uint8_t>&& textureBuffer, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode)
+: mImageUri(),
+  mSamplerFlags(samplerFlags),
+  mMinImageDimensions(minImageDimensions),
+  mSamplingMode(samplingMode),
+  mTextureBuffer(std::move(textureBuffer))
+{
+}
+
 MaterialDefinition::RawData
-MaterialDefinition::LoadRaw(const std::string& imagesPath) const
+MaterialDefinition::LoadRaw(const std::string& imagesPath)
 {
   RawData raw;
 
@@ -124,7 +190,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
   raw.mTextures.reserve(numBuffers);
 
   // Load textures
-  auto iTexture   = mTextureStages.cbegin();
+  auto iTexture   = mTextureStages.begin();
   auto checkStage = [&](uint32_t flags) {
     return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
   };
@@ -132,26 +198,26 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
   // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
   if(checkStage(ALBEDO | METALLIC))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
 
     if(checkStage(NORMAL | ROUGHNESS))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else // single value normal-roughness
     {
       const auto bufferSize = 4;
       uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
   }
   else
   {
     if(checkStage(ALBEDO))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else if(mNeedAlbedoTexture) // single value albedo, albedo-alpha or albedo-metallic
@@ -178,14 +244,14 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
       buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
       buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
       buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
 
     // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
     const bool createMetallicRoughnessAndNormal = hasTransparency || std::distance(mTextureStages.begin(), iTexture) > 0;
     if(checkStage(METALLIC | ROUGHNESS))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
@@ -194,12 +260,12 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
       // glTF2 uses B & G, so we might as well just set all components to 1.0.
       const auto bufferSize = 4;
       uint8_t*   buffer     = new uint8_t[bufferSize]{0xff, 0xff, 0xff, 0xff};
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
 
     if(checkStage(NORMAL))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else if(mNeedNormalTexture)
@@ -208,13 +274,13 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
       {
         const auto bufferSize = 3;
         uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
-        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
       }
       else // single-value normal-roughness
       {
         const auto bufferSize = 4;
         uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
-        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
       }
     }
   }
@@ -222,31 +288,31 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
   // Extra textures.
   if(checkStage(SUBSURFACE))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
   if(checkStage(OCCLUSION))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
   if(checkStage(EMISSIVE))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
   if(checkStage(SPECULAR))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
   if(checkStage(SPECULAR_COLOR))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
@@ -260,12 +326,16 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
   uint32_t n = 0;
   for(auto& tData : raw.mTextures)
   {
-    auto& pixels  = tData.mPixels;
-    auto  texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
-    texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
-    if(tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
+    auto&   pixels = tData.mPixels;
+    Texture texture;
+    if(pixels)
     {
-      texture.GenerateMipmaps();
+      texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
+      texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
+      if(tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
+      {
+        texture.GenerateMipmaps();
+      }
     }
 
     textureSet.SetTexture(n, texture);
@@ -312,9 +382,7 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
 
 bool MaterialDefinition::CheckTextures(uint32_t flags) const
 {
-  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
-           return MaskMatch(ts.mSemantic, flags);
-         }) != mTextureStages.end();
+  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) { return MaskMatch(ts.mSemantic, flags); }) != mTextureStages.end();
 }
 
 } // namespace Loader