Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / MFC

Not Just an Image List Control, Neat, 3D, iTunes Style

Rate me:
Please Sign up or sign in to vote.
4.73/5 (22 votes)
29 Oct 2007CPOL4 min read 70.8K   3.5K   100   15
The image list controls shows all imaes in 3D, animate the items when selected and let control item size, transparency, positions and animation speed

Screenshot - AlbumViewer.gif

Introduction

The inspiration has been iTunes album list control. This image list control lets you display your images in a new 3D manner. No more boring images shown one after another but comes with a cool animation. The zoom level of unselected items can be controls and also the height of shadow, transparency of each unselected item, their position and animation speed.

This is a very useful control for displaying images in GUI rich applications. The control is derived from CListCtrl class so it can be extended so support conventional list control views or cool animated 3D view.

Background

As I mentioned, the inspiration was iTunes album control. Enjoying music over one of these getting cooler weekends I wondered what would it take to have cool control for showing images with smooth and neat animation as iTunes and hence took up the challenge and found out it only needed some 10 algebra equations and good use of GDI+ to achieve it.

This control loads full size images, in the demo, it uses images as large as 800x600 and by using GDI+ efficiently you can animate, zoom and control alpha without any flicker and in a very smooth manner.

This article is most useful for developers looking for more information on using GDI+ painting. Even though GDI+ got almost everything needed but could not get the images to draw like iTunes. It allows to shear the images, however doesn't allow to make height of both sides of images different. I did not really try to use matrix transformations however I think the animation and positioning of images could be done better using them.

Using the Code

To use the control in your project, add AlubumCtrl.h and AlbumCtrl.cpp, add a list control on your form/dialog box, create a member variable for this list control of type CAlbumCtrl and add the following code in your OnInitDialog or anywhere else where you want to add new items to the list control:

C++
m_ctlAlbum.AddItem(L"images\\Water lilies.jpg");
m_ctlAlbum.AddItem(L"images\\Sunset.jpg");
m_ctlAlbum.AddItem(L"images\\Winter.jpg");
m_ctlAlbum.AddItem(L"images\\Blue hills.jpg");

m_ctlAlbum.SetCurrentItem(2);

m_sldAlpha.SetRange(0,100);
m_sldAnim.SetRange(0,100);
m_sldShadow.SetRange(0,100);
m_sldZoom.SetRange(0,100);
m_sldElevation.SetRange(-300,100);

m_sldElevation.SetPos(m_ctlAlbum.GetItemElevation());

m_sldAlpha.SetPos(m_ctlAlbum.GetItemAlpha());
m_sldAnim.SetPos(m_ctlAlbum.GetAnimSpeed());
m_sldShadow.SetPos(m_ctlAlbum.GetItemShadow());
m_sldZoom.SetPos(m_ctlAlbum.GetItemZoom());

Calculating Item Positions

The following function is the most important function in placing the control and surprisingly the smallest portion of code. Well, it did take some 4 iterations of fine tuning the algebra and code before it came to this. :)

C++
RectF CAlbumCtrl::CalcItemRect(const int n, const RectF rcBase, bool bLeft)
{
    float r = m_fRatio;
    float p = pow(r,n);
    int h = rcBase.Height;
    int w = rcBase.Width;
    int y = 0;
    int x = 0;

    for(int j=1;j<=n;j++)
        y += (m_nItemY*h*pow(r,j))/100;

    if( bLeft )
    {
        int x = 0;
        for(int j=2;j<=n+1;j++)
            x += w*pow(r,j);

        return RectF(rcBase.X-x, rcBase.Y-y, w*p, h*p);
    }
        
    x = rcBase.X + w;

    for(int j=1;j<n;j++)
        x += w*pow(r,j);

    for(int j=1;j<n+1;j++)
        x -= (1-r)*w*pow(r,j);

    return RectF(x, rcBase.Y-y, w*p, h*p);
}

Drawing Items

This is the second most important function in the control. This picks up the items image, calculates the shadow width, applies alpha to image and decreasing alpha to the shadow and if required, places text over selected images.

C++
void CAlbumCtrl::DrawItem
     (const int nItem, RectF rc, Graphics &grf, float fAlpha, bool bDrawText)
{
    // centre image
    // draw the items only if they are inside the visible area
    // to keep repainting fast

    if( nItem < 0 || nItem > m_vecItems.size()-1 )
        return;

    Bitmap bitmap(m_vecItems[nItem].wszItem.c_str());

    CRect r;
    GetClientRect(&r);

    RectF rcWnd(r.left, r.top, r.Width(), r.Height());
    if(!rcWnd.IntersectsWith(rc))
        return;

    Bitmap     bmp(rc.Width, rc.Height, PixelFormat32bppARGB );

    Graphics graphic(&bmp);

    ImageAttributes imgAttrb;

    RectF rcDraw(0, 0, rc.Width, rc.Height);
    graphic.DrawImage(&bitmap, rcDraw, 0, 0, bitmap.GetWidth(), 
                      bitmap.GetHeight(), UnitPixel, &imgAttrb);

    graphic.DrawRectangle(&Pen(Color(80,80,80), 2),rcDraw);
    
    ColorMatrix bmpAlpha = {
                             1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
                             0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
                             0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
                             0.0f, 0.0f, 0.0f, fAlpha, 0.0f,
                             0.0f, 0.0f, 0.0f, 0.0f, 1.0f
                            };

    imgAttrb.SetColorMatrix(&bmpAlpha);
    grf.DrawImage(&bmp, rc, 0, 0, rc.Width, rc.Height, UnitPixel, &imgAttrb);

    int iHeight = bmp.GetHeight();
    int iWidth = bmp.GetWidth();

    // reflection percent

    if( !m_nItemShadow )
        m_nItemShadow = 1;

    float nRef = 100.0/m_nItemShadow;
    RectF rcRef = RectF(rc.X, rc.Y+rc.Height, rc.Width, rc.Height/nRef);
    Bitmap     bmpRef(rcDraw.Width, rcDraw.Height/nRef, PixelFormat32bppARGB );

    Color color, colorTemp;
    for(UINT iRow = iHeight; iRow > iHeight-iHeight/nRef; iRow--)
    {
       for(UINT iColumn = 0; iColumn < iWidth; iColumn++)
       {
           // decrease the alpha by 1 value for each line in bottom margin
           double dAlpha = (iHeight/nRef-(iHeight-iRow))*255/(iHeight/nRef);
          bmp.GetPixel(iColumn, iRow, &color);
          colorTemp.SetValue(color.MakeARGB(
             //(BYTE)(5*(iHeight/nRef-(iHeight-iRow))), 
             (BYTE)dAlpha,
             color.GetRed(),
             color.GetGreen(),
             color.GetBlue()));
          bmpRef.SetPixel(iColumn, iHeight-iRow, colorTemp);
       }
    }
    grf.DrawImage(&bmpRef, rcRef, 0, 0, rcRef.Width, rcRef.Height, 
    UnitPixel, &imgAttrb);
    if( bDrawText )
    {
        //wstring wszText = m_vecItems[nItem].wszItem; 
        //Font myFont(L"Tahoma",12,FontStyleRegular,UnitPixel);
        //
        //RectF rcText;
        //grf.MeasureString(wszText.c_str(), wszText.length(), &myFont, 
    // rcRef, &rcText);
        //grf.DrawString(wszText.c_str(),wszText.length(), &myFont, 
    // PointF(rcRef.X+rcRef.Width-rcText.Width-14, 
    // rcRef.Y+(rcRef.Height-rcText.Height)/2-rcText.Height), 
    // &SolidBrush(Color(255,255,255))); 
        //wchar_t temp[10];
        //wszText = _itow(nItem+1, temp, 10);
        //grf.DrawString(wszText.c_str(),wszText.length(), &myFont, 
    // PointF(rcRef.X+2, rcRef.Y+(rcRef.Height-rcText.Height)/2-rcText.Height), 
    // &SolidBrush(Color(255,255,255))); 
        //grf.DrawString(wszText.c_str(),wszText.length(), &myFont, 
    // PointF(rcRef.X, rcRef.Y), &SolidBrush(Color(255,255,255))); 
    }
}

Behind the Scenes Involved Maths

The most interesting portion of the control is the maths involved in positioning and animating the items. In that, there is different algebra involved in placing/animating the items on the left and right of the center selected item.

Position the Items in the Control

Calculating the Width of Any Item

Wn = W (r/100)n

Calculating the Height of Any Item

Hn = H (r/100)n

Calculating the Y Position of Any Item

Yn = Y - n

i = 1
H/e(r/100)

Calculating The X Position of Items on the Left of Selected Item

Xn = X - n+1

i = 2
W(r/100)

Calculating the X Position of Items on the Right of Selected Item

Xn = X + W + n-1

i = 1
W(r/100)i - n+1

i = 1
W(1-r/100)(r/100)

where:

  • H = Height of the selected item
  • W = Width of the selected item
  • X = X position of the selected item
  • Y = Y position of the selected item
  • r = Current zoom level in %
  • e = Current elevation of Y

Animating Items in the Control

Animating Items Towards the Left

Animating Items on the Left Side

∆X = c(Xn+1-Xn)/l
Xn = Xn + ∆X

∆Y = c(Yn+1-Yn)/l

Yn = Yn + ∆Y


∆W = c(Wn+1-Wn)/l
Wn = Wn + ∆W

∆H = c(Hn+1-Hn)/l
Hn = Hn + ∆H

Animating Items on the Right Side

∆X = c(Xn+1-Xn)/l
Xn-1 = Xn + ∆X

∆Y = c(Yn+1-Yn)/l
Yn-1 = Yn - ∆Y

∆W = c(Wn+1-Wn)/l
Wn = Wn - ∆W

∆H = c(Hn+1-Hn)/l
Hn = Hn - ∆H

Animating Items Towards the Right

Animating Items on the Left Side

∆X = c(Xn+1-Xn)/l
Xn = Xn + ∆X

∆Y = c(Yn+1-Yn)/l

Yn = Yn + ∆Y


∆W = c(Wn+1-Wn)/l
Wn = Wn + ∆W

∆H = c(Hn+1-Hn)/l
Hn = Hn + ∆H

Animating Items on the Right Side

∆X = c(Xn+1-Xn)/l
Xn = Xn + ∆X

∆Y = c(Yn+1-Yn)/l
Yn = Yn - ∆Y

∆W = c(Wn-1-Wn)/l
Wn = Wn - ∆W

∆H = c(Hn-1-Hn)/l
Hn = Hn - ∆H

where:

  • c = Current animation step
  • l = Total animation steps count

Future Features

I am planning to add view switch buttons so users can switch back/forth to Icon view/List View/Report View/Album View.

History

  • 29th October, 2007: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Hong Kong Hong Kong
innovating, managing and developing next generation media products and services

Comments and Discussions

 
GeneralMy vote of 1 Pin
Saravanan.rex6-Apr-09 19:59
Saravanan.rex6-Apr-09 19:59 
Generalcool Pin
Dr.Luiji13-Nov-07 20:47
professionalDr.Luiji13-Nov-07 20:47 
GeneralRe: cool Pin
Ashok Jaiswal14-Nov-07 16:50
Ashok Jaiswal14-Nov-07 16:50 
GeneralRe: cool Pin
Dr.Luiji15-Nov-07 10:15
professionalDr.Luiji15-Nov-07 10:15 
QuestionBarking up the wrong tree? Pin
azonenberg8-Nov-07 4:19
azonenberg8-Nov-07 4:19 
AnswerRe: Barking up the wrong tree? Pin
Ashok Jaiswal14-Nov-07 16:51
Ashok Jaiswal14-Nov-07 16:51 
GeneralPretty much unusable Pin
Scope30-Oct-07 4:09
Scope30-Oct-07 4:09 
GeneralRe: Pretty much unusable Pin
Ashok Jaiswal30-Oct-07 7:47
Ashok Jaiswal30-Oct-07 7:47 
GeneralToo wide Pin
David Crow29-Oct-07 10:29
David Crow29-Oct-07 10:29 
GeneralRe: Too wide Pin
Ashok Jaiswal29-Oct-07 10:34
Ashok Jaiswal29-Oct-07 10:34 
done, thanx for pointing out

live life to the fullest

QuestionWhat improvement!! Pin
andyj11525-Oct-07 5:24
andyj11525-Oct-07 5:24 
AnswerRe: What improvement!! Pin
Ashok Jaiswal25-Oct-07 7:46
Ashok Jaiswal25-Oct-07 7:46 
GeneralExcellent Article Pin
rajantawate1(http//www.tawateventures.com24-Oct-07 5:19
rajantawate1(http//www.tawateventures.com24-Oct-07 5:19 
GeneralRe: Excellent Article Pin
Ashok Jaiswal24-Oct-07 7:19
Ashok Jaiswal24-Oct-07 7:19 
GeneralRe: Excellent Article Pin
NarendraSinghJTV8-Oct-09 1:00
NarendraSinghJTV8-Oct-09 1:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.