Merge "Support Underline to Markup using underlined-character-run" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / rendering / atlas / text-atlas-renderer.cpp
1 /*
2  * Copyright (c) 2021 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/atlas/text-atlas-renderer.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/animation/constraints.h>
25 #include <dali/public-api/rendering/geometry.h>
26 #include <dali/public-api/rendering/renderer.h>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
30 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
31 #include <dali-toolkit/internal/text/glyph-run.h>
32 #include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
33 #include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
34 #include <dali-toolkit/internal/text/text-view.h>
35
36 using namespace Dali;
37 using namespace Dali::Toolkit;
38 using namespace Dali::Toolkit::Text;
39
40 namespace
41 {
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_RENDERING");
44 #endif
45
46 const float    ZERO(0.0f);
47 const float    HALF(0.5f);
48 const float    ONE(1.0f);
49 const uint32_t DEFAULT_ATLAS_WIDTH  = 512u;
50 const uint32_t DEFAULT_ATLAS_HEIGHT = 512u;
51 const uint32_t MAX_ATLAS_WIDTH  = 1024u;
52 const uint32_t MAX_ATLAS_HEIGHT = 1024u;
53 const uint32_t DOUBLE_PIXEL_PADDING = 4u;//Padding will be added twice to Atlas
54 const uint16_t NO_OUTLINE           = 0u;
55 } // namespace
56
57 struct AtlasRenderer::Impl
58 {
59   enum Style
60   {
61     STYLE_NORMAL,
62     STYLE_DROP_SHADOW
63   };
64
65   struct MeshRecord
66   {
67     MeshRecord()
68     : mAtlasId(0u)
69     {
70     }
71
72     uint32_t             mAtlasId;
73     AtlasManager::Mesh2D mMesh;
74   };
75
76   /**
77    * brief Struct used to generate the underline mesh.
78    * There is one Extent per line of text.
79    */
80   struct Extent
81   {
82     Extent()
83     : mBaseLine(0.0f),
84       mLeft(0.0f),
85       mRight(0.0f),
86       mUnderlinePosition(0.0f),
87       mUnderlineThickness(0.0f),
88       mMeshRecordIndex(0u),
89       mUnderlineChunkId(0u)
90     {
91     }
92
93     float    mBaseLine;
94     float    mLeft;
95     float    mRight;
96     float    mUnderlinePosition;
97     float    mUnderlineThickness;
98     uint32_t mMeshRecordIndex;
99     uint32_t mUnderlineChunkId;
100   };
101
102   struct MaxBlockSize
103   {
104     MaxBlockSize()
105     : mFontId(0),
106       mNeededBlockWidth(0),
107       mNeededBlockHeight(0)
108     {
109     }
110
111     FontId   mFontId;
112     uint32_t mNeededBlockWidth;
113     uint32_t mNeededBlockHeight;
114   };
115
116   struct CheckEntry
117   {
118     CheckEntry()
119     : mFontId(0),
120       mIndex(0)
121     {
122     }
123
124     FontId           mFontId;
125     Text::GlyphIndex mIndex;
126   };
127
128   struct TextCacheEntry
129   {
130     TextCacheEntry()
131     : mFontId{0u},
132       mIndex{0u},
133       mImageId{0u},
134       mOutlineWidth{0u},
135       isItalic{false},
136       isBold{false}
137     {
138     }
139
140     FontId           mFontId;
141     Text::GlyphIndex mIndex;
142     uint32_t         mImageId;
143     uint16_t         mOutlineWidth;
144     bool             isItalic : 1;
145     bool             isBold : 1;
146   };
147
148   Impl()
149   : mDepth(0)
150   {
151     mGlyphManager = AtlasGlyphManager::Get();
152     mFontClient   = TextAbstraction::FontClient::Get();
153
154     mQuadVertexFormat["aPosition"] = Property::VECTOR2;
155     mQuadVertexFormat["aTexCoord"] = Property::VECTOR2;
156     mQuadVertexFormat["aColor"]    = Property::VECTOR4;
157   }
158
159   bool IsGlyphUnderlined(GlyphIndex              index,
160                          const Vector<GlyphRun>& underlineRuns)
161   {
162     for(Vector<GlyphRun>::ConstIterator it    = underlineRuns.Begin(),
163                                         endIt = underlineRuns.End();
164         it != endIt;
165         ++it)
166     {
167       const GlyphRun& run = *it;
168
169       if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
170       {
171         return true;
172       }
173     }
174
175     return false;
176   }
177
178   void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
179   {
180     const bool glyphNotCached = !mGlyphManager.IsCached(glyph.fontId, glyph.index, style, slot); // Check FontGlyphRecord vector for entry with glyph index and fontId
181
182     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached) ? "not cached" : "cached");
183
184     if(glyphNotCached)
185     {
186       MaxBlockSize& blockSize = mBlockSizes[0u];
187
188       if(lastFontId != glyph.fontId)
189       {
190         uint32_t index = 0u;
191         // Looks through all stored block sizes until finds the one which mataches required glyph font it.  Ensures new atlas block size will match existing for same font id.
192         // CalculateBlocksSize() above ensures a block size entry exists.
193         for(std::vector<MaxBlockSize>::const_iterator it    = mBlockSizes.begin(),
194                                                       endIt = mBlockSizes.end();
195             it != endIt;
196             ++it, ++index)
197         {
198           const MaxBlockSize& blockSizeEntry = *it;
199           if(blockSizeEntry.mFontId == glyph.fontId)
200           {
201             blockSize = mBlockSizes[index];
202           }
203         }
204       }
205
206       // Create a new image for the glyph
207       PixelData bitmap;
208
209       // Whether the glyph is an outline.
210       const bool isOutline = 0u != style.outline;
211
212       // Whether the current glyph is a color one.
213       const bool isColorGlyph = mFontClient.IsColorGlyph(glyph.fontId, glyph.index);
214
215       if(!isOutline || (isOutline && !isColorGlyph))
216       {
217         // Retrieve the emoji's bitmap.
218         TextAbstraction::FontClient::GlyphBufferData glyphBufferData;
219         glyphBufferData.width  = isColorGlyph ? glyph.width : 0; // Desired width and height.
220         glyphBufferData.height = isColorGlyph ? glyph.height : 0;
221
222         mFontClient.CreateBitmap(glyph.fontId,
223                                  glyph.index,
224                                  glyph.isItalicRequired,
225                                  glyph.isBoldRequired,
226                                  glyphBufferData,
227                                  style.outline);
228
229         // Create the pixel data.
230         bitmap = PixelData::New(glyphBufferData.buffer,
231                                 glyphBufferData.width * glyphBufferData.height * GetBytesPerPixel(glyphBufferData.format),
232                                 glyphBufferData.width,
233                                 glyphBufferData.height,
234                                 glyphBufferData.format,
235                                 PixelData::DELETE_ARRAY);
236
237         if(bitmap)
238         {
239           // Ensure that the next image will fit into the current block size
240           if(bitmap.GetWidth() > blockSize.mNeededBlockWidth)
241           {
242             blockSize.mNeededBlockWidth = bitmap.GetWidth();
243           }
244
245           if(bitmap.GetHeight() > blockSize.mNeededBlockHeight)
246           {
247             blockSize.mNeededBlockHeight = bitmap.GetHeight();
248           }
249
250           // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
251
252           // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
253           uint32_t default_width = DEFAULT_ATLAS_WIDTH;
254           uint32_t default_height = DEFAULT_ATLAS_HEIGHT;
255
256           while (
257             (blockSize.mNeededBlockWidth >= (default_width - (DOUBLE_PIXEL_PADDING + 1u)) ||
258              blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u)))
259             &&
260             (default_width < MAX_ATLAS_WIDTH &&
261              default_height < MAX_ATLAS_HEIGHT))
262           {
263             default_width <<= 1u;
264             default_height <<= 1u;
265           }
266           mGlyphManager.SetNewAtlasSize(default_width,
267                                         default_height,
268                                         blockSize.mNeededBlockWidth,
269                                         blockSize.mNeededBlockHeight);
270
271           // Locate a new slot for our glyph
272           mGlyphManager.Add(glyph, style, bitmap, slot); // slot will be 0 is glyph not added
273         }
274       }
275     }
276     else
277     {
278       // We have 2+ copies of the same glyph
279       mGlyphManager.AdjustReferenceCount(glyph.fontId, glyph.index, style, 1); //increment
280     }
281   }
282
283   void GenerateMesh(const GlyphInfo&         glyph,
284                     const Vector2&           position,
285                     const Vector4&           color,
286                     uint16_t                 outline,
287                     AtlasManager::AtlasSlot& slot,
288                     bool                     underlineGlyph,
289                     float                    currentUnderlinePosition,
290                     float                    currentUnderlineThickness,
291                     std::vector<MeshRecord>& meshContainer,
292                     Vector<TextCacheEntry>&  newTextCache,
293                     Vector<Extent>&          extents,
294                     uint32_t                 underlineChunkId)
295   {
296     // Generate mesh data for this quad, plugging in our supplied position
297     AtlasManager::Mesh2D newMesh;
298     mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
299
300     TextCacheEntry textCacheEntry;
301     textCacheEntry.mFontId       = glyph.fontId;
302     textCacheEntry.mImageId      = slot.mImageId;
303     textCacheEntry.mIndex        = glyph.index;
304     textCacheEntry.mOutlineWidth = outline;
305     textCacheEntry.isItalic      = glyph.isItalicRequired;
306     textCacheEntry.isBold        = glyph.isBoldRequired;
307
308     newTextCache.PushBack(textCacheEntry);
309
310     AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
311
312     for(unsigned int index = 0u, size = newMesh.mVertices.Count();
313         index < size;
314         ++index)
315     {
316       AtlasManager::Vertex2D& vertex = *(verticesBuffer + index);
317
318       // Set the color of the vertex.
319       vertex.mColor = color;
320     }
321
322     // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas)
323     StitchTextMesh(meshContainer,
324                    newMesh,
325                    extents,
326                    position.y + glyph.yBearing,
327                    underlineGlyph,
328                    currentUnderlinePosition,
329                    currentUnderlineThickness,
330                    slot,
331                    underlineChunkId);
332   }
333
334   void CreateActors(const std::vector<MeshRecord>& meshContainer,
335                     const Size&                    textSize,
336                     const Vector4&                 color,
337                     const Vector4&                 shadowColor,
338                     const Vector2&                 shadowOffset,
339                     Actor                          textControl,
340                     Property::Index                animatablePropertyIndex,
341                     bool                           drawShadow)
342   {
343     if(!mActor)
344     {
345       // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues.
346       mActor = Actor::New();
347       mActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
348       mActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
349       mActor.SetProperty(Actor::Property::SIZE, textSize);
350       mActor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
351     }
352
353     for(std::vector<MeshRecord>::const_iterator it    = meshContainer.begin(),
354                                                 endIt = meshContainer.end();
355         it != endIt;
356         ++it)
357     {
358       const MeshRecord& meshRecord = *it;
359
360       Actor actor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL);
361
362       // Whether the actor has renderers.
363       const bool hasRenderer = actor.GetRendererCount() > 0u;
364
365       // Create an effect if necessary
366       if(hasRenderer &&
367          drawShadow)
368       {
369         // Change the color of the vertices.
370         for(Vector<AtlasManager::Vertex2D>::Iterator vIt    = meshRecord.mMesh.mVertices.Begin(),
371                                                      vEndIt = meshRecord.mMesh.mVertices.End();
372             vIt != vEndIt;
373             ++vIt)
374         {
375           AtlasManager::Vertex2D& vertex = *vIt;
376
377           vertex.mColor = shadowColor;
378         }
379
380         Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_DROP_SHADOW);
381 #if defined(DEBUG_ENABLED)
382         shadowActor.SetProperty(Dali::Actor::Property::NAME, "Text Shadow renderable actor");
383 #endif
384         // Offset shadow in x and y
385         shadowActor.RegisterProperty("uOffset", shadowOffset);
386         Dali::Renderer renderer(shadowActor.GetRendererAt(0));
387         int            depthIndex = renderer.GetProperty<int>(Dali::Renderer::Property::DEPTH_INDEX);
388         renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1);
389         mActor.Add(shadowActor);
390       }
391
392       if(hasRenderer)
393       {
394         mActor.Add(actor);
395       }
396     }
397   }
398
399   void AddGlyphs(Text::ViewInterface&     view,
400                  Actor                    textControl,
401                  Property::Index          animatablePropertyIndex,
402                  const Vector<Vector2>&   positions,
403                  const Vector<GlyphInfo>& glyphs,
404                  const Vector4&           defaultColor,
405                  const Vector4* const     colorsBuffer,
406                  const ColorIndex* const  colorIndicesBuffer,
407                  int                      depth,
408                  float                    minLineOffset)
409   {
410     AtlasManager::AtlasSlot slot;
411     slot.mImageId = 0u;
412     slot.mAtlasId = 0u;
413
414     AtlasManager::AtlasSlot slotOutline;
415     slotOutline.mImageId = 0u;
416     slotOutline.mAtlasId = 0u;
417
418     std::vector<MeshRecord> meshContainer;
419     std::vector<MeshRecord> meshContainerOutline;
420     Vector<Extent>          extents;
421     mDepth = depth;
422
423     const Vector2& textSize(view.GetLayoutSize());
424     const Vector2  halfTextSize(textSize * 0.5f);
425     const Vector2& shadowOffset(view.GetShadowOffset());
426     const Vector4& shadowColor(view.GetShadowColor());
427     const bool     underlineEnabled = view.IsUnderlineEnabled();
428     const Vector4& underlineColor(view.GetUnderlineColor());
429     const float    underlineHeight = view.GetUnderlineHeight();
430     const uint16_t outlineWidth    = view.GetOutlineWidth();
431     const Vector4& outlineColor(view.GetOutlineColor());
432     const bool     isOutline = 0u != outlineWidth;
433
434     const bool useDefaultColor = (NULL == colorsBuffer);
435
436     // Get the underline runs.
437     const Length     numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
438     Vector<GlyphRun> underlineRuns;
439     underlineRuns.Resize(numberOfUnderlineRuns);
440     view.GetUnderlineRuns(underlineRuns.Begin(),
441                           0u,
442                           numberOfUnderlineRuns);
443
444     bool thereAreUnderlinedGlyphs = false;
445
446     float  currentUnderlinePosition  = ZERO;
447     float  currentUnderlineThickness = underlineHeight;
448     FontId lastFontId                = 0;
449     FontId lastUnderlinedFontId      = 0;
450     Style  style                     = STYLE_NORMAL;
451
452     if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
453     {
454       style = STYLE_DROP_SHADOW;
455     }
456
457     CalculateBlocksSize(glyphs);
458
459     // Avoid emptying mTextCache (& removing references) until after incremented references for the new text
460     Vector<TextCacheEntry> newTextCache;
461     const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
462     const Vector2* const   positionsBuffer = positions.Begin();
463     const Vector2          lineOffsetPosition(minLineOffset, 0.f);
464
465     //For septated underlined chunks. (this is for Markup case)
466     uint32_t underlineChunkId = 0u; // give id for each chunk.
467     bool     isPreUnderlined = false; // status of underlined for previous glyph.
468
469     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
470     {
471       const GlyphInfo& glyph             = *(glyphsBuffer + i);
472       const bool       isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns);
473       thereAreUnderlinedGlyphs           = thereAreUnderlinedGlyphs || isGlyphUnderlined;
474
475       // No operation for white space
476       if(glyph.width && glyph.height)
477       {
478         // Are we still using the same fontId as previous
479         if(isGlyphUnderlined && (glyph.fontId != lastUnderlinedFontId))
480         {
481           // We need to fetch fresh font underline metrics
482           FontMetrics fontMetrics;
483           mFontClient.GetFontMetrics(glyph.fontId, fontMetrics);
484           currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
485           const float descender    = ceil(fabsf(fontMetrics.descender));
486
487           if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
488           {
489             currentUnderlineThickness = fontMetrics.underlineThickness;
490
491             // Ensure underline will be at least a pixel high
492             if(currentUnderlineThickness < ONE)
493             {
494               currentUnderlineThickness = ONE;
495             }
496             else
497             {
498               currentUnderlineThickness = ceil(currentUnderlineThickness);
499             }
500           }
501
502           // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
503           if(currentUnderlinePosition > descender)
504           {
505             currentUnderlinePosition = descender;
506           }
507
508           if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
509           {
510             // Move offset down by one ( EFL behavior )
511             currentUnderlinePosition = ONE;
512           }
513
514           lastUnderlinedFontId = glyph.fontId;
515
516         } // underline
517
518         AtlasGlyphManager::GlyphStyle style;
519         style.isItalic = glyph.isItalicRequired;
520         style.isBold   = glyph.isBoldRequired;
521
522         // Retrieves and caches the glyph's bitmap.
523         CacheGlyph(glyph, lastFontId, style, slot);
524
525         // Retrieves and caches the outline glyph's bitmap.
526         if(isOutline)
527         {
528           style.outline = outlineWidth;
529           CacheGlyph(glyph, lastFontId, style, slotOutline);
530         }
531
532         // Move the origin (0,0) of the mesh to the center of the actor
533         const Vector2& temp     = *(positionsBuffer + i);
534         const Vector2  position = Vector2(roundf(temp.x), temp.y) - halfTextSize - lineOffsetPosition; // roundf() avoids pixel alignment issues.
535
536         if(0u != slot.mImageId) // invalid slot id, glyph has failed to be added to atlas
537         {
538           Vector2 positionPlusOutlineOffset = position;
539           if(isOutline)
540           {
541             // Add an offset to the text.
542             const float outlineWidthOffset = static_cast<float>(outlineWidth);
543             positionPlusOutlineOffset += Vector2(outlineWidthOffset, outlineWidthOffset);
544           }
545
546           // Get the color of the character.
547           const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndicesBuffer + i);
548           const Vector4&   color      = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + colorIndex - 1u);
549
550           GenerateMesh(glyph,
551                        positionPlusOutlineOffset,
552                        color,
553                        NO_OUTLINE,
554                        slot,
555                        isGlyphUnderlined,
556                        currentUnderlinePosition,
557                        currentUnderlineThickness,
558                        meshContainer,
559                        newTextCache,
560                        extents,
561                        underlineChunkId);
562
563           lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
564         }
565
566         if(isOutline && (0u != slotOutline.mImageId)) // invalid slot id, glyph has failed to be added to atlas
567         {
568           GenerateMesh(glyph,
569                        position,
570                        outlineColor,
571                        outlineWidth,
572                        slotOutline,
573                        false,
574                        currentUnderlinePosition,
575                        currentUnderlineThickness,
576                        meshContainerOutline,
577                        newTextCache,
578                        extents,
579                        0u);
580         }
581
582
583         //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
584         // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u>   World</u> Hello <u>World</u>"
585         if( isPreUnderlined && (isPreUnderlined != isGlyphUnderlined))
586         {
587           underlineChunkId++;
588         }
589         //Keep status of underlined for previous glyph to check consecutive indices
590         isPreUnderlined = isGlyphUnderlined;
591       }
592     } // glyphs
593
594     // Now remove references for the old text
595     RemoveText();
596     mTextCache.Swap(newTextCache);
597
598     if(thereAreUnderlinedGlyphs)
599     {
600       // Check to see if any of the text needs an underline
601       GenerateUnderlines(meshContainer, extents, underlineColor);
602     }
603
604     // For each MeshData object, create a mesh actor and add to the renderable actor
605     bool isShadowDrawn = false;
606     if(!meshContainerOutline.empty())
607     {
608       const bool drawShadow = STYLE_DROP_SHADOW == style;
609       CreateActors(meshContainerOutline,
610                    textSize,
611                    outlineColor,
612                    shadowColor,
613                    shadowOffset,
614                    textControl,
615                    animatablePropertyIndex,
616                    drawShadow);
617
618       isShadowDrawn = drawShadow;
619     }
620
621     // For each MeshData object, create a mesh actor and add to the renderable actor
622     if(!meshContainer.empty())
623     {
624       const bool drawShadow = !isShadowDrawn && (STYLE_DROP_SHADOW == style);
625       CreateActors(meshContainer,
626                    textSize,
627                    defaultColor,
628                    shadowColor,
629                    shadowOffset,
630                    textControl,
631                    animatablePropertyIndex,
632                    drawShadow);
633     }
634
635 #if defined(DEBUG_ENABLED)
636     Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics();
637     DALI_LOG_INFO(gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", metrics.mGlyphCount, metrics.mAtlasMetrics.mAtlasCount, metrics.mAtlasMetrics.mTextureMemoryUsed / 1024);
638
639     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "%s\n", metrics.mVerboseGlyphCounts.c_str());
640
641     for(uint32_t i = 0; i < metrics.mAtlasMetrics.mAtlasCount; ++i)
642     {
643       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "   Atlas [%i] %sPixels: %s Size: %ix%i, BlockSize: %ix%i, BlocksUsed: %i/%i\n", i + 1, i > 8 ? "" : " ", metrics.mAtlasMetrics.mAtlasMetrics[i].mPixelFormat == Pixel::L8 ? "L8  " : "BGRA", metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mWidth, metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mHeight, metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mBlockWidth, metrics.mAtlasMetrics.mAtlasMetrics[i].mSize.mBlockHeight, metrics.mAtlasMetrics.mAtlasMetrics[i].mBlocksUsed, metrics.mAtlasMetrics.mAtlasMetrics[i].mTotalBlocks);
644     }
645 #endif
646   }
647
648   void RemoveText()
649   {
650     for(Vector<TextCacheEntry>::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter)
651     {
652       AtlasGlyphManager::GlyphStyle style;
653       style.outline  = oldTextIter->mOutlineWidth;
654       style.isItalic = oldTextIter->isItalic;
655       style.isBold   = oldTextIter->isBold;
656       mGlyphManager.AdjustReferenceCount(oldTextIter->mFontId, oldTextIter->mIndex, style, -1 /*decrement*/);
657     }
658     mTextCache.Resize(0);
659   }
660
661   Actor CreateMeshActor(Actor textControl, Property::Index animatablePropertyIndex, const Vector4& defaultColor, const MeshRecord& meshRecord, const Vector2& actorSize, Style style)
662   {
663     VertexBuffer quadVertices = VertexBuffer::New(mQuadVertexFormat);
664     quadVertices.SetData(const_cast<AtlasManager::Vertex2D*>(&meshRecord.mMesh.mVertices[0]), meshRecord.mMesh.mVertices.Size());
665
666     Geometry quadGeometry = Geometry::New();
667     quadGeometry.AddVertexBuffer(quadVertices);
668     quadGeometry.SetIndexBuffer(&meshRecord.mMesh.mIndices[0], meshRecord.mMesh.mIndices.Size());
669
670     TextureSet textureSet(mGlyphManager.GetTextures(meshRecord.mAtlasId));
671
672     // Choose the shader to use.
673     const bool isColorShader = (STYLE_DROP_SHADOW != style) && (Pixel::BGRA8888 == mGlyphManager.GetPixelFormat(meshRecord.mAtlasId));
674     Shader     shader;
675     if(isColorShader)
676     {
677       // The glyph is an emoji and is not a shadow.
678       if(!mShaderRgba)
679       {
680         mShaderRgba = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_RGBA_SHADER_FRAG);
681       }
682       shader = mShaderRgba;
683     }
684     else
685     {
686       // The glyph is text or a shadow.
687       if(!mShaderL8)
688       {
689         mShaderL8 = Shader::New(SHADER_TEXT_ATLAS_SHADER_VERT, SHADER_TEXT_ATLAS_L8_SHADER_FRAG);
690       }
691       shader = mShaderL8;
692     }
693
694     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "defaultColor[%f, %f, %f, %f ]\n", defaultColor.r, defaultColor.g, defaultColor.b, defaultColor.a);
695
696     Dali::Property::Index shaderTextColorIndex = shader.RegisterProperty("textColorAnimatable", defaultColor);
697
698     if(animatablePropertyIndex != Property::INVALID_INDEX)
699     {
700       // create constraint for the animatable text's color Property with textColorAnimatable in the shader.
701       if(shaderTextColorIndex)
702       {
703         Constraint constraint = Constraint::New<Vector4>(shader, shaderTextColorIndex, EqualToConstraint());
704         constraint.AddSource(Source(textControl, animatablePropertyIndex));
705         constraint.Apply();
706       }
707     }
708     else
709     {
710       // If not animating the text colour then set to 1's so shader uses the current vertex color
711       shader.RegisterProperty("textColorAnimatable", Vector4(1.0, 1.0, 1.0, 1.0));
712     }
713
714     Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, shader);
715     renderer.SetTextures(textureSet);
716     renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
717     renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT + mDepth);
718
719     Actor actor = Actor::New();
720 #if defined(DEBUG_ENABLED)
721     actor.SetProperty(Dali::Actor::Property::NAME, "Text renderable actor");
722 #endif
723     actor.AddRenderer(renderer);
724     // Keep all of the origins aligned
725     actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
726     actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
727     actor.SetProperty(Actor::Property::SIZE, actorSize);
728     actor.RegisterProperty("uOffset", Vector2::ZERO);
729     actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
730
731     return actor;
732   }
733
734   void StitchTextMesh(std::vector<MeshRecord>& meshContainer,
735                       AtlasManager::Mesh2D&    newMesh,
736                       Vector<Extent>&          extents,
737                       float                    baseLine,
738                       bool                     underlineGlyph,
739                       float                    underlinePosition,
740                       float                    underlineThickness,
741                       AtlasManager::AtlasSlot& slot,
742                       uint32_t                 underlineChunkId)
743   {
744     if(slot.mImageId)
745     {
746       float left  = newMesh.mVertices[0].mPosition.x;
747       float right = newMesh.mVertices[1].mPosition.x;
748
749       // Check to see if there's a mesh data object that references the same atlas ?
750       uint32_t index = 0;
751       for(std::vector<MeshRecord>::iterator mIt    = meshContainer.begin(),
752                                             mEndIt = meshContainer.end();
753           mIt != mEndIt;
754           ++mIt, ++index)
755       {
756         if(slot.mAtlasId == mIt->mAtlasId)
757         {
758           // Append the mesh to the existing mesh and adjust any extents
759           Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
760
761           if(underlineGlyph)
762           {
763             AdjustExtents(extents,
764                           meshContainer,
765                           index,
766                           left,
767                           right,
768                           baseLine,
769                           underlinePosition,
770                           underlineThickness,
771                           underlineChunkId);
772           }
773
774           return;
775         }
776       }
777
778       // No mesh data object currently exists that references this atlas, so create a new one
779       MeshRecord meshRecord;
780       meshRecord.mAtlasId = slot.mAtlasId;
781       meshRecord.mMesh    = newMesh;
782       meshContainer.push_back(meshRecord);
783
784       if(underlineGlyph)
785       {
786         // Adjust extents for this new meshrecord
787         AdjustExtents(extents,
788                       meshContainer,
789                       meshContainer.size() - 1u,
790                       left,
791                       right,
792                       baseLine,
793                       underlinePosition,
794                       underlineThickness,
795                       underlineChunkId);
796       }
797     }
798   }
799
800   void AdjustExtents(Vector<Extent>&          extents,
801                      std::vector<MeshRecord>& meshRecords,
802                      uint32_t                 index,
803                      float                    left,
804                      float                    right,
805                      float                    baseLine,
806                      float                    underlinePosition,
807                      float                    underlineThickness,
808                      uint32_t                 underlineChunkId)
809   {
810     bool foundExtent = false;
811     for(Vector<Extent>::Iterator eIt    = extents.Begin(),
812                                  eEndIt = extents.End();
813         eIt != eEndIt;
814         ++eIt)
815     {
816       if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId)
817       {
818         foundExtent = true;
819         if(left < eIt->mLeft)
820         {
821           eIt->mLeft = left;
822         }
823         if(right > eIt->mRight)
824         {
825           eIt->mRight = right;
826         }
827
828         if(underlinePosition > eIt->mUnderlinePosition)
829         {
830           eIt->mUnderlinePosition = underlinePosition;
831         }
832         if(underlineThickness > eIt->mUnderlineThickness)
833         {
834           eIt->mUnderlineThickness = underlineThickness;
835         }
836       }
837     }
838     if(!foundExtent)
839     {
840       Extent extent;
841       extent.mLeft               = left;
842       extent.mRight              = right;
843       extent.mBaseLine           = baseLine;
844       extent.mUnderlinePosition  = underlinePosition;
845       extent.mUnderlineThickness = underlineThickness;
846       extent.mMeshRecordIndex    = index;
847       extent.mUnderlineChunkId = underlineChunkId;
848       extents.PushBack(extent);
849     }
850   }
851
852   void CalculateBlocksSize(const Vector<GlyphInfo>& glyphs)
853   {
854     for(Vector<GlyphInfo>::ConstIterator glyphIt    = glyphs.Begin(),
855                                          glyphEndIt = glyphs.End();
856         glyphIt != glyphEndIt;
857         ++glyphIt)
858     {
859       const FontId fontId    = (*glyphIt).fontId;
860       bool         foundFont = false;
861
862       for(std::vector<MaxBlockSize>::const_iterator blockIt    = mBlockSizes.begin(),
863                                                     blockEndIt = mBlockSizes.end();
864           blockIt != blockEndIt;
865           ++blockIt)
866       {
867         if((*blockIt).mFontId == fontId) // Different size fonts will have a different fontId
868         {
869           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize match found fontID(%u) glyphIndex(%u)\n", fontId, (*glyphIt).index);
870           foundFont = true;
871           break;
872         }
873       }
874
875       if(!foundFont)
876       {
877         FontMetrics fontMetrics;
878         mFontClient.GetFontMetrics(fontId, fontMetrics);
879
880         MaxBlockSize maxBlockSize;
881         maxBlockSize.mNeededBlockWidth  = static_cast<uint32_t>(fontMetrics.height);
882         maxBlockSize.mNeededBlockHeight = maxBlockSize.mNeededBlockWidth;
883         maxBlockSize.mFontId            = fontId;
884         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::CalculateBlocksSize New font with no matched blocksize, setting blocksize[%u]\n", maxBlockSize.mNeededBlockWidth);
885         mBlockSizes.push_back(maxBlockSize);
886       }
887     }
888   }
889
890   void GenerateUnderlines(std::vector<MeshRecord>& meshRecords,
891                           Vector<Extent>&          extents,
892                           const Vector4&           underlineColor)
893   {
894     AtlasManager::Mesh2D newMesh;
895     unsigned short       faceIndex = 0;
896     for(Vector<Extent>::ConstIterator eIt    = extents.Begin(),
897                                       eEndIt = extents.End();
898         eIt != eEndIt;
899         ++eIt)
900     {
901       AtlasManager::Vertex2D vert;
902       uint32_t               index = eIt->mMeshRecordIndex;
903       Vector2                uv    = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
904
905       // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
906       float u         = HALF / uv.x;
907       float v         = HALF / uv.y;
908       float thickness = eIt->mUnderlineThickness;
909       float baseLine  = eIt->mBaseLine + eIt->mUnderlinePosition - (thickness * HALF);
910       float tlx       = eIt->mLeft;
911       float brx       = eIt->mRight;
912
913       vert.mPosition.x  = tlx;
914       vert.mPosition.y  = baseLine;
915       vert.mTexCoords.x = ZERO;
916       vert.mTexCoords.y = ZERO;
917       vert.mColor       = underlineColor;
918       newMesh.mVertices.PushBack(vert);
919
920       vert.mPosition.x  = brx;
921       vert.mPosition.y  = baseLine;
922       vert.mTexCoords.x = u;
923       vert.mColor       = underlineColor;
924       newMesh.mVertices.PushBack(vert);
925
926       vert.mPosition.x  = tlx;
927       vert.mPosition.y  = baseLine + thickness;
928       vert.mTexCoords.x = ZERO;
929       vert.mTexCoords.y = v;
930       vert.mColor       = underlineColor;
931       newMesh.mVertices.PushBack(vert);
932
933       vert.mPosition.x  = brx;
934       vert.mPosition.y  = baseLine + thickness;
935       vert.mTexCoords.x = u;
936       vert.mColor       = underlineColor;
937       newMesh.mVertices.PushBack(vert);
938
939       // Six indices in counter clockwise winding
940       newMesh.mIndices.PushBack(faceIndex + 1u);
941       newMesh.mIndices.PushBack(faceIndex);
942       newMesh.mIndices.PushBack(faceIndex + 2u);
943       newMesh.mIndices.PushBack(faceIndex + 2u);
944       newMesh.mIndices.PushBack(faceIndex + 3u);
945       newMesh.mIndices.PushBack(faceIndex + 1u);
946       faceIndex += 4;
947
948       Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
949     }
950   }
951
952   Actor                       mActor;            ///< The actor parent which renders the text
953   AtlasGlyphManager           mGlyphManager;     ///< Glyph Manager to handle upload and caching
954   TextAbstraction::FontClient mFontClient;       ///< The font client used to supply glyph information
955   Shader                      mShaderL8;         ///< The shader for glyphs and emoji's shadows.
956   Shader                      mShaderRgba;       ///< The shader for emojis.
957   std::vector<MaxBlockSize>   mBlockSizes;       ///< Maximum size needed to contain a glyph in a block within a new atlas
958   Vector<TextCacheEntry>      mTextCache;        ///< Caches data from previous render
959   Property::Map               mQuadVertexFormat; ///< Describes the vertex format for text
960   int                         mDepth;            ///< DepthIndex passed by control when connect to stage
961 };
962
963 Text::RendererPtr AtlasRenderer::New()
964 {
965   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Text::AtlasRenderer::New()\n");
966
967   return Text::RendererPtr(new AtlasRenderer());
968 }
969
970 Actor AtlasRenderer::Render(Text::ViewInterface& view,
971                             Actor                textControl,
972                             Property::Index      animatablePropertyIndex,
973                             float&               alignmentOffset,
974                             int                  depth)
975 {
976   DALI_LOG_INFO(gLogFilter, Debug::General, "Text::AtlasRenderer::Render()\n");
977
978   UnparentAndReset(mImpl->mActor);
979
980   Length numberOfGlyphs = view.GetNumberOfGlyphs();
981
982   if(numberOfGlyphs > 0u)
983   {
984     Vector<GlyphInfo> glyphs;
985     glyphs.Resize(numberOfGlyphs);
986
987     Vector<Vector2> positions;
988     positions.Resize(numberOfGlyphs);
989
990     numberOfGlyphs = view.GetGlyphs(glyphs.Begin(),
991                                     positions.Begin(),
992                                     alignmentOffset,
993                                     0u,
994                                     numberOfGlyphs);
995
996     glyphs.Resize(numberOfGlyphs);
997     positions.Resize(numberOfGlyphs);
998
999     const Vector4* const    colorsBuffer       = view.GetColors();
1000     const ColorIndex* const colorIndicesBuffer = view.GetColorIndices();
1001     const Vector4&          defaultColor       = view.GetTextColor();
1002
1003     mImpl->AddGlyphs(view,
1004                      textControl,
1005                      animatablePropertyIndex,
1006                      positions,
1007                      glyphs,
1008                      defaultColor,
1009                      colorsBuffer,
1010                      colorIndicesBuffer,
1011                      depth,
1012                      alignmentOffset);
1013
1014     /* In the case where AddGlyphs does not create a renderable Actor for example when glyphs are all whitespace create a new Actor. */
1015     /* This renderable actor is used to position the text, other "decorations" can rely on there always being an Actor regardless of it is whitespace or regular text. */
1016     if(!mImpl->mActor)
1017     {
1018       mImpl->mActor = Actor::New();
1019     }
1020   }
1021
1022   return mImpl->mActor;
1023 }
1024
1025 AtlasRenderer::AtlasRenderer()
1026 {
1027   mImpl = new Impl();
1028 }
1029
1030 AtlasRenderer::~AtlasRenderer()
1031 {
1032   mImpl->RemoveText();
1033   delete mImpl;
1034 }