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 );
83 // Clear the background
84 unsigned int bufferSize( width * height * Dali::Pixel::GetBytesPerPixel( pixelformat ) );
85 unsigned char* background = new unsigned char[bufferSize];
86 memset( background, 0, bufferSize );
87 PixelData backgroundPixels = PixelData::New( background, bufferSize, width, height, pixelformat, PixelData::DELETE_ARRAY );
88 atlas.Upload( backgroundPixels, 0u, 0u, 0u, 0u, width, height );
90 AtlasDescriptor atlasDescriptor;
91 atlasDescriptor.mAtlas = atlas;
92 atlasDescriptor.mSize = size;
93 atlasDescriptor.mPixelFormat = pixelformat;
94 atlasDescriptor.mTotalBlocks = ( ( width - 1u ) / blockWidth ) * ( ( height - 1u ) / blockHeight );
95 atlasDescriptor.mAvailableBlocks = atlasDescriptor.mTotalBlocks;
97 bufferSize = blockWidth * SINGLE_PIXEL_PADDING * Dali::Pixel::GetBytesPerPixel(pixelformat);
98 unsigned char* bufferHorizontalStrip = new unsigned char[bufferSize];
99 memset( bufferHorizontalStrip, 0, bufferSize );
100 atlasDescriptor.mHorizontalStrip = PixelData::New( bufferHorizontalStrip, bufferSize, blockWidth, SINGLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
102 bufferSize = SINGLE_PIXEL_PADDING * (blockHeight - DOUBLE_PIXEL_PADDING) * Dali::Pixel::GetBytesPerPixel(pixelformat);
103 unsigned char* bufferVerticalStrip = new unsigned char[bufferSize];
104 memset( bufferVerticalStrip, 0, bufferSize );
105 atlasDescriptor.mVerticalStrip = PixelData::New( bufferVerticalStrip, bufferSize, SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
107 bufferSize = Dali::Pixel::GetBytesPerPixel(pixelformat);
108 unsigned char* buffer = new unsigned char[bufferSize];
109 memset( buffer, 0xFF, bufferSize );
110 PixelData filledPixelImage = PixelData::New( buffer, bufferSize, 1u, 1u, pixelformat, PixelData::DELETE_ARRAY );
111 atlas.Upload( filledPixelImage, 0u, 0u, 0u, 0u, 1u, 1u );
112 mAtlasList.push_back( atlasDescriptor );
113 return mAtlasList.size();
116 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
118 mAddFailPolicy = policy;
121 bool AtlasManager::Add( const PixelData& image,
122 Toolkit::AtlasManager::AtlasSlot& slot,
123 Toolkit::AtlasManager::AtlasId atlas )
125 bool created = false;
126 Pixel::Format pixelFormat = image.GetPixelFormat();
127 SizeType width = image.GetWidth();
128 SizeType height = image.GetHeight();
129 SizeType foundAtlas = 0;
133 AtlasSlotDescriptor desc;
135 // If there is a preferred atlas then check for room in that first
138 foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
141 // Search current atlases to see if there is a good match
142 while( !foundAtlas && index < mAtlasList.size() )
144 foundAtlas = CheckAtlas( index, width, height, pixelFormat );
148 // If we can't find a suitable atlas then check the policy to determine action
151 if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
153 foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
156 DALI_LOG_ERROR("Failed to create an atlas of %i x %i blocksize: %i x %i.\n",
157 mNewAtlasSize.mWidth,
158 mNewAtlasSize.mHeight,
159 mNewAtlasSize.mBlockWidth,
160 mNewAtlasSize.mBlockHeight );
164 foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
167 if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
169 // Haven't found an atlas for this image!!!!!!
170 DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
175 // Work out which the block we're going to use
176 // Is there currently a next free block available ?
177 if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
179 // Yes, so select our next block
180 desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
184 // Our next block must be from the free list, fetch from the start of the list
185 desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
186 mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
189 desc.mImageWidth = width;
190 desc.mImageHeight = height;
191 desc.mAtlasId = foundAtlas + 1u;
194 // See if there's a previously freed image ID that we can assign to this new image
195 uint32_t imageId = 0u;
196 for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
198 if ( !mImageList[ i ].mCount )
206 mImageList.PushBack( desc );
207 slot.mImageId = mImageList.Size();
211 mImageList[ imageId - 1u ] = desc;
212 slot.mImageId = imageId;
214 slot.mAtlasId = foundAtlas + 1u;
216 // Upload the buffer image into the atlas
217 UploadImage( image, desc );
221 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
224 Pixel::Format pixelFormat )
226 AtlasManager::SizeType result = 0u;
227 if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
229 // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
230 if ( ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() )
231 && width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
232 && height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
240 void AtlasManager::UploadImage( const PixelData& image,
241 const AtlasSlotDescriptor& desc )
243 // Get the atlas to upload the image to
244 SizeType atlas = desc.mAtlasId - 1u;
246 // Check to see that the pixel formats are compatible
247 if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
249 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
253 SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
254 SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
255 SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
257 SizeType blockX = desc.mBlock % atlasWidthInBlocks;
258 SizeType blockY = desc.mBlock / atlasWidthInBlocks;
259 SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
260 SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
262 SizeType width = image.GetWidth();
263 SizeType height = image.GetHeight();
265 // Blit image 1 pixel to the right and down into the block to compensate for texture filtering
266 if ( !mAtlasList[ atlas ].mAtlas.Upload( image, 0u, 0u,
267 blockOffsetX + SINGLE_PIXEL_PADDING,
268 blockOffsetY + SINGLE_PIXEL_PADDING,
271 DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
275 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
278 mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
279 mAtlasList[ atlas ].mHorizontalStrip.GetHeight()) )
281 DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
285 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
287 blockOffsetY + SINGLE_PIXEL_PADDING,
288 mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
289 mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
291 DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
295 if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
297 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
299 blockOffsetY + height + SINGLE_PIXEL_PADDING,
300 mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
301 mAtlasList[ atlas ].mHorizontalStrip.GetHeight() ) )
303 DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
308 if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
310 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
311 blockOffsetX + width + SINGLE_PIXEL_PADDING,
312 blockOffsetY + SINGLE_PIXEL_PADDING,
313 mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
314 mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
316 DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
321 void AtlasManager::GenerateMeshData( ImageId id,
322 const Vector2& position,
323 Toolkit::AtlasManager::Mesh2D& meshData,
328 // Read the atlas Id to use for this image
329 SizeType imageId = id - 1u;
330 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
331 SizeType width = mImageList[ imageId ].mImageWidth;
332 SizeType height = mImageList[ imageId ].mImageHeight;
334 AtlasMeshFactory::CreateQuad( width,
336 mImageList[ imageId ].mBlock,
337 mAtlasList[ atlas ].mSize,
341 // Mesh created so increase the reference count, if we're asked to
344 mImageList[ imageId ].mCount++;
349 DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
353 Dali::Texture AtlasManager::GetAtlasContainer( AtlasId atlas ) const
355 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
356 Dali::Texture atlasContainer;
357 if ( atlas && atlas-- <= mAtlasList.size() )
359 atlasContainer = mAtlasList[ atlas ].mAtlas;
361 return atlasContainer;
364 bool AtlasManager::Remove( ImageId id )
366 // Decrements the reference count of this image, and removes the blocks if zero.
367 SizeType imageId = id - 1u;
368 bool removed = false;
370 if ( id > mImageList.Size() )
372 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
376 // If we attempt to free an image that is already freed then do nothing, other than log
377 if ( !mImageList[ imageId ].mCount )
379 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
383 if ( 2u > --mImageList[ imageId ].mCount )
385 // 'Remove the blocks' from this image and add to the atlas' freelist
387 mImageList[ imageId ].mCount = 0;
388 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
389 mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
394 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
396 DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
397 AtlasManager::AtlasId atlasId = 0u;
398 if ( id && id-- <= mImageList.Size() )
400 atlasId = mImageList[ id ].mAtlasId;
405 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
407 mNewAtlasSize = size;
409 // Add on padding for borders around atlas entries
410 mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
411 mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
414 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
416 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
417 if ( atlas && atlas-- <= mAtlasList.size() )
419 return mAtlasList[ atlas ].mSize;
424 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
426 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
427 AtlasManager::SizeType freeBlocks = 0u;
428 if ( atlas && atlas-- <= mAtlasList.size() )
430 freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
435 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
437 return mAtlasList.size();
440 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) const
442 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
443 Pixel::Format pixelFormat = Pixel::RGBA8888;
444 if ( atlas && atlas-- <= mAtlasList.size() )
446 pixelFormat = mAtlasList[ atlas ].mPixelFormat;
451 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
453 Toolkit::AtlasManager::AtlasMetricsEntry entry;
454 uint32_t textureMemoryUsed = 0;
455 uint32_t atlasCount = mAtlasList.size();
456 metrics.mAtlasCount = atlasCount;
457 metrics.mAtlasMetrics.Resize(0);
459 for ( uint32_t i = 0; i < atlasCount; ++i )
461 entry.mSize = mAtlasList[ i ].mSize;
462 entry.mTotalBlocks = mAtlasList[ i ].mTotalBlocks;
463 entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[ i ].mAvailableBlocks + mAtlasList[ i ].mFreeBlocksList.Size();
464 entry.mPixelFormat = GetPixelFormat( i + 1 );
466 metrics.mAtlasMetrics.PushBack( entry );
468 uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
469 if ( entry.mPixelFormat == Pixel::BGRA8888 )
474 textureMemoryUsed += size;
477 metrics.mTextureMemoryUsed = textureMemoryUsed;
480 TextureSet AtlasManager::GetTextures( AtlasId atlas ) const
482 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
483 TextureSet textureSet;
484 if ( atlas && atlas-- <= mAtlasList.size() )
486 textureSet = mAtlasList[ atlas ].mTextureSet;
491 void AtlasManager::SetTextures( AtlasId atlas, TextureSet& textureSet )
493 DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
494 if ( atlas && atlas-- <= mAtlasList.size() )
496 mAtlasList[ atlas ].mTextureSet = textureSet;
500 } // namespace Internal
502 } // namespace Toolkit