1 /* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Some DIB related functions 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 */ 7 8 #include "precomp.h" 9 #include <math.h> 10 11 INT g_fileSize = 0; 12 float g_xDpi = 96; 13 float g_yDpi = 96; 14 SYSTEMTIME g_fileTime; 15 16 /* FUNCTIONS ********************************************************/ 17 18 // Convert DPI (dots per inch) into PPCM (pixels per centimeter) 19 float PpcmFromDpi(float dpi) 20 { 21 // 1 DPI is 0.0254 meter. 1 centimeter is 1/100 meter. 22 return dpi / (0.0254f * 100.0f); 23 } 24 25 HBITMAP 26 CreateDIBWithProperties(int width, int height) 27 { 28 BITMAPINFO bmi; 29 ZeroMemory(&bmi, sizeof(BITMAPINFO)); 30 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 31 bmi.bmiHeader.biWidth = width; 32 bmi.bmiHeader.biHeight = height; 33 bmi.bmiHeader.biPlanes = 1; 34 bmi.bmiHeader.biBitCount = 24; 35 return CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, NULL, NULL, 0); 36 } 37 38 HBITMAP 39 CreateMonoBitmap(int width, int height, BOOL bWhite) 40 { 41 HBITMAP hbm = CreateBitmap(width, height, 1, 1, NULL); 42 if (hbm == NULL) 43 return NULL; 44 45 if (bWhite) 46 { 47 HDC hdc = CreateCompatibleDC(NULL); 48 HGDIOBJ hbmOld = SelectObject(hdc, hbm); 49 RECT rc = { 0, 0, width, height }; 50 FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); 51 SelectObject(hdc, hbmOld); 52 DeleteDC(hdc); 53 } 54 55 return hbm; 56 } 57 58 HBITMAP 59 CreateColorDIB(int width, int height, COLORREF rgb) 60 { 61 HBITMAP ret = CreateDIBWithProperties(width, height); 62 if (!ret) 63 return NULL; 64 65 if (rgb) 66 { 67 HDC hdc = CreateCompatibleDC(NULL); 68 HGDIOBJ hbmOld = SelectObject(hdc, ret); 69 RECT rc; 70 SetRect(&rc, 0, 0, width, height); 71 HBRUSH hbr = CreateSolidBrush(rgb); 72 FillRect(hdc, &rc, hbr); 73 DeleteObject(hbr); 74 SelectObject(hdc, hbmOld); 75 DeleteDC(hdc); 76 } 77 78 return ret; 79 } 80 81 HBITMAP CopyMonoImage(HBITMAP hbm, INT cx, INT cy) 82 { 83 BITMAP bm; 84 if (!GetObject(hbm, sizeof(bm), &bm)) 85 return NULL; 86 87 if (cx == 0 || cy == 0) 88 { 89 cx = bm.bmWidth; 90 cy = bm.bmHeight; 91 } 92 93 HBITMAP hbmNew = CreateBitmap(cx, cy, 1, 1, NULL); 94 if (!hbmNew) 95 return NULL; 96 97 HDC hdc1 = CreateCompatibleDC(NULL); 98 HDC hdc2 = CreateCompatibleDC(NULL); 99 HGDIOBJ hbm1Old = SelectObject(hdc1, hbm); 100 HGDIOBJ hbm2Old = SelectObject(hdc2, hbmNew); 101 StretchBlt(hdc2, 0, 0, cx, cy, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); 102 SelectObject(hdc1, hbm1Old); 103 SelectObject(hdc2, hbm2Old); 104 DeleteDC(hdc1); 105 DeleteDC(hdc2); 106 return hbmNew; 107 } 108 109 HBITMAP CachedBufferDIB(HBITMAP hbm, int minimalWidth, int minimalHeight) 110 { 111 if (minimalWidth <= 0) 112 minimalWidth = 1; 113 if (minimalHeight <= 0) 114 minimalHeight = 1; 115 116 BITMAP bm; 117 if (!GetObject(hbm, sizeof(bm), &bm)) 118 hbm = NULL; 119 120 if (hbm && minimalWidth <= bm.bmWidth && minimalHeight <= bm.bmHeight) 121 return hbm; 122 123 if (hbm) 124 DeleteObject(hbm); 125 126 return CreateDIBWithProperties((minimalWidth * 3) / 2, (minimalHeight * 3) / 2); 127 } 128 129 int 130 GetDIBWidth(HBITMAP hBitmap) 131 { 132 BITMAP bm; 133 GetObject(hBitmap, sizeof(BITMAP), &bm); 134 return bm.bmWidth; 135 } 136 137 int 138 GetDIBHeight(HBITMAP hBitmap) 139 { 140 BITMAP bm; 141 GetObject(hBitmap, sizeof(BITMAP), &bm); 142 return bm.bmHeight; 143 } 144 145 BOOL SaveDIBToFile(HBITMAP hBitmap, LPCWSTR FileName, BOOL fIsMainFile) 146 { 147 CImageDx img; 148 img.Attach(hBitmap); 149 HRESULT hr = img.SaveDx(FileName, GUID_NULL, g_xDpi, g_yDpi); 150 img.Detach(); 151 152 if (FAILED(hr)) 153 { 154 ShowError(IDS_SAVEERROR, FileName); 155 return FALSE; 156 } 157 158 if (!fIsMainFile) 159 return TRUE; 160 161 WIN32_FIND_DATAW find; 162 HANDLE hFind = ::FindFirstFileW(FileName, &find); 163 if (hFind == INVALID_HANDLE_VALUE) 164 { 165 ShowError(IDS_SAVEERROR, FileName); 166 return FALSE; 167 } 168 ::FindClose(hFind); 169 170 SetFileInfo(FileName, &find, TRUE); 171 g_imageSaved = TRUE; 172 return TRUE; 173 } 174 175 void SetFileInfo(LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isAFile) 176 { 177 // update file time and size 178 if (pFound) 179 { 180 FILETIME ft; 181 ::FileTimeToLocalFileTime(&pFound->ftLastWriteTime, &ft); 182 ::FileTimeToSystemTime(&ft, &g_fileTime); 183 184 g_fileSize = pFound->nFileSizeLow; 185 } 186 else 187 { 188 ZeroMemory(&g_fileTime, sizeof(g_fileTime)); 189 g_fileSize = 0; 190 } 191 192 // update g_szFileName 193 if (name && name[0]) 194 { 195 CStringW strName = name; 196 ::GetFullPathNameW(strName, _countof(g_szFileName), g_szFileName, NULL); 197 // The following code won't work correctly when (name == g_szFileName): 198 // ::GetFullPathNameW(name, _countof(g_szFileName), g_szFileName, NULL); 199 } 200 else 201 { 202 ::LoadStringW(g_hinstExe, IDS_DEFAULTFILENAME, g_szFileName, _countof(g_szFileName)); 203 } 204 205 // set title 206 CString strTitle; 207 strTitle.Format(IDS_WINDOWTITLE, PathFindFileName(g_szFileName)); 208 mainWindow.SetWindowText(strTitle); 209 210 // update file info and recent 211 g_isAFile = isAFile; 212 if (g_isAFile) 213 registrySettings.SetMostRecentFile(g_szFileName); 214 215 g_imageSaved = TRUE; 216 } 217 218 HBITMAP InitializeImage(LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile) 219 { 220 COLORREF white = RGB(255, 255, 255); 221 HBITMAP hBitmap = CreateColorDIB(registrySettings.BMPWidth, registrySettings.BMPHeight, white); 222 if (hBitmap == NULL) 223 return NULL; 224 225 HDC hScreenDC = ::GetDC(NULL); 226 g_xDpi = ::GetDeviceCaps(hScreenDC, LOGPIXELSX); 227 g_yDpi = ::GetDeviceCaps(hScreenDC, LOGPIXELSY); 228 ::ReleaseDC(NULL, hScreenDC); 229 230 return SetBitmapAndInfo(hBitmap, name, pFound, isFile); 231 } 232 233 HBITMAP SetBitmapAndInfo(HBITMAP hBitmap, LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile) 234 { 235 // update image 236 imageModel.PushImageForUndo(hBitmap); 237 imageModel.ClearHistory(); 238 239 SetFileInfo(name, pFound, isFile); 240 g_imageSaved = TRUE; 241 return hBitmap; 242 } 243 244 HBITMAP DoLoadImageFile(HWND hwnd, LPCWSTR name, BOOL fIsMainFile) 245 { 246 // find the file 247 WIN32_FIND_DATA find; 248 HANDLE hFind = ::FindFirstFileW(name, &find); 249 if (hFind == INVALID_HANDLE_VALUE) // does not exist 250 { 251 ShowError(IDS_LOADERRORTEXT, name); 252 return NULL; 253 } 254 ::FindClose(hFind); 255 256 // is file empty? 257 if (find.nFileSizeLow == 0 && find.nFileSizeHigh == 0) 258 { 259 if (fIsMainFile) 260 return InitializeImage(name, &find, TRUE); 261 } 262 263 // load the image 264 CImageDx img; 265 float xDpi = 0, yDpi = 0; 266 HRESULT hr = img.LoadDx(name, &xDpi, &yDpi); 267 if (FAILED(hr)) 268 { 269 ShowError(IDS_LOADERRORTEXT, name); 270 return NULL; 271 } 272 273 HBITMAP hBitmap = img.Detach(); 274 if (!fIsMainFile) 275 return hBitmap; 276 277 if (xDpi <= 0 || yDpi <= 0) 278 { 279 HDC hDC = ::GetDC(NULL); 280 xDpi = ::GetDeviceCaps(hDC, LOGPIXELSX); 281 yDpi = ::GetDeviceCaps(hDC, LOGPIXELSY); 282 ::ReleaseDC(NULL, hDC); 283 } 284 285 g_xDpi = xDpi; 286 g_yDpi = yDpi; 287 288 SetBitmapAndInfo(hBitmap, name, &find, TRUE); 289 return hBitmap; 290 } 291 292 HBITMAP Rotate90DegreeBlt(HDC hDC1, INT cx, INT cy, BOOL bRight, BOOL bMono) 293 { 294 HBITMAP hbm2; 295 if (bMono) 296 hbm2 = ::CreateBitmap(cy, cx, 1, 1, NULL); 297 else 298 hbm2 = CreateDIBWithProperties(cy, cx); 299 if (!hbm2) 300 return NULL; 301 302 HDC hDC2 = CreateCompatibleDC(NULL); 303 HGDIOBJ hbm2Old = SelectObject(hDC2, hbm2); 304 if (bRight) 305 { 306 for (INT y = 0; y < cy; ++y) 307 { 308 for (INT x = 0; x < cx; ++x) 309 { 310 COLORREF rgb = GetPixel(hDC1, x, y); 311 SetPixelV(hDC2, cy - (y + 1), x, rgb); 312 } 313 } 314 } 315 else 316 { 317 for (INT y = 0; y < cy; ++y) 318 { 319 for (INT x = 0; x < cx; ++x) 320 { 321 COLORREF rgb = GetPixel(hDC1, x, y); 322 SetPixelV(hDC2, y, cx - (x + 1), rgb); 323 } 324 } 325 } 326 SelectObject(hDC2, hbm2Old); 327 DeleteDC(hDC2); 328 return hbm2; 329 } 330 331 #ifndef M_PI 332 #define M_PI 3.14159265 333 #endif 334 335 HBITMAP SkewDIB(HDC hDC1, HBITMAP hbm, INT nDegree, BOOL bVertical, BOOL bMono) 336 { 337 if (nDegree == 0) 338 return CopyDIBImage(hbm); 339 340 const double eTan = tan(abs(nDegree) * M_PI / 180); 341 342 BITMAP bm; 343 GetObjectW(hbm, sizeof(bm), &bm); 344 INT cx = bm.bmWidth, cy = bm.bmHeight, dx = 0, dy = 0; 345 if (bVertical) 346 dy = INT(cx * eTan); 347 else 348 dx = INT(cy * eTan); 349 350 if (dx == 0 && dy == 0) 351 return CopyDIBImage(hbm); 352 353 HBITMAP hbmNew; 354 if (bMono) 355 hbmNew = CreateMonoBitmap(cx + dx, cy + dy, FALSE); 356 else 357 hbmNew = CreateColorDIB(cx + dx, cy + dy, RGB(255, 255, 255)); 358 if (!hbmNew) 359 return NULL; 360 361 HDC hDC2 = CreateCompatibleDC(NULL); 362 HGDIOBJ hbm2Old = SelectObject(hDC2, hbmNew); 363 if (bVertical) 364 { 365 for (INT x = 0; x < cx; ++x) 366 { 367 INT delta = INT(x * eTan); 368 if (nDegree > 0) 369 BitBlt(hDC2, x, (dy - delta), 1, cy, hDC1, x, 0, SRCCOPY); 370 else 371 BitBlt(hDC2, x, delta, 1, cy, hDC1, x, 0, SRCCOPY); 372 } 373 } 374 else 375 { 376 for (INT y = 0; y < cy; ++y) 377 { 378 INT delta = INT(y * eTan); 379 if (nDegree > 0) 380 BitBlt(hDC2, (dx - delta), y, cx, 1, hDC1, 0, y, SRCCOPY); 381 else 382 BitBlt(hDC2, delta, y, cx, 1, hDC1, 0, y, SRCCOPY); 383 } 384 } 385 386 SelectObject(hDC2, hbm2Old); 387 DeleteDC(hDC2); 388 return hbmNew; 389 } 390 391 struct BITMAPINFODX : BITMAPINFO 392 { 393 RGBQUAD bmiColorsAdditional[256 - 1]; 394 }; 395 396 HGLOBAL BitmapToClipboardDIB(HBITMAP hBitmap) 397 { 398 BITMAP bm; 399 if (!GetObject(hBitmap, sizeof(BITMAP), &bm)) 400 return NULL; 401 402 BITMAPINFODX bmi; 403 ZeroMemory(&bmi, sizeof(bmi)); 404 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 405 bmi.bmiHeader.biWidth = bm.bmWidth; 406 bmi.bmiHeader.biHeight = bm.bmHeight; 407 bmi.bmiHeader.biPlanes = 1; 408 bmi.bmiHeader.biBitCount = bm.bmBitsPixel; 409 bmi.bmiHeader.biCompression = BI_RGB; 410 bmi.bmiHeader.biSizeImage = bm.bmWidthBytes * bm.bmHeight; 411 412 INT cColors; 413 if (bm.bmBitsPixel < 16) 414 cColors = 1 << bm.bmBitsPixel; 415 else 416 cColors = 0; 417 418 HDC hDC = CreateCompatibleDC(NULL); 419 420 if (cColors) 421 { 422 HGDIOBJ hbmOld = SelectObject(hDC, hBitmap); 423 cColors = GetDIBColorTable(hDC, 0, cColors, bmi.bmiColors); 424 SelectObject(hDC, hbmOld); 425 } 426 427 DWORD cbColors = cColors * sizeof(RGBQUAD); 428 DWORD dwSize = sizeof(BITMAPINFOHEADER) + cbColors + bmi.bmiHeader.biSizeImage; 429 HGLOBAL hGlobal = GlobalAlloc(GHND | GMEM_SHARE, dwSize); 430 if (hGlobal) 431 { 432 LPBYTE pb = (LPBYTE)GlobalLock(hGlobal); 433 if (pb) 434 { 435 CopyMemory(pb, &bmi, sizeof(BITMAPINFOHEADER)); 436 pb += sizeof(BITMAPINFOHEADER); 437 438 CopyMemory(pb, bmi.bmiColors, cbColors); 439 pb += cbColors; 440 441 GetDIBits(hDC, hBitmap, 0, bm.bmHeight, pb, &bmi, DIB_RGB_COLORS); 442 443 GlobalUnlock(hGlobal); 444 } 445 else 446 { 447 GlobalFree(hGlobal); 448 hGlobal = NULL; 449 } 450 } 451 452 DeleteDC(hDC); 453 454 return hGlobal; 455 } 456 457 HBITMAP BitmapFromClipboardDIB(HGLOBAL hGlobal) 458 { 459 LPBYTE pb = (LPBYTE)GlobalLock(hGlobal); 460 if (!pb) 461 return NULL; 462 463 LPBITMAPINFO pbmi = (LPBITMAPINFO)pb; 464 pb += pbmi->bmiHeader.biSize; 465 466 INT cColors = 0, cbColors = 0; 467 if (pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER)) 468 { 469 LPBITMAPCOREINFO pbmci = (LPBITMAPCOREINFO)pbmi; 470 WORD BitCount = pbmci->bmciHeader.bcBitCount; 471 if (BitCount < 16) 472 { 473 cColors = (1 << BitCount); 474 cbColors = cColors * sizeof(RGBTRIPLE); 475 pb += cbColors; 476 } 477 } 478 else if (pbmi->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER)) 479 { 480 WORD BitCount = pbmi->bmiHeader.biBitCount; 481 if (BitCount < 16) 482 { 483 cColors = (1 << BitCount); 484 cbColors = cColors * sizeof(RGBQUAD); 485 pb += cbColors; 486 } 487 } 488 489 HDC hDC = CreateCompatibleDC(NULL); 490 HBITMAP hBitmap = CreateDIBSection(hDC, pbmi, DIB_RGB_COLORS, NULL, NULL, 0); 491 if (hBitmap) 492 { 493 SetDIBits(hDC, hBitmap, 0, labs(pbmi->bmiHeader.biHeight), pb, pbmi, DIB_RGB_COLORS); 494 } 495 DeleteDC(hDC); 496 497 GlobalUnlock(hGlobal); 498 499 return hBitmap; 500 } 501 502 HBITMAP BitmapFromHEMF(HENHMETAFILE hEMF) 503 { 504 ENHMETAHEADER header; 505 if (!GetEnhMetaFileHeader(hEMF, sizeof(header), &header)) 506 return NULL; 507 508 CRect rc = *(LPRECT)&header.rclBounds; 509 INT cx = rc.Width(), cy = rc.Height(); 510 HBITMAP hbm = CreateColorDIB(cx, cy, RGB(255, 255, 255)); 511 if (!hbm) 512 return NULL; 513 514 HDC hDC = CreateCompatibleDC(NULL); 515 HGDIOBJ hbmOld = SelectObject(hDC, hbm); 516 PlayEnhMetaFile(hDC, hEMF, &rc); 517 SelectObject(hDC, hbmOld); 518 DeleteDC(hDC); 519 520 return hbm; 521 } 522