Merge "There is an issue with scale, so change to LINEAR. and fixed pixel alignment...
[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   Toolkit::AtlasManager::AtlasSize EMPTY_SIZE;
45
46   bool IsBlockSizeSufficient( uint32_t width, uint32_t height, uint32_t requiredBlockWidth, uint32_t requiredBlockHeight )
47   {
48     return ( width + DOUBLE_PIXEL_PADDING <= requiredBlockWidth ) && ( height + DOUBLE_PIXEL_PADDING <= requiredBlockHeight );
49   }
50 }
51
52 AtlasManager::AtlasManager()
53 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
54 {
55   mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
56   mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
57   mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
58   mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
59 }
60
61 AtlasManagerPtr AtlasManager::New()
62 {
63   AtlasManagerPtr internal = new AtlasManager();
64   return internal;
65 }
66
67 AtlasManager::~AtlasManager()
68 {
69 }
70
71 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
72 {
73   SizeType width = size.mWidth;
74   SizeType height = size.mHeight;
75   SizeType blockWidth = size.mBlockWidth;
76   SizeType blockHeight = size.mBlockHeight;
77
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 )
80   {
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 );
83     return 0;
84   }
85
86   Dali::Texture atlas = Dali::Texture::New( TextureType::TEXTURE_2D, pixelformat, width, height );
87
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 );
94
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;
101
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 );
106
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 );
111
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();
119 }
120
121 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
122 {
123   mAddFailPolicy = policy;
124 }
125
126 bool AtlasManager::Add( const PixelData& image,
127                         Toolkit::AtlasManager::AtlasSlot& slot,
128                         Toolkit::AtlasManager::AtlasId atlas )
129 {
130   bool created = false;
131   Pixel::Format pixelFormat = image.GetPixelFormat();
132   SizeType width = image.GetWidth();
133   SizeType height = image.GetHeight();
134   SizeType foundAtlas = 0;
135   SizeType index = 0;
136   slot.mImageId = 0;
137
138   AtlasSlotDescriptor desc;
139
140   // If there is a preferred atlas then check for room in that first
141   if ( atlas-- )
142   {
143     foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
144   }
145
146   // Search current atlases to see if there is a good match
147   while( ( 0u == foundAtlas ) && ( index < mAtlasList.size() ) )
148   {
149     foundAtlas = CheckAtlas( index, width, height, pixelFormat );
150     ++index;
151   }
152
153   // If we can't find a suitable atlas then check the policy to determine action
154   if ( 0u == foundAtlas )
155   {
156     if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
157     {
158       if ( IsBlockSizeSufficient( width, height, mNewAtlasSize.mBlockWidth, mNewAtlasSize.mBlockHeight ) ) // Checks if image fits within the atlas blocks
159       {
160         foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat ); // Creating atlas with mNewAtlasSize, may not be the needed size!
161         if (  0u == foundAtlas )
162         {
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 );
168           return false;
169         }
170         else
171         {
172           created = true;
173         }
174       }
175     }
176
177     if ( (  0u == foundAtlas )  || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
178     {
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");
181       return false;
182     }
183   }
184
185   foundAtlas--; // Atlas created successfully, decrement by 1 to get <vector> index (starts at 0 not 1)
186
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 )
190   {
191     // Yes, so select our next block
192     desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
193   }
194   else
195   {
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() );
199   }
200
201   desc.mImageWidth = width;
202   desc.mImageHeight = height;
203   desc.mAtlasId = foundAtlas + 1u;  // Ids start from 1 not the 0 index
204   desc.mCount = 1u;
205
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 )
209   {
210     if ( !mImageList[ i ].mCount )
211     {
212       imageId = i + 1u;
213       break;
214     }
215   }
216   if ( !imageId )
217   {
218     mImageList.PushBack( desc );
219     slot.mImageId = mImageList.Size();
220   }
221   else
222   {
223     mImageList[ imageId - 1u ] = desc;
224     slot.mImageId = imageId;
225   }
226   slot.mAtlasId = foundAtlas + 1u; // Ids start from 1 not the 0 index
227
228   // Upload the buffer image into the atlas
229   UploadImage( image, desc );
230   return created;
231 }
232
233 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
234                                                  SizeType width,
235                                                  SizeType height,
236                                                  Pixel::Format pixelFormat )
237 {
238   AtlasManager::SizeType result = 0u;
239   if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
240   {
241     // Check to see if the image will fit in these blocks
242
243     const SizeType availableBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
244
245     if ( availableBlocks && IsBlockSizeSufficient( width, height,mAtlasList[ atlas ].mSize.mBlockWidth, mAtlasList[ atlas ].mSize.mBlockHeight ) )
246     {
247       result = atlas + 1u; // Atlas ids start from 1 not 0
248     }
249   }
250   return result;
251 }
252
253 void AtlasManager::UploadImage( const PixelData& image,
254                                 const AtlasSlotDescriptor& desc )
255 {
256   // Get the atlas to upload the image to
257   SizeType atlas = desc.mAtlasId - 1u;
258
259   // Check to see that the pixel formats are compatible
260   if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
261   {
262     DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
263     return;
264   }
265
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;
269
270   SizeType blockX = desc.mBlock % atlasWidthInBlocks;
271   SizeType blockY = desc.mBlock / atlasWidthInBlocks;
272   SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
273   SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
274
275   SizeType width = image.GetWidth();
276   SizeType height = image.GetHeight();
277
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,
282                                            width, height) )
283   {
284     DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
285   }
286
287   // Blit top strip
288   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
289                                            blockOffsetX,
290                                            blockOffsetY,
291                                            mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
292                                            mAtlasList[ atlas ].mHorizontalStrip.GetHeight()) )
293   {
294     DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
295   }
296
297   // Blit left strip
298   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
299                                            blockOffsetX,
300                                            blockOffsetY + SINGLE_PIXEL_PADDING,
301                                            mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
302                                            mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
303   {
304     DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
305   }
306
307   // Blit bottom strip
308   if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
309   {
310     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
311                                              blockOffsetX,
312                                              blockOffsetY + height + SINGLE_PIXEL_PADDING,
313                                              mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
314                                              mAtlasList[ atlas ].mHorizontalStrip.GetHeight() ) )
315     {
316       DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
317     }
318   }
319
320   // Blit right strip
321   if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
322   {
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() ) )
328     {
329       DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
330     }
331   }
332 }
333
334 void AtlasManager::GenerateMeshData( ImageId id,
335                                      const Vector2& position,
336                                      Toolkit::AtlasManager::Mesh2D& meshData,
337                                      bool addReference )
338 {
339   if ( id )
340   {
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;
346
347     AtlasMeshFactory::CreateQuad( width,
348                                   height,
349                                   mImageList[ imageId ].mBlock,
350                                   mAtlasList[ atlas ].mSize,
351                                   position,
352                                   meshData );
353
354     // Mesh created so increase the reference count, if we're asked to
355     if ( addReference )
356     {
357       mImageList[ imageId ].mCount++;
358     }
359   }
360   else
361   {
362     DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
363   }
364 }
365
366 Dali::Texture AtlasManager::GetAtlasContainer( AtlasId atlas ) const
367 {
368   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
369   Dali::Texture atlasContainer;
370   if ( atlas && atlas-- <= mAtlasList.size() )
371   {
372     atlasContainer = mAtlasList[ atlas ].mAtlas;
373   }
374   return atlasContainer;
375 }
376
377 bool AtlasManager::Remove( ImageId id )
378 {
379   // Decrements the reference count of this image, and removes the blocks if zero.
380   SizeType imageId = id - 1u;
381   bool removed = false;
382
383   if ( id > mImageList.Size() )
384      {
385     DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
386     return false;
387   }
388
389   // If we attempt to free an image that is already freed then do nothing, other than log
390   if ( !mImageList[ imageId ].mCount )
391   {
392     DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
393     return false;
394   }
395
396   if ( 2u > --mImageList[ imageId ].mCount )
397   {
398     // 'Remove the blocks' from this image and add to the atlas' freelist
399     removed = true;
400     mImageList[ imageId ].mCount = 0;
401     SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
402     mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
403   }
404   return removed;
405 }
406
407 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
408 {
409   DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
410   AtlasManager::AtlasId atlasId = 0u;
411   if ( id && id-- <= mImageList.Size() )
412   {
413     atlasId = mImageList[ id ].mAtlasId;
414   }
415   return atlasId;
416 }
417
418 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
419 {
420   mNewAtlasSize = size;
421
422   // Add on padding for borders around atlas entries
423   mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
424   mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
425 }
426
427 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
428 {
429   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
430   if ( atlas && atlas-- <= mAtlasList.size() )
431   {
432     return mAtlasList[ atlas ].mSize;
433   }
434   return EMPTY_SIZE;
435 }
436
437 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
438 {
439   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
440   AtlasManager::SizeType freeBlocks = 0u;
441   if ( atlas && atlas-- <= mAtlasList.size() )
442   {
443     freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
444   }
445   return freeBlocks;
446 }
447
448 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
449 {
450   return mAtlasList.size();
451 }
452
453 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) const
454 {
455   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
456   Pixel::Format pixelFormat = Pixel::RGBA8888;
457   if ( atlas && atlas-- <= mAtlasList.size() )
458   {
459     pixelFormat = mAtlasList[ atlas ].mPixelFormat;
460   }
461   return pixelFormat;
462 }
463
464 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
465 {
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);
471
472   for ( uint32_t i = 0; i < atlasCount; ++i )
473   {
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 );
478
479     metrics.mAtlasMetrics.PushBack( entry );
480
481     uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
482     if ( entry.mPixelFormat == Pixel::BGRA8888 )
483     {
484       size <<= 2;
485     }
486
487     textureMemoryUsed += size;
488
489   }
490   metrics.mTextureMemoryUsed = textureMemoryUsed;
491 }
492
493 TextureSet AtlasManager::GetTextures( AtlasId atlas ) const
494 {
495   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
496   TextureSet textureSet;
497   if ( atlas && atlas-- <= mAtlasList.size() )
498   {
499     textureSet = mAtlasList[ atlas ].mTextureSet;
500   }
501   return textureSet;
502 }
503
504 void AtlasManager::SetTextures( AtlasId atlas, TextureSet& textureSet )
505 {
506   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
507   if ( atlas && atlas-- <= mAtlasList.size() )
508   {
509     mAtlasList[ atlas ].mTextureSet = textureSet;
510   }
511 }
512
513 } // namespace Internal
514
515 } // namespace Toolkit
516
517 } // namespace Dali
518
519