2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "image-atlas-impl.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>
35 typedef unsigned char PixelBuffer;
37 Texture ImageAtlas::PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects )
39 // Record each block size
40 Dali::Vector<Uint16Pair> blockSizes;
41 SizeType count = pixelData.size();
42 for( SizeType index = 0; index < count; index++ )
44 blockSizes.PushBack( ImageDimensions( pixelData[index].GetWidth(), pixelData[index].GetHeight() ) );
47 // Ask atlasPacker for packing position of each block
48 Dali::Vector<Uint16Pair> packPositions;
49 ImageDimensions atlasSize = AtlasPacker::GroupPack( blockSizes, packPositions );
51 // Prepare for outout texture rect array
53 textureRects.Resize( count );
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() );
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++ )
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() );
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
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() ),
86 mWidth( static_cast<float>(width) ),
87 mHeight( static_cast<float>( height ) ),
88 mPixelFormat( pixelFormat )
90 mAsyncLoader.ImageLoadedSignal().Connect( this, &ImageAtlas::UploadToAtlas );
93 ImageAtlas::~ImageAtlas()
95 const std::size_t count = mLoadingTaskInfoContainer.Count();
96 for( std::size_t i=0; i < count; ++i )
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 )
103 mLoadingTaskInfoContainer[i]->observer->Unregister( *this );
107 mLoadingTaskInfoContainer.Clear();
110 IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
112 IntrusivePtr<ImageAtlas> internal = new ImageAtlas( width, height, pixelFormat );
117 Texture ImageAtlas::GetAtlas()
122 float ImageAtlas::GetOccupancyRate() const
124 return 1.f - static_cast<float>( mPacker.GetAvailableArea() ) / ( mWidth*mHeight );
127 void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
129 mBrokenImageSize = Dali::GetClosestImageSize( brokenImageUrl );
130 if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
132 mBrokenImageUrl = brokenImageUrl;
136 bool ImageAtlas::Upload( Vector4& textureRect,
137 const std::string& url,
138 ImageDimensions size,
139 FittingMode::Type fittingMode,
140 bool orientationCorrection,
141 AtlasUploadObserver* atlasUploadObserver )
143 ImageDimensions dimensions = size;
144 ImageDimensions zero;
145 if( size == zero ) // image size not provided
147 dimensions = Dali::GetClosestImageSize( url );
148 if( dimensions == zero ) // Fail to read the image & broken image file exists
150 if( !mBrokenImageUrl.empty() )
152 return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver );
156 textureRect = Vector4::ZERO;
162 unsigned int packPositionX = 0;
163 unsigned int packPositionY = 0;
164 if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
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
174 if( atlasUploadObserver )
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 );
187 bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
189 unsigned int packPositionX = 0;
190 unsigned int packPositionY = 0;
191 if( mPacker.Pack( pixelData.GetWidth(), pixelData.GetHeight(), packPositionX, packPositionY ) )
193 mAtlas.Upload( pixelData, 0u, 0u, packPositionX, packPositionY, pixelData.GetWidth(), pixelData.GetHeight() );
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
207 void ImageAtlas::Remove( const Vector4& textureRect )
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) );
215 void ImageAtlas::ObserverDestroyed( AtlasUploadObserver* observer )
217 const std::size_t count = mLoadingTaskInfoContainer.Count();
218 for( std::size_t i=0; i < count; ++i )
220 if( mLoadingTaskInfoContainer[i]->observer == observer )
222 // the observer is destructing, so its member function should not be called anymore
223 mLoadingTaskInfoContainer[i]->observer = NULL;
228 void ImageAtlas::UploadToAtlas( uint32_t id, PixelData pixelData )
230 if( mLoadingTaskInfoContainer[0]->loadTaskId == id)
232 Rect<unsigned int> packRect( mLoadingTaskInfoContainer[0]->packRect );
233 if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 ))
235 if(!mBrokenImageUrl.empty()) // replace with the broken image
237 UploadBrokenImage( packRect );
242 if( pixelData.GetWidth() < packRect.width || pixelData.GetHeight() < packRect.height )
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 );
249 mAtlas.Upload( pixelData, 0u, 0u, packRect.x, packRect.y, packRect.width, packRect.height );
252 if( mLoadingTaskInfoContainer[0]->observer )
254 mLoadingTaskInfoContainer[0]->observer->UploadCompleted();
255 mLoadingTaskInfoContainer[0]->observer->Unregister( *this );
258 mLoadingTaskInfoContainer.Erase( mLoadingTaskInfoContainer.Begin() );
262 void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
264 Devel::PixelBuffer brokenBuffer = LoadImageFromFile( mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
265 SizeType loadedWidth = brokenBuffer.GetWidth();
266 SizeType loadedHeight = brokenBuffer.GetHeight();
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)
274 packX += (area.width - loadedWidth)/2;
275 needBackgroundClear = true;
277 if( area.height > loadedHeight)
279 packY += (area.height - loadedHeight)/2;
280 needBackgroundClear = true;
283 if( needBackgroundClear )
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++ )
292 PixelData pixelData = Devel::PixelBuffer::Convert( background );
293 mAtlas.Upload( pixelData, 0u, 0u, area.x, area.y, area.width, area.height );
296 PixelData brokenPixelData = Devel::PixelBuffer::Convert( brokenBuffer );
297 mAtlas.Upload( brokenPixelData, 0u, 0u, packX, packY, loadedWidth, loadedHeight );
300 } // namespace Internal
302 } // namespace Toolkit