近期在项目中遇到一个问题,如何在实现从内存中加载RBG帧数据,然后提交到hdc上显示,这里假设大家对win32程序已经很熟了,有了相关的框架,如果不熟的小伙伴可以看我的老师编写的一本书《游戏程序设计基础》,书中的附加代码对于2d和3d的项目都很实用。

下面我们来介绍如何在MyDraw函数中从内存中加载帧数据。首先帧数据是指RGB值,由于目前我只了解到在Win32中显示的图像都是位图,Bitmap,所以我们需要定义对应的格式数据,不过这里只需要两个:

1. 位图信息:

 int nx = WINDOW_WIDTH; // 图像的宽度int ny = WINDOW_HEIGHT; // 图像的高度int channels = 3; // 3代表rgb三通道,4则表示还有A,Alpha透明通道// unsigned char *data = new unsigned char[nx*ny*channels];BITMAPINFO bmi; // 创建一个位图信息数据::ZeroMemory(&bmi, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmi.bmiHeader.biWidth = nx;  // 指定位图宽度bmi.bmiHeader.biHeight = ny; // 指定位图高度bmi.bmiHeader.biPlanes = 1;  // 指定层数,位图有能是多维的bmi.bmiHeader.biBitCount = 24; // 指定每个像素的位数,这里是rgb,分别用char类型表示,共24位// 如果你的帧数据是rgba,这里应制定32bmi.bmiHeader.biCompression = BI_RGB; // BI_RGB = 0,表示压缩程度,0是无损耗,图像质量高bmi.bmiHeader.biSizeImage = nx * ny * 3; // 图像的大小

2. 帧数据 data,格式为一维rgb(a)字符数组

绘制内存中的图像

有两种方式可以直接将内存中的帧数据data绘制到hdc上:

第一种方式:

 HDC hCompatibleDC = CreateCompatibleDC(hdc);HBITMAP hCompatibleBitmap = CreateCompatibleBitmap(hdc, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight);HBITMAP hOldBitmap = (HBITMAP)SelectObject(hCompatibleDC, hCompatibleBitmap);SetDIBits(hdc, hCompatibleBitmap, 0, bmi.bmiHeader.biHeight, data, (BITMAPINFO*)&bmi.bmiHeader, DIB_RGB_COLORS);BitBlt(hdc, 0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, hCompatibleDC, 0, 0, SRCCOPY);SelectObject(hCompatibleDC, hOldBitmap);DeleteObject(hCompatibleDC);

第二种方式:

 StretchDIBits(g_hDc, 0, 0, bmi.bmiHeader.biWidth,bmi.bmiHeader.biHeight, 0, 0, bmi.bmiHeader.biWidth,bmi.bmiHeader.biHeight, data, (BITMAPINFO*)&bmi.bmiHeader,DIB_RGB_COLORS, SRCCOPY);

两种方式的效果是一样的,但是很明显第二种更简洁。参考了如下文章:

https://www.cnblogs.com/lidabo/p/3542732.html

这里需要注意,date中的帧数据(rgb(a)字符数组)中数据的顺序需要修正一下,否则得到的图像是上下颠倒的。

另外,如果结果图像倾斜了,很有可能是因为我们的原始数据是rgba格式,而我们却在位图信息中制定了rgb三通道(或者相反)。

附上全部代码:

WinMain.cpp(您需要创建win32项目才能运行)

#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>#include <windows.h>
#pragma comment(lib,"winmm.lib")      // 调用PlaySound函数所需库文件#define WINDOW_WIDTH   1500
#define WINDOW_HEIGHT   800
#define WINDOW_TITLE    L"【从内存中输出图像演示】客户端"    #include "vec3.h"   // 双引号应用的是程序目录的相对路径中的头文件// 控制台输出
HANDLE hOutput;
unsigned long lgsize;HINSTANCE g_hInst;
HWND g_hWnd;
HDC g_hDc;
DWORD   g_tPre = 0, g_tNow = 0;       //声明l两个变量来记录时间,g_tPre记录上一次绘图的时间,g_tNow记录此次准备绘图的时间int                 MyWindowsClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
void                MyDraw(HWND hwnd);int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR     lpCmdLine,int       nCmdShow)
{MSG msg = { 0 };MyWindowsClass(hInstance);if (!InitInstance(hInstance, nCmdShow)){return FALSE;}while (msg.message != WM_QUIT)       //使用while循环,如果消息不是WM_QUIT消息,就继续循环{if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))   //查看应用程序消息队列,有消息时将队列中的消息派发出去。{TranslateMessage(&msg);       //将虚拟键消息转换为字符消息DispatchMessage(&msg);           //分发一个消息给窗口程序。}else{g_tNow = GetTickCount();   //获取当前系统时间if (g_tNow - g_tPre >= 5)        //当此次循环运行与上次绘图时间相差0.005秒时再进行重绘操作MyDraw(g_hWnd);}}return msg.wParam;
}int MyWindowsClass(HINSTANCE hInstance)
{WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = (WNDPROC)WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = NULL;wcex.hCursor = NULL;wcex.hCursor = LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wcex.lpszMenuName = NULL;wcex.lpszClassName = L"gamebase";wcex.hIconSm = NULL;return RegisterClassEx(&wcex);
}BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{int i;g_hInst = hInstance;HBITMAP bmp;g_hWnd = CreateWindow(L"gamebase", WINDOW_TITLE,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);if (!g_hWnd){return FALSE;}MoveWindow(g_hWnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true); //调整窗口显示时的位置,使窗口左上角位于(250,80)处ShowWindow(g_hWnd, nCmdShow); //调用ShowWindow函数来显示窗口UpdateWindow(g_hWnd);g_hDc = GetDC(g_hWnd);// 开启命令行用于显示输出AllocConsole();hOutput = GetStdHandle(STD_OUTPUT_HANDLE);char strbuf[50] = "******HelloWorld!************\n";WriteFile(hOutput, strbuf, strlen(strbuf), &lgsize, 0);return TRUE;
}LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{PAINTSTRUCT ps;int i;switch (message){case WM_DESTROY:PostQuitMessage(0);FreeConsole();ReleaseDC(g_hWnd, g_hDc);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}void MyDraw(HWND hwnd)
{int nx = 200; // 图像宽度int ny = 100; // 图像高度int channels = 3; // 代表rgb三通道 恢复tcpsocket时改成4!unsigned char *data = new unsigned char[nx*ny*channels];// 构建内存rgb图像for (int j = ny - 1; j >= 0; j--) {for (int i = 0; i < nx; i++) {/*float r = float(i) / float(nx);float g = float(j) / float(ny);float b = 0.2;*/vec3 col(float(i) / float(nx), float(j) / float(ny), 0.2);int ir = int(255.99*col[0]);int ig = int(255.99*col[1]);int ib = int(255.99*col[2]);data[(ny - j - 1)*nx * 3 + 3 * i] = ib;data[(ny - j - 1)*nx * 3 + 3 * i + 1] = ig;data[(ny - j - 1)*nx * 3 + 3 * i + 2] = ir;// fs << ir << " " << ig << " " << ib << "\n";}}unsigned char *dataflip = new unsigned char[nx*ny*channels];// 旋转90°//for (int i = 0; i < 200 * 100 * 3; i++)//{//    dataflip[i] = data[200 * 100 * 3 - i];//}// 次对角线对称//for (int j = 0; j < 100; j++) //{//  for (int i = 0; i < 200 * 3; i+=3)//  {//     dataflip[j * 200 * 3 + i] = data[j * 200 * 3 + (199 * 3 - i)];//     dataflip[j * 200 * 3 + i + 1] = data[j * 200 * 3 + (199 * 3 - i -1)];//     dataflip[j * 200 * 3 + i + 2] = data[j * 200 * 3 + (199 * 3 - i - 2)];//    }//}// 垂直对称for (int i = 0; i < 200 * 3; i++){for (int j = 0; j < 100; j++){dataflip[j * 200 * 3 + i] = data[(99 - j) * 200 * 3 + i];}}BITMAPINFO bmi;::ZeroMemory(&bmi, sizeof(BITMAPINFO));bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmi.bmiHeader.biWidth = nx;bmi.bmiHeader.biHeight = ny;bmi.bmiHeader.biPlanes = 1;bmi.bmiHeader.biBitCount = 24; // 恢复tcpsocket时改成32!bmi.bmiHeader.biCompression = BI_RGB;bmi.bmiHeader.biSizeImage = nx * ny * channels; // 恢复tcpsocket时改成4!//HDC hCompatibleDC = CreateCompatibleDC(g_hDc);//HBITMAP hCompatibleBitmap = CreateCompatibleBitmap(g_hDc, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight);//HBITMAP hOldBitmap = (HBITMAP)SelectObject(hCompatibleDC, hCompatibleBitmap);//SetDIBits(g_hDc, hCompatibleBitmap, 0, bmi.bmiHeader.biHeight, data, (BITMAPINFO*)&bmi.bmiHeader, DIB_RGB_COLORS);//BitBlt(g_hDc, 0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, hCompatibleDC, 0, 0, SRCCOPY);//SelectObject(hCompatibleDC, hOldBitmap);//DeleteObject(hCompatibleDC);// 执行绘制StretchDIBits(g_hDc, 0, 0, bmi.bmiHeader.biWidth,bmi.bmiHeader.biHeight, 0, 0, bmi.bmiHeader.biWidth,bmi.bmiHeader.biHeight, dataflip, (BITMAPINFO*)&bmi.bmiHeader,DIB_RGB_COLORS, SRCCOPY);delete data;delete dataflip;g_tPre = GetTickCount();
}

vec3.h(取自《Raytracing in One Weekend》绪论部分,用于辅助构建内存rgb图像)

//
// Created by YouXin on 2020/2/15.
//#ifndef INC_2_1_OUTPUTANIMAGE_VEC3_H
#define INC_2_1_OUTPUTANIMAGE_VEC3_H#include <iostream>
#include <math.h>
#include <stdlib.h>class vec3 {
public:vec3() {}vec3(float e0, float e1, float e2) { e[0] = e0; e[1] = e1; e[2] = e2; }inline float x() const { return e[0]; }inline float y() const { return e[1]; }inline float z() const { return e[2]; }inline float r() const { return e[0]; }inline float g() const { return e[1]; }inline float b() const { return e[2]; }inline const vec3& operator+() const { return *this; }inline vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }inline float operator[](int i) const { return e[i]; }inline float& operator[](int i) { return e[i]; }inline vec3& operator+=(const vec3 &v2);inline vec3& operator-=(const vec3 &v2);inline vec3& operator*=(const vec3 &v2);inline vec3& operator/=(const vec3 &v2);inline vec3& operator*=(const float t);inline vec3& operator/=(const float t);inline float length() const { return sqrt(e[0]*e[0] + e[1]*e[1] + e[2]*e[2]); }inline float squared_length() const { return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; }inline void make_unit_vector();float e[3];
};
inline std::istream& operator>>(std::istream &is, vec3 &t) {is >> t.e[0] >> t.e[1] >> t.e[2];return is;
}inline std::ostream& operator<<(std::ostream &os, const vec3 &t) {os << t.e[0] << " " << t.e[1] << " " << t.e[2];return os;
}inline void vec3::make_unit_vector() {float k = 1.0 / sqrt(e[0]*e[0] + e[1]*e[1] + e[2]*e[2]);e[0] *= k; e[1] *= k; e[2] *= k;
}inline vec3 operator+(const vec3 &v1, const vec3 &v2) {return vec3(v1.e[0] + v2.e[0], v1.e[1] + v2.e[1], v1.e[2] + v2.e[2]);
}inline vec3 operator-(const vec3 &v1, const vec3 &v2) {return vec3(v1.e[0] - v2.e[0], v1.e[1] - v2.e[1], v1.e[2] - v2.e[2]);
}inline vec3 operator*(const vec3 &v1, const vec3 &v2) {return vec3(v1.e[0] * v2.e[0], v1.e[1] * v2.e[1], v1.e[2] * v2.e[2]);
}inline vec3 operator*(float t, const vec3 &v) {return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
}inline vec3 operator*(const vec3 &v, float t) {return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
}inline vec3 operator/(const vec3 &v1, const vec3 &v2) {return vec3(v1.e[0] / v2.e[0], v1.e[1] / v2.e[1], v1.e[2] / v2.e[2]);
}inline vec3 operator/(vec3 v, float t) {return vec3(v.e[0]/t, v.e[1]/t, v.e[2]/t);
}inline float dot(const vec3 &v1, const vec3 &v2) {return v1.e[0]*v2.e[0]+ v1.e[1]*v2.e[1]+ v1.e[2]*v2.e[2];
}inline vec3 cross(const vec3 &v1, const vec3 &v2) {return vec3(v1.e[1] * v2.e[2] - v1.e[2] * v2.e[1],v1.e[2] * v2.e[0] - v1.e[0] * v2.e[2],v1.e[0] * v2.e[1] - v1.e[1] * v2.e[0]);
}inline vec3& vec3::operator+=(const vec3 &v) {e[0] += v.e[0];e[1] += v.e[1];e[2] += v.e[2];return *this;
}inline vec3& vec3::operator-=(const vec3& v) {e[0] -= v.e[0];e[1] -= v.e[1];e[2] -= v.e[2];return *this;
}inline vec3& vec3::operator*=(const vec3 &v) {e[0] *= v.e[0];e[1] *= v.e[1];e[2] *= v.e[2];return *this;
}inline vec3& vec3::operator*=(const float t) {e[0] *= t;e[1] *= t;e[2] *= t;return *this;
}inline vec3& vec3::operator/=(const vec3 &v) {e[0] /= v.e[0];e[1] /= v.e[1];e[2] /= v.e[2];return *this;
}inline vec3& vec3::operator/=(const float t) {float k = 1.0/t;e[0] *= k;e[1] *= k;e[2] *= k;return *this;
}inline vec3 unit_vector(vec3 v) {return v / v.length();
}#endif //INC_2_1_OUTPUTANIMAGE_VEC3_H

vec3.cpp

//
// Created by YouXin on 2020/2/15.
//#include "vec3.h"

显示效果如下:

Win32编程之从内存中加载位图,并显示到hdc上相关推荐

  1. Windows编程 内存中加载图片并显示 Direct离屏表面的实现

    版本:VS2015 语言:C++ 前段时间去白空轨了,感觉快燃尽了.没有看Windows的书,所以博客也没更,不过请组织放心,从现在开始,即使是节假日,我也会仔细钻研DirectX的. 今天是第七章的 ...

  2. 从内存中加载并启动一个exe

    从内存中加载并启动一个exe 文章作者:Idle_ (阿呆) 信息来源:[url]http://cnxhacker.net/article/show/2821.html[/url] windows似乎 ...

  3. 从内存中加载并运行exe(两种方法)

    windows似乎只提供了一种启动进程的方法:即必须从一个可执行文件中加载并启动.      而下面这段代码就是提供一种可以直接从内存中启动一个exe的变通办法.      用途嘛,     也许可以 ...

  4. 从内存中加载DLL Delphi版(转)

    源:从内存中加载DLL DELPHI版 原文 : http://www.2ccc.com/article.asp?articleid=5784 MemLibrary.pas //从内存中加载DLL D ...

  5. SDL 从内存流中加载图像并显示

    最近做项目需要实现这么一个功能,需要将Android平板上显示的内容实时传送到投影仪上显示. 连接投影仪的机器安装了 CentOS 6,采用 SDL 开发库做 UI 界面.系统本来的需求只要在投影仪上 ...

  6. Twebbrowser从内存中加载页面

    //从内存中加载页面(比加载htm文件速度快)uses ActiveX; procedure WBLoadHTML(WebBrowser: TWebBrowser; HTMLCode: tstring ...

  7. 从内存中加载DLL DELPHI版

    //从内存中加载DLL DELPHI版 unit MemLibrary; interface uses Windows;function memLoadLibrary(pLib: Pointer): ...

  8. 从内存中加载并运行exe

    {配合anskya的AnyWhereFileToPas效果不错} { ******************************************************* } { *     ...

  9. Openlayers中加载GeoJson文件显示地图

    场景 Openlayers下载与加载geoserver的wms服务显示地图: Openlayers下载与加载geoserver的wms服务显示地图_BADAO_LIUMANG_QIZHI的博客-CSD ...

最新文章

  1. WCDMA中的URA和LA/RA
  2. 创业公司Starry拟推1G网速的无线宽带服务
  3. vSphere5.5中嵌套华为FusionCompute注意要点
  4. MySQL查询之聚合查询
  5. python字符串去头尾_带你认识优秀的python代码
  6. CentOS配置DHCP服务器
  7. 如何快速写一款小而美的“上滑无限加载的控件”?| 博文精选
  8. 面向对象的数据库db4o: 安装并使用db4o
  9. 6月读书《有效的管理者》笔记
  10. 调试和测试 Swing 代码
  11. DDR4 vs LPDDR4 vs LPDDR4x:有什么区别?
  12. 用虚拟化平台建NAS服务器,虚拟化之NAS存储
  13. 李开复清华演讲:为什么今天是人工智能的黄金时代?
  14. python识别人脸的年龄和性别_「年龄识别」人脸属性分析--性别、年龄和表情识别 - seo实验室...
  15. 魔兽星期二服务器维护,魔兽世界例行维护,假如我星期一晚上通宵,在星期二凌晨五点维护时我没退出游戏,会被强制退出么?...
  16. 论文笔记目录(ver2.0)
  17. Java生成四种格式的二维码
  18. Ubuntu与windows之间实现复制粘贴
  19. 矩阵对角线求和C++
  20. 【Valve】Dead Thread Detection Valve

热门文章

  1. camLine赢取中国建材碲化镉薄膜厂制造软件合同
  2. UE5 C++ 斯坦福 Note1
  3. 使用npm命令一些错误原因
  4. android模拟器跑的时候卡,解决小蚁安卓模拟器运行一直卡在94%的方法
  5. 【Python】Python八种数据导入方法,你掌握了吗?
  6. java 对json 格式做参数格式校验
  7. 性价比高的蓝牙耳机排行榜10强,2023年蓝牙耳机推荐盘点
  8. 最全的CSS浏览器兼容问题整理(IE6.0、IE7.0 与 FireFox)(收藏)
  9. seekg()与tellg()相关文件操作
  10. OAuth2.0授权码/oauth/authorize接口调用unauthorized异常