Merge "Ensure BaseHandle class move noexcept (toolkit public-api)" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / environment-map-loader.cpp
1 /*
2  * Copyright (c) 2023 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 // FILE HEADER
19 #include <dali-scene3d/public-api/loader/environment-map-loader.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <string.h>
24 #include <cmath>
25 #include <filesystem>
26
27 // INTERNAL INCLUDES
28 #include <dali-scene3d/public-api/loader/ktx-loader.h>
29
30 namespace Dali
31 {
32 namespace
33 {
34 const std::string_view KTX_EXTENSION = ".ktx";
35
36 static constexpr uint32_t NUMBER_OF_ENVIRONMENT_MAP_TYPE = 5;
37 static constexpr uint32_t NUMBER_OF_CUBE_MAP_TYPE        = 4;
38
39 /**
40  * @brief cube map face index
41  * Cube map layer order is as fallows:
42  * POSITIVE_X, NEGATIVE_X, POSITIVE_Y, NEGATIVE_Y, POSITIVE_Z, NEGATIVE_Z. @see CubeMapLayer
43  * The indices are for 4 kind of environment cube map. Cross_horizontal, Array_horizontal, Cross_vertical, and Array_vertical.
44  */
45 static constexpr uint32_t CUBEMAP_INDEX_X[NUMBER_OF_CUBE_MAP_TYPE][6] = {{2, 0, 1, 1, 1, 3}, {0, 1, 2, 3, 4, 5}, {1, 1, 1, 1, 0, 2}, {0, 0, 0, 0, 0, 0}};
46 static constexpr uint32_t CUBEMAP_INDEX_Y[NUMBER_OF_CUBE_MAP_TYPE][6] = {{1, 1, 0, 2, 1, 1}, {0, 0, 0, 0, 0, 0}, {1, 3, 0, 2, 1, 1}, {0, 1, 2, 3, 4, 5}};
47
48 static constexpr Vector2 NUMBER_OF_CUBE_FACE[NUMBER_OF_CUBE_MAP_TYPE] =
49   {
50     Vector2(4, 3),
51     Vector2(6, 1),
52     Vector2(3, 4),
53     Vector2(1, 6)};
54
55 static constexpr float expectedAspectRatios[NUMBER_OF_ENVIRONMENT_MAP_TYPE] =
56   {
57     4.0f / 3.0f,
58     6.0f / 1.0f,
59     3.0f / 4.0f,
60     1.0f / 6.0f,
61     2.0f / 1.0f};
62
63 enum CubeType
64 {
65   CROSS_HORIZONTAL = 0, // Cross horizontal style cube map
66   ARRAY_HORIZONTAL,     // array horizontal style cube map
67   CROSS_VERTICAL,       // Cross vertical style cube map
68   ARRAY_VERTICAL,       // array vertical style cube map
69   NONE
70 };
71
72 uint8_t* GetCroppedBuffer(uint8_t* sourceBuffer, uint32_t bytesPerPixel, uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset, uint32_t xFaceSize, uint32_t yFaceSize)
73 {
74   uint32_t byteSize   = bytesPerPixel * xFaceSize * yFaceSize;
75   uint8_t* destBuffer = reinterpret_cast<uint8_t*>(malloc(byteSize + 4u));
76
77   int32_t srcStride  = width * bytesPerPixel;
78   int32_t destStride = xFaceSize * bytesPerPixel;
79   int32_t srcOffset  = xOffset * bytesPerPixel + yOffset * srcStride;
80   int32_t destOffset = 0;
81   for(uint16_t row = yOffset; row < yOffset + yFaceSize; ++row)
82   {
83     memcpy(destBuffer + destOffset, sourceBuffer + srcOffset, destStride);
84     srcOffset += srcStride;
85     destOffset += destStride;
86   }
87
88   return destBuffer;
89 }
90
91 PixelData GetCubeFace(Devel::PixelBuffer pixelBuffer, uint32_t faceIndex, CubeType cubeType, float faceWidth, float faceHeight)
92 {
93   PixelData pixelData;
94   if(cubeType != NONE)
95   {
96     uint8_t* imageBuffer   = pixelBuffer.GetBuffer();
97     uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(pixelBuffer.GetPixelFormat());
98     uint32_t imageWidth    = pixelBuffer.GetWidth();
99     uint32_t imageHeight   = pixelBuffer.GetHeight();
100
101     uint32_t xOffset = CUBEMAP_INDEX_X[cubeType][faceIndex] * static_cast<uint32_t>(faceWidth);
102     uint32_t yOffset = CUBEMAP_INDEX_Y[cubeType][faceIndex] * static_cast<uint32_t>(faceHeight);
103
104     uint32_t finalFaceWidth  = (xOffset + static_cast<uint32_t>(faceWidth) < imageWidth) ? static_cast<uint32_t>(faceWidth) : imageWidth - xOffset;
105     uint32_t finalFaceHeight = (yOffset + static_cast<uint32_t>(faceHeight) < imageHeight) ? static_cast<uint32_t>(faceHeight) : imageHeight - yOffset;
106     uint8_t* tempImageBuffer = GetCroppedBuffer(imageBuffer, bytesPerPixel, imageWidth, imageHeight, xOffset, yOffset, finalFaceWidth, finalFaceHeight);
107     pixelData                = PixelData::New(tempImageBuffer, finalFaceWidth * finalFaceHeight * bytesPerPixel, finalFaceWidth, finalFaceHeight, pixelBuffer.GetPixelFormat(), PixelData::FREE);
108   }
109   return pixelData;
110 }
111
112 /**
113  * @brief Loads environment map data texture from an image url.
114  *
115  * @param[in] environmentMapUrl The environment map file url.
116  * @param[out] environmentMapData The data structure with all pixel data objects.
117  * @return bool True if the loading is succeded.
118  */
119 bool LoadEnvironmentMapData(const std::string& environmentMapUrl, Scene3D::Loader::EnvironmentMapData& environmentMapData)
120 {
121   // Diffuse Environment Map
122   if(environmentMapUrl.empty())
123   {
124     return false;
125   }
126
127   Devel::PixelBuffer pixelBuffer = LoadImageFromFile(environmentMapUrl);
128   if(pixelBuffer)
129   {
130     CubeType cubeType    = NONE;
131     uint32_t imageWidth  = pixelBuffer.GetWidth();
132     uint32_t imageHeight = pixelBuffer.GetHeight();
133     /**
134      * If the environment map type is not EQUIRECTANGULAR,
135      * The type should be defined internally.
136      * DALi checkes aspect ratio of input image and find the closest type.
137      * If the environment map type is CUBEMAP, DALi determines which of the following styles is closest:
138      * cross horizontal, cross vertical, array horizontal, and array vertical.
139      * When the environment map type is AUTO, it finds the closest type, including the Equirectangular type.
140      */
141     if(environmentMapData.GetEnvironmentMapType() != Scene3D::EnvironmentMapType::EQUIRECTANGULAR)
142     {
143       float aspectRatio = (float)imageWidth / (float)imageHeight;
144
145       float    minDistance = FLT_MAX;
146       uint32_t typeCount   = (environmentMapData.GetEnvironmentMapType() == Scene3D::EnvironmentMapType::CUBEMAP) ? NUMBER_OF_CUBE_MAP_TYPE : NUMBER_OF_ENVIRONMENT_MAP_TYPE;
147       for(uint32_t i = 0; i < typeCount; ++i)
148       {
149         float distance = fabs(aspectRatio - expectedAspectRatios[i]);
150         if(distance < minDistance)
151         {
152           minDistance = distance;
153           cubeType    = static_cast<CubeType>(i);
154         }
155       }
156     }
157
158     if(cubeType != NONE)
159     {
160       float faceWidth = imageWidth, faceHeight = imageHeight;
161       faceWidth /= NUMBER_OF_CUBE_FACE[static_cast<uint32_t>(cubeType)].x;
162       faceHeight /= NUMBER_OF_CUBE_FACE[static_cast<uint32_t>(cubeType)].y;
163       environmentMapData.mPixelData.resize(6);
164       for(uint32_t i = 0; i < 6; ++i)
165       {
166         environmentMapData.mPixelData[i].resize(1);
167       }
168       for(uint32_t i = 0; i < 6; ++i)
169       {
170         environmentMapData.mPixelData[i][0] = GetCubeFace(pixelBuffer, i, cubeType, faceWidth, faceHeight);
171       }
172       environmentMapData.SetEnvironmentMapType(Scene3D::EnvironmentMapType::CUBEMAP);
173     }
174     else
175     {
176       environmentMapData.mPixelData.resize(1);
177       environmentMapData.mPixelData[0].resize(1);
178       environmentMapData.mPixelData[0][0] = Devel::PixelBuffer::Convert(pixelBuffer);
179       environmentMapData.SetEnvironmentMapType(Scene3D::EnvironmentMapType::EQUIRECTANGULAR);
180     }
181
182     if(!environmentMapData.mPixelData.empty() && !environmentMapData.mPixelData[0].empty() && environmentMapData.mPixelData[0][0])
183     {
184       const uint32_t pixelDataWidth  = environmentMapData.mPixelData[0][0].GetWidth();
185       const uint32_t pixelDataHeight = environmentMapData.mPixelData[0][0].GetHeight();
186
187       if(pixelDataWidth > 0u && pixelDataHeight > 0u)
188       {
189         uint32_t mipmap = static_cast<uint32_t>(1 + std::floor(std::log2(std::min(pixelDataWidth, pixelDataHeight))));
190         environmentMapData.SetMipmapLevels(mipmap);
191       }
192     }
193     return true;
194   }
195   return false;
196 }
197 } // namespace
198
199 namespace Scene3D
200 {
201 namespace Loader
202 {
203 bool LoadEnvironmentMap(const std::string& environmentMapUrl, EnvironmentMapData& environmentMapData)
204 {
205   std::filesystem::path modelPath(environmentMapUrl);
206   std::string           extension = modelPath.extension();
207   std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
208
209   bool successed = (extension == KTX_EXTENSION) ? Dali::Scene3D::Loader::LoadKtxData(environmentMapUrl, environmentMapData) : LoadEnvironmentMapData(environmentMapUrl, environmentMapData);
210
211   return successed;
212 }
213
214 } // namespace Loader
215 } // namespace Scene3D
216 } // namespace Dali