20_使用SDL显示BMP图片
文本的主要内容是:使用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);
- srcrect:源矩形框,代表截取纹理哪一部分出来;dstrect:目标矩形框,代表纹理渲染到 Rendering Target 哪一个部分;如下图:
- 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图片的更多相关文章
- 【秒懂音视频开发】21_显示BMP图片
文本的主要内容是:使用SDL显示一张BMP图片,算是为后面的<播放YUV>做准备. 为什么是显示BMP图片?而不是显示JPG或PNG图片? 因为SDL内置了加载BMP的API,使用起来会更 ...
- 嵌入式linux------SDL移植(am335x下显示bmp图片)
#include<stdio.h> #include "/usr/local/ffmpeg_arm/include/SDL/SDL.h" char *bmp_name[ ...
- MFC对话框显示BMP图片
1.MFC对话框显示BMP图片我们先从简单的开始吧.先分一个类: (一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID) (二) 动态载入图片(即只需要在程序中指定图片的路径即可载入) ...
- Linux framebuffer显示bmp图片【转】
本文转载自:http://blog.csdn.net/luxiaoxun/article/details/7622988 framebuffer简介 帧缓冲(framebuffer)是Linux为显示 ...
- OPENGL 显示BMP图片+旋转
VS2010/Windows 7/ 1. 需包含头文件 stdio.h, glaux.h, glut.h.需要对应的lib,并添加包含路径 2. 窗口显示用glut库的函数 3. bmp图片从本地读取 ...
- MFC下用sdl 显示bmp、rgb、yuv
#include <libsdl/SDL.h>//#include "SDL.h"#ifdef TEST_VGA16 /* Define this if you wan ...
- MFC CListCtrl 显示bmp图片
m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle()| LVS_EX_SUBITEMIMAGES | LVS_EX_GRIDLINES); ...
- VC加载显示bmp图片的函数
void ShowBitmap(HDC hdc,const char *srcpath) { HBITMAP hBitmap = (HBITMAP)::LoadImage(0, srcpath, IM ...
- MFC对话框中显示BMP,JPG图片
//************************************ // 方法说明: 显示JPG和GIF.BMP图片 // 参数说明: CDC * pDC 设 ...
- MFC 对话框Picture Control(图片控件)中静态和动态显示Bmp图片
版权声明:本文为博主原创文章,转载请注明CSDN博客源地址! 共同学习,一起进步~ https://blog.csdn.net/Eastmount/article/details/26404733 ...
随机推荐
- deque的rotate方法
deque对象支持旋转操作,可以将元素向左或向右循环移动. 例如: from collections import deque dq = deque([1, 2, 3, 4]) dq.rotate(1 ...
- react 聊聊setState异步背后的原理,react如何感知setState下的同步与异步?
壹 ❀ 引 在react中的setState是同步还是异步?react为什么要将其设计成异步?一文中,我们介绍了setState同步异步问题,解释了何种情况下同步与异步,异步带来了什么好处,以及rea ...
- JS Leetcode 1370. 上升下降字符串 题解分析,桶排序与charCodeAt fromCharCode妙用
壹 ❀ 引 本题来自LeetCode1370. 上升下降字符串,难度简单,是一道考察对于字符串遍历熟练度的题目,题目描述如下: 给你一个字符串 s ,请你根据下面的算法重新构造字符串: 从 s 中选出 ...
- Python def() 后的-> 符号的作用
python – 定义函数 def 后面的 ->,:表示的含义-> 常常出现在python函数定义的函数名后面,为函数添加元数据,描述函数返回的类型. : 表示参数的类型建议符示例: de ...
- OpenCV开发笔记(六十八):红胖子8分钟带你使用特征点Flann最邻近差值匹配识别(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- 使用矩池云 Docker 虚拟机安装VNC、Conda、Python及CUDA
矩池云虚拟机支持 Docker 使用,但是由于虚拟机目前不支持启动时传递环境变量来设置VNC.Jupyterlab 连接密码,所以我们没有创建相关基础镜像(设置固定密码容易泄漏),下面给大家介绍手动安 ...
- CoaXPress 2.0 FPGA 4 Channel Host and Device FMC Card User Manual
Hello-FPGA CoaXPress 2.0 FMC Card User Manual 4 1 CoaXPress 简介 4 2 CoaXPress 4R FMC 5 2.1 硬件特性 5 2.2 ...
- Spingboot替换掉jar包里面的@Bean
问题 如下图,我们需要替换掉JsoncCfg配置类里面的YCloudObjectMapper这个Bean. 这个Bean是在依赖的第三方jar包中的,因为用了@Bean而不是像@Component这种 ...
- 需要入门IT行业并且想做java后台小伙伴-简单谈谈后台开发Spring与SpringBoot
1.Spring能做什么 1.1.Spring的能力 1.2.Spring的生态 https://spring.io/projects/spring-boot 覆盖了: web开发 数据访问 安全控制 ...
- 【Azure App Service】当App Service中使用系统标识无法获取Access Token时
问题描述 App Serive上的应用配置了系统标识(System Identity),通过系统标识获取到访问Key Vault资源的Access Token.但这次确遇见了无法获取到正常的Acces ...

