2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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/internal/event/text/atlas/atlas.h>
21 #include <dali/public-api/common/dali-common.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/internal/event/text/atlas/debug/atlas-debug.h>
34 namespace // un-named namespace
37 void GetByteAndBitPosition( unsigned int blockNum, unsigned int& bytePos, unsigned int& bitPos )
39 // divide by 8 to calculate the byte this block is in
40 // 1 byte holds bitmask for 8 blocks
41 // Example: If block = 10, then block is in byte 1.
43 // byte 0 byte 1 byte 3
44 // [ 0000,0000 ], [ 00 X ..... ] , [.........]
45 // 0123 4567 89 ^10th bit set
46 // blockNum / 8 = byte number
49 bytePos = blockNum >> 3; // >>3 = divide by 8
51 // calculate the bit, within the byte which the block uses
52 // the lower 3 bits define it's position with the block
53 // if block = 10, in binary this is 1010. Lower 3 bits = 010.
54 // So bit position = 2 ( zero based)
56 bitPos = blockNum & 7; // AND With binary 111 to lower 3 bits
59 } // un-named namespace
62 Atlas::Atlas(const unsigned int atlasSize,
63 const unsigned int blockSize)
67 DALI_ASSERT_DEBUG(mBlockSize > 0 && atlasSize >= blockSize);
70 unsigned int totalBlocks = GetBlocksPerRow() * GetBlocksPerRow();
72 DALI_ASSERT_DEBUG( (totalBlocks % 8) == 0 && "Atlas num blocks must be factor of 8" );
74 // block allocation is using a bitmask in a 1D array.
75 // e.g. 256 blocks require 256 bits to say whether a block is allocated
76 // 256 / 8 = 32 bytes (or >> 3 to divide by 8 ).
77 unsigned int bitMaskBytes = totalBlocks >> 3;
79 mFreeBlocks.resize( bitMaskBytes ); // contents auto-initialised to zero
87 void Atlas::CloneContents( Atlas* clone )
89 // Internally atlas allocation is done using a 1 dimensional array.
90 // A single bit set in the array represents an allocation.
92 // So an atlas of size 8 x 8 blocks is 64 bits long.
94 // When cloning we keep the allocated blocks in the same 2D space.
97 // Original (4 x 4) --> New Atlas ( 8 x 8)
100 // 0010 ----------> 0010 0000
109 // If we want the X,Y position of character 'X' in original atlas, it will be identical in the new atlas.
110 // This allows the glyph bitmap to be uploaded to an identical place in the gl texture
111 // to the old texture.
112 // Original aim of this approach was to avoid re-calcualating uv co-ordinates.
113 // However as the texture width / height has changed, the uv values also need adjusting (scaling)
115 DALI_ASSERT_DEBUG( clone->mSize <= mSize);
117 // go through each allocated block in the cloned atlas, and add to the this atlas.
119 BlockLookup::const_iterator endIter = clone->mBlockLookup.end();
120 for( BlockLookup::const_iterator iter = clone->mBlockLookup.begin(); iter != endIter; ++iter )
122 unsigned int key = (*iter).first;
123 unsigned int block = (*iter).second;
124 unsigned int row,column;
126 clone->GetPositionOfBlock( block, row, column );
128 unsigned int newBlockId = AllocateBlock( row, column );
130 mBlockLookup[ key ] = newBlockId;
134 DebugPrintAtlasWithIds( clone->mFreeBlocks, clone->mBlockLookup, clone->GetBlocksPerRow());
135 DebugPrintAtlasWithIds( mFreeBlocks,mBlockLookup, GetBlocksPerRow() );
139 bool Atlas::Insert( unsigned int id)
141 unsigned int blockNum(0);
143 bool ok = AllocateBlock( blockNum );
147 DALI_ASSERT_DEBUG( 0 && "Atlas full ");
152 DALI_ASSERT_ALWAYS( mBlockLookup.find(id) == mBlockLookup.end() && "Inserted duplicate id into the atlas" );
154 // store the link between block number and unique id
155 mBlockLookup[id] = blockNum;
158 DebugPrintAtlas( mFreeBlocks, GetBlocksPerRow() );
164 void Atlas::Remove(unsigned int id)
166 BlockLookup::const_iterator iter = mBlockLookup.find( id );
168 DALI_ASSERT_ALWAYS( iter != mBlockLookup.end() && "Failed to find id in atlas\n");
170 DeAllocateBlock( (*iter).second );
172 // remove the id from the lookup
173 mBlockLookup.erase( id );
176 unsigned int Atlas::GetSize() const
181 void Atlas::GetXYPosition( unsigned int id, unsigned int& xPos, unsigned int& yPos ) const
185 unsigned int blockNum( GetBlockNumber( id ) );
186 FillAtlasItem( blockNum, item, DONT_CALCULATE_UV );
192 UvRect Atlas::GetUvCoordinates( unsigned int id ) const
196 unsigned int blockNum( GetBlockNumber( id ) );
197 FillAtlasItem( blockNum, item, CALCULATE_UV );
209 bool Atlas::AllocateBlock( unsigned int& blockNum )
211 // scan the bitmask for a free block
212 // each byte is a bitmask for 8 blocks, so 0000 0011, means blocks 1 and 2 are allocated
213 for( size_t i = 0, end = mFreeBlocks.size(); i < end; ++i )
215 // check if a free bit is available
216 unsigned char mask = mFreeBlocks[i];
219 for( int n = 0; n < 8 ; n++)
221 // check if a bit is not set.
222 if( ! (mask & (1 << n) ) )
224 // we have found a free bit, set it to 1.
226 blockNum = i * 8 + n;
227 mFreeBlocks[i] = mask;
238 void Atlas::DeAllocateBlock( unsigned int blockNum )
240 unsigned int bytePos,bitPos;
242 GetByteAndBitPosition( blockNum, bytePos, bitPos );
244 unsigned char mask = mFreeBlocks[ bytePos ];
246 // check the block was allocated
247 DALI_ASSERT_DEBUG( ((mask & (1<< bitPos))) && "DeAllocated a block, that was never allocated" );
250 mask &= ~(1 << bitPos);
252 mFreeBlocks[ bytePos ] = mask;
256 void Atlas::FillAtlasItem( unsigned int blockNum, AtlasItem& atlasItem, UvMode mode ) const
258 UvRect& uv(atlasItem.uv);
260 unsigned int block1dPos = blockNum * mBlockSize;
262 unsigned int blockX = block1dPos % mSize;
263 unsigned int blockY = mBlockSize * floor( block1dPos / mSize ) ;
265 atlasItem.xPos = blockX;
266 atlasItem.yPos = blockY;
268 if( mode == DONT_CALCULATE_UV )
272 const float ratio = 1.0f / mSize;
274 uv.u0 = ratio * (blockX);
275 uv.v0 = ratio * (blockY);
276 uv.u2 = ratio * (blockX + mBlockSize);
277 uv.v2 = ratio * (blockY + mBlockSize);
281 unsigned int Atlas::GetBlockNumber( unsigned int id) const
283 BlockLookup::const_iterator iter = mBlockLookup.find( id );
285 DALI_ASSERT_ALWAYS( iter != mBlockLookup.end() );
287 return (*iter).second;
290 unsigned int Atlas::GetBlocksPerRow() const
292 return mSize / mBlockSize;
295 void Atlas::GetPositionOfBlock( unsigned int block1dPos, unsigned int& row, unsigned int& column )
300 column = block1dPos % GetBlocksPerRow();
302 row = floor( block1dPos / GetBlocksPerRow() ) ;
304 unsigned int bytePos, bitPos;
306 GetByteAndBitPosition( block1dPos, bytePos, bitPos );
309 unsigned int Atlas::AllocateBlock( unsigned int row, unsigned int column )
311 unsigned int blockNum = (row * GetBlocksPerRow()) + column;
313 unsigned int bytePos, bitPos;
315 GetByteAndBitPosition( blockNum, bytePos, bitPos );
317 unsigned char& mask = mFreeBlocks.at( bytePos);
319 mask |= 1<< bitPos; // set the bit to mark as allocated
325 } // namespace Internal