Text - Fix for emojis resampling.
[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
47 AtlasManager::AtlasManager()
48 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
49 {
50   mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
51   mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
52   mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
53   mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
54 }
55
56 AtlasManagerPtr AtlasManager::New()
57 {
58   AtlasManagerPtr internal = new AtlasManager();
59   return internal;
60 }
61
62 AtlasManager::~AtlasManager()
63 {
64 }
65
66 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
67 {
68   SizeType width = size.mWidth;
69   SizeType height = size.mHeight;
70   SizeType blockWidth = size.mBlockWidth;
71   SizeType blockHeight = size.mBlockHeight;
72
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 )
75   {
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 );
78     return 0;
79   }
80
81   Dali::Texture atlas = Dali::Texture::New( TextureType::TEXTURE_2D, pixelformat, width, height );
82
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 );
89
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;
96
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 );
101
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 );
106
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();
114 }
115
116 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
117 {
118   mAddFailPolicy = policy;
119 }
120
121 bool AtlasManager::Add( const PixelData& image,
122                         Toolkit::AtlasManager::AtlasSlot& slot,
123                         Toolkit::AtlasManager::AtlasId atlas )
124 {
125   bool created = false;
126   Pixel::Format pixelFormat = image.GetPixelFormat();
127   SizeType width = image.GetWidth();
128   SizeType height = image.GetHeight();
129   SizeType foundAtlas = 0;
130   SizeType index = 0;
131   slot.mImageId = 0;
132
133   AtlasSlotDescriptor desc;
134
135   // If there is a preferred atlas then check for room in that first
136   if ( atlas-- )
137   {
138     foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
139   }
140
141   // Search current atlases to see if there is a good match
142   while( !foundAtlas && index < mAtlasList.size() )
143   {
144     foundAtlas = CheckAtlas( index, width, height, pixelFormat );
145     ++index;
146   }
147
148   // If we can't find a suitable atlas then check the policy to determine action
149   if ( !foundAtlas-- )
150   {
151     if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
152     {
153       foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
154       if ( !foundAtlas-- )
155       {
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 );
161         return created;
162       }
163       created = true;
164       foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
165     }
166
167     if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
168     {
169       // Haven't found an atlas for this image!!!!!!
170       DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
171       return created;
172     }
173   }
174
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 )
178   {
179     // Yes, so select our next block
180     desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
181   }
182   else
183   {
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() );
187   }
188
189   desc.mImageWidth = width;
190   desc.mImageHeight = height;
191   desc.mAtlasId = foundAtlas + 1u;
192   desc.mCount = 1u;
193
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 )
197   {
198     if ( !mImageList[ i ].mCount )
199     {
200       imageId = i + 1u;
201       break;
202     }
203   }
204   if ( !imageId )
205   {
206     mImageList.PushBack( desc );
207     slot.mImageId = mImageList.Size();
208   }
209   else
210   {
211     mImageList[ imageId - 1u ] = desc;
212     slot.mImageId = imageId;
213   }
214   slot.mAtlasId = foundAtlas + 1u;
215
216   // Upload the buffer image into the atlas
217   UploadImage( image, desc );
218   return created;
219 }
220
221 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
222                                                  SizeType width,
223                                                  SizeType height,
224                                                  Pixel::Format pixelFormat )
225 {
226   AtlasManager::SizeType result = 0u;
227   if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
228   {
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 )
233     {
234       result = atlas + 1u;
235     }
236   }
237   return result;
238 }
239
240 void AtlasManager::UploadImage( const PixelData& image,
241                                 const AtlasSlotDescriptor& desc )
242 {
243   // Get the atlas to upload the image to
244   SizeType atlas = desc.mAtlasId - 1u;
245
246   // Check to see that the pixel formats are compatible
247   if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
248   {
249     DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
250     return;
251   }
252
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;
256
257   SizeType blockX = desc.mBlock % atlasWidthInBlocks;
258   SizeType blockY = desc.mBlock / atlasWidthInBlocks;
259   SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
260   SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
261
262   SizeType width = image.GetWidth();
263   SizeType height = image.GetHeight();
264
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,
269                                            width, height) )
270   {
271     DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
272   }
273
274   // Blit top strip
275   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
276                                            blockOffsetX,
277                                            blockOffsetY,
278                                            mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
279                                            mAtlasList[ atlas ].mHorizontalStrip.GetHeight()) )
280   {
281     DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
282   }
283
284   // Blit left strip
285   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
286                                            blockOffsetX,
287                                            blockOffsetY + SINGLE_PIXEL_PADDING,
288                                            mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
289                                            mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
290   {
291     DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
292   }
293
294   // Blit bottom strip
295   if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
296   {
297     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
298                                              blockOffsetX,
299                                              blockOffsetY + height + SINGLE_PIXEL_PADDING,
300                                              mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
301                                              mAtlasList[ atlas ].mHorizontalStrip.GetHeight() ) )
302     {
303       DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
304     }
305   }
306
307   // Blit right strip
308   if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
309   {
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() ) )
315     {
316       DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
317     }
318   }
319 }
320
321 void AtlasManager::GenerateMeshData( ImageId id,
322                                      const Vector2& position,
323                                      Toolkit::AtlasManager::Mesh2D& meshData,
324                                      bool addReference )
325 {
326   if ( id )
327   {
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;
333
334     AtlasMeshFactory::CreateQuad( width,
335                                   height,
336                                   mImageList[ imageId ].mBlock,
337                                   mAtlasList[ atlas ].mSize,
338                                   position,
339                                   meshData );
340
341     // Mesh created so increase the reference count, if we're asked to
342     if ( addReference )
343     {
344       mImageList[ imageId ].mCount++;
345     }
346   }
347   else
348   {
349     DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
350   }
351 }
352
353 Dali::Texture AtlasManager::GetAtlasContainer( AtlasId atlas ) const
354 {
355   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
356   Dali::Texture atlasContainer;
357   if ( atlas && atlas-- <= mAtlasList.size() )
358   {
359     atlasContainer = mAtlasList[ atlas ].mAtlas;
360   }
361   return atlasContainer;
362 }
363
364 bool AtlasManager::Remove( ImageId id )
365 {
366   // Decrements the reference count of this image, and removes the blocks if zero.
367   SizeType imageId = id - 1u;
368   bool removed = false;
369
370   if ( id > mImageList.Size() )
371      {
372     DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
373     return false;
374   }
375
376   // If we attempt to free an image that is already freed then do nothing, other than log
377   if ( !mImageList[ imageId ].mCount )
378   {
379     DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
380     return false;
381   }
382
383   if ( 2u > --mImageList[ imageId ].mCount )
384   {
385     // 'Remove the blocks' from this image and add to the atlas' freelist
386     removed = true;
387     mImageList[ imageId ].mCount = 0;
388     SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
389     mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
390   }
391   return removed;
392 }
393
394 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
395 {
396   DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
397   AtlasManager::AtlasId atlasId = 0u;
398   if ( id && id-- <= mImageList.Size() )
399   {
400     atlasId = mImageList[ id ].mAtlasId;
401   }
402   return atlasId;
403 }
404
405 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
406 {
407   mNewAtlasSize = size;
408
409   // Add on padding for borders around atlas entries
410   mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
411   mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
412 }
413
414 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
415 {
416   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
417   if ( atlas && atlas-- <= mAtlasList.size() )
418   {
419     return mAtlasList[ atlas ].mSize;
420   }
421   return EMPTY_SIZE;
422 }
423
424 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
425 {
426   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
427   AtlasManager::SizeType freeBlocks = 0u;
428   if ( atlas && atlas-- <= mAtlasList.size() )
429   {
430     freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
431   }
432   return freeBlocks;
433 }
434
435 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
436 {
437   return mAtlasList.size();
438 }
439
440 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) const
441 {
442   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
443   Pixel::Format pixelFormat = Pixel::RGBA8888;
444   if ( atlas && atlas-- <= mAtlasList.size() )
445   {
446     pixelFormat = mAtlasList[ atlas ].mPixelFormat;
447   }
448   return pixelFormat;
449 }
450
451 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
452 {
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);
458
459   for ( uint32_t i = 0; i < atlasCount; ++i )
460   {
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 );
465
466     metrics.mAtlasMetrics.PushBack( entry );
467
468     uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
469     if ( entry.mPixelFormat == Pixel::BGRA8888 )
470     {
471       size <<= 2;
472     }
473
474     textureMemoryUsed += size;
475
476   }
477   metrics.mTextureMemoryUsed = textureMemoryUsed;
478 }
479
480 TextureSet AtlasManager::GetTextures( AtlasId atlas ) const
481 {
482   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
483   TextureSet textureSet;
484   if ( atlas && atlas-- <= mAtlasList.size() )
485   {
486     textureSet = mAtlasList[ atlas ].mTextureSet;
487   }
488   return textureSet;
489 }
490
491 void AtlasManager::SetTextures( AtlasId atlas, TextureSet& textureSet )
492 {
493   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
494   if ( atlas && atlas-- <= mAtlasList.size() )
495   {
496     mAtlasList[ atlas ].mTextureSet = textureSet;
497   }
498 }
499
500 } // namespace Internal
501
502 } // namespace Toolkit
503
504 } // namespace Dali
505
506