How can I determine the "maximum texture height" for a given device?

Yup, this could work. Add one word at a time and watch the height, and as soon as it increases back off one word and you have the end of line. Good logic but I’m worried about the complexity of this code and the performance of course. It would be so good to have some improvements to the display.newText() to support this discussion which brings us to your recommendations below…

I agree these are all great suggestions. Need to put the chosen font & font size into the mix as well as these affect overall the text height. Lets hope Corona Labs will be able to improve display.newText() eventually. 

a) system.getInfo(“maxTextureSize”).  It’s always a square.

b) Yes, it is likely the best approach.  The scrollView doesn’t have a dimension limit.  Text is generated into an image.  We call the native font rendering and return a rectangular graphic.  The problem you are running into is if the image is either wider or taller than the device’s maxTextureSize.  You are putting multiple small items in the scrollView so it won’t be a problem.  Now if you tried to capture the whole scrollView to a new texture, you would run into a problem.

c) No, no way that I know of. 

You can safely assume that most devices you are going to likely encounter today has a minimum maxTextureSize of 2048px.  You can do your math accordingly.

Hey Rob, 

Thank you so much for your help. I can certainly live with this solution but it is one more thing to remember to build into a system in order to make it rock solid. It could be well worth it to put this in a tutorial at some point. 

I don’t know how best to do the chopping up of text without coding a trial-error loop… Even in a trial-error loop its a challenge…

The thing is that depending on the device width the text height will differ and hence you will hit the texture limit on a different place. You also can’t put two textObjects’ next to each other unless you cut off one at the end of a line and start the other where the first ended. This way lines vertically match each other. How do I determine where line breaks (wrapping) occur in my text when its given to display.newText() which renders the whole thing graphically. I know what needs to be done but I don’t know how best to do it.

Any ideas on how I can tackle this challenge? Thank you very much for your insight.

Actually I need a tutorial idea for the week.  This one could be a fun one to take on.

Thanks for the idea!

Rob

Hip hip hooray!!! Can’t wait for Tuesday then! I would really really appreciate your excellent tutorial on this topic. Thank you very much. 

You know now that you’re expecting this, Murphy will rear his ugly head and change my plans, right?

I hate Murphy  :slight_smile:

Rob 1 - Murphy 0 …

Thank you very much for today’s tutorial. Great idea to break at paragraphs. I will try it on my app which pulls text from my db and see how things go later when I’m at my dev machine. This is just to say thanks for delivering this tutorial at such short notice.

Regards,
Kerem

Quite welcome!

Rob

This was a great example on simple algorithm design too. Well done. You see my immediate thought was 

  1. get device max texture height

  2. assess text at hand to see if it will exceed max texture height (no simple way to do)

  3. if needed break text into smaller chunks just before it exceeds max texture height but always at the end of a line (no simple way to do)

  4. create display.newText object with text chunks just below max texture height. 

All that was a daunting task and perhaps would end up creating 1 or 2 less display objects vs your method which is 

  1. break text into chunks for each paragraph

  2. display paragraphs

perhaps more objects but certainly less effort in both coding and also at runtime.

Great example in good algo design and thinking. Hats off!  :-)

PS. Some comments on the tutorial code. Would you like the discussion there or should we continue here? Thanks

The blog’s comments don’t handle code well.  Better to have it here.

Rob

Agreed. I’ll repost what I posted there so we can chew this around here. Thanks much once again. 

For some reason the string.match did not work well with my data. It might have even been a copy/paste from the blog. I know sometimes some characters get messed up when you copy/paste from the blog.

Anyways, playing with your idea, I used a slightly less refined way of getting the same result. Sharing below. Not sure why string.match is not working well but this approach is. Will poke around some more to understand this better.

Also note I’m looking for “\r\n” to avoid one extra line inserted in between my paragraphs. In other words, when I looked for \n to define paragraphs I ended up with extra lines in between my actual paragraphs. Changing from “\n” to “\r\n” helped solve that problem.

 repeat local b, e = string.find(tmpString, "\r\n") if b then print(b .. " " .. e) paragraph = string.sub(tmpString, 1, b-1) tmpString = string.sub(tmpString, e+1) else paragraph = tmpString tmpString = "" end options.text = paragraph paragraphs[#paragraphs+1] = display.newText( options ) paragraphs[#paragraphs].anchorX = 0 paragraphs[#paragraphs].anchorY = 0 paragraphs[#paragraphs].x = 10 paragraphs[#paragraphs].y = yOffset paragraphs[#paragraphs]:setFillColor( 0 ) scrollView:insert( paragraphs[#paragraphs] ) yOffset = yOffset + paragraphs[#paragraphs].height print( #paragraphs, paragraph ) until tmpString == nil or string.len( tmpString ) == 0

One question about the two different approaches:

Faced with the texture limit issue, my instinct would also have been to use Ksan’s initial approach of breaking the text into chunks that have a height just below the device’s vertical texture limit, but I agree that Rob’s approach of breaking the text into paragraph chunks is much simpler.

However, with Rob’s approach don’t you run the risk that one paragraph will show as white if paragraph.height is larger than 1024? The size-base approach of Ksan seems failure free, whereas the simpler approach has that one failure point. To prevent the simpler paragraph-based approach from failing on a user’s device, seems like you have only two options:  

  1. trap error then find a “natural” breakpoint in the paragraph: requires that you add a “if paragraph.height > smallestMaxTextureLimit then flag an error or log a message” in your code, where smallestMaxTextureLimit is the smallest value of maxTextureHeight between all devices that you want your app to support, that you ensure that when you test, you visit all parts of your app that use this paragraph-based multi-line text display strategy, that you find that paragraph in your code, read it, that you determine a natural break place which will pass the test (could be difficult), and finally that you and manually break it and re-run the test. This is all because of one or more paragraphs that happened to have greater height than the smallest max texture size limit you want your support. 
  2. alternatively, add a “if paragraph.height > smallestMaxTextureLimit then flag an error or log a message” in your code, but you add code to automatically further subdivide the offending paragraph into two (or more) chunks of lines using Ksan’s max-texture-size-based approach, with a gap between chunks such that the two chunks will still appear to form one paragraph. But this requires implementing the more complex strategy, and then the question is why not use this more complex size-based approach throughout, since that approach cannot fail? 

If you really want to be care-free and not have to worry that some text in your app will be unreadable, your only option is #2. If you don’t care so much that this could happen, or have a really reliable way of exerting the multi-line text of your app so you never miss any during testing, you can use simpler #1

Yes, it’s a risk, but given that most paragraphs are likely going to just be a few lines long, its probably safe.  For any device where the texture limit is that low, 1024 is probably greater than the screen size on the device and I can’t imagine a paragraph being bigger than the screen.   For a 12 point font, that’s that’s over 50 lines of text.

Rob

Oliver, you’re absolutely right. This thought occurred to me as I was implementing Rob’s solution. Thankfully, with the app I am working on now, the paragraphs are short and are more than likely to remain that way but I fully agree in that this is not a 100% solution. 

I think the problem with what I was thinking of doing is that the API is not mature enough to support it yet. I don’t believe we have anyway to determine how tall a block of text will be once its rendered without actually creating the display object. Only then we can check its height. Even then, the situation might be that the text might have overrun maxTextureHeight and might have gotten chopped off and we won’t know. Sadly, display.newText() does not return any results to tell us whether it succeeded displaying all text or whether it failed and managed to show only x characters of the text body. 

So in order to implement a rock solid solution we would need display.newText() to return some results. something like 0 to show 0 errors and full rendering or a number > 0 to show us how many characters it was able to successfully render. I would also suggest it always cuts off at line ends and never mid line. This way we can check this return and if its greater than 0 we can then render the rest of the text in additional display objects. One more feature request to submit I suppose…

Rob, am I missing anything from the scenario above? Is there any other way to pre-determine the height of a text block before it is rendered?

Thank you very much for your kind support.

Regards,

Kerem

You can check the height to see if it’s greater than the maxTextureSize.  If that’s the case, then it failed.  It’s as good a test as us returning something else.

Rob

Ok I see how we can detect it failed but there is no way to know where in the text it failed. I think it makes a lot of sense to return a result. Why not, many other API elements do return results to let us know if they succeeded or not. This appears to be an oversight IMHO.

If the height is less than 2x, maxTextureHeight, then cut the text in half and you will fit.  I guess this could be turned into the following math:

numBlocks = math.ceil( text.height / maxTextureHeight )

Divide your text into “numBlocks” chunks.

Rob

Hmm, that sounds like a good approach to know how many chunks to chop into but how to know where a line ends? Any ways to get that bit? Thanks once again for your ideas. Most appreciated. 

Line ending is the tricky part. Once you have height/maxTextureSize you would have to cut the text at the nearest end of word that is below that ratio, then add one word at a time: every time you add a word, the height will be constant except when “line feed” has occurred, this means the last word should be removed and you have your first chunk.

A number of things would be useful:

  • display.newText() to return how many characters of the given text fit in a given height.
  • display.newText() could return the index of the first character not displayable, or 0 if all fit: obj, err = display.newText(text) would show err=0 unless text didn’t all fit, then err would be the last character that is visible, so from err+1 to end of string is your next chunk and you know the last line is complete. 
  • it should be possible to determine the height of a piece of text for a given width, without creating a Text object, like display.getTextHeight(text).