I had need to display a console similar to VGA. ?This took much more pain than I expected so I’ll share the code to spare others. ?It’s in Python so you’ll have to tweak it for C, but the point is you can see all the SDL2 calls needed to get something to work. ?I think I extracted the relevant parts.
The font glyphs are stored as a 256x1 RGBA texture.??The char is from the old extended ascii font (cp850) whereas the font used (Lucida Console) is in Unicode. ?The font mapping is done once when the font texture is created. ?This simple texture works because the numbers of chars is small. ?To support a full font, the code would have to adapt to (cache) the chars used.
Since it’s modelled after VGA, it’s 80x25 with one byte of char and one byte of color information. ?Drawing is basically a lot of fill rects each with a character blended on top. ?There’s nothing smart done for blank lines or scrolling cases.
The code is not optimized much but it was good enough for now so I moved on.
I detected but didn’t render underlines.
I didn’t use sdl ttf strings for every draw because I 1) wanted only textures used, 2) had extreme color needs, 3) didn’t need/want font issues like kerning.
Maybe this helps.
-Roger
from os import path, getenv
import sdl2
import ctypes
import sdl2.sdlttf as ttf
class Console():
? ? def init(self, cpu, port):
? ? ? ? self.columns = 80
? ? ? ? self.rows = 25
? ? ? ? self.window = None
? ? ? ? self.renderer = None
? ? ? ? sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO)
? ? ? ? ttf.TTF_Init()
? ? ? ? # need fonts that display 195 extended ascii (251c unicode) like (Lucida)?
? ? ? ? for f in [“lucon.ttf”]:
? ? ? ? ? ? ? ? self.font = ttf.TTF_OpenFont(f, 20) # look locally first?
? ? ? ? ? ? ? ? if not bool(self.font): ?# font != None
? ? ? ? ? ? ? ? ? ? self.font = ttf.TTF_OpenFont(path.join(getenv(‘windir’), “Fonts”, f), 20)
? ? ? ? ? ? ? ? if bool(self.font):
? ? ? ? ? ? ? ? ? ? #print 'found ’ + f + ’ ’ + 'named ’ + ttf.TTF_FontFaceFamilyName(self.font)
? ? ? ? ? ? ? ? ? ? break
? ? ? ? if not bool(self.font):
? ? ? ? ? ? raise Exception(‘no monospace fonts found’)
? ? ? ? self.font_texture = None
? ? ? ? # remember we do not automatically display a window unless the?
? ? ? ? # the code initializes the video.
? ? def font_set(self):
? ? ? ? # copy images of all 256 chars to self.font_texture?
? ? ? ? self.font_texture = sdl2.SDL_CreateTexture(self.renderer,?
? ? ? ? ? ? sdl2.SDL_PIXELFORMAT_RGBA8888, sdl2.SDL_TEXTUREACCESS_TARGET,?
? ? ? ? ? ? self.char_width * 256, self.char_height)
? ? ? ? sdl2.SDL_SetRenderTarget(self.renderer, self.font_texture)
? ? ? ? sdl2.SDL_SetRenderDrawColor(self.renderer, 0, 0, 0, 0) # black alpha
? ? ? ? sdl2.SDL_RenderClear(self.renderer)
? ? ? ? sdl2.SDL_SetRenderDrawBlendMode(self.renderer, sdl2.SDL_BLENDMODE_NONE)
? ? ? ? r = sdl2.SDL_Rect(0, 0, self.char_width, self.char_height)
? ? ? ? white = Color(‘white’)
? ? ? ? for c in range(1, 256):
? ? ? ? ? ? #char_surface = ttf.TTF_RenderGlyph_Solid(self.font, ord(chr©.decode(‘cp850’)), white)
? ? ? ? ? ? char_surface = ttf.TTF_RenderGlyph_Blended(self.font, ord(chr©.decode(‘cp850’)), white)
? ? ? ? ? ? char_texture = sdl2.SDL_CreateTextureFromSurface(self.renderer, char_surface)
? ? ? ? ? ? r.x = self.char_width * c
? ? ? ? ? ? sdl2.SDL_RenderCopy(self.renderer, char_texture, None, r)
? ? ? ? ? ? sdl2.SDL_FreeSurface(char_surface)
? ? ? ? ? ? sdl2.SDL_DestroyTexture(char_texture)
? ? ? ? sdl2.SDL_SetRenderTarget(self.renderer, None)
? ? ? ? sdl2.SDL_SetTextureBlendMode(self.font_texture, sdl2.SDL_BLENDMODE_BLEND) # for the background
? ? def reconfigure(self):
? ? ? ? app_name = path.splitext(path.basename(argv[0]))[0]
? ? ? ? width = ctypes.c_int()
? ? ? ? ttf.TTF_GlyphMetrics(self.font, ord(‘0’), None, None, None, None, width)
? ? ? ? self.char_width = width.value
? ? ? ? self.char_height = ttf.TTF_FontLineSkip(self.font)
? ? ? ? self.window = sdl2.SDL_CreateWindow(app_name,
? ? ? ? ? ? sdl2.SDL_WINDOWPOS_UNDEFINED,
? ? ? ? ? ? sdl2.SDL_WINDOWPOS_UNDEFINED,
? ? ? ? ? ? self.columns * self.char_width, self.rows * self.char_height, 0)
? ? ? ? self.renderer = sdl2.SDL_CreateRenderer(self.window, -1, sdl2.SDL_RENDERER_ACCELERATED)
? ? ? ? sdl2.SDL_SetRenderDrawColor(self.renderer, 0, 0, 0, 255) # black
? ? ? ? sdl2.SDL_RenderClear(self.renderer)
? ? ? ? sdl2.SDL_RenderPresent(self.renderer)
? ? ? ? if self.font_texture == None:
? ? ? ? ? ? self.font_set()
? ? def draw(self):
? ? ? ? if self.window:
? ? ? ? ? ? e = sdl2.SDL_Event()
? ? ? ? ? ? if sdl2.SDL_PollEvent(ctypes.byref(e)) != 0:
? ? ? ? ? ? ? ? if e.type == sdl2.events.SDL_QUIT:
? ? ? ? ? ? ? ? ? ? sdl2.SDL_DestroyTexture(self.font_texture)
? ? ? ? ? ? ? ? ? ? sdl2.SDL_DestroyWindow(self.window)
? ? ? ? ? ? ? ? ? ? self.window = None
? ? ? ? ? ? ? ? ? ? sdl2.SDL_DestroyRenderer(self.renderer)
? ? ? ? ? ? ? ? ? ? self.renderer = None
? ? ? ? ? ? ? ? ? ? sdl2.SDL_Quit()
? ? ? ? ? ? ? ? ? ? self.cpu.keep_going = False
? ? ? ? ? ? ? ? ? ? return # exit to avoid the draw below
? ? ? ? ? ? ? ? elif e.type == sdl2.events.SDL_KEYDOWN:
? ? ? ? ? ? ? ? ? ? print “key %d (%d)” % (e.key.keysym.sym, e.key.keysym.scancode)
? ? ? ? ? ? ? ? ? ? print e.key.keysym.sym
? ? ? ? ? ? ? ? ? ? self.handle_keypress(e.key.keysym.sym)
? ? ? ? ? ? self.draw_text()
? ? def draw_text(self):
? ? ? ? r = sdl2.SDL_Rect(0, 0, self.char_width, self.char_height)
? ? ? ? char_r = sdl2.SDL_Rect(0 * self.char_width, 0, self.char_width, self.char_height)
? ? ? ? sdl2.SDL_SetRenderDrawBlendMode(self.renderer, sdl2.SDL_BLENDMODE_NONE)
? ? ? ? back_last = 0
? ? ? ? palette_index = back_last * 3
? ? ? ? # the VGA RGB palette is 6 bits per color channel. ?
? ? ? ? # So shift it up to fill a full 8 bits each channel.?
? ? ? ? sdl2.SDL_SetRenderDrawColor(self.renderer,?
? ? ? ? ? ? self.palette_data[palette_index] << 2,?
? ? ? ? ? ? self.palette_data[palette_index + 1] << 2,?
? ? ? ? ? ? self.palette_data[palette_index + 2] << 2,
? ? ? ? ? ? 0xff)
? ? ? ? fore_last = 1
? ? ? ? palette_index = fore_last * 3
? ? ? ? sdl2.SDL_SetTextureColorMod(self.font_texture,?
? ? ? ? ? ? self.palette_data[palette_index] << 2,?
? ? ? ? ? ? self.palette_data[palette_index + 1] << 2,?
? ? ? ? ? ? self.palette_data[palette_index + 2] << 2)
? ? ? ? screen_p = 0xb8000
? ? ? ? screen_y = 0
? ? ? ? for y in range(self.rows):
? ? ? ? ? ? screen_x = 0
? ? ? ? ? ? for x in range(self.columns):
? ? ? ? ? ? ? ? c = self.cpu.m.m[screen_p]
? ? ? ? ? ? ? ? attribute = self.cpu.m.m[screen_p + 1]
? ? ? ? ? ? ? ? r.x = screen_x
? ? ? ? ? ? ? ? r.y = screen_y
? ? ? ? ? ? ? ? back = (attribute >> 4) & 0b1111
? ? ? ? ? ? ? ? if back != back_last:
? ? ? ? ? ? ? ? ? ? back_last = back
? ? ? ? ? ? ? ? ? ? palette_index = back * 3
? ? ? ? ? ? ? ? ? ? # the VGA RGB palette is 6 bits per color channel. ?
? ? ? ? ? ? ? ? ? ? # So shift it up to fill a full 8 bits each channel.?
? ? ? ? ? ? ? ? ? ? sdl2.SDL_SetRenderDrawColor(self.renderer,?
? ? ? ? ? ? ? ? ? ? ? ? self.palette_data[palette_index] << 2,?
? ? ? ? ? ? ? ? ? ? ? ? self.palette_data[palette_index + 1] << 2,?
? ? ? ? ? ? ? ? ? ? ? ? self.palette_data[palette_index + 2] << 2,
? ? ? ? ? ? ? ? ? ? ? ? 0xff)
? ? ? ? ? ? ? ? sdl2.SDL_RenderFillRect(self.renderer, r)
? ? ? ? ? ? ? ? # could draw/merge all rects first, then go back and render chars
? ? ? ? ? ? ? ? fore = attribute & 0b1111
? ? ? ? ? ? ? ? if fore != fore_last:
? ? ? ? ? ? ? ? ? ? fore_last = fore
? ? ? ? ? ? ? ? ? ? palette_index = fore * 3
? ? ? ? ? ? ? ? ? ? sdl2.SDL_SetTextureColorMod(self.font_texture,?
? ? ? ? ? ? ? ? ? ? ? ? self.palette_data[palette_index] << 2,?
? ? ? ? ? ? ? ? ? ? ? ? self.palette_data[palette_index + 1] << 2,?
? ? ? ? ? ? ? ? ? ? ? ? self.palette_data[palette_index + 2] << 2)
? ? ? ? ? ? ? ? char_r.x = c * self.char_width
? ? ? ? ? ? ? ? res = sdl2.SDL_RenderCopy(self.renderer, self.font_texture, char_r, r)
? ? ? ? ? ? ? ? u = (attribute & 0b1110111) == 0b0000001
? ? ? ? ? ? ? ? if u:
? ? ? ? ? ? ? ? ? ? print 'underline ’ + chr©
? ? ? ? ? ? ? ? screen_x += self.char_width
? ? ? ? ? ? ? ? screen_p += 2
? ? ? ? ? ? screen_y += self.char_height
? ? ? ? sdl2.SDL_RenderPresent(self.renderer)On Sat, Sep 28, 2013 at 05:02:32PM +0000, NuclearDonkey wrote:
The performance hit I’m getting is coming from my console text rendering. Right now lets say we have 10 lines being rendered from a stored log of?
*
*
*
*
*
*