dacb141218e2b8e31e03e226f159c0465b73818d
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / atlas-manager-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 // CLASS HEADER
18 #include <dali-toolkit/internal/text/rendering/atlas/atlas-manager-impl.h>
19
20 // EXTERNAL INCLUDES
21 #include <string.h>
22 #include <dali/integration-api/debug.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 namespace
37 {
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;
46 }
47
48 AtlasManager::AtlasManager()
49 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
50 {
51   mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
52   mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
53   mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
54   mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
55 }
56
57 AtlasManagerPtr AtlasManager::New()
58 {
59   AtlasManagerPtr internal = new AtlasManager();
60   return internal;
61 }
62
63 AtlasManager::~AtlasManager()
64 {
65 }
66
67 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
68 {
69   SizeType width = size.mWidth;
70   SizeType height = size.mHeight;
71   SizeType blockWidth = size.mBlockWidth;
72   SizeType blockHeight = size.mBlockHeight;
73
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 )
76   {
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 );
79     return 0;
80   }
81
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;
90
91   atlasDescriptor.mHorizontalStrip = BufferImage::New( blockWidth, SINGLE_PIXEL_PADDING, pixelformat );
92   atlasDescriptor.mVerticalStrip = BufferImage::New( SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat );
93
94   PixelBuffer* buffer = atlasDescriptor.mHorizontalStrip.GetBuffer();
95   if( buffer == NULL )
96   {
97     DALI_LOG_ERROR("atlasDescriptor.mHorizontalStrip.GetBuffer() returns NULL\n");
98     return 0;
99   }
100   memset( buffer, 0, atlasDescriptor.mHorizontalStrip.GetBufferSize() );
101
102   buffer = atlasDescriptor.mVerticalStrip.GetBuffer();
103   if( buffer == NULL )
104   {
105     DALI_LOG_ERROR("atlasDescriptor.mVerticalStrip.GetBuffer() returns NULL\n");
106     return 0;
107   }
108   memset( buffer, 0, atlasDescriptor.mVerticalStrip.GetBufferSize() );
109
110   BufferImage filledPixelImage = BufferImage::New( 1u, 1u, pixelformat );
111   buffer = filledPixelImage.GetBuffer();
112   if( buffer == NULL)
113   {
114     DALI_LOG_ERROR("filledPixelImage.GetBuffer() returns NULL\n");
115     return 0;
116   }
117
118   memset( buffer, 0xFF, filledPixelImage.GetBufferSize() );
119   atlas.Upload( filledPixelImage, 0, 0 );
120   mAtlasList.push_back( atlasDescriptor );
121   return mAtlasList.size();
122 }
123
124 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
125 {
126   mAddFailPolicy = policy;
127 }
128
129 bool AtlasManager::Add( const BufferImage& image,
130                         Toolkit::AtlasManager::AtlasSlot& slot,
131                         Toolkit::AtlasManager::AtlasId atlas )
132 {
133   bool created = false;
134   Pixel::Format pixelFormat = image.GetPixelFormat();
135   SizeType width = image.GetWidth();
136   SizeType height = image.GetHeight();
137   SizeType foundAtlas = 0;
138   SizeType index = 0;
139   slot.mImageId = 0;
140
141   AtlasSlotDescriptor desc;
142
143   // If there is a preferred atlas then check for room in that first
144   if ( atlas-- )
145   {
146     foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
147   }
148
149   // Search current atlases to see if there is a good match
150   while( !foundAtlas && index < mAtlasList.size() )
151   {
152     foundAtlas = CheckAtlas( index, width, height, pixelFormat );
153     ++index;
154   }
155
156   // If we can't find a suitable atlas then check the policy to determine action
157   if ( !foundAtlas-- )
158   {
159     if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
160     {
161       foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
162       if ( !foundAtlas-- )
163       {
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 );
169         return created;
170       }
171       created = true;
172       foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
173     }
174
175     if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
176     {
177       // Haven't found an atlas for this image!!!!!!
178       DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
179       return created;
180     }
181   }
182
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 )
186   {
187     // Yes, so select our next block
188     desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
189   }
190   else
191   {
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() );
195   }
196
197   desc.mImageWidth = width;
198   desc.mImageHeight = height;
199   desc.mAtlasId = foundAtlas + 1u;
200   desc.mCount = 1u;
201
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 )
205   {
206     if ( !mImageList[ i ].mCount )
207     {
208       imageId = i + 1u;
209       break;
210     }
211   }
212   if ( !imageId )
213   {
214     mImageList.PushBack( desc );
215     slot.mImageId = mImageList.Size();
216   }
217   else
218   {
219     mImageList[ imageId - 1u ] = desc;
220     slot.mImageId = imageId;
221   }
222   slot.mAtlasId = foundAtlas + 1u;
223
224   // Upload the buffer image into the atlas
225   UploadImage( image, desc );
226   return created;
227 }
228
229 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
230                                                  SizeType width,
231                                                  SizeType height,
232                                                  Pixel::Format pixelFormat )
233 {
234   AtlasManager::SizeType result = 0u;
235   if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
236   {
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 )
241     {
242       result = atlas + 1u;
243     }
244   }
245   return result;
246 }
247
248 void AtlasManager::UploadImage( const BufferImage& image,
249                                 const AtlasSlotDescriptor& desc )
250 {
251   // Get the atlas to upload the image to
252   SizeType atlas = desc.mAtlasId - 1u;
253
254   // Check to see that the pixel formats are compatible
255   if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
256   {
257     DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
258     return;
259   }
260
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;
264
265   SizeType blockX = desc.mBlock % atlasWidthInBlocks;
266   SizeType blockY = desc.mBlock / atlasWidthInBlocks;
267   SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
268   SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
269
270   SizeType width = image.GetWidth();
271   SizeType height = image.GetHeight();
272
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 ) )
277   {
278     DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
279   }
280
281   // Blit top strip
282   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip,
283                                            blockOffsetX,
284                                            blockOffsetY ) )
285   {
286     DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
287   }
288
289   // Blit left strip
290   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip,
291                                            blockOffsetX,
292                                            blockOffsetY + SINGLE_PIXEL_PADDING ) )
293   {
294     DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
295   }
296
297   // Blit bottom strip
298   if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
299   {
300     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip,
301                                              blockOffsetX,
302                                              blockOffsetY + height + SINGLE_PIXEL_PADDING ) )
303     {
304       DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
305     }
306   }
307
308   // Blit right strip
309   if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
310   {
311     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip,
312                                              blockOffsetX + width + SINGLE_PIXEL_PADDING,
313                                              blockOffsetY + SINGLE_PIXEL_PADDING ) )
314     {
315       DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
316     }
317   }
318 }
319
320 void AtlasManager::GenerateMeshData( ImageId id,
321                                      const Vector2& position,
322                                      Toolkit::AtlasManager::Mesh2D& meshData,
323                                      bool addReference )
324 {
325   if ( id )
326   {
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;
332
333     AtlasMeshFactory::CreateQuad( width,
334                                   height,
335                                   mImageList[ imageId ].mBlock,
336                                   mAtlasList[ atlas ].mSize,
337                                   position,
338                                   meshData );
339
340     // Mesh created so increase the reference count, if we're asked to
341     if ( addReference )
342     {
343       mImageList[ imageId ].mCount++;
344     }
345   }
346   else
347   {
348     DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
349   }
350 }
351
352 Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
353 {
354   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
355   Dali::Atlas atlasContainer;
356   if ( atlas && atlas-- <= mAtlasList.size() )
357   {
358     atlasContainer = mAtlasList[ atlas ].mAtlas;
359   }
360   return atlasContainer;
361 }
362
363 bool AtlasManager::Remove( ImageId id )
364 {
365   // Decrements the reference count of this image, and removes the blocks if zero.
366   SizeType imageId = id - 1u;
367   bool removed = false;
368
369   if ( id > mImageList.Size() )
370      {
371     DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
372     return false;
373   }
374
375   // If we attempt to free an image that is already freed then do nothing, other than log
376   if ( !mImageList[ imageId ].mCount )
377   {
378     DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
379     return false;
380   }
381
382   if ( 2u > --mImageList[ imageId ].mCount )
383   {
384     // 'Remove the blocks' from this image and add to the atlas' freelist
385     removed = true;
386     mImageList[ imageId ].mCount = 0;
387     SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
388     mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
389   }
390   return removed;
391 }
392
393 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
394 {
395   DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
396   AtlasManager::AtlasId atlasId = 0u;
397   if ( id && id-- <= mImageList.Size() )
398   {
399     atlasId = mImageList[ id ].mAtlasId;
400   }
401   return atlasId;
402 }
403
404 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
405 {
406   mNewAtlasSize = size;
407
408   // Add on padding for borders around atlas entries
409   mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
410   mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
411 }
412
413 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
414 {
415   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
416   if ( atlas && atlas-- <= mAtlasList.size() )
417   {
418     return mAtlasList[ atlas ].mSize;
419   }
420   return EMPTY_SIZE;
421 }
422
423 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
424 {
425   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
426   AtlasManager::SizeType freeBlocks = 0u;
427   if ( atlas && atlas-- <= mAtlasList.size() )
428   {
429     freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
430   }
431   return freeBlocks;
432 }
433
434 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
435 {
436   return mAtlasList.size();
437 }
438
439 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
440 {
441   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
442   Pixel::Format pixelFormat = Pixel::RGBA8888;
443   if ( atlas && atlas-- <= mAtlasList.size() )
444   {
445     pixelFormat = mAtlasList[ atlas ].mPixelFormat;
446   }
447   return pixelFormat;
448 }
449
450 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
451 {
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);
457
458   for ( uint32_t i = 0; i < atlasCount; ++i )
459   {
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 );
464
465     metrics.mAtlasMetrics.PushBack( entry );
466
467     uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
468     if ( entry.mPixelFormat == Pixel::BGRA8888 )
469     {
470       size <<= 2;
471     }
472
473     textureMemoryUsed += size;
474
475   }
476   metrics.mTextureMemoryUsed = textureMemoryUsed;
477 }
478
479 Material AtlasManager::GetMaterial( AtlasId atlas ) const
480 {
481   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
482   Material material;
483   if ( atlas && atlas-- <= mAtlasList.size() )
484   {
485     material = mAtlasList[ atlas ].mMaterial;
486   }
487   return material;
488 }
489
490 void AtlasManager::SetMaterial( AtlasId atlas, Material& material )
491 {
492   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
493   if ( atlas && atlas-- <= mAtlasList.size() )
494   {
495     mAtlasList[ atlas ].mMaterial = material;
496   }
497 }
498
499 } // namespace Internal
500
501 } // namespace Toolkit
502
503 } // namespace Dali
504
505