Renamed Atlas to avoid name clash with new version
[platform/core/uifw/dali-core.git] / dali / internal / event / text / atlas / texture-atlas.cpp
1 /*
2  * Copyright (c) 2014 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
18 // CLASS HEADER
19 #include <dali/internal/event/text/atlas/texture-atlas.h>
20
21 // INTERNAL INCLUDES
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>
25
26 // EXTERNAL INCLUDES
27 #include <math.h>
28
29 namespace Dali
30 {
31
32 namespace Internal
33 {
34
35 namespace // un-named namespace
36 {
37
38 void GetByteAndBitPosition( unsigned int blockNum, unsigned int& bytePos, unsigned int& bitPos )
39 {
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.
43   //
44   //   byte 0            byte 1             byte 3
45   // [ 0000,0000 ],  [ 00  X  ..... ]   ,    [.........]
46   //   0123 4567       89  ^10th bit set
47   // blockNum / 8 = byte number
48   //
49
50   bytePos = blockNum >> 3;  // >>3 = divide by 8
51
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)
56
57   bitPos = blockNum & 7;  // AND With binary 111 to lower 3 bits
58 }
59
60 } // un-named namespace
61
62
63 TextureAtlas::TextureAtlas(const unsigned int atlasSize,
64                            const unsigned int blockSize)
65 : mSize(atlasSize),
66   mBlockSize(blockSize)
67 {
68   DALI_ASSERT_DEBUG(mBlockSize > 0  && atlasSize  >= blockSize);
69
70   // Atlases are square
71   unsigned int totalBlocks = GetBlocksPerRow() * GetBlocksPerRow();
72
73   DALI_ASSERT_DEBUG(  (totalBlocks % 8) == 0 && "Atlas num blocks must be factor of 8" );
74
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;
79
80   mFreeBlocks.resize( bitMaskBytes );  // contents auto-initialised to zero
81 }
82
83 TextureAtlas::~TextureAtlas()
84 {
85
86 }
87
88 void TextureAtlas::CloneContents( TextureAtlas* clone )
89 {
90   // Internally atlas allocation is done using a 1 dimensional array.
91   // A single bit set in the array represents an allocation.
92   //
93   // So an atlas of size 8 x 8 blocks is 64 bits long.
94   //
95   // When cloning we keep the allocated blocks in the same 2D space.
96   //
97   //
98   //  Original (4 x 4)  --> New Atlas ( 8 x 8)
99   //
100   //  1110                   1110 0000
101   //  0010      ---------->  0010 0000
102   //  0000                   0000 0000
103   //  1001                   1001 0000
104   //
105   //                         0000 0000
106   //                         0000 0000
107   //                         0000 0000
108   //                         0000 0000
109   //
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)
115
116   DALI_ASSERT_DEBUG( clone->mSize <= mSize);
117
118   // go through each allocated block in the cloned atlas, and add to the this atlas.
119
120   BlockLookup::const_iterator endIter = clone->mBlockLookup.end();
121   for( BlockLookup::const_iterator iter = clone->mBlockLookup.begin(); iter != endIter; ++iter )
122   {
123     unsigned int key = (*iter).first;
124     unsigned int block = (*iter).second;
125     unsigned int row,column;
126
127     clone->GetPositionOfBlock( block, row, column );
128
129     unsigned int newBlockId = AllocateBlock( row, column );
130
131     mBlockLookup[ key ] = newBlockId;
132   }
133
134 #ifdef DEBUG_ATLAS
135   DebugPrintAtlasWithIds( clone->mFreeBlocks, clone->mBlockLookup, clone->GetBlocksPerRow());
136   DebugPrintAtlasWithIds( mFreeBlocks,mBlockLookup,  GetBlocksPerRow() );
137 #endif
138 }
139
140 bool TextureAtlas::Insert( unsigned int id)
141 {
142   unsigned int blockNum(0);
143
144   bool ok = AllocateBlock( blockNum );
145
146   if (!ok)
147   {
148     DALI_ASSERT_DEBUG( 0 && "Atlas full ");
149     // Atlas full
150     return false;
151   }
152
153   DALI_ASSERT_ALWAYS( mBlockLookup.find(id) == mBlockLookup.end() && "Inserted duplicate id into the atlas" );
154
155   // store the link between block number and unique id
156   mBlockLookup[id] = blockNum;
157
158 #ifdef DEBUG_ATLAS
159   DebugPrintAtlas( mFreeBlocks, GetBlocksPerRow() );
160 #endif
161
162   return true;
163 }
164
165 void TextureAtlas::Remove(unsigned int id)
166 {
167   BlockLookup::const_iterator iter = mBlockLookup.find( id );
168
169   DALI_ASSERT_ALWAYS( iter != mBlockLookup.end() && "Failed to find id in atlas\n");
170
171   DeAllocateBlock( (*iter).second );
172
173   // remove the id from the lookup
174   mBlockLookup.erase( id );
175 }
176
177 unsigned int TextureAtlas::GetSize() const
178 {
179   return mSize;
180 }
181
182 void TextureAtlas::GetXYPosition( unsigned int id, unsigned int& xPos, unsigned int& yPos ) const
183 {
184   AtlasItem item;
185
186   unsigned int blockNum( GetBlockNumber( id ) );
187   FillAtlasItem( blockNum, item, DONT_CALCULATE_UV );
188
189   xPos = item.xPos;
190   yPos = item.yPos;
191 }
192
193 UvRect TextureAtlas::GetUvCoordinates( unsigned int id ) const
194 {
195   AtlasItem item;
196
197   unsigned int blockNum( GetBlockNumber( id ) );
198   FillAtlasItem( blockNum, item, CALCULATE_UV );
199
200   return item.uv;
201 }
202
203 TextureAtlas::TextureAtlas()
204 :mSize( 0 ),
205  mBlockSize( 0 )
206 {
207
208 }
209
210 bool TextureAtlas::AllocateBlock( unsigned int& blockNum )
211 {
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 )
215   {
216     // check if a free bit is available
217     unsigned char mask = mFreeBlocks[i];
218     if( 0xFF != mask)
219     {
220       for( int n = 0; n < 8 ; n++)
221       {
222         // check if a bit is not set.
223         if( ! (mask & (1 << n) ) )
224         {
225           // we have found a free bit, set it to 1.
226           mask|= (1 << n);
227           blockNum = i * 8 + n;
228           mFreeBlocks[i] = mask;
229
230           return true;
231         }
232       }
233     }
234   }
235   blockNum = 0;
236   return false;
237 }
238
239 void TextureAtlas::DeAllocateBlock( unsigned int blockNum )
240 {
241   unsigned int bytePos,bitPos;
242
243   GetByteAndBitPosition( blockNum, bytePos, bitPos );
244
245   unsigned char mask = mFreeBlocks[ bytePos ];
246
247   // check the block was allocated
248   DALI_ASSERT_DEBUG( ((mask & (1<< bitPos))) && "DeAllocated a block, that was never allocated" );
249
250   // clear the bit
251   mask &= ~(1 << bitPos);
252
253   mFreeBlocks[ bytePos ] = mask;
254
255 }
256
257 void TextureAtlas::FillAtlasItem( unsigned int blockNum, AtlasItem& atlasItem, UvMode mode ) const
258 {
259   UvRect& uv(atlasItem.uv);
260
261   unsigned int block1dPos = blockNum * mBlockSize;
262
263   unsigned int blockX = block1dPos  % mSize;
264   unsigned int blockY = mBlockSize * floor( block1dPos / mSize );
265
266   atlasItem.xPos = blockX;
267   atlasItem.yPos = blockY;
268
269   if( mode == DONT_CALCULATE_UV )
270   {
271     return;
272   }
273   const float ratio = 1.0f / mSize;
274
275   uv.u0 = ratio * (blockX);
276   uv.v0 = ratio * (blockY);
277   uv.u2 = ratio * (blockX + mBlockSize);
278   uv.v2 =  ratio * (blockY + mBlockSize);
279
280 }
281
282 unsigned int TextureAtlas::GetBlockNumber( unsigned int id) const
283 {
284   BlockLookup::const_iterator iter = mBlockLookup.find( id );
285
286   DALI_ASSERT_ALWAYS( iter != mBlockLookup.end() );
287
288   return  (*iter).second;
289 }
290
291 unsigned int TextureAtlas::GetBlocksPerRow() const
292 {
293   return  mSize / mBlockSize;
294 }
295
296 void TextureAtlas::GetPositionOfBlock( unsigned int block1dPos, unsigned int& row, unsigned int& column )
297 {
298   column = 0;
299   if( block1dPos > 0)
300   {
301     column =  block1dPos % GetBlocksPerRow();
302   }
303   row =  floor( block1dPos / GetBlocksPerRow() );
304
305   unsigned int bytePos, bitPos;
306
307   GetByteAndBitPosition( block1dPos, bytePos, bitPos );
308 }
309
310 unsigned int TextureAtlas::AllocateBlock( unsigned int row, unsigned int column )
311 {
312   unsigned int blockNum = (row * GetBlocksPerRow()) + column;
313
314   unsigned int bytePos, bitPos;
315
316   GetByteAndBitPosition( blockNum, bytePos, bitPos );
317
318   unsigned char& mask = mFreeBlocks.at( bytePos);
319
320   mask |= 1<< bitPos; // set the bit to mark as allocated
321
322   return blockNum;
323 }
324
325
326 } // namespace Internal
327
328 } // namespace Dali