2 * Copyright (c) 2014 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.
19 #include <dali/internal/event/text/atlas/texture-atlas.h>
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/internal/event/text/atlas/debug/atlas-debug.h>
35 namespace // un-named namespace
38 void GetByteAndBitPosition( unsigned int blockNum, unsigned int& bytePos, unsigned int& bitPos )
40 // divide by 8 to calculate the byte this block is in
41 // 1 byte holds bitmask for 8 blocks
42 // Example: If block = 10, then block is in byte 1.
44 // byte 0 byte 1 byte 3
45 // [ 0000,0000 ], [ 00 X ..... ] , [.........]
46 // 0123 4567 89 ^10th bit set
47 // blockNum / 8 = byte number
50 bytePos = blockNum >> 3; // >>3 = divide by 8
52 // calculate the bit, within the byte which the block uses
53 // the lower 3 bits define it's position with the block
54 // if block = 10, in binary this is 1010. Lower 3 bits = 010.
55 // So bit position = 2 ( zero based)
57 bitPos = blockNum & 7; // AND With binary 111 to lower 3 bits
60 } // un-named namespace
63 TextureAtlas::TextureAtlas(const unsigned int atlasSize,
64 const unsigned int blockSize)
68 DALI_ASSERT_DEBUG(mBlockSize > 0 && atlasSize >= blockSize);
71 unsigned int totalBlocks = GetBlocksPerRow() * GetBlocksPerRow();
73 DALI_ASSERT_DEBUG( (totalBlocks % 8) == 0 && "Atlas num blocks must be factor of 8" );
75 // block allocation is using a bitmask in a 1D array.
76 // e.g. 256 blocks require 256 bits to say whether a block is allocated
77 // 256 / 8 = 32 bytes (or >> 3 to divide by 8 ).
78 unsigned int bitMaskBytes = totalBlocks >> 3;
80 mFreeBlocks.resize( bitMaskBytes ); // contents auto-initialised to zero
83 TextureAtlas::~TextureAtlas()
88 void TextureAtlas::CloneContents( TextureAtlas* clone )
90 // Internally atlas allocation is done using a 1 dimensional array.
91 // A single bit set in the array represents an allocation.
93 // So an atlas of size 8 x 8 blocks is 64 bits long.
95 // When cloning we keep the allocated blocks in the same 2D space.
98 // Original (4 x 4) --> New Atlas ( 8 x 8)
101 // 0010 ----------> 0010 0000
110 // If we want the X,Y position of character 'X' in original atlas, it will be identical in the new atlas.
111 // This allows the glyph bitmap to be uploaded to an identical place in the gl texture
112 // to the old texture.
113 // Original aim of this approach was to avoid re-calcualating uv co-ordinates.
114 // However as the texture width / height has changed, the uv values also need adjusting (scaling)
116 DALI_ASSERT_DEBUG( clone->mSize <= mSize);
118 // go through each allocated block in the cloned atlas, and add to the this atlas.
120 BlockLookup::const_iterator endIter = clone->mBlockLookup.end();
121 for( BlockLookup::const_iterator iter = clone->mBlockLookup.begin(); iter != endIter; ++iter )
123 unsigned int key = (*iter).first;
124 unsigned int block = (*iter).second;
125 unsigned int row,column;
127 clone->GetPositionOfBlock( block, row, column );
129 unsigned int newBlockId = AllocateBlock( row, column );
131 mBlockLookup[ key ] = newBlockId;
135 DebugPrintAtlasWithIds( clone->mFreeBlocks, clone->mBlockLookup, clone->GetBlocksPerRow());
136 DebugPrintAtlasWithIds( mFreeBlocks,mBlockLookup, GetBlocksPerRow() );
140 bool TextureAtlas::Insert( unsigned int id)
142 unsigned int blockNum(0);
144 bool ok = AllocateBlock( blockNum );
148 DALI_ASSERT_DEBUG( 0 && "Atlas full ");
153 DALI_ASSERT_ALWAYS( mBlockLookup.find(id) == mBlockLookup.end() && "Inserted duplicate id into the atlas" );
155 // store the link between block number and unique id
156 mBlockLookup[id] = blockNum;
159 DebugPrintAtlas( mFreeBlocks, GetBlocksPerRow() );
165 void TextureAtlas::Remove(unsigned int id)
167 BlockLookup::const_iterator iter = mBlockLookup.find( id );
169 DALI_ASSERT_ALWAYS( iter != mBlockLookup.end() && "Failed to find id in atlas\n");
171 DeAllocateBlock( (*iter).second );
173 // remove the id from the lookup
174 mBlockLookup.erase( id );
177 unsigned int TextureAtlas::GetSize() const
182 void TextureAtlas::GetXYPosition( unsigned int id, unsigned int& xPos, unsigned int& yPos ) const
186 unsigned int blockNum( GetBlockNumber( id ) );
187 FillAtlasItem( blockNum, item, DONT_CALCULATE_UV );
193 UvRect TextureAtlas::GetUvCoordinates( unsigned int id ) const
197 unsigned int blockNum( GetBlockNumber( id ) );
198 FillAtlasItem( blockNum, item, CALCULATE_UV );
203 TextureAtlas::TextureAtlas()
210 bool TextureAtlas::AllocateBlock( unsigned int& blockNum )
212 // scan the bitmask for a free block
213 // each byte is a bitmask for 8 blocks, so 0000 0011, means blocks 1 and 2 are allocated
214 for( size_t i = 0, end = mFreeBlocks.size(); i < end; ++i )
216 // check if a free bit is available
217 unsigned char mask = mFreeBlocks[i];
220 for( int n = 0; n < 8 ; n++)
222 // check if a bit is not set.
223 if( ! (mask & (1 << n) ) )
225 // we have found a free bit, set it to 1.
227 blockNum = i * 8 + n;
228 mFreeBlocks[i] = mask;
239 void TextureAtlas::DeAllocateBlock( unsigned int blockNum )
241 unsigned int bytePos,bitPos;
243 GetByteAndBitPosition( blockNum, bytePos, bitPos );
245 unsigned char mask = mFreeBlocks[ bytePos ];
247 // check the block was allocated
248 DALI_ASSERT_DEBUG( ((mask & (1<< bitPos))) && "DeAllocated a block, that was never allocated" );
251 mask &= ~(1 << bitPos);
253 mFreeBlocks[ bytePos ] = mask;
257 void TextureAtlas::FillAtlasItem( unsigned int blockNum, AtlasItem& atlasItem, UvMode mode ) const
259 UvRect& uv(atlasItem.uv);
261 unsigned int block1dPos = blockNum * mBlockSize;
263 unsigned int blockX = block1dPos % mSize;
264 unsigned int blockY = mBlockSize * floor( block1dPos / mSize );
266 atlasItem.xPos = blockX;
267 atlasItem.yPos = blockY;
269 if( mode == DONT_CALCULATE_UV )
273 const float ratio = 1.0f / mSize;
275 uv.u0 = ratio * (blockX);
276 uv.v0 = ratio * (blockY);
277 uv.u2 = ratio * (blockX + mBlockSize);
278 uv.v2 = ratio * (blockY + mBlockSize);
282 unsigned int TextureAtlas::GetBlockNumber( unsigned int id) const
284 BlockLookup::const_iterator iter = mBlockLookup.find( id );
286 DALI_ASSERT_ALWAYS( iter != mBlockLookup.end() );
288 return (*iter).second;
291 unsigned int TextureAtlas::GetBlocksPerRow() const
293 return mSize / mBlockSize;
296 void TextureAtlas::GetPositionOfBlock( unsigned int block1dPos, unsigned int& row, unsigned int& column )
301 column = block1dPos % GetBlocksPerRow();
303 row = floor( block1dPos / GetBlocksPerRow() );
305 unsigned int bytePos, bitPos;
307 GetByteAndBitPosition( block1dPos, bytePos, bitPos );
310 unsigned int TextureAtlas::AllocateBlock( unsigned int row, unsigned int column )
312 unsigned int blockNum = (row * GetBlocksPerRow()) + column;
314 unsigned int bytePos, bitPos;
316 GetByteAndBitPosition( blockNum, bytePos, bitPos );
318 unsigned char& mask = mFreeBlocks.at( bytePos);
320 mask |= 1<< bitPos; // set the bit to mark as allocated
326 } // namespace Internal