#pragma once #include #include #include "stb_image.h" #include "stb_image_write.h" class CImage { public: enum { createAlphaChannel = 0x01 }; CImage() { Init(); } ~CImage() { Destroy(); } operator HBITMAP() const { return m_hBitmap; } BOOL IsNull() const { return m_hBitmap == NULL; } int GetWidth() const { return m_width; } int GetHeight() const { return m_height; } int GetPitch() const { return m_pitch; } int GetBPP() const { return 32; } void* GetBits() const { return m_bits; } BOOL Create(int w, int h, int bpp, DWORD flags = 0) { Destroy(); if (bpp != 32) return FALSE; BITMAPINFO bmi = {}; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = -h; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; void* bits = NULL; m_hBitmap = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); if (!m_hBitmap) return FALSE; m_bits = (BYTE*)bits; m_width = w; m_height = h; m_pitch = w * 4; m_hasAlpha = (flags & createAlphaChannel) != 0; memset(m_bits, 0, w * h * 4); return TRUE; } void Destroy() { ReleaseDC(); if (m_hBitmap) { DeleteObject(m_hBitmap); m_hBitmap = NULL; } Init(); } BOOL Load(LPCTSTR fileName) { Destroy(); HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { return FALSE; } DWORD size = GetFileSize(hFile, NULL); BYTE* buf = (BYTE*)malloc(size); DWORD read = 0; if (ReadFile(hFile, buf, size, &read, NULL) == FALSE || read != size) { free(buf); CloseHandle(hFile); return FALSE; } BOOL result = FALSE; if (IsPNG(buf)) { result = LoadPNG(buf, size); } else if (IsBMP(buf)) { result = LoadBMP(buf, size); } free(buf); return result; } static void png_write_func(void* context, void* data, int size) { HANDLE hFile = (HANDLE)context; DWORD written = 0; WriteFile(hFile, data, size, &written, NULL); } BOOL Save(LPCTSTR fileName) { if (!m_bits || !fileName) return FALSE; if (IsPNGFileName(fileName)) return SavePNG(fileName); if (IsBMPFileName(fileName)) return SaveBMP(fileName); return FALSE; } BOOL IsPNGFileName(LPCTSTR fileName) { LPCTSTR ext = _tcsrchr(fileName, _T('.')); if (!ext) return FALSE; return _tcsicmp(ext, _T(".png")) == 0; } BOOL IsBMPFileName(LPCTSTR fileName) { LPCTSTR ext = _tcsrchr(fileName, _T('.')); if (!ext) return FALSE; return _tcsicmp(ext, _T(".bmp")) == 0; } BOOL SavePNG(LPCTSTR fileName) { HANDLE hFile = CreateFile( fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; size_t size = (size_t)m_width * m_height * 4; BYTE* tmp = (BYTE*)malloc(size); if (!tmp) { CloseHandle(hFile); return FALSE; } for (int y = 0; y < m_height; y++) { BYTE* src = m_bits + y * m_pitch; BYTE* dst = tmp + y * m_width * 4; for (int x = 0; x < m_width; x++) { dst[x * 4 + 0] = src[x * 4 + 2]; dst[x * 4 + 1] = src[x * 4 + 1]; dst[x * 4 + 2] = src[x * 4 + 0]; dst[x * 4 + 3] = 255; } } int result = stbi_write_png_to_func( png_write_func, (void*)hFile, m_width, m_height, 4, tmp, m_width * 4); free(tmp); CloseHandle(hFile); return result != 0; } BOOL SaveBMP(LPCTSTR fileName) { HANDLE hFile = CreateFile( fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; int rowSize = ((m_width * 3 + 3) / 4) * 4; int dataSize = rowSize * m_height; BITMAPFILEHEADER bf = {}; bf.bfType = 0x4D42; // 'BM' bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bf.bfSize = bf.bfOffBits + dataSize; BITMAPINFOHEADER bi = {}; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = m_width; bi.biHeight = m_height; // bottom-up bi.biPlanes = 1; bi.biBitCount = 24; bi.biCompression = BI_RGB; DWORD written = 0; WriteFile(hFile, &bf, sizeof(bf), &written, NULL); WriteFile(hFile, &bi, sizeof(bi), &written, NULL); BYTE* line = (BYTE*)malloc(rowSize); if (!line) { CloseHandle(hFile); return FALSE; } for (int y = m_height - 1; y >= 0; y--) { BYTE* src = m_bits + y * m_pitch; BYTE* dst = line; memset(line, 0, rowSize); for (int x = 0; x < m_width; x++) { dst[0] = src[0]; // B dst[1] = src[1]; // G dst[2] = src[2]; // R src += 4; dst += 3; } if (!WriteFile(hFile, line, rowSize, &written, NULL) || written != (DWORD)rowSize) { free(line); CloseHandle(hFile); return FALSE; } } free(line); CloseHandle(hFile); return TRUE; } BOOL BitBlt(HDC hDest, int x, int y, DWORD rop = SRCCOPY) const { return BitBlt(hDest, x, y, m_width, m_height, 0, 0, rop); } BOOL BitBlt(HDC hDest, int x, int y, int w, int h, int sx, int sy, DWORD rop) const { HDC hSrc = GetDC(); BOOL r = ::BitBlt(hDest, x, y, w, h, hSrc, sx, sy, rop); ReleaseDC(); return r; } COLORREF GetPixel(int x, int y) const { BYTE* p = m_bits + y * m_pitch + x * 4; return RGB(p[2], p[1], p[0]); } HDC GetDC() const { if (!m_hDC) { m_hDC = CreateCompatibleDC(NULL); m_hOldBitmap = (HBITMAP)SelectObject(m_hDC, m_hBitmap); } return m_hDC; } void ReleaseDC() const { if (m_hDC) { SelectObject(m_hDC, m_hOldBitmap); DeleteDC(m_hDC); m_hDC = NULL; m_hOldBitmap = NULL; } } void Attach(HBITMAP hBitmap) { Destroy(); m_hBitmap = hBitmap; BITMAP bm; GetObject(hBitmap, sizeof(bm), &bm); m_width = bm.bmWidth; m_height = bm.bmHeight; m_pitch = bm.bmWidthBytes; m_bits = (BYTE*)bm.bmBits; m_hasAlpha = (bm.bmBitsPixel == 32); } HBITMAP Detach() { HBITMAP h = m_hBitmap; Init(); return h; } private: void Init() { m_hBitmap = NULL; m_bits = NULL; m_width = m_height = m_pitch = 0; m_hDC = NULL; m_hOldBitmap = NULL; m_hasAlpha = false; } BOOL IsPNG(BYTE* p) { static BYTE sig[8] = { 137,80,78,71,13,10,26,10 }; return memcmp(p, sig, 8) == 0; } BOOL IsBMP(BYTE* p) { return p[0] == 'B' && p[1] == 'M'; } BOOL LoadPNG(BYTE* data, int size) { int w, h, c; unsigned char* img = stbi_load_from_memory(data, size, &w, &h, &c, 4); if (!img) return FALSE; Create(w, h, 32, createAlphaChannel); for (int i = 0; i < w * h; i++) { BYTE r = img[i * 4 + 0]; BYTE g = img[i * 4 + 1]; BYTE b = img[i * 4 + 2]; BYTE a = img[i * 4 + 3]; m_bits[i * 4 + 0] = b; m_bits[i * 4 + 1] = g; m_bits[i * 4 + 2] = r; m_bits[i * 4 + 3] = a; } stbi_image_free(img); return TRUE; } BOOL LoadBMP(BYTE* data, int) { BITMAPFILEHEADER* bf = (BITMAPFILEHEADER*)data; BITMAPINFOHEADER* bi = (BITMAPINFOHEADER*)(data + sizeof(BITMAPFILEHEADER)); BYTE* src = data + bf->bfOffBits; Create(bi->biWidth, abs(bi->biHeight), 32); for (int y = 0; y < m_height; y++) { BYTE* d = m_bits + y * m_pitch; BYTE* s = src + (m_height - 1 - y) * ((m_width * 3 + 3) & ~3); for (int x = 0; x < m_width; x++) { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = 255; d += 4; s += 3; } } return TRUE; } private: HBITMAP m_hBitmap; BYTE* m_bits; int m_width, m_height, m_pitch; mutable HDC m_hDC; mutable HBITMAP m_hOldBitmap; bool m_hasAlpha; };