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 const uint32_t FILLED_PIXEL( -1 );
45 Toolkit::AtlasManager::AtlasSize EMPTY_SIZE;
48 AtlasManager::AtlasManager()
49 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
51 mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
52 mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
53 mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
54 mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
57 AtlasManagerPtr AtlasManager::New()
59 AtlasManagerPtr internal = new AtlasManager();
63 AtlasManager::~AtlasManager()
67 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
69 SizeType width = size.mWidth;
70 SizeType height = size.mHeight;
71 SizeType blockWidth = size.mBlockWidth;
72 SizeType blockHeight = size.mBlockHeight;
74 // Check to see if the atlas is large enough to hold a single block even ?
75 if ( blockWidth + DOUBLE_PIXEL_PADDING + 1u > width || blockHeight + DOUBLE_PIXEL_PADDING + 1u > height )
77 DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
78 width, height, blockWidth + DOUBLE_PIXEL_PADDING + 1u, blockHeight + DOUBLE_PIXEL_PADDING + 1u );
82 Dali::Atlas atlas = Dali::Atlas::New( width, height, pixelformat );
83 atlas.Clear( Vector4::ZERO );
84 AtlasDescriptor atlasDescriptor;
85 atlasDescriptor.mAtlas = atlas;
86 atlasDescriptor.mSize = size;
87 atlasDescriptor.mPixelFormat = pixelformat;
88 atlasDescriptor.mTotalBlocks = ( ( width - 1u ) / blockWidth ) * ( ( height - 1u ) / blockHeight );
89 atlasDescriptor.mAvailableBlocks = atlasDescriptor.mTotalBlocks;
91 atlasDescriptor.mHorizontalStrip = BufferImage::New( blockWidth, SINGLE_PIXEL_PADDING, pixelformat );
92 atlasDescriptor.mVerticalStrip = BufferImage::New( SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat );
94 PixelBuffer* buffer = atlasDescriptor.mHorizontalStrip.GetBuffer();
97 DALI_LOG_ERROR("atlasDescriptor.mHorizontalStrip.GetBuffer() returns NULL\n");
100 memset( buffer, 0, atlasDescriptor.mHorizontalStrip.GetBufferSize() );
102 buffer = atlasDescriptor.mVerticalStrip.GetBuffer();
105 DALI_LOG_ERROR("atlasDescriptor.mVerticalStrip.GetBuffer() returns NULL\n");
108 memset( buffer, 0, atlasDescriptor.mVerticalStrip.GetBufferSize() );
110 BufferImage filledPixelImage = BufferImage::New( 1u, 1u, pixelformat );
111 buffer = filledPixelImage.GetBuffer();
114 DALI_LOG_ERROR("filledPixelImage.GetBuffer() returns NULL\n");
118 memset( buffer, 0xFF, filledPixelImage.GetBufferSize() );
119 atlas.Upload( filledPixelImage, 0, 0 );
120 mAtlasList.push_back( atlasDescriptor );
121 return mAtlasList.size();
124 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
126 mAddFailPolicy = policy;
129 bool AtlasManager::Add( const BufferImage& image,
130 Toolkit::AtlasManager::AtlasSlot& slot,
131 Toolkit::AtlasManager::AtlasId atlas )
133 bool created = false;
134 Pixel::Format pixelFormat = image.GetPixelFormat();
135 SizeType width = image.GetWidth();
136 SizeType height = image.GetHeight();
137 SizeType foundAtlas = 0;
141 AtlasSlotDescriptor desc;
143 // If there is a preferred atlas then check for room in that first
146 foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
149 // Search current atlases to see if there is a good match
150 while( !foundAtlas && index < mAtlasList.size() )
152 foundAtlas = CheckAtlas( index, width, height, pixelFormat );
156 // If we can't find a suitable atlas then check the policy to determine action
159 if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
161 foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
164 DALI_LOG_ERROR("Failed to create an atlas of %i x %i blocksize: %i x %i.\n",
165 mNewAtlasSize.mWidth,
166 mNewAtlasSize.mHeight,
167 mNewAtlasSize.mBlockWidth,
168 mNewAtlasSize.mBlockHeight );
172 foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
175 if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
177 // Haven't found an atlas for this image!!!!!!
178 DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
183 // Work out which the block we're going to use
184 // Is there currently a next free block available ?
185 if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
187 // Yes, so select our next block
188 desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
192 // Our next block must be from the free list, fetch from the start of the list
193 desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
194 mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
197 desc.mImageWidth = width;
198 desc.mImageHeight = height;
199 desc.mAtlasId = foundAtlas + 1u;
202 // See if there's a previously freed image ID that we can assign to this new image
203 uint32_t imageId = 0u;
204 for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
206 if ( !mImageList[ i ].mCount )
214 mImageList.PushBack( desc );
215 slot.mImageId = mImageList.Size();
219 mImageList[ imageId - 1u ] = desc;
220 slot.mImageId = imageId;
222 slot.mAtlasId = foundAtlas + 1u;
224 // Upload the buffer image into the atlas
225 UploadImage( image, desc );
229 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
232 Pixel::Format pixelFormat )
234 AtlasManager::SizeType result = 0u;
235 if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
237 // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
238 if ( ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() )
239 && width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
240 && height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
248 void AtlasManager::UploadImage( const BufferImage& image,
249 const AtlasSlotDescriptor& desc )
251 // Get the atlas to upload the image to
252 SizeType atlas = desc.mAtlasId - 1u;
254 // Check to see that the pixel formats are compatible
255 if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
257 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
261 SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
262 SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
263 SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
265 SizeType blockX = desc.mBlock % atlasWidthInBlocks;
266 SizeType blockY = desc.mBlock / atlasWidthInBlocks;
267 SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
268 SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
270 SizeType width = image.GetWidth();
271 SizeType height = image.GetHeight();
273 // Blit image 1 pixel to the right and down into the block to compensate for texture filtering
274 if ( !mAtlasList[ atlas ].mAtlas.Upload( image,
275 blockOffsetX + SINGLE_PIXEL_PADDING,
276 blockOffsetY + SINGLE_PIXEL_PADDING ) )
278 DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
282 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip,
286 DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
290 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip,
292 blockOffsetY + SINGLE_PIXEL_PADDING ) )
294 DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
298 if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
300 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip,
302 blockOffsetY + height + SINGLE_PIXEL_PADDING ) )
304 DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
309 if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
311 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip,
312 blockOffsetX + width + SINGLE_PIXEL_PADDING,
313 blockOffsetY + SINGLE_PIXEL_PADDING ) )
315 DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
320 void AtlasManager::GenerateMeshData( ImageId id,
321 const Vector2& position,
322 Toolkit::AtlasManager::Mesh2D& meshData,
327 // Read the atlas Id to use for this image
328 SizeType imageId = id - 1u;
329 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
330 SizeType width = mImageList[ imageId ].mImageWidth;
331 SizeType height = mImageList[ imageId ].mImageHeight;
333 AtlasMeshFactory::CreateQuad( width,
335 mImageList[ imageId ].mBlock,
336 mAtlasList[ atlas ].mSize,
340 // Mesh created so increase the reference count, if we're asked to
343 mImageList[ imageId ].mCount++;
348 DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
352 Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
354 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
355 Dali::Atlas atlasContainer;
356 if ( atlas && atlas-- <= mAtlasList.size() )
358 atlasContainer = mAtlasList[ atlas ].mAtlas;
360 return atlasContainer;
363 bool AtlasManager::Remove( ImageId id )
365 // Decrements the reference count of this image, and removes the blocks if zero.
366 SizeType imageId = id - 1u;
367 bool removed = false;
369 if ( id > mImageList.Size() )
371 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
375 // If we attempt to free an image that is already freed then do nothing, other than log
376 if ( !mImageList[ imageId ].mCount )
378 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
382 if ( 2u > --mImageList[ imageId ].mCount )
384 // 'Remove the blocks' from this image and add to the atlas' freelist
386 mImageList[ imageId ].mCount = 0;
387 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
388 mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
393 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
395 DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
396 AtlasManager::AtlasId atlasId = 0u;
397 if ( id && id-- <= mImageList.Size() )
399 atlasId = mImageList[ id ].mAtlasId;
404 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
406 mNewAtlasSize = size;
408 // Add on padding for borders around atlas entries
409 mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
410 mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
413 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
415 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
416 if ( atlas && atlas-- <= mAtlasList.size() )
418 return mAtlasList[ atlas ].mSize;
423 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
425 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
426 AtlasManager::SizeType freeBlocks = 0u;
427 if ( atlas && atlas-- <= mAtlasList.size() )
429 freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
434 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
436 return mAtlasList.size();
439 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
441 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
442 Pixel::Format pixelFormat = Pixel::RGBA8888;
443 if ( atlas && atlas-- <= mAtlasList.size() )
445 pixelFormat = mAtlasList[ atlas ].mPixelFormat;
450 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
452 Toolkit::AtlasManager::AtlasMetricsEntry entry;
453 uint32_t textureMemoryUsed = 0;
454 uint32_t atlasCount = mAtlasList.size();
455 metrics.mAtlasCount = atlasCount;
456 metrics.mAtlasMetrics.Resize(0);
458 for ( uint32_t i = 0; i < atlasCount; ++i )
460 entry.mSize = mAtlasList[ i ].mSize;
461 entry.mTotalBlocks = mAtlasList[ i ].mTotalBlocks;
462 entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[ i ].mAvailableBlocks + mAtlasList[ i ].mFreeBlocksList.Size();
463 entry.mPixelFormat = GetPixelFormat( i + 1 );
465 metrics.mAtlasMetrics.PushBack( entry );
467 uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
468 if ( entry.mPixelFormat == Pixel::BGRA8888 )
473 textureMemoryUsed += size;
476 metrics.mTextureMemoryUsed = textureMemoryUsed;
479 Material AtlasManager::GetMaterial( AtlasId atlas ) const
481 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
483 if ( atlas && atlas-- <= mAtlasList.size() )
485 material = mAtlasList[ atlas ].mMaterial;
490 void AtlasManager::SetMaterial( AtlasId atlas, Material& material )
492 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
493 if ( atlas && atlas-- <= mAtlasList.size() )
495 mAtlasList[ atlas ].mMaterial = material;
499 } // namespace Internal
501 } // namespace Toolkit