I recently wrote the following routine to scale text:
--scales some text, used for title function scale_text(text,tlx,tly,sx,sy,col) print(text,0,0,col) for y=0,7 do for x=0,#text*8-1 do local col=pget(x,y) if col!=0 then local nx=x*sx+tlx local ny=y*sy+tly rectfill(nx,ny,nx+sx,ny+sy,col) end end end print(text,0,0,0) end
However it depends on first printing the text to the screen.
I was trying to think of ways I could enable scaled text printing while other things are going on. Right now I just keep a black strip at the top of the screen of my current game so that I can scale text whenever I would like.
One idea I had was to create a table outside of Pico-8 that matches the Pico-8 font, and each row would be a single byte where every bit is a pixel, 8 bytes each for all characters in the font, then look up the correct bit when scaling each character, instead. Problem with that is the space it could use up, I guess.
I was kinda hoping the font would be in some kind of pseudo "system ROM" readable using PEEK, but I didn't see this listed in the memory map.
Another idea I had was to print every character, read the pixels and keep the data in a table. It'd be a sort of ugly "blip" of text right as the game starts up, though, but it might be so fast nobody would notice...
You can just do a cls() at the start of _draw, then print your text, pget it all, then cls() again and do what you normally want to do. It'll work fine. Try this in a blank cart. You'll see what I mean:
function _draw() cls(12) --clear:blue print("a",0,0,7) --text c1=pget(0,0) --should be 7 c2=pget(1,1) --should be 12 cls() --clear again print("c1="..c1,10,10,7) print("c2="..c2,10,16,7) end
I had it clear to blue, instead of just cls(), so you could see it doesn't do anything like cause a flicker or something like that.
Oh yeah! Dur. I should have realized this was possible...it's all drawn to a backbuffer first right? Nothing you drew shows up til _draw exits, correct? Thanks!
Also, if you're writing code that doesn't re-draw the screen every frame, for whatever reason, then you could first do a memcpy of the first five or six rows of vram to an offscreen ram location, write out your text to the top of the screen, make use of it, and then memcpy the saved bit back before the end of frame.
It's too bad there isn't a function, DRAWBUF(addr) or somesuch, that says where the drawing primitives write to, rather than hardcoding it to 0x6000. That would allow the sort of thing above without having to save/reload visible screen memory.
Is this still a good way to accomplish scaling text? Since this several versions old now, thought maybe there's a better way now.
But if this is still valid, I'm having trouble connecting the dots between the scale_text() function offered and the explanation given by Dylan.
I mean, I understand why the double cls() and pget() works but that info doesn't make use of the scaling function. I thought the latter referenced the first but it does not.
Old thread, but thought I would pop in with my current solution. I take advantage of the new capability to set the screen as the sprite sheet. It does not do any cls(), and is somewhat plug-and-play, although it is prone to small graphical glitches.
This requires that you also implement "oprint", or outline print.
function oprint(str,x,y,c,co) --outline print for xx=-1,1,1 do for yy=-1,1,1 do print(str,x+xx,y+yy,co) end end print(str,x,y,c) end function bigprint(str,x,y,c,co,scale) poke(0x5f54,0x60) -- screen is spritesheet. spr and sspr use screen as source oprint(str,x,y,c,co) -- outline print to desired location local x0 = x-1 local y0 = y-1 local w = #str*4 + 2 local h = 7 -- only works on single line strings for now palt(0b1111111111111111) -- set all colors transparent palt(c,false) -- only draw the text and outline colors palt(co,false) sspr(x0,y0,w,h,x0,y0,w*scale,h*scale) -- stretch the text with desired scale palt() poke(0x5f54,0x00) -- set spritesheet back to source for spr end
This is a quick and dirty solution. This works by:
- printing the text normally
- Using the screen as our sprite sheet
- using sspr to literally draw the stretched text ON TOP of the old text
An occasional issue is that the original text sometimes "peeks out" from behind the stretched text (see below). This is why I use outline-print - it does a slightly better job at covering the old text.
Interested in seeing if anyone has improvements!
Cool, thanks for sharing the code, gonna play with it. I'm wondering if you can combine it with the cls() trick to avoid having the "peek out" problem...? Because of course the words I'm using have very noticeable peeking :(
@morningtoast I have been thinking about this today, and I decided to finally do it the right way. Behold, bprint!!
Ultimately, if you want a "perfect" drawing of big text, you need a buffer to print the text normally first. My original implementation would print right on the screen, then print ON TOP of that, meaning some artifacts from print 1 would sometimes peek out from behind the new text.
This new implementation uses the first row in the sprite sheet as a buffer. I do the following:
- Copy the first row of sprites to user memory at 0x4300. This SHOULD be safe, as long as you aren't pulling any other shenanigans with user memory.
- We then black out the first row of sprites.
- The string to be printed is printed into the now-empty sprite row
- Use our new "sprites" which contain the text, we can just use SSPR to print the text
- Copy the sprites BACK to the sprite sheet from user memory, so you can continue drawing them.
I think this is a pretty plug-and-play solution to do what you want. It should be safe, again, as long as you aren't using user memory for anything else.
I was not aware of control codes! - the docs have them.
Looks like we now have lots of special characters that can modify printing behavior, including doubling text size, pausing between printing characters (nice to have the built in!), and outlining text.
Will probably play with these on the next game. For titles, it may still be nice to have a custom function to bigprint, and I don't see any codes for centering text.
@paloblancogames - Thanks for the code update. I am using it in my upcoming game, so thanks for making it drop-in ready.
@merwok - Thanks for the tip...I'll have to look up how to use custom fonts and control codes. I'm still not very good and understanding how memory functions are applied. I usually try to avoid them, lol.
If anyone has a demo/example of control codes in action for altering font sizes, etc. and can link, that'd be awesome to see. I think it's always easier to see a working example and deconstructing it to learn.
The code is a little verbose, with a few one-line functions in there. You can probably inline these and save yourself a bunch of tokens, but for the sake of an educational cart, I wanted to be long-winded to make sure things are clear. You also probably only need obprint, but I wanted to show both options.
The code you provided above I did reduce down quite a bit, it's working great. But are those control codes? The manual talks about print() command with special characters and escape codes and such. I guess I thought control codes can make the magic happen without all the poke() stuff...maybe not.
It sounded like that with these control codes you could, say, double font size with a simple print() and some extra escape characters attached rather than poking and memory stuff.
I'm probably thinking/wishing for it to be simpler and that's usually not the case with PICO-8 but even using the function you've provided is wonderful. I'll just account for the tokens from the start next time I use it.
function scale_text(str,x,y,c,scale) memcpy(0x4300,0x0,0x0200) memset(0x0,0,0x0200) poke(0x5f55,0x00) print(str,0,0,7) poke(0x5f55,0x60) local w,h = #str*4,5 pal(7,c) palt(0,true) sspr(0,0,w,h,x,y,w*scale,h*scale) pal() memcpy(0x0,0x4300,0x0200) end
[Please log in to post a comment]