用Direct2D和DWM来做简单的动画效果

0.由来

画流程图的时候需要根据数据画出每帧流场图制作出“动画”,而在Win7以上平台,Direct2D和IUAnimation结合可以做出很流畅的动画来。

1.一个简单例子

先看看效果

用Direct2D和DWM来做简单的动画效果_第1张图片
这个例子是根据MS官方的一个Win32例子改在MFC对话框下实现的。这里基本没有用到IUAnimation类,而是用了DWM。下面贴代码

2.流程

a.初始化D2D相关类,初始化DWM对象
b.构造D2D绘图对象
c.画图
d.按照DWM返回值刷新绘图对象的位置

3.部分代码

//根据start end duration计算实时位置,共4种方法-线性匀速,指数
template <class T>
class Animation
{
public:
    Animation(T start, T end, T duration) :
        m_Start(start),
        m_End(end),
        m_Duration(duration)
    {
    }

    void SetStart(T start)
    {
        m_Start = start;
    }

    T GetStart()
    {
        return m_Start;
    }

    void SetEnd(T end)
    {
        m_End = end;
    }

    T GetEnd()
    {
        return m_End;
    }

    void SetDuration(T duration)
    {
        m_Duration = max(0, duration);
    }

    T GetDuration()
    {
        return m_Duration;
    }

    T GetValue(T time)
    {
        time = min(max(time, 0), m_Duration);
        return ComputeValue(time);
    }

protected:
    virtual T ComputeValue(T time) = 0;

    T m_Duration;
    T m_Start;
    T m_End;
};



template <class T>
class LinearAnimation : public Animation<T>
{
public:
    LinearAnimation(T start=0, T end=0, T duration=0) :
        Animation(start, end, duration)
    {
    }
protected:
    virtual T ComputeValue(T time)
    {
        return m_Start + ((m_End - m_Start) * (time / m_Duration));
    }
};



template <class T>
class EaseInExponentialAnimation : public Animation<T>
{
public:
    EaseInExponentialAnimation(T start=0, T end=0, T duration=0) :
        Animation(start, end, duration)
    {
    }
protected:
    T ComputeValue(T time)
    {
        return m_Start + (m_End - m_Start) * pow(2, 10 * (time/m_Duration - 1));
    }
};


template <class T>
class EaseOutExponentialAnimation : public Animation<T>
{
public:
    EaseOutExponentialAnimation(T start=0, T end=0, T duration=0) :
        Animation(start, end, duration)
    {
    }
protected:
    T ComputeValue(T time)
    {
        return m_Start + (m_End - m_Start) * (-pow(2, -10 * time/m_Duration) + 1);
    }
};



template <class T>
class EaseInOutExponentialAnimation : public Animation<T>
{
public:
    EaseInOutExponentialAnimation(T start=0, T end=0, T duration=0) :
        Animation(start, end, duration)
    {
    }
protected:
    T ComputeValue(T time)
    {
        //compute the current time relative to the midpoint
        time = time / (m_Duration / 2);
        //if we haven't reached the midpoint, we want to do the ease-in portion
        if (time < 1)
        {
            return m_Start + (m_End - m_Start)/2 * pow(2, 10 * (time - 1));
        }
        //otherwise, do the ease-out portion
        return m_Start + (m_End - m_Start)/2 * (-pow(2, -10 * --time) + 2);
    }
};

template<class Interface>
inline void SafeRelease(Interface **ppInterfaceToRelease)
{
    if (*ppInterfaceToRelease != NULL)
    {
        (*ppInterfaceToRelease)->Release();

        (*ppInterfaceToRelease) = NULL;
    }
}
//d2d操作类
class CD2D
{
public:
    CD2D();
    ~CD2D();
    HRESULT CreateResources(HWND hWnd);
    HRESULT CreateDeviceResources(void);
    HRESULT Init(void);
    HRESULT OnRender(void);
private:
    HWND m_hwnd;
    CRect rc;
    ID2D1Factory *m_pD2DFactory;
    ID2D1HwndRenderTarget *m_pRT;
    ID2D1PathGeometry *m_pPathGeometry;
    ID2D1PathGeometry *m_pObjectGeometry;
    ID2D1SolidColorBrush *m_pRedBrush;
    ID2D1SolidColorBrush *m_pYellowBrush;

    LinearAnimation<float> m_Animation;//线性

    DWM_TIMING_INFO m_DwmTimingInfo;
};
CD2D::CD2D() :
    m_hwnd(NULL),
    m_pD2DFactory(NULL),
    m_pRT(NULL),
    m_pPathGeometry(NULL),
    m_pObjectGeometry(NULL),
    m_pRedBrush(NULL),
    m_pYellowBrush(NULL),
    m_Animation()
{
}

CD2D::~CD2D()
{
    SafeRelease(&m_pD2DFactory);
    SafeRelease(&m_pRT);
    SafeRelease(&m_pPathGeometry);
    SafeRelease(&m_pObjectGeometry);
    SafeRelease(&m_pRedBrush);
    SafeRelease(&m_pYellowBrush);
}


HRESULT CD2D::CreateResources(HWND hWnd)
{
    HRESULT hr;
    ID2D1GeometrySink *pSink = NULL;

    // Create a Direct2D factory.
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);

    D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat(
        DXGI_FORMAT_B8G8R8A8_UNORM,
        D2D1_ALPHA_MODE_IGNORE
        );
    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties();
    props.pixelFormat = pixelFormat;
    props.dpiX = 96.0f;
    props.dpiY = 96.0f;

    GetClientRect(hWnd, &rc);

    // Create a Direct2D render target
    hr = m_pD2DFactory->CreateHwndRenderTarget(
        props,
        D2D1::HwndRenderTargetProperties(
            hWnd,
            D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)
            ),
        &m_pRT
        );

    if (SUCCEEDED(hr))
    {
        // Create the path geometry.
        hr = m_pD2DFactory->CreatePathGeometry(&m_pPathGeometry);
    }
    if (SUCCEEDED(hr))
    {
        // Write to the path geometry using the geometry sink. We are going to create a
        // spiral
        hr = m_pPathGeometry->Open(&pSink);
    }
    if (SUCCEEDED(hr))
    {
        D2D1_POINT_2F currentLocation = { 0, 0 };

        pSink->BeginFigure(currentLocation, D2D1_FIGURE_BEGIN_FILLED);

        D2D1_POINT_2F locDelta = { 2, 2 };
        float radius = 3;

        for (UINT i = 0; i < 30; ++i)
        {
            currentLocation.x += radius * locDelta.x;
            currentLocation.y += radius * locDelta.y;

            pSink->AddArc(
                D2D1::ArcSegment(
                    currentLocation,
                    D2D1::SizeF(2 * radius, 2 * radius), // radiusx/y
                    0.0f, // rotation angle
                    D2D1_SWEEP_DIRECTION_CLOCKWISE,
                    D2D1_ARC_SIZE_SMALL
                    )
                );

            locDelta = D2D1::Point2F(-locDelta.y, locDelta.x);

            radius += 3;
        }

        pSink->EndFigure(D2D1_FIGURE_END_OPEN);

        hr = pSink->Close();
    }
    if (SUCCEEDED(hr))
    {
        // Create the path geometry.
        hr = m_pD2DFactory->CreatePathGeometry(&m_pObjectGeometry);
    }
    if (SUCCEEDED(hr))
    {
        // Write to the object geometry using the geometry sink.
        // We are going to create a simple triangle
        hr = m_pObjectGeometry->Open(&pSink);
    }
    if (SUCCEEDED(hr))
    {
        pSink->BeginFigure(
            D2D1::Point2F(0.0f, 0.0f),
            D2D1_FIGURE_BEGIN_FILLED
            );

        const D2D1_POINT_2F ptTriangle[] = { { -10.0f, -10.0f },{ -10.0f, 10.0f },{ 0.0f, 0.0f } };
        pSink->AddLines(ptTriangle, 3);

        pSink->EndFigure(D2D1_FIGURE_END_OPEN);

        hr = pSink->Close();
    }

    SafeRelease(&pSink);

    return hr;
}


HRESULT CD2D::CreateDeviceResources(void)
{
    HRESULT hr = S_OK;
    if (m_pRT != nullptr)
    {

        if (SUCCEEDED(hr))
        {
            // Create a red brush.
            hr = m_pRT->CreateSolidColorBrush(
                D2D1::ColorF(D2D1::ColorF::Red),
                &m_pRedBrush
                );
        }
        if (SUCCEEDED(hr))
        {
            // Create a yellow brush.
            hr = m_pRT->CreateSolidColorBrush(
                D2D1::ColorF(D2D1::ColorF::Yellow),
                &m_pYellowBrush
                );
        }
    }
    return hr;

}

HRESULT CD2D::Init(void)
{
    HRESULT hr;

    float length = 0;

    hr = m_pPathGeometry->ComputeLength(
        NULL, //no transform
        &length
        );
    if (SUCCEEDED(hr))
    {
        m_Animation.SetStart(0);        //start at beginning of path
        m_Animation.SetEnd(length);     //length at end of path
        m_Animation.SetDuration(5.0f);  //seconds

        ZeroMemory(&m_DwmTimingInfo, sizeof(m_DwmTimingInfo));
        m_DwmTimingInfo.cbSize = sizeof(m_DwmTimingInfo);

        // Get the composition refresh rate. If the DWM isn't running,
        // get the refresh rate from GDI -- probably going to be 60Hz
        if (FAILED(DwmGetCompositionTimingInfo(NULL, &m_DwmTimingInfo)))
        {
            HDC hdc = GetDC(m_hwnd);
            m_DwmTimingInfo.rateCompose.uiDenominator = 1;
            m_DwmTimingInfo.rateCompose.uiNumerator = GetDeviceCaps(hdc, VREFRESH);
            ReleaseDC(m_hwnd, hdc);
        }


    }
    return hr;

}




HRESULT CD2D::OnRender(void)
{
    HRESULT hr;

    if (!(m_pRT->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))//是否堵塞
    {
        D2D1_POINT_2F point;
        D2D1_POINT_2F tangent;
        D2D1_MATRIX_3X2_F triangleMatrix;
        D2D1_SIZE_F rtSize = m_pRT->GetSize();
        float minWidthHeightScale = min(rtSize.width, rtSize.height) / 512;

        D2D1::Matrix3x2F scale = D2D1::Matrix3x2F::Scale(
            minWidthHeightScale,
            minWidthHeightScale
            );

        D2D1::Matrix3x2F translation = D2D1::Matrix3x2F::Translation(
            rtSize.width / 2,
            rtSize.height / 2
            );

        // Prepare to draw.
        m_pRT->BeginDraw();

        // Reset to identity transform
        m_pRT->SetTransform(D2D1::Matrix3x2F::Identity());

        //clear the render target contents
        m_pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));

        //center the path
        m_pRT->SetTransform(scale * translation);

        //draw the path in red
        m_pRT->DrawGeometry(m_pPathGeometry, m_pRedBrush);

        static float float_time = 0.0f;

        float length = m_Animation.GetValue(float_time);

        // Ask the geometry to give us the point that corresponds with the
        // length at the current time.
        hr = m_pPathGeometry->ComputePointAtLength(length, NULL, &point, &tangent);

        ASSERT(SUCCEEDED(hr));

        // Reorient the triangle so that it follows the
        // direction of the path.
        triangleMatrix = D2D1::Matrix3x2F(
            tangent.x, tangent.y,
            -tangent.y, tangent.x,
            point.x, point.y
            );

        m_pRT->SetTransform(triangleMatrix * scale * translation);

        // Draw the yellow triangle.
        m_pRT->FillGeometry(m_pObjectGeometry, m_pYellowBrush);

        // Commit the drawing operations.
        hr = m_pRT->EndDraw();

        if (hr == D2DERR_RECREATE_TARGET)
        {
            hr = S_OK;
        }

        // When we reach the end of the animation, loop back to the beginning.
        if (float_time >= m_Animation.GetDuration())
        {
            float_time = 0.0f;
        }
        else
        {
            float_time += static_cast<float>(m_DwmTimingInfo.rateCompose.uiDenominator) /
                static_cast<float>(m_DwmTimingInfo.rateCompose.uiNumerator);
        }

    }


    return hr;


}

对话框界面中,删除所有按钮和static文本框,声明d2d变量m_d2d,在OnInitDialog()里初始化

HRESULT hr;
    hr = m_d2d.CreateResources(GetSafeHwnd());
    if (SUCCEEDED(hr))
    {
        hr = m_d2d.CreateDeviceResources();
        if (SUCCEEDED(hr))
        {
            hr = m_d2d.Init();
        }
    }

OnPaint()

    m_d2d.OnRender();
    //  CDialogEx::OnPaint();一定要注释掉,会自己刷新

你可能感兴趣的:(MFC,Direct2D,杂项)