Last active
December 11, 2017 18:17
-
-
Save tjb0607/e3c012a3df7f33d9d8ba to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************************** | |
* | |
* Author: Tyler Beatty | |
* Date Created: 2015-03-02 | |
* Last modified: 2015-03-04 | |
* Lab number CST 116 Final Project | |
* Filename bdf-cli-render.cpp | |
* | |
* Overview: | |
* A BDF font is loaded into a Font class containing an array of 256 ASCII characters, | |
* each containing a binary bitmap, stored as an array of numbers. Once loaded, it will | |
* get a string from cin (or command line args) and place each character one at a time | |
* in a binary bitmap containing the current line of text, stored as an array of bitsets. | |
* Once the current line of text has no room left for another character, it will print out | |
* and clear the bitmap to move onto the next line. The bitmap is printed out two rows at a | |
* time using these characters in a single line of text: ▄, █, ▀. Also, the code is fully | |
* portable and handles command line arguments very well. | |
* | |
* Input: | |
* Options (see output of --help), a bitmap font file in the BDF format, a string to render. | |
* | |
* Output: | |
* The given string will be rendered in the given font to the command line, such that each | |
* character contains two pixels. | |
* | |
* Example output: | |
* | |
* tyler@desktop ~/programming/bdf-cli-render | |
* % ./bdf-cli-render | |
* Enter font directory [default: fonts/Tewi.bdf]: | |
* Enter string to render: Hello, world! | |
* | |
* █ █ ▀█ ▀█ ▀█ █ █ | |
* █▄▄▄█ ▄▀▀▀▄ █ █ ▄▀▀▀▄ █ ▄ █ ▄▀▀▀▄ █▄▀▀▄ █ ▄▀▀▀█ █ | |
* █ █ █▀▀▀▀ █ █ █ █ ▄▄ █ █ █ █ █ █ █ █ █ ▀ | |
* ▀ ▀ ▀▀▀▀ ▀▀ ▀▀ ▀▀▀ ▀█ ▀ ▀ ▀▀▀ ▀ ▀▀ ▀▀▀▀ ▀ | |
* ▀ | |
* Enter string to render: █ | |
* | |
********************************************************************************************/ | |
#include <iostream> | |
#include <iomanip> | |
#include <fstream> | |
#include <cstring> | |
#include <sstream> | |
#include <new> | |
#include <bitset> // todo: replace bitsets with dynamic bitsets from Boost. Putting this off until after I turn it in so that it'll run on the lab computers without installing extra stuff. | |
#include <stdexcept> | |
using namespace std; | |
const int STRING_SIZE = 256; | |
#ifdef _WIN32 | |
const char FULL_BLOCK[] = { (char)(unsigned char)219, '\0' }; // double typecast to prevent compiler warnings | |
const char UPPER_HALF_BLOCK[] = { (char)(unsigned char)223, '\0' }; | |
const char LOWER_HALF_BLOCK[] = { (char)(unsigned char)220, '\0' }; | |
const char DEFAULT_FONT[] = "fonts\\Tewi.bdf"; | |
#else | |
const char FULL_BLOCK[] = "\u2588"; // unicode | |
const char UPPER_HALF_BLOCK[] = "\u2580"; | |
const char LOWER_HALF_BLOCK[] = "\u2584"; | |
const char DEFAULT_FONT[] = "fonts/Tewi.bdf"; | |
#endif | |
const int TERMINAL_WIDTH = 80; | |
class Character { | |
public: | |
unsigned int* bitmap; // use a short array for the bitmap because a bitset's size must be known during compile | |
short dwidth; | |
short bbxWidth; | |
short bbxHeight; | |
short bbxOffsetX; | |
short bbxOffsetY; | |
short bitsPerRow; | |
}; | |
class Font { | |
private: | |
void InitBitmaps(); | |
bool LoadCharacter(ifstream& fontFile); | |
short ReadBitmap(ifstream& fontFile, unsigned int* bitmap); | |
public: | |
void DeleteBitmaps(); | |
bool Load(ifstream &fontFile); | |
short bbxWidth; | |
short bbxHeight; | |
short bbxOffsetX; | |
short bbxOffsetY; | |
short ascent; | |
short descent; | |
short bitsPerRow; // of character's bitmap | |
Character chars[256]; | |
}; | |
class FontRenderer { | |
private: | |
bool ParseArgs(int argc, char* argv[], char* stringToPrint, int stpLen); | |
bool PutChar(Font font, char myChar); | |
void PrintBitmap(); | |
public: | |
bool SetVars(ifstream& fontFile, int argc, char* argv[], char* stringToPrint, int stpLen); // return false if the program should exit | |
void InitBitmap(Font font); | |
void DeleteBitmap(); | |
void RenderString(Font font, char* stringToPrint); | |
void ClearBitmap(); | |
bitset<TERMINAL_WIDTH>* bitmap; | |
char* fontDir; | |
bool monospaceMode; // allow more than one pixel of padding on the left | |
bool compactMode; // only print non-empty lines | |
bool fullMode; // use bbxHeight instead of ascent + descent | |
bool listen; | |
short bmpHeight; | |
short bmpOffsetY; | |
short posX; | |
}; | |
void PrintHelp() | |
{ | |
cout << left; | |
cout << "Usage: bdf-cli-render [OPTION] [MESSAGE]" << endl; | |
cout << "Render message to CLI using BDF font" << endl; | |
cout << setw(6) << " -f, " << setw(20) << "--font=FILE" << "specify font" << endl; | |
cout << setw(6) << " -c, " << setw(20) << "--compact" << "compact mode (don't print empty lines)" << endl; | |
cout << setw(6) << " -m, " << setw(20) << "--monospace" << "monospace mode (allow padding of leftmost char)" << endl; | |
cout << setw(6) << " -F, " << setw(20) << "--full" << "use the full bounding box height instead of the" << endl; | |
cout << setw(26) << " " << "font height (useful for special symbols)" << endl; | |
cout << setw(6) << " -h, " << setw(20) << "--help" << "print this message and quit" << endl; | |
} | |
// String functions because visual studio's compiler complains and I don't feel like importing another lib for strcpy_s, strcat_s | |
bool StrCpy(char* destination, const char* source, int num) | |
{ | |
int i = 0; | |
while (source[i] != '\0' && i < num) | |
{ | |
destination[i] = source[i]; | |
i++; | |
} | |
if (i == num && source[i - 1] == '\0') | |
return false; | |
else | |
return true; | |
} | |
// returns whether successful | |
bool StrCat(char* destination, const char* source, int num) | |
{ | |
int destLen = strlen(destination); | |
return StrCpy(destination + destLen, source, num - destLen); | |
} | |
bool StrJoin(char* destination, const char* source, int num, char joinChar) | |
{ | |
int destLen = strlen(destination); | |
if ( num > destLen ) | |
{ | |
if ( destLen ) | |
{ | |
*(destination + destLen) = joinChar; | |
return StrCpy(destination + destLen + 1, source, num - destLen - 1); | |
} | |
else | |
{ | |
return StrCpy(destination, source, num); | |
} | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// returns the length of str2 if the first sequence of characters in str1 matches str2, else returns 0 | |
int StrStartsWith(const char* str1, const char* str2) | |
{ | |
int i = 0; | |
while (str2[i] != '\0') | |
{ | |
if (str2[i] != str1[i]) | |
return 0; | |
i++; | |
} | |
return i; | |
} | |
// read up to '\n' or '\0', return false if ends with '\0', silently truncate to stringSize | |
bool GetLine(istream &inputStream, char outputString[], int stringSize) | |
{ | |
char currentChar; | |
int stringIndex = 0; | |
inputStream.get(currentChar); | |
while (currentChar != '\n' && currentChar != '\0' && | |
(stringIndex + 1) < stringSize) | |
{ | |
outputString[stringIndex] = currentChar; | |
inputStream.get(currentChar); | |
stringIndex++; | |
} | |
outputString[stringIndex] = '\0'; | |
return currentChar != '\0'; | |
} | |
// read up to ' ', '\n', '\0', or stringSize; return the char that terminated the read | |
char GetWord(ifstream &fontFile, char string[], int stringSize) | |
{ | |
char currentChar; | |
int stringIndex = 0; | |
fontFile.get(currentChar); | |
while (currentChar != '\n' && currentChar != ' ' && currentChar != '\0' && | |
(stringIndex + 1) < stringSize) | |
{ | |
string[stringIndex] = currentChar; | |
fontFile.get(currentChar); | |
stringIndex++; | |
} | |
string[stringIndex] = '\0'; | |
return currentChar; | |
} | |
// move to the properties of a found string, return the pointer to the string found | |
char* MoveToNext(ifstream &fontFile, const char strings[5][32], int numStrings) | |
{ | |
char currentWord[STRING_SIZE]; | |
char currentChar = '\n'; | |
char* foundItem = nullptr; | |
do | |
{ | |
if (currentChar != '\n') | |
fontFile.ignore(STRING_SIZE, '\n'); // ignore characters until next line | |
currentChar = GetWord(fontFile, currentWord, STRING_SIZE); | |
if (currentChar == '\r') | |
currentChar = GetWord(fontFile, currentWord, STRING_SIZE); // handle DOS-style newlines | |
for (int i = 0; i < numStrings; i++) | |
{ | |
if (strcmp(strings[i], currentWord) == 0) | |
{ | |
foundItem = (char *)strings[i]; // the string it found | |
} | |
} | |
} while (foundItem == nullptr && !fontFile.eof()); | |
return foundItem; | |
} | |
int CalcBitsPerRow(short bbxWidth) | |
{ | |
return (bbxWidth + 7) & ~7; // adds 1 bit so that it will always round up, then truncates the last 3 binary digits to make it divisible by 8 | |
} | |
void Font::InitBitmaps() | |
{ | |
for (int i = 0; i < 256; i++) | |
{ | |
chars[i].bitmap = new unsigned int[bbxHeight]; | |
} | |
} | |
void Font::DeleteBitmaps() | |
{ | |
for (int i = 0; i < 256; i++) | |
{ | |
delete[] chars[i].bitmap; | |
} | |
} | |
// takes a list of 2-digit hex numbers and converts it to a bool matrix, returns the bits per row | |
short Font::ReadBitmap(ifstream &fontFile, unsigned int* bitmap) | |
{ | |
char currentWord[STRING_SIZE]; | |
int i = 0; | |
int bitsPerRow = 0; | |
GetWord(fontFile, currentWord, STRING_SIZE); | |
while (strcmp(currentWord, "ENDCHAR")) | |
{ | |
if (!i) | |
bitsPerRow = strlen(currentWord) * 4; | |
stringstream tmp; // temporary stringstream for converting hex to a number | |
tmp << hex << currentWord; | |
tmp >> bitmap[i]; // bitmap[i] now stores the bits of the bitmap's row as a number, so that when converted to binary, 0 is a pixel, 1 is no pixel | |
GetWord(fontFile, currentWord, STRING_SIZE); | |
i++; | |
} | |
return bitsPerRow; | |
} | |
bool Font::LoadCharacter(ifstream &fontFile) | |
{ | |
const char bdfCharStrings[5][32] = { | |
"ENCODING", | |
"DWIDTH", | |
"BBX", | |
"BITMAP", | |
"ENDCHAR" | |
}; | |
short charEncoding; | |
bool endOfChar = false; | |
while (!endOfChar) | |
{ | |
char* foundItem; | |
foundItem = MoveToNext(fontFile, bdfCharStrings, 5); | |
if (foundItem == nullptr) | |
{ | |
cout << "ERROR: Invalid BDF file." << endl; | |
return false; | |
} | |
// convert pointer to index of pointer in bdfMetaStrings | |
int foundItemIndex = 0; | |
while (foundItem != bdfCharStrings[foundItemIndex]) | |
{ | |
foundItemIndex++; | |
if (foundItemIndex >= 5) | |
{ | |
cout << "Internal error: bad pointer" << endl; | |
return false; | |
} | |
} | |
switch (foundItemIndex) | |
{ | |
case 0: // ENCODING | |
fontFile >> charEncoding; | |
if (charEncoding > 255) | |
return false; | |
break; | |
case 1: // DWIDTH | |
fontFile >> chars[charEncoding].dwidth; | |
break; | |
case 2: // BBX | |
fontFile >> chars[charEncoding].bbxWidth; | |
fontFile >> chars[charEncoding].bbxHeight; | |
fontFile >> chars[charEncoding].bbxOffsetX; | |
fontFile >> chars[charEncoding].bbxOffsetY; | |
break; | |
case 3: // BITMAP | |
chars[charEncoding].bitsPerRow = ReadBitmap(fontFile, chars[charEncoding].bitmap); | |
endOfChar = true; | |
break; | |
//case 4: // ENDCHAR (this shouldn't happen) | |
// endOfChar = true; | |
// break; | |
} | |
} | |
return true; | |
} | |
// returns whether or not loading the font was successful | |
bool Font::Load(ifstream &fontFile) | |
{ | |
const char bdfStartString[5][32] = { | |
"STARTFONT", | |
"", "", "", "" | |
}; | |
if (MoveToNext(fontFile, bdfStartString, 1) == nullptr) | |
{ | |
cout << "ERROR: Not a BDF file." << endl; | |
throw; | |
} | |
const char bdfStrings[5][32] = { | |
"FONTBOUNDINGBOX", | |
"FONT_ASCENT", | |
"FONT_DESCENT", | |
"STARTCHAR", | |
"ENDFONT" | |
}; | |
bool done = false; | |
while (!done) | |
{ | |
char* foundItem; | |
foundItem = MoveToNext(fontFile, bdfStrings, 5); | |
if (foundItem == nullptr) | |
{ | |
cout << "ERROR: Invalid BDF file." << endl; | |
return false; | |
} | |
// convert pointer to index of pointer in bdfMetaStrings | |
int foundItemIndex = 0; | |
while (foundItem != bdfStrings[foundItemIndex]) | |
{ | |
foundItemIndex++; | |
if (foundItemIndex >= 5) | |
{ | |
cout << "Internal error: bad pointer" << endl; | |
return false; | |
} | |
} | |
switch (foundItemIndex) | |
{ | |
case 0: // FONTBOUNDINGBOX | |
fontFile >> bbxWidth; | |
fontFile >> bbxHeight; | |
fontFile >> bbxOffsetX; | |
fontFile >> bbxOffsetY; | |
bitsPerRow = CalcBitsPerRow(bbxWidth); | |
InitBitmaps(); | |
break; | |
case 1: // FONT_ASCENT | |
fontFile >> ascent; | |
break; | |
case 2: // FONT_DESCENT | |
fontFile >> descent; | |
break; | |
case 3: // STARTCHAR | |
if (!LoadCharacter(fontFile)) | |
done = true; | |
break; | |
case 4: | |
done = true; | |
break; | |
} | |
if (fontFile.eof()) | |
done = true; | |
} | |
return true; | |
} | |
// places the character on the bitmap and returns true iff there's enough room | |
bool FontRenderer::PutChar(Font font, char currentChar) | |
{ | |
Character myCharacter = font.chars[(unsigned char)currentChar]; | |
// coordinates of top right corner of boundary box | |
if (posX == 0) | |
{ | |
if(!monospaceMode) | |
posX = -myCharacter.bbxOffsetX - font.bbxOffsetX; | |
else | |
posX = -font.bbxOffsetX; | |
} | |
int topRightX = posX + myCharacter.bbxOffsetX + font.bbxOffsetX + myCharacter.bitsPerRow; | |
int topRightY = bmpOffsetY + font.bbxOffsetY - myCharacter.bbxOffsetY - myCharacter.bbxHeight + font.bbxHeight; | |
if (topRightX >= TERMINAL_WIDTH) | |
return false; // render bitmap & move to next line | |
for (int charbmpY = 0; charbmpY < myCharacter.bbxHeight; charbmpY++) | |
{ | |
unsigned short charbmpRow = myCharacter.bitmap[charbmpY]; | |
for (int charbmpX = 0; charbmpX < font.bitsPerRow; charbmpX++) | |
{ | |
if (charbmpRow % 2) | |
{ | |
//coordinates of the current pixel to be placed | |
int pxposX = topRightX - charbmpX; | |
int pxposY = charbmpY + topRightY; | |
if (pxposX >= 0 && pxposX < TERMINAL_WIDTH && | |
pxposY >= 0 && pxposY < bmpHeight) // assert that the pixel being written is in bounds | |
bitmap[pxposY].set(pxposX, 1); | |
} | |
charbmpRow /= 2; | |
} | |
} | |
posX += myCharacter.dwidth; | |
return true; | |
} | |
// prints out the bool matrix | |
void FontRenderer::PrintBitmap() | |
{ | |
for (int i = 0; i < bmpHeight; i += 2) | |
{ | |
if ( !compactMode || !bitmap[i].none() || !bitmap[i+1].none() ) // compactMode doesn't print blank lines | |
{ | |
for (int j = 0; j < TERMINAL_WIDTH; j++) | |
{ | |
if (bitmap[i][j] == 1) | |
{ | |
if (bitmap[i + 1][j] == 1) | |
{ | |
cout << FULL_BLOCK; | |
} | |
else | |
{ | |
cout << UPPER_HALF_BLOCK; | |
} | |
} | |
else | |
{ | |
if (bitmap[i + 1][j] == 1) | |
{ | |
cout << LOWER_HALF_BLOCK; | |
} | |
else | |
{ | |
cout << ' '; | |
//cout << "\u2591"; | |
} | |
} | |
bitmap[i].reset(j); | |
bitmap[i + 1].reset(j); | |
if (bitmap[i].none() && bitmap[i + 1].none()) | |
break; // break from loop if the rest of the characters in the current line are spaces | |
} | |
cout << endl; | |
} | |
} | |
} | |
// clear the bitmap for the next line | |
void FontRenderer::ClearBitmap() | |
{ | |
for (int i = 0; i < bmpHeight; i++) | |
{ | |
bitmap[i].reset(); | |
} | |
return; | |
} | |
void FontRenderer::InitBitmap(Font font) | |
{ | |
if ( !fullMode ) | |
{ | |
bmpHeight = font.ascent + font.descent; | |
bmpOffsetY = font.ascent - font.bbxHeight - font.bbxOffsetY; | |
} | |
else | |
{ | |
bmpHeight = font.bbxHeight; | |
bmpOffsetY = 0; | |
} | |
if (bmpHeight % 2) | |
bmpHeight++; //make bmpHeight even | |
bitmap = new bitset<TERMINAL_WIDTH>[bmpHeight]; // bitmap image containing the pixels for the current text line | |
ClearBitmap(); | |
} | |
void FontRenderer::DeleteBitmap() | |
{ | |
delete[] bitmap; | |
} | |
// main function for printing out a string | |
void FontRenderer::RenderString(Font font, char* stringToPrint) | |
{ | |
int i = 0; | |
while (stringToPrint[i] != '\0') | |
{ | |
if ( stringToPrint[i] == '\n' ) | |
{ | |
PrintBitmap(); | |
posX = 0; | |
} | |
else if ( stringToPrint[i] == '\\' && stringToPrint[i+1] == 'n' ) // "\n" becomes newline | |
{ | |
PrintBitmap(); | |
posX = 0; | |
i++; | |
} | |
else | |
{ | |
if ( stringToPrint[i] == '\\' ) // allow for "\\n" to make literal "\n" | |
i++; | |
while ( !PutChar(font, stringToPrint[i]) ) // put the current character | |
{ | |
PrintBitmap(); // if there wasn't enough room, start a new line | |
posX = 0; | |
} | |
} | |
i++; | |
} | |
PrintBitmap(); // print out the current line | |
return; | |
} | |
// huge mess | |
bool FontRenderer::ParseArgs(int argc, char* argv[], char* stringToPrint, int stpLen) | |
{ | |
int argi = 1; // start at 1 to ignore executable location | |
int argi_increment = 1; | |
while ( argi < argc ) | |
{ | |
if ( argv[argi][0] == '-' ) | |
{ | |
// handle --args as full words but -args as individual letters | |
if ( argv[argi][1] == '-' ) | |
{ | |
if ( strcmp( argv[argi] + 2, "compact" ) == 0 ) | |
{ | |
compactMode = true; | |
fullMode = true; // don't cut off any characters | |
} | |
else if ( StrStartsWith( argv[argi] + 2, "font=" ) ) | |
{ | |
if ( argv[argi] + 7 != '\0' ) | |
{ | |
fontDir = argv[argi] + 7; | |
} | |
else | |
{ | |
throw invalid_argument("No font given"); | |
} | |
} | |
else if ( strcmp( argv[argi] + 2, "font" ) == 0 ) | |
{ | |
if ( argc > argi + 1 ) | |
{ | |
fontDir = argv[argi+1]; | |
argi_increment = 2; | |
} | |
else | |
{ | |
throw invalid_argument("No font given"); | |
} | |
} | |
else if ( strcmp( argv[argi] + 2, "help" ) == 0 ) | |
{ | |
PrintHelp(); | |
return false; | |
} | |
else if ( strcmp( argv[argi] + 2, "monospace" ) == 0 ) | |
{ | |
monospaceMode = true; | |
} | |
else if ( strcmp( argv[argi] + 2, "full" ) == 0 ) | |
{ | |
fullMode = true; | |
} | |
else | |
{ | |
StrJoin(stringToPrint, argv[argi], stpLen, ' '); | |
listen = false; | |
} | |
} | |
else if ( argv[argi][1] != '\0' ) | |
{ | |
int j = 1; | |
while ( argv[argi][j] != '\0' && j < 10 ) | |
{ | |
switch ( argv[argi][j] ) | |
{ | |
case 'f': | |
if ( argc > argi + 1 ) | |
{ | |
fontDir = argv[argi+1]; | |
argi_increment += 1; | |
} | |
else | |
{ | |
throw invalid_argument("No font given"); | |
} | |
break; | |
case 'm': | |
monospaceMode = true; | |
break; | |
case 'F': | |
fullMode = true; | |
break; | |
case 'c': | |
compactMode = true; | |
fullMode = true; // don't cut off any characters | |
break; | |
case 'h': | |
case '?': | |
PrintHelp(); | |
return false; | |
break; | |
default: | |
StrJoin(stringToPrint, argv[argi], stpLen, ' '); | |
listen = false; | |
break; | |
} | |
j += 1; | |
} | |
} | |
else | |
{ | |
StrJoin(stringToPrint, argv[argi], stpLen, ' '); | |
listen = false; | |
} | |
} | |
else | |
{ | |
StrJoin(stringToPrint, argv[argi], stpLen, ' '); | |
listen = false; | |
} | |
argi += argi_increment; | |
argi_increment = 1; | |
} | |
} | |
bool FontRenderer::SetVars(ifstream& fontFile, int argc, char* argv[], char* stringToPrint, int stpLen) | |
{ | |
monospaceMode = false; | |
fullMode = false; | |
compactMode = false; | |
listen = true; | |
posX = 0; | |
if ( ParseArgs(argc, argv, stringToPrint, stpLen) ) | |
{ | |
if ( fontDir != nullptr ) | |
{ | |
fontFile.open(fontDir); | |
if ( !fontFile.is_open() ) | |
{ | |
char errorMsg[STRING_SIZE] = "Unable to open file: "; | |
StrCat(errorMsg, fontDir, STRING_SIZE); | |
throw invalid_argument(errorMsg); | |
} | |
} | |
else | |
{ | |
while ( !fontFile.is_open() ) | |
{ | |
cout << "Enter font BDF filepath [default: " << DEFAULT_FONT << "]: "; | |
char fontDir[STRING_SIZE]; | |
cin.getline(fontDir, STRING_SIZE); | |
if (fontDir[0] == '\0') //default | |
fontFile.open(DEFAULT_FONT); | |
else | |
fontFile.open(fontDir); | |
} | |
} | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
int main(int argc, char* argv[]) // input handling mostly | |
{ | |
char* stringToPrint = new char[STRING_SIZE]; | |
stringToPrint[0] = '\0'; | |
ifstream fontFile; | |
FontRenderer renderer; | |
if ( renderer.SetVars(fontFile, argc, argv, stringToPrint, STRING_SIZE) ) | |
{ | |
Font font; | |
font.Load(fontFile); | |
fontFile.close(); | |
renderer.InitBitmap(font); | |
if ( argc == 1 ) | |
cout << "Enter string to render: "; | |
if ( renderer.listen ) | |
{ | |
while ( GetLine(cin, stringToPrint, STRING_SIZE) ) | |
{ | |
renderer.RenderString(font, stringToPrint); | |
renderer.ClearBitmap(); | |
renderer.posX = 0; | |
if ( argc == 1 ) | |
cout << "Enter string to render: "; | |
} | |
} | |
else | |
{ | |
renderer.RenderString(font, stringToPrint); | |
} | |
renderer.DeleteBitmap(); | |
font.DeleteBitmaps(); | |
} | |
delete[] stringToPrint; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment