Merge "Convert the Underline and Shadow deprecated APIs to the new ones." into devel...
[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/public-api/images/resource-image.h>
25 #include <dali/devel-api/adaptor-framework/bitmap-loader.h>
26 #include <dali/integration-api/debug.h>
27
28 namespace Dali
29 {
30
31 namespace Toolkit
32 {
33
34 namespace Internal
35 {
36
37 ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat )
38 : mAtlas( Texture::New( Dali::TextureType::TEXTURE_2D, pixelFormat, width, height ) ),
39   mPacker( width, height ),
40   mAsyncLoader( Toolkit::AsyncImageLoader::New() ),
41   mBrokenImageUrl(""),
42   mBrokenImageSize(),
43   mWidth( static_cast<float>(width) ),
44   mHeight( static_cast<float>( height ) ),
45   mPixelFormat( pixelFormat )
46 {
47   mAsyncLoader.ImageLoadedSignal().Connect( this, &ImageAtlas::UploadToAtlas );
48 }
49
50 ImageAtlas::~ImageAtlas()
51 {
52   const std::size_t count = mLoadingTaskInfoContainer.Count();
53   for( std::size_t i=0; i < count; ++i )
54   {
55     // Call unregister to every observer in the list.
56     // Note that, the Atlas can be registered to same observer multiple times, and the Unregister method only remove one item each time.
57     // 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.
58     if( mLoadingTaskInfoContainer[i]->observer )
59     {
60       mLoadingTaskInfoContainer[i]->observer->Unregister( *this );
61     }
62   }
63
64   mLoadingTaskInfoContainer.Clear();
65 }
66
67 IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
68 {
69   IntrusivePtr<ImageAtlas> internal = new ImageAtlas( width, height, pixelFormat );
70
71   return internal;
72 }
73
74 Texture ImageAtlas::GetAtlas()
75 {
76   return mAtlas;
77 }
78
79 float ImageAtlas::GetOccupancyRate() const
80 {
81   return 1.f - static_cast<float>( mPacker.GetAvailableArea() ) / ( mWidth*mHeight );
82 }
83
84 void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
85 {
86   mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
87   if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
88   {
89     mBrokenImageUrl = brokenImageUrl;
90   }
91 }
92
93 bool ImageAtlas::Upload( Vector4& textureRect,
94                          const std::string& url,
95                          ImageDimensions size,
96                          FittingMode::Type fittingMode,
97                          bool orientationCorrection,
98                          AtlasUploadObserver* atlasUploadObserver )
99 {
100   ImageDimensions dimensions = size;
101   ImageDimensions zero;
102   if( size == zero ) // image size not provided
103   {
104     dimensions = ResourceImage::GetImageSize( url );
105     if( dimensions == zero ) // Fail to read the image & broken image file exists
106     {
107       if( !mBrokenImageUrl.empty() )
108       {
109         return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver );
110       }
111       else
112       {
113         textureRect = Vector4::ZERO;
114         return true;
115       }
116     }
117   }
118
119   unsigned int packPositionX = 0;
120   unsigned int packPositionY = 0;
121   if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
122   {
123     unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection );
124     mLoadingTaskInfoContainer.PushBack( new LoadingTaskInfo( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver ) );
125     // apply the half pixel correction
126     textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
127     textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
128     textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
129     textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
130
131     if( atlasUploadObserver )
132     {
133       // register to the observer,
134       // Not that a matching unregister call should be invoked in UploadToAtlas if the observer is still alive by then.
135       atlasUploadObserver->Register( *this );
136     }
137
138     return true;
139   }
140
141   return false;
142 }
143
144 bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
145 {
146   unsigned int packPositionX = 0;
147   unsigned int packPositionY = 0;
148   if( mPacker.Pack( pixelData.GetWidth(), pixelData.GetHeight(), packPositionX, packPositionY ) )
149   {
150     mAtlas.Upload( pixelData, 0u, 0u, packPositionX, packPositionY, pixelData.GetWidth(), pixelData.GetHeight() );
151
152     // apply the half pixel correction
153     textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
154     textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
155     textureRect.z = ( static_cast<float>( packPositionX + pixelData.GetWidth() )-0.5f ) / mWidth; // right
156     textureRect.w = ( static_cast<float>( packPositionY + pixelData.GetHeight() )-0.5f ) / mHeight;// bottom
157
158     return true;
159   }
160
161   return false;
162 }
163
164 void ImageAtlas::Remove( const Vector4& textureRect )
165 {
166   mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
167                        static_cast<SizeType>(textureRect.y*mHeight),
168                        static_cast<SizeType>((textureRect.z-textureRect.x)*mWidth+1.f),
169                        static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
170 }
171
172 void ImageAtlas::ObserverDestroyed( AtlasUploadObserver* observer )
173 {
174   const std::size_t count = mLoadingTaskInfoContainer.Count();
175   for( std::size_t i=0; i < count; ++i )
176   {
177     if( mLoadingTaskInfoContainer[i]->observer == observer )
178     {
179       // the observer is destructing, so its member function should not be called anymore
180       mLoadingTaskInfoContainer[i]->observer = NULL;
181     }
182   }
183 }
184
185 void ImageAtlas::UploadToAtlas( uint32_t id, PixelData pixelData )
186 {
187   if(  mLoadingTaskInfoContainer[0]->loadTaskId == id)
188   {
189     Rect<unsigned int> packRect( mLoadingTaskInfoContainer[0]->packRect  );
190     if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 ))
191     {
192       if(!mBrokenImageUrl.empty()) // replace with the broken image
193       {
194         UploadBrokenImage( packRect );
195       }
196     }
197     else
198     {
199       if( pixelData.GetWidth() < packRect.width || pixelData.GetHeight() < packRect.height  )
200       {
201         DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
202             pixelData.GetWidth(), pixelData.GetHeight(),
203             packRect.width, packRect.height );
204       }
205
206       mAtlas.Upload( pixelData, 0u, 0u, packRect.x, packRect.y, packRect.width, packRect.height );
207     }
208
209     if( mLoadingTaskInfoContainer[0]->observer )
210     {
211       mLoadingTaskInfoContainer[0]->observer->UploadCompleted();
212       mLoadingTaskInfoContainer[0]->observer->Unregister( *this );
213     }
214
215     mLoadingTaskInfoContainer.Erase( mLoadingTaskInfoContainer.Begin() );
216   }
217 }
218
219 void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
220 {
221   BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
222   loader.Load();
223   SizeType loadedWidth = loader.GetPixelData().GetWidth();
224   SizeType loadedHeight = loader.GetPixelData().GetHeight();
225
226   bool needBackgroundClear = false;
227   SizeType packX = area.x;
228   SizeType packY = area.y;
229   // locate the broken image in the middle.
230   if( area.width > loadedWidth)
231   {
232     packX += (area.width - loadedWidth)/2;
233     needBackgroundClear = true;
234   }
235   if( area.height > loadedHeight)
236   {
237     packY += (area.height - loadedHeight)/2;
238     needBackgroundClear = true;
239   }
240
241   if( needBackgroundClear )
242   {
243     SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
244     PixelBuffer* buffer = new PixelBuffer [size];
245     PixelData background = PixelData::New( buffer, size, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
246     for( SizeType idx = 0; idx < size; idx++ )
247     {
248       buffer[idx] = 0x00;
249     }
250     mAtlas.Upload( background, 0u, 0u, area.x, area.y, area.width, area.height );
251   }
252
253   mAtlas.Upload( loader.GetPixelData(), 0u, 0u, packX, packY, loadedWidth, loadedHeight );
254 }
255
256 } // namespace Internal
257
258 } // namespace Toolkit
259
260 } // namespace Dali