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