Updated all cpp files to new format
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / image-loader / image-atlas-impl.cpp
1 /*
2  * Copyright (c) 2021 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 // CLASS HEADER
19 #include "image-atlas-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/signals/callback.h>
25 #include <string.h>
26
27 namespace Dali
28 {
29 namespace Toolkit
30 {
31 namespace Internal
32 {
33 typedef unsigned char PixelBuffer;
34
35 Texture ImageAtlas::PackToAtlas(const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects)
36 {
37   // Record each block size
38   Dali::Vector<Uint16Pair> blockSizes;
39   SizeType                 count = pixelData.size();
40   for(SizeType index = 0; index < count; index++)
41   {
42     blockSizes.PushBack(ImageDimensions(pixelData[index].GetWidth(), pixelData[index].GetHeight()));
43   }
44
45   // Ask atlasPacker for packing position of each block
46   Dali::Vector<Uint16Pair> packPositions;
47   ImageDimensions          atlasSize = AtlasPacker::GroupPack(blockSizes, packPositions);
48
49   // Prepare for outout texture rect array
50   textureRects.Clear();
51   textureRects.Resize(count);
52
53   // create the texture for uploading the multiple pixel data
54   Texture atlasTexture = Texture::New(Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888, atlasSize.GetWidth(), atlasSize.GetHeight());
55
56   float atlasWidth  = static_cast<float>(atlasTexture.GetWidth());
57   float atlasHeight = static_cast<float>(atlasTexture.GetHeight());
58   int   packPositionX, packPositionY;
59   // Upload the pixel data one by one to its packing position, and record the texture rects
60   for(SizeType index = 0; index < count; index++)
61   {
62     packPositionX = packPositions[index].GetX();
63     packPositionY = packPositions[index].GetY();
64     atlasTexture.Upload(pixelData[index], 0u, 0u, packPositionX, packPositionY, pixelData[index].GetWidth(), pixelData[index].GetHeight());
65
66     // Apply the half pixel correction to avoid the color bleeding between neighbour blocks
67     textureRects[index].x = (static_cast<float>(packPositionX) + 0.5f) / atlasWidth;                                 // left
68     textureRects[index].y = (static_cast<float>(packPositionY) + 0.5f) / atlasHeight;                                // right
69     textureRects[index].z = (static_cast<float>(packPositionX + pixelData[index].GetWidth()) - 0.5f) / atlasWidth;   // right
70     textureRects[index].w = (static_cast<float>(packPositionY + pixelData[index].GetHeight()) - 0.5f) / atlasHeight; // bottom
71   }
72
73   return atlasTexture;
74 }
75
76 ImageAtlas::ImageAtlas(SizeType width, SizeType height, Pixel::Format pixelFormat)
77 : mAtlas(Texture::New(Dali::TextureType::TEXTURE_2D, pixelFormat, width, height)),
78   mPacker(width, height),
79   mAsyncLoader(Toolkit::AsyncImageLoader::New()),
80   mBrokenImageUrl(""),
81   mBrokenImageSize(),
82   mWidth(static_cast<float>(width)),
83   mHeight(static_cast<float>(height)),
84   mPixelFormat(pixelFormat)
85 {
86   mAsyncLoader.ImageLoadedSignal().Connect(this, &ImageAtlas::UploadToAtlas);
87 }
88
89 ImageAtlas::~ImageAtlas()
90 {
91   const std::size_t count = mLoadingTaskInfoContainer.Count();
92   for(std::size_t i = 0; i < count; ++i)
93   {
94     // Call unregister to every observer in the list.
95     // Note that, the Atlas can be registered to same observer multiple times, and the Unregister method only remove one item each time.
96     // In this way, the atlas is actually detached from a observer either every upload call invoked by this observer is completed or atlas is destroyed.
97     if(mLoadingTaskInfoContainer[i]->observer)
98     {
99       mLoadingTaskInfoContainer[i]->observer->Unregister(*this);
100     }
101   }
102
103   mLoadingTaskInfoContainer.Clear();
104 }
105
106 IntrusivePtr<ImageAtlas> ImageAtlas::New(SizeType width, SizeType height, Pixel::Format pixelFormat)
107 {
108   IntrusivePtr<ImageAtlas> internal = new ImageAtlas(width, height, pixelFormat);
109
110   return internal;
111 }
112
113 Texture ImageAtlas::GetAtlas()
114 {
115   return mAtlas;
116 }
117
118 float ImageAtlas::GetOccupancyRate() const
119 {
120   return 1.f - static_cast<float>(mPacker.GetAvailableArea()) / (mWidth * mHeight);
121 }
122
123 void ImageAtlas::SetBrokenImage(const std::string& brokenImageUrl)
124 {
125   mBrokenImageSize = Dali::GetClosestImageSize(brokenImageUrl);
126   if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0) // check the url is valid
127   {
128     mBrokenImageUrl = brokenImageUrl;
129   }
130 }
131
132 bool ImageAtlas::Upload(Vector4&             textureRect,
133                         const std::string&   url,
134                         ImageDimensions      size,
135                         FittingMode::Type    fittingMode,
136                         bool                 orientationCorrection,
137                         AtlasUploadObserver* atlasUploadObserver)
138 {
139   ImageDimensions dimensions = size;
140   ImageDimensions zero;
141   if(size == zero) // image size not provided
142   {
143     dimensions = Dali::GetClosestImageSize(url);
144     if(dimensions == zero) // Fail to read the image & broken image file exists
145     {
146       if(!mBrokenImageUrl.empty())
147       {
148         return Upload(textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver);
149       }
150       else
151       {
152         textureRect = Vector4::ZERO;
153         return true;
154       }
155     }
156   }
157
158   unsigned int packPositionX = 0;
159   unsigned int packPositionY = 0;
160   if(mPacker.Pack(dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY))
161   {
162     unsigned short loadId = mAsyncLoader.Load(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection);
163     mLoadingTaskInfoContainer.PushBack(new LoadingTaskInfo(loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver));
164     // apply the half pixel correction
165     textureRect.x = (static_cast<float>(packPositionX) + 0.5f) / mWidth;                      // left
166     textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight;                     // right
167     textureRect.z = (static_cast<float>(packPositionX + dimensions.GetX()) - 0.5f) / mWidth;  // right
168     textureRect.w = (static_cast<float>(packPositionY + dimensions.GetY()) - 0.5f) / mHeight; // bottom
169
170     if(atlasUploadObserver)
171     {
172       // register to the observer,
173       // Not that a matching unregister call should be invoked in UploadToAtlas if the observer is still alive by then.
174       atlasUploadObserver->Register(*this);
175     }
176
177     return true;
178   }
179
180   return false;
181 }
182
183 bool ImageAtlas::Upload(Vector4& textureRect, PixelData pixelData)
184 {
185   unsigned int packPositionX = 0;
186   unsigned int packPositionY = 0;
187   if(mPacker.Pack(pixelData.GetWidth(), pixelData.GetHeight(), packPositionX, packPositionY))
188   {
189     mAtlas.Upload(pixelData, 0u, 0u, packPositionX, packPositionY, pixelData.GetWidth(), pixelData.GetHeight());
190
191     // apply the half pixel correction
192     textureRect.x = (static_cast<float>(packPositionX) + 0.5f) / mWidth;                          // left
193     textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight;                         // right
194     textureRect.z = (static_cast<float>(packPositionX + pixelData.GetWidth()) - 0.5f) / mWidth;   // right
195     textureRect.w = (static_cast<float>(packPositionY + pixelData.GetHeight()) - 0.5f) / mHeight; // bottom
196
197     return true;
198   }
199
200   return false;
201 }
202
203 void ImageAtlas::Remove(const Vector4& textureRect)
204 {
205   mPacker.DeleteBlock(static_cast<SizeType>(textureRect.x * mWidth),
206                       static_cast<SizeType>(textureRect.y * mHeight),
207                       static_cast<SizeType>((textureRect.z - textureRect.x) * mWidth + 1.f),
208                       static_cast<SizeType>((textureRect.w - textureRect.y) * mHeight + 1.f));
209 }
210
211 void ImageAtlas::ObserverDestroyed(AtlasUploadObserver* observer)
212 {
213   const std::size_t count = mLoadingTaskInfoContainer.Count();
214   for(std::size_t i = 0; i < count; ++i)
215   {
216     if(mLoadingTaskInfoContainer[i]->observer == observer)
217     {
218       // the observer is destructing, so its member function should not be called anymore
219       mLoadingTaskInfoContainer[i]->observer = NULL;
220     }
221   }
222 }
223
224 void ImageAtlas::UploadToAtlas(uint32_t id, PixelData pixelData)
225 {
226   if(mLoadingTaskInfoContainer[0]->loadTaskId == id)
227   {
228     Rect<unsigned int> packRect(mLoadingTaskInfoContainer[0]->packRect);
229     if(!pixelData || (pixelData.GetWidth() == 0 && pixelData.GetHeight() == 0))
230     {
231       if(!mBrokenImageUrl.empty()) // replace with the broken image
232       {
233         UploadBrokenImage(packRect);
234       }
235     }
236     else
237     {
238       if(pixelData.GetWidth() < packRect.width || pixelData.GetHeight() < packRect.height)
239       {
240         DALI_LOG_ERROR("Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
241                        pixelData.GetWidth(),
242                        pixelData.GetHeight(),
243                        packRect.width,
244                        packRect.height);
245       }
246
247       mAtlas.Upload(pixelData, 0u, 0u, packRect.x, packRect.y, packRect.width, packRect.height);
248     }
249
250     if(mLoadingTaskInfoContainer[0]->observer)
251     {
252       mLoadingTaskInfoContainer[0]->observer->UploadCompleted();
253       mLoadingTaskInfoContainer[0]->observer->Unregister(*this);
254     }
255
256     mLoadingTaskInfoContainer.Erase(mLoadingTaskInfoContainer.Begin());
257   }
258 }
259
260 void ImageAtlas::UploadBrokenImage(const Rect<unsigned int>& area)
261 {
262   Devel::PixelBuffer brokenBuffer = LoadImageFromFile(mBrokenImageUrl, ImageDimensions(area.width, area.height));
263   SizeType           loadedWidth  = brokenBuffer.GetWidth();
264   SizeType           loadedHeight = brokenBuffer.GetHeight();
265
266   bool     needBackgroundClear = false;
267   SizeType packX               = area.x;
268   SizeType packY               = area.y;
269   // locate the broken image in the middle.
270   if(area.width > loadedWidth)
271   {
272     packX += (area.width - loadedWidth) / 2;
273     needBackgroundClear = true;
274   }
275   if(area.height > loadedHeight)
276   {
277     packY += (area.height - loadedHeight) / 2;
278     needBackgroundClear = true;
279   }
280
281   if(needBackgroundClear)
282   {
283     SizeType           size       = area.width * area.height * Pixel::GetBytesPerPixel(mPixelFormat);
284     Devel::PixelBuffer background = Devel::PixelBuffer::New(area.width, area.height, mPixelFormat);
285     unsigned char*     buffer     = background.GetBuffer();
286     for(SizeType idx = 0; idx < size; idx++)
287     {
288       buffer[idx] = 0x00;
289     }
290     PixelData pixelData = Devel::PixelBuffer::Convert(background);
291     mAtlas.Upload(pixelData, 0u, 0u, area.x, area.y, area.width, area.height);
292   }
293
294   PixelData brokenPixelData = Devel::PixelBuffer::Convert(brokenBuffer);
295   mAtlas.Upload(brokenPixelData, 0u, 0u, packX, packY, loadedWidth, loadedHeight);
296 }
297
298 } // namespace Internal
299
300 } // namespace Toolkit
301
302 } // namespace Dali