Text Using Graphics: Difference between revisions
(Created page with "While QB64 offers _FONT and Unicode text options, Qbasic was limited in what it could offer. This shortfall could be overcome in various ways including using sprite pictures. Thanks to QB64, we can have the best in both worlds without creating them yourself! :::::::::1) The characters can be loaded in a file for Qbasic. :::::::::2) Text fonts can be sized without re-loading them. :::::::::3) Data files are smaller than BSAVE image files. :::::::::4} Proper c...") |
No edit summary |
||
(17 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
While QB64 offers [[_FONT]] and [[Unicode]] text options, | While QB64 offers [[_FONT]] and [[Unicode]] text options, QBasic was limited in what it could offer. This shortfall could be overcome in various ways including using sprite pictures. Thanks to QB64, we can have the best in both worlds without creating them yourself! | ||
:::::::::1) The characters can be loaded in a file for | :::::::::1) The characters can be loaded in a file for QBasic. | ||
:::::::::2) Text fonts can be sized without re-loading them. | :::::::::2) Text fonts can be sized without re-loading them. | ||
:::::::::3) Data files are smaller than [[BSAVE]] image files. | :::::::::3) Data files are smaller than [[BSAVE]] image files. | ||
:::::::::4} Proper character spacing is already done for you. | :::::::::4} Proper character spacing is already done for you. | ||
== Bit Packing Pixel Data == | |||
{{TextStart}} | In QBasic, text blocks were always 8 pixels wide no matter what screen mode was used. This allowed the total data value of a row to be set by reading 8 pixels using [[POINT]]. The value can be stored in one [[_BYTE]](8 bits) of data using [[ASCII]] string characters. | ||
{{TextStart}} | |||
{{Cb|SCREEN}} 12 'text characters are 8 X 16 pixel blocks | {{Cb|SCREEN}} 12 'text characters are 8 X 16 pixel blocks | ||
{{Cb|DIM}} {{Cb|SHARED}} Char(0 {{Cb|TO}} 255, 0 {{Cb|TO}} 15) {{Cb|AS}} {{Cb|STRING}} * 1 'store data by character row byte values | {{Cb|DIM}} {{Cb|SHARED}} Char(0 {{Cb|TO}} 255, 0 {{Cb|TO}} 15) {{Cb|AS}} {{Cb|STRING}} * 1 'store data by character row byte values | ||
Line 31: | Line 31: | ||
{{Cb|NEXT}} | {{Cb|NEXT}} | ||
{{Cb|PALETTE}} 'restore all palette colors | {{Cb|PALETTE}} 'restore all palette colors | ||
{{Cb|END SUB}} | {{Cb|END SUB}} | ||
{{TextEnd}} | {{TextEnd}} | ||
: Above [[ASCII]] Characters 7, 9, 10, 11, 12, 13, 28, 29, 30, and 31 won't print in | {{Small|QBasic concept by Artelius}} | ||
: Above [[ASCII]] Characters 7, 9, 10, 11, 12, 13, 28, 29, 30, and 31 won't print in QBasic! '''QB64''' can print them with [[_PRINTSTRING]] or by using [[PRINT]] after [[_CONTROLCHR]] OFF is set. | |||
<center>'''Bit Packing'''</center> | <center>'''Bit Packing'''</center> | ||
{{ | {{FixedStart}} | ||
{{Cb|FOR...NEXT|FOR}} col% = 7 {{Cb|TO}} 0 {{Cb|STEP}} -1 'read pixels from right to left | {{Cb|FOR...NEXT|FOR}} col% = 7 {{Cb|TO}} 0 {{Cb|STEP}} -1 'read pixels from right to left | ||
byte = byte * 2 - ({{Cb|POINT}}(col%, row%) > 0) 'bit-packing with 2 ^ bit | byte = byte * 2 - ({{Cb|POINT}}(col%, row%) > 0) 'bit-packing with 2 ^ bit | ||
{{Cb|NEXT}} | {{Cb|NEXT}} | ||
{{ | {{FixedEnd}} | ||
: Bit packing is done by doubling the current byte value and adding one for each bit where a pixel is not colored black. | : Bit packing is done by doubling the current byte value and adding one for each bit where a pixel is not colored black. | ||
:The [[boolean]] statement | :The [[boolean]] statement {{InlineCode}}(POINT(col%, row%) > 0){{InlineCodeEnd}} evaluates to ''true(-1)'' when a pixel is on or ''false(0)'' when a pixel is off. This effectively adds 1 to the byte value. Each loop that byte value is doubled as [[POINT]] goes from right to left. If the left-most pixel is on, it will only add one to the byte value as expected. This makes the total sum of the bit values or the byte value equal to 255 if all of the pixels are on as shown below: | ||
{{ | {{FixedStart}} ''' Bit\Column #: 0 1 2 3 4 5 6 7 TOTAL ''' | ||
Exponent 2 ^: 0 1 2 3 4 5 6 7 | Exponent 2 ^: 0 1 2 3 4 5 6 7 | ||
Bit Value On: 1 2 4 8 16 32 64 128 255 | Bit Value On: 1 2 4 8 16 32 64 128 255 | ||
{{ | {{FixedEnd}} | ||
: Alpha-numeric text byte values will never be close to 255 because the first and last columns will normally be 0 as they are used to space text characters. Some characters, such as [[CHR$]](219) can have byte totals of 255 and have a value every row however. | : Alpha-numeric text byte values will never be close to 255 because the first and last columns will normally be 0 as they are used to space text characters. Some characters, such as [[CHR$]](219) can have byte totals of 255 and have a value every row however. | ||
''Example Code:'' Displays the 16 one byte row values saved as [[ASCII]] characters in the [[STRING]] array using [[_PRINTSTRING]]. | ''Example Code:'' Displays the 16 one byte row values saved as [[ASCII]] characters in the [[STRING]] array using [[_PRINTSTRING]]. | ||
{{CodeStart}} | {{CodeStart}} | ||
{{Cl|SCREEN}} 12 'text characters are 8 X 16 pixel blocks | {{Cl|SCREEN}} 12 'text characters are 8 X 16 pixel blocks | ||
{{Cl|DIM}} {{Cl|SHARED}} Char(0 {{Cl|TO}} 255, 0 {{Cl|TO}} 15) {{Cl|AS}} {{Cl|STRING}} * 1 'store data by character row byte values | {{Cl|DIM}} {{Cl|SHARED}} Char(0 {{Cl|TO}} 255, 0 {{Cl|TO}} 15) {{Cl|AS}} {{Cl|STRING}} * 1 'store data by character row byte values | ||
Line 62: | Line 63: | ||
{{Cl|COLOR}} 11: {{Cl|LOCATE}} 10, 10: {{Cl|INPUT}} "Enter keypress or an ASCII code 1 to 255: ", cod$ | {{Cl|COLOR}} 11: {{Cl|LOCATE}} 10, 10: {{Cl|INPUT}} "Enter keypress or an ASCII code 1 to 255: ", cod$ | ||
{{Cl|CLS}}: code = {{Cl|VAL}}(cod$) | {{Cl|CLS}}: code = {{Cl|VAL}}(cod$) | ||
{{Cl|IF...THEN|IF}} (code <= 0 {{Cl|AND (boolean)|AND}} {{Cl|LEN}}(cod$) > 0) {{Cl|OR (boolean)|OR}} code > 255 {{Cl|THEN}} code = {{Cl|ASC}}(cod$) | {{Cl|IF...THEN|IF}} (code <= 0 {{Cl|AND (boolean)|AND}} {{Cl|LEN}}(cod$) > 0) {{Cl|OR (boolean)|OR}} code > 255 {{Cl|THEN}} code = {{Cl|ASC (function)|ASC}}(cod$) | ||
{{Cl|COLOR}} 14: {{Cl|_PRINTSTRING}} (0, 50), {{Cl|LTRIM$}}({{Cl|STR$}}(code)) | {{Cl|COLOR}} 14: {{Cl|_PRINTSTRING}} (0, 50), {{Cl|LTRIM$}}({{Cl|STR$}}(code)) | ||
{{Cl|FOR...NEXT|FOR}} n = 0 {{Cl|TO}} 15 | {{Cl|FOR...NEXT|FOR}} n = 0 {{Cl|TO}} 15 | ||
Line 91: | Line 92: | ||
: The [[ASCII]] character data in the array can also be saved to a comma separated file with [[WRITE]] for later use. In fact, the data can be read and displayed by | : The [[ASCII]] character data in the array can also be saved to a comma separated file with [[WRITE]] for later use. In fact, the data can be read and displayed by QBasic programs later using special [[_LOADFONT|fonts]] or [[Unicode]]! Wider characters will require larger values than [[_BYTE]]. | ||
<p style="text-align: center">([[#toc|Return to Top]])</p> | <p style="text-align: center">([[#toc|Return to Top]])</p> | ||
==Displaying Text Characters== | |||
== Displaying Text Characters == | |||
Once the data is saved, we need something to convert the data back into text characters. There are several ways to do this simply by using [[PSET]] for normal sizes or [[CIRCLE]] or [[LINE]] to amplify the displayed character sizes. Use a normal [[FOR...NEXT|FOR]] 0 TO 7 loop to read: | Once the data is saved, we need something to convert the data back into text characters. There are several ways to do this simply by using [[PSET]] for normal sizes or [[CIRCLE]] or [[LINE]] to amplify the displayed character sizes. Use a normal [[FOR...NEXT|FOR]] 0 TO 7 loop to read: | ||
<center>'''[[ASCII]] 8 X 16 Text character size increased using [[CIRCLE]]'''</center> | <center>'''[[ASCII]] 8 X 16 Text character size increased using [[CIRCLE]]'''</center> | ||
{{CodeStart}} | {{CodeStart}} | ||
{{Cl|SCREEN}} 12 'text characters are 8 X 16 pixel blocks | {{Cl|SCREEN}} 12 'text characters are 8 X 16 pixel blocks | ||
{{Cl|DIM}} {{Cl|SHARED}} Char(0 {{Cl|TO}} 255, 0 {{Cl|TO}} 15) {{Cl|AS}} {{Cl|STRING}} * 1 'store data by character row byte values | {{Cl|DIM}} {{Cl|SHARED}} Char(0 {{Cl|TO}} 255, 0 {{Cl|TO}} 15) {{Cl|AS}} {{Cl|STRING}} * 1 'store data by character row byte values | ||
Line 109: | Line 111: | ||
{{Cl|COLOR}} 14: {{Cl|LOCATE}} 26, 5 | {{Cl|COLOR}} 14: {{Cl|LOCATE}} 26, 5 | ||
{{Cl|FOR...NEXT|FOR}} yy = 100 {{Cl|TO}} 220 {{Cl|STEP}} 8 | {{Cl|FOR...NEXT|FOR}} yy = 100 {{Cl|TO}} 220 {{Cl|STEP}} 8 | ||
rowval = {{Cl|ASC}}(Char$(ch, rowy)) 'pixel row value | rowval = {{Cl|ASC (function)|ASC}}(Char$(ch, rowy)) 'pixel row value | ||
{{Cl|SELECT CASE}} ch | {{Cl|SELECT CASE}} ch | ||
{{Cl|CASE}} 8, 10, 178, 182, 185, 186, 199, 204, 206, 215, 219, 222 | {{Cl|CASE}} 8, 10, 178, 182, 185, 186, 199, 204, 206, 215, 219, 222 | ||
Line 117: | Line 119: | ||
SetCHR 25, {{Cl|POS}}(0) - 2, 11, rowval | SetCHR 25, {{Cl|POS}}(0) - 2, 11, rowval | ||
colx = 0 | colx = 0 | ||
{{Cl|FOR...NEXT|FOR}} xx = 300 {{Cl|TO}} 356 {{Cl|STEP}} 8 | {{Cl|FOR...NEXT|FOR}} xx = 300 {{Cl|TO}} 356 {{Cl|STEP}} 8 | ||
{{Cl|IF...THEN|IF}} (rowval {{Cl|AND (boolean)|AND}} 2 ^ colx) > 0 {{Cl|THEN}} | {{Cl|IF...THEN|IF}} (rowval {{Cl|AND (boolean)|AND}} 2 ^ colx) > 0 {{Cl|THEN}} | ||
{{Cl|CIRCLE}} (xx, yy), 4, 7 'display text character as full white circles | {{Cl|CIRCLE}} (xx, yy), 4, 7 'display text character as full white circles | ||
Line 138: | Line 140: | ||
{{Cl|LOCATE}} 26, 4: {{Cl|PRINT}} {{Cl|SPACE$}}(75) | {{Cl|LOCATE}} 26, 4: {{Cl|PRINT}} {{Cl|SPACE$}}(75) | ||
ch = {{Cl|VAL}}(ch$) | ch = {{Cl|VAL}}(ch$) | ||
{{Cl|IF...THEN|IF}} ch = 0 {{Cl|AND (boolean)|AND}} {{Cl|LEN}}(ch$) {{Cl|THEN}} ch = {{Cl|ASC}}(ch$) | {{Cl|IF...THEN|IF}} ch = 0 {{Cl|AND (boolean)|AND}} {{Cl|LEN}}(ch$) {{Cl|THEN}} ch = {{Cl|ASC (function)|ASC}}(ch$) | ||
{{Cl|LOOP}} {{Cl|UNTIL}} ch = 0 {{Cl|OR (boolean)|OR}} ch > 255 | {{Cl|LOOP}} {{Cl|UNTIL}} ch = 0 {{Cl|OR (boolean)|OR}} ch > 255 | ||
{{Cl|END}} | {{Cl|END}} | ||
Line 145: | Line 147: | ||
Srow = 16 * (Trow - 1): Scol = 8 * (Tcol - 1) 'convert text to graphic coordinates | Srow = 16 * (Trow - 1): Scol = 8 * (Tcol - 1) 'convert text to graphic coordinates | ||
{{Cl|FOR...NEXT|FOR}} y = 0 {{Cl|TO}} 15 | {{Cl|FOR...NEXT|FOR}} y = 0 {{Cl|TO}} 15 | ||
ybyte$ = Char$(ASCode, y): yval = {{Cl|ASC}}(ybyte$) | ybyte$ = Char$(ASCode, y): yval = {{Cl|ASC (function)|ASC}}(ybyte$) | ||
{{Cl|FOR...NEXT|FOR}} x = 0 {{Cl|TO}} 7 | {{Cl|FOR...NEXT|FOR}} x = 0 {{Cl|TO}} 7 | ||
{{Cl|IF...THEN|IF}} (yval {{Cl|AND}} 2 ^ x) > 0 {{Cl|THEN}} {{Cl|PSET}} (Scol + x, Srow + y), FG | {{Cl|IF...THEN|IF}} (yval {{Cl|AND}} 2 ^ x) > 0 {{Cl|THEN}} {{Cl|PSET}} (Scol + x, Srow + y), FG | ||
Line 166: | Line 168: | ||
{{Cl|NEXT}} | {{Cl|NEXT}} | ||
{{Cl|PALETTE}} 'restore all palette colors | {{Cl|PALETTE}} 'restore all palette colors | ||
{{Cl|END SUB}} | {{Cl|END SUB}} | ||
{{CodeEnd}} | {{CodeEnd}} | ||
{{ | {{Small|Code by Ted Weissgerber}} | ||
: The program above shows each array [[_BYTE|byte]] character value and character. [[PRINT]] will not print some characters in QB64 or QB. | : The program above shows each array [[_BYTE|byte]] character value and character. [[PRINT]] will not print some characters in QB64 or QB. | ||
<center>Use '''[[_CONTROLCHR]] OFF''' to [[PRINT]] control characters in QB64!</center> | <center>Use '''[[_CONTROLCHR]] OFF''' to [[PRINT]] control characters in QB64!</center> | ||
Line 174: | Line 176: | ||
<center>'''[[ASCII]] 8 X 16 Text Character size increased using [[LINE]]'''</center> | <center>'''[[ASCII]] 8 X 16 Text Character size increased using [[LINE]]'''</center> | ||
{{CodeStart}} | {{CodeStart}} | ||
{{Cl|SUB}} DisplayText (Xpos, Ypos, FG, BG, Xsize, Ysize, text$) | {{Cl|SUB}} DisplayText (Xpos, Ypos, FG, BG, Xsize, Ysize, text$) | ||
x = Xpos: y = Ypos: Xoff = (8 * Xsize): L = {{Cl|LEN}}(text$) | x = Xpos: y = Ypos: Xoff = (8 * Xsize): L = {{Cl|LEN}}(text$) | ||
Line 181: | Line 183: | ||
{{Cl|END IF}} | {{Cl|END IF}} | ||
{{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | {{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | ||
tx = {{Cl|ASC}}({{Cl|MID$}}(text$, character, 1)) 'get ASCII value | tx = {{Cl|ASC (function)|ASC}}({{Cl|MID$ (function)|MID$}}(text$, character, 1)) 'get ASCII value | ||
{{Cl|FOR...NEXT|FOR}} r = 0 {{Cl|TO}} 15 'current row of 16 | {{Cl|FOR...NEXT|FOR}} r = 0 {{Cl|TO}} 15 'current row of 16 | ||
{{Cl|FOR...NEXT|FOR}} c = 0 {{Cl|TO}} 7 ' 'cycle through 8 bit values | {{Cl|FOR...NEXT|FOR}} c = 0 {{Cl|TO}} 7 ' 'cycle through 8 bit values | ||
{{Cl|IF...THEN|IF}} {{Cl|ASC}}(Char(tx, r)) {{Cl|AND}} 2 ^ c {{Cl|THEN}} 'if bit is on | {{Cl|IF...THEN|IF}} {{Cl|ASC (function)|ASC}}(Char(tx, r)) {{Cl|AND}} 2 ^ c {{Cl|THEN}} 'if bit is on | ||
{{Cl|LINE}} (x, y)-(x + Xsize - 1, y + Ysize - 1), FG, BF | {{Cl|LINE}} (x, y)-(x + Xsize - 1, y + Ysize - 1), FG, BF | ||
{{Cl|END IF}} 'adapted from code by TerryRitchie @ www.QB64.net | {{Cl|END IF}} 'adapted from code by TerryRitchie @ www.QB64.net | ||
Line 195: | Line 197: | ||
Xpos = Xpos + Xoff 'set to next character column | Xpos = Xpos + Xoff 'set to next character column | ||
{{Cl|NEXT}} character 'next character | {{Cl|NEXT}} character 'next character | ||
{{Cl|END SUB}} | {{Cl|END SUB}} | ||
{{CodeEnd}} | {{CodeEnd}} | ||
{{ | {{Small|Adapted from code by Terry Ritchie}} | ||
: '''NOTE: This procedure requires Char [[STRING]] array data created by the TextSave [[SUB]] from above to be run first!''' | : '''NOTE: This procedure requires Char [[STRING]] array data created by the TextSave [[SUB]] from above to be run first!''' | ||
:* ''Xpos'' and ''Ypos'' parameters set the top left graphic start position coordinate of the text string. [[LOCATE]] cannot be used. | :* ''Xpos'' and ''Ypos'' parameters set the top left graphic start position coordinate of the text string. [[LOCATE]] cannot be used. | ||
:* ''FG'' and ''BG'' determine the text foreground and background colors respectively. If BG is 0, then no background color is used. | :* ''FG'' and ''BG'' determine the text foreground and background colors respectively. If BG is 0, then no background color is used. | ||
:* ''Xsize'' and ''Ysize'' determines the size multiple increase of the letters. One is normal size, two is double size etc. | :* ''Xsize'' and ''Ysize'' determines the size multiple increase of the letters. One is normal size, two is double size etc. | ||
Line 207: | Line 209: | ||
<p style="text-align: center">([[#toc|Return to Top]])</p> | <p style="text-align: center">([[#toc|Return to Top]])</p> | ||
==Font and Unicode Conversion== | |||
== Font and Unicode Conversion == | |||
To convert different sized [[_FONT|fonts]] or [[Unicode]] characters, first determine the text block size to find how much data is required: | To convert different sized [[_FONT|fonts]] or [[Unicode]] characters, first determine the text block size to find how much data is required: | ||
{{CodeStart}} 'Code must be run in QB64 ONLY! | {{CodeStart}} 'Code must be run in QB64 ONLY! | ||
{{Cl|DEFINT}} A-Z | {{Cl|DEFINT}} A-Z | ||
{{Cl|DIM}} {{Cl|SHARED}} high% 'value is shared with both SUB procedures! | {{Cl|DIM}} {{Cl|SHARED}} high% 'value is shared with both SUB procedures! | ||
Line 255: | Line 258: | ||
x = Xpos: y = Ypos: L = {{Cl|LEN}}(text$) | x = Xpos: y = Ypos: L = {{Cl|LEN}}(text$) | ||
{{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | {{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | ||
tx = {{Cl|ASC}}({{Cl|MID$}}(text$, character, 1)) 'get each letter's ASCII value | tx = {{Cl|ASC (function)|ASC}}({{Cl|MID$ (function)|MID$}}(text$, character, 1)) 'get each letter's ASCII value | ||
wide% = Char(tx, high%): Xoff = wide% * Xsize 'get font width from high row | wide% = Char(tx, high%): Xoff = wide% * Xsize 'get font width from high row | ||
{{Cl|FOR...NEXT|FOR}} r% = 0 {{Cl|TO}} high% - 1 'current row value | {{Cl|FOR...NEXT|FOR}} r% = 0 {{Cl|TO}} high% - 1 'current row value | ||
Line 289: | Line 292: | ||
{{Cl|NEXT}} | {{Cl|NEXT}} | ||
{{Cl|PALETTE}} 'restore all palette colors | {{Cl|PALETTE}} 'restore all palette colors | ||
{{Cl|END SUB}} | {{Cl|END SUB}} | ||
{{CodeEnd}} | {{CodeEnd}} | ||
{{ | {{Small|Code by Ted Weissgerber}} | ||
: ''Explanation:'' [[_PRINTWIDTH]] can read the pixel width of one character and can be used to measure non-monospace fonts. | : ''Explanation:'' [[_PRINTWIDTH]] can read the pixel width of one character and can be used to measure non-monospace fonts. | ||
:If the character width is wider than 8 pixels, we can no longer store the exponent of two values in one [[_BYTE|byte]] or represent that byte as an [[ASCII]] character so we can use an [[INTEGER]] or [[LONG]] array to hold the values, which can be saved to file with [[PUT]]. | :If the character width is wider than 8 pixels, we can no longer store the exponent of two values in one [[_BYTE|byte]] or represent that byte as an [[ASCII]] character so we can use an [[INTEGER]] or [[LONG]] array to hold the values, which can be saved to file with [[PUT]]. | ||
Line 297: | Line 300: | ||
<center>'''Variable font widths'''</center> | <center>'''Variable font widths'''</center> | ||
: Some fonts vary in width for every character so we can store ''wide&'' as a [[HEX$]] value in the ''high&'' array row value as we use 0 through ''high&'' - 1 to store the bit totals. Save the ''high&'' [[_FONTHEIGHT]] value for the sub-procedures and to dimension an array! | : Some fonts vary in width for every character so we can store ''wide&'' as a [[HEX$]] value in the ''high&'' array row value as we use 0 through ''high&'' - 1 to store the bit totals. Save the ''high&'' [[_FONTHEIGHT]] value for the sub-procedures and to dimension an array! | ||
:'''This allows the use of DisplayText [[SUB]] in a | :'''This allows the use of DisplayText [[SUB]] in a QBasic program to read the data when you save the array data to a file.''' | ||
<p style="text-align: center">([[#toc|Return to Top]])</p> | <p style="text-align: center">([[#toc|Return to Top]])</p> | ||
==Reading the Data File== | |||
== Reading the Data File == | |||
<center>'''Reading the file data in a QB64 program only'''</center> | <center>'''Reading the file data in a QB64 program only'''</center> | ||
{{CodeStart}} | {{CodeStart}} | ||
{{Cl|DEFINT}} A-Z | {{Cl|DEFINT}} A-Z | ||
{{Cl|DIM}} {{Cl|SHARED}} high% 'must share with DisplayText SUB! | {{Cl|DIM}} {{Cl|SHARED}} high% 'must share with DisplayText SUB! | ||
Line 326: | Line 330: | ||
x = Xpos: y = Ypos: L = {{Cl|LEN}}(text$) | x = Xpos: y = Ypos: L = {{Cl|LEN}}(text$) | ||
{{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | {{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | ||
tx = {{Cl|ASC}}({{Cl|MID$}}(text$, character, 1)) 'get ASCII value | tx = {{Cl|ASC (function)|ASC}}({{Cl|MID$ (function)|MID$}}(text$, character, 1)) 'get ASCII value | ||
wide% = Char(tx, high%): Xoff = wide% * Xsize 'read variable font widths | wide% = Char(tx, high%): Xoff = wide% * Xsize 'read variable font widths | ||
{{Cl|FOR...NEXT|FOR}} r% = 0 {{Cl|TO}} high% - 1 'current row value | {{Cl|FOR...NEXT|FOR}} r% = 0 {{Cl|TO}} high% - 1 'current row value | ||
Line 342: | Line 346: | ||
Xpos = Xpos + Xoff 'set to next character column | Xpos = Xpos + Xoff 'set to next character column | ||
{{Cl|NEXT}} character | {{Cl|NEXT}} character | ||
{{Cl|END SUB}} | {{Cl|END SUB}} | ||
{{CodeEnd}} | {{CodeEnd}} | ||
<center>''Note:'' Use empty parenthesis after the [[Arrays|array]] name to [[PUT]] # or [[GET]] # the entire array, even multi-dimensional ones.</center> | <center>''Note:'' Use empty parenthesis after the [[Arrays|array]] name to [[PUT]] # or [[GET]] # the entire array, even multi-dimensional ones.</center> | ||
<center>'''Reading the file data in a | <center>'''Reading the file data in a QBasic or QB64 program'''</center> | ||
{{CodeStart}} | {{CodeStart}} | ||
{{Cl|DEFINT}} A-Z | {{Cl|DEFINT}} A-Z | ||
{{Cl|SCREEN}} 12 | {{Cl|SCREEN}} 12 | ||
Line 382: | Line 386: | ||
x = Xpos: y = Ypos: L = {{Cl|LEN}}(text$) | x = Xpos: y = Ypos: L = {{Cl|LEN}}(text$) | ||
{{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | {{Cl|FOR...NEXT|FOR}} character = 1 {{Cl|TO}} L 'go through text chars | ||
tx = {{Cl|ASC}}({{Cl|MID$}}(text$, character, 1)) 'get ASCII value | tx = {{Cl|ASC (function)|ASC}}({{Cl|MID$ (function)|MID$}}(text$, character, 1)) 'get ASCII value | ||
wide% = Char(tx, high%): Xoff = wide% * Xsize 'read variable font widths | wide% = Char(tx, high%): Xoff = wide% * Xsize 'read variable font widths | ||
{{Cl|FOR...NEXT|FOR}} r% = 0 {{Cl|TO}} high% - 1 'current row value | {{Cl|FOR...NEXT|FOR}} r% = 0 {{Cl|TO}} high% - 1 'current row value | ||
Line 398: | Line 402: | ||
Xpos = Xpos + Xoff 'set to next character column | Xpos = Xpos + Xoff 'set to next character column | ||
{{Cl|NEXT}} character | {{Cl|NEXT}} character | ||
{{Cl|END SUB}} | {{Cl|END SUB}} | ||
{{CodeEnd}} | {{CodeEnd}} | ||
<center>The QB64 [[PUT]] [[BINARY]] file can be read to the array by reading the row values for all of the characters as above.</center> | <center>The QB64 [[PUT]] [[BINARY]] file can be read to the array by reading the row values for all of the characters as above.</center> | ||
Line 407: | Line 411: | ||
<p style="text-align: center">([[#toc|Return to Top]])</p> | <p style="text-align: center">([[#toc|Return to Top]])</p> | ||
{{PageSeeAlso}} | |||
* [[_PRINTSTRING]], [[_PRINTWIDTH]] | * [[_PRINTSTRING]], [[_PRINTWIDTH]] | ||
* [[_LOADFONT]], [[_FONTHEIGHT]] | * [[_LOADFONT]], [[_FONTHEIGHT]] | ||
* [[_MAPUNICODE]], [[_CONTROLCHR]] | * [[_MAPUNICODE]], [[_CONTROLCHR]] | ||
* [[Boolean]], [[AND]] | * [[Boolean]], [[AND]] | ||
* [[ASCII]], [[Unicode]], [[Code Pages]] | * [[ASCII]], [[Unicode]], [[Code Pages]] | ||
{{ | {{PageReferences}} |
Latest revision as of 13:24, 19 November 2024
While QB64 offers _FONT and Unicode text options, QBasic was limited in what it could offer. This shortfall could be overcome in various ways including using sprite pictures. Thanks to QB64, we can have the best in both worlds without creating them yourself!
- 1) The characters can be loaded in a file for QBasic.
- 2) Text fonts can be sized without re-loading them.
- 3) Data files are smaller than BSAVE image files.
- 4} Proper character spacing is already done for you.
Bit Packing Pixel Data
In QBasic, text blocks were always 8 pixels wide no matter what screen mode was used. This allowed the total data value of a row to be set by reading 8 pixels using POINT. The value can be stored in one _BYTE(8 bits) of data using ASCII string characters.
SCREEN 12 'text characters are 8 X 16 pixel blocks DIM SHARED Char(0 TO 255, 0 TO 15) AS STRING * 1 'store data by character row byte values TextSave 'call to SUB at start of program SUB TextSave OUT &H3C8, 1: OUT &H3C9, 0: OUT &H3C9, 0: OUT &H3C9, 0 'print text as background color COLOR 1 'hide the character printing FOR ascii% = 0 TO 255 'Draw map of each character IF ascii% = 7 THEN ascii% = 8 'eliminate beep character sound when printed LOCATE 1, 1: PRINT CHR$(ascii%) 'PRINT ASCII characters to top left corner FOR row% = 0 TO 15 'read 16 row byte values byte = 0 'reset value every row FOR col% = 7 TO 0 STEP -1 'read 8 pixels from right to left byte = byte * 2 - (POINT(col%, row%) > 0) 'bit-packing with 2 ^ bit NEXT Char(ascii%, row%) = CHR$(byte) 'convert row byte value to ASCII character NEXT LOCATE CSRLIN, POS(0) - 1: PRINT SPACE$(1) 'erase previous character anywhere NEXT PALETTE 'restore all palette colors END SUB |
- Above ASCII Characters 7, 9, 10, 11, 12, 13, 28, 29, 30, and 31 won't print in QBasic! QB64 can print them with _PRINTSTRING or by using PRINT after _CONTROLCHR OFF is set.
FOR col% = 7 TO 0 STEP -1 'read pixels from right to left byte = byte * 2 - (POINT(col%, row%) > 0) 'bit-packing with 2 ^ bit NEXT |
- Bit packing is done by doubling the current byte value and adding one for each bit where a pixel is not colored black.
- The boolean statement (POINT(col%, row%) > 0) evaluates to true(-1) when a pixel is on or false(0) when a pixel is off. This effectively adds 1 to the byte value. Each loop that byte value is doubled as POINT goes from right to left. If the left-most pixel is on, it will only add one to the byte value as expected. This makes the total sum of the bit values or the byte value equal to 255 if all of the pixels are on as shown below:
Bit\Column #: 0 1 2 3 4 5 6 7 TOTAL Exponent 2 ^: 0 1 2 3 4 5 6 7 Bit Value On: 1 2 4 8 16 32 64 128 255 |
- Alpha-numeric text byte values will never be close to 255 because the first and last columns will normally be 0 as they are used to space text characters. Some characters, such as CHR$(219) can have byte totals of 255 and have a value every row however.
Example Code: Displays the 16 one byte row values saved as ASCII characters in the STRING array using _PRINTSTRING.
SCREEN 12 'text characters are 8 X 16 pixel blocks DIM SHARED Char(0 TO 255, 0 TO 15) AS STRING * 1 'store data by character row byte values TextSave 'call to SUB at start of program DO COLOR 11: LOCATE 10, 10: INPUT "Enter keypress or an ASCII code 1 to 255: ", cod$ CLS: code = VAL(cod$) IF (code <= 0 AND LEN(cod$) > 0) OR code > 255 THEN code = ASC(cod$) COLOR 14: _PRINTSTRING (0, 50), LTRIM$(STR$(code)) FOR n = 0 TO 15 ch$ = Char(code, n) _PRINTSTRING (n * 30 + 40, 50), ch$ + "," NEXT _PRINTSTRING (600, 50), CHR$(code) LOOP UNTIL LEN(cod$) = 0 END SUB TextSave OUT &H3C8, 1: OUT &H3C9, 0: OUT &H3C9, 0: OUT &H3C9, 0 'print text as background color COLOR 1 'hide the character printing FOR ascii% = 0 TO 255 'Draw map of each character _PRINTSTRING (0, 0), CHR$(ascii%) 'PRINT ASCII characters to top left corner FOR row% = 0 TO 15 'read 16 row byte values byte = 0 'reset value every row FOR col% = 7 TO 0 STEP -1 'read 8 pixels from right to left byte = byte * 2 - (POINT(col%, row%) > 0) 'bit-packing with 2 ^ bit NEXT Char(ascii%, row%) = CHR$(byte) 'convert row byte value to ASCII character NEXT NEXT PALETTE 'restore all palette colors END SUB |
- The ASCII character data in the array can also be saved to a comma separated file with WRITE for later use. In fact, the data can be read and displayed by QBasic programs later using special fonts or Unicode! Wider characters will require larger values than _BYTE.
Displaying Text Characters
Once the data is saved, we need something to convert the data back into text characters. There are several ways to do this simply by using PSET for normal sizes or CIRCLE or LINE to amplify the displayed character sizes. Use a normal FOR 0 TO 7 loop to read:
SCREEN 12 'text characters are 8 X 16 pixel blocks DIM SHARED Char(0 TO 255, 0 TO 15) AS STRING * 1 'store data by character row byte values TextSave 'call to SUB at start of program ch = 1 'character 1 DO: rowy = 0 COLOR 14: LOCATE 26, 5 FOR yy = 100 TO 220 STEP 8 rowval = ASC(Char$(ch, rowy)) 'pixel row value SELECT CASE ch CASE 8, 10, 178, 182, 185, 186, 199, 204, 206, 215, 219, 222 PRINT STR$(rowval); 'compact the values CASE ELSE: PRINT rowval; END SELECT SetCHR 25, POS(0) - 2, 11, rowval colx = 0 FOR xx = 300 TO 356 STEP 8 IF (rowval AND 2 ^ colx) > 0 THEN CIRCLE (xx, yy), 4, 7 'display text character as full white circles PAINT STEP(0, 0), 15, 7 ' LINE (xx - 4, yy - 4)-(xx + 3, yy + 3), 15, BF ELSE: CIRCLE (xx, yy), 4, 1 'display background as full blue circles PAINT STEP(0, 0), 1 ' LINE (xx - 4, yy - 4)-(xx + 3, yy + 3), 1, BF END IF colx = colx + 1 NEXT _DELAY .2 rowy = rowy + 1 NEXT LOCATE 22, 40: PRINT SPACE$(39) LOCATE 22, 5: COLOR 13: LINE INPUT "Enter ASCII code(1 to 255) or press key character (Enter quits): ", ch$ LOCATE 23, 2: PRINT SPACE$(78) LOCATE 25, 4: PRINT SPACE$(75) LOCATE 26, 4: PRINT SPACE$(75) ch = VAL(ch$) IF ch = 0 AND LEN(ch$) THEN ch = ASC(ch$) LOOP UNTIL ch = 0 OR ch > 255 END SUB SetCHR (Trow, Tcol, FG, ASCode) 'displays ASCII character value from array Srow = 16 * (Trow - 1): Scol = 8 * (Tcol - 1) 'convert text to graphic coordinates FOR y = 0 TO 15 ybyte$ = Char$(ASCode, y): yval = ASC(ybyte$) FOR x = 0 TO 7 IF (yval AND 2 ^ x) > 0 THEN PSET (Scol + x, Srow + y), FG NEXT NEXT END SUB SUB TextSave OUT &H3C8, 1: OUT &H3C9, 0: OUT &H3C9, 0: OUT &H3C9, 0 'print text as background color COLOR 1 'hide the character printing FOR ascii% = 0 TO 255 'Draw map of each character _PRINTSTRING (0, 0), CHR$(ascii%) 'PRINT ASCII characters to top left corner FOR row% = 0 TO 15 'read 16 row byte values byte = 0 'reset value every row FOR col% = 7 TO 0 STEP -1 'read 8 pixels from right to left byte = byte * 2 - (POINT(col%, row%) > 0) 'bit-packing with 2 ^ bit NEXT Char(ascii%, row%) = CHR$(byte) 'convert row byte value to ASCII character NEXT NEXT PALETTE 'restore all palette colors END SUB |
- The program above shows each array byte character value and character. PRINT will not print some characters in QB64 or QB.
SUB DisplayText (Xpos, Ypos, FG, BG, Xsize, Ysize, text$) x = Xpos: y = Ypos: Xoff = (8 * Xsize): L = LEN(text$) IF BG THEN 'set BackGround if not 0 LINE (x - (2 * Xsize), y - Ysize)-(x + (L * Xoff), y + (16 * Ysize)), BG, BF END IF FOR character = 1 TO L 'go through text chars tx = ASC(MID$(text$, character, 1)) 'get ASCII value FOR r = 0 TO 15 'current row of 16 FOR c = 0 TO 7 ' 'cycle through 8 bit values IF ASC(Char(tx, r)) AND 2 ^ c THEN 'if bit is on LINE (x, y)-(x + Xsize - 1, y + Ysize - 1), FG, BF END IF 'adapted from code by TerryRitchie @ www.QB64.net x = x + Xsize 'move x position NEXT c 'next bit y = y + Ysize 'move y position x = Xpos 'reset column position NEXT r y = Ypos 'reset y position Xpos = Xpos + Xoff 'set to next character column NEXT character 'next character END SUB |
- NOTE: This procedure requires Char STRING array data created by the TextSave SUB from above to be run first!
- Xpos and Ypos parameters set the top left graphic start position coordinate of the text string. LOCATE cannot be used.
- FG and BG determine the text foreground and background colors respectively. If BG is 0, then no background color is used.
- Xsize and Ysize determines the size multiple increase of the letters. One is normal size, two is double size etc.
- text$ is the text STRING that is to be printed. There will not be an error or screen roll if text goes out of the screen area!
Font and Unicode Conversion
To convert different sized fonts or Unicode characters, first determine the text block size to find how much data is required:
'Code must be run in QB64 ONLY! DEFINT A-Z DIM SHARED high% 'value is shared with both SUB procedures! DO INPUT "Enter Screen mode 1, 2 or 7 to 13 or 256 for _NEWIMAGE: ", scr$ mode% = VAL(scr$) LOOP UNTIL mode% > 0 SELECT CASE mode% CASE 1, 2, 7 TO 13: SCREEN mode% CASE 256: SCREEN _NEWIMAGE(800, 600, mode%) CASE ELSE: PRINT "Invalid screen mode selected!": END END SELECT INPUT "Enter first name of TTF font to use or hit Enter for block size: ", TTFont$ IF LEN(TTFont$) THEN INPUT "Enter font height: ", hi$ height% = VAL(hi$) IF height% > 0 THEN fnt& = _LOADFONT("C:\Windows\Fonts\" + TTFont$ + ".ttf", height%, style$) IF fnt& <= 0 THEN PRINT "Invalid Font handle!": END _FONT fnt& END IF 'add Unicode Code Page data using _MAPUNICODE here: high% = _FONTHEIGHT: wid% = _PRINTWIDTH("W") DIM SHARED Char(0 TO 255, 0 TO high%) AS INTEGER 'size for integer MKI$ strings up to 16 wide TextSave 'create character pixel data DisplayText 10, 20, 15, 2, 2, "This is a graphic test of font!" _PRINTSTRING (10, 120), "Actual text or font size =" + STR$(wid%) + " X" + STR$(high%) Char(0, 0) = high% 'place font height at start of array and file. Other dimension always 255 ff = FREEFILE OPEN "FontText.bin" FOR OUTPUT AS #ff 'erase previous data CLOSE #ff OPEN "FontText.bin" FOR BINARY AS #ff PUT #ff, , Char() 'PUT the entire array into a BINARY file CLOSE #ff END SUB DisplayText (Xpos, Ypos, FG, Xsize, Ysize, text$) x = Xpos: y = Ypos: L = LEN(text$) FOR character = 1 TO L 'go through text chars tx = ASC(MID$(text$, character, 1)) 'get each letter's ASCII value wide% = Char(tx, high%): Xoff = wide% * Xsize 'get font width from high row FOR r% = 0 TO high% - 1 'current row value value% = Char(tx, r%) 'row byte value FOR c% = 0 TO wide% - 1 'cycle through bit values IF value% AND 2 ^ c% THEN 'if bit is on LINE (x, y)-(x + Xsize - 1, y + Ysize - 1), FG, BF END IF 'adapted from code by TerryRitchie @ www.QB64.net x = x + Xsize 'move x position NEXT c% 'next column bit y = y + Ysize 'move y position x = Xpos 'reset column position NEXT r% y = Ypos 'reset y position Xpos = Xpos + Xoff 'set to next character column NEXT character END SUB SUB TextSave OUT &H3C8, 15: OUT &H3C9, 0: OUT &H3C9, 0: OUT &H3C9, 0 'print text as background color FOR ascii% = 0 TO 255 'Draw map of each character CLS _PRINTSTRING (0, 0), CHR$(ascii%) 'PRINT ASCII characters to top left corner wide% = _PRINTWIDTH(CHR$(ascii%)) FOR row% = 0 TO high% - 1 'read row byte values byte% = 0 'reset value every row FOR col% = wide% - 1 TO 0 STEP -1 'read pixels from right to left byte% = byte% * 2 - (POINT(col%, row%) > 0) 'bit-packing with 2 ^ bit NEXT Char(ascii%, row%) = byte% 'convert row byte value to 2 byte MKI$ character NEXT Char(ascii%, high%) = wide% 'place individual font widths into array high row NEXT PALETTE 'restore all palette colors END SUB |
- Explanation: _PRINTWIDTH can read the pixel width of one character and can be used to measure non-monospace fonts.
- If the character width is wider than 8 pixels, we can no longer store the exponent of two values in one byte or represent that byte as an ASCII character so we can use an INTEGER or LONG array to hold the values, which can be saved to file with PUT.
- Some fonts vary in width for every character so we can store wide& as a HEX$ value in the high& array row value as we use 0 through high& - 1 to store the bit totals. Save the high& _FONTHEIGHT value for the sub-procedures and to dimension an array!
- This allows the use of DisplayText SUB in a QBasic program to read the data when you save the array data to a file.
Reading the Data File
DEFINT A-Z DIM SHARED high% 'must share with DisplayText SUB! SCREEN _NEWIMAGE(800, 600, 256) f = FREEFILE OPEN "FontText.bin" FOR BINARY AS #f GET #f, , high% 'GET the first integer value as the second array dimension SEEK #f, 1 'SEEK moves back to the beginning of the file PRINT high% REDIM SHARED Char(0 TO 255, 0 TO high%) AS INTEGER 'Dynamic array GET #f, , Char() 'one GET moves all of the data back to the array once it is sized correctly CLOSE #f Char(0, 0) = 0 DisplayText 10, 100, 12, 4, 4, "This is the font from the BIN file!" END SUB DisplayText (Xpos, Ypos, FG, Xsize, Ysize, text$) x = Xpos: y = Ypos: L = LEN(text$) FOR character = 1 TO L 'go through text chars tx = ASC(MID$(text$, character, 1)) 'get ASCII value wide% = Char(tx, high%): Xoff = wide% * Xsize 'read variable font widths FOR r% = 0 TO high% - 1 'current row value value% = Char(tx, r%) FOR c% = 0 TO wide% - 1 'cycle through bit values IF value% AND 2 ^ c% THEN 'if bit is on LINE (x, y)-(x + Xsize - 1, y + Ysize - 1), FG, BF END IF 'adapted from code by TerryRitchie @ www.QB64.net x = x + Xsize 'move x position NEXT c% 'next bit y = y + Ysize 'move y position x = Xpos 'reset column position NEXT r% y = Ypos 'reset y position Xpos = Xpos + Xoff 'set to next character column NEXT character END SUB |
DEFINT A-Z SCREEN 12 DIM SHARED high% 'must share with DisplayText SUB! f = FREEFILE OPEN "FontText.bin" FOR BINARY AS #f GET #f, , high% 'GET the first integer value as the second array dimension REDIM SHARED Char(0 TO 255, 0 TO high%) AS INTEGER 'Dynamic array ReadData (f) DisplayText 10, 100, 12, 4, 4, "This is the font from the BIN file!" END SUB ReadData (f) SEEK #f, 1 'SEEK moves back to the beginning of the file PRINT high% DO FOR row = 0 TO high% 'read each row as a group of 256 values FOR code = 0 TO 255 'read the row value for each character GET #f, , valu% IF EOF(f) THEN EXIT DO 'prevents bad data Char(code, row) = valu% NEXT: NEXT LOOP UNTIL 1 = 1 'one time DO loop if End Of File CLOSE #f Char(0, 0) = 0 'remove the high% value from CHR$(0) array data END SUB SUB DisplayText (Xpos, Ypos, FG, Xsize, Ysize, text$) x = Xpos: y = Ypos: L = LEN(text$) FOR character = 1 TO L 'go through text chars tx = ASC(MID$(text$, character, 1)) 'get ASCII value wide% = Char(tx, high%): Xoff = wide% * Xsize 'read variable font widths FOR r% = 0 TO high% - 1 'current row value value% = Char(tx, r%) FOR c% = 0 TO wide% - 1 'cycle through bit values IF value% AND 2 ^ c% THEN 'if bit is on LINE (x, y)-(x + Xsize - 1, y + Ysize - 1), FG, BF END IF 'adapted from code by TerryRitchie @ www.QB64.net x = x + Xsize 'move x position NEXT 'next bit y = y + Ysize 'move y position x = Xpos 'reset column position NEXT r% y = Ypos 'reset y position Xpos = Xpos + Xoff 'set to next character column NEXT character END SUB |
See also
- _PRINTSTRING, _PRINTWIDTH
- _LOADFONT, _FONTHEIGHT
- _MAPUNICODE, _CONTROLCHR
- Boolean, AND
- ASCII, Unicode, Code Pages