[dali_2.3.21] Merge branch 'devel/master'
[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 <dali/integration-api/debug.h>
22 #include <string.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
26
27 namespace Dali
28 {
29 namespace Toolkit
30 {
31 namespace Internal
32 {
33 namespace
34 {
35 const uint32_t                   DEFAULT_ATLAS_WIDTH(512u);
36 const uint32_t                   DEFAULT_ATLAS_HEIGHT(512u);
37 const uint32_t                   DEFAULT_BLOCK_WIDTH(16u);
38 const uint32_t                   DEFAULT_BLOCK_HEIGHT(16u);
39 const uint32_t                   SINGLE_PIXEL_PADDING(1u);
40 const uint32_t                   DOUBLE_PIXEL_PADDING(SINGLE_PIXEL_PADDING << 1);
41 Toolkit::AtlasManager::AtlasSize EMPTY_SIZE;
42
43 bool IsBlockSizeSufficient(uint32_t width, uint32_t height, uint32_t requiredBlockWidth, uint32_t requiredBlockHeight)
44 {
45   return (width + DOUBLE_PIXEL_PADDING <= requiredBlockWidth) && (height + DOUBLE_PIXEL_PADDING <= requiredBlockHeight);
46 }
47 } // namespace
48
49 AtlasManager::AtlasManager()
50 : mAddFailPolicy(Toolkit::AtlasManager::FAIL_ON_ADD_CREATES)
51 {
52   mNewAtlasSize.mWidth       = DEFAULT_ATLAS_WIDTH;
53   mNewAtlasSize.mHeight      = DEFAULT_ATLAS_HEIGHT;
54   mNewAtlasSize.mBlockWidth  = DEFAULT_BLOCK_WIDTH;
55   mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
56 }
57
58 AtlasManagerPtr AtlasManager::New()
59 {
60   AtlasManagerPtr internal = new AtlasManager();
61   return internal;
62 }
63
64 AtlasManager::~AtlasManager()
65 {
66 }
67
68 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas(const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat)
69 {
70   SizeType width       = size.mWidth;
71   SizeType height      = size.mHeight;
72   SizeType blockWidth  = size.mBlockWidth;
73   SizeType blockHeight = size.mBlockHeight;
74
75   // Check to see if the atlas is large enough to hold a single block even ?
76   if(blockWidth + DOUBLE_PIXEL_PADDING + 1u > width || blockHeight + DOUBLE_PIXEL_PADDING + 1u > height)
77   {
78     DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
79                    width,
80                    height,
81                    blockWidth + DOUBLE_PIXEL_PADDING + 1u,
82                    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 + static_cast<SizeType>(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, blockOffsetX + SINGLE_PIXEL_PADDING, blockOffsetY + SINGLE_PIXEL_PADDING, width, height))
280   {
281     DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
282   }
283
284   // Blit top strip
285   if(!mAtlasList[atlas].mAtlas.Upload(mAtlasList[atlas].mHorizontalStrip, 0u, 0u, blockOffsetX, blockOffsetY, mAtlasList[atlas].mHorizontalStrip.GetWidth(), mAtlasList[atlas].mHorizontalStrip.GetHeight()))
286   {
287     DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
288   }
289
290   // Blit left strip
291   if(!mAtlasList[atlas].mAtlas.Upload(mAtlasList[atlas].mVerticalStrip, 0u, 0u, blockOffsetX, blockOffsetY + SINGLE_PIXEL_PADDING, mAtlasList[atlas].mVerticalStrip.GetWidth(), mAtlasList[atlas].mVerticalStrip.GetHeight()))
292   {
293     DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
294   }
295
296   // Blit bottom strip
297   if(blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[atlas].mSize.mHeight)
298   {
299     if(!mAtlasList[atlas].mAtlas.Upload(mAtlasList[atlas].mHorizontalStrip, 0u, 0u, blockOffsetX, blockOffsetY + height + SINGLE_PIXEL_PADDING, mAtlasList[atlas].mHorizontalStrip.GetWidth(), mAtlasList[atlas].mHorizontalStrip.GetHeight()))
300     {
301       DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
302     }
303   }
304
305   // Blit right strip
306   if(blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[atlas].mSize.mWidth)
307   {
308     if(!mAtlasList[atlas].mAtlas.Upload(mAtlasList[atlas].mVerticalStrip, 0u, 0u, blockOffsetX + width + SINGLE_PIXEL_PADDING, blockOffsetY + SINGLE_PIXEL_PADDING, mAtlasList[atlas].mVerticalStrip.GetWidth(), mAtlasList[atlas].mVerticalStrip.GetHeight()))
309     {
310       DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
311     }
312   }
313 }
314
315 void AtlasManager::GenerateMeshData(ImageId                        id,
316                                     const Vector2&                 position,
317                                     Toolkit::AtlasManager::Mesh2D& meshData,
318                                     bool                           addReference)
319 {
320   if(id)
321   {
322     // Read the atlas Id to use for this image
323     SizeType imageId = id - 1u;
324     SizeType atlas   = mImageList[imageId].mAtlasId - 1u;
325     SizeType width   = mImageList[imageId].mImageWidth;
326     SizeType height  = mImageList[imageId].mImageHeight;
327
328     AtlasMeshFactory::CreateQuad(width,
329                                  height,
330                                  mImageList[imageId].mBlock,
331                                  mAtlasList[atlas].mSize,
332                                  position,
333                                  meshData);
334
335     // Mesh created so increase the reference count, if we're asked to
336     if(addReference)
337     {
338       mImageList[imageId].mCount++;
339     }
340   }
341   else
342   {
343     DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
344   }
345 }
346
347 Dali::Texture AtlasManager::GetAtlasContainer(AtlasId atlas) const
348 {
349   DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
350   Dali::Texture atlasContainer;
351   if(atlas && atlas-- <= mAtlasList.size())
352   {
353     atlasContainer = mAtlasList[atlas].mAtlas;
354   }
355   return atlasContainer;
356 }
357
358 bool AtlasManager::Remove(ImageId id)
359 {
360   // Decrements the reference count of this image, and removes the blocks if zero.
361   SizeType imageId = id - 1u;
362   bool     removed = false;
363
364   if(id > mImageList.Size())
365   {
366     DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id);
367     return false;
368   }
369
370   // If we attempt to free an image that is already freed then do nothing, other than log
371   if(!mImageList[imageId].mCount)
372   {
373     DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id);
374     return false;
375   }
376
377   if(2u > --mImageList[imageId].mCount)
378   {
379     // 'Remove the blocks' from this image and add to the atlas' freelist
380     removed                    = true;
381     mImageList[imageId].mCount = 0;
382     SizeType atlas             = mImageList[imageId].mAtlasId - 1u;
383     mAtlasList[atlas].mFreeBlocksList.PushBack(mImageList[imageId].mBlock);
384   }
385   return removed;
386 }
387
388 AtlasManager::AtlasId AtlasManager::GetAtlas(ImageId id) const
389 {
390   DALI_ASSERT_DEBUG(id && id <= mImageList.Size());
391   AtlasManager::AtlasId atlasId = 0u;
392   if(id && id-- <= mImageList.Size())
393   {
394     atlasId = mImageList[id].mAtlasId;
395   }
396   return atlasId;
397 }
398
399 void AtlasManager::SetNewAtlasSize(const Toolkit::AtlasManager::AtlasSize& size)
400 {
401   mNewAtlasSize = size;
402
403   // Add on padding for borders around atlas entries
404   mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
405   mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
406 }
407
408 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize(AtlasId atlas)
409 {
410   DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
411   if(atlas && atlas-- <= mAtlasList.size())
412   {
413     return mAtlasList[atlas].mSize;
414   }
415   return EMPTY_SIZE;
416 }
417
418 AtlasManager::SizeType AtlasManager::GetFreeBlocks(AtlasId atlas) const
419 {
420   DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
421   AtlasManager::SizeType freeBlocks = 0u;
422   if(atlas && atlas-- <= mAtlasList.size())
423   {
424     freeBlocks = mAtlasList[atlas].mAvailableBlocks + mAtlasList[atlas].mFreeBlocksList.Size();
425   }
426   return freeBlocks;
427 }
428
429 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
430 {
431   return mAtlasList.size();
432 }
433
434 Pixel::Format AtlasManager::GetPixelFormat(AtlasId atlas) const
435 {
436   DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
437   Pixel::Format pixelFormat = Pixel::RGBA8888;
438   if(atlas && atlas-- <= mAtlasList.size())
439   {
440     pixelFormat = mAtlasList[atlas].mPixelFormat;
441   }
442   return pixelFormat;
443 }
444
445 void AtlasManager::GetMetrics(Toolkit::AtlasManager::Metrics& metrics)
446 {
447   Toolkit::AtlasManager::AtlasMetricsEntry entry;
448   uint32_t                                 textureMemoryUsed = 0;
449   uint32_t                                 atlasCount        = mAtlasList.size();
450   metrics.mAtlasCount                                        = atlasCount;
451   metrics.mAtlasMetrics.Resize(0);
452
453   for(uint32_t i = 0; i < atlasCount; ++i)
454   {
455     entry.mSize        = mAtlasList[i].mSize;
456     entry.mTotalBlocks = mAtlasList[i].mTotalBlocks;
457     entry.mBlocksUsed  = entry.mTotalBlocks - mAtlasList[i].mAvailableBlocks + static_cast<SizeType>(mAtlasList[i].mFreeBlocksList.Size());
458     entry.mPixelFormat = GetPixelFormat(i + 1);
459
460     metrics.mAtlasMetrics.PushBack(entry);
461
462     uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
463     if(entry.mPixelFormat == Pixel::BGRA8888)
464     {
465       size <<= 2;
466     }
467
468     textureMemoryUsed += size;
469   }
470   metrics.mTextureMemoryUsed = textureMemoryUsed;
471 }
472
473 TextureSet AtlasManager::GetTextures(AtlasId atlas) const
474 {
475   DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
476   TextureSet textureSet;
477   if(atlas && atlas-- <= mAtlasList.size())
478   {
479     textureSet = mAtlasList[atlas].mTextureSet;
480   }
481   return textureSet;
482 }
483
484 void AtlasManager::SetTextures(AtlasId atlas, TextureSet& textureSet)
485 {
486   DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
487   if(atlas && atlas-- <= mAtlasList.size())
488   {
489     mAtlasList[atlas].mTextureSet = textureSet;
490   }
491 }
492
493 } // namespace Internal
494
495 } // namespace Toolkit
496
497 } // namespace Dali