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