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