C++ classes that implement scrolling simulated-LED displays

LedScroll demo - main dialog
LedScroll main dialog

Introduction

This is a set of C++ classes that implement scrolling simulated-LED displays.
These classes are written in standard C++, with no MFC or STL.
The project is built using the open-source MinGW toolchain.

Features of these Classes

Background

For several years, I have been experimenting with ways to utilize old DOS-era bit-mapped raster font files in Windows programs. I had been thinking about ways to generate scrolling text, but wasn't making much headway.

Then I discovered Nic Wilson's 2002 article on the CodeProject website, offering a C++ class (and demo program) which would produce horizontally- and vertically-scrolling text that resembled LED displays. This class elegantly handled the string manipulation and character mapping required for the scrolling actions, and did a nice job of utilizing existing WinAPI frame controls.

However, his project used static bitmap files to generate the display characters, which made it difficult to add other character sets. I incorporated the portions of his class which did not deal with bitmap files, into my project, and replaced the static bitmaps with classes to read bit-mapped font files and convert them into in-memory bitmaps for display.

Running the Demo Program

The demo program is a stand-alone utility. However, it expects to find the raster fonts in the directory ..\fntcol relative to the program directory.

Beginning with version 1.04 of this package, I have included a separate dialog which allows the user to view the different raster font files which are used by it. It also allows the viewer to alter any of the settable parameters in the lrender class that is used by both of these programs to render the fonts. The following image shows the header of this dialog. This font viewer window is accessed by clicking the Font Viewer button on the main dialog.

LedScroll demo - Font Viewer dialog
Font Viewer dialog

Using the Code

Classes included in this package are listed below:

Instantiating the Class

//  in resource file, within dialog-box declaration
    LTEXT           "",     IDC_LCDLEFT,     10,  42, 270, 18, 0, WS_EX_CLIENTEDGE

//  struct definition for CMatrixStatic constructor
// typedef struct led_data_s {
//    uint   ControlID ;         //  Windows ID code used for this control
//    char   *msg ;              //  displayed message string
//    char   *font_name ;        //  Name of font file to use for display
//    uint   diameter ;          //  diameter of pixels in font
//    uint   bit_gap ;           //  gap between pixels in character
//    uint   char_gap ;          //  gap between characters
//    uint   pixel_type ;        //  square or round pixels
//    int    columns ;           //  number of columns in display; 0 uses strlen(msg)
//    int    rows ;              //  number of rows in display; 0 uses 1
//    uint   new_scroll_rate ;   //  scroll rate in 0.1 seconds (or whatever timer runs at)
//    mbmd_t scroll_dir ;        //  scroll direction (left, right, up, down, static)
//    bool   set_auto_padding ;  //  Use auto-padding?
//    char   auto_padding_char ; //  Padding character, if set; 0 uses ' '
//    COLORREF bgnd ;            //  background color
//    COLORREF set_bit ;         //  color of set bits
//    COLORREF clear_bit ;       //  color of cleared bits
// } led_data_t, *led_data_p ;

static led_data_t led_left = {
   IDC_LCDLEFT, "Scrl Left..", "..\\fntcol\\readable.f08", 3, 1, 1, SQUARE_PIXELS,
   13, 1, 10, MBD_LEFT, true, '!', RGB(0, 0, 0), RGB(255, 60, 0), RGB(103, 30, 0)
} ;

static CMatrixStatic *m_lcdleft;

   //***************************************************************************
   //  instantiate the class (typically in WM_INITDIALOG / WM_CREATE handler)
   //  
   //  All of these actions must be done in this sequence,
   //  except for those labeled (optional)
   //***************************************************************************
   case WM_INITDIALOG:
      m_lcdleft        = new CMatrixStatic(hwnd, &led_left) ;
      //  start the timer for display update
      //  The class is designed to run on 100msec update
      main_timer_id = SetTimer(hwnd, IDT_TIMER_MAIN, 100, (TIMERPROC) NULL) ;
      return true; // left this out of previous version!!

   //***************************************************************************
   //  update the scrolling display(s), in WM_TIMER
   //***************************************************************************
   case WM_TIMER:
      m_lcdleft->OnTimer() ;
      return true;

   //***************************************************************************
   //  re-draw the control(s), if image was minimized and maximized
   //***************************************************************************
   case WM_SIZE:
      if (wParam == SIZE_RESTORED) {
         m_lcdleft->DialogRestored() ;
      } 
      break;

   //***************************************************************************
   //  re-draw the control(s), if image was covered and then restored
   //***************************************************************************
   case WM_ERASEBKGND:
      m_lcdleft->DialogRestored() ;
      return false;

Instantiating and using the Font Viewer

The font viewer bypasses the matrixstatic class and utilizes the lrender class directly, since it does not need the scrolling capabilities. Instantiating this class is similar to the main class:

//  define data struct for this element
static lrender_init_t test_init = {
   font_name, 3, 1, 2, SQUARE_PIXELS, 
   RGB(31, 31, 31), RGB(63, 181, 255), RGB(23, 64, 103)
} ;

   //  instantiate the object
   test_led = new lrender(hwndShowFont, &test_init) ;
   test_led->set_clipping_region() ;  //  set clipping region
   update_display() ;    //  draw the display

The only new feature is the set_clipping_region() method. The font characters are rendered within an LTEXT control, and it is possible to resize the characters so that they actually extend outside the visible control. Therefore, I set a clipping region within that control, to avoid drawing in unwanted areas. The technique for setting the clipping region is:

void lrender::set_clipping_region(void)
{
   RECT r2;
   GetClientRect(hwndParent, &r2);
   HRGN clipregion = CreateRectRgnIndirect(&r2);
   SelectClipRgn(hdcParent, clipregion);
   DeleteObject(clipregion);
}

Note that the Font Viewer dialog is 1150x840 pixels in size.