From: HyunJu Shin Date: Thu, 14 Dec 2017 00:11:01 +0000 (+0000) Subject: Merge "Add codes to prevent underflow" into devel/master X-Git-Tag: dali_1.3.4~5 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=70d097e215f27a8b6602d4a41e037567ea0c2a04;hp=08a5346b49f4ca8234bf97750e1e030ef89b2750 Merge "Add codes to prevent underflow" into devel/master --- diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp index 8d73622..02f2e66 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Controller.cpp @@ -184,9 +184,16 @@ int UtcDaliTextControllerImfEvent(void) // Enables the text input. controller->EnableTextInput( decorator ); + // Set the placeholder text. + controller->SetPlaceholderText( Controller::PLACEHOLDER_TYPE_INACTIVE, "Hello Dali" ); + // Creates an ImfManager. ImfManager imfManager = ImfManager::Get(); + // For coverage. + imfEvent = ImfManager::ImfEventData( ImfManager::GETSURROUNDING, "", 0, 0 ); + controller->OnImfEvent( imfManager, imfEvent ); + // Send VOID event. imfEvent = ImfManager::ImfEventData( ImfManager::VOID, "", 0, 0 ); controller->OnImfEvent( imfManager, imfEvent ); diff --git a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp index 45214f8..35e1ae7 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp @@ -1163,18 +1163,15 @@ int UtcDaliToolkitTextlabelTextDirection(void) tet_infoline(" UtcDaliToolkitTextlabelTextDirection"); TextLabel label = TextLabel::New(); + DALI_TEST_EQUALS( label.GetProperty< int >( DevelTextLabel::Property::TEXT_DIRECTION ), static_cast< int >( Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ), TEST_LOCATION ); + label.SetProperty( TextLabel::Property::TEXT, "Hello world" ); label.SetProperty( TextLabel::Property::POINT_SIZE, 20 ); Stage::GetCurrent().Add( label ); - application.SendNotification(); - application.Render(); - DALI_TEST_EQUALS( label.GetProperty< int >( DevelTextLabel::Property::TEXT_DIRECTION ), static_cast< int >( Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT ), TEST_LOCATION ); label.SetProperty( TextLabel::Property::TEXT, "ﻡﺮﺤﺑﺍ ﺏﺎﻠﻋﺎﻠﻣ ﻡﺮﺤﺑﺍ" ); - application.SendNotification(); - application.Render(); DALI_TEST_EQUALS( label.GetProperty< int >( DevelTextLabel::Property::TEXT_DIRECTION ), static_cast< int >( Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT ), TEST_LOCATION ); END_TEST; diff --git a/dali-toolkit/internal/text/markup-processor-helper-functions.cpp b/dali-toolkit/internal/text/markup-processor-helper-functions.cpp index 8af6140..76d9a6a 100644 --- a/dali-toolkit/internal/text/markup-processor-helper-functions.cpp +++ b/dali-toolkit/internal/text/markup-processor-helper-functions.cpp @@ -109,6 +109,13 @@ void FloatToString( float value, std::string& floatStr ) floatStr = ss.str(); } +void UintToString( unsigned int value, std::string& uIntStr ) +{ + std::stringstream ss; + ss << value; + uIntStr = ss.str(); +} + void UintColorToVector4( unsigned int color, Vector4& retColor ) { retColor.a = static_cast( ( color & 0xFF000000 ) >> 24u ) / 255.f; diff --git a/dali-toolkit/internal/text/markup-processor-helper-functions.h b/dali-toolkit/internal/text/markup-processor-helper-functions.h index 45f586c..d1ecbc8 100644 --- a/dali-toolkit/internal/text/markup-processor-helper-functions.h +++ b/dali-toolkit/internal/text/markup-processor-helper-functions.h @@ -121,6 +121,14 @@ float StringToFloat( const char* const floatStr ); void FloatToString( float value, std::string& floatStr ); /** + * @brief Converts an unsigned int into a string. + * + * @param[in] value The unsigned int value. + * @param[out] uIntStr The string. + */ +void UintToString( unsigned int value, std::string& uIntStr ); + +/** * @brief Converts an ARGB color packed in 4 byte unsigned int into a Vector4 color used in Dali. * * @param[in] color An ARGB color packed in an unsigned int. diff --git a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.cpp b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.cpp index 0d72be9..957fd8e 100644 --- a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.cpp @@ -44,6 +44,7 @@ AtlasGlyphManager::AtlasGlyphManager() } void AtlasGlyphManager::Add( const Text::GlyphInfo& glyph, + const uint32_t outlineWidth, const PixelData& bitmap, Dali::Toolkit::AtlasManager::AtlasSlot& slot ) { @@ -62,6 +63,7 @@ void AtlasGlyphManager::Add( const Text::GlyphInfo& glyph, GlyphRecordEntry record; record.mIndex = glyph.index; + record.mOutlineWidth = outlineWidth; record.mImageId = slot.mImageId; record.mCount = 1; @@ -98,6 +100,7 @@ void AtlasGlyphManager::GenerateMeshData( uint32_t imageId, bool AtlasGlyphManager::IsCached( Text::FontId fontId, Text::GlyphIndex index, + uint32_t outlineWidth, Dali::Toolkit::AtlasManager::AtlasSlot& slot ) { for ( std::vector< FontGlyphRecord >::iterator fontGlyphRecordIt = mFontGlyphRecords.begin(); @@ -110,7 +113,7 @@ bool AtlasGlyphManager::IsCached( Text::FontId fontId, glyphRecordIt != fontGlyphRecordIt->mGlyphRecords.End(); ++glyphRecordIt ) { - if ( glyphRecordIt->mIndex == index ) + if ( glyphRecordIt->mIndex == index && glyphRecordIt->mOutlineWidth == outlineWidth ) { slot.mImageId = glyphRecordIt->mImageId; slot.mAtlasId = mAtlasManager.GetAtlas( slot.mImageId ); @@ -171,7 +174,7 @@ const Toolkit::AtlasGlyphManager::Metrics& AtlasGlyphManager::GetMetrics() return mMetrics; } -void AtlasGlyphManager::AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, int32_t delta ) +void AtlasGlyphManager::AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, uint32_t outlineWidth, int32_t delta ) { if( 0 != delta ) { @@ -187,7 +190,7 @@ void AtlasGlyphManager::AdjustReferenceCount( Text::FontId fontId, Text::GlyphIn glyphRecordIt != fontGlyphRecordIt->mGlyphRecords.End(); ++glyphRecordIt ) { - if ( glyphRecordIt->mIndex == index ) + if ( glyphRecordIt->mIndex == index && glyphRecordIt->mOutlineWidth == outlineWidth ) { glyphRecordIt->mCount += delta; DALI_ASSERT_DEBUG( glyphRecordIt->mCount >= 0 && "Glyph ref-count should not be negative" ); diff --git a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.h b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.h index a2232f9..0d32834 100644 --- a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.h +++ b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager-impl.h @@ -51,6 +51,7 @@ public: struct GlyphRecordEntry { Text::GlyphIndex mIndex; + uint32_t mOutlineWidth; uint32_t mImageId; int32_t mCount; }; @@ -70,6 +71,7 @@ public: * @copydoc Toolkit::AtlasGlyphManager::Add */ void Add( const Text::GlyphInfo& glyph, + const uint32_t outlineWidth, const PixelData& bitmap, Dali::Toolkit::AtlasManager::AtlasSlot& slot ); @@ -85,6 +87,7 @@ public: */ bool IsCached( Text::FontId fontId, Text::GlyphIndex index, + uint32_t outlineWidth, Dali::Toolkit::AtlasManager::AtlasSlot& slot ); /** @@ -105,7 +108,7 @@ public: /** * @copydoc toolkit::AtlasGlyphManager::AdjustReferenceCount */ - void AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, int32_t delta ); + void AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, uint32_t outlineWidth, int32_t delta ); /** * @copydoc Toolkit::AtlasGlyphManager::GetTextures diff --git a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.cpp b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.cpp index 743bc6c..bd6850d 100644 --- a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.cpp @@ -69,10 +69,11 @@ AtlasGlyphManager::AtlasGlyphManager(Internal::AtlasGlyphManager *impl) } void AtlasGlyphManager::Add( const Text::GlyphInfo& glyph, + const uint32_t outlineWidth, const PixelData& bitmap, AtlasManager::AtlasSlot& slot ) { - GetImplementation(*this).Add( glyph, bitmap, slot ); + GetImplementation(*this).Add( glyph, outlineWidth, bitmap, slot ); } void AtlasGlyphManager::GenerateMeshData( uint32_t imageId, @@ -86,9 +87,10 @@ void AtlasGlyphManager::GenerateMeshData( uint32_t imageId, bool AtlasGlyphManager::IsCached( Text::FontId fontId, Text::GlyphIndex index, + uint32_t outlineWidth, AtlasManager::AtlasSlot& slot ) { - return GetImplementation(*this).IsCached( fontId, index, slot ); + return GetImplementation(*this).IsCached( fontId, index, outlineWidth, slot ); } void AtlasGlyphManager::SetNewAtlasSize( uint32_t width, uint32_t height, uint32_t blockWidth, uint32_t blockHeight ) @@ -116,9 +118,9 @@ const Toolkit::AtlasGlyphManager::Metrics& AtlasGlyphManager::GetMetrics() return GetImplementation(*this).GetMetrics(); } -void AtlasGlyphManager::AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, int32_t delta ) +void AtlasGlyphManager::AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, uint32_t outlineWidth, int32_t delta ) { - GetImplementation(*this).AdjustReferenceCount( fontId, index, delta ); + GetImplementation(*this).AdjustReferenceCount( fontId, index, outlineWidth, delta ); } } // namespace Toolkit diff --git a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h index 1bc058b..c41415e 100644 --- a/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h +++ b/dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h @@ -80,10 +80,12 @@ public: * @brief Ask Atlas Manager to add a glyph * * @param[in] glyph glyph to add to an atlas + * @param[in] outlineWidth the outline width of the glyph * @param[in] bitmap bitmap to use for glyph addition * @param[out] slot information returned by atlas manager for addition */ void Add( const Text::GlyphInfo& glyph, + const uint32_t outlineWidth, const PixelData& bitmap, AtlasManager::AtlasSlot& slot ); @@ -103,12 +105,14 @@ public: * * @param[in] fontId The font that this glyph comes from * @param[in] index The GlyphIndex of this glyph + * @param[in] outlineWidth The outline width of this glyph * @param[out] slot container holding information about the glyph( mImage = 0 indicates not being cached ) * * @return Whether glyph is cached or not ? */ bool IsCached( Text::FontId fontId, Text::GlyphIndex index, + uint32_t outlineWidth, AtlasManager::AtlasSlot& slot ); /** @@ -160,9 +164,10 @@ public: * * @param[in] fontId The font this image came from * @param[in] index The index of the glyph + * @param[in] outlineWidth The outline width of the glyph * @param[in] delta The adjustment to make to the reference count */ - void AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, int32_t delta ); + void AdjustReferenceCount( Text::FontId fontId, Text::GlyphIndex index, uint32_t outlineWidth, int32_t delta ); private: diff --git a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp index 62865d6..3ef1965 100644 --- a/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp +++ b/dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp @@ -94,7 +94,7 @@ const float HALF( 0.5f ); const float ONE( 1.0f ); const uint32_t DEFAULT_ATLAS_WIDTH = 512u; const uint32_t DEFAULT_ATLAS_HEIGHT = 512u; -const int NO_OUTLINE( 0 ); +const uint32_t NO_OUTLINE = 0; } struct AtlasRenderer::Impl @@ -172,12 +172,14 @@ struct AtlasRenderer::Impl TextCacheEntry() : mFontId( 0 ), mIndex( 0 ), + mOutlineWidth( 0 ), mImageId( 0 ) { } FontId mFontId; Text::GlyphIndex mIndex; + uint32_t mOutlineWidth; uint32_t mImageId; }; @@ -211,6 +213,207 @@ struct AtlasRenderer::Impl return false; } + void CacheGlyph( const GlyphInfo& glyph, FontId lastFontId, uint32_t outline, AtlasManager::AtlasSlot& slot ) + { + const bool glyphNotCached = !mGlyphManager.IsCached( glyph.fontId, glyph.index, outline, slot ); // Check FontGlyphRecord vector for entry with glyph index and fontId + + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached)?"not cached":"cached" ); + + if( glyphNotCached ) + { + MaxBlockSize& blockSize = mBlockSizes[0u]; + + if ( lastFontId != glyph.fontId ) + { + uint32_t index = 0u; + // 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. + // CalculateBlocksSize() above ensures a block size entry exists. + for( std::vector::const_iterator it = mBlockSizes.begin(), + endIt = mBlockSizes.end(); + it != endIt; + ++it, ++index ) + { + const MaxBlockSize& blockSizeEntry = *it; + if( blockSizeEntry.mFontId == glyph.fontId ) + { + blockSize = mBlockSizes[index]; + } + } + } + + // Create a new image for the glyph + PixelData bitmap; + + // Whether the glyph is an outline. + const bool isOutline = 0u != outline; + + // Whether the current glyph is a color one. + const bool isColorGlyph = mFontClient.IsColorGlyph( glyph.fontId, glyph.index ); + + if( !isOutline || ( isOutline && !isColorGlyph) ) + { + // Retrieve the emoji's bitmap. + TextAbstraction::FontClient::GlyphBufferData glyphBufferData; + glyphBufferData.width = isColorGlyph ? glyph.width : 0; // Desired width and height. + glyphBufferData.height = isColorGlyph ? glyph.height : 0; + + mFontClient.CreateBitmap( glyph.fontId, + glyph.index, + glyphBufferData, + outline ); + + // Create the pixel data. + bitmap = PixelData::New( glyphBufferData.buffer, + glyphBufferData.width * glyphBufferData.height * GetBytesPerPixel( glyphBufferData.format ), + glyphBufferData.width, + glyphBufferData.height, + glyphBufferData.format, + PixelData::DELETE_ARRAY ); + + if( bitmap ) + { + // Ensure that the next image will fit into the current block size + if( bitmap.GetWidth() > blockSize.mNeededBlockWidth ) + { + blockSize.mNeededBlockWidth = bitmap.GetWidth(); + } + + if( bitmap.GetHeight() > blockSize.mNeededBlockHeight ) + { + blockSize.mNeededBlockHeight = bitmap.GetHeight(); + } + + // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas + + // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice. + mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, + DEFAULT_ATLAS_HEIGHT, + blockSize.mNeededBlockWidth, + blockSize.mNeededBlockHeight ); + + // Locate a new slot for our glyph + mGlyphManager.Add( glyph, outline, bitmap, slot ); // slot will be 0 is glyph not added + } + } + } + else + { + // We have 2+ copies of the same glyph + mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, outline, 1 ); //increment + } + } + + void GenerateMesh( const GlyphInfo& glyph, + const Vector2& position, + const Vector4& color, + uint32_t outline, + AtlasManager::AtlasSlot& slot, + bool underlineGlyph, + float currentUnderlinePosition, + float currentUnderlineThickness, + std::vector& meshContainer, + Vector& newTextCache, + Vector& extents ) + { + // Generate mesh data for this quad, plugging in our supplied position + AtlasManager::Mesh2D newMesh; + mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh ); + + TextCacheEntry textCacheEntry; + textCacheEntry.mFontId = glyph.fontId; + textCacheEntry.mImageId = slot.mImageId; + textCacheEntry.mIndex = glyph.index; + textCacheEntry.mOutlineWidth = outline; + + newTextCache.PushBack( textCacheEntry ); + + AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin(); + + for( unsigned int index = 0u, size = newMesh.mVertices.Count(); + index < size; + ++index ) + { + AtlasManager::Vertex2D& vertex = *( verticesBuffer + index ); + + // Set the color of the vertex. + vertex.mColor = color; + } + + // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas) + StitchTextMesh( meshContainer, + newMesh, + extents, + position.y + glyph.yBearing, + underlineGlyph, + currentUnderlinePosition, + currentUnderlineThickness, + slot ); + } + + void CreateActors( const std::vector& meshContainer, + const Size& textSize, + const Vector4& color, + const Vector4& shadowColor, + const Vector2& shadowOffset, + Actor textControl, + Property::Index animatablePropertyIndex, + bool drawShadow ) + { + if( !mActor ) + { + // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues. + mActor = Actor::New(); + mActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + mActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mActor.SetSize( textSize ); + mActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + } + + for( std::vector< MeshRecord >::const_iterator it = meshContainer.begin(), + endIt = meshContainer.end(); + it != endIt; ++it ) + { + const MeshRecord& meshRecord = *it; + + Actor actor = CreateMeshActor( textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_NORMAL ); + + // Whether the actor has renderers. + const bool hasRenderer = actor.GetRendererCount() > 0u; + + // Create an effect if necessary + if( hasRenderer && + drawShadow ) + { + // Change the color of the vertices. + for( Vector::Iterator vIt = meshRecord.mMesh.mVertices.Begin(), + vEndIt = meshRecord.mMesh.mVertices.End(); + vIt != vEndIt; + ++vIt ) + { + AtlasManager::Vertex2D& vertex = *vIt; + + vertex.mColor = shadowColor; + } + + Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, color, meshRecord, textSize, STYLE_DROP_SHADOW ); +#if defined(DEBUG_ENABLED) + shadowActor.SetName( "Text Shadow renderable actor" ); +#endif + // Offset shadow in x and y + shadowActor.RegisterProperty("uOffset", shadowOffset ); + Dali::Renderer renderer( shadowActor.GetRendererAt( 0 ) ); + int depthIndex = renderer.GetProperty(Dali::Renderer::Property::DEPTH_INDEX); + renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1 ); + mActor.Add( shadowActor ); + } + + if( hasRenderer ) + { + mActor.Add( actor ); + } + } + } + void AddGlyphs( Text::ViewInterface& view, Actor textControl, Property::Index animatablePropertyIndex, @@ -223,18 +426,28 @@ struct AtlasRenderer::Impl float minLineOffset ) { AtlasManager::AtlasSlot slot; + slot.mImageId = 0u; + slot.mAtlasId = 0u; + + AtlasManager::AtlasSlot slotOutline; + slotOutline.mImageId = 0u; + slotOutline.mAtlasId = 0u; + std::vector< MeshRecord > meshContainer; + std::vector< MeshRecord > meshContainerOutline; Vector< Extent > extents; - TextCacheEntry textCacheEntry; mDepth = depth; const Vector2& textSize( view.GetLayoutSize() ); const Vector2 halfTextSize( textSize * 0.5f ); const Vector2& shadowOffset( view.GetShadowOffset() ); const Vector4& shadowColor( view.GetShadowColor() ); - const bool underlineEnabled( view.IsUnderlineEnabled() ); + const bool underlineEnabled = view.IsUnderlineEnabled(); const Vector4& underlineColor( view.GetUnderlineColor() ); - const float underlineHeight( view.GetUnderlineHeight() ); + const float underlineHeight = view.GetUnderlineHeight(); + const unsigned int outlineWidth = view.GetOutlineWidth(); + const Vector4& outlineColor( view.GetOutlineColor() ); + const bool isOutline = 0u != outlineWidth; const bool useDefaultColor = ( NULL == colorsBuffer ); @@ -250,7 +463,6 @@ struct AtlasRenderer::Impl float currentUnderlinePosition = ZERO; float currentUnderlineThickness = underlineHeight; - uint32_t currentBlockSize = 0; FontId lastFontId = 0; FontId lastUnderlinedFontId = 0; Style style = STYLE_NORMAL; @@ -271,14 +483,14 @@ struct AtlasRenderer::Impl for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i ) { const GlyphInfo& glyph = *( glyphsBuffer + i ); - const bool underlineGlyph = underlineEnabled || IsGlyphUnderlined( i, underlineRuns ); - thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || underlineGlyph; + const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined( i, underlineRuns ); + thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined; // No operation for white space if( glyph.width && glyph.height ) { // Are we still using the same fontId as previous - if( underlineGlyph && ( glyph.fontId != lastUnderlinedFontId ) ) + if( isGlyphUnderlined && ( glyph.fontId != lastUnderlinedFontId ) ) { // We need to fetch fresh font underline metrics FontMetrics fontMetrics; @@ -316,85 +528,13 @@ struct AtlasRenderer::Impl lastUnderlinedFontId = glyph.fontId; } // underline - bool glyphNotCached = !mGlyphManager.IsCached( glyph.fontId, glyph.index, slot ); // Check FontGlyphRecord vector for entry with glyph index and fontId - - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "AddGlyphs fontID[%u] glyphIndex[%u] [%s]\n", glyph.fontId, glyph.index, (glyphNotCached)?"not cached":"cached" ); + // Retrieves and caches the glyph's bitmap. + CacheGlyph( glyph, lastFontId, NO_OUTLINE, slot ); - if( glyphNotCached ) + // Retrieves and caches the outline glyph's bitmap. + if( isOutline ) { - MaxBlockSize& blockSize = mBlockSizes[currentBlockSize]; - - if ( lastFontId != glyph.fontId ) - { - uint32_t index = 0u; - // 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. - // CalculateBlocksSize() above ensures a block size entry exists. - for( std::vector::const_iterator it = mBlockSizes.begin(), - endIt = mBlockSizes.end(); - it != endIt; - ++it, ++index ) - { - const MaxBlockSize& blockSizeEntry = *it; - if( blockSizeEntry.mFontId == glyph.fontId ) - { - blockSize = mBlockSizes[index]; - } - } - } - - // Create a new image for the glyph - PixelData bitmap; - - // Whether the current glyph is a color one. - const bool isColorGlyph = mFontClient.IsColorGlyph( glyph.fontId, glyph.index ); - - // Retrieve the emoji's bitmap. - TextAbstraction::FontClient::GlyphBufferData glyphBufferData; - glyphBufferData.width = isColorGlyph ? glyph.width : 0; // Desired width and height. - glyphBufferData.height = isColorGlyph ? glyph.height : 0; - - mFontClient.CreateBitmap( glyph.fontId, - glyph.index, - glyphBufferData, - NO_OUTLINE ); - - // Create the pixel data. - bitmap = PixelData::New( glyphBufferData.buffer, - glyph.width * glyph.height * GetBytesPerPixel( glyphBufferData.format ), - glyph.width, - glyph.height, - glyphBufferData.format, - PixelData::DELETE_ARRAY ); - - if( bitmap ) - { - // Ensure that the next image will fit into the current block size - if( bitmap.GetWidth() > blockSize.mNeededBlockWidth ) - { - blockSize.mNeededBlockWidth = bitmap.GetWidth(); - } - - if( bitmap.GetHeight() > blockSize.mNeededBlockHeight ) - { - blockSize.mNeededBlockHeight = bitmap.GetHeight(); - } - - // If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas - - // Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice. - mGlyphManager.SetNewAtlasSize( DEFAULT_ATLAS_WIDTH, - DEFAULT_ATLAS_HEIGHT, - blockSize.mNeededBlockWidth, - blockSize.mNeededBlockHeight ); - - // Locate a new slot for our glyph - mGlyphManager.Add( glyph, bitmap, slot ); // slot will be 0 is glyph not added - } - } - else - { - // We have 2+ copies of the same glyph - mGlyphManager.AdjustReferenceCount( glyph.fontId, glyph.index, 1/*increment*/ ); + CacheGlyph( glyph, lastFontId, outlineWidth, slotOutline ); } // Move the origin (0,0) of the mesh to the center of the actor @@ -402,42 +542,47 @@ struct AtlasRenderer::Impl if ( 0u != slot.mImageId ) // invalid slot id, glyph has failed to be added to atlas { - // Generate mesh data for this quad, plugging in our supplied position - AtlasManager::Mesh2D newMesh; - mGlyphManager.GenerateMeshData( slot.mImageId, position, newMesh ); - textCacheEntry.mFontId = glyph.fontId; - textCacheEntry.mImageId = slot.mImageId; - textCacheEntry.mIndex = glyph.index; - newTextCache.PushBack( textCacheEntry ); - - AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin(); + Vector2 positionPlusOutlineOffset = position; + if( isOutline ) + { + // Add an offset to the text. + const float outlineWidthOffset = static_cast( outlineWidth ); + positionPlusOutlineOffset += Vector2( outlineWidthOffset, outlineWidthOffset ); + } // Get the color of the character. const ColorIndex colorIndex = useDefaultColor ? 0u : *( colorIndicesBuffer + i ); const Vector4& color = ( useDefaultColor || ( 0u == colorIndex ) ) ? defaultColor : *( colorsBuffer + colorIndex - 1u ); - for( unsigned int index = 0u, size = newMesh.mVertices.Count(); - index < size; - ++index ) - { - AtlasManager::Vertex2D& vertex = *( verticesBuffer + index ); - - // Set the color of the vertex. - vertex.mColor = color; - } - - // Find an existing mesh data object to attach to ( or create a new one, if we can't find one using the same atlas) - StitchTextMesh( meshContainer, - newMesh, - extents, - position.y + glyph.yBearing, - underlineGlyph, - currentUnderlinePosition, - currentUnderlineThickness, - slot ); + GenerateMesh( glyph, + positionPlusOutlineOffset, + color, + NO_OUTLINE, + slot, + isGlyphUnderlined, + currentUnderlinePosition, + currentUnderlineThickness, + meshContainer, + newTextCache, + extents); lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId. } + + if( isOutline&& ( 0u != slotOutline.mImageId ) ) // invalid slot id, glyph has failed to be added to atlas + { + GenerateMesh( glyph, + position, + outlineColor, + outlineWidth, + slotOutline, + false, + currentUnderlinePosition, + currentUnderlineThickness, + meshContainerOutline, + newTextCache, + extents); + } } } // glyphs @@ -452,62 +597,36 @@ struct AtlasRenderer::Impl } // For each MeshData object, create a mesh actor and add to the renderable actor - if( !meshContainer.empty() ) + bool isShadowDrawn = false; + if( !meshContainerOutline.empty() ) { - if( !mActor ) - { - // Create a container actor to act as a common parent for text and shadow, to avoid color inheritence issues. - mActor = Actor::New(); - mActor.SetParentOrigin( ParentOrigin::TOP_LEFT ); - mActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - mActor.SetSize( textSize ); - mActor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); - } - - for( std::vector< MeshRecord >::iterator it = meshContainer.begin(), - endIt = meshContainer.end(); - it != endIt; ++it ) - { - MeshRecord& meshRecord = *it; - - Actor actor = CreateMeshActor( textControl, animatablePropertyIndex, defaultColor, meshRecord, textSize, STYLE_NORMAL ); - - // Whether the actor has renderers. - const bool hasRenderer = actor.GetRendererCount() > 0u; - - // Create an effect if necessary - if( hasRenderer && - ( style == STYLE_DROP_SHADOW ) ) - { - // Change the color of the vertices. - for( Vector::Iterator vIt = meshRecord.mMesh.mVertices.Begin(), - vEndIt = meshRecord.mMesh.mVertices.End(); - vIt != vEndIt; - ++vIt ) - { - AtlasManager::Vertex2D& vertex = *vIt; - - vertex.mColor = shadowColor; - } - - Actor shadowActor = CreateMeshActor(textControl, animatablePropertyIndex, defaultColor, meshRecord, textSize, STYLE_DROP_SHADOW ); -#if defined(DEBUG_ENABLED) - shadowActor.SetName( "Text Shadow renderable actor" ); -#endif - // Offset shadow in x and y - shadowActor.RegisterProperty("uOffset", shadowOffset ); - Dali::Renderer renderer( shadowActor.GetRendererAt( 0 ) ); - int depthIndex = renderer.GetProperty(Dali::Renderer::Property::DEPTH_INDEX); - renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, depthIndex - 1 ); - mActor.Add( shadowActor ); - } + const bool drawShadow = STYLE_DROP_SHADOW == style; + CreateActors( meshContainerOutline, + textSize, + outlineColor, + shadowColor, + shadowOffset, + textControl, + animatablePropertyIndex, + drawShadow ); + + isShadowDrawn = drawShadow; + } - if( hasRenderer ) - { - mActor.Add( actor ); - } - } + // For each MeshData object, create a mesh actor and add to the renderable actor + if( !meshContainer.empty() ) + { + const bool drawShadow = !isShadowDrawn && ( STYLE_DROP_SHADOW == style ); + CreateActors( meshContainer, + textSize, + defaultColor, + shadowColor, + shadowOffset, + textControl, + animatablePropertyIndex, + drawShadow ); } + #if defined(DEBUG_ENABLED) Toolkit::AtlasGlyphManager::Metrics metrics = mGlyphManager.GetMetrics(); DALI_LOG_INFO( gLogFilter, Debug::General, "TextAtlasRenderer::GlyphManager::GlyphCount: %i, AtlasCount: %i, TextureMemoryUse: %iK\n", @@ -536,7 +655,7 @@ struct AtlasRenderer::Impl { for( Vector< TextCacheEntry >::Iterator oldTextIter = mTextCache.Begin(); oldTextIter != mTextCache.End(); ++oldTextIter ) { - mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, -1/*decrement*/ ); + mGlyphManager.AdjustReferenceCount( oldTextIter->mFontId, oldTextIter->mIndex, oldTextIter->mOutlineWidth, -1/*decrement*/ ); } mTextCache.Resize( 0 ); } @@ -611,6 +730,7 @@ struct AtlasRenderer::Impl actor.SetSize( actorSize ); actor.RegisterProperty("uOffset", Vector2::ZERO ); actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR ); + return actor; } diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 874503d..5101640 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -888,7 +888,7 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) TextAbstraction::FontDescription defaultFontDescription; TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; - if( IsShowingPlaceholderText() && ( NULL != mEventData->mPlaceholderFont ) ) + if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) ) { // If the placeholder font is set specifically, only placeholder font is changed. defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription; @@ -1144,7 +1144,7 @@ float Controller::Impl::GetDefaultFontLineHeight() void Controller::Impl::OnCursorKeyEvent( const Event& event ) { - if( NULL == mEventData ) + if( NULL == mEventData || !IsShowingRealText() ) { // Nothing to do if there is no text input. return; diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp index d054cbc..7ab4e6c 100755 --- a/dali-toolkit/internal/text/text-controller.cpp +++ b/dali-toolkit/internal/text/text-controller.cpp @@ -1182,14 +1182,14 @@ const Vector4& Controller::GetOutlineColor() const return mImpl->mModel->mVisualModel->GetOutlineColor(); } -void Controller::SetOutlineWidth( float width ) +void Controller::SetOutlineWidth( unsigned int width ) { mImpl->mModel->mVisualModel->SetOutlineWidth( width ); mImpl->RequestRelayout(); } -float Controller::GetOutlineWidth() const +unsigned int Controller::GetOutlineWidth() const { return mImpl->mModel->mVisualModel->GetOutlineWidth(); } @@ -2105,8 +2105,15 @@ void Controller::GetPlaceholderProperty( Property::Map& map ) Toolkit::DevelText::TextDirection::Type Controller::GetTextDirection() { - const LineRun* const firstline = mImpl->mModel->mVisualModel->mLines.Begin(); - if ( firstline && firstline->direction ) + if( ( 0u == mImpl->mModel->mLogicalModel->mText.Count() ) ) + { + return Toolkit::DevelText::TextDirection::LEFT_TO_RIGHT; + } + + const Character character = mImpl->mModel->mLogicalModel->mText[0]; + Script script = TextAbstraction::GetCharacterScript( character ); + + if( TextAbstraction::IsRightToLeftScript( script ) ) { return Toolkit::DevelText::TextDirection::RIGHT_TO_LEFT; } @@ -2737,7 +2744,17 @@ ImfManager::ImfCallbackData Controller::OnImfEvent( ImfManager& imfManager, cons if( retrieveText ) { - mImpl->GetText( numberOfWhiteSpaces, text ); + if( !mImpl->IsShowingPlaceholderText() ) + { + // Retrieves the normal text string. + mImpl->GetText( numberOfWhiteSpaces, text ); + } + else + { + // When the current text is Placeholder Text, the surrounding text should be empty string. + // It means DALi should send empty string ("") to IME. + text = ""; + } } ImfManager::ImfCallbackData callbackData( ( retrieveText || retrieveCursor ), cursorPosition, text, false ); @@ -3697,7 +3714,7 @@ bool Controller::DeleteEvent( int keyCode ) 1, UPDATE_INPUT_STYLE ); } - else if( ( mImpl->mEventData->mPrimaryCursorPosition >= 0 ) && ( keyCode == Dali::DevelKey::DALI_KEY_DELETE ) ) + else if( keyCode == Dali::DevelKey::DALI_KEY_DELETE ) { // Remove the character after the current cursor position removed = RemoveText( 0, diff --git a/dali-toolkit/internal/text/text-controller.h b/dali-toolkit/internal/text/text-controller.h index 07d91be..88831db 100755 --- a/dali-toolkit/internal/text/text-controller.h +++ b/dali-toolkit/internal/text/text-controller.h @@ -834,14 +834,14 @@ public: // Default style & Input style * * @param[in] width The width in pixels of the outline, 0 indicates no outline */ - void SetOutlineWidth( float width ); + void SetOutlineWidth( unsigned int width ); /** * @brief Retrieves the width of an outline * * @return The width of the outline. */ - float GetOutlineWidth() const; + unsigned int GetOutlineWidth() const; /** * @brief Sets the emboss's properties string. diff --git a/dali-toolkit/internal/text/text-effects-style.cpp b/dali-toolkit/internal/text/text-effects-style.cpp index 7c08ebe..e56eac3 100755 --- a/dali-toolkit/internal/text/text-effects-style.cpp +++ b/dali-toolkit/internal/text/text-effects-style.cpp @@ -125,26 +125,45 @@ bool ParseUnderlineProperties( const Property::Map& underlinePropertiesMap, if( ENABLE_KEY == valueGet.first.stringKey ) { /// Enable key. - const std::string enableStr = valueGet.second.Get(); - enabled = Text::TokenComparison( TRUE_TOKEN, enableStr.c_str(), enableStr.size() ); + if( valueGet.second.GetType() == Dali::Property::STRING ) + { + const std::string enableStr = valueGet.second.Get(); + enabled = Text::TokenComparison( TRUE_TOKEN, enableStr.c_str(), enableStr.size() ); + } + else + { + enabled = valueGet.second.Get(); + } } else if( COLOR_KEY == valueGet.first.stringKey ) { /// Color key. colorDefined = true; - const std::string colorStr = valueGet.second.Get(); - - Text::ColorStringToVector4( colorStr.c_str(), colorStr.size(), color ); + if( valueGet.second.GetType() == Dali::Property::STRING ) + { + const std::string colorStr = valueGet.second.Get(); + Text::ColorStringToVector4( colorStr.c_str(), colorStr.size(), color ); + } + else + { + color = valueGet.second.Get(); + } } else if( HEIGHT_KEY == valueGet.first.stringKey ) { /// Height key. heightDefined = true; - const std::string heightStr = valueGet.second.Get(); - - height = StringToFloat( heightStr.c_str() ); + if( valueGet.second.GetType() == Dali::Property::STRING ) + { + const std::string heightStr = valueGet.second.Get(); + height = StringToFloat( heightStr.c_str() ); + } + else + { + height = valueGet.second.Get(); + } } } @@ -155,7 +174,7 @@ bool ParseOutlineProperties( const Property::Map& underlinePropertiesMap, bool& colorDefined, Vector4& color, bool& widthDefined, - float& width ) + unsigned int& width ) { const unsigned int numberOfItems = underlinePropertiesMap.Count(); @@ -174,7 +193,7 @@ bool ParseOutlineProperties( const Property::Map& underlinePropertiesMap, { /// Width key. widthDefined = true; - width = valueGet.second.Get(); + width = static_cast( valueGet.second.Get() ); } } @@ -218,7 +237,7 @@ bool SetUnderlineProperties( ControllerPtr controller, const Property::Value& va heightDefined, height ); - controller->UnderlineSetByString( !empty); + controller->UnderlineSetByString( !empty ); } } else @@ -546,7 +565,7 @@ bool SetOutlineProperties( ControllerPtr controller, const Property::Value& valu bool colorDefined = false; Vector4 color; bool widthDefined = false; - float width = 0.f; + unsigned int width = 0u; bool empty = true; @@ -581,7 +600,7 @@ bool SetOutlineProperties( ControllerPtr controller, const Property::Value& valu update = true; } - if( widthDefined && ( fabsf( controller->GetOutlineWidth() - width ) > Math::MACHINE_EPSILON_1000 ) ) + if( widthDefined && ( controller->GetOutlineWidth() != width ) ) { controller->SetOutlineWidth( width ); update = true; @@ -590,9 +609,9 @@ bool SetOutlineProperties( ControllerPtr controller, const Property::Value& valu else { // Disable outline - if( fabsf( controller->GetOutlineWidth() ) > Math::MACHINE_EPSILON_1000 ) + if( 0u != controller->GetOutlineWidth() ) { - controller->SetOutlineWidth( 0.0f ); + controller->SetOutlineWidth( 0u ); update = true; } } @@ -627,7 +646,7 @@ void GetOutlineProperties( ControllerPtr controller, Property::Value& value, Eff else { const Vector4& color = controller->GetOutlineColor(); - const float width = controller->GetOutlineWidth(); + const unsigned int width = controller->GetOutlineWidth(); Property::Map map; @@ -636,7 +655,7 @@ void GetOutlineProperties( ControllerPtr controller, Property::Value& value, Eff map.Insert( COLOR_KEY, colorStr ); std::string widthStr; - FloatToString( width, widthStr ); + UintToString( width, widthStr ); map.Insert( WIDTH_KEY, widthStr ); value = map; diff --git a/dali-toolkit/internal/text/text-effects-style.h b/dali-toolkit/internal/text/text-effects-style.h index 521f52c..e2b0f0c 100755 --- a/dali-toolkit/internal/text/text-effects-style.h +++ b/dali-toolkit/internal/text/text-effects-style.h @@ -84,7 +84,7 @@ bool ParseOutlineProperties( const Property::Map& outlineProperties, bool& colorDefined, Vector4& color, bool& widthDefined, - float& width ); + unsigned int& width ); /** * @brief Sets the underline properties. diff --git a/dali-toolkit/internal/text/text-view-interface.h b/dali-toolkit/internal/text/text-view-interface.h index efa88ad..96f9005 100644 --- a/dali-toolkit/internal/text/text-view-interface.h +++ b/dali-toolkit/internal/text/text-view-interface.h @@ -172,6 +172,21 @@ public: virtual void GetUnderlineRuns( GlyphRun* underlineRuns, UnderlineRunIndex index, Length numberOfRuns ) const = 0; + + /** + * @brief Retrieve the outline color. + * + * @return The outline color. + */ + virtual const Vector4& GetOutlineColor() const = 0; + + /** + * @brief Retrieves the width of an outline + * + * @return The width of the outline. + */ + virtual unsigned int GetOutlineWidth() const = 0; + }; } // namespace Text diff --git a/dali-toolkit/internal/text/text-view.cpp b/dali-toolkit/internal/text/text-view.cpp index 9e8d0be..62c0008 100644 --- a/dali-toolkit/internal/text/text-view.cpp +++ b/dali-toolkit/internal/text/text-view.cpp @@ -402,6 +402,24 @@ void View::GetUnderlineRuns( GlyphRun* underlineRuns, } } +const Vector4& View::GetOutlineColor() const +{ + if( mImpl->mVisualModel ) + { + return mImpl->mVisualModel->GetOutlineColor(); + } + return Vector4::ZERO; +} + +unsigned int View::GetOutlineWidth() const +{ + if( mImpl->mVisualModel ) + { + return mImpl->mVisualModel->GetOutlineWidth(); + } + return 0u; +} + } // namespace Text } // namespace Toolkit diff --git a/dali-toolkit/internal/text/text-view.h b/dali-toolkit/internal/text/text-view.h index d494d2b..51426de 100644 --- a/dali-toolkit/internal/text/text-view.h +++ b/dali-toolkit/internal/text/text-view.h @@ -131,6 +131,16 @@ public: UnderlineRunIndex index, Length numberOfRuns ) const; + /** + * @copydoc Dali::Toolkit::Text::ViewInterface::GetOutlineColor() + */ + virtual const Vector4& GetOutlineColor() const; + + /** + * @copydoc Dali::Toolkit::Text::ViewInterface::GetOutlineWidth() + */ + virtual unsigned int GetOutlineWidth() const; + private: // Undefined diff --git a/dali-toolkit/internal/text/visual-model-impl.cpp b/dali-toolkit/internal/text/visual-model-impl.cpp index 9caa523..5373a6a 100755 --- a/dali-toolkit/internal/text/visual-model-impl.cpp +++ b/dali-toolkit/internal/text/visual-model-impl.cpp @@ -370,7 +370,7 @@ void VisualModel::SetUnderlineHeight( float height ) mUnderlineHeight = height; } -void VisualModel::SetOutlineWidth( float width ) +void VisualModel::SetOutlineWidth( unsigned int width ) { mOutlineWidth = width; } @@ -415,7 +415,7 @@ float VisualModel::GetUnderlineHeight() const return mUnderlineHeight; } -float VisualModel::GetOutlineWidth() const +unsigned int VisualModel::GetOutlineWidth() const { return mOutlineWidth; } @@ -449,7 +449,7 @@ VisualModel::VisualModel() mControlSize(), mShadowOffset(), mUnderlineHeight( 0.0f ), - mOutlineWidth( 0.0f ), + mOutlineWidth( 0u ), mShadowBlurRadius( 0.0f ), mNaturalSize(), mLayoutSize(), diff --git a/dali-toolkit/internal/text/visual-model-impl.h b/dali-toolkit/internal/text/visual-model-impl.h index 28d2269..24be0c7 100755 --- a/dali-toolkit/internal/text/visual-model-impl.h +++ b/dali-toolkit/internal/text/visual-model-impl.h @@ -320,14 +320,14 @@ public: * * @param[in] width The width in pixels of the outline, 0 indicates no outline */ - void SetOutlineWidth( float width ); + void SetOutlineWidth( unsigned int width ); /** * @brief Retrieves the width of an outline * * @return The width of the outline. */ - float GetOutlineWidth() const; + unsigned int GetOutlineWidth() const; protected: @@ -369,7 +369,7 @@ public: Size mControlSize; ///< The size of the UI control. Vector2 mShadowOffset; ///< Offset for drop shadow, 0 indicates no shadow float mUnderlineHeight; ///< Fixed height for underline to override font metrics. - float mOutlineWidth; ///< Width of outline. + unsigned int mOutlineWidth; ///< Width of outline. float mShadowBlurRadius; ///< Blur radius of shadow, 0 indicates no blur. private: diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.cpp b/dali-toolkit/internal/visuals/texture-manager-impl.cpp index b2e0d03..c867b91 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.cpp +++ b/dali-toolkit/internal/visuals/texture-manager-impl.cpp @@ -47,7 +47,9 @@ size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) { using Dali::EnvironmentVariable::GetEnvironmentVariable; auto numberString = GetEnvironmentVariable(environmentVariable); - auto numberOfThreads = numberString ? std::strtol(numberString, nullptr, 10) : 0; + auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; + constexpr auto MAX_NUMBER_OF_THREADS = 100u; + DALI_ASSERT_ALWAYS( numberOfThreads < MAX_NUMBER_OF_THREADS ); return (numberOfThreads > 0) ? numberOfThreads : defaultValue; }