2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali-toolkit/internal/text/rendering/atlas/atlas-manager-impl.h>
21 #include <dali/integration-api/debug.h>
25 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
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;
43 bool IsBlockSizeSufficient(uint32_t width, uint32_t height, uint32_t requiredBlockWidth, uint32_t requiredBlockHeight)
45 return (width + DOUBLE_PIXEL_PADDING <= requiredBlockWidth) && (height + DOUBLE_PIXEL_PADDING <= requiredBlockHeight);
49 AtlasManager::AtlasManager()
50 : mAddFailPolicy(Toolkit::AtlasManager::FAIL_ON_ADD_CREATES)
52 mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH;
53 mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT;
54 mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH;
55 mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT;
58 AtlasManagerPtr AtlasManager::New()
60 AtlasManagerPtr internal = new AtlasManager();
64 AtlasManager::~AtlasManager()
68 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas(const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat)
70 SizeType width = size.mWidth;
71 SizeType height = size.mHeight;
72 SizeType blockWidth = size.mBlockWidth;
73 SizeType blockHeight = size.mBlockHeight;
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)
78 DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
81 blockWidth + DOUBLE_PIXEL_PADDING + 1u,
82 blockHeight + DOUBLE_PIXEL_PADDING + 1u);
86 Dali::Texture atlas = Dali::Texture::New(TextureType::TEXTURE_2D, pixelformat, width, height);
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);
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;
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);
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);
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();
121 void AtlasManager::SetAddPolicy(Toolkit::AtlasManager::AddFailPolicy policy)
123 mAddFailPolicy = policy;
126 bool AtlasManager::Add(const PixelData& image,
127 Toolkit::AtlasManager::AtlasSlot& slot,
128 Toolkit::AtlasManager::AtlasId atlas)
130 bool created = false;
131 Pixel::Format pixelFormat = image.GetPixelFormat();
132 SizeType width = image.GetWidth();
133 SizeType height = image.GetHeight();
134 SizeType foundAtlas = 0;
138 AtlasSlotDescriptor desc;
140 // If there is a preferred atlas then check for room in that first
143 foundAtlas = CheckAtlas(atlas, width, height, pixelFormat);
146 // Search current atlases to see if there is a good match
147 while((0u == foundAtlas) && (index < mAtlasList.size()))
149 foundAtlas = CheckAtlas(index, width, height, pixelFormat);
153 // If we can't find a suitable atlas then check the policy to determine action
156 if(Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy)
158 if(IsBlockSizeSufficient(width, height, mNewAtlasSize.mBlockWidth, mNewAtlasSize.mBlockHeight)) // Checks if image fits within the atlas blocks
160 foundAtlas = CreateAtlas(mNewAtlasSize, pixelFormat); // Creating atlas with mNewAtlasSize, may not be the needed size!
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);
177 if((0u == foundAtlas) || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy)
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");
185 foundAtlas--; // Atlas created successfully, decrement by 1 to get <vector> index (starts at 0 not 1)
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)
191 // Yes, so select our next block
192 desc.mBlock = mAtlasList[foundAtlas].mTotalBlocks - mAtlasList[foundAtlas].mAvailableBlocks--;
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());
201 desc.mImageWidth = width;
202 desc.mImageHeight = height;
203 desc.mAtlasId = foundAtlas + 1u; // Ids start from 1 not the 0 index
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)
210 if(!mImageList[i].mCount)
218 mImageList.PushBack(desc);
219 slot.mImageId = mImageList.Size();
223 mImageList[imageId - 1u] = desc;
224 slot.mImageId = imageId;
226 slot.mAtlasId = foundAtlas + 1u; // Ids start from 1 not the 0 index
228 // Upload the buffer image into the atlas
229 UploadImage(image, desc);
233 AtlasManager::SizeType AtlasManager::CheckAtlas(SizeType atlas,
236 Pixel::Format pixelFormat)
238 AtlasManager::SizeType result = 0u;
239 if(pixelFormat == mAtlasList[atlas].mPixelFormat)
241 // Check to see if the image will fit in these blocks
243 const SizeType availableBlocks = mAtlasList[atlas].mAvailableBlocks + mAtlasList[atlas].mFreeBlocksList.Size();
245 if(availableBlocks && IsBlockSizeSufficient(width, height, mAtlasList[atlas].mSize.mBlockWidth, mAtlasList[atlas].mSize.mBlockHeight))
247 result = atlas + 1u; // Atlas ids start from 1 not 0
253 void AtlasManager::UploadImage(const PixelData& image,
254 const AtlasSlotDescriptor& desc)
256 // Get the atlas to upload the image to
257 SizeType atlas = desc.mAtlasId - 1u;
259 // Check to see that the pixel formats are compatible
260 if(image.GetPixelFormat() != mAtlasList[atlas].mPixelFormat)
262 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
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;
270 SizeType blockX = desc.mBlock % atlasWidthInBlocks;
271 SizeType blockY = desc.mBlock / atlasWidthInBlocks;
272 SizeType blockOffsetX = (blockX * atlasBlockWidth) + 1u;
273 SizeType blockOffsetY = (blockY * atlasBlockHeight) + 1u;
275 SizeType width = image.GetWidth();
276 SizeType height = image.GetHeight();
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))
281 DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
285 if(!mAtlasList[atlas].mAtlas.Upload(mAtlasList[atlas].mHorizontalStrip, 0u, 0u, blockOffsetX, blockOffsetY, mAtlasList[atlas].mHorizontalStrip.GetWidth(), mAtlasList[atlas].mHorizontalStrip.GetHeight()))
287 DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
291 if(!mAtlasList[atlas].mAtlas.Upload(mAtlasList[atlas].mVerticalStrip, 0u, 0u, blockOffsetX, blockOffsetY + SINGLE_PIXEL_PADDING, mAtlasList[atlas].mVerticalStrip.GetWidth(), mAtlasList[atlas].mVerticalStrip.GetHeight()))
293 DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
297 if(blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[atlas].mSize.mHeight)
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()))
301 DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
306 if(blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[atlas].mSize.mWidth)
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()))
310 DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
315 void AtlasManager::GenerateMeshData(ImageId id,
316 const Vector2& position,
317 Toolkit::AtlasManager::Mesh2D& meshData,
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;
328 AtlasMeshFactory::CreateQuad(width,
330 mImageList[imageId].mBlock,
331 mAtlasList[atlas].mSize,
335 // Mesh created so increase the reference count, if we're asked to
338 mImageList[imageId].mCount++;
343 DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n");
347 Dali::Texture AtlasManager::GetAtlasContainer(AtlasId atlas) const
349 DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
350 Dali::Texture atlasContainer;
351 if(atlas && atlas-- <= mAtlasList.size())
353 atlasContainer = mAtlasList[atlas].mAtlas;
355 return atlasContainer;
358 bool AtlasManager::Remove(ImageId id)
360 // Decrements the reference count of this image, and removes the blocks if zero.
361 SizeType imageId = id - 1u;
362 bool removed = false;
364 if(id > mImageList.Size())
366 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id);
370 // If we attempt to free an image that is already freed then do nothing, other than log
371 if(!mImageList[imageId].mCount)
373 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id);
377 if(2u > --mImageList[imageId].mCount)
379 // 'Remove the blocks' from this image and add to the atlas' freelist
381 mImageList[imageId].mCount = 0;
382 SizeType atlas = mImageList[imageId].mAtlasId - 1u;
383 mAtlasList[atlas].mFreeBlocksList.PushBack(mImageList[imageId].mBlock);
388 AtlasManager::AtlasId AtlasManager::GetAtlas(ImageId id) const
390 DALI_ASSERT_DEBUG(id && id <= mImageList.Size());
391 AtlasManager::AtlasId atlasId = 0u;
392 if(id && id-- <= mImageList.Size())
394 atlasId = mImageList[id].mAtlasId;
399 void AtlasManager::SetNewAtlasSize(const Toolkit::AtlasManager::AtlasSize& size)
401 mNewAtlasSize = size;
403 // Add on padding for borders around atlas entries
404 mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
405 mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
408 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize(AtlasId atlas)
410 DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
411 if(atlas && atlas-- <= mAtlasList.size())
413 return mAtlasList[atlas].mSize;
418 AtlasManager::SizeType AtlasManager::GetFreeBlocks(AtlasId atlas) const
420 DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
421 AtlasManager::SizeType freeBlocks = 0u;
422 if(atlas && atlas-- <= mAtlasList.size())
424 freeBlocks = mAtlasList[atlas].mAvailableBlocks + mAtlasList[atlas].mFreeBlocksList.Size();
429 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
431 return mAtlasList.size();
434 Pixel::Format AtlasManager::GetPixelFormat(AtlasId atlas) const
436 DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
437 Pixel::Format pixelFormat = Pixel::RGBA8888;
438 if(atlas && atlas-- <= mAtlasList.size())
440 pixelFormat = mAtlasList[atlas].mPixelFormat;
445 void AtlasManager::GetMetrics(Toolkit::AtlasManager::Metrics& metrics)
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);
453 for(uint32_t i = 0; i < atlasCount; ++i)
455 entry.mSize = mAtlasList[i].mSize;
456 entry.mTotalBlocks = mAtlasList[i].mTotalBlocks;
457 entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[i].mAvailableBlocks + mAtlasList[i].mFreeBlocksList.Size();
458 entry.mPixelFormat = GetPixelFormat(i + 1);
460 metrics.mAtlasMetrics.PushBack(entry);
462 uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
463 if(entry.mPixelFormat == Pixel::BGRA8888)
468 textureMemoryUsed += size;
470 metrics.mTextureMemoryUsed = textureMemoryUsed;
473 TextureSet AtlasManager::GetTextures(AtlasId atlas) const
475 DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
476 TextureSet textureSet;
477 if(atlas && atlas-- <= mAtlasList.size())
479 textureSet = mAtlasList[atlas].mTextureSet;
484 void AtlasManager::SetTextures(AtlasId atlas, TextureSet& textureSet)
486 DALI_ASSERT_DEBUG(atlas && atlas <= mAtlasList.size());
487 if(atlas && atlas-- <= mAtlasList.size())
489 mAtlasList[atlas].mTextureSet = textureSet;
493 } // namespace Internal
495 } // namespace Toolkit