2 * Copyright (c) 2015 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/public-api/images/resource-image.h>
25 #include <dali/integration-api/debug.h>
36 ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat )
37 : mPacker( width, height ),
39 mCompleteQueue( new EventThreadCallback( MakeCallback( this, &ImageAtlas::UploadToAtlas ) ) ),
40 mLoadingThread( mLoadQueue, mCompleteQueue ),
43 mPixelFormat( pixelFormat ),
44 mLoadingThreadStarted( false )
46 mAtlas = Atlas::New( width, height, pixelFormat );
47 mWidth = static_cast<float>(width);
48 mHeight = static_cast<float>( height );
51 ImageAtlas::~ImageAtlas()
53 if( mLoadingThreadStarted )
55 // add an empty task would stop the loading thread from contional wait.
56 mLoadQueue.AddTask( NULL );
57 // stop the loading thread
58 mLoadingThread.Join();
59 // The atlas can still be used as texture after ImageAtlas has been thrown away,
60 // so make sure all the loaded bitmap been uploaded to atlas
65 IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
67 IntrusivePtr<ImageAtlas> internal = new ImageAtlas( width, height, pixelFormat );
71 Image ImageAtlas::GetAtlas()
76 void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
78 mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
79 if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
81 mBrokenImageUrl = brokenImageUrl;
85 bool ImageAtlas::Upload( Vector4& textureRect,
86 const std::string& url,
88 FittingMode::Type fittingMode,
89 bool orientationCorrection )
91 ImageDimensions dimensions = size;
93 if( size == zero ) // image size not provided
95 dimensions = ResourceImage::GetImageSize( url );
96 if( dimensions == zero ) // Fail to read the image & broken image file exists
98 if( !mBrokenImageUrl.empty() )
100 return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
104 textureRect = Vector4::ZERO;
110 if( static_cast<unsigned int>(dimensions.GetWidth() * dimensions.GetHeight()) > mPacker.GetAvailableArea() )
115 unsigned int packPositionX = 0;
116 unsigned int packPositionY = 0;
117 if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
119 if( !mLoadingThreadStarted )
121 mLoadingThread.Start();
122 mLoadingThreadStarted = true;
125 LoadingTask* newTask = new LoadingTask(BitmapLoader::New(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection ),
126 packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight());
127 mLoadQueue.AddTask( newTask );
129 // apply the half pixel correction
130 textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
131 textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
132 textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
133 textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
141 bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
143 unsigned int packPositionX = 0;
144 unsigned int packPositionY = 0;
145 if( mPacker.Pack( pixelData.GetWidth(), pixelData.GetHeight(), packPositionX, packPositionY ) )
147 mAtlas.Upload( pixelData, packPositionX, packPositionY );
149 // apply the half pixel correction
150 textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
151 textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
152 textureRect.z = ( static_cast<float>( packPositionX + pixelData.GetWidth() )-0.5f ) / mWidth; // right
153 textureRect.w = ( static_cast<float>( packPositionY + pixelData.GetHeight() )-0.5f ) / mHeight;// bottom
161 void ImageAtlas::Remove( const Vector4& textureRect )
163 mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
164 static_cast<SizeType>(textureRect.y*mHeight),
165 static_cast<SizeType>((textureRect.z-textureRect.x)*mWidth+1.f),
166 static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
169 void ImageAtlas::UploadToAtlas()
171 while( LoadingTask* next = mCompleteQueue.NextTask() )
173 if( ! next->loader.IsLoaded() )
175 if(!mBrokenImageUrl.empty()) // replace with the broken image
177 UploadBrokenImage( next->packRect );
180 DALI_LOG_ERROR( "Failed to load the image: %s\n", (next->loader.GetUrl()).c_str());
184 if( next->loader.GetPixelData().GetWidth() < next->packRect.width || next->loader.GetPixelData().GetHeight() < next->packRect.height )
186 DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
187 next->loader.GetPixelData().GetWidth(),
188 next->loader.GetPixelData().GetHeight(),
189 next->packRect.width,
190 next->packRect.height );
193 mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y );
200 void ImageAtlas::UploadBrokenImage( const Rect<SizeType>& area )
202 BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
204 SizeType loadedWidth = loader.GetPixelData().GetWidth();
205 SizeType loadedHeight = loader.GetPixelData().GetHeight();
207 bool needBackgroundClear = false;
208 SizeType packX = area.x;
209 SizeType packY = area.y;
210 // locate the broken image in the middle.
211 if( area.width > loadedWidth)
213 packX += (area.width - loadedWidth)/2;
214 needBackgroundClear = true;
216 if( area.height > loadedHeight)
218 packY += (area.height - loadedHeight)/2;
219 needBackgroundClear = true;
222 if( needBackgroundClear )
224 SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
225 PixelBuffer* buffer = new PixelBuffer [size];
226 PixelData background = PixelData::New( buffer, size, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
227 for( SizeType idx = 0; idx < size; idx++ )
231 mAtlas.Upload( background, area.x, area.y );
234 mAtlas.Upload( loader.GetPixelData(), packX, packY );
237 } // namespace Internal
239 } // namespace Toolkit