Author Topic: dynamically detect available # of text rows+columns under a custom screen size?  (Read 4639 times)

0 Members and 1 Guest are viewing this topic.

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
I'm looking for something like the _WIDTH and _HEIGHT functions
https://wiki.qb64.org/wiki/WIDTH_(function)
https://wiki.qb64.org/wiki/HEIGHT
but for text, when the screen is not in text mode.

The docs say _WIDTH and _HEIGHT return the number of characters if the image is in text only mode, else it returns the number of pixels:
Quote
To get the width of the current program screen window use zero for the handle value or nothing: columns& = _WIDTH(0) or columns& = _WIDTH
To get the height of the current program screen window use zero for the handle value or nothing: lines& = _HEIGHT(0) or lines& = _HEIGHT
If the image specified by imageHandle& is in text only(SCREEN 0) mode, the number of characters per row is returned.
If the image specified by imageHandle& is in graphics mode, the number of pixels per row is returned.

The test program below returns the number of pixels.
How would you programmatically determine the number of characters (rows/columns) when the screen is not in text mode, for example Screen _NewImage(1280, 1024, 32) ?

Code: QB64: [Select]
  1. Dim in$
  2. Screen _NewImage(1280, 1024, 32): _ScreenMove 0, 0
  3. PRINT "Showing available text rows/columns of Screen _NewImage(1280, 1024, 32)"
  4. PRINT "_WIDTH(0) = " + _TRIM$(STR$( _WIDTH(0) ))
  5. PRINT "_HEIGHT(0) = " + _TRIM$(STR$( _HEIGHT(0) ))
  6. INPUT "PRESS <ENTER> TO CONTINUE";in$
  7.  

Much appreciated...

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
You can get the width of a string s$ with _PrintWidth(s$) and you can get the the height of a font with _FontHeight(FontHandle&), these are in pixels.

BTW you are usually in default font 16 = height

So you just loaded a font, how many whole rows will fit on the screen?
_Height\_FontHeight(FontHandle&)

First row at 0
next at FontHeight
next at 2*FontHeight
next at 3*FontHeight
...
« Last Edit: January 03, 2022, 05:20:22 pm by bplus »

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Hi @madscijr

In graphical mode text use font (default is _FONT 16), so what you need is _FontHeight, _Fontwidth and _PrintWidth.
How much columns is on graphic screen: _Width \ _FontWidth, for rows _Height \ _FontHeight.
If you use other fonts, use MONOSPACE type.

Let say on screen 640 x 480 and default font it is 640 \ 8 = 80 columns and
                                                                        480 \ 16 = 30 rows

 

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
In graphical mode text use font (default is _FONT 16), so what you need is _FontHeight, _Fontwidth and _PrintWidth.
How much columns is on graphic screen: _Width \ _FontWidth, for rows _Height \ _FontHeight.
Let say on screen 640 x 480 and default font it is 640 \ 8 = 80 columns and 480 \ 16 = 30 rows

That should work, thanks!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
You can get the width of a string s$ with _PrintWidth(s$) and you can get the the height of a font with _FontHeight(FontHandle&), these are in pixels.

Thanks - I have not used _PrintWidth, it seems like it could be useful!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
You can get the width of a string s$ with _PrintWidth(s$) and you can get the the height of a font with _FontHeight(FontHandle&), these are in pixels.
So you just loaded a font, how many whole rows will fit on the screen?
_Height\_FontHeight(FontHandle&)
In graphical mode text use font (default is _FONT 16), so what you need is _FontHeight, _Fontwidth and _PrintWidth.
How much columns is on graphic screen: _Width \ _FontWidth, for rows _Height \ _FontHeight.

OK, I have a followup question.

I wrote a test program that computes the expected number of text columns and rows for various resolutions,
and for each tries to print that amount of columns/rows to the screen.

However, for certain resolutions it isn't working as expected:
  • at 800x600 we expect 100 columns, 38 rows. When the program tries to print the last row, there is an error #5 at "LOCATE 38,1".
  • at 1920x1080 we expect 248 columns, 68 rows. When the program tries to print the last row, there is an error #5 at "LOCATE 68,1".
  • at 2048x1152 we expect 256 columns, 72 rows. No errors, but only 70 rows and 255 columns fit on the screen (row 71 and column 256 are mostly cut off).
  • at 3840x2160 we expect 480 columns, 135 rows. No errors, but only 133 rows and 479 columns fit on the screen (row 134 and column 134 are mostly cut off).

What gives?
Why does it throw an error for some resolutions but for others simply truncates the rows?
I am seeing the same results in both windowed and full screen modes.

My test program is below if you want to try replicating my results.
It catches errors on the LOCATE so it won't blow up.

Code: QB64: [Select]
  1. ' Text rows/columns per screen resolution test
  2. ' Version 1.0 by madscijr
  3.  
  4. ' boolean constants:
  5. Const FALSE = 0
  6. Const TRUE = Not FALSE
  7.  
  8. ' global variables
  9. Dim Shared m_ProgramPath$: m_ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
  10. Dim Shared m_ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
  11. Dim Shared m_bTesting As Integer: m_bTesting = FALSE
  12. Dim Shared m_sCode As String: m_sCode = ""
  13. Dim Shared m_sError As String: m_sError = ""
  14.  
  15. ' LOCAL VARIABLES
  16. Dim in$
  17.  
  18. ' ****************************************************************************************************************************************************************
  19. ' ACTIVATE DEBUGGING WINDOW
  20. If m_bTesting = TRUE Then
  21.     $Console
  22.     _Delay 4
  23.     _Console On
  24.     _Echo "Started " + m_ProgramName$
  25.     _Echo "Debugging on..."
  26. ' ****************************************************************************************************************************************************************
  27.  
  28. ' =============================================================================
  29. ' START THE MAIN ROUTINE
  30. main
  31.  
  32. ' =============================================================================
  33. ' FINISH
  34. Print m_ProgramName$ + " finished."
  35. Input "Press <ENTER> to continue", in$
  36.  
  37. ' ****************************************************************************************************************************************************************
  38. ' DEACTIVATE DEBUGGING WINDOW
  39. If m_bTesting = TRUE Then
  40. ' ****************************************************************************************************************************************************************
  41.  
  42. System ' return control to the operating system
  43.  
  44. ' /////////////////////////////////////////////////////////////////////////////
  45.  
  46. ErrHandler:
  47. m_sError = "Error #" + _Trim$(Str$(Err)) + " at line " + _Trim$(Str$(_ErrorLine)) + ": " + m_sCode
  48.  
  49. ' /////////////////////////////////////////////////////////////////////////////
  50.  
  51. Sub main
  52.     Dim RoutineName As String: RoutineName = "main"
  53.     Dim in$
  54.     Dim result$
  55.  
  56.     Screen 0
  57.     Do
  58.         Cls
  59.         Print m_ProgramName$
  60.         Print
  61.         Print "Screen resolution tests"
  62.         Print
  63.         Print "1. Count text width/height (windowed)"
  64.         Print "2. Count text width/height (full screen)"
  65.         Print
  66.         Print "What to do? ('q' to exit)"
  67.  
  68.         Input in$: in$ = LCase$(Left$(in$, 1))
  69.  
  70.         If in$ = "1" Then
  71.             result$ = GetTextWidthHeight$(FALSE)
  72.         ElseIf in$ = "2" Then
  73.             result$ = GetTextWidthHeight$(TRUE)
  74.         End If
  75.  
  76.         If Len(result$) > 0 Then
  77.             Print result$
  78.         End If
  79.  
  80.     Loop Until in$ = "q"
  81. End Sub ' main
  82.  
  83. ' /////////////////////////////////////////////////////////////////////////////
  84. ' dynamically detect available # of text rows+columns under a custom screen size?
  85. ' https://qb64forum.alephc.xyz/index.php?topic=4550.0
  86.  
  87. ' Petr:
  88. ' In graphical mode text use font (default is _FONT 16),
  89. ' so what you need is _FontHeight, _FontWidth and _PrintWidth.
  90. ' How much columns is on graphic screen:
  91. ' _Width \ _FontWidth, for rows _Height \ _FontHeight.
  92. ' If you use other fonts, use MONOSPACE type.
  93. ' Let say on screen 640 x 480 and default font
  94. ' it is 640 \ 8 = 80 columns and 480 \ 16 = 30 rows
  95.  
  96. ' GetNextTextWidthHeight$  640,  480, "VGA (4:3)"
  97. ' GetNextTextWidthHeight$  720,  480, "480p (4:3, 16:9)"
  98. ' GetNextTextWidthHeight$  800,  600, "SVGA (4:3)"
  99. ' GetNextTextWidthHeight$ 1024,  768, "XGA (4:3)"
  100. ' GetNextTextWidthHeight$ 1280, 1024, "SXGA (5:4)"
  101. ' GetNextTextWidthHeight$ 1920, 1080, "FHD (16:9)"
  102. ' GetNextTextWidthHeight$ 2048, 1152, "QWXGA (16:9)"
  103. ' GetNextTextWidthHeight$ 3840, 2160, "4K UHD (16:9)"
  104. ' GetNextTextWidthHeight$ 7680, 4320, "8K UHD (16:9)"
  105.  
  106. Function GetNextTextWidthHeight$ (iScreenWidth As Integer, iScreenHeight As Integer, sDescription As String)
  107.     Dim RoutineName As String: RoutineName = ""
  108.  
  109.     ' currently set up to not try to change screen resolution to less than VGA or bigger than 4k
  110.     Dim iMinWidth As Integer: iMinWidth = 640
  111.     Dim iMaxWidth As Integer: iMaxWidth = 3840
  112.     Dim iMinHeight As Integer: iMinHeight = 480
  113.     Dim iMaxHeight As Integer: iMaxHeight = 2160
  114.  
  115.     Dim in$
  116.     Dim iCols As Integer
  117.     Dim iRows As Integer
  118.     Dim iLoopRows As Integer
  119.     Dim iLoopCols As Integer
  120.     Dim iCount As Integer
  121.     Dim sResult As String: sResult = ""
  122.     Dim bExit As Integer: bExit = FALSE
  123.  
  124.     ' ONLY TEST FOR ALLOWED RESOLUTIONS
  125.     If (iScreenWidth >= iMinWidth) And (iScreenWidth <= iMaxWidth) And (iScreenHeight >= iMinHeight) And (iScreenHeight <= iMaxHeight) Then
  126.         Do
  127.             ' =============================================================================
  128.             ' SHOW RESOLUTION AND EXPECTED # OF CHARACTERS TO FIT
  129.             Screen 0
  130.             Cls
  131.  
  132.             'PRINT "Screen size:"
  133.             'PRINT "    _WIDTH(0)   = " + _TRIM$(STR$( _WIDTH(0) ))
  134.             'PRINT "    _HEIGHT(0)  = " + _TRIM$(STR$( _HEIGHT(0) ))
  135.             'Print "Font size:"
  136.             'Print "    _FontWidth  = " + _Trim$(Str$(_FontWidth))
  137.             'Print "    _FontHeight = " + _Trim$(Str$(_FontHeight))
  138.             ' To measure actual available columns/rows:
  139.             'iCols = _WIDTH(0) / _FontWidth
  140.             'iRows = _HEIGHT(0) / _FontHeight
  141.             'PRINT "Current characters per screen:"
  142.             'PRINT "    # columns = _WIDTH(0)  / _FontWidth  = " + _TRIM$(STR$( _WIDTH(0) )) + " / " + _TRIM$(STR$(_FontWidth)) + " = " + _TRIM$(STR$(iCols))
  143.             'PRINT "    #    rows = _HEIGHT(0) / _FontHeight = " + _TRIM$(STR$( _HEIGHT(0) )) + " / " + _TRIM$(STR$(_FontHeight)) + " = " + _TRIM$(STR$(iRows))
  144.  
  145.             ' Calculate for a hypothetical resolution:
  146.             iCols = iScreenWidth / _FontWidth
  147.             iRows = iScreenHeight / _FontHeight
  148.  
  149.             Print "Screen resolution: " + _Trim$(Str$(iScreenWidth)) + "x" + _Trim$(Str$(iScreenHeight)) + " " + sDescription
  150.             Print "Font size        : " + _Trim$(Str$(_FontWidth)) + "x" + _Trim$(Str$(_FontHeight))
  151.             Print "Text cols x rows : " + _Trim$(Str$(iCols)) + "x" + _Trim$(Str$(iRows))
  152.             Print
  153.             Print "Options"
  154.             Print "-------"
  155.             Print "R     test rows"
  156.             Print "C     test columns"
  157.             Print "ENTER continue to next resolution"
  158.             Print "Q     quit testing resolutions"
  159.             Print
  160.             Input "What to do? "; in$
  161.             in$ = Left$(LCase$(_Trim$(in$)), 1)
  162.  
  163.             ' -----------------------------------------------------------------------------
  164.             ' TEST TO SEE IF THE # OF ROWS WE EXPECT REALLY DO FIT ON THE SCREEN
  165.             If in$ = "r" Then
  166.                 Screen _NewImage(iScreenWidth, iScreenHeight, 32): _ScreenMove 0, 0
  167.                 Cls
  168.                 For iLoopRows = 1 To iRows
  169.                     On Error GoTo ErrHandler
  170.                     m_sCode = "LOCATE " + cstr$(iLoopRows) + ", 1"
  171.                     Locate iLoopRows, 1
  172.                     On Error GoTo 0
  173.  
  174.                     If Len(m_sError) = 0 Then
  175.                         Print cstr$(iLoopRows);
  176.                     Else
  177.                         Do
  178.                             Locate 1, 1: Print String$(iCols, " ");
  179.                             Locate 2, 1: Print String$(iCols, " ");
  180.                             Locate 3, 1: Print String$(iCols, " ");
  181.                             Locate 1, 1: Print m_sError
  182.                             Locate 2, 1: Input "Type y to continue or n to exit this resolution"; in$
  183.                             in$ = Left$(LCase$(_Trim$(in$)), 1)
  184.                             If in$ = "n" Then
  185.                                 bExit = TRUE
  186.                                 Exit Do
  187.                             ElseIf in$ = "y" Then
  188.                                 Exit Do
  189.                             End If
  190.                         Loop
  191.                     End If
  192.  
  193.                     If bExit = TRUE Then
  194.                         bExit = FALSE
  195.                         Exit For
  196.                     End If
  197.                 Next iLoopRows
  198.  
  199.                 If in$ = "y" Then
  200.                     m_sError = "": m_sCode = ""
  201.                 ElseIf in$ <> "y" And in$ <> "n" Then
  202.                     Locate 1, 10
  203.                     Print _Trim$(Str$(iRows)) + " rows expected at resolution " + cstr$(iScreenWidth) + "x" + cstr$(iScreenHeight)
  204.                     Locate 2, 10
  205.                     Input "Press <ENTER> to continue"; in$
  206.                 End If
  207.  
  208.                 ' -----------------------------------------------------------------------------
  209.                 ' TEST TO SEE IF THE # OF COLUMNS WE EXPECT REALLY DO FIT ON THE SCREEN
  210.             ElseIf in$ = "c" Then
  211.                 Screen _NewImage(iScreenWidth, iScreenHeight, 32): _ScreenMove 0, 0
  212.                 Cls
  213.                 Print _Trim$(Str$(iCols)) + " columns expected at resolution " + cstr$(iScreenWidth) + "x" + cstr$(iScreenHeight)
  214.                 Print
  215.  
  216.                 ' print hundreds
  217.                 iCount = 0
  218.                 For iLoopCols = 1 To iCols
  219.                     iCount = iCount + 1
  220.                     If iLoopCols < iCols Then
  221.                         Print Left$(Right$("   " + cstr$(iCount), 3), 1);
  222.                     Else
  223.                         Print Left$(Right$("   " + cstr$(iCount), 3), 1)
  224.                     End If
  225.                 Next iLoopCols
  226.  
  227.                 ' print tens
  228.                 iCount = 0
  229.                 For iLoopCols = 1 To iCols
  230.                     iCount = iCount + 1
  231.                     If iLoopCols < iCols Then
  232.                         Print Mid$(Right$("   " + cstr$(iCount), 3), 2, 1);
  233.                     Else
  234.                         Print Mid$(Right$("   " + cstr$(iCount), 3), 2, 1)
  235.                     End If
  236.                 Next iLoopCols
  237.  
  238.                 ' print ones
  239.                 iCount = 0
  240.                 For iLoopCols = 1 To iCols
  241.                     iCount = iCount + 1
  242.                     If iLoopCols < iCols Then
  243.                         Print Right$(cstr$(iCount), 1);
  244.                     Else
  245.                         Print Right$(cstr$(iCount), 1)
  246.                     End If
  247.                 Next iLoopCols
  248.  
  249.                 Print
  250.                 Input "Press <ENTER> to continue"; in$
  251.  
  252.                 ' -----------------------------------------------------------------------------
  253.                 ' EXIT + MOVE ON TO THE NEXT RESOLUTION
  254.             Else
  255.                 Exit Do
  256.             End If
  257.  
  258.             If Len(m_sError) > 0 Then
  259.                 m_sError = ""
  260.                 m_sCode = ""
  261.                 Exit Do
  262.             End If
  263.         Loop
  264.     Else
  265.         ' ASK THE USER IF THEY WANT TO MOVE ON TO THE NEXT RESOLUTION OR QUIT
  266.         Input "Q to quit, or <ENTER> to continue to next resolution"; in$
  267.         in$ = Left$(LCase$(_Trim$(in$)), 1)
  268.     End If
  269.  
  270.     If in$ = "q" Then
  271.         sResult = "q"
  272.     End If
  273.  
  274.     CleanupAndExit:
  275.     Screen 0
  276.     GetNextTextWidthHeight$ = sResult
  277.  
  278. End Function ' GetNextTextWidthHeight$
  279.  
  280. ' /////////////////////////////////////////////////////////////////////////////
  281.  
  282. Function GetTextWidthHeight$ (bFullScreen As Integer)
  283.     Dim sResult As String: sResult = ""
  284.  
  285.     If bFullScreen = TRUE Then
  286.     End If
  287.  
  288.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(640, 480, "VGA (4:3)")
  289.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(720, 480, "480p (4:3, 16:9)")
  290.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(800, 600, "SVGA (4:3)")
  291.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(1024, 768, "XGA (4:3)")
  292.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(1280, 1024, "SXGA (5:4)")
  293.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(1920, 1080, "FHD (16:9)")
  294.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(2048, 1152, "QWXGA (16:9)")
  295.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(3840, 2160, "4K UHD (16:9)")
  296.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(7680, 4320, "8K UHD (16:9)")
  297.  
  298.     If bFullScreen = TRUE Then
  299.         _FullScreen _Off
  300.     End If
  301.  
  302.     GetTextWidthHeight$ = ""
  303. End Function ' GetTextWidthHeight$
  304.  
  305.  
  306. ' /////////////////////////////////////////////////////////////////////////////
  307. ' Convert a value to string and trim it (because normal Str$ adds spaces)
  308.  
  309. Function cstr$ (myValue)
  310.     'cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
  311.     cstr$ = _Trim$(Str$(myValue))
  312. End Function ' cstr$
  313.  
  314. ' /////////////////////////////////////////////////////////////////////////////
  315.  
  316. Sub DebugPrint (s$)
  317.     If m_bTesting = TRUE Then
  318.         _Echo s$
  319.     End If
  320. End Sub ' DebugPrint
  321.  
  322.  

« Last Edit: January 04, 2022, 01:18:24 pm by madscijr »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
I will take on only one issue you mention @madscijr

Quote
at 800x600 we expect 100 columns, 38 rows. When the program tries to print the last row, there is an error #5 at "LOCATE 38,1".

600 /16 = 37.5 so you only have 37 whole rows.

Code: QB64: [Select]
  1. Screen _NewImage(800, 600, 32)
  2. _PrintString (0, 37 * 16), "hello world"
  3.  
  4. Print " see .5 hello world at bottom?  oh but! the first row is row 0 *16"
  5. Print " so you go from 0 * 16 to 36 * 16  rows, press any"
  6. Print "rows go from 0 to 36 = 37 rows"
  7. _PrintString (0, 36 * 16), "hello world, press any to finish"
  8.  

Quote
_Height\_FontHeight(FontHandle&)
Notice in your quote of me, I used a  \  not / that (the former \) is Integer Divide not float divide (the later /).

I've been advised and I pass it on to you, get in habit of _PrintString it wont give you errors that Locate does PLUS much more freedom on graphics screen to print text anywhere!
« Last Edit: January 04, 2022, 01:48:01 pm by bplus »

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
I will take on only one issue you mention @madscijr
...
Notice in your quote of me, I used a  \  not / that (the former \) is Integer Divide not float divide (the later /).

Aha... I was erroneously doing regular division and not integer division. My bad!
Thanks for pointing that out.

I've been advised and I pass it on to you, get in habit of _PrintString it wont give you errors that Locate does PLUS much more freedom on graphics screen to print text anywhere!

Thanks for the tip!
I see it's pretty straightforward, you just need to calculate the x,y based on font width/height.
Code: QB64: [Select]
  1. x% = _FontWidth * (col% - 1)
  2. y% = _FontHeight * (row% - 1)
  3. _PRINTSTRING (x%, y%), mystring$
  4.  

So with that in place (updated code below), I am still seeing the last couple of rows/columns truncated for these resolutions
  • at 2048x1152 we expect 256 columns, 72 rows. No errors, but only 70 rows and 255 columns fit on the screen (row 71 and column 256 are mostly cut off).
  • at 3840x2160 we expect 480 columns, 135 rows. No errors, but only 133 rows and 479 columns fit on the screen (row 134 and column 134 are mostly cut off).

Any thoughts welcome, and thanks again!

Code: QB64: [Select]
  1. ' Text rows/columns per screen resolution test
  2. ' Version 1.03 by madscijr
  3.  
  4. ' boolean constants:
  5. Const FALSE = 0
  6. Const TRUE = Not FALSE
  7.  
  8. ' global variables
  9. Dim Shared m_ProgramPath$: m_ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
  10. Dim Shared m_ProgramName$: m_ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
  11. Dim Shared m_bTesting As Integer: m_bTesting = FALSE
  12. Dim Shared m_sCode As String: m_sCode = ""
  13. Dim Shared m_sError As String: m_sError = ""
  14.  
  15. ' LOCAL VARIABLES
  16. Dim in$
  17.  
  18. ' ****************************************************************************************************************************************************************
  19. ' ACTIVATE DEBUGGING WINDOW
  20. If m_bTesting = TRUE Then
  21.     $Console
  22.     _Delay 4
  23.     _Console On
  24.     _Echo "Started " + m_ProgramName$
  25.     _Echo "Debugging on..."
  26. ' ****************************************************************************************************************************************************************
  27.  
  28. ' =============================================================================
  29. ' START THE MAIN ROUTINE
  30. main
  31.  
  32. ' =============================================================================
  33. ' FINISH
  34. Print m_ProgramName$ + " finished."
  35. Input "Press <ENTER> to continue", in$
  36.  
  37. ' ****************************************************************************************************************************************************************
  38. ' DEACTIVATE DEBUGGING WINDOW
  39. If m_bTesting = TRUE Then
  40. ' ****************************************************************************************************************************************************************
  41.  
  42. System ' return control to the operating system
  43.  
  44. ' /////////////////////////////////////////////////////////////////////////////
  45.  
  46. ErrHandler:
  47. m_sError = "Error #" + _Trim$(Str$(Err)) + " at line " + _Trim$(Str$(_ErrorLine)) + ": " + m_sCode
  48.  
  49. ' /////////////////////////////////////////////////////////////////////////////
  50.  
  51. Sub main
  52.     Dim RoutineName As String: RoutineName = "main"
  53.     Dim in$
  54.     Dim result$
  55.  
  56.     Screen 0
  57.     Do
  58.         Cls
  59.         Print m_ProgramName$
  60.         Print
  61.         Print "Screen resolution tests"
  62.         Print
  63.         Print "1. Count text width/height (windowed)"
  64.         Print "2. Count text width/height (full screen)"
  65.         Print
  66.         Print "What to do? ('q' to exit)"
  67.  
  68.         Input in$: in$ = LCase$(Left$(in$, 1))
  69.  
  70.         If in$ = "1" Then
  71.             result$ = GetTextWidthHeight$(FALSE)
  72.         ElseIf in$ = "2" Then
  73.             result$ = GetTextWidthHeight$(TRUE)
  74.         End If
  75.  
  76.         If Len(result$) > 0 Then
  77.             Print result$
  78.         End If
  79.  
  80.     Loop Until in$ = "q"
  81. End Sub ' main
  82.  
  83. ' /////////////////////////////////////////////////////////////////////////////
  84. ' dynamically detect available # of text rows+columns under a custom screen size?
  85. ' https://qb64forum.alephc.xyz/index.php?topic=4550.0
  86.  
  87. ' Petr:
  88. ' In graphical mode text use font (default is _FONT 16),
  89. ' so what you need is _FontHeight, _FontWidth and _PrintWidth.
  90. ' How much columns is on graphic screen:
  91. ' _Width \ _FontWidth, for rows _Height \ _FontHeight.
  92. ' If you use other fonts, use MONOSPACE type.
  93. ' Let say on screen 640 x 480 and default font
  94. ' it is 640 \ 8 = 80 columns and 480 \ 16 = 30 rows
  95.  
  96. ' GetNextTextWidthHeight$  640,  480, "VGA (4:3)"
  97. ' GetNextTextWidthHeight$  720,  480, "480p (4:3, 16:9)"
  98. ' GetNextTextWidthHeight$  800,  600, "SVGA (4:3)"
  99. ' GetNextTextWidthHeight$ 1024,  768, "XGA (4:3)"
  100. ' GetNextTextWidthHeight$ 1280, 1024, "SXGA (5:4)"
  101. ' GetNextTextWidthHeight$ 1920, 1080, "FHD (16:9)"
  102. ' GetNextTextWidthHeight$ 2048, 1152, "QWXGA (16:9)"
  103. ' GetNextTextWidthHeight$ 3840, 2160, "4K UHD (16:9)"
  104. ' GetNextTextWidthHeight$ 7680, 4320, "8K UHD (16:9)"
  105.  
  106. Function GetNextTextWidthHeight$ (iScreenWidth As Integer, iScreenHeight As Integer, sDescription As String)
  107.     Dim RoutineName As String: RoutineName = ""
  108.  
  109.     ' currently set up to not try to change screen resolution to less than VGA or bigger than 4k
  110.     Dim iMinWidth As Integer: iMinWidth = 640
  111.     Dim iMaxWidth As Integer: iMaxWidth = 3840
  112.     Dim iMinHeight As Integer: iMinHeight = 480
  113.     Dim iMaxHeight As Integer: iMaxHeight = 2160
  114.  
  115.     Dim in$
  116.     Dim iCols As Integer
  117.     Dim iRows As Integer
  118.     Dim iLoopRows As Integer
  119.     Dim iLoopCols As Integer
  120.     Dim iCount As Integer
  121.     Dim sResult As String: sResult = ""
  122.     Dim bExit As Integer: bExit = FALSE
  123.     Dim iX As Integer
  124.     Dim iY As Integer
  125.  
  126.     ' ONLY TEST FOR ALLOWED RESOLUTIONS
  127.     If (iScreenWidth >= iMinWidth) And (iScreenWidth <= iMaxWidth) And (iScreenHeight >= iMinHeight) And (iScreenHeight <= iMaxHeight) Then
  128.         Do
  129.             ' =============================================================================
  130.             ' SHOW RESOLUTION AND EXPECTED # OF CHARACTERS TO FIT
  131.             Screen 0
  132.             Cls
  133.  
  134.             'PRINT "Screen size:"
  135.             'PRINT "    _WIDTH(0)   = " + _TRIM$(STR$( _WIDTH(0) ))
  136.             'PRINT "    _HEIGHT(0)  = " + _TRIM$(STR$( _HEIGHT(0) ))
  137.             'Print "Font size:"
  138.             'Print "    _FontWidth  = " + _Trim$(Str$(_FontWidth))
  139.             'Print "    _FontHeight = " + _Trim$(Str$(_FontHeight))
  140.             ' To measure actual available columns/rows:
  141.             'iCols = _WIDTH(0) \ _FontWidth
  142.             'iRows = _HEIGHT(0) \ _FontHeight
  143.             'PRINT "Current characters per screen:"
  144.             'PRINT "    # columns = _WIDTH(0)  \ _FontWidth  = " + _TRIM$(STR$( _WIDTH(0) )) + " \ " + _TRIM$(STR$(_FontWidth)) + " = " + _TRIM$(STR$(iCols))
  145.             'PRINT "    #    rows = _HEIGHT(0) \ _FontHeight = " + _TRIM$(STR$( _HEIGHT(0) )) + " \ " + _TRIM$(STR$(_FontHeight)) + " = " + _TRIM$(STR$(iRows))
  146.  
  147.             ' Calculate for a hypothetical resolution: (thanks bplus and petr!)
  148.             'iCols = iScreenWidth \ _FontWidth
  149.             'iRows = iScreenHeight \ _FontHeight
  150.             iCols = iScreenWidth \ _FontWidth
  151.             iRows = iScreenHeight \ _FontHeight
  152.  
  153.             Print "Screen resolution: " + _Trim$(Str$(iScreenWidth)) + "x" + _Trim$(Str$(iScreenHeight)) + " " + sDescription
  154.             Print "Font size        : " + _Trim$(Str$(_FontWidth)) + "x" + _Trim$(Str$(_FontHeight))
  155.             Print "Text cols x rows : " + _Trim$(Str$(iCols)) + "x" + _Trim$(Str$(iRows))
  156.             Print
  157.             Print "Options"
  158.             Print "-------"
  159.             Print "R     test rows"
  160.             Print "C     test columns"
  161.             Print "ENTER continue to next resolution"
  162.             Print "Q     quit testing resolutions"
  163.             Print
  164.             Input "What to do? "; in$
  165.             in$ = Left$(LCase$(_Trim$(in$)), 1)
  166.  
  167.             ' -----------------------------------------------------------------------------
  168.             ' TEST TO SEE IF THE # OF ROWS WE EXPECT REALLY DO FIT ON THE SCREEN
  169.             If in$ = "r" Then
  170.                 Screen _NewImage(iScreenWidth, iScreenHeight, 32): _ScreenMove 0, 0
  171.                 Cls
  172.                 For iLoopRows = 1 To iRows
  173.                     iX = 0 ' _FontWidth * (iLoopCols - 1)
  174.                     iY = _FontHeight * (iLoopRows - 1)
  175.                     _PrintString (iX, iY), cstr$(iLoopRows)
  176.                 Next iLoopRows
  177.  
  178.                 Locate 1, 10
  179.                 Print _Trim$(Str$(iRows)) + " rows expected at resolution " + cstr$(iScreenWidth) + "x" + cstr$(iScreenHeight)
  180.                 Locate 2, 10
  181.                 Input "Press <ENTER> to continue"; in$
  182.  
  183.                 ' -----------------------------------------------------------------------------
  184.                 ' TEST TO SEE IF THE # OF COLUMNS WE EXPECT REALLY DO FIT ON THE SCREEN
  185.             ElseIf in$ = "c" Then
  186.                 Screen _NewImage(iScreenWidth, iScreenHeight, 32): _ScreenMove 0, 0
  187.                 Cls
  188.                 Print _Trim$(Str$(iCols)) + " columns expected at resolution " + cstr$(iScreenWidth) + "x" + cstr$(iScreenHeight)
  189.                 Print
  190.  
  191.                 ' print hundreds
  192.                 iCount = 0
  193.                 For iLoopCols = 1 To iCols
  194.                     iCount = iCount + 1
  195.                     If iLoopCols < iCols Then
  196.                         Print Left$(Right$("   " + cstr$(iCount), 3), 1);
  197.                     Else
  198.                         Print Left$(Right$("   " + cstr$(iCount), 3), 1)
  199.                     End If
  200.                 Next iLoopCols
  201.  
  202.                 ' print tens
  203.                 iCount = 0
  204.                 For iLoopCols = 1 To iCols
  205.                     iCount = iCount + 1
  206.                     If iLoopCols < iCols Then
  207.                         Print Mid$(Right$("   " + cstr$(iCount), 3), 2, 1);
  208.                     Else
  209.                         Print Mid$(Right$("   " + cstr$(iCount), 3), 2, 1)
  210.                     End If
  211.                 Next iLoopCols
  212.  
  213.                 ' print ones
  214.                 iCount = 0
  215.                 For iLoopCols = 1 To iCols
  216.                     iCount = iCount + 1
  217.                     If iLoopCols < iCols Then
  218.                         Print Right$(cstr$(iCount), 1);
  219.                     Else
  220.                         Print Right$(cstr$(iCount), 1)
  221.                     End If
  222.                 Next iLoopCols
  223.  
  224.                 Print
  225.                 Input "Press <ENTER> to continue"; in$
  226.  
  227.                 ' -----------------------------------------------------------------------------
  228.                 ' EXIT + MOVE ON TO THE NEXT RESOLUTION
  229.             Else
  230.                 Exit Do
  231.             End If
  232.  
  233.             If Len(m_sError) > 0 Then
  234.                 m_sError = ""
  235.                 m_sCode = ""
  236.                 Exit Do
  237.             End If
  238.         Loop
  239.     Else
  240.         ' ASK THE USER IF THEY WANT TO MOVE ON TO THE NEXT RESOLUTION OR QUIT
  241.         Input "Q to quit, or <ENTER> to continue to next resolution"; in$
  242.         in$ = Left$(LCase$(_Trim$(in$)), 1)
  243.     End If
  244.  
  245.     If in$ = "q" Then
  246.         sResult = "q"
  247.     End If
  248.  
  249.     CleanupAndExit:
  250.     Screen 0
  251.     GetNextTextWidthHeight$ = sResult
  252.  
  253. End Function ' GetNextTextWidthHeight$
  254.  
  255. ' /////////////////////////////////////////////////////////////////////////////
  256.  
  257. Function GetTextWidthHeight$ (bFullScreen As Integer)
  258.     Dim sResult As String: sResult = ""
  259.  
  260.     If bFullScreen = TRUE Then
  261.     End If
  262.  
  263.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(640, 480, "VGA (4:3)")
  264.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(720, 480, "480p (4:3, 16:9)")
  265.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(800, 600, "SVGA (4:3)")
  266.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(1024, 768, "XGA (4:3)")
  267.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(1280, 1024, "SXGA (5:4)")
  268.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(1920, 1080, "FHD (16:9)")
  269.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(2048, 1152, "QWXGA (16:9)")
  270.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(3840, 2160, "4K UHD (16:9)")
  271.     If sResult <> "q" Then sResult = GetNextTextWidthHeight$(7680, 4320, "8K UHD (16:9)")
  272.  
  273.     If bFullScreen = TRUE Then
  274.         _FullScreen _Off
  275.     End If
  276.  
  277.     GetTextWidthHeight$ = ""
  278. End Function ' GetTextWidthHeight$
  279.  
  280.  
  281. ' /////////////////////////////////////////////////////////////////////////////
  282. ' Convert a value to string and trim it (because normal Str$ adds spaces)
  283.  
  284. Function cstr$ (myValue)
  285.     'cstr$ = LTRIM$(RTRIM$(STR$(myValue)))
  286.     cstr$ = _Trim$(Str$(myValue))
  287. End Function ' cstr$
  288.  
  289. ' /////////////////////////////////////////////////////////////////////////////
  290.  
  291. Sub DebugPrint (s$)
  292.     If m_bTesting = TRUE Then
  293.         _Echo s$
  294.     End If
  295. End Sub ' DebugPrint
  296.  
  297.  

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
OK remember in graphics screen rows and cols start at 0 (not 1 as used for Locate).

So if there are 480 cols they go from col# 0 to col 479# in text cells

likewise if there are 135 rows they will start at row#0 and go to row# 135-1=134

and with _PrintString you will have to mult col# by 8 and row# by 16 for default font.

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
OK remember in graphics screen rows and cols start at 0 (not 1 as used for Locate).

So if there are 480 cols they go from col# 0 to col 479# in text cells

likewise if there are 135 rows they will start at row#0 and go to row# 135-1=134

and with _PrintString you will have to mult col# by 8 and row# by 16 for default font.

I have the rows / cols starting at 0 with _PrintString, it's just strange how the last couple rows/columsns get truncated at the higher resolutions... No 8k display here yet, so I can't test that one, lol

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
I have the rows / cols starting at 0 with _PrintString, it's just strange how the last couple rows/columsns get truncated at the higher resolutions... No 8k display here yet, so I can't test that one, lol

They get truncated due to your screen resolution and font size.

For example, let's create a screen 1280 pixels wide.   Now, let's load a font that is 12 pixels wide.

1280 / 12 = 106 columns of characters.

Of that 106, we have a can print a full set of text starting at pixel 0, and going to pixel 1260.  This is column 0 to column 105 -- which is our 106 columns of full character displaying.

Now, printing on column 105 puts a character from pixel 1260 to 1271.  Our screen is 1280 pixels wide...  We have 8 pixels to the right of the screen that we can't print a full character on.

IF, however, we want to print at column 106 -- which is beyond the bounds of our calculations. (remember, 0 TO 105 is 106 characters, so when we calculate WIDE / _FONTWIDTH, we start our column count with BASE 0 and not BASE 1.) 

106 * 12 = 1272, which is where we start to print a 12 pixel wide character onto the screen, but have to stop due to the screen being only 1280 pixels total.  We only put 8 pixels to the screen (we stop at 1279, instead of 1280 once again, thanks to BASE 0 and not BASE 1 counting.  0 to 1279 *IS* a 1280-pixel wide screen), so the most you can toss is 2/3 of a character onto the screen.

If you don't want that 2/3 of a character cut-off, then leave those last 8 pixels blank as a border for your text and ONLY print from column 0 to column 105 -- which is the 106 columns you calculated with WIDTH \ FONTWIDTH.

The cutoff you keep talking about is coming from printing from 0 to width\fontwidth, instead of from 0 to width\fontwidth -1 to offset for BASE 0 calculations.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
So with that in place (updated code below), I am still seeing the last couple of rows/columns truncated for these resolutions
  • at 2048x1152 we expect 256 columns, 72 rows. No errors, but only 70 rows and 255 columns fit on the screen (row 71 and column 256 are mostly cut off).
  • at 3840x2160 we expect 480 columns, 135 rows. No errors, but only 133 rows and 479 columns fit on the screen (row 134 and column 134 are mostly cut off).

Here's a little program to show that the screen is indeed those dimensions:

Code: QB64: [Select]
  1. 'at 2048x1152 we expect 256 columns, 72 rows. No errors, but only 70 rows and 255 columns fit on the screen
  2. '    (row 71 and column 256 are mostly cut off).
  3.  
  4.  
  5. Screen _NewImage(2048, 1152, 256)
  6.  
  7. For column = 1 To 256
  8.     Color column Mod 100 + 1
  9.     Locate 1, column: Print _Trim$(Str$(column \ 100));
  10.     Locate 2, column:: Print _Trim$(Str$(column \ 10 Mod 10));
  11.     Locate 3, column:: Print _Trim$(Str$(column Mod 10));
  12.  
  13. For row = 1 To 72
  14.     Locate row, 1: Print "ROW:"; row;

As you can tell, there's no cut off with either row or column, as long as we print to starting from LOCATE position 1,1 to 72,256.

Now, if we if we going to use _PRINTSTING instead, we'd print from position(row-1, column-1) to ((72-1) * _Fontheight, (256-1) * _fontwidth), to print from 0 * fontheight,0 * fontwidth to 71 * fontheight,255 * fontwidth.

Printstring starts at 0,0.  Locate starts at 1,1.  You have to account for the difference in your starting position when swapping from locate to printstring.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
OK remember in graphics screen rows and cols start at 0 (not 1 as used for Locate).
As you can tell, there's no cut off with either row or column, as long as we print to starting from LOCATE position 1,1 to 72,256.

I just wanted to post a followup to let you know I figured out the issue I was having, in case anyone runs into this in the future.
There were two things causing what I was seeing
  • Running in windowed mode for the highest resolution for my monitor, the rows were appearing cut off because (duh) the title bar of the window was taking the top n pixels. Running _FullScreen _SquarePixels solved the problem with the rows appearing cut off
  • My display is a 4k TV with bezels around the edges which, from my viewing angle, hide the rightmost 1/4 inch of the screen. I leaned over a little and realized what I thought was being cut off was really just hidden behind the bezel. Doh!

So anyway, thank you guys for patiently explaining how the screen dimensions work and all.
I did learn from it, how to calculate the number of text rows+columns, and about _PrintString, which comes in useful!
Thanks again!

PS In case it helps anyone, the following function saves having to do the calculation and makes for cleaner looking code:
Code: QB64: [Select]
  1. Sub PrintString (iRow As Integer, iCol As Integer, MyString As String)
  2.     Dim iX As Integer
  3.     Dim iY As Integer
  4.     iX = _FontWidth * iCol
  5.     iY = _FontHeight * iRow
  6.     _PrintString (iX, iY), MyString
  7. End Sub ' PrintString
  8.  
« Last Edit: January 10, 2022, 02:48:41 pm by madscijr »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
   iX = _FontWidth * iCol
   iY = _FontHeight * iRow


*As long as you remember to start your column and row counting at 0.  Otherwise, add a -1 in there to duplicate LOCATE coordinates fully.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
    • View Profile
   iX = _FontWidth * iCol
   iY = _FontHeight * iRow
*As long as you remember to start your column and row counting at 0.  Otherwise, add a -1 in there to duplicate LOCATE coordinates fully.  ;)

Right on