Vector-based text rendering
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / vector-based / vector-blob-atlas.cpp
1 /*
2  * Copyright (c) 2016 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-toolkit/internal/text/rendering/vector-based/vector-blob-atlas.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 namespace
25 {
26
27 #if defined(DEBUG_ENABLED)
28   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING");
29 #endif
30
31 }
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Text
40 {
41
42 static void
43 EncodeBlobCoordinate( unsigned int cornerX, unsigned int cornerY,
44                       unsigned int atlasX, unsigned int atlasY,
45                       unsigned int nominalWidth, unsigned int nominalHeight,
46                       BlobCoordinate* v )
47 {
48   DALI_ASSERT_DEBUG(0 == (atlasX & ~0x7F));
49   DALI_ASSERT_DEBUG(0 == (atlasY & ~0x7F));
50   DALI_ASSERT_DEBUG(0 == (cornerX & ~1));
51   DALI_ASSERT_DEBUG(0 == (cornerY & ~1));
52   DALI_ASSERT_DEBUG(0 == (nominalWidth & ~0x3F));
53   DALI_ASSERT_DEBUG(0 == (nominalHeight & ~0x3F));
54
55   unsigned int x = (((atlasX << 6) | nominalWidth) << 1)  | cornerX;
56   unsigned int y = (((atlasY << 6) | nominalHeight) << 1) | cornerY;
57
58   unsigned int encoded = (x << 16) | y;
59
60   v->u = encoded >> 16;
61   v->v = encoded & 0xFFFF;
62 }
63
64 VectorBlobAtlas::VectorBlobAtlas( unsigned int textureWidth,
65                                   unsigned int textureHeight,
66                                   unsigned int itemWidth,
67                                   unsigned int itemHeightQuantum )
68 : mTextureWidth( textureWidth ),
69   mTextureHeight( textureHeight ),
70   mItemWidth( itemWidth ),
71   mItemHeightQuantum( itemHeightQuantum ),
72   mCursorX( 0 ),
73   mCursorY( 0 ),
74   mIsFull( false )
75 {
76   DALI_LOG_INFO( gLogFilter, Debug::General, "Blob atlas %p size %dx%d, item width %d, height quantum %d\n", this, textureWidth, textureHeight, itemWidth, itemHeightQuantum );
77
78   mAtlasTexture = BufferImage::New( textureWidth, textureHeight, Pixel::RGBA8888 );
79
80   mTextureSet = TextureSet::New();
81   mTextureSet.SetImage( 0, mAtlasTexture );
82 }
83
84 bool VectorBlobAtlas::IsFull() const
85 {
86   return mIsFull;
87 }
88
89 bool VectorBlobAtlas::FindGlyph( FontId fontId,
90                                  GlyphIndex glyphIndex,
91                                  BlobCoordinate* coords )
92 {
93   const unsigned int size( mItemLookup.size() );
94
95   for( unsigned int i=0; i<size; ++i )
96   {
97     if( mItemLookup[i].fontId     == fontId &&
98         mItemLookup[i].glyphIndex == glyphIndex )
99     {
100       const Item& item = mItemCache[ mItemLookup[i].cacheIndex ];
101
102       coords[0] = item.coords[0];
103       coords[1] = item.coords[1];
104       coords[2] = item.coords[2];
105       coords[3] = item.coords[3];
106
107       return true;
108     }
109   }
110
111   return false;
112 }
113
114 bool VectorBlobAtlas::AddGlyph( unsigned int fontId,
115                                 unsigned int glyphIndex,
116                                 VectorBlob* blob,
117                                 unsigned int length,
118                                 unsigned int nominalWidth,
119                                 unsigned int nominalHeight,
120                                 BlobCoordinate* coords )
121 {
122   if( mIsFull )
123   {
124     return false;
125   }
126
127   unsigned int w, h, x, y;
128
129   w = mItemWidth;
130   h = (length + w - 1) / w;
131
132   if( mCursorY + h > mTextureHeight )
133   {
134     // Go to next column
135     mCursorX += mItemWidth;
136     mCursorY = 0;
137   }
138
139   if( mCursorX + w <= mTextureWidth && mCursorY + h <= mTextureHeight )
140   {
141     x = mCursorX;
142     y = mCursorY;
143     mCursorY += (h + mItemHeightQuantum - 1) & ~(mItemHeightQuantum - 1);
144   }
145   else
146   {
147     DALI_LOG_INFO( gLogFilter, Debug::General, "Blob atlas %p is now FULL\n", this );
148
149     // The atlas is now considered to be full
150     mIsFull = true;
151     return false;
152   }
153
154   if (w * h == length)
155   {
156     TexSubImage( x, y, w, h, blob );
157   }
158   else
159   {
160     TexSubImage( x, y, w, h-1, blob );
161
162     // Upload the last row separately
163     TexSubImage( x, y + h - 1, length - (w * (h - 1)), 1 , blob + w * (h - 1));
164   }
165
166   DALI_LOG_INFO( gLogFilter, Debug::General, "Blob atlas %p capacity %d filled %d %f\%\n",
167                  this,
168                  mTextureWidth*mTextureHeight,
169                  mCursorY*mItemWidth + mCursorX*mTextureHeight,
170                  100.0f * (float)(mCursorY*mItemWidth + mCursorX*mTextureHeight) / (float)(mTextureWidth*mTextureHeight) );
171
172   Key key;
173   key.fontId = fontId;
174   key.glyphIndex = glyphIndex;
175   key.cacheIndex = mItemCache.size();
176   mItemLookup.push_back( key );
177
178   x /= mItemWidth;
179   y /= mItemHeightQuantum;
180
181   Item item;
182   EncodeBlobCoordinate( 0, 0, x, y, nominalWidth, nominalHeight, &item.coords[0] ); // BOTTOM_LEFT
183   EncodeBlobCoordinate( 0, 1, x, y, nominalWidth, nominalHeight, &item.coords[1] ); // TOP_LEFT
184   EncodeBlobCoordinate( 1, 0, x, y, nominalWidth, nominalHeight, &item.coords[2] ); // BOTTOM_RIGHT
185   EncodeBlobCoordinate( 1, 1, x, y, nominalWidth, nominalHeight, &item.coords[3] ); // TOP_RIGHT
186   mItemCache.push_back( item );
187
188   coords[0] = item.coords[0];
189   coords[1] = item.coords[1];
190   coords[2] = item.coords[2];
191   coords[3] = item.coords[3];
192
193   return true;
194 }
195
196 void VectorBlobAtlas::TexSubImage( unsigned int offsetX,
197                                    unsigned int offsetY,
198                                    unsigned int width,
199                                    unsigned int height,
200                                    VectorBlob* blob )
201 {
202   PixelBuffer* pixbuf = mAtlasTexture.GetBuffer();
203   size_t pos;
204   size_t dataIndex = 0;
205   for( size_t y= offsetY; y< height + offsetY; y++ )
206   {
207     pos = y * mTextureWidth * 4;
208     for( size_t x = offsetX; x < width + offsetX; x++ )
209     {
210       pixbuf[pos+x*4] =  0xFF & blob[dataIndex].r;
211       pixbuf[pos+x*4+1] = 0xFF & blob[dataIndex].g;
212       pixbuf[pos+x*4+2] = 0xFF & blob[dataIndex].b;
213       pixbuf[pos+x*4+3] = 0xFF & blob[dataIndex].a;
214       dataIndex++;
215     }
216   }
217
218   mAtlasTexture.Update();
219 }
220
221 } // namespace Text
222
223 } // namespace Toolkit
224
225 } // namespace Dali