• Beginning SDL 2.0(3) SDL介绍及BMP渲染


    SDL是一个跨平台的多媒体库。为了实现跨平台,SDL提供了一个简单的界面库抽象,比如提供了SDL_Window用于表示窗口句柄,SDL_Surface、SDL_Texture、SDL_Renderer用于处理画面刷新及基本的图形绘制,提供各种事件(鼠标、键盘、游戏手柄等)输入事件、窗口消息事件用于模拟基于消息的事件处理机制。同时也提供了线程创建、销毁以及同步的机制,在此基础上上也提供了文件访问、字体渲染、多格式图片加载、混音器等扩展功能。

    正是由于SDL的跨平台特性,如果你仅仅是希望知道SDL的功能,并能够应用SDL做简单的开发,那这篇文件不适合你,建议去SDL官网上看看Lazy Foo的教程或者Beginning SDL 2.0(1) SDL功能简介

    撰写本文的主要目的在于,我希望可以在window框架下使用SDL渲染YUV数据,仅仅调用视频渲染有关的函数,其他跨平台的机制不需要,windows平台下都提供了对应的机制。或者你现有基于window框架的程序,希望使用SDL渲染视频,这也是一篇不错的介绍文章。

    一、准备工作

    我使用的开发环境是VS2010,SDL使用V2.0.0.3的发布版本,并且安装在C:dev-toolsSDL2-2.0.3目录下。请按照其他任何SDL的开发环境配置号vs的包含路径和库路径。

    同时创建一个Win32的工程,命名为0_Win32_bmp_render。

    编译并运行,会有如下窗口显示: 

    二、SDLVideoRender基类

    为了实现抽象化的概念,我们先设定下SDL视频渲染类的对外接口,并给出基本的框架。

    #include <SDL.h>
    //#include <SDL_main.h>
    #pragma comment(lib, "SDL2.lib")
    
    class SDLVideoRender
    {
    public:
        SDLVideoRender();
        virtual ~SDLVideoRender();
    
        virtual bool Init(HWND show_wnd, RECT show_rect);
        virtual void Deinit();
    
        // width x height resolution
        // data[] for YUV, stride is linesize of each raw
        virtual void Update(int width, int height, unsigned char *data[3], int stride[3]) = 0;
        virtual bool Render() = 0;
    
    protected:
        SDL_Window * m_sdl_window;
        SDL_Rect m_show_rect;
    };

    其中调用Init函数时需要指定绘制的窗口句柄及显示区域(相对于窗口的客户区坐标)。

    如果需要刷新窗口(给定YUV)数据,调用Update接口。

    Render接口用于实现画面的显示。

    m_sdl_window指针是SDL提供的窗口抽象句柄。

    SDL_Rect是SDL给出的矩形定义形式,其定义如下:

    typedef struct SDL_Rect
    {
        int x, y;
        int w, h;
    } SDL_Rect;

    那么初始化Init函数需要完成哪些功能呢?创建SDL_Window并保存显示区域,其实现代码如下:

    bool SDLVideoRender::Init(HWND show_wnd, RECT show_rect)
    {
        // 初始化窗口句柄为空或者显示区域为空
        if (nullptr == show_wnd || IsRectEmpty(&show_rect))
        {
            return false;
        }
    
        if (nullptr != m_sdl_window)
        {
            return true;
        }
    
        if (SDL_WasInit(SDL_INIT_VIDEO))
        {
            SDL_InitSubSystem(SDL_INIT_VIDEO);
        }
    
        m_sdl_window = SDL_CreateWindowFrom(show_wnd);
        if (nullptr == m_sdl_window)
        {
            return false;
        }
    
        m_show_rect.x = show_rect.left;
        m_show_rect.y = show_rect.top;
        m_show_rect.w = show_rect.right - show_rect.left;
        m_show_rect.h = show_rect.bottom - show_rect.top;
    
        return true;
    }

    Deinit函数功能正好相反,代码如下:

    void SDLVideoRender::Deinit()
    {
        if (nullptr != m_sdl_window)
        {
            SDL_DestroyWindow(m_sdl_window);
            m_sdl_window = nullptr;
        }
    }
    View Code

    OK,到此我们的SDLVideoRender基类功能基本完善,任何需要绘制渲染视频的程序都可以通过这个基类接口刷新。

     

    三、BMP渲染实现

    我们第一个实现的功能可能不是直接加载YUV数据,而是通过加载BMP位图,并渲染显示,来简单了解SDL提供的视频渲染机制。

    这里我们添加一个BmpRender类,继承自SDLVideoRender,并重写Init、Deinit、Render三个函数。类头文件如下:

    class BmpVideoRender: SDLVideoRender
    {
    public:
        BmpVideoRender();
        ~BmpVideoRender();
    
        bool Init(HWND show_wnd, RECT show_rect);
        void Deinit();
    
        // width x height resolution
        // data[] for YUV, stride is linesize of each raw
        void Update(int width, int height, unsigned char *data[3], int stride[3]){}
        bool Render();
    
    private:
        SDL_Surface * m_bmp_surface;
        
    };
    View Code

    Init函数添加了Bmp文件加载到SDL_Surface的代码。Deinit中添加了销毁SDL_Surface的代码。其实现如下:

    bool BmpVideoRender::Init(HWND show_wnd, RECT show_rect)
    {
        if (!SDLVideoRender::Init(show_wnd, show_rect))
        {
            return false;
        }
    
        m_bmp_surface = SDL_LoadBMP("hello_world.bmp");
        if (nullptr == m_bmp_surface)
        {
            return false;
        }
    
        return true;
    }
    void BmpVideoRender::Deinit()
    {
        if (nullptr != m_bmp_surface)
        {
            SDL_FreeSurface(m_bmp_surface);
            m_bmp_surface = nullptr;
        }
    
        SDLVideoRender::Deinit();
    }

    Render给出了如何将SDL_Surface渲染到SDL_Window上的机制。

    bool BmpVideoRender::Render()
    {
        if (nullptr != m_bmp_surface)
        {
            SDL_Surface * window_surface = SDL_GetWindowSurface(m_sdl_window);
            SDL_BlitScaled(m_bmp_surface, NULL, window_surface,  &m_show_rect);
    
            SDL_UpdateWindowSurfaceRects(m_sdl_window, &m_show_rect, 1);
        }
    
        return true;
    }

    原理很简单,先获取窗口的surface,直接通过SDL_BlitScaled函数将BMP位图Surface渲染到我们事先指定的区域。

    四、集成到window程序中

    既然BmpRender已经完成了,那么怎么在Window程序中调用呢?

    以win32的测试程序为例(第一步vs自动生成的代码)。

    首先在0_Win32_bmp_render.cpp文件开头添加

    HWND hMainWnd;
    BmpVideoRender bmpRender;

    并在_tWinMain的主消息循环之前添加:(注意InvalidateRect是必须的,否则你可以去掉试试。)

        if (0 != SDL_Init(SDL_INIT_VIDEO))
        {
            return FALSE;
        }
    
        RECT show_rect = {0};
        GetClientRect(hMainWnd, &show_rect);
        show_rect.right/=2;
        show_rect.bottom/=2;
        bmpRender.Init(hMainWnd, show_rect);
        InvalidateRect(hMainWnd, &show_rect, TRUE);

    在消息循环结束的时候添加:

        bmpRender.Deinit();
        SDL_Quit();

    在InitInstance中添加

    hMainWnd = hWnd;

    最后在WndProc消息处理函数的WM_PAINT的EndPaint函数之后添加

    bmpRender.Render();

    编译运行,就会得到下面效果图:(我们将图片缩放到客户区的左上角的1/4的区域)

    总结

    这是一篇很简单的SDL绘制位图的demo。基本概念涉及到SDL_Window、SDL_Rect、SDL_Surface、BMP位图加载、Surface缩放、窗口绘制区域获取和刷新。

    这里说明一点,据SDL官网介绍,SDL_Surface的实现是纯软件实现的,不支持硬件加速。如果对性能要求比较苛刻,建议不要过多依赖SDL_Surface。

    相关代码可以从我的git下载,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目录下。

    ----------------------------------------------------------------------------------------------------------------------------

    本文作者:Tocy  e-mail: zyvj@qq.com

    版权所有@2015,请勿用于商业用途,转载请注明原文地址。本人保留所有权利

  • 相关阅读:
    JQuery 练习题解析
    php 分页
    php批量删除
    PHP 多条件查询之简单租房系统
    php MySQLi数据库操作 封装类
    PHP MySQLi 增删改查
    jquery Deferred使用经验
    http2.0笔记
    window.name 跨域
    浏览器缓存读取机制大解底
  • 原文地址:https://www.cnblogs.com/tocy/p/Beginning-SDL2-3-BMP-Render.html
Copyright © 2020-2023  润新知