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