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.
18 #include <dali-toolkit/internal/text/rendering/atlas/atlas-manager-impl.h>
22 #include <dali/integration-api/debug.h>
25 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
38 const uint32_t DEFAULT_ATLAS_WIDTH( 512u );
39 const uint32_t DEFAULT_ATLAS_HEIGHT( 512u );
40 const uint32_t DEFAULT_BLOCK_WIDTH( 16u );
41 const uint32_t DEFAULT_BLOCK_HEIGHT( 16u );
42 const uint32_t SINGLE_PIXEL_PADDING( 1u );
43 const uint32_t DOUBLE_PIXEL_PADDING( SINGLE_PIXEL_PADDING << 1 );
44 Toolkit::AtlasManager::AtlasSize EMPTY_SIZE;
47 AtlasManager::AtlasManager()
48 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
50 mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
51 mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
52 mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
53 mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
56 AtlasManagerPtr AtlasManager::New()
58 AtlasManagerPtr internal = new AtlasManager();
62 AtlasManager::~AtlasManager()
66 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
68 SizeType width = size.mWidth;
69 SizeType height = size.mHeight;
70 SizeType blockWidth = size.mBlockWidth;
71 SizeType blockHeight = size.mBlockHeight;
73 // Check to see if the atlas is large enough to hold a single block even ?
74 if ( blockWidth + DOUBLE_PIXEL_PADDING + 1u > width || blockHeight + DOUBLE_PIXEL_PADDING + 1u > height )
76 DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
77 width, height, blockWidth + DOUBLE_PIXEL_PADDING + 1u, blockHeight + DOUBLE_PIXEL_PADDING + 1u );
81 Dali::Texture atlas = Dali::Texture::New( TextureType::TEXTURE_2D, pixelformat, width, height );
82 AtlasDescriptor atlasDescriptor;
83 atlasDescriptor.mAtlas = atlas;
84 atlasDescriptor.mSize = size;
85 atlasDescriptor.mPixelFormat = pixelformat;
86 atlasDescriptor.mTotalBlocks = ( ( width - 1u ) / blockWidth ) * ( ( height - 1u ) / blockHeight );
87 atlasDescriptor.mAvailableBlocks = atlasDescriptor.mTotalBlocks;
89 unsigned int bufferSize( blockWidth * SINGLE_PIXEL_PADDING * Dali::Pixel::GetBytesPerPixel(pixelformat) );
90 unsigned char* bufferHorizontalStrip = new unsigned char[bufferSize];
91 memset( bufferHorizontalStrip, 0, bufferSize );
92 atlasDescriptor.mHorizontalStrip = PixelData::New( bufferHorizontalStrip, bufferSize, blockWidth, SINGLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
94 bufferSize = SINGLE_PIXEL_PADDING * (blockHeight - DOUBLE_PIXEL_PADDING) * Dali::Pixel::GetBytesPerPixel(pixelformat);
95 unsigned char* bufferVerticalStrip = new unsigned char[bufferSize];
96 memset( bufferVerticalStrip, 0, bufferSize );
97 atlasDescriptor.mVerticalStrip = PixelData::New( bufferVerticalStrip, bufferSize, SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
99 bufferSize = Dali::Pixel::GetBytesPerPixel(pixelformat);
100 unsigned char* buffer = new unsigned char[bufferSize];
101 memset( buffer, 0xFF, bufferSize );
102 PixelData filledPixelImage = PixelData::New( buffer, bufferSize, 1u, 1u, pixelformat, PixelData::DELETE_ARRAY );
103 atlas.Upload( filledPixelImage, 0u, 0u, 0u, 0u, 1u, 1u );
104 mAtlasList.push_back( atlasDescriptor );
105 return mAtlasList.size();
108 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
110 mAddFailPolicy = policy;
113 bool AtlasManager::Add( const PixelData& image,
114 Toolkit::AtlasManager::AtlasSlot& slot,
115 Toolkit::AtlasManager::AtlasId atlas )
117 bool created = false;
118 Pixel::Format pixelFormat = image.GetPixelFormat();
119 SizeType width = image.GetWidth();
120 SizeType height = image.GetHeight();
121 SizeType foundAtlas = 0;
125 AtlasSlotDescriptor desc;
127 // If there is a preferred atlas then check for room in that first
130 foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
133 // Search current atlases to see if there is a good match
134 while( !foundAtlas && index < mAtlasList.size() )
136 foundAtlas = CheckAtlas( index, width, height, pixelFormat );
140 // If we can't find a suitable atlas then check the policy to determine action
143 if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
145 foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
148 DALI_LOG_ERROR("Failed to create an atlas of %i x %i blocksize: %i x %i.\n",
149 mNewAtlasSize.mWidth,
150 mNewAtlasSize.mHeight,
151 mNewAtlasSize.mBlockWidth,
152 mNewAtlasSize.mBlockHeight );
156 foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
159 if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
161 // Haven't found an atlas for this image!!!!!!
162 DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
167 // Work out which the block we're going to use
168 // Is there currently a next free block available ?
169 if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
171 // Yes, so select our next block
172 desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
176 // Our next block must be from the free list, fetch from the start of the list
177 desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
178 mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
181 desc.mImageWidth = width;
182 desc.mImageHeight = height;
183 desc.mAtlasId = foundAtlas + 1u;
186 // See if there's a previously freed image ID that we can assign to this new image
187 uint32_t imageId = 0u;
188 for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
190 if ( !mImageList[ i ].mCount )
198 mImageList.PushBack( desc );
199 slot.mImageId = mImageList.Size();
203 mImageList[ imageId - 1u ] = desc;
204 slot.mImageId = imageId;
206 slot.mAtlasId = foundAtlas + 1u;
208 // Upload the buffer image into the atlas
209 UploadImage( image, desc );
213 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
216 Pixel::Format pixelFormat )
218 AtlasManager::SizeType result = 0u;
219 if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
221 // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
222 if ( ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() )
223 && width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
224 && height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
232 void AtlasManager::UploadImage( const PixelData& image,
233 const AtlasSlotDescriptor& desc )
235 // Get the atlas to upload the image to
236 SizeType atlas = desc.mAtlasId - 1u;
238 // Check to see that the pixel formats are compatible
239 if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
241 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
245 SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
246 SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
247 SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
249 SizeType blockX = desc.mBlock % atlasWidthInBlocks;
250 SizeType blockY = desc.mBlock / atlasWidthInBlocks;
251 SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
252 SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
254 SizeType width = image.GetWidth();
255 SizeType height = image.GetHeight();
257 // Blit image 1 pixel to the right and down into the block to compensate for texture filtering
258 if ( !mAtlasList[ atlas ].mAtlas.Upload( image, 0u, 0u,
259 blockOffsetX + SINGLE_PIXEL_PADDING,
260 blockOffsetY + SINGLE_PIXEL_PADDING,
263 DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
267 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
270 mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
271 mAtlasList[ atlas ].mHorizontalStrip.GetHeight()) )
273 DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
277 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
279 blockOffsetY + SINGLE_PIXEL_PADDING,
280 mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
281 mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
283 DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
287 if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
289 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
291 blockOffsetY + height + SINGLE_PIXEL_PADDING,
292 mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
293 mAtlasList[ atlas ].mHorizontalStrip.GetHeight() ) )
295 DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
300 if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
302 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
303 blockOffsetX + width + SINGLE_PIXEL_PADDING,
304 blockOffsetY + SINGLE_PIXEL_PADDING,
305 mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
306 mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
308 DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
313 void AtlasManager::GenerateMeshData( ImageId id,
314 const Vector2& position,
315 Toolkit::AtlasManager::Mesh2D& meshData,
320 // Read the atlas Id to use for this image
321 SizeType imageId = id - 1u;
322 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
323 SizeType width = mImageList[ imageId ].mImageWidth;
324 SizeType height = mImageList[ imageId ].mImageHeight;
326 AtlasMeshFactory::CreateQuad( width,
328 mImageList[ imageId ].mBlock,
329 mAtlasList[ atlas ].mSize,
333 // Mesh created so increase the reference count, if we're asked to
336 mImageList[ imageId ].mCount++;
341 DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
345 Dali::Texture AtlasManager::GetAtlasContainer( AtlasId atlas ) const
347 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
348 Dali::Texture atlasContainer;
349 if ( atlas && atlas-- <= mAtlasList.size() )
351 atlasContainer = mAtlasList[ atlas ].mAtlas;
353 return atlasContainer;
356 bool AtlasManager::Remove( ImageId id )
358 // Decrements the reference count of this image, and removes the blocks if zero.
359 SizeType imageId = id - 1u;
360 bool removed = false;
362 if ( id > mImageList.Size() )
364 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
368 // If we attempt to free an image that is already freed then do nothing, other than log
369 if ( !mImageList[ imageId ].mCount )
371 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
375 if ( 2u > --mImageList[ imageId ].mCount )
377 // 'Remove the blocks' from this image and add to the atlas' freelist
379 mImageList[ imageId ].mCount = 0;
380 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
381 mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
386 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
388 DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
389 AtlasManager::AtlasId atlasId = 0u;
390 if ( id && id-- <= mImageList.Size() )
392 atlasId = mImageList[ id ].mAtlasId;
397 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
399 mNewAtlasSize = size;
401 // Add on padding for borders around atlas entries
402 mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
403 mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
406 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
408 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
409 if ( atlas && atlas-- <= mAtlasList.size() )
411 return mAtlasList[ atlas ].mSize;
416 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
418 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
419 AtlasManager::SizeType freeBlocks = 0u;
420 if ( atlas && atlas-- <= mAtlasList.size() )
422 freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
427 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
429 return mAtlasList.size();
432 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) const
434 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
435 Pixel::Format pixelFormat = Pixel::RGBA8888;
436 if ( atlas && atlas-- <= mAtlasList.size() )
438 pixelFormat = mAtlasList[ atlas ].mPixelFormat;
443 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
445 Toolkit::AtlasManager::AtlasMetricsEntry entry;
446 uint32_t textureMemoryUsed = 0;
447 uint32_t atlasCount = mAtlasList.size();
448 metrics.mAtlasCount = atlasCount;
449 metrics.mAtlasMetrics.Resize(0);
451 for ( uint32_t i = 0; i < atlasCount; ++i )
453 entry.mSize = mAtlasList[ i ].mSize;
454 entry.mTotalBlocks = mAtlasList[ i ].mTotalBlocks;
455 entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[ i ].mAvailableBlocks + mAtlasList[ i ].mFreeBlocksList.Size();
456 entry.mPixelFormat = GetPixelFormat( i + 1 );
458 metrics.mAtlasMetrics.PushBack( entry );
460 uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
461 if ( entry.mPixelFormat == Pixel::BGRA8888 )
466 textureMemoryUsed += size;
469 metrics.mTextureMemoryUsed = textureMemoryUsed;
472 TextureSet AtlasManager::GetTextures( AtlasId atlas ) const
474 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
475 TextureSet textureSet;
476 if ( atlas && atlas-- <= mAtlasList.size() )
478 textureSet = mAtlasList[ atlas ].mTextureSet;
483 void AtlasManager::SetTextures( AtlasId atlas, TextureSet& textureSet )
485 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
486 if ( atlas && atlas-- <= mAtlasList.size() )
488 mAtlasList[ atlas ].mTextureSet = textureSet;
492 } // namespace Internal
494 } // namespace Toolkit