音视频技术应用(9)-合并两幅图像, 使用SDL渲染并保存
本节记录下如何合并两幅图像,并且使用SDL对其渲染,然后使用QT保存合成后的图像
两幅图像我们分别选取了一幅800*500和一幅600*300的图像,目标是把它们进行横向合并:
由上图可知,如果合并完成的话,最终图像的尺寸应该是1400 * 500
首先准备好这两幅图像
800 * 500
600 * 300
并且将其拷贝到bin/x86目录下,然后在上一节的基础上,对已有的代码进行修改:
#include "sdlqtrgb.h" #include <iostream> #include <qmessagebox.h> #include <sdl/SDL.h> #pragma comment(lib, "SDL2.lib") using namespace std; static int sdl_width = 0; static int sdl_height = 0; static SDL_Window* sdl_window = NULL; static SDL_Renderer* sdl_render = NULL; static SDL_Texture* sdl_texture = NULL; static int pixel_size = 4; // 材质的像素格式是ARGB8888, 占4字节 static unsigned char* rgb = NULL; SDLQtRGB::SDLQtRGB(QWidget *parent) : QWidget(parent) { ui.setupUi(this); // 取得label的宽高 sdl_width = ui.label->width(); sdl_height = ui.label->height(); // 1. 初始化SDL if (SDL_Init(SDL_INIT_VIDEO)) { cout << SDL_GetError() << endl; return; } // 2. 创建窗口, 这里取得label所对应的窗口句柄 sdl_window = SDL_CreateWindowFrom((void*)ui.label->winId()); if (!sdl_window) { cout << SDL_GetError() << endl; return; } // 3. 创建渲染器 sdl_render = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED); if (!sdl_render) { cout << SDL_GetError() << endl; return; } // 加载两幅图片 QImage img1("001.png"); QImage img2("002.png"); if (img1.isNull() || img2.isNull()) { QMessageBox::information(this, "", "open image failed"); return; } // 计算合并后的图片的宽高 int out_w = img1.width() + img2.width(); int out_h = img1.height() > img2.height() ? img1.height() : img2.height(); // 这里img1的图片尺寸略高, 横向合并的话,取img1的高度 sdl_width = out_w; sdl_height = out_h; // 重新更新窗口的大小 resize(sdl_width, sdl_height); // 重新设置lable的位置和大小 ui.label->move(0, 0); // 令lable 移动到(0, 0)的初始位置 ui.label->resize(sdl_width, sdl_height); // 4. 根据label控件的宽高来创建材质 sdl_texture = SDL_CreateTexture(sdl_render, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, sdl_width, sdl_height ); if (!sdl_texture) { cout << SDL_GetError() << endl; return; } // 申请一块内存空间用于存放RGB数据 rgb = new unsigned char[sdl_width * sdl_height * pixel_size]; // 默认设置为透明 memset(rgb, 0, sdl_width * sdl_height * pixel_size); // 合并图片 for (int i = 0; i < sdl_height; i++) // 遍历以行数为基准 { // 首先要取得每行的起始位置,因为是横向合并,所以每行的起始位置 = 第一幅图片的行宽 + 第二幅图片的行宽)* i int begin = i * sdl_width * pixel_size; // 先复制第一幅图片这一行的内容 if (i < img1.height()) { memcpy(rgb + begin, img1.scanLine(i), img1.width() * pixel_size); // scanLine是QT提供的函数, 用于取得图片第几行的内容 } // 准备复制第二幅图像的内容,注意这里的begin不能再从起始位置开始,因为起始位置已经复制了第一幅图片的行内容,因此要加上第一行的内容,紧跟着第一幅图片这一行的 // 后面开始复制 begin += img1.width() * pixel_size; // 再复制第二幅图片这一行的内容 if (i < img2.height()) { memcpy(rgb + begin, img2.scanLine(i), img2.width() * pixel_size); } } // 将合并后的图像存储到一个新的QImage对象当中 // 注意这里保存的图像的像素格式必须与材质中的像素格式保持一致,材质中的像素格式为ARGB8888,对应QT中的像素格式为ARGB32, 否则会出错 QImage out(rgb, sdl_width, sdl_height, QImage::Format_ARGB32); out.save("out.png"); // 每隔10ms调用一次timerEvent函数 startTimer(10); } void SDLQtRGB::timerEvent(QTimerEvent* ev) { // 5. 动态更新材质信息 if (SDL_UpdateTexture(sdl_texture, NULL, rgb, sdl_width * pixel_size)) { cout << SDL_GetError() << endl; return; } // 6. 清理屏幕 if (SDL_RenderClear(sdl_render)) { cout << SDL_GetError() << endl; return; } // 7. 复制材质到渲染器对象 SDL_Rect rect; rect.x = 0; rect.y = 0; rect.w = sdl_width; rect.h = sdl_height; if (SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect)) { cout << SDL_GetError() << endl; return; } // 8. 执行渲染操作 SDL_RenderPresent(sdl_render); }
运行:
打开bin\x86目录,找到out.png图片:
右键,查看该图片属性:
可以看到合并后的图像尺寸刚好是1400*500。
<完>