Variable Width Font

Here is my code for a variable width font of height 6 that I developed for the Game Jam 4. I haven’t used GIT much before, so hopefully this works. Feel free to use the code or tell me how to improve it.

VarFont6.hex (21.9 KB)

4 Likes

Wow … that looks great. So much easier ro read than my fixed font.

Okay what the heck, I also literally just made a variable width font with the same size and general style. Great minds think alike?

ArduboyCapture

I had an entirely different strategy for my font. I have an “atlas” array that has all the x values of each character. It subtracts the next x value from the previous one to get the width.

Your font looks great. I like you descender on the lower case letters and comma.

I found using a table to index into the font took up more bytes, but your mileage may vary.

I’m not sure a font on its own constitutes a non-program,
it’s more of a library than an actual program.
That said, I’m not the one judging the contest.

Personally I prefer to just use the GitHub interface.
Git itself is a convoluted pain in the neck.


As for what could be improved, a few thoughs and comments…

‘Tangible’ issues:

  • LINE_HEIGHT and LETTER_SPACING should be static, and preferably constexpr. If they aren’t static then they end up being readonly member variables, which means they consume RAM, something that could be avoided by making them static. This also reduces progmem because it allows them to be considered ‘true’ compile-time constants instead of just readonly variables.
  • In printChar, when i is 4, data has an indeterminate value because it’s not assigned a value in the else branch. This is a bug. Presumably you are hoping that data retains the value it had in the previous loop iteration?
  • Wrapping the strings in your .ino in F macros will save RAM - specifically over 100 bytes of RAM.
  • Using a default parameter in setCursor wastes progmem when setting inverse isn’t actually neccessary or desired because you’re enforcing an extra write. It would be cheaper to have two overloads of setCursor, one that sets inverse and one that does not.
  • Instead of doing ((data & 0x80) == 0x80) it’s theoretically cheaper to do ((data & 0x80) != 0), but fortunately in this case it seems the compiler realises this and performs that optimisation anyway.

Subjective and stylistic issues:

  • Using const on non-reference parameters is technically redundant. (See this SO question and this SO question.)
  • Though if you are going to stick with that, do it for all parameters - printChar has a non-const y parameter
  • Instead of making a user-defined constructor, you could use default member initialisers for each of the member variables that you want to initialise. E.g.
bool inverse = false;
int8_t cursorX = 0;
int8_t cursorY = 0;
int8_t baseX = 0;
  • It’s better to not name constants in all-caps (e.g. LINE_HEIGHT should be lineHeight). Ideally only macros should be in all-caps.
  • Naming a variable ptr when it isn’t actually a pointer is somewhat confusing.
  • Having a setCursor function whilst not making cursorX and cursorY private sends mixed messages about how the API should be used. Similarly having a setInverse while inverse is public is somewhat confusing.
  • Using short names like idx, inv and ptr harms readability.
  • Instead of (arduboy.frameCount & 0x1f) < 16 you could just use arduboy.everyXFrames(16).

There is a sample program … that’s good enough for an entry (although it falls into the useless category by itself).

1 Like

Fair point. I love a good technicality.

(Also I just realised I should have said ‘non-game’ instead of ‘non-program’. Half asleep as per usual.)

This wasn’t meant to be a submission but just part of a bigger project. If having the topic here is bad, it can be moved.

Thanks for the comments. I made most of the changes you suggested and pushed them onto the repository.

I don’t think this is equivalent, since I made a cursor with a 50% duty cycle. Using everyXFrames would flash it for 1 frame out of 16, where my code is on for 16 frames and off for 16 frames.

1 Like

Not at all. Call it a submission to the JAM - I would!

My main concern is that if this is supposed to be a library available for general use rather than an actual entry then it seems a bit odd to have it in a game jam category.
In that case it should be in the same category as other libraries (either Development or Development/Assets) so that it’s easier to find.

I’m glad you appreciated them.
Personally I think the code’s looking a lot better as a result,
and it’s certainly using less memory at least.

Fair point, I didn’t think about the implications of the 1.
It’s a cheap way to do it, but it’s not the most obvious.
Taking that into account I think (arduboy.frameCount % 32) < 16 is probably more readable.

The resulting code will be the same anyway,
the compiler always translates a modulo by a power of two into a &,
but using a modulo expresses the intent better I think.

(Ultimately this is splitting hairs anyway though,
since it’s really just a demo program for using the library.)


Since I forgot to link to it earlier, my source for the claim that only macros should be in all caps is the C++ Core Guidelines sections ES.9 and ES.32.

I know you made the change anyway,
but I wanted to point out that particular suggestion wasn’t merely personal preference,
it’s a recommendation made by beings far greater than I.

I’ll move it.

@pharap, I had a question about how to reference the Arduboy2 class from within a library class. I had the following at the top of the .cpp file:

extern Arduboy2 arduboy;

Is there a cleaner way using the constructor or something without wasting memory?

1 Like

I’m glad you mention that, because you’ve reminded me of something I forgot to point out.

Arduboy2 is derived from Arduboy2Base,
and what Arduboy2 adds on top of Arduboy2Base is the default font system,
so if someone’s using your custom font then they might want to use Arduboy2Base to save memory.
(Arduboy2Base doesn’t inherit from Print, so it doesn’t have a ‘vtable’, which saves a bit of memory.)

There is a way to refer to Arduboy2 or (better yet) Arduboy2Base throug an object’s constructor, but it will consume a bit of memory.
Specifically it will cost 2 more bytes of RAM (the size of a pointer) and possibly a bit of extra progmem,
but it’s more flexible because:

  • It doesn’t force the person to use a specific name for their arduboy object
  • The end user is free to use Arduboy2Base or Arduboy2, depending on their needs

That specific way would be to take an Arduboy2Base as a reference parameter in the constructor and store a reference to it.

E.g.

class VarFont6 : public Print
{
	// ...
private:
	Arduboy2Base & arduboy;
	// ...
public:
	// constexpr is optional
	constexpr VarFont6(Arduboy2Base & arduboy) :
		arduboy(arduboy)
	{
	}
};

However, in your case there’s also another alternative.

Because you aren’t using any of the built in drawing functions and you’re just doing your own drawing directly to the screen buffer, you could instead do what @Botisaurus’s Tinyfont does and just store a pointer to the screen buffer.

E.g.

class VarFont6 : public Print
{
	// ...
private:
	uint8_t * screenBuffer;
	// ...
public:
	constexpr VarFont6(uint8_t * screenBuffer) :
		screenBuffer(screenBuffer)
	{
	}
	// ...
};
Yet more options...

Or, if you’re feeling brave you could do:
E.g.

class VarFont6 : public Print
{
	// ...
private:
	uint8_t * screenBuffer;
	// ...
public:
	constexpr VarFont6(uint8_t (&screenBuffer)[((WIDTH * HEIGHT) / 8)]) :
		screenBuffer(&screenBuffer)
	{
	}
	// ...
};

Which forces the array passed in to be exactly the size of the Arduboy’s screen buffer (and an actual array rather than merely a pointer).

That approach can be mixed with the other approach without causing any ambiguity:

class VarFont6 : public Print
{
	// ...
private:
	uint8_t * screenBuffer;
	// ...
public:
	constexpr VarFont6(uint8_t * screenBuffer) :
		screenBuffer(screenBuffer)
	{
	}

	constexpr VarFont6(uint8_t (&screenBuffer)[((WIDTH * HEIGHT) / 8)]) :
		screenBuffer(&screenBuffer)
	{
	}
	// ...
};

But if you want the cheapest option and you’re sure that your library will only ever be used on the Arduboy then you can do this:

class VarFont6 : public Print
{
	// ...
private:
	uint8_t * screenBuffer = Arduboy2Base::sbuffer;
	// ...
};

Ultimately the option you chose should depend on how you intend the library to be used,
and whether you prefer to use less memory or provide more flexibility.

Thanks! That was just what I was looking for.

1 Like