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;
46 bool IsBlockSizeSufficient( uint32_t width, uint32_t height, uint32_t requiredBlockWidth, uint32_t requiredBlockHeight )
48 return ( width + DOUBLE_PIXEL_PADDING <= requiredBlockWidth ) && ( height + DOUBLE_PIXEL_PADDING <= requiredBlockHeight );
52 AtlasManager::AtlasManager()
53 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
55 mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
56 mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
57 mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
58 mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
61 AtlasManagerPtr AtlasManager::New()
63 AtlasManagerPtr internal = new AtlasManager();
67 AtlasManager::~AtlasManager()
71 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
73 SizeType width = size.mWidth;
74 SizeType height = size.mHeight;
75 SizeType blockWidth = size.mBlockWidth;
76 SizeType blockHeight = size.mBlockHeight;
78 // Check to see if the atlas is large enough to hold a single block even ?
79 if ( blockWidth + DOUBLE_PIXEL_PADDING + 1u > width || blockHeight + DOUBLE_PIXEL_PADDING + 1u > height )
81 DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
82 width, height, blockWidth + DOUBLE_PIXEL_PADDING + 1u, blockHeight + DOUBLE_PIXEL_PADDING + 1u );
86 Dali::Texture atlas = Dali::Texture::New( TextureType::TEXTURE_2D, pixelformat, width, height );
88 // Clear the background
89 unsigned int bufferSize( width * height * Dali::Pixel::GetBytesPerPixel( pixelformat ) );
90 unsigned char* background = new unsigned char[bufferSize];
91 memset( background, 0, bufferSize );
92 PixelData backgroundPixels = PixelData::New( background, bufferSize, width, height, pixelformat, PixelData::DELETE_ARRAY );
93 atlas.Upload( backgroundPixels, 0u, 0u, 0u, 0u, width, height );
95 AtlasDescriptor atlasDescriptor;
96 atlasDescriptor.mAtlas = atlas;
97 atlasDescriptor.mSize = size;
98 atlasDescriptor.mPixelFormat = pixelformat;
99 atlasDescriptor.mTotalBlocks = ( ( width - 1u ) / blockWidth ) * ( ( height - 1u ) / blockHeight );
100 atlasDescriptor.mAvailableBlocks = atlasDescriptor.mTotalBlocks;
102 bufferSize = blockWidth * SINGLE_PIXEL_PADDING * Dali::Pixel::GetBytesPerPixel(pixelformat);
103 unsigned char* bufferHorizontalStrip = new unsigned char[bufferSize];
104 memset( bufferHorizontalStrip, 0, bufferSize );
105 atlasDescriptor.mHorizontalStrip = PixelData::New( bufferHorizontalStrip, bufferSize, blockWidth, SINGLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
107 bufferSize = SINGLE_PIXEL_PADDING * (blockHeight - DOUBLE_PIXEL_PADDING) * Dali::Pixel::GetBytesPerPixel(pixelformat);
108 unsigned char* bufferVerticalStrip = new unsigned char[bufferSize];
109 memset( bufferVerticalStrip, 0, bufferSize );
110 atlasDescriptor.mVerticalStrip = PixelData::New( bufferVerticalStrip, bufferSize, SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
112 bufferSize = Dali::Pixel::GetBytesPerPixel(pixelformat);
113 unsigned char* buffer = new unsigned char[bufferSize];
114 memset( buffer, 0xFF, bufferSize );
115 PixelData filledPixelImage = PixelData::New( buffer, bufferSize, 1u, 1u, pixelformat, PixelData::DELETE_ARRAY );
116 atlas.Upload( filledPixelImage, 0u, 0u, 0u, 0u, 1u, 1u );
117 mAtlasList.push_back( atlasDescriptor );
118 return mAtlasList.size();
121 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
123 mAddFailPolicy = policy;
126 bool AtlasManager::Add( const PixelData& image,
127 Toolkit::AtlasManager::AtlasSlot& slot,
128 Toolkit::AtlasManager::AtlasId atlas )
130 bool created = false;
131 Pixel::Format pixelFormat = image.GetPixelFormat();
132 SizeType width = image.GetWidth();
133 SizeType height = image.GetHeight();
134 SizeType foundAtlas = 0;
138 AtlasSlotDescriptor desc;
140 // If there is a preferred atlas then check for room in that first
143 foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
146 // Search current atlases to see if there is a good match
147 while( ( 0u == foundAtlas ) && ( index < mAtlasList.size() ) )
149 foundAtlas = CheckAtlas( index, width, height, pixelFormat );
153 // If we can't find a suitable atlas then check the policy to determine action
154 if ( 0u == foundAtlas )
156 if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
158 if ( IsBlockSizeSufficient( width, height, mNewAtlasSize.mBlockWidth, mNewAtlasSize.mBlockHeight ) ) // Checks if image fits within the atlas blocks
160 foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat ); // Creating atlas with mNewAtlasSize, may not be the needed size!
161 if ( 0u == foundAtlas )
163 DALI_LOG_ERROR("Failed to create an atlas of %i x %i blocksize: %i x %i.\n",
164 mNewAtlasSize.mWidth,
165 mNewAtlasSize.mHeight,
166 mNewAtlasSize.mBlockWidth,
167 mNewAtlasSize.mBlockHeight );
177 if ( ( 0u == foundAtlas ) || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
179 // Haven't found an atlas for this image ( may have failed to add image to atlas )
180 DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
185 foundAtlas--; // Atlas created successfully, decrement by 1 to get <vector> index (starts at 0 not 1)
187 // Work out which the block we're going to use
188 // Is there currently a next free block available ?
189 if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
191 // Yes, so select our next block
192 desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
196 // Our next block must be from the free list, fetch from the start of the list
197 desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
198 mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
201 desc.mImageWidth = width;
202 desc.mImageHeight = height;
203 desc.mAtlasId = foundAtlas + 1u; // Ids start from 1 not the 0 index
206 // See if there's a previously freed image ID that we can assign to this new image
207 uint32_t imageId = 0u;
208 for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
210 if ( !mImageList[ i ].mCount )
218 mImageList.PushBack( desc );
219 slot.mImageId = mImageList.Size();
223 mImageList[ imageId - 1u ] = desc;
224 slot.mImageId = imageId;
226 slot.mAtlasId = foundAtlas + 1u; // Ids start from 1 not the 0 index
228 // Upload the buffer image into the atlas
229 UploadImage( image, desc );
233 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
236 Pixel::Format pixelFormat )
238 AtlasManager::SizeType result = 0u;
239 if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
241 // Check to see if the image will fit in these blocks
243 const SizeType availableBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
245 if ( availableBlocks && IsBlockSizeSufficient( width, height,mAtlasList[ atlas ].mSize.mBlockWidth, mAtlasList[ atlas ].mSize.mBlockHeight ) )
247 result = atlas + 1u; // Atlas ids start from 1 not 0
253 void AtlasManager::UploadImage( const PixelData& image,
254 const AtlasSlotDescriptor& desc )
256 // Get the atlas to upload the image to
257 SizeType atlas = desc.mAtlasId - 1u;
259 // Check to see that the pixel formats are compatible
260 if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
262 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
266 SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
267 SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
268 SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
270 SizeType blockX = desc.mBlock % atlasWidthInBlocks;
271 SizeType blockY = desc.mBlock / atlasWidthInBlocks;
272 SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
273 SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
275 SizeType width = image.GetWidth();
276 SizeType height = image.GetHeight();
278 // Blit image 1 pixel to the right and down into the block to compensate for texture filtering
279 if ( !mAtlasList[ atlas ].mAtlas.Upload( image, 0u, 0u,
280 blockOffsetX + SINGLE_PIXEL_PADDING,
281 blockOffsetY + SINGLE_PIXEL_PADDING,
284 DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
288 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
291 mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
292 mAtlasList[ atlas ].mHorizontalStrip.GetHeight()) )
294 DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
298 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
300 blockOffsetY + SINGLE_PIXEL_PADDING,
301 mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
302 mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
304 DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
308 if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
310 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
312 blockOffsetY + height + SINGLE_PIXEL_PADDING,
313 mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
314 mAtlasList[ atlas ].mHorizontalStrip.GetHeight() ) )
316 DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
321 if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
323 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
324 blockOffsetX + width + SINGLE_PIXEL_PADDING,
325 blockOffsetY + SINGLE_PIXEL_PADDING,
326 mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
327 mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
329 DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
334 void AtlasManager::GenerateMeshData( ImageId id,
335 const Vector2& position,
336 Toolkit::AtlasManager::Mesh2D& meshData,
341 // Read the atlas Id to use for this image
342 SizeType imageId = id - 1u;
343 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
344 SizeType width = mImageList[ imageId ].mImageWidth;
345 SizeType height = mImageList[ imageId ].mImageHeight;
347 AtlasMeshFactory::CreateQuad( width,
349 mImageList[ imageId ].mBlock,
350 mAtlasList[ atlas ].mSize,
354 // Mesh created so increase the reference count, if we're asked to
357 mImageList[ imageId ].mCount++;
362 DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
366 Dali::Texture AtlasManager::GetAtlasContainer( AtlasId atlas ) const
368 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
369 Dali::Texture atlasContainer;
370 if ( atlas && atlas-- <= mAtlasList.size() )
372 atlasContainer = mAtlasList[ atlas ].mAtlas;
374 return atlasContainer;
377 bool AtlasManager::Remove( ImageId id )
379 // Decrements the reference count of this image, and removes the blocks if zero.
380 SizeType imageId = id - 1u;
381 bool removed = false;
383 if ( id > mImageList.Size() )
385 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
389 // If we attempt to free an image that is already freed then do nothing, other than log
390 if ( !mImageList[ imageId ].mCount )
392 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
396 if ( 2u > --mImageList[ imageId ].mCount )
398 // 'Remove the blocks' from this image and add to the atlas' freelist
400 mImageList[ imageId ].mCount = 0;
401 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
402 mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
407 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
409 DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
410 AtlasManager::AtlasId atlasId = 0u;
411 if ( id && id-- <= mImageList.Size() )
413 atlasId = mImageList[ id ].mAtlasId;
418 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
420 mNewAtlasSize = size;
422 // Add on padding for borders around atlas entries
423 mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
424 mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
427 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
429 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
430 if ( atlas && atlas-- <= mAtlasList.size() )
432 return mAtlasList[ atlas ].mSize;
437 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
439 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
440 AtlasManager::SizeType freeBlocks = 0u;
441 if ( atlas && atlas-- <= mAtlasList.size() )
443 freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
448 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
450 return mAtlasList.size();
453 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) const
455 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
456 Pixel::Format pixelFormat = Pixel::RGBA8888;
457 if ( atlas && atlas-- <= mAtlasList.size() )
459 pixelFormat = mAtlasList[ atlas ].mPixelFormat;
464 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
466 Toolkit::AtlasManager::AtlasMetricsEntry entry;
467 uint32_t textureMemoryUsed = 0;
468 uint32_t atlasCount = mAtlasList.size();
469 metrics.mAtlasCount = atlasCount;
470 metrics.mAtlasMetrics.Resize(0);
472 for ( uint32_t i = 0; i < atlasCount; ++i )
474 entry.mSize = mAtlasList[ i ].mSize;
475 entry.mTotalBlocks = mAtlasList[ i ].mTotalBlocks;
476 entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[ i ].mAvailableBlocks + mAtlasList[ i ].mFreeBlocksList.Size();
477 entry.mPixelFormat = GetPixelFormat( i + 1 );
479 metrics.mAtlasMetrics.PushBack( entry );
481 uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
482 if ( entry.mPixelFormat == Pixel::BGRA8888 )
487 textureMemoryUsed += size;
490 metrics.mTextureMemoryUsed = textureMemoryUsed;
493 TextureSet AtlasManager::GetTextures( AtlasId atlas ) const
495 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
496 TextureSet textureSet;
497 if ( atlas && atlas-- <= mAtlasList.size() )
499 textureSet = mAtlasList[ atlas ].mTextureSet;
504 void AtlasManager::SetTextures( AtlasId atlas, TextureSet& textureSet )
506 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
507 if ( atlas && atlas-- <= mAtlasList.size() )
509 mAtlasList[ atlas ].mTextureSet = textureSet;
513 } // namespace Internal
515 } // namespace Toolkit