Changed Atlas manager to use Dali::Texture instead of Dali::Atlas
[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   AtlasDescriptor atlasDescriptor;
83   atlasDescriptor.mAtlas = atlas;
84   atlasDescriptor.mSize = size;
85   atlasDescriptor.mPixelFormat = pixelformat;
86   atlasDescriptor.mTotalBlocks = ( ( width - 1u ) / blockWidth ) * ( ( height - 1u ) / blockHeight );
87   atlasDescriptor.mAvailableBlocks = atlasDescriptor.mTotalBlocks;
88
89   unsigned int bufferSize(  blockWidth * SINGLE_PIXEL_PADDING * Dali::Pixel::GetBytesPerPixel(pixelformat) );
90   unsigned char* bufferHorizontalStrip = new unsigned char[bufferSize];
91   memset( bufferHorizontalStrip, 0, bufferSize );
92   atlasDescriptor.mHorizontalStrip = PixelData::New( bufferHorizontalStrip, bufferSize, blockWidth, SINGLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
93
94   bufferSize = SINGLE_PIXEL_PADDING * (blockHeight - DOUBLE_PIXEL_PADDING) * Dali::Pixel::GetBytesPerPixel(pixelformat);
95   unsigned char* bufferVerticalStrip = new unsigned char[bufferSize];
96   memset( bufferVerticalStrip, 0, bufferSize );
97   atlasDescriptor.mVerticalStrip = PixelData::New( bufferVerticalStrip, bufferSize, SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat, PixelData::DELETE_ARRAY );
98
99   bufferSize = Dali::Pixel::GetBytesPerPixel(pixelformat);
100   unsigned char* buffer = new unsigned char[bufferSize];
101   memset( buffer, 0xFF, bufferSize );
102   PixelData filledPixelImage = PixelData::New( buffer, bufferSize, 1u, 1u, pixelformat, PixelData::DELETE_ARRAY );
103   atlas.Upload( filledPixelImage, 0u, 0u, 0u, 0u, 1u, 1u );
104   mAtlasList.push_back( atlasDescriptor );
105   return mAtlasList.size();
106 }
107
108 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
109 {
110   mAddFailPolicy = policy;
111 }
112
113 bool AtlasManager::Add( const PixelData& image,
114                         Toolkit::AtlasManager::AtlasSlot& slot,
115                         Toolkit::AtlasManager::AtlasId atlas )
116 {
117   bool created = false;
118   Pixel::Format pixelFormat = image.GetPixelFormat();
119   SizeType width = image.GetWidth();
120   SizeType height = image.GetHeight();
121   SizeType foundAtlas = 0;
122   SizeType index = 0;
123   slot.mImageId = 0;
124
125   AtlasSlotDescriptor desc;
126
127   // If there is a preferred atlas then check for room in that first
128   if ( atlas-- )
129   {
130     foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
131   }
132
133   // Search current atlases to see if there is a good match
134   while( !foundAtlas && index < mAtlasList.size() )
135   {
136     foundAtlas = CheckAtlas( index, width, height, pixelFormat );
137     ++index;
138   }
139
140   // If we can't find a suitable atlas then check the policy to determine action
141   if ( !foundAtlas-- )
142   {
143     if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
144     {
145       foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
146       if ( !foundAtlas-- )
147       {
148         DALI_LOG_ERROR("Failed to create an atlas of %i x %i blocksize: %i x %i.\n",
149                        mNewAtlasSize.mWidth,
150                        mNewAtlasSize.mHeight,
151                        mNewAtlasSize.mBlockWidth,
152                        mNewAtlasSize.mBlockHeight );
153         return created;
154       }
155       created = true;
156       foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
157     }
158
159     if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
160     {
161       // Haven't found an atlas for this image!!!!!!
162       DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
163       return created;
164     }
165   }
166
167   // Work out which the block we're going to use
168   // Is there currently a next free block available ?
169   if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
170   {
171     // Yes, so select our next block
172     desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
173   }
174   else
175   {
176     // Our next block must be from the free list, fetch from the start of the list
177     desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
178     mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
179   }
180
181   desc.mImageWidth = width;
182   desc.mImageHeight = height;
183   desc.mAtlasId = foundAtlas + 1u;
184   desc.mCount = 1u;
185
186   // See if there's a previously freed image ID that we can assign to this new image
187   uint32_t imageId = 0u;
188   for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
189   {
190     if ( !mImageList[ i ].mCount )
191     {
192       imageId = i + 1u;
193       break;
194     }
195   }
196   if ( !imageId )
197   {
198     mImageList.PushBack( desc );
199     slot.mImageId = mImageList.Size();
200   }
201   else
202   {
203     mImageList[ imageId - 1u ] = desc;
204     slot.mImageId = imageId;
205   }
206   slot.mAtlasId = foundAtlas + 1u;
207
208   // Upload the buffer image into the atlas
209   UploadImage( image, desc );
210   return created;
211 }
212
213 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
214                                                  SizeType width,
215                                                  SizeType height,
216                                                  Pixel::Format pixelFormat )
217 {
218   AtlasManager::SizeType result = 0u;
219   if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
220   {
221     // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
222     if ( ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() )
223            && width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
224            && height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
225     {
226       result = atlas + 1u;
227     }
228   }
229   return result;
230 }
231
232 void AtlasManager::UploadImage( const PixelData& image,
233                                 const AtlasSlotDescriptor& desc )
234 {
235   // Get the atlas to upload the image to
236   SizeType atlas = desc.mAtlasId - 1u;
237
238   // Check to see that the pixel formats are compatible
239   if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
240   {
241     DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
242     return;
243   }
244
245   SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
246   SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
247   SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
248
249   SizeType blockX = desc.mBlock % atlasWidthInBlocks;
250   SizeType blockY = desc.mBlock / atlasWidthInBlocks;
251   SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
252   SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
253
254   SizeType width = image.GetWidth();
255   SizeType height = image.GetHeight();
256
257   // Blit image 1 pixel to the right and down into the block to compensate for texture filtering
258   if ( !mAtlasList[ atlas ].mAtlas.Upload( image, 0u, 0u,
259                                            blockOffsetX + SINGLE_PIXEL_PADDING,
260                                            blockOffsetY + SINGLE_PIXEL_PADDING,
261                                            width, height) )
262   {
263     DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
264   }
265
266   // Blit top strip
267   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
268                                            blockOffsetX,
269                                            blockOffsetY,
270                                            mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
271                                            mAtlasList[ atlas ].mHorizontalStrip.GetHeight()) )
272   {
273     DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
274   }
275
276   // Blit left strip
277   if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
278                                            blockOffsetX,
279                                            blockOffsetY + SINGLE_PIXEL_PADDING,
280                                            mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
281                                            mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
282   {
283     DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
284   }
285
286   // Blit bottom strip
287   if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
288   {
289     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, 0u, 0u,
290                                              blockOffsetX,
291                                              blockOffsetY + height + SINGLE_PIXEL_PADDING,
292                                              mAtlasList[ atlas ].mHorizontalStrip.GetWidth(),
293                                              mAtlasList[ atlas ].mHorizontalStrip.GetHeight() ) )
294     {
295       DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
296     }
297   }
298
299   // Blit right strip
300   if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
301   {
302     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, 0u, 0u,
303                                              blockOffsetX + width + SINGLE_PIXEL_PADDING,
304                                              blockOffsetY + SINGLE_PIXEL_PADDING,
305                                              mAtlasList[ atlas ].mVerticalStrip.GetWidth(),
306                                              mAtlasList[ atlas ].mVerticalStrip.GetHeight() ) )
307     {
308       DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
309     }
310   }
311 }
312
313 void AtlasManager::GenerateMeshData( ImageId id,
314                                      const Vector2& position,
315                                      Toolkit::AtlasManager::Mesh2D& meshData,
316                                      bool addReference )
317 {
318   if ( id )
319   {
320     // Read the atlas Id to use for this image
321     SizeType imageId = id - 1u;
322     SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
323     SizeType width = mImageList[ imageId ].mImageWidth;
324     SizeType height = mImageList[ imageId ].mImageHeight;
325
326     AtlasMeshFactory::CreateQuad( width,
327                                   height,
328                                   mImageList[ imageId ].mBlock,
329                                   mAtlasList[ atlas ].mSize,
330                                   position,
331                                   meshData );
332
333     // Mesh created so increase the reference count, if we're asked to
334     if ( addReference )
335     {
336       mImageList[ imageId ].mCount++;
337     }
338   }
339   else
340   {
341     DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
342   }
343 }
344
345 Dali::Texture AtlasManager::GetAtlasContainer( AtlasId atlas ) const
346 {
347   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
348   Dali::Texture atlasContainer;
349   if ( atlas && atlas-- <= mAtlasList.size() )
350   {
351     atlasContainer = mAtlasList[ atlas ].mAtlas;
352   }
353   return atlasContainer;
354 }
355
356 bool AtlasManager::Remove( ImageId id )
357 {
358   // Decrements the reference count of this image, and removes the blocks if zero.
359   SizeType imageId = id - 1u;
360   bool removed = false;
361
362   if ( id > mImageList.Size() )
363      {
364     DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
365     return false;
366   }
367
368   // If we attempt to free an image that is already freed then do nothing, other than log
369   if ( !mImageList[ imageId ].mCount )
370   {
371     DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
372     return false;
373   }
374
375   if ( 2u > --mImageList[ imageId ].mCount )
376   {
377     // 'Remove the blocks' from this image and add to the atlas' freelist
378     removed = true;
379     mImageList[ imageId ].mCount = 0;
380     SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
381     mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
382   }
383   return removed;
384 }
385
386 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
387 {
388   DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
389   AtlasManager::AtlasId atlasId = 0u;
390   if ( id && id-- <= mImageList.Size() )
391   {
392     atlasId = mImageList[ id ].mAtlasId;
393   }
394   return atlasId;
395 }
396
397 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
398 {
399   mNewAtlasSize = size;
400
401   // Add on padding for borders around atlas entries
402   mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
403   mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
404 }
405
406 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
407 {
408   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
409   if ( atlas && atlas-- <= mAtlasList.size() )
410   {
411     return mAtlasList[ atlas ].mSize;
412   }
413   return EMPTY_SIZE;
414 }
415
416 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
417 {
418   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
419   AtlasManager::SizeType freeBlocks = 0u;
420   if ( atlas && atlas-- <= mAtlasList.size() )
421   {
422     freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
423   }
424   return freeBlocks;
425 }
426
427 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
428 {
429   return mAtlasList.size();
430 }
431
432 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) const
433 {
434   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
435   Pixel::Format pixelFormat = Pixel::RGBA8888;
436   if ( atlas && atlas-- <= mAtlasList.size() )
437   {
438     pixelFormat = mAtlasList[ atlas ].mPixelFormat;
439   }
440   return pixelFormat;
441 }
442
443 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
444 {
445   Toolkit::AtlasManager::AtlasMetricsEntry entry;
446   uint32_t textureMemoryUsed = 0;
447   uint32_t atlasCount = mAtlasList.size();
448   metrics.mAtlasCount = atlasCount;
449   metrics.mAtlasMetrics.Resize(0);
450
451   for ( uint32_t i = 0; i < atlasCount; ++i )
452   {
453     entry.mSize = mAtlasList[ i ].mSize;
454     entry.mTotalBlocks = mAtlasList[ i ].mTotalBlocks;
455     entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[ i ].mAvailableBlocks + mAtlasList[ i ].mFreeBlocksList.Size();
456     entry.mPixelFormat = GetPixelFormat( i + 1 );
457
458     metrics.mAtlasMetrics.PushBack( entry );
459
460     uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
461     if ( entry.mPixelFormat == Pixel::BGRA8888 )
462     {
463       size <<= 2;
464     }
465
466     textureMemoryUsed += size;
467
468   }
469   metrics.mTextureMemoryUsed = textureMemoryUsed;
470 }
471
472 TextureSet AtlasManager::GetTextures( AtlasId atlas ) const
473 {
474   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
475   TextureSet textureSet;
476   if ( atlas && atlas-- <= mAtlasList.size() )
477   {
478     textureSet = mAtlasList[ atlas ].mTextureSet;
479   }
480   return textureSet;
481 }
482
483 void AtlasManager::SetTextures( AtlasId atlas, TextureSet& textureSet )
484 {
485   DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
486   if ( atlas && atlas-- <= mAtlasList.size() )
487   {
488     mAtlasList[ atlas ].mTextureSet = textureSet;
489   }
490 }
491
492 } // namespace Internal
493
494 } // namespace Toolkit
495
496 } // namespace Dali
497
498