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 void ImageAtlas::Remove( const Vector4& textureRect )
143 mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
144 static_cast<SizeType>(textureRect.y*mHeight),
145 static_cast<SizeType>((textureRect.z-textureRect.x)*mWidth+1.f),
146 static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
149 void ImageAtlas::UploadToAtlas()
151 while( LoadingTask* next = mCompleteQueue.NextTask() )
153 if( ! next->loader.IsLoaded() )
155 if(!mBrokenImageUrl.empty()) // replace with the broken image
157 UploadBrokenImage( next->packRect );
160 DALI_LOG_ERROR( "Failed to load the image: %s\n", (next->loader.GetUrl()).c_str());
164 if( next->loader.GetPixelData()->GetWidth() < next->packRect.width || next->loader.GetPixelData()->GetHeight() < next->packRect.height )
166 DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
167 next->loader.GetPixelData()->GetWidth(),
168 next->loader.GetPixelData()->GetHeight(),
169 next->packRect.width,
170 next->packRect.height );
173 mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y );
180 void ImageAtlas::UploadBrokenImage( const Rect<SizeType>& area )
182 BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
184 SizeType loadedWidth = loader.GetPixelData()->GetWidth();
185 SizeType loadedHeight = loader.GetPixelData()->GetHeight();
187 bool needBackgroundClear = false;
188 SizeType packX = area.x;
189 SizeType packY = area.y;
190 // locate the broken image in the middle.
191 if( area.width > loadedWidth)
193 packX += (area.width - loadedWidth)/2;
194 needBackgroundClear = true;
196 if( area.height > loadedHeight)
198 packY += (area.height - loadedHeight)/2;
199 needBackgroundClear = true;
202 if( needBackgroundClear )
204 SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
205 PixelBuffer* buffer = new PixelBuffer [size];
206 PixelDataPtr background = PixelData::New( buffer, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
207 for( SizeType idx = 0; idx < size; idx++ )
211 mAtlas.Upload( background, area.x, area.y );
214 mAtlas.Upload( loader.GetPixelData(), packX, packY );
217 } // namespace Internal
219 } // namespace Toolkit