ImageHelper.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #include "pch.h"
  2. #include "ImageHelper.h"
  3. #include <shlwapi.h>
  4. bool CImageHelper::SaveBitmapToFile(HBITMAP hBitmap, const std::string& filename)
  5. {
  6. //1. 创建位图文件
  7. const auto file = CreateFileA(filename.c_str(), GENERIC_WRITE,
  8. 0, nullptr, CREATE_ALWAYS,
  9. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  10. nullptr);
  11. if (file == INVALID_HANDLE_VALUE)
  12. {
  13. return false;
  14. }
  15. //2. 计算位图文件每个像素所占字节数
  16. const auto bitCount = GetBitmapBitCount();
  17. //3. 获取位图结构
  18. BITMAP bitmap;
  19. ::GetObject(hBitmap, sizeof(bitmap), reinterpret_cast<LPSTR>(&bitmap));
  20. //位图中像素字节大小(32字节对齐)
  21. const DWORD bmBitsSize = ((bitmap.bmWidth * bitCount + 31) / 32) * 4 * bitmap.bmHeight;
  22. //调色板大小
  23. const DWORD paletteSize = 0;
  24. //4. 构造位图信息头
  25. BITMAPINFOHEADER bmpInfoHeader; //位图信息头结构
  26. bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
  27. bmpInfoHeader.biWidth = bitmap.bmWidth;
  28. bmpInfoHeader.biHeight = bitmap.bmHeight;
  29. bmpInfoHeader.biPlanes = 1;
  30. bmpInfoHeader.biBitCount = bitCount;
  31. bmpInfoHeader.biCompression = BI_RGB;
  32. bmpInfoHeader.biSizeImage = 0;
  33. bmpInfoHeader.biXPelsPerMeter = 0;
  34. bmpInfoHeader.biYPelsPerMeter = 0;
  35. bmpInfoHeader.biClrImportant = 0;
  36. bmpInfoHeader.biClrUsed = 0;
  37. //5. 构造位图文件头
  38. BITMAPFILEHEADER bmpFileHeader;
  39. bmpFileHeader.bfType = 0x4D42; //"BM"
  40. //位图文件大小
  41. const DWORD dibSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paletteSize + bmBitsSize;
  42. bmpFileHeader.bfSize = dibSize;
  43. bmpFileHeader.bfReserved1 = 0;
  44. bmpFileHeader.bfReserved2 = 0;
  45. bmpFileHeader.bfOffBits = static_cast<DWORD>(sizeof(BITMAPFILEHEADER))
  46. + static_cast<DWORD>(sizeof(BITMAPINFOHEADER)) + paletteSize;
  47. //6. 为位图内容分配内存
  48. const auto dib = GlobalAlloc(GHND, bmBitsSize + paletteSize + sizeof(BITMAPINFOHEADER)); //内存句柄
  49. const auto lpBmpInfoHeader = static_cast<LPBITMAPINFOHEADER>(GlobalLock(dib)); //指向位图信息头结构
  50. *lpBmpInfoHeader = bmpInfoHeader;
  51. //7. 处理调色板
  52. ProcessPalette(hBitmap, bitmap, paletteSize, lpBmpInfoHeader);
  53. //8. 写入文件
  54. DWORD written = 0; //写入文件字节数
  55. WriteFile(file, reinterpret_cast<LPSTR>(&bmpFileHeader), sizeof(BITMAPFILEHEADER),
  56. &written, nullptr); //写入位图文件头
  57. WriteFile(file, reinterpret_cast<LPSTR>(lpBmpInfoHeader), dibSize,
  58. &written, nullptr); //写入位图文件其余内容
  59. //9. 清理资源
  60. GlobalUnlock(dib);
  61. GlobalFree(dib);
  62. CloseHandle(file);
  63. return true;
  64. }
  65. //计算位图文件每个像素所占字节数
  66. WORD CImageHelper::GetBitmapBitCount()
  67. {
  68. const auto dc = ::CreateDCA("DISPLAY", nullptr, nullptr, nullptr);
  69. //当前分辨率下每像素所占字节数
  70. const auto bits = ::GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES);
  71. ::DeleteDC(dc);
  72. //位图中每像素所占字节数
  73. WORD bitCount;
  74. if (bits <= 1)
  75. bitCount = 1;
  76. else if (bits <= 4)
  77. bitCount = 4;
  78. else if (bits <= 8)
  79. bitCount = 8;
  80. else
  81. bitCount = 24;
  82. return bitCount;
  83. }
  84. //处理调色板
  85. void CImageHelper::ProcessPalette(HBITMAP hBitmap, const BITMAP& bitmap,
  86. DWORD paletteSize, LPBITMAPINFOHEADER lpBmpInfoHeader)
  87. {
  88. HANDLE oldPalette = nullptr;
  89. HDC dc = nullptr;
  90. const auto palette = GetStockObject(DEFAULT_PALETTE);
  91. if (palette != nullptr)
  92. {
  93. dc = ::GetDC(nullptr);
  94. oldPalette = ::SelectPalette(dc, static_cast<HPALETTE>(palette), FALSE);
  95. ::RealizePalette(dc); //实现设备调色板
  96. }
  97. //获取该调色板下新的像素值
  98. GetDIBits(dc, hBitmap, 0, static_cast<UINT>(bitmap.bmHeight),
  99. reinterpret_cast<LPSTR>(lpBmpInfoHeader) + sizeof(BITMAPINFOHEADER) + paletteSize,
  100. reinterpret_cast<BITMAPINFO*>(lpBmpInfoHeader), DIB_RGB_COLORS);
  101. //恢复调色板
  102. if (oldPalette != nullptr)
  103. {
  104. ::SelectPalette(dc, static_cast<HPALETTE>(oldPalette), TRUE);
  105. ::RealizePalette(dc);
  106. ::ReleaseDC(nullptr, dc);
  107. }
  108. }