► Games

► Sound & Music

► Watches & Clocks


► Power Supplies

► Computers

► Graphics

► Thermometers

► Wearables

► Test Equipment

► Tutorials

► Libraries

► PCB-Based Projects

By processor

AVR ATtiny

► ATtiny10

► ATtiny2313

► ATtiny84

► ATtiny841

► ATtiny85

► ATtiny861

► ATtiny88

AVR ATmega

► ATmega328

► ATmega1284

AVR 0-series and 1-series

► ATmega4809

► ATtiny1604

► ATtiny1614

► ATtiny3216

► ATtiny3227

► ATtiny402

► ATtiny404

► ATtiny414

► ATtiny814

AVR DA/DB-series

► AVR128DA28

► AVR128DA32

► AVR128DA48

► AVR128DB28



► RP2040

► RA4M1

About me

  • About me
  • Twitter
  • Mastodon


RSS feed

Smooth Big Text

21st October 2020

This article describes a simple way to add automatic smoothing to bitmapped characters, so you can use a standard 6x8 pixel character set to generate smooth double-sized characters on a graphics display:


Left: Double-sized characters plotted on a graphics display.
Right: The versions smoothed using the routine in this article. 


Small OLED monochrome graphics displays are ideal for displaying instrument readings and other data, and they're usually easier to interface than classic seven-segment LED displays. The most convenient font to use is one based on 6x8 pixel characters, but this is often too small to be readable at a distance, so the solution I've used in several of my projects is to double the size of the font. This works fine, but the characters look blocky. One solution would be to provide character definitions for a 12x16 pixel font, but this would take up four times as much program memory.

This article describes a simpler solution, which automatically smooths the double-sized characters from the standard 6x8 character set to generate the appearance of a higher-resolution font.

I tested the routine with an Adafruit 128x32 I2C OLED display which uses an SSD1306 driver [1]. It would also work with the low-cost module from AliExpress [2]. The display is controlled by an ATtiny85, and it's essentially the same circuit as my earlier project Tiny Thermocouple Thermometer, so see that for the wiring and programming details.

How it works

The smoothing is performed as characters are plotted by the PlotChar() routine. The version described here is designed for plotting characters on an I2C OLED display, but it could easily be modified to work with SPI displays:

void PlotChar(int c, int line, int column) {
  // Set column address range
  Wire.write(0x21); Wire.write(column); Wire.write(column + Scale*6 - 1);
  // Set page address range
  Wire.write(0x22); Wire.write(line); Wire.write(line + Scale - 1);
  uint8_t col0 = pgm_read_byte(&CharMap[c-32][0]);
  int col0L, col0R, col1L, col1R;
  col0L = Stretch(col0);
  col0R = col0L;
  for (uint8_t col = 1 ; col < 5; col++) {
    uint8_t col1 = pgm_read_byte(&CharMap[c-32][col]);
    col1L = Stretch(col1);
    col1R = col1L;
    if (Scale == 1) Wire.write(col0);
    // Smoothing
    else {
      if (Smooth) {
        for (int i=6; i>=0; i--) {
          for (int j=1; j<3; j++) {
            if (((col0>>i & 0b11) == (3-j)) && ((col1>>i & 0b11) == j)) {
              col0R = col0R | 1<<((i*2)+j);
              col1L = col1L | 1<<((i*2)+3-j);
      Wire.write(col0L); Wire.write(col0L>>8); 
      Wire.write(col0R); Wire.write(col0R>>8);
      col0L = col1L; col0R = col1R;
    col0 = col1;
  if (Scale == 1) Wire.write(col0);
  else {
    Wire.write(col0L); Wire.write(col0L>>8); 
    Wire.write(col0R); Wire.write(col0R>>8); 

The global variable Scale allows you to select what size of character to plot. Setting it to 1 gives normal, 6x8 pixel characters, and setting it to 2 gives double-sized characters, 12x16 pixels.

The global variable Smooth allows you to select whether to smooth the double-sized characters. Setting it to false gives blocky characters, or true gives the smoothed characters described in this article. It has no effect if Scale is 1.

PlotChar() takes three parameters:

  • c is the ASCII character to be plotted, which can be from 32 (space) to 127 (block).
  • line is the line for the text. On a display with a vertical resolution of 32, line can be from 0 to 3. With double-sized characters line determines the top line.
  • column is the starting column for the text in pixels, and can be from 0 to 131 for displays with a horizontal resolution of 132.

The PlotChar() routine plots the character a column of pixels at a time, left to right. The key lines that actually do the smoothing are:

for (int j=1; j<3; j++) {
  if (((col0>>i & 0b11) == (3-j)) && ((col1>>i & 0b11) == j)) {
    col0R = col0R | 1<<((i*2)+j);
    col1L = col1L | 1<<((i*2)+3-j);

The variables col0 and col1 are two successive 8-pixel high columns in the normal-resolution character, and col0L, col0R, col1L, and col1R are the 16-pixel high left and right versions of these in the double-resolution character.

The smoothing works by checking whether one of the following two situations A or B occur anywhere in the double-resolution character:


If they do, two extra pixels are added as shown in the second diagrams.

Here's the whole Smooth Big Text program used to generate the photos at the start of this article: Smooth Big Text Program.

Earlier projects

I developed this routine for a new project I'm currently working on, but I've gone back and added it to a few earlier projects that use double-sized characters on an OLED display:

Four-Channel Thermometer

Programmable Signal Generator

  1. ^ Monochrome 128x32 I2C OLED graphic display on Adafruit.
  2. ^ 0.91 inch 128x32 I2C IIC Serial OLED LCD Display Module on AliExpress.

blog comments powered by Disqus