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