此文章为小结,仅供参考。
第一种情况,从桌面DC获取RGBA的数据。 32位
HDC hdc, hdcTemp; RECT rect; BYTE* bitPointer; int x, y; int red, green, blue, alpha; while(true) { hdc = GetDC(HWND_DESKTOP); GetWindowRect(hWND_Desktop, &rect); int MAX_WIDTH = rect.right; int MAX_HEIGHT = rect.bottom; hdcTemp = CreateCompatibleDC(hdc); BITMAPINFO bitmap; bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader); bitmap.bmiHeader.biWidth = MAX_WIDTH; bitmap.bmiHeader.biHeight = MAX_HEIGHT; bitmap.bmiHeader.biPlanes = 1; bitmap.bmiHeader.biBitCount = 32; bitmap.bmiHeader.biCompression = BI_RGB; bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT; bitmap.bmiHeader.biClrUsed = 0; bitmap.bmiHeader.biClrImportant = 0; HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL); SelectObject(hdcTemp, hBitmap2); BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY); for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4) { red = (int)bitPointer[i]; green = (int)bitPointer[i+1]; blue = (int)bitPointer[i+2]; alpha = (int)bitPointer[i+3]; } }
第二种情况,从文件中获取RGB数据,此处为24位
#include <Windows.h> #include <vector> #include <iostream> #include <fstream> using namespace std; void main() { HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"panda.bmp", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE); BITMAP bm; GetObject(hbitmap, sizeof(bm), &bm); //don't continue for hi color bitmaps if (bm.bmBitsPixel > 24) return; int ncolors = 1 << bm.bmBitsPixel; HDC memdc = CreateCompatibleDC(NULL); int bmpinfo_size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ncolors; std::vector<BYTE> buf(bmpinfo_size); BITMAPINFO* bmpinfo = (BITMAPINFO*)buf.data(); bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); if (!GetDIBits(memdc, hbitmap, 0, bm.bmHeight, NULL, bmpinfo, DIB_RGB_COLORS)) { DWORD err = GetLastError(); //... } int dibsize = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight; std::vector<BYTE> dib(dibsize); if (!GetDIBits(memdc, hbitmap, 0, (UINT)bm.bmHeight, &dib[0], bmpinfo, DIB_RGB_COLORS)) { DWORD err = GetLastError(); } int dibsize1 = bm.bmWidth * 4 * bm.bmHeight; //这适用于32位 的bmp //此后将得到的RGB数据写入File中用于对比 std::ofstream myfile("myoutput.txt"); for (int i = 0; i < dibsize; i += 3) { blue = (int)dib[i]; green = (int)dib[i + 1]; red = (int)dib[i + 2]; std::cout << red << " " << green << " " << blue << std::endl; myfile << red << " " << green << " " << blue << endl; } myfile.close();
//可以借助第四个例子,查看dib是否有效(测试是有效的)
//比如: HBITMAP hbmp = CreateDIBitmap(hdc, &bmih, CBM_INIT, &dib[0], &dbmi, DIB_RGB_COLORS); 图片是镜像的,还需要一些其他处理,可以参考我的另外一篇文章:https://www.cnblogs.com/strive-sun/p/11975384.html // HWND consoleWindow = GetConsoleWindow(); // HDC hdc = GetDC(consoleWindow); // int k = 0; // for (int i = 0; i < bm.bmHeight; i++) // { // for (int j = 0; j < bm.bmWidth; j++) // { // SetPixel(hdc, j, i, RGB((int)dib[k+2], (int)dib[k + 1], (int)dib[k])); // k++; // } // } getchar(); }
第三种情况, 将32位的RGBA数据写入bmp文件中,此处没有写入文件,而是复制到hdc用于直接查看
#include <windows.h> LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg = { 0 }; WNDCLASS wc = { 0 }; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); wc.lpszClassName = L"createdibsection_example"; if (!RegisterClass(&wc)) return 1; if (!CreateWindow(wc.lpszClassName, L"createdibsection example", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL)) return 2; while (GetMessage(&msg, NULL, 0, 0) > 0) DispatchMessage(&msg); return 0; } HBITMAP CreateBitmapAndFillPtrToItsData(unsigned char** ptr_data, int wd, int hgt) { HDC hdcScreen = GetDC(NULL); BITMAPINFO bmi; memset(&bmi, 0, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = wd; bmi.bmiHeader.biHeight = -hgt; // top-down bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; auto bmp = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, (void**)ptr_data, NULL, NULL); ReleaseDC(NULL, hdcScreen); return bmp; } void CopyInPixelData(unsigned char* ptr_data, int wd, int hgt) { // this is just an example for tutorial purposes ... generate a red circle // in a white field ... real code would load from a file, etc. int c_x = wd / 2; int c_y = hgt / 2; int radius = c_x; int i = 0; for (int y = 0; y < hgt; y++) { for (int x = 0; x < wd; x++) { if ((x - c_x) * (x - c_x) + (y - c_y) * (y - c_y) <= radius * radius) { ptr_data[i++] = 0; ptr_data[i++] = 0; ptr_data[i++] = 255; ptr_data[i++] = 0; } else { ptr_data[i++] = 255; ptr_data[i++] = 255; ptr_data[i++] = 255; ptr_data[i++] = 0; } } } } HBITMAP CreateBitmapFromPixelDataExample(int wd, int hgt) { // create a bitmap such that we get a pointer to where its data is stored unsigned char* ptr_data; auto bitmap = CreateBitmapAndFillPtrToItsData(&ptr_data, wd, hgt); // fill in some pixel data... CopyInPixelData(ptr_data, wd, hgt); return bitmap; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static HBITMAP bitmap; switch (message) { case WM_CREATE: { bitmap = CreateBitmapFromPixelDataExample(85, 85); } break; case WM_CLOSE: PostQuitMessage(0); break; case WM_PAINT: { RECT r; GetClientRect(hWnd, &r); auto hdc_bitmap = CreateCompatibleDC(NULL); auto hbm_old = (HBITMAP)SelectObject(hdc_bitmap, bitmap); PAINTSTRUCT ps; auto hdc = BeginPaint(hWnd, &ps); // clear bkgd FillRect(hdc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); // paint in the bitmap we generated from pixel data... BitBlt(hdc, 10, 10, 85, 85, hdc_bitmap, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); SelectObject(hdc_bitmap, hbm_old); DeleteDC(hdc_bitmap); } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
第四种情况,将24位的RGB数据写入bmp文件
// creating input unsigned char pixels[160*120*3]; for (int i=0; i<160*120*3; i++) pixels[i] = (i%4==1)*255; // An BGR (not RGB) 160x120 image. // at this point we have some input BITMAPINFOHEADER bmih; bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = 160; bmih.biHeight = -120; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biCompression = BI_RGB ; bmih.biSizeImage = 0; bmih.biXPelsPerMeter = 10; bmih.biYPelsPerMeter = 10; bmih.biClrUsed =0; bmih.biClrImportant =0; BITMAPINFO dbmi; ZeroMemory(&dbmi, sizeof(dbmi)); dbmi.bmiHeader = bmih; dbmi.bmiColors->rgbBlue = 0; dbmi.bmiColors->rgbGreen = 0; dbmi.bmiColors->rgbRed = 0; dbmi.bmiColors->rgbReserved = 0; HDC hdc = ::GetDC(NULL); HBITMAP hbmp = CreateDIBitmap(hdc, &bmih, CBM_INIT, pixels, &dbmi, DIB_RGB_COLORS); if (hbmp == NULL) { ::MessageBox(NULL, L"Could not load the desired image image", L"Error", MB_OK); return; } ::ReleaseDC(NULL, hdc); // a little test if everything is OK OpenClipboard(NULL); EmptyClipboard(); SetClipboardData(CF_BITMAP, hbmp); CloseClipboard(); // cleanup DeleteObject(hbmp);
第五种情况, 使用SetPixel将bmp(24bit)的RGB写到hdc中
#include<iostream> #include<fstream> #include <string> #include<windows.h> using namespace std; #pragma pack(1) struct header { char header[2]; int32_t filesize; int16_t reser; int16_t reser1; int32_t dataoffset; }; struct infoheader { int32_t headersize; int32_t width; int32_t height; int16_t plans; int16_t bpp; int32_t compression; int32_t datasize; int32_t re; int32_t ve; int32_t color; int32_t importantcolor; }; struct PIxel { unsigned char G; unsigned char B; unsigned char R; }; int main() { header h; infoheader info; PIxel* p; ifstream file("panda.bmp", ios::binary); if (file.is_open()) { cout << "true" << endl; file.read((char*)&h, sizeof(h)); file.read((char*)&info, sizeof(info)); cout << info.width << " " << info.height << " " << h.filesize << " " << info.bpp << endl; int pa = info.width % 4; int size = (((24 * info.width + 31) & ~31) / 8)* info.height; char* arr = new char[size]; file.read(arr, size); char* temp = arr; int sizep = info.height * info.width; p = new PIxel[sizep]; for (int i = info.height - 1; i >= 0; i--) { for (int j = 0; j < info.width; j++) { int index = i * (info.width) + j; p[index].B = *(temp++); p[index].G = *(temp++); p[index].R = *(temp++); } temp += pa; } HWND consoleWindow = GetConsoleWindow(); HDC hdc = GetDC(consoleWindow); for (int i = 0; i < info.height; i++) { for (int j = 0; j < info.width; j++) { int index = i * (info.width) + j; PIxel m = p[index]; SetPixel(hdc, j, i, RGB(m.R, m.G, m.B)); } } ReleaseDC(consoleWindow, hdc); } return 0; }
学习bmp这部分需要长时间的积累,我虽然看了很多文档以及很多例子。 到现在头脑都没完全理过来,希望这后面的学习中能够慢慢领悟吧。
一些有意思的链接: 从HBITMAP获取字节
注意:
- 获取位图的颜色数据最快的方法不是逐像素获取(GetPixel),而是使用创建一个DIB(使用
CreateDIBSection
API或类似工具)并在那里复制原始位图,或者首先分别创建原始位图。
类似地,GetDIBits
将获得位图数据的副本。这样使用CreateDIBSection的好处
是,我们可以同时使用位图和指向实际数据的指针,而不必在使用位图时同时进行两种转换。 (需要在实践中体会)
Link: 如何从HBITMAP获取RGBQUAD?
- 通常我们的位图是24或32位,即每个像素3个字节,如果需要alpha透明通道,则是4个字节(1个字节8位)。数据需要逐行布置,并且必须与DWORD对齐。如果将每个像素设置为32位,则不必担心填充行/对齐方式。如果是24位,则需要计算。
参考上面第二个例子