文本的主要内容是:使用SDL显示一张BMP图片,算是为后面的《显示YUV图片》做准备。

为什么是显示BMP图片?而不是显示JPG或PNG图片?

  • 因为SDL内置了加载BMP的API,使用起来会更加简单,便于初学者学习使用SDL
  • 如果想要轻松加载JPG、PNG等其他格式的图片,可以使用第三方库:SDL_image

png转bmp

将之前的png图片转成bmp图片

先通过 ffprobe in.png命令查看png图片的一些格式

Input #0, png_pipe, from 'in.png':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: png, rgb24(pc), 512x512, 25 tbr, 25 tbn, 25 tbc

然后通过ffmpeg命令将png图片转成bmp

ffmpeg -i in.png -s 512x512 -pix_fmt rgb24 in.bmp

宏定义

#include <SDL2/SDL.h>
#include <QDebug> // 出错了就执行goto end
#define END(judge, func) \
if (judge) { \
qDebug() << #func << "Error" << SDL_GetError(); \
goto end; \
}

SDL2 是一个纯 C 语音的库,我们在 C++ 代码中调用不需要加extern "C",是因为 SDL 内部做了判断,如果是 C++ 环境自动帮我们添加 extern "C"

变量定义

// 窗口
SDL_Window *window = nullptr;
// 渲染上下文
SDL_Renderer *renderer = nullptr;
// 像素数据
SDL_Surface *surface = nullptr;
// 纹理(直接跟特定驱动程序相关的像素数据)
SDL_Texture *texture = nullptr;

初始化子系统

// 初始化Video子系统
END(SDL_Init(SDL_INIT_VIDEO), SDL_Init);

加载BMP

#ifdef Q_OS_WIN
#define FILENAME "../test/in.bmp"
#else
#define FILENAME "/Users/zuojie/QtProjects/audio-video-dev/test/in.bmp"
#endif // 加载BMP
surface = SDL_LoadBMP(FILENAME);
END(!surface, SDL_LoadBMP);

创建窗口

// 创建窗口
window = SDL_CreateWindow(
// 窗口标题
"SDL显示BMP图片",
// 窗口的 x 坐标(SDL_WINDOWPOS_UNDEFINED:不指定 SDL_WINDOWPOS_CENTERED:中间)
SDL_WINDOWPOS_UNDEFINED,
// 窗口的 y 坐标
SDL_WINDOWPOS_UNDEFINED,
// 窗口宽度(跟图片宽度一样)
surface->w,
// 窗口高度(跟图片高度一样)
surface->h,
// 显示窗口,SDL_WindowFlags枚举值
SDL_WINDOW_SHOWN
);
END(!window, SDL_CreateWindow);

SDL_WindowFlags 枚举:

typedef enum
{
SDL_WINDOW_FULLSCREEN = 0x00000001, /**< 全屏窗口 */
SDL_WINDOW_OPENGL = 0x00000002, /**< 窗口可用于 OpenGL 上下文 */
SDL_WINDOW_SHOWN = 0x00000004, /**< 显示窗口 */
SDL_WINDOW_HIDDEN = 0x00000008, /**< 隐藏窗口 */
SDL_WINDOW_BORDERLESS = 0x00000010, /**< 不显示窗口装饰 */
SDL_WINDOW_RESIZABLE = 0x00000020, /**< 窗口可调整大小 */
SDL_WINDOW_MINIMIZED = 0x00000040, /**< 窗口最小化 */
SDL_WINDOW_MAXIMIZED = 0x00000080, /**< 窗口最大化 */
SDL_WINDOW_INPUT_GRABBED = 0x00000100, /**< 窗口可捕获键盘输入焦点 */
SDL_WINDOW_INPUT_FOCUS = 0x00000200, /**< 窗口拥有输入焦点 */
SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< 窗口拥有光标 */
SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */
SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /**< window should be created in high-DPI mode if supported.
On macOS NSHighResolutionCapable must be set true in the
application's Info.plist for this to have any effect. */
SDL_WINDOW_MOUSE_CAPTURE = 0x00004000, /**< window has mouse captured (unrelated to INPUT_GRABBED) */
SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000, /**< 窗口永远置于最前 */
SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /**< window should not be added to the taskbar */
SDL_WINDOW_UTILITY = 0x00020000, /**< window should be treated as a utility window */
SDL_WINDOW_TOOLTIP = 0x00040000, /**< window should be treated as a tooltip */
SDL_WINDOW_POPUP_MENU = 0x00080000, /**< window should be treated as a popup menu */
SDL_WINDOW_VULKAN = 0x10000000, /**< window usable for Vulkan surface */
SDL_WINDOW_METAL = 0x20000000 /**< window usable for Metal view */
} SDL_WindowFlags;

我们也可以从一个已经存在的本地窗口创建 window,传入的参数是指向本地窗口的指针。我创建了一个 QLabel,此处我们传入 QLabel 的 winId() 就可以:

创建渲染上下文

// 创建渲染上下文(默认的渲染目标是window)
renderer = SDL_CreateRenderer(window,
// 要初始化的渲染设备的索引,设置 -1 则初始化第一个支持 flags 的设备
-1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC);
if (!renderer) { // 说明开启硬件加速失败
renderer = SDL_CreateRenderer(window, -1, 0);
}
END(!renderer, SDL_CreateRenderer);

renderer 创建时传入的 window 是默认的渲染目标。SDL 文档SDL_GetRenderTarget 中也有说明。

SDL_RendererFlags:

/**
* \brief Flags used when creating a rendering context
*/
typedef enum
{
SDL_RENDERER_SOFTWARE = 0x00000001, /**< 使用软件加速 */
SDL_RENDERER_ACCELERATED = 0x00000002, /**< 使用硬件加速 */
SDL_RENDERER_PRESENTVSYNC = 0x00000004, /**< 和显示器刷新率同步 */
SDL_RENDERER_TARGETTEXTURE = 0x00000008 /**< 渲染器支持渲染到纹理 */
} SDL_RendererFlags;

这里我们是参考ffplay源码的写法:

创建纹理

// 创建纹理
texture = SDL_CreateTextureFromSurface(
renderer,
surface);
END(!texture, SDL_CreateTextureFromSurface);

渲染

// 设置绘制颜色(这里随便设置了一个颜色:黄色)
END(SDL_SetRenderDrawColor(renderer,
255, 255, 0,
SDL_ALPHA_OPAQUE),
SDL_SetRenderDrawColor); // 用DrawColor清除渲染目标
END(SDL_RenderClear(renderer),
SDL_RenderClear); // 复制纹理到渲染目标上(默认是window)可以使用SDL_SetRenderTarget()修改渲染目标
// srcrect源矩形框,dstrect目标矩形框,两者都传nullptr表示整个纹理渲染到整个目标上去
END(SDL_RenderCopy(renderer, texture, nullptr, nullptr),
SDL_RenderCopy); // 将此前的所有需要渲染的内容更新到屏幕上
SDL_RenderPresent(renderer);
  1. srcrect:源矩形框,代表截取纹理哪一部分出来;dstrect:目标矩形框,代表纹理渲染到 Rendering Target 哪一个部分;如下图:

  2. srcrect 和 dstrect 全都传 nullptr,将整一个纹理渲染到整个渲染目标上去:

延迟退出

// 延迟3秒退出
SDL_Delay(3000);

如果我想让显示的bmp图片窗口一直显示呢?

while (!isInterruptionRequested()) {
SDL_Event event;
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT:
goto end;
}
}

释放资源

end:
// 释放资源
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();

代码链接

SDL的扩展用法

如果我们没有拷贝纹理数据到渲染目标的话,窗口会是一片漆黑。我们是可以设置渲染目标背景色的:

// 设置绘制颜色(画笔颜色) SDL_ALPHA_OPAQUE = 255
END(SDL_SetRenderDrawColor(renderer, 255, 255, 0, SDL_ALPHA_OPAQUE),SDL_SetRenderDrawColor);
// 设置绘制颜色(画笔颜色)清除渲染目标
END(SDL_RenderClear(renderer),SDL_RenderClear);

也可以在窗口绘制矩形框:

// 画一个黄色矩形框
SDL_SetRenderDrawColor(renderer,255,255,0,SDL_ALPHA_OPAQUE);
rect = {0, 0, 50, 50};
// SDL_RenderDrawRect(renderer, &rect);//空心矩形框
SDL_RenderFillRect(renderer, &rect);//实心矩形框

还可以修改渲染目标。有时候需要把同一个图形在窗口绘制多次,那么我们就可以新建一个 texture,然后修改渲染目标为 texture,在当前 texture 绘制完成后,修改渲染目标为 window,再复制 texture 到 window:

// 创建Texture
SDL_Texture *ShowBmpThread::createTexture(SDL_Renderer *renderer){
// 创建纹理
SDL_Texture *texture = SDL_CreateTexture(renderer, // 渲染上下文
SDL_PIXELFORMAT_RGB24, // SDL_PixelFormatEnum,参考文档:https://wiki.libsdl.org/SDL_PixelFormatEnum
SDL_TEXTUREACCESS_TARGET, // SDL_TextureAccess,此处我们要把纹理作为渲染目标,选择:SDL_TEXTUREACCESS_TARGET,参考文档:https://wiki.libsdl.org/SDL_TextureAccess
50, // 纹理的宽
50); // 纹理的高
if (!texture) return nullptr; // 设置纹理为渲染目标
if (SDL_SetRenderTarget(renderer, texture)) return nullptr;
// 设置 texture 背景色
// if (SDL_SetRenderDrawColor(renderer, 0, 155, 0, SDL_ALPHA_OPAQUE)) return nullptr;
// if (SDL_RenderClear(renderer)) return nullptr;
// 设置绘制颜色
if (SDL_SetRenderDrawColor(renderer, 255, 255, 0, SDL_ALPHA_OPAQUE)) return nullptr;
// 绘制矩形框
SDL_Rect rect = {0, 0, 50, 50};
if (SDL_RenderDrawRect(renderer, &rect)) return nullptr;
// 绘制线条
if (SDL_RenderDrawLine(renderer, 0, 0, 50, 50)) return nullptr;
if (SDL_RenderDrawLine(renderer, 50, 0, 0, 50)) return nullptr;
return texture;
}

监听鼠标点击事件,重新绘制矩形框到渲染目标window上:

while (!isInterruptionRequested()) {
SDL_Event event;
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_QUIT:
goto end;
case SDL_MOUSEBUTTONUP:
showClick(event, renderer, texture);
break;
}
}
void ShowBmpThread::showClick(SDL_Event &event,
SDL_Renderer *renderer,
SDL_Texture *texture) {
SDL_MouseButtonEvent btn = event.button;
int w = 0;
int h = 0;
// 查询纹理宽高
if (SDL_QueryTexture(texture, nullptr, nullptr, &w, &h)) return;
int x = btn.x - (w >> 1);
int y = btn.y - (h >> 1);
SDL_Rect dstRect = {x, y, w, h}; // 清除①
// if (SDL_RenderClear(renderer)) return; // 复制纹理到渲染目标
if (SDL_RenderCopy(renderer, texture, nullptr, &dstRect)) return; // SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
// SDL_RenderDrawRect(renderer, &dstRect); // 更新渲染操作到屏幕上
SDL_RenderPresent(renderer);
}

在showClick方法中打开①处代码的注释if (SDL_RenderClear(renderer)) return;,就可以实现显示新的矩形框后清楚原有的矩形框

扩展的代码链接

注意:上面代码是在window环境下运行在子线程中的,如果是mac环境则需要放在主线程中

20_使用SDL显示BMP图片的更多相关文章

  1. 【秒懂音视频开发】21_显示BMP图片

    文本的主要内容是:使用SDL显示一张BMP图片,算是为后面的<播放YUV>做准备. 为什么是显示BMP图片?而不是显示JPG或PNG图片? 因为SDL内置了加载BMP的API,使用起来会更 ...

  2. 嵌入式linux------SDL移植(am335x下显示bmp图片)

    #include<stdio.h> #include "/usr/local/ffmpeg_arm/include/SDL/SDL.h" char *bmp_name[ ...

  3. MFC对话框显示BMP图片

    1.MFC对话框显示BMP图片我们先从简单的开始吧.先分一个类: (一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID) (二) 动态载入图片(即只需要在程序中指定图片的路径即可载入) ...

  4. Linux framebuffer显示bmp图片【转】

    本文转载自:http://blog.csdn.net/luxiaoxun/article/details/7622988 framebuffer简介 帧缓冲(framebuffer)是Linux为显示 ...

  5. OPENGL 显示BMP图片+旋转

    VS2010/Windows 7/ 1. 需包含头文件 stdio.h, glaux.h, glut.h.需要对应的lib,并添加包含路径 2. 窗口显示用glut库的函数 3. bmp图片从本地读取 ...

  6. MFC下用sdl 显示bmp、rgb、yuv

    #include <libsdl/SDL.h>//#include "SDL.h"#ifdef TEST_VGA16 /* Define this if you wan ...

  7. MFC CListCtrl 显示bmp图片

    m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle()| LVS_EX_SUBITEMIMAGES | LVS_EX_GRIDLINES); ...

  8. VC加载显示bmp图片的函数

    void ShowBitmap(HDC hdc,const char *srcpath) { HBITMAP hBitmap = (HBITMAP)::LoadImage(0, srcpath, IM ...

  9. MFC对话框中显示BMP,JPG图片

    //************************************ // 方法说明:    显示JPG和GIF.BMP图片 // 参数说明:    CDC * pDC           设 ...

  10. MFC 对话框Picture Control(图片控件)中静态和动态显示Bmp图片

    版权声明:本文为博主原创文章,转载请注明CSDN博客源地址! 共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/26404733   ...

随机推荐

  1. 打开PDB报错ORA-30013

    多租户架构,之前还在做运维的时期接触也不多.遇到多租户问题,第一反应是有些发虚的. 但实际很多问题很简单,也容易解决.本文就是一个例子. 问题:RAC节点2打开所有PDB时,报错ORA-30013. ...

  2. [JVM]逃逸分析

    逃逸分析 JVM的内存分配策略 首先回顾一下JVM的内存分配策略. JVM的内存包括方法区.堆.虚拟机栈.本地方法栈.程序计数器.一般情况下JVM运行时的数据都是存在栈和堆上的.栈用来存放一些基本变量 ...

  3. NC20909 游戏

    题目链接 题目 题目描述 有 n 个人围成一个环玩传球游戏,每轮游戏手里拿着球的那个人必须将球传给他(她)的一个朋友.游戏一共进行了 m 轮,初始球在第 a 个人手中,问游戏结束后球在第 b 个人手中 ...

  4. NC20667 数学题

    题目链接 题目 题目描述 最近,华东交通大学ACM训练基地的老阿姨被一个数学问题困扰了很久,她希望你能够帮她解决这个问题. 这个数学问题是这样的,给你一个N,要求你计算 gcd(a,b)表示a和b的最 ...

  5. Python 装饰器解析(二)

    前面一篇文章介绍了python装饰器,最后引入了functools.wraps的使用,本篇文章将对它进行深入的探究. functools模块提供了一系列的高阶函数以及对可调用对象的操作,其中为人熟知的 ...

  6. javascript 对http的get请求参数编码encodeURIComponent、encodeURI 和Java 解码

    JavaScript 代码encode functionfindNE(){ var nd = document.getElementById("NE").value; nd = e ...

  7. 【Unity3D】VideoPlayer组件

    1 简介 ​ AudioSource组件中介绍了音频的播放,本文将介绍基于 VideoPlayer 组件实现视频播放. ​ VideoPlayer 属性面板如下: Source:视频源类型,有 2 种 ...

  8. 【Lua】Lua基础语法

    1 Lua 简介 ​ Lua 是一个小巧的脚本语言,用标准C语言编写而成,由巴西里约热内卢天主教大学的 Roberto Ierusalimschy.Waldemar Celes 和 Luiz Henr ...

  9. XML和JSON的比较

    XML和JSON的比较 XML与JSON都可以用来描述或者存储数据,两者都有各自的优点,使用场景取决于需求. 描述 XML 可扩展标记语言Extensible Markup Language,是一种用 ...

  10. image could not be accessed on a registry to record its digest

    问题说明: 在管理节点执行docker stack xxx 方式运行服务,报如题错误. 问题原因: docker swarm运行需要一个镜像仓库才行,所有节点都去这个仓库统一镜像. 来看下官方的解释: ...