Logo

Overview Demo Video Splines Spline wrapper funtions Setup of points on the grid Drawing the small letters Function to draw the entire static logo Function to draw animated spinning logo

Overview The animated splash screen logo of my name is made with a few different techniques: -the large capital letters (M, D, W) are generated with splines. -the small letters are font based and drawn by a function that scales x and y independantly and mirrors if the scales are negative.
Demo Video
Splines In Allegro5, in the 'drawing primitives add-on', I use the function: 'al_draw_spline()'

    void al_draw_spline(float points[8], ALLEGRO_COLOR color, float thickness)
    Draws a Bézier spline given 4 control points.
    Parameters:
    points - An array of 4 pairs of coordinates of the 4 control points
    color - Color of the spline
    thickness - Thickness of the spline

The bezier spline is specified by the four x/y control points in the points array: points[0] and points[1] contain the coordinates of the first control point, points[2] and points[3] are the second point, etc. Control points 0 and 3 are the ends of the spline, and points 1 and 2 are guides. The curve probably won't pass through points 1 and 2, but they affect the shape of the curve between points 0 and 3 The easiest way to think of it is that the curve starts at p0, heading in the direction of p1, but curves round so that it arrives at p3 from the direction of p2.
Spline wrapper funtions I made a wrapper function for al_draw_spline() to add circles at the ends of the splines:

    void mspline(float *par, ALLEGRO_COLOR col, int thickness)
    {
       al_draw_spline(par, col, thickness);
       float r = (float)thickness * .8;
       al_draw_filled_circle(par[0], par[1], r, col);
       al_draw_filled_circle(par[6], par[7], r, col);
    }

Then I made another wrapper function to add a fading effect:

    void mfspline(float *par, int col, int thickness)
    {
       ALLEGRO_COLOR c = palette_color[col];
       float r, g, b;
       al_unmap_rgb_f(c, &r, &g, &b);

       for (int a = thickness; a>0; a--)
       {
          float f = (float)(a) / (float)(thickness); // fade in
          // float f = (float)(thickness-a) / (float)(thickness); // fade out
          mspline(par, al_map_rgb_f(r*f, g*f, b*f), a);
       }
    }

Setup of points on the grid I set up an array with space for 9 splines.

    float points[9][8];

The "M" and "W" are each made of 4 splines. The "D" is made from one spline. All of the points for these splines are on cartesian grid with the center at 0,0. The upper left corner is -200, -200 and the lower right corner is +200, +200. This was chosen so that the entire grid can be scaled in the x and y axis independently by multiplying x or y coordinates by a scaling value. There are only 3 seed splines. - the large outer arms of 'M' and 'W' are mirrored copies of spline 0 (0, 1, 4, 5) - the small inner arms of 'M' and 'W' are mirrored copies of spline 2 (2, 3, 6, 7) - 'D' is made from spline 8 with the lower points mirrored from the top ones The other splines are derived from these seed splines, using x and y axis mirroring and translation. All of the constants in 'seed_mdw()', I got from another function I wrote to set up the splines graphically. I adjusted the control points until I liked how they looked, then hard coded them into seed_mdw().

    void seed_mdw(void)
    {
       // outer arms start and end pos are all fixed
       points[0][0] = -200;
       points[0][1] = 0;
       points[0][6] = -200;
       points[0][7] = -200;

       points[1][0] = 0;
       points[1][1] = 0;
       points[1][6] = 0;
       points[1][7] = -200;

       points[4][0] = 0;
       points[4][1] = 0;

       points[4][6] = 0;
       points[4][7] = 200;

       points[5][0] = 200;
       points[5][1] = 0;
       points[5][6] = 200;
       points[5][7] = 200;

       // outer arm (spline 0) control points
       points[0][2] = -115;
       points[0][3] = -93;
       points[0][4] = -105;
       points[0][5] = -169;

       // inner arm (spline 2) control points
       points[2][0] = -170;
       points[2][1] = -170;
       points[2][2] = -123;
       points[2][3] = -168;
       points[2][4] = -107;
       points[2][5] = -147;
       points[2][6] = -91;
       points[2][7] = -131;

       // 'D' has only 1 spline, start point and first control point are mirrored in the y axis
       points[8][0] = -70;
       points[8][1] = -40;
       points[8][2] = 132;
       points[8][3] = -88;
    }

Then I set up all of the mirrored splines.

    void fill_mdw(void)
    {
       // --- outer arms ---
       // mirror spline 0 to 1, 4, 5

       // get control points from spline 0
       float cp1x = points[0][0] - points[0][2];
       float cp1y = points[0][1] - points[0][3];
       float cp2x = points[0][0] - points[0][4];
       float cp2y = points[0][1] - points[0][5];

       // apply to other splines
       points[1][2] = points[1][0] + cp1x;
       points[1][3] = points[1][1] - cp1y;
       points[1][4] = points[1][0] + cp2x;
       points[1][5] = points[1][1] - cp2y;

       points[4][2] = points[4][0] - cp1x;
       points[4][3] = points[4][1] + cp1y;
       points[4][4] = points[4][0] - cp2x;
       points[4][5] = points[4][1] + cp2y;

       points[5][2] = points[5][0] + cp1x;
       points[5][3] = points[5][1] + cp1y;
       points[5][4] = points[5][0] + cp2x;
       points[5][5] = points[5][1] + cp2y;

       // --- inner arms ---
       // mirror spline 2 to 3, 6, 7

       // get inner arm offsets from outer arm
       float ia1x = points[0][0] - points[2][0]; // inner arm x1
       float ia1y = points[0][1] - points[2][1]; // inner arm y1
       float ia2x = points[0][0] - points[2][6]; // inner arm x2
       float ia2y = points[0][1] - points[2][7]; // inner arm y2

       // get inner arm control points
       cp1x = points[2][0] - points[2][2]; // inner arm x1
       cp1y = points[2][1] - points[2][3]; // inner arm y1
       cp2x = points[2][0] - points[2][4]; // inner arm x2
       cp2y = points[2][1] - points[2][5]; // inner arm y2

       // apply to other inner arms
       points[3][0] = points[1][0] + ia1x;
       points[3][1] = points[1][1] - ia1y;
       points[3][6] = points[1][0] + ia2x;
       points[3][7] = points[1][1] - ia2y;

       points[3][2] = points[3][0] + cp1x;
       points[3][3] = points[3][1] - cp1y;
       points[3][4] = points[3][0] + cp2x;
       points[3][5] = points[3][1] - cp2y;

       points[6][0] = points[4][0] - ia1x;
       points[6][1] = points[4][1] + ia1y;
       points[6][6] = points[4][0] - ia2x;
       points[6][7] = points[4][1] + ia2y;

       points[6][2] = points[6][0] - cp1x;
       points[6][3] = points[6][1] + cp1y;
       points[6][4] = points[6][0] - cp2x;
       points[6][5] = points[6][1] + cp2y;

       points[7][0] = points[5][0] + ia1x;
       points[7][1] = points[5][1] + ia1y;
       points[7][6] = points[5][0] + ia2x;
       points[7][7] = points[5][1] + ia2y;

       points[7][2] = points[7][0] + cp1x;
       points[7][3] = points[7][1] + cp1y;
       points[7][4] = points[7][0] + cp2x;
       points[7][5] = points[7][1] + cp2y;

       // --- spline 8 - used for 'd'
       // mirror upper points to lower with same x
       points[8][6] =  points[8][0]; // same x
       points[8][7] = -points[8][1]; // mirror y
       points[8][4] =  points[8][2]; // same x
       points[8][5] = -points[8][3]; // mirror y
    }

Drawing the small letters To draw the small letters, I use the function 'idw()' to draw scaled text. X and Y can be scaled independantly, and if either scale is negative the text is mirrored. The text bitmaps are generated only once, as it was too slow to generate them every time.

    void idw(int txt, int x, int y, float x_scale, float y_scale)
    {
       int bbx1, bby1, bbw1, bbh1;
       al_get_text_dimensions(f1, "ichael", &bbx1, &bby1, &bbw1, &bbh1);

       if (logo_text_bitmaps_create)
       {
          logo_text_bitmaps_create = 0;
          al_destroy_bitmap(logo_ichael);
          logo_ichael = al_create_bitmap(bbw1,bbh1);
          al_set_target_bitmap(logo_ichael);
          al_clear_to_color(al_map_rgb(0,0,0));
          al_draw_text(f1, palette_color[8], 0-bbx1, 0-bby1, 0, "ichael");
          al_convert_mask_to_alpha(logo_ichael, al_map_rgb(0, 0, 0)) ;

          al_destroy_bitmap(logo_avid);
          logo_avid = al_create_bitmap(bbw1,bbh1);
          al_set_target_bitmap(logo_avid);
          al_clear_to_color(al_map_rgb(0,0,0));
          al_draw_text(f1, palette_color[90], 0-bbx1, 0-bby1, 0, "avid");
          al_convert_mask_to_alpha(logo_avid, al_map_rgb(0, 0, 0)) ;

          al_destroy_bitmap(logo_eiss);
          logo_eiss = al_create_bitmap(bbw1,bbh1);
          al_set_target_bitmap(logo_eiss);
          al_clear_to_color(al_map_rgb(0,0,0));
          al_draw_text(f1, palette_color[8], 0-bbx1, 0-bby1, 0, "eiss");
          al_convert_mask_to_alpha(logo_eiss, al_map_rgb(0, 0, 0)) ;
       }

       x_scale *=  72 / (float) al_get_font_line_height(f1);
       y_scale *=  24 / (float) al_get_font_line_height(f1);

       int flags = 0;
       if (x_scale < 0) flags |= ALLEGRO_FLIP_HORIZONTAL;
       if (y_scale < 0) flags |= ALLEGRO_FLIP_VERTICAL;

       // offset x pos if scale is negative
       if (x_scale < 0) x -= abs( (int) ((float)bbw1 * x_scale) );

       // offset y pos if scale is negative
       if (y_scale < 0) y -= abs( (int) ((float)bbh1 * y_scale) );

       al_set_target_backbuffer(display);

       if (txt == 1) al_draw_scaled_rotated_bitmap(logo_ichael, 0, 0, x, y, fabs(x_scale), fabs(y_scale), 0, flags);
       if (txt == 2) al_draw_scaled_rotated_bitmap(logo_avid,   0, 0, x, y, fabs(x_scale), fabs(y_scale), 0, flags);
       if (txt == 3) al_draw_scaled_rotated_bitmap(logo_eiss,   0, 0, x, y, fabs(x_scale), fabs(y_scale), 0, flags);
    }

Function to draw the entire static logo Finally here is the function that draws the whole thing:

    void draw_mdw(int x, int y, float x_scale, float y_scale, int line_thickness)
    {
       int c1 = 10; //color 1 (red)
       int c2 = 8;  //color 2 (purple)

       // determine thickness based on both scales
       float hy_scale = sqrt( (x_scale * x_scale) + (y_scale * y_scale));
       float t = 2 + hy_scale * 7;

       float draw_points[9][8];

       // apply scale
       for (int j=0; j<9; j++)
          for (int i=0; i<8; i+=2)
          {
             draw_points[j][i]   = points[j][i]   * x_scale;
             draw_points[j][i+1] = points[j][i+1] * y_scale;
          }

       // apply offset
       for (int j=0; j<9; j++)
          for (int i=0; i<8; i+=2)
          {
             draw_points[j][i] += x;
             draw_points[j][i+1] += y;
          }

       // drawing order
       int order = 0; // normal with inner arms in front
       if (x_scale < 0) order = !order;
       if (y_scale < 0) order = !order;

       if (order)
       {
          mfspline(draw_points[8], c1, t);
          mfspline(draw_points[2], c1, t);
          mfspline(draw_points[3], c1, t);
          mfspline(draw_points[6], c1, t);
          mfspline(draw_points[7], c1, t);
          mfspline(draw_points[0], c2, t);
          mfspline(draw_points[1], c2, t);
          mfspline(draw_points[4], c2, t);
          mfspline(draw_points[5], c2, t);
       }
       else
       {
          mfspline(draw_points[0], c2, t);
          mfspline(draw_points[1], c2, t);
          mfspline(draw_points[4], c2, t);
          mfspline(draw_points[5], c2, t);
          mfspline(draw_points[8], c1, t);
          mfspline(draw_points[2], c1, t);
          mfspline(draw_points[3], c1, t);
          mfspline(draw_points[6], c1, t);
          mfspline(draw_points[7], c1, t);
       }

       // show the rest of the name
       float xs = x_scale * 2;
       float ys = y_scale * 3;
       void idw(int txt, int x, int y, float x_scale, float y_scale);
       idw(1, (int)(x - 54  * x_scale), (int)(y - 130 * y_scale), xs, ys); // ichael
       idw(2, (int)(x + 90  * x_scale), (int)(y -  15 * y_scale), xs, ys); // avid
       idw(3, (int)(x + 146 * x_scale), (int)(y + 120 * y_scale), xs, ys); // eiss
    }

Function to draw animated spinning logo This function is non-blocking and needs to be called in a loop, so it can do other things like process the event loop, etc.

    int mdw_an2(void)
    {
       float x_scale = mdw_splash_logo_scale;
       float y_scale = mdw_splash_logo_scale;

       // 1st grow while rotating 256   0    - 255
       // 2nd xlink                64   256  - 319
       // 3nd static              128   320  - 447
       // 4th spin back for dual  192   448  - 639
       // 5th dual flip           256   640  - 895
       // 6th spin back to orig    64   896  - 959
       // total shrink frames 320 (mode 5 and 6)

       if (++mdw_an_seq > 959) return 1;

       // grow and spin in both axis
       if ((mdw_an_seq > -1) && (mdw_an_seq < 256))
       {
          al_fixed t = al_itofix(mdw_an_seq);
          float s = (float)mdw_an_seq / 320;
          if (s > 1) s = 1;
          x_scale = s * mdw_splash_logo_scale * al_fixtof(al_fixsin(t));
          y_scale = s * mdw_splash_logo_scale * al_fixtof(al_fixcos(t));
       }
       // fix x scale
       if ((mdw_an_seq > 255) && (mdw_an_seq < 319))
       {
          al_fixed t = al_itofix(mdw_an_seq-256);
          float s = (float)mdw_an_seq / 320;
          if (s > 1) s = 1;
          x_scale = s * mdw_splash_logo_scale * al_fixtof(al_fixsin(t));
          y_scale = s * mdw_splash_logo_scale;
       }
       // freeze
       if ((mdw_an_seq > 319) && (mdw_an_seq < 448))
       {
          x_scale = mdw_splash_logo_scale;
          y_scale = mdw_splash_logo_scale;
       }
       // spin back to prepare for dual flip...
       if ((mdw_an_seq > 447) && (mdw_an_seq < 640))
       {
          al_fixed t = al_itofix(mdw_an_seq-448);
          x_scale = mdw_splash_logo_scale * al_fixtof(al_fixcos(t));
       }
       // shrink and move
       if ((mdw_an_seq > 639) && (mdw_an_seq < 960))
       {
          mdw_splash_logo_x -= mdw_logo_x_dec;
          mdw_splash_logo_y -= mdw_logo_y_dec;
          mdw_splash_logo_scale -= mdw_logo_scale_dec;
       }
       // dual flip
       if ((mdw_an_seq > 639) && (mdw_an_seq < 896))
       {
          al_fixed t = al_itofix(mdw_an_seq-640);
          x_scale = mdw_splash_logo_scale * al_fixtof(al_fixsin(t));
          y_scale = mdw_splash_logo_scale * al_fixtof(al_fixcos(t));
       }
       // back to original
       if ((mdw_an_seq > 895) && (mdw_an_seq < 960))
       {
          al_fixed t = al_itofix(mdw_an_seq-896);
          x_scale = mdw_splash_logo_scale * al_fixtof(al_fixsin(t));
          y_scale = mdw_splash_logo_scale;
       }
       draw_mdw((int)mdw_splash_logo_x, (int)mdw_splash_logo_y, x_scale, y_scale, mdw_splash_logo_th);
       return 0;
    }