TextObjects on mobile do not correctly use TextSize and LineHeight

Reproduction Steps
Each line of a TextObject in Roblox is supposed to be rendered at this height (in px):
math.floor(TextObject.TextSize*TextObject.LineHeight)*(lineNum-1).

This behavior is properly followed on PC. However, mobile devices do not render TextObjects according to this formula.

This issue is 100% reproducible on:

  • OnePlus 7T
  • Samsung Galaxy 10+
  • iPhone SE 2nd gen

To visualize this rendering issue, we can try overlaying TextLabels at the expected line positions and see how the underlying TextLabel does not align.
Here is an uncopylocked repro place. You can join from a desktop and a phone to witness the difference in behavior. (Pink is overlay labels, green is underlying label)

Here is what it looks like on my laptop: (Win10, RTX 3060, Ryzen 9 5900HS)

desktop

Here is what it looks like in the mobile emulator of Studio:

Here is what it looks like on OnePlus 7T: (OxygenOS, Adreno 640, Kryo 485)

Here is what it looks like on iPhone SE 2nd gen:

Expected Behavior
I expect the TextObjects to render the way they do in Studio and on other platforms.

Actual Behavior
See above. The TextObject does not correctly respect the TextSize and LineHeight properties on mobile.

Workaround
No workaround.

Issue Area: Engine
Issue Type: Display
Impact: High
Frequency: Constantly

21 Likes

Further discovery:
The issue seems to be somewhat related to the DPI or resolution of the mobile device- it does not affect all devices equally.

If you look closely, you’ll see that different mobile devices have different results. They’re all broken, ofc, but broken by varying amounts.



5 Likes

what happens if you wrap the floor() over the whole statement rather than without the line count?

Here is what it looks like on my Samsung A50 (Android 11):

The line spacing is less than the expected! So I thought I would post it since all of your tests overshoot.

1 Like

Thanks for your report! Do you mind answering the following questions:

  • Is this a new issue that started to occur recently?
  • What text size are you using for this? Could you specifically try using text size 12 or 24 to see if you can repro the issue?
3 Likes
  • Is this a new issue that started to occur recently?

Unfortunately I do not have an answer to this, as I only began work on my mobile version recently.

  • What text size are you using for this? Could you specifically try using text size 12 or 24 to see if you can repro the issue?

As you can see in the repro, TextSize was set to 15.

With TextSize of 12, I get the following result:


With TextSize of 24, I get a much smaller error but still very noticeable:

1 Like

I think the problem here is that the formula that is used to compute the text position is not correct when the screen has HiDPI. HiDPI is currently only supported on mobile and Mac/Studio, but we are working toward enabling it on PC and Mac clients as well, so this will soon affect all platforms. PC/Studio will unfortunately lag somewhat behind due to limitations of third party libraries we depend on.

The correct formula may be like this:

math.floor(TextObject.TextSize * TextObject.LineHeight * DPIScale) / DPIScale *(lineNum-1)

Could you confirm that? I don’t know if there’s a way for you to determine the DPI scale of the device, but you should at least be able to get that information from the manufacturer, and if we don’t expose this to Lua yet, then we should add that to the API.

1 Like

There is no way to get DPI scale from Luau, so I literally cannot use that formula (without hard coding it for this one particular test case).

Can this DPI change be delayed until after DPI scale is exposed to developers? It’s a breaking change as it completely changes how things on screen are displayed and we as devs are currently unable to support it even if we try.


Test result: (Ping to notify of edit @nacasagu)
I hardcoded DPIScale to 402 based on GSMArena’s spec for the OnePlus 7T.
It now looks wrong on PC ofc, but even still looks wrong on my phone. Either I got the wrong ppi or the formula is still incorrect.

Sorry, but the change has been in effect on mobile for many years. Reverting it would result in an unacceptable quality degradation on Mobile. I’ll bring this up to the attention of the relevant developers.

Note, the PPI is not the same as the “dpi scale”, to compute the dpi scale you usually have to divide the ppi by 160, so in your case the DPI Scale should be 2.5. It’s possible that the formula we use to place the text is more sophisticated than what I suggested. I can investigate a bit more if the proposed formula doesn’t work.

3 Likes

local DPIScale = 402/160 still does not work.

Make sure the DPIscale is set to 2.5 exactly. The DPIScale factor is rounded to the closest 1/4 interval.

2.5 still does not work.

2 Likes

Hi @boatbomber , please check out this place for a workaround for this issue. There are three main points here: 1. Use textbounds to calculate the real lineHeight instead of using the LineHeight property. 2. Listen to textbounds’s changing signal to render again. 3. Instead of setting labels’ sizes, set the TextYAlignment to top. Please let us know if this works for you. Thank you!

2 Likes

Thanks for following up! It took me a minute to parse out the solution from the demo place because it’s filled with unused prototype/debug code but I think I understand it now. I will attempt to implement this into my systems and report back.

What kills me is that we tried this almost exact solution about a month ago and it didn’t work. The only change is setting TextYAlignment to Top. We were so close :weary:

I’m losing my mind. I just opened up my codebase to implement this and I apparently already did a month ago. My game is working. It absolutely wasn’t a month ago. Was there an engine update??

image

1 Like

Follow up: this solution only deals with the Y axis, but I need to be able to get the position along the X axis as well.

I’ve come up with a solution in a similar vein but it only works on monospace fonts.

local LongestLine = 1
local Lines = string.split(Text, "\n")
for _, line in ipairs(Lines) do
	LongestLine = math.max(LongestLine, #line)
end

local TextSizeX = TextObject.TextBounds.X / LongestLine

-- CurrentDepth = x pos, CurrentLine = y pos
local Position = UDim2.fromScale(
	(CurrentDepth * TextSizeX) / AbsoluteSizeX,
	(LineHeight * (CurrentLine - 1)) / AbsoluteSizeY
)

Thanks for the feedbacks. Glad to hear that Y axis is fixed. So what is the issue and expected behavior along the X axis? Could you please provide a screenshot or, even better, a repro place/file? Is the X axis issue also happening in this place you provided TextLabel overlay mobile bug - Roblox? If so, what devices are issues happening on?

Ah, I think failed to communicate the issue I’m talking about.

There is no misalignment when overlaying using a full line as in your repro place, since the X position of your overlay is 0 and 0 stays the same regardless of DPI. This method works (as far as my testing has found, at least) and covers some of my use cases.

The issue is when attempting to cover a specific word, not an entire line. Since the X position of the word might not be 0 anymore and the HiDPI means that X position is no longer just math.ceil(TextSize/2) * WordDepth the overlay label might not accurately cover the word.

In my earlier reply, I mentioned using a workaround for this that is similar to how we solved the Y axis.

-- Find the longest line
local LongestLine = 1
local Lines = string.split(Text, "\n")
for _, line in ipairs(Lines) do
	LongestLine = math.max(LongestLine, #line)
end

-- Compute the width of a char, assuming monospace font
local TextSizeX = TextObject.TextBounds.X / LongestLine

-- Position the overlay
-- CurrentDepth = x pos, CurrentLine = y pos
local Position = UDim2.fromScale(
	(CurrentDepth * TextSizeX) / AbsoluteSizeX,
	(LineHeight * (CurrentLine - 1)) / AbsoluteSizeY
)

Rather than get an offset pixel value for TextSizeX like on non HiDPI, here we use a Scale value that’s computed from TextBounds.

The problem with this is that it only works on a monospace font- it assumes that each character is evenly spaced so the width of a character would be totalWidth/numChars.

Here’s this method working on Code, a monospace font:

Here we see it fails on a proportional font, Gotham:
image

Note that because our method assumes monospace, it will fail on proportional regardless of DPI.

I have updated the original place with this new version: (which also includes cleaner code and only one script, making it easier to work with)

For some reason I am unable to open the workaround place. I can open the repro from OP, but when I try to edit the workaround place, (Three dots > Edit, is there another way I don’t know of that may work?) it takes ages, with a dialog box telling me it’s loading. It reaches attempt #2 then fails, by which time 2-4 minutes have passed. I get the following error:

The link redirects to #bug-reports:studio-bugs, however I’m not a regular and therefore cannot report bugs (is there some way to do so via Studio or the website for non-regulars?). It seems to affect only this specific place, so is there some sort of setting you can change? If not, no worries, but please could you post the relevant code on here/Github/Pastebin.

Edit:
Thanks for the solution. It hadn’t occurred to me to use a plugin to download the source. For future viewers, you can download the .rbxl file using some chrome extensions, such as BTRoblox which I installed for this. There is also a bug report filed already (I missed it when I looked earlier).

I had the same problem, I just downloaded the place file and edited locally instead.