Text Using Graphics

From QB64 Wiki
Revision as of 16:32, 11 October 2011 by imported>Clippy (→‎Displaying Text Characters)
Jump to navigation Jump to search

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!


Bit Packing Text 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.


Qbasic concept by Artelius

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!


Bit Packing

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 -1(true) when a pixel is on or 0(false) 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 in QB64.

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

Note: ASCII character values of CHR$(0), CHR$(32) and CHR$(255) will only display an empty space!


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.

(Return to Top)

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:


ASCII 8 X 16 Text character size increased using CIRCLEs

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

Code by Ted Weissgerber
The program above shows each array byte character value and character. PRINT will not print some characters in QB64 or QB.


ASCII 8 X 16 Text Character size increased using LINE

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

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!
  • 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 multiple size 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!


(Return to Top)

Font and Unicode Conversion

To convert different sized fonts or Unicode characters we first have to determine the text block size to find how much data is required:

DO INPUT "Enter Screen mode 1, 2 or 7 to 13 or 256, 32 for _NEWIMAGE: ", scr$ mode% = VAL(scr$) LOOP UNTIL mode% > 0 SELECT CASE mode% CASE 1, 2, 7 TO 13: SCREEN mode% CASE 256, 32: SCREEN _NEWIMAGE(800, 600, mode%) CASE ELSE: PRINT "Invalid mode selected!": END END SELECT INPUT "Enter first name of TTF font to use or hit enter for text 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 TextSize wide&, high& 'get the font or current screen mode's text block pixel size _PRINTSTRING (20, 100), "Block size = " + CHR$(1) + STR$(wide&) + " X" + STR$(high&) + " " + CHR$(2) END SUB TextSize (TextWidth&, TextHeight&) TextWidth& = _PRINTWIDTH("W") 'measure width of one font or text character TextHeight& = _FONTHEIGHT 'can measure normal text block heights also 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 will have to use an INTEGER or LONG array or convert the values to strings using MKI$ or MKL$.

(Return to Top)

References

See also:



Navigation:
Go to Keyword Reference - Alphabetical
Go to Keyword Reference - By usage
Go to Main WIKI Page