This is implemented to avoid text quality degradation when scaling up with Actor::SetScale().
Renders by scaling up the point size and texture size to the given scale.
* This property is only available in ASYNC_AUTO, ASYNC_MANUAL.
* RenderScale is only valid when it is 1.0f or greater.
Change-Id: I9a11e6aa413c4aada6eb08e0f58b1aafdff7b83c
Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
application.Render();
END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliToolkitTextLabelAsyncRenderScale(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline(" UtcDaliToolkitTextLabelAsyncRenderScale");
+
+ // Include glyphs with advance of 0 for testing.
+ std::string text = "Hello World Render SCA̧LE Test!";
+
+ TextLabel label = TextLabel::New(text);
+ DALI_TEST_CHECK(label);
+
+ // Avoid a crash when core load gl resources.
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE);
+
+ // Set the dpi of AsyncTextLoader and FontClient to be identical.
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+ application.GetScene().Add(label);
+
+ // Connect to the async text rendered signal.
+ ConnectionTracker* testTracker = new ConnectionTracker();
+ DevelTextLabel::AsyncTextRenderedSignal(label).Connect(&TestAsyncTextRendered);
+
+ bool asyncTextRendered = false;
+ label.ConnectSignal(testTracker, "asyncTextRendered", CallbackFunctor(&asyncTextRendered));
+
+ gAsyncTextRenderedCalled = false;
+ gAsyncTextRenderedWidth = 0.0f;
+ gAsyncTextRenderedHeight = 0.0f;
+
+ float expectedWidth = 110.0f;
+ float expectedHeight = 50.0f;
+
+ // Case where both original and scaled textures have sufficient control size.
+ label.SetProperty(DevelTextLabel::Property::RENDER_SCALE, 1.045f);
+ label.SetProperty(TextLabel::Property::PIXEL_SNAP_FACTOR, 1.0f);
+ label.SetProperty(TextLabel::Property::PIXEL_SIZE, 20);
+ label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+ label.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+ label.SetProperty(DevelTextLabel::Property::RENDER_MODE, DevelTextLabel::Render::ASYNC_AUTO);
+ label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+ DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+ DALI_TEST_CHECK(asyncTextRendered);
+ DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+ expectedWidth = 60.0f;
+ expectedHeight = 50.0f;
+
+ asyncTextRendered = false;
+ gAsyncTextRenderedCalled = false;
+ gAsyncTextRenderedWidth = 0.0f;
+ gAsyncTextRenderedHeight = 0.0f;
+
+ // Case where the scaled texture exceeds the control size.
+ label.SetProperty(TextLabel::Property::PIXEL_SIZE, 12);
+ label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+ DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+ DALI_TEST_CHECK(asyncTextRendered);
+ DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+ expectedWidth = 500.0f;
+ expectedHeight = 50.0f;
+
+ asyncTextRendered = false;
+ gAsyncTextRenderedCalled = false;
+ gAsyncTextRenderedWidth = 0.0f;
+ gAsyncTextRenderedHeight = 0.0f;
+
+ // Text fit early return case.
+ Property::Map textFitMapSet;
+ textFitMapSet["enable"] = true;
+ textFitMapSet["minSize"] = 10.f;
+ textFitMapSet["maxSize"] = 30.f;
+ textFitMapSet["stepSize"] = 5.f;
+ textFitMapSet["fontSizeType"] = "pixelSize";
+ label.SetProperty(Toolkit::DevelTextLabel::Property::TEXT_FIT, textFitMapSet);
+ label.SetProperty(Actor::Property::SIZE, Vector2(expectedWidth, expectedHeight));
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, ASYNC_TEXT_THREAD_TIMEOUT), true, TEST_LOCATION);
+
+ DALI_TEST_CHECK(gAsyncTextRenderedCalled);
+ DALI_TEST_CHECK(asyncTextRendered);
+ DALI_TEST_EQUALS(expectedWidth, gAsyncTextRenderedWidth, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+ DALI_TEST_EQUALS(expectedHeight, gAsyncTextRenderedHeight, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+ END_TEST;
+}
const char* const PROPERTY_NAME_ASYNC_LINE_COUNT = "asyncLineCount";
const char* const PROPERTY_NAME_ELLIPSIS_MODE = "ellipsisMode";
const char* const PROPERTY_NAME_FONT_VARIATIONS = "fontVariations";
+const char* const PROPERTY_NAME_RENDER_SCALE = "renderScale";
const std::string DEFAULT_FONT_DIR("/resources/fonts");
const unsigned int EMOJI_FONT_SIZE = 3840u; // 60 * 64
DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_ASYNC_LINE_COUNT) == DevelTextLabel::Property::ASYNC_LINE_COUNT);
DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_ELLIPSIS_MODE) == DevelTextLabel::Property::ELLIPSIS_MODE);
DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_FONT_VARIATIONS) == DevelTextLabel::Property::FONT_VARIATIONS);
+ DALI_TEST_CHECK(label.GetPropertyIndex(PROPERTY_NAME_RENDER_SCALE) == DevelTextLabel::Property::RENDER_SCALE);
END_TEST;
}
label.SetProperty(DevelTextLabel::Property::ELLIPSIS_MODE, Toolkit::DevelText::Ellipsize::AUTO_SCROLL);
DALI_TEST_EQUALS(label.GetProperty<int>(DevelTextLabel::Property::ELLIPSIS_MODE), static_cast<int>(Toolkit::DevelText::Ellipsize::AUTO_SCROLL), TEST_LOCATION);
+ // Render Scale
+ label.SetProperty(TextLabel::Property::PIXEL_SNAP_FACTOR, 1.0f);
+ DALI_TEST_EQUALS(label.GetProperty<float>(TextLabel::Property::PIXEL_SNAP_FACTOR), 1.0f, TEST_LOCATION);
+
+ label.SetProperty(DevelTextLabel::Property::RENDER_SCALE, 1.045f);
+ DALI_TEST_EQUALS(label.GetProperty<float>(DevelTextLabel::Property::RENDER_SCALE), 1.045f, TEST_LOCATION);
+
+ label.SetProperty(DevelTextLabel::Property::RENDER_SCALE, 1.0f);
+ DALI_TEST_EQUALS(label.GetProperty<float>(DevelTextLabel::Property::RENDER_SCALE), 1.0f, TEST_LOCATION);
+
+ // Invalid value
+ label.SetProperty(DevelTextLabel::Property::RENDER_SCALE, 0.5f);
+ DALI_TEST_EQUALS(label.GetProperty<float>(DevelTextLabel::Property::RENDER_SCALE), 1.0f, TEST_LOCATION);
+
+ label.SetProperty(TextLabel::Property::PIXEL_SNAP_FACTOR, 0.0f);
+ DALI_TEST_EQUALS(label.GetProperty<float>(TextLabel::Property::PIXEL_SNAP_FACTOR), 0.0f, TEST_LOCATION);
+
// Check font variations property
Property::Map fontVariationsMapSet;
Property::Map fontVariationsMapGet;
* @note This property can be used only when using variable fonts.
*/
FONT_VARIATIONS,
+
+ /**
+ * @brief Renders a texture at a given scale.
+ * @details Name "renderScale", type Property::FLOAT.
+ * @note This property is only available in ASYNC_AUTO, ASYNC_MANUAL.
+ * RenderScale is only valid when it is 1.0f or greater.
+ * Renders by scaling up the point size and texture size to the given scale.
+ * However, the size of the text control does not change.
+ * When using Actor::SetScale(), setting RenderScale to the same scale can ensure the rendering quality of the text.
+ */
+ RENDER_SCALE,
};
} // namespace Property
DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextLabel, "ellipsisMode", INTEGER, ELLIPSIS_MODE )
DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY(Toolkit, TextLabel, "isScrolling", BOOLEAN, IS_SCROLLING )
DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextLabel, "fontVariations", MAP, FONT_VARIATIONS )
+DALI_DEVEL_PROPERTY_REGISTRATION(Toolkit, TextLabel, "renderScale", FLOAT, RENDER_SCALE )
-DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, TextLabel, "textColor", Color::BLACK, TEXT_COLOR )
-DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorRed", TEXT_COLOR_RED, TEXT_COLOR, 0)
-DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorGreen", TEXT_COLOR_GREEN, TEXT_COLOR, 1)
-DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorBlue", TEXT_COLOR_BLUE, TEXT_COLOR, 2)
-DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorAlpha", TEXT_COLOR_ALPHA, TEXT_COLOR, 3)
+DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, TextLabel, "textColor", Color::BLACK, TEXT_COLOR )
+DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorRed", TEXT_COLOR_RED, TEXT_COLOR, 0)
+DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorGreen", TEXT_COLOR_GREEN, TEXT_COLOR, 1)
+DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorBlue", TEXT_COLOR_BLUE, TEXT_COLOR, 2)
+DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION(Toolkit, TextLabel, "textColorAlpha", TEXT_COLOR_ALPHA, TEXT_COLOR, 3)
+DALI_ANIMATABLE_PROPERTY_REGISTRATION(Toolkit, TextLabel, "pixelSnapFactor", FLOAT, PIXEL_SNAP_FACTOR)
DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "anchorClicked", SIGNAL_ANCHOR_CLICKED )
DALI_SIGNAL_REGISTRATION(Toolkit, TextLabel, "textFitChanged", SIGNAL_TEXT_FIT_CHANGED )
impl.mIsAsyncRenderNeeded = true;
break;
}
+ case Toolkit::DevelTextLabel::Property::RENDER_SCALE:
+ {
+ float renderScale = value.Get<float>();
+ if(renderScale < 1.0f)
+ {
+ DALI_LOG_DEBUG_INFO("RenderScale must be greater than or equal to 1.0f. It will change as follows:%f -> 1.0\n", renderScale);
+ renderScale = 1.0f;
+ }
+
+ if(fabsf(renderScale - impl.mController->GetRenderScale()) > Math::MACHINE_EPSILON_1)
+ {
+ impl.mController->SetRenderScale(renderScale);
+ impl.mIsAsyncRenderNeeded = true;
+ impl.RequestTextRelayout();
+ }
+ break;
+ }
}
// Request relayout when text update is needed. It's necessary to call it
value = variationsMap;
break;
}
+ case Toolkit::DevelTextLabel::Property::RENDER_SCALE:
+ {
+ value = impl.mController->GetRenderScale();
+ break;
+ }
}
}
TextVisual::SetAsyncTextInterface(mVisual, this);
TextVisual::SetAnimatableTextColorProperty(mVisual, Toolkit::TextLabel::Property::TEXT_COLOR);
+ self.SetProperty(Toolkit::TextLabel::Property::PIXEL_SNAP_FACTOR, 0.0f);
mController = TextVisual::GetController(mVisual);
DALI_ASSERT_DEBUG(mController && "Invalid Text Controller")
parameters.cutout = mController->IsTextCutout();
parameters.backgroundWithCutoutEnabled = mController->IsBackgroundWithCutoutEnabled();
parameters.backgroundColorWithCutout = mController->GetBackgroundColorWithCutout();
-
Property::Map variationsMap;
mController->GetVariationsMap(variationsMap);
parameters.variationsMap = variationsMap;
+ parameters.renderScale = mController->GetRenderScale();
return parameters;
}
void TextLabel::AsyncSetupAutoScroll(Text::AsyncTextRenderInfo renderInfo)
{
// Pure Virtual from AsyncTextInterface
- Size verifiedSize(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
-
- Size controlSize = renderInfo.controlSize;
- float wrapGap = renderInfo.autoScrollWrapGap;
-
- PixelData data = renderInfo.autoScrollPixelData;
- Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D,
- data.GetPixelFormat(),
- data.GetWidth(),
- data.GetHeight());
+ Size verifiedSize = renderInfo.size;
+ Size controlSize = renderInfo.controlSize;
+ float wrapGap = renderInfo.autoScrollWrapGap;
+ PixelData data = renderInfo.autoScrollPixelData;
+ Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(), data.GetWidth(), data.GetHeight());
texture.Upload(data);
TextureSet textureSet = TextureSet::New();
UNIFORM highp float uHorizontalAlign;
UNIFORM highp float uVerticalAlign;
UNIFORM highp mat4 uMvpMatrix;
+ UNIFORM highp vec3 uScale;
+ UNIFORM highp float pixelSnapFactor;
};
UNIFORM_BLOCK VisualVertBlock
highp vec4 vertexPosition = vec4( ( aPosition + anchorPoint ) * visualSize + visualOffset + origin * uSize.xy, 0.0, 1.0 );
+ vec2 snappedPosition = vertexPosition.xy;
+ snappedPosition.x = floor(snappedPosition.x * uScale.x + 0.5) / uScale.x;
+ snappedPosition.y = floor(snappedPosition.y * uScale.y + 0.5) / uScale.y;
+
+ snappedPosition.x = snappedPosition.x + (1.0 - abs(mod(uSize.x, 2.0) - 1.0)) * 0.5;
+ snappedPosition.y = snappedPosition.y + (1.0 - abs(mod(uSize.y, 2.0) - 1.0)) * 0.5;
+
+ vertexPosition.xy = mix(vertexPosition.xy, snappedPosition, pixelSnapFactor);
+
gl_Position = uMvpMatrix * vertexPosition;
}
\ No newline at end of file
{
UNIFORM highp mat4 uMvpMatrix;
UNIFORM highp vec3 uSize;
+ UNIFORM highp vec3 uScale;
+ UNIFORM highp float pixelSnapFactor;
};
UNIFORM_BLOCK VisualVertBlock
{
vec2 visualSize = mix(size * uSize.xy, size, offsetSizeMode.zw ) + extraSize;
vec2 visualOffset = mix(offset * uSize.xy, offset, offsetSizeMode.xy);
- return vec4( (aPosition + anchorPoint) * visualSize + visualOffset + origin * uSize.xy, 0.0, 1.0 );
+ vec4 result = vec4( (aPosition + anchorPoint) * visualSize + visualOffset + origin * uSize.xy, 0.0, 1.0 );
+
+ vec2 snappedPosition = result.xy;
+ snappedPosition.x = floor(snappedPosition.x * uScale.x + 0.5) / uScale.x;
+ snappedPosition.y = floor(snappedPosition.y * uScale.y + 0.5) / uScale.y;
+
+ snappedPosition.x = snappedPosition.x + (1.0 - abs(mod(uSize.x, 2.0) - 1.0)) * 0.5;
+ snappedPosition.y = snappedPosition.y + (1.0 - abs(mod(uSize.y, 2.0) - 1.0)) * 0.5;
+
+ result.xy = mix(result.xy, snappedPosition, pixelSnapFactor);
+
+ return result;
}
void main()
1.0f // VerticalAlignment::BOTTOM
};
+float ConvertToEven(float value)
+{
+ int intValue(static_cast<int>(value));
+ return static_cast<float>(intValue + (intValue & 1));
+}
+
DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_ASYNC, false);
} // namespace
// Validate Fonts.
////////////////////////////////////////////////////////////////////////////////
- float scale = parameters.fontSizeScale;
-
+ float scale = parameters.fontSizeScale * parameters.renderScale;
TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * scale;
//Get the number of points per one unit of point-size
// Set information for creating pixel datas.
AsyncTextRenderInfo renderInfo;
- renderInfo.width = static_cast<uint32_t>(layoutSize.x);
- renderInfo.height = static_cast<uint32_t>(layoutSize.y);
+
+ bool isRenderScale = parameters.renderScale > 1.0f ? true : false;
+ if(isRenderScale)
+ {
+ float width = (layoutSize.width / parameters.renderScale) * (layoutSize.width / ((layoutSize.width / parameters.renderScale) * parameters.renderScale));
+ float height = (layoutSize.height / parameters.renderScale) * (layoutSize.height / ((layoutSize.height / parameters.renderScale) * parameters.renderScale));
+ renderInfo.size = Size(width, height);
+ }
+ else
+ {
+ renderInfo.size = layoutSize;
+ }
// Set the direction of text.
renderInfo.isTextDirectionRTL = mIsTextDirectionRTL;
if(cutoutEnabled)
{
- renderInfo.renderedSize = Size(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
+ renderInfo.renderedSize = renderInfo.size;
}
else
{
- renderInfo.renderedSize = Size(parameters.textWidth, parameters.textHeight);
+ float renderedWidth = isRenderScale ? parameters.renderScaleWidth : parameters.textWidth;
+ float renderedHeight = isRenderScale ? parameters.renderScaleHeight : parameters.textHeight;
+ renderInfo.renderedSize = Size(renderedWidth, renderedHeight);
}
return renderInfo;
}
-AsyncTextRenderInfo AsyncTextLoader::RenderText(AsyncTextParameters& parameters)
+AsyncTextRenderInfo AsyncTextLoader::RenderText(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize)
{
DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_RENDER_TEXT");
+ Size textNaturalSize = naturalSize;
+ bool cachedNaturalSize = useCachedNaturalSize;
+
if(parameters.requestType == Async::RENDER_CONSTRAINT)
{
- Size textNaturalSize = ComputeNaturalSize(parameters);
+ if(!cachedNaturalSize)
+ {
+ textNaturalSize = ComputeNaturalSize(parameters);
+ cachedNaturalSize = true;
+ }
// textWidth is widthConstraint
if(parameters.textWidth > textNaturalSize.width)
{
// In case of CONSTRAINT, the natural size has already been calculated.
// So we can skip Initialize and Update at this stage.
// Only the layout is newly calculated to obtain the height.
- bool layoutOnly = (parameters.requestType == Async::RENDER_CONSTRAINT);
+ bool layoutOnly = cachedNaturalSize;
float height = ComputeHeightForWidth(parameters, parameters.textWidth, layoutOnly);
// textHeight is heightConstraint.
}
else
{
- Initialize();
- Update(parameters);
+ if(!cachedNaturalSize)
+ {
+ Initialize();
+ Update(parameters);
+ }
bool layoutUpdated = false;
Layout(parameters, layoutUpdated);
}
return layoutSize.height;
}
+Size AsyncTextLoader::SetupRenderScale(AsyncTextParameters& parameters, bool& cachedNaturalSize)
+{
+ if(parameters.isTextFitEnabled || parameters.isTextFitArrayEnabled)
+ {
+ // If text fit, only update the scaled size.
+ parameters.renderScaleWidth = parameters.textWidth;
+ parameters.renderScaleHeight = parameters.textHeight;
+ parameters.textWidth = ConvertToEven(ceil(parameters.textWidth * parameters.renderScale));
+ parameters.textHeight = ConvertToEven(ceil(parameters.textHeight * parameters.renderScale));
+ parameters.minLineSize = ConvertToEven(ceil(parameters.minLineSize * parameters.renderScale));
+ cachedNaturalSize = false;
+ return Size::ZERO;
+ }
+
+ float renderScale = parameters.renderScale;
+ // Set render scale to 1.0 to compute the original scale natural size.
+ parameters.renderScale = 1.0f;
+ Size originalNaturalSize = ComputeNaturalSize(parameters);
+
+ // Restore render scale.
+ parameters.renderScale = renderScale;
+
+ // Check if the original text is ellipsized or not.
+ bool widthEllipsized = parameters.textWidth < originalNaturalSize.width ? true : false;
+ bool heightEllipsized = parameters.textHeight < originalNaturalSize.height ? true : false;
+
+ // Store the computed natural size to avoid redundant calculations.
+ Size naturalSize = ComputeNaturalSize(parameters);
+ cachedNaturalSize = true;
+
+ // Update the scaled size.
+ parameters.renderScaleWidth = parameters.textWidth;
+ parameters.renderScaleHeight = parameters.textHeight;
+ parameters.textWidth = ConvertToEven(ceil(parameters.textWidth * parameters.renderScale));
+ parameters.textHeight = ConvertToEven(ceil(parameters.textHeight * parameters.renderScale));
+ parameters.minLineSize = ConvertToEven(ceil(parameters.minLineSize * parameters.renderScale));
+
+ // The texture in RenderScale needs to be resized because it exceeds the control size.
+ if(!widthEllipsized && naturalSize.width > parameters.textWidth)
+ {
+ float renderScaleGap = ceil(naturalSize.width - parameters.textWidth);
+ if(renderScaleGap > 0.0f)
+ {
+ Vector<GlyphInfo>& glyphs = mTextModel->mVisualModel->mGlyphs;
+ const Length numberOfGlyphs = static_cast<Length>(glyphs.Count());
+ if(numberOfGlyphs > 1u)
+ {
+ naturalSize.width -= (renderScaleGap - 1.0f);
+ parameters.textWidth = naturalSize.width;
+
+ uint32_t numberOfAdvance = 0u;
+ float sumOfGap = 0.0f;
+ float gap = renderScaleGap / static_cast<float>(numberOfGlyphs - 1u);
+
+ // Reduce the advance of all glyphs slightly to fit the width to the control size.
+ // Reducing the advance of the last glyph is pointless.
+ for(Length index = 0u; index < numberOfGlyphs - 1u; index++)
+ {
+ if(glyphs[index].advance > 0.0f)
+ {
+ glyphs[index].advance -= gap;
+ numberOfAdvance++;
+ sumOfGap += gap;
+ }
+ }
+
+ // Remove all remaining gaps.
+ if(numberOfAdvance > 0u && fabsf(renderScaleGap - sumOfGap) > Math::MACHINE_EPSILON_1000)
+ {
+ float remainedGap = (renderScaleGap - sumOfGap) / static_cast<float>(numberOfAdvance);
+ for(Length index = 0u; index < numberOfGlyphs - 1u; index++)
+ {
+ if(glyphs[index].advance > 0.0f)
+ {
+ glyphs[index].advance -= remainedGap;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Adjust the size to ensure same ellipsis behavior as the original text.
+ parameters.textWidth = widthEllipsized ? std::min(parameters.textWidth, naturalSize.width - 1) : std::max(parameters.textWidth, naturalSize.width);
+ parameters.textHeight = heightEllipsized ? std::min(parameters.textHeight, naturalSize.height - 1) : std::max(parameters.textHeight, naturalSize.height);
+
+ // Update the control size because textWidth and textHeight have been adjusted. (Skip Initialize and Update)
+ mTextModel->mVisualModel->mControlSize = Size(parameters.textWidth, parameters.textHeight);
+
+ return naturalSize;
+}
+
Size AsyncTextLoader::ComputeNaturalSize(AsyncTextParameters& parameters)
{
#ifdef TRACE_ENABLED
AsyncTextRenderInfo AsyncTextLoader::GetHeightForWidth(AsyncTextParameters& parameters)
{
- float height = ComputeHeightForWidth(parameters, parameters.textWidth, false);
+ float height = ComputeHeightForWidth(parameters, parameters.textWidth, false);
+
AsyncTextRenderInfo renderInfo;
renderInfo.renderedSize.width = parameters.textWidth;
renderInfo.renderedSize.height = height;
AsyncTextRenderInfo AsyncTextLoader::GetNaturalSize(AsyncTextParameters& parameters)
{
- Size textNaturalSize = ComputeNaturalSize(parameters);
+ Size textNaturalSize = ComputeNaturalSize(parameters);
+ textNaturalSize.width = ConvertToEven(textNaturalSize.width);
+ textNaturalSize.height = ConvertToEven(textNaturalSize.height);
+
AsyncTextRenderInfo renderInfo;
renderInfo.renderedSize = textNaturalSize;
renderInfo.requestType = Async::COMPUTE_NATURAL_SIZE;
return renderInfo;
}
-AsyncTextRenderInfo AsyncTextLoader::RenderAutoScroll(AsyncTextParameters& parameters)
+AsyncTextRenderInfo AsyncTextLoader::RenderAutoScroll(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize)
{
DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_ASYNC_RENDER_AUTO_SCROLL");
Size controlSize(parameters.textWidth, parameters.textHeight);
// As relayout of text may not be done at this point natural size is used to get size. Single line scrolling only.
- Size textNaturalSize = ComputeNaturalSize(parameters);
- textNaturalSize.width += (parameters.padding.start + parameters.padding.end);
- textNaturalSize.height += (parameters.padding.top + parameters.padding.bottom);
+ Size textNaturalSize;
+ if(useCachedNaturalSize)
+ {
+ textNaturalSize = naturalSize;
+ }
+ else
+ {
+ textNaturalSize = ComputeNaturalSize(parameters);
+ textNaturalSize.width += (parameters.padding.start + parameters.padding.end);
+ textNaturalSize.height += (parameters.padding.top + parameters.padding.bottom);
+ }
if(parameters.requestType == Async::RENDER_FIXED_WIDTH || parameters.requestType == Async::RENDER_CONSTRAINT)
{
parameters.textWidth = actualWidth;
// Store the control size and calculated wrap gap in render info.
- renderInfo.controlSize = controlSize;
+ bool isRenderScale = parameters.renderScale > 1.0f ? true : false;
+ float renderedWidth = isRenderScale ? parameters.renderScaleWidth : controlSize.width;
+ float renderedHeight = isRenderScale ? parameters.renderScaleHeight : controlSize.height;
+ renderInfo.controlSize = Size(renderedWidth, renderedHeight);
+ renderInfo.renderedSize = Size(renderedWidth, renderedHeight);
renderInfo.autoScrollWrapGap = wrapGap;
- renderInfo.renderedSize = controlSize;
-
return renderInfo;
}
return true;
}
-AsyncTextRenderInfo AsyncTextLoader::RenderTextFit(AsyncTextParameters& parameters)
+AsyncTextRenderInfo AsyncTextLoader::RenderTextFit(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize)
{
+ Size textNaturalSize = naturalSize;
+ bool cachedNaturalSize = useCachedNaturalSize;
+
if(parameters.requestType == Async::RENDER_CONSTRAINT)
{
- Size textNaturalSize = ComputeNaturalSize(parameters);
+ if(!cachedNaturalSize)
+ {
+ textNaturalSize = ComputeNaturalSize(parameters);
+ cachedNaturalSize = true;
+ }
// textWidth is widthConstraint
if(parameters.textWidth > textNaturalSize.width)
{
// In case of CONSTRAINT, the natural size has already been calculated.
// So we can skip Initialize and Update at this stage.
// Only the layout is newly calculated to obtain the height.
- bool layoutOnly = (parameters.requestType == Async::RENDER_CONSTRAINT);
+ bool layoutOnly = cachedNaturalSize;
float height = ComputeHeightForWidth(parameters, parameters.textWidth, layoutOnly);
// textHeight is heightConstraint
// Worker thread
+ /**
+ * @copydoc Dali::AsyncTextLoader::SetupRenderScale()
+ */
+ Size SetupRenderScale(AsyncTextParameters& parameters, bool& cachedNaturalSize);
+
+ /**
+ * @copydoc Dali::AsyncTextLoader::ComputeNaturalSize()
+ */
+ Size ComputeNaturalSize(AsyncTextParameters& parameters);
+
/**
* @copydoc Dali::AsyncTextLoader::RenderText()
*/
- AsyncTextRenderInfo RenderText(AsyncTextParameters& parameters);
+ AsyncTextRenderInfo RenderText(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize);
/**
* @copydoc Dali::AsyncTextLoader::RenderTextFit()
*/
- AsyncTextRenderInfo RenderTextFit(AsyncTextParameters& parameters);
+ AsyncTextRenderInfo RenderTextFit(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize);
/**
* @copydoc Dali::AsyncTextLoader::RenderAutoScroll()
*/
- AsyncTextRenderInfo RenderAutoScroll(AsyncTextParameters& parameters);
+ AsyncTextRenderInfo RenderAutoScroll(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize);
/**
* @copydoc Dali::AsyncTextLoader::GetNaturalSize()
*/
AsyncTextRenderInfo Render(AsyncTextParameters& parameters);
- /**
- * @brief Compute natural size of text.
- *
- * @param[in] parameters All options required to compute size of text.
- *
- * @return The natural size of text.
- */
- Size ComputeNaturalSize(AsyncTextParameters& parameters);
-
/**
* @brief Compute height for width of text.
*
return AsyncTextLoader(asyncTextLoaderImpl);
}
-AsyncTextRenderInfo AsyncTextLoader::RenderText(AsyncTextParameters& parameters)
+Size AsyncTextLoader::SetupRenderScale(AsyncTextParameters& parameters, bool& cachedNaturalSize)
{
- return GetImplementation(*this).RenderText(parameters);
+ return GetImplementation(*this).SetupRenderScale(parameters, cachedNaturalSize);
}
-AsyncTextRenderInfo AsyncTextLoader::RenderTextFit(AsyncTextParameters& parameters)
+Size AsyncTextLoader::ComputeNaturalSize(AsyncTextParameters& parameters)
{
- return GetImplementation(*this).RenderTextFit(parameters);
+ return GetImplementation(*this).ComputeNaturalSize(parameters);
}
-AsyncTextRenderInfo AsyncTextLoader::RenderAutoScroll(AsyncTextParameters& parameters)
+AsyncTextRenderInfo AsyncTextLoader::RenderText(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize)
{
- return GetImplementation(*this).RenderAutoScroll(parameters);
+ return GetImplementation(*this).RenderText(parameters, useCachedNaturalSize, naturalSize);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderTextFit(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize)
+{
+ return GetImplementation(*this).RenderTextFit(parameters, useCachedNaturalSize, naturalSize);
+}
+
+AsyncTextRenderInfo AsyncTextLoader::RenderAutoScroll(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize)
+{
+ return GetImplementation(*this).RenderAutoScroll(parameters, useCachedNaturalSize, naturalSize);
}
AsyncTextRenderInfo AsyncTextLoader::GetNaturalSize(AsyncTextParameters& parameters)
struct AsyncTextParameters
{
AsyncTextParameters()
- : requestType{Async::RENDER_FIXED_SIZE},
- manualRender{false},
- maxTextureSize{0},
- text{},
- fontSize{0.f},
- textColor{Color::BLACK},
+ : text{},
fontFamily{},
- fontWeight{FontWeight::NONE},
- fontWidth{FontWidth::NONE},
- fontSlant{FontSlant::NONE},
- isMultiLine{false},
- ellipsis{true},
- enableMarkup{false},
- removeFrontInset{true},
- removeBackInset{true},
+ textColor{Color::BLACK},
+ underlineColor{Color::BLACK},
+ strikethroughColor{Color::BLACK},
+ shadowColor{Color::BLACK},
+ outlineColor{Color::WHITE},
+ backgroundColorWithCutout{Color::TRANSPARENT},
+ shadowOffset{},
+ outlineOffset{},
+ padding{0u, 0u, 0u, 0u},
+ variationsMap{},
+ textFitArray{},
+ fontSize{0.f},
minLineSize{0.f},
lineSpacing{0.f},
relativeLineSize{1.f},
fontSizeScale{1.f},
textWidth{0.f},
textHeight{0.f},
- padding{0u, 0u, 0u, 0u},
- horizontalAlignment{Text::HorizontalAlignment::BEGIN},
- verticalAlignment{Text::VerticalAlignment::TOP},
- verticalLineAlignment{DevelText::VerticalLineAlignment::TOP},
- lineWrapMode{Text::LineWrap::WORD},
- layoutDirection{Dali::LayoutDirection::LEFT_TO_RIGHT},
- layoutDirectionPolicy{DevelText::MatchLayoutDirection::INHERIT},
- ellipsisPosition{DevelText::EllipsisPosition::END},
- ellipsisMode{DevelText::Ellipsize::TRUNCATE},
- isUnderlineEnabled{false},
- underlineType{Text::Underline::SOLID},
- underlineColor{Color::BLACK},
underlineHeight{0.f},
dashedUnderlineWidth{2.f},
dashedUnderlineGap{1.f},
- isStrikethroughEnabled{false},
- strikethroughColor{Color::BLACK},
strikethroughHeight{0.f},
shadowBlurRadius{0.f},
- shadowColor{Color::BLACK},
- shadowOffset{},
- outlineWidth{0u},
- outlineColor{Color::WHITE},
outlineBlurRadius{0.f},
- outlineOffset{},
- isTextFitEnabled{false},
textFitMinSize{10.f},
textFitMaxSize{100.f},
textFitStepSize{1.f},
- isTextFitArrayEnabled{false},
- textFitArray{},
- isAutoScrollEnabled{false},
- autoScrollStopMode{TextLabel::AutoScrollStopMode::FINISH_LOOP},
+ autoScrollLoopDelay{0.0f},
+ renderScale{1.0f},
+ renderScaleWidth{0.f},
+ renderScaleHeight{0.f},
+ maxTextureSize{0},
autoScrollSpeed{1},
autoScrollLoopCount{1},
- autoScrollLoopDelay{0.0f},
autoScrollGap{0},
+ outlineWidth{0u},
+ requestType{Async::RENDER_FIXED_SIZE},
+ horizontalAlignment{Text::HorizontalAlignment::BEGIN},
+ verticalAlignment{Text::VerticalAlignment::TOP},
+ lineWrapMode{Text::LineWrap::WORD},
+ underlineType{Text::Underline::SOLID},
+ layoutDirection{Dali::LayoutDirection::LEFT_TO_RIGHT},
+ verticalLineAlignment{DevelText::VerticalLineAlignment::TOP},
+ layoutDirectionPolicy{DevelText::MatchLayoutDirection::INHERIT},
+ ellipsisPosition{DevelText::EllipsisPosition::END},
+ ellipsisMode{DevelText::Ellipsize::TRUNCATE},
+ autoScrollStopMode{TextLabel::AutoScrollStopMode::FINISH_LOOP},
+ fontWeight{FontWeight::NONE},
+ fontWidth{FontWidth::NONE},
+ fontSlant{FontSlant::NONE},
+ manualRender{false},
+ isMultiLine{false},
+ ellipsis{true},
+ enableMarkup{false},
+ removeFrontInset{true},
+ removeBackInset{true},
+ isUnderlineEnabled{false},
+ isStrikethroughEnabled{false},
+ isTextFitEnabled{false},
+ isTextFitArrayEnabled{false},
+ isAutoScrollEnabled{false},
isAutoScrollMaxTextureExceeded{false},
cutout{false},
- backgroundWithCutoutEnabled{false},
- backgroundColorWithCutout{Color::TRANSPARENT},
- variationsMap{}
+ backgroundWithCutoutEnabled{false}
{
}
{
}
- Async::RequestType requestType;
- bool manualRender : 1;
-
- int maxTextureSize; ///< The maximum size of texture.
- std::string text; ///< The text to be rendered encoded in utf8.
- float fontSize; ///< The font's size (in points).
- Vector4 textColor; ///< The default text's color. Default is white.
-
- std::string fontFamily; ///< The font's family.
- FontWeight fontWeight; ///< The font's weight.
- FontWidth fontWidth; ///< The font's width.
- FontSlant fontSlant; ///< The font's slant.
-
- bool isMultiLine : 1; ///< Whether the multi-line layout is enabled.
- bool ellipsis : 1; ///< Whether the ellipsis layout option is enabled.
- bool enableMarkup : 1; ///< Whether the mark-up processor is enabled.
- bool removeFrontInset : 1; ///< Whether to ignore xBearing of the first glyph. Default is true.
- bool removeBackInset : 1; ///< Whether to ignore advance of the last glyph. Default is true.
-
- float minLineSize; ///< The line's minimum size (in pixels).
- float lineSpacing; ///< The default extra space between lines in points. (in pixels).
- float relativeLineSize; ///< The relative height of the line (a factor that will be multiplied by text height).
- float characterSpacing; ///< The space between characters.
- float fontSizeScale; ///< The font's size scale.
-
- float textWidth; ///< The width in pixels of the boundaries where the text is going to be laid-out.
- float textHeight; ///< The height in pixels of the boundaries where the text is going to be laid-out.
- Extents padding; ///< The padding of the boundaries where the text is going to be laid-out.
+ std::string text; ///< The text to be rendered encoded in utf8.
+ std::string fontFamily; ///< The font's family.
+
+ Vector4 textColor; ///< The default text's color. Default is white.
+ Vector4 underlineColor;
+ Vector4 strikethroughColor;
+ Vector4 shadowColor;
+ Vector4 outlineColor;
+ Vector4 backgroundColorWithCutout; ///< Background color with cutout.
+
+ Vector2 shadowOffset;
+ Vector2 outlineOffset;
+
+ Extents padding; ///< The padding of the boundaries where the text is going to be laid-out.
+ Property::Map variationsMap; ///< The map for variable fonts. it might be replaced by variable map run.
+ std::vector<DevelTextLabel::FitOption> textFitArray;
+
+ float fontSize; ///< The font's size (in points).
+ float minLineSize; ///< The line's minimum size (in pixels).
+ float lineSpacing; ///< The default extra space between lines in points. (in pixels).
+ float relativeLineSize; ///< The relative height of the line (a factor that will be multiplied by text height).
+ float characterSpacing; ///< The space between characters.
+ float fontSizeScale; ///< The font's size scale.
+ float textWidth; ///< The width in pixels of the boundaries where the text is going to be laid-out.
+ float textHeight; ///< The height in pixels of the boundaries where the text is going to be laid-out.
+ float underlineHeight;
+ float dashedUnderlineWidth;
+ float dashedUnderlineGap;
+ float strikethroughHeight;
+ float shadowBlurRadius;
+ float outlineBlurRadius;
+ float textFitMinSize;
+ float textFitMaxSize;
+ float textFitStepSize;
+ float autoScrollLoopDelay;
+ float renderScale; ///< The render scale.
+ float renderScaleWidth; ///< The requested original textWidth when using render scale.
+ float renderScaleHeight; ///< The requested original textHeight when using render scale.
+
+ int maxTextureSize; ///< The maximum size of texture.
+ int autoScrollSpeed; ///< auto scroll properties.
+ int autoScrollLoopCount;
+ int autoScrollGap;
+
+ uint16_t outlineWidth; ///< The width of the outline, if it is greater than 1, it is enabled.
+
+ Async::RequestType requestType;
Text::HorizontalAlignment::Type horizontalAlignment; ///< The horizontal alignment: one of {BEGIN, CENTER, END}.
Text::VerticalAlignment::Type verticalAlignment; ///< The vertical alignment: one of {TOP, CENTER, BOTTOM}.
- DevelText::VerticalLineAlignment::Type verticalLineAlignment; ///< The vertical line alignment: one of {TOP, MIDDLE, BOTTOM}.
Text::LineWrap::Mode lineWrapMode; ///< The line wrap mode: one of {WORD, CHARACTER, HYPHENATION, MIXED}.
+ Text::Underline::Type underlineType; ///< The type of underline: one of {SOLID, DASHED, DOUBLE}.
Dali::LayoutDirection::Type layoutDirection; ///< The layout direction: one of {LEFT_TO_RIGHT, RIGHT_TO_LEFT}.
+ DevelText::VerticalLineAlignment::Type verticalLineAlignment; ///< The vertical line alignment: one of {TOP, MIDDLE, BOTTOM}.
DevelText::MatchLayoutDirection layoutDirectionPolicy; ///< The policy used to set the text layout direction : one of {INHERIT, LOCALE, CONTENTS}.
DevelText::EllipsisPosition::Type ellipsisPosition; ///< The position of the ellipsis glyph: one of {END, START, MIDDLE}.
DevelText::Ellipsize::Mode ellipsisMode; ///< The mode of the ellipsis: one of {TRUNCATE, AUTO_SCROLL}.
-
- bool isUnderlineEnabled : 1; ///< Underline properties
- Text::Underline::Type underlineType;
- Vector4 underlineColor;
- float underlineHeight;
- float dashedUnderlineWidth;
- float dashedUnderlineGap;
-
- bool isStrikethroughEnabled : 1; ///< Strikethrough properties
- Vector4 strikethroughColor;
- float strikethroughHeight;
-
- float shadowBlurRadius; ///< Shadow properties
- Vector4 shadowColor;
- Vector2 shadowOffset;
-
- uint16_t outlineWidth; ///< Outline properties
- Vector4 outlineColor;
- float outlineBlurRadius;
- Vector2 outlineOffset;
-
- bool isTextFitEnabled : 1; ///< TextFit
- float textFitMinSize;
- float textFitMaxSize;
- float textFitStepSize;
-
- bool isTextFitArrayEnabled : 1; ///< TextFitArray
- std::vector<DevelTextLabel::FitOption> textFitArray;
-
- bool isAutoScrollEnabled : 1; ///< Auto scroll
- TextLabel::AutoScrollStopMode::Type autoScrollStopMode;
- int autoScrollSpeed;
- int autoScrollLoopCount;
- float autoScrollLoopDelay;
- int autoScrollGap;
- bool isAutoScrollMaxTextureExceeded : 1;
-
- bool cutout : 1; ///< Cutout enabled flag
- bool backgroundWithCutoutEnabled : 1; ///< Background with cutout enabled flag.
- Vector4 backgroundColorWithCutout; ///< Background color with cutout.
-
- Property::Map variationsMap; ///< The map for variable fonts. it might be replaced by variable map run.
+ TextLabel::AutoScrollStopMode::Type autoScrollStopMode; ///< The auto scroll stop mode: one of {FINISH_LOOP, IMMEDIATE}.
+ FontWeight fontWeight; ///< The font's weight.
+ FontWidth fontWidth; ///< The font's width.
+ FontSlant fontSlant; ///< The font's slant.
+
+ bool manualRender : 1; ///< Whether the manual rendered or not.
+ bool isMultiLine : 1; ///< Whether the multi-line layout is enabled.
+ bool ellipsis : 1; ///< Whether the ellipsis layout option is enabled.
+ bool enableMarkup : 1; ///< Whether the mark-up processor is enabled.
+ bool removeFrontInset : 1; ///< Whether to ignore xBearing of the first glyph. Default is true.
+ bool removeBackInset : 1; ///< Whether to ignore advance of the last glyph. Default is true.
+ bool isUnderlineEnabled : 1; ///< Underline enabeld flag.
+ bool isStrikethroughEnabled : 1; ///< Strikethrough enabeld flag.
+ bool isTextFitEnabled : 1; ///< TextFit enabeld flag.
+ bool isTextFitArrayEnabled : 1; ///< TextFitArray enabeld flag.
+ bool isAutoScrollEnabled : 1; ///< Auto scroll enabeld flag.
+ bool isAutoScrollMaxTextureExceeded : 1; ///< Whether the auto scroll texture size exceeds the maximum texture width.
+ bool cutout : 1; ///< Cutout enabled flag.
+ bool backgroundWithCutoutEnabled : 1; ///< Background with cutout enabled flag.
};
struct AsyncTextRenderInfo
overlayStylePixelData(),
maskPixelData(),
autoScrollPixelData(),
- width(0u),
- height(0u),
+ size(),
controlSize(),
renderedSize(),
lineCount(0),
PixelData overlayStylePixelData;
PixelData maskPixelData;
PixelData autoScrollPixelData;
- uint32_t width;
- uint32_t height;
+ Size size;
Size controlSize;
Size renderedSize;
int lineCount;
*/
bool IsModuleClearNeeded();
+ /**
+ * @brief Setup render scale.
+ * Sets the control size to be rendered to fit the given scale.
+ * The scaled rendering result cannot be exactly the same as the original.
+ * However, we guarantee the ellipsis result.
+ * If the original is ellipsised, the scaled result will always be ellipsised.
+ * If the original is not ellipsised, the scaled result will not be ellipsised.
+ * Occasionally, the scaled result exceeds the size of the control.
+ * Since we need to ensure the size of the control, we slightly reduce the glyph's advance to adjust the total width to fit the control size.
+ * While this may cause rendering quality issues at smaller point sizes, there is almost no noticeable difference at moderate sizes of 20pt or larger.
+ *
+ * @param[in] parameters All options required to compute size of text.
+ * @param[out] cachedNaturalSize Whether the natural size has been calculated.
+ *
+ * @return The natural size of text.
+ */
+ Size SetupRenderScale(AsyncTextParameters& parameters, bool& cachedNaturalSize);
+
+ /**
+ * @brief Compute natural size of text.
+ *
+ * @param[in] parameters All options required to compute size of text.
+ *
+ * @return The natural size of text.
+ */
+ Size ComputeNaturalSize(AsyncTextParameters& parameters);
+
/**
* @brief Renders text into a pixel buffer.
*
* @param[in] parameters All options required to render text.
+ * @param[in] useCachedNaturalSize Indicates whether to use the provided natural size or calculate it internally.
+ * @param[in] naturalSize The natural size of the text to be used if useCachedNaturalSize is true.
*
* @return An AsyncTextRenderInfo.
*/
- AsyncTextRenderInfo RenderText(AsyncTextParameters& parameters);
+ AsyncTextRenderInfo RenderText(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize);
/**
* @brief Renders text into a pixel buffer.
*
* @param[in] parameters All options required to render text.
+ * @param[in] useCachedNaturalSize Indicates whether to use the provided natural size or calculate it internally.
+ * @param[in] naturalSize The natural size of the text to be used if useCachedNaturalSize is true.
*
* @return An AsyncTextRenderInfo.
*/
- AsyncTextRenderInfo RenderTextFit(AsyncTextParameters& parameters);
+ AsyncTextRenderInfo RenderTextFit(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize);
/**
* @brief Renders text into a pixel buffer.
*
* @param[in] parameters All options required to render text.
+ * @param[in] useCachedNaturalSize Indicates whether to use the provided natural size or calculate it internally.
+ * @param[in] naturalSize The natural size of the text to be used if useCachedNaturalSize is true.
*
* @return An AsyncTextRenderInfo.
*/
- AsyncTextRenderInfo RenderAutoScroll(AsyncTextParameters& parameters);
+ AsyncTextRenderInfo RenderAutoScroll(AsyncTextParameters& parameters, bool useCachedNaturalSize, const Size& naturalSize);
/**
* @brief Gets the natural size of text.
case Text::Async::RENDER_FIXED_WIDTH:
case Text::Async::RENDER_CONSTRAINT:
{
+ // To avoid duplicate calculation, we can skip Initialize and Update.
+ Size naturalSize = Size::ZERO;
+ bool cachedNaturalSize = false;
+
+ if(mParameters.renderScale > 1.0f)
+ {
+#ifdef TRACE_ENABLED
+ if(gTraceFilter && gTraceFilter->IsTraceEnabled())
+ {
+ DALI_LOG_RELEASE_INFO("SetupRenderScale : %f\n", mParameters.renderScale);
+ }
+#endif
+ naturalSize = mLoader.SetupRenderScale(mParameters, cachedNaturalSize);
+ }
+
if(mParameters.ellipsis && !mParameters.isMultiLine && mParameters.ellipsisMode == DevelText::Ellipsize::AUTO_SCROLL)
{
- Text::AsyncTextRenderInfo naturalSizeInfo;
- naturalSizeInfo = mLoader.GetNaturalSize(mParameters);
- if(mParameters.textWidth < naturalSizeInfo.renderedSize.width)
+ if(!cachedNaturalSize)
+ {
+ naturalSize = mLoader.ComputeNaturalSize(mParameters);
+ cachedNaturalSize = true;
+ }
+ if(mParameters.textWidth < naturalSize.width)
{
#ifdef TRACE_ENABLED
if(gTraceFilter && gTraceFilter->IsTraceEnabled())
}
#endif
mParameters.isAutoScrollEnabled = true;
- mRenderInfo = mLoader.RenderAutoScroll(mParameters);
+ mRenderInfo = mLoader.RenderAutoScroll(mParameters, cachedNaturalSize, naturalSize);
}
else
{
DALI_LOG_RELEASE_INFO("RenderText, Ellipsize::AUTO_SCROLL\n");
}
#endif
- mRenderInfo = mLoader.RenderText(mParameters);
+ mRenderInfo = mLoader.RenderText(mParameters, cachedNaturalSize, naturalSize);
}
}
else if(mParameters.isAutoScrollEnabled && !mParameters.isMultiLine)
DALI_LOG_RELEASE_INFO("RenderAutoScroll\n");
}
#endif
- mRenderInfo = mLoader.RenderAutoScroll(mParameters);
+ mRenderInfo = mLoader.RenderAutoScroll(mParameters, cachedNaturalSize, naturalSize);
}
else if(mParameters.isTextFitEnabled || mParameters.isTextFitArrayEnabled)
{
DALI_LOG_RELEASE_INFO("RenderTextFit\n");
}
#endif
- mRenderInfo = mLoader.RenderTextFit(mParameters);
+ mRenderInfo = mLoader.RenderTextFit(mParameters, cachedNaturalSize, naturalSize);
}
else
{
DALI_LOG_RELEASE_INFO("RenderText\n");
}
#endif
- mRenderInfo = mLoader.RenderText(mParameters);
+ mRenderInfo = mLoader.RenderText(mParameters, cachedNaturalSize, naturalSize);
}
break;
}
mTextFitLineSize(0.f),
mFontSizeScale(DEFAULT_FONT_SIZE_SCALE),
mDisabledColorOpacity(DEFAULT_DISABLED_COLOR_OPACITY),
+ mRenderScale(1.0f),
mFontSizeScaleEnabled(true),
mTextFitEnabled(false),
mTextFitChanged(false),
float mTextFitLineSize; ///< This is the LineSize that is the standard when performing TextFit.
float mFontSizeScale; ///< Scale value for Font Size. Default 1.0
float mDisabledColorOpacity; ///< Color opacity when disabled.
+ float mRenderScale; ///< The render scale. Default 1.0
bool mFontSizeScaleEnabled : 1; ///< Whether the font size scale is enabled.
bool mTextFitEnabled : 1; ///< Whether the text's fit is enabled.
bool mTextFitChanged : 1; ///< Whether the text fit property has changed.
mImpl->mEllipsisMode = ellipsisMode;
}
+void Controller::SetRenderScale(const float renderScale)
+{
+ mImpl->mRenderScale = renderScale;
+}
+
+float Controller::GetRenderScale() const
+{
+ return mImpl->mRenderScale;
+}
+
void Controller::SetCharacterSpacing(float characterSpacing)
{
mImpl->mModel->mVisualModel->SetCharacterSpacing(characterSpacing);
*/
void SetEllipsisMode(Toolkit::DevelText::Ellipsize::Mode ellipsisMode);
+ /**
+ * @brief Sets the render scale
+ * @param[in] renderScale The render scale
+ */
+ void SetRenderScale(const float renderScale);
+
+ /**
+ * @brief Retrieves the render scale
+ * @return The value of the render scale
+ */
+ float GetRenderScale() const;
+
/**
* @brief Retrieves ignoreSpaceAfterText value from model
* @return The value of ignoreSpaceAfterText
// Calculate the size of the visual that can fit the text.
// The size of the text after it has been laid-out, size of pixel data buffer.
- Size layoutSize(static_cast<float>(renderInfo.width), static_cast<float>(renderInfo.height));
+ Size layoutSize = renderInfo.size;
+
+ // Set textWidth, textHeight to the original size requested for rendering.
+ bool isRenderScale = parameters.renderScale > 1.0f ? true : false;
+ if(isRenderScale)
+ {
+ parameters.textWidth = parameters.renderScaleWidth;
+ parameters.textHeight = parameters.renderScaleHeight;
+ }
// Calculate the offset for vertical alignment only, as the layout engine will do the horizontal alignment.
Vector2 alignmentOffset;
// This affects font rendering quality.
// It need to be integerized.
visualTransformOffset.x = roundf(parameters.padding.start + alignmentOffset.x);
- visualTransformOffset.y = roundf(parameters.padding.top + alignmentOffset.y);
+ visualTransformOffset.y = isRenderScale ? roundf((layoutSize.y + parameters.padding.top + alignmentOffset.y) * 2.0f) * 0.5f - layoutSize.y :
+ roundf(parameters.padding.top + alignmentOffset.y);
}
SetRequireRender(renderInfo.isCutout);
const int maxTextureSize = Dali::GetMaxTextureSize();
// No tiling required. Use the default renderer.
- if(renderInfo.height < static_cast<uint32_t>(maxTextureSize))
+ if(renderInfo.size.height < static_cast<float>(maxTextureSize))
{
// Filter mode needs to be set to linear to produce better quality while scaling.
Sampler sampler = Sampler::New();
Sampler sampler = Sampler::New();
sampler.SetFilterMode(FilterMode::LINEAR, FilterMode::LINEAR);
- int verifiedWidth = static_cast<int>(renderInfo.width);
- int verifiedHeight = static_cast<int>(renderInfo.height);
+ int verifiedWidth = static_cast<int>(renderInfo.size.width);
+ int verifiedHeight = static_cast<int>(renderInfo.size.height);
// Set information for creating textures.
TilingInfo info(verifiedWidth, maxTextureSize);
* @see TEXT_COLOR
*/
TEXT_COLOR_ALPHA,
+
+ /**
+ * @brief A pixel snap factor.
+ * @details Name "pixelSnapFactor", type Property::FLOAT.
+ * @note A factor of pixel snap, it should be 0.0 ~ 1.0.
+ * Controls the degree of pixel snapping applied to the visual position.
+ * A value of 0.0 means no snapping is applied (original position is preserved), while 1.0 applies full pixel alignment.
+ * Intermediate values blend smoothly between the original and snapped positions using linear interpolation (mix) in the vertex shader.
+ * Typical usage:
+ * To ensure both smooth animations and sharp visual alignment,
+ * transition the pixelSnapFactor value gradually from 0.0 to 1.0 during or after animations.
+ * This allows the snapping to engage seamlessly without visible jitter or popping, maintaining both visual quality and motion fluidity.
+ * Use 0.0 during animation to avoid snapping artifacts.
+ * Gradually increase to 1.0 as the animation settles, for crisp pixel alignment.
+ */
+ PIXEL_SNAP_FACTOR,
};
};