Sync TortoiseIDiff and TortoiseUDiff from TortoiseSVN
[TortoiseGit.git] / src / Utils / MiscUI / Picture.cpp
blobbfd860ff8a3b79984b5f040237ef1763957c0434
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2012 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include <olectl.h>
21 #include <shlwapi.h>
22 #include <locale>
23 #include <algorithm>
24 #include "Picture.h"
25 #include "SmartHandle.h"
26 #include <atlbase.h>
28 #pragma comment(lib, "shlwapi.lib")
29 #pragma comment(lib, "gdiplus.lib")
31 #define HIMETRIC_INCH 2540
33 CPicture::CPicture()
34 : m_IPicture(NULL)
35 , m_Height(0)
36 , m_Weight(0)
37 , m_Width(0)
38 , pBitmap(NULL)
39 , bHaveGDIPlus(false)
40 , m_ip(InterpolationModeDefault)
41 , hIcons(NULL)
42 , lpIcons(NULL)
43 , nCurrentIcon(0)
44 , bIsIcon(false)
45 , bIsTiff(false)
46 , m_nSize(0)
47 , m_ColorDepth(0)
48 , hGlobal(NULL)
49 , gdiplusToken(NULL)
53 CPicture::~CPicture()
55 FreePictureData(); // Important - Avoid Leaks...
56 delete pBitmap;
57 if (bHaveGDIPlus)
58 GdiplusShutdown(gdiplusToken);
62 void CPicture::FreePictureData()
64 if (m_IPicture != NULL)
66 m_IPicture->Release();
67 m_IPicture = NULL;
68 m_Height = 0;
69 m_Weight = 0;
70 m_Width = 0;
71 m_nSize = 0;
73 if (hIcons)
75 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
76 if (lpIconDir)
78 for (int i=0; i<lpIconDir->idCount; ++i)
80 DestroyIcon(hIcons[i]);
83 delete [] hIcons;
84 hIcons = NULL;
86 delete [] lpIcons;
89 // Util function to ease loading of FreeImage library
90 static FARPROC s_GetProcAddressEx(HMODULE hDll, const char* procName, bool& valid)
92 FARPROC proc = NULL;
94 if (valid)
96 proc = GetProcAddress(hDll, procName);
98 if (!proc)
99 valid = false;
102 return proc;
105 tstring CPicture::GetFileSizeAsText(bool bAbbrev /* = true */)
107 TCHAR buf[100] = {0};
108 if (bAbbrev)
109 StrFormatByteSize(m_nSize, buf, _countof(buf));
110 else
111 _stprintf_s(buf, _T("%ld Bytes"), m_nSize);
113 return tstring(buf);
116 bool CPicture::Load(tstring sFilePathName)
118 bool bResult = false;
119 bIsIcon = false;
120 lpIcons = NULL;
121 //CFile PictureFile;
122 //CFileException e;
123 FreePictureData(); // Important - Avoid Leaks...
125 // No-op if no file specified
126 if (sFilePathName.empty())
127 return true;
129 // Load & initialize the GDI+ library if available
130 HMODULE hGdiPlusLib = AtlLoadSystemLibraryUsingFullPath(_T("gdiplus.dll"));
131 if (hGdiPlusLib && GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) == Ok)
133 bHaveGDIPlus = true;
135 // Since we loaded the gdiplus.dll only to check if it's available, we
136 // can safely free the library here again - GdiplusStartup() loaded it too
137 // and reference counting will make sure that it stays loaded until GdiplusShutdown()
138 // is called.
139 FreeLibrary(hGdiPlusLib);
141 // Attempt to load using GDI+ if available
142 if (bHaveGDIPlus)
144 pBitmap = new Bitmap(sFilePathName.c_str(), FALSE);
145 GUID guid;
146 pBitmap->GetRawFormat(&guid);
148 if (pBitmap->GetLastStatus() != Ok)
150 delete pBitmap;
151 pBitmap = NULL;
154 // gdiplus only loads the first icon found in an icon file
155 // so we have to handle icon files ourselves :(
157 // Even though gdiplus can load icons, it can't load the new
158 // icons from Vista - in Vista, the icon format changed slightly.
159 // But the LoadIcon/LoadImage API still can load those icons,
160 // at least those dimensions which are also used on pre-Vista
161 // systems.
162 // For that reason, we don't rely on gdiplus telling us if
163 // the image format is "icon" or not, we also check the
164 // file extension for ".ico".
165 std::transform(sFilePathName.begin(), sFilePathName.end(), sFilePathName.begin(), ::tolower);
166 bIsIcon = (guid == ImageFormatIcon) || (_tcsstr(sFilePathName.c_str(), _T(".ico")) != NULL);
167 bIsTiff = (guid == ImageFormatTIFF) || (_tcsstr(sFilePathName.c_str(), _T(".tiff")) != NULL);
169 if (bIsIcon)
171 // Icon file, get special treatment...
172 if (pBitmap)
174 // Cleanup first...
175 delete (pBitmap);
176 pBitmap = NULL;
177 bIsIcon = true;
180 CAutoFile hFile = CreateFile(sFilePathName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
181 if (hFile)
183 BY_HANDLE_FILE_INFORMATION fileinfo;
184 if (GetFileInformationByHandle(hFile, &fileinfo))
186 lpIcons = new BYTE[fileinfo.nFileSizeLow];
187 DWORD readbytes;
188 if (ReadFile(hFile, lpIcons, fileinfo.nFileSizeLow, &readbytes, NULL))
190 // we have the icon. Now gather the information we need later
191 if (readbytes >= sizeof(ICONDIR))
193 // we are going to open same file second time so we have to close the file now
194 hFile.CloseHandle();
196 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
197 if ((lpIconDir->idCount)&&(lpIconDir->idCount * sizeof(ICONDIR) <= fileinfo.nFileIndexLow))
201 nCurrentIcon = 0;
202 hIcons = new HICON[lpIconDir->idCount];
203 m_Width = lpIconDir->idEntries[0].bWidth;
204 m_Height = lpIconDir->idEntries[0].bHeight;
205 for (int i=0; i<lpIconDir->idCount; ++i)
207 hIcons[i] = (HICON)LoadImage(NULL, sFilePathName.c_str(), IMAGE_ICON,
208 lpIconDir->idEntries[i].bWidth,
209 lpIconDir->idEntries[i].bHeight,
210 LR_LOADFROMFILE);
212 bResult = true;
214 catch (...)
216 delete [] lpIcons;
217 lpIcons = NULL;
218 bResult = false;
221 else
223 delete [] lpIcons;
224 lpIcons = NULL;
225 bResult = false;
228 else
230 delete [] lpIcons;
231 lpIcons = NULL;
232 bResult = false;
235 else
237 delete [] lpIcons;
238 lpIcons = NULL;
243 else if (pBitmap) // Image loaded successfully with GDI+
245 m_Height = pBitmap->GetHeight();
246 m_Width = pBitmap->GetWidth();
247 bResult = true;
250 // If still failed to load the file...
251 if (!bResult)
253 // Attempt to load the FreeImage library as an optional DLL to support additional formats
255 // NOTE: Currently just loading via FreeImage & using GDI+ for drawing.
256 // It might be nice to remove this dependency in the future.
257 HMODULE hFreeImageLib = LoadLibrary(_T("FreeImage.dll"));
259 // FreeImage DLL functions
260 typedef const char* (__stdcall *FreeImage_GetVersion_t)(void);
261 typedef int (__stdcall *FreeImage_GetFileType_t)(const TCHAR *filename, int size);
262 typedef int (__stdcall *FreeImage_GetFIFFromFilename_t)(const TCHAR *filename);
263 typedef void* (__stdcall *FreeImage_Load_t)(int format, const TCHAR *filename, int flags);
264 typedef void (__stdcall *FreeImage_Unload_t)(void* dib);
265 typedef int (__stdcall *FreeImage_GetColorType_t)(void* dib);
266 typedef unsigned (__stdcall *FreeImage_GetWidth_t)(void* dib);
267 typedef unsigned (__stdcall *FreeImage_GetHeight_t)(void* dib);
268 typedef void (__stdcall *FreeImage_ConvertToRawBits_t)(BYTE *bits, void *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown);
270 //FreeImage_GetVersion_t FreeImage_GetVersion = NULL;
271 FreeImage_GetFileType_t FreeImage_GetFileType = NULL;
272 FreeImage_GetFIFFromFilename_t FreeImage_GetFIFFromFilename = NULL;
273 FreeImage_Load_t FreeImage_Load = NULL;
274 FreeImage_Unload_t FreeImage_Unload = NULL;
275 //FreeImage_GetColorType_t FreeImage_GetColorType = NULL;
276 FreeImage_GetWidth_t FreeImage_GetWidth = NULL;
277 FreeImage_GetHeight_t FreeImage_GetHeight = NULL;
278 FreeImage_ConvertToRawBits_t FreeImage_ConvertToRawBits = NULL;
280 if (hFreeImageLib)
282 bool exportsValid = true;
284 //FreeImage_GetVersion = (FreeImage_GetVersion_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetVersion@0", valid);
285 FreeImage_GetWidth = (FreeImage_GetWidth_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetWidth@4", exportsValid);
286 FreeImage_GetHeight = (FreeImage_GetHeight_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetHeight@4", exportsValid);
287 FreeImage_Unload = (FreeImage_Unload_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_Unload@4", exportsValid);
288 FreeImage_ConvertToRawBits = (FreeImage_ConvertToRawBits_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_ConvertToRawBits@32", exportsValid);
290 #ifdef UNICODE
291 FreeImage_GetFileType = (FreeImage_GetFileType_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFileTypeU@8", exportsValid);
292 FreeImage_GetFIFFromFilename = (FreeImage_GetFIFFromFilename_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFIFFromFilenameU@4", exportsValid);
293 FreeImage_Load = (FreeImage_Load_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_LoadU@12", exportsValid);
294 #else
295 FreeImage_GetFileType = (FreeImage_GetFileType_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFileType@8", exportsValid);
296 FreeImage_GetFIFFromFilename = (FreeImage_GetFIFFromFilename_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFIFFromFilename@4", exportsValid);
297 FreeImage_Load = (FreeImage_Load_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_Load@12", exportsValid);
298 #endif
300 //const char* version = FreeImage_GetVersion();
302 // Check the DLL is using compatible exports
303 if (exportsValid)
305 // Derive file type from file header.
306 int fileType = FreeImage_GetFileType(sFilePathName.c_str(), 0);
307 if (fileType < 0)
309 // No file header available, attempt to parse file name for extension.
310 fileType = FreeImage_GetFIFFromFilename(sFilePathName.c_str());
313 // If we have a valid file type
314 if (fileType >= 0)
316 void* dib = FreeImage_Load(fileType, sFilePathName.c_str(), 0);
318 if (dib)
320 unsigned width = FreeImage_GetWidth(dib);
321 unsigned height = FreeImage_GetHeight(dib);
323 // Create a GDI+ bitmap to load into...
324 pBitmap = new Bitmap(width, height, PixelFormat32bppARGB);
326 if (pBitmap && pBitmap->GetLastStatus() == Ok)
328 // Write & convert the loaded data into the GDI+ Bitmap
329 Rect rect(0, 0, width, height);
330 BitmapData bitmapData;
331 if (pBitmap->LockBits(&rect, ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData) == Ok)
333 FreeImage_ConvertToRawBits((BYTE*)bitmapData.Scan0, dib, bitmapData.Stride, 32, 0xff << RED_SHIFT, 0xff << GREEN_SHIFT, 0xff << BLUE_SHIFT, FALSE);
335 pBitmap->UnlockBits(&bitmapData);
337 m_Width = width;
338 m_Height = height;
339 bResult = true;
341 else // Failed to lock the destination Bitmap
343 delete pBitmap;
344 pBitmap = NULL;
347 else // Bitmap allocation failed
349 delete pBitmap;
350 pBitmap = NULL;
353 FreeImage_Unload(dib);
354 dib = NULL;
359 FreeLibrary(hFreeImageLib);
360 hFreeImageLib = NULL;
364 else // GDI+ Unavailable...
366 int nSize = 0;
367 pBitmap = NULL;
368 CAutoFile hFile = CreateFile(sFilePathName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
369 if (hFile)
371 BY_HANDLE_FILE_INFORMATION fileinfo;
372 if (GetFileInformationByHandle(hFile, &fileinfo))
374 BYTE * buffer = new BYTE[fileinfo.nFileSizeLow];
375 DWORD readbytes;
376 if (ReadFile(hFile, buffer, fileinfo.nFileSizeLow, &readbytes, NULL))
378 if (LoadPictureData(buffer, readbytes))
380 m_nSize = fileinfo.nFileSizeLow;
381 bResult = true;
384 delete [] buffer;
387 else
388 return bResult;
390 m_Name = sFilePathName;
391 m_Weight = nSize; // Update Picture Size Info...
393 if(m_IPicture != NULL) // Do Not Try To Read From Memory That Does Not Exist...
395 m_IPicture->get_Height(&m_Height);
396 m_IPicture->get_Width(&m_Width);
397 // Calculate Its Size On a "Standard" (96 DPI) Device Context
398 m_Height = MulDiv(m_Height, 96, HIMETRIC_INCH);
399 m_Width = MulDiv(m_Width, 96, HIMETRIC_INCH);
401 else // Picture Data Is Not a Known Picture Type
403 m_Height = 0;
404 m_Width = 0;
405 bResult = false;
409 if ((bResult)&&(m_nSize == 0))
411 CAutoFile hFile = CreateFile(sFilePathName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
412 if (hFile)
414 BY_HANDLE_FILE_INFORMATION fileinfo;
415 if (GetFileInformationByHandle(hFile, &fileinfo))
417 m_nSize = fileinfo.nFileSizeLow;
422 m_ColorDepth = GetColorDepth();
424 return(bResult);
427 bool CPicture::LoadPictureData(BYTE *pBuffer, int nSize)
429 bool bResult = false;
431 HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nSize);
433 if(hGlobal == NULL)
435 return(false);
438 void* pData = GlobalLock(hGlobal);
439 if (pData == NULL)
440 return false;
441 memcpy(pData, pBuffer, nSize);
442 GlobalUnlock(hGlobal);
444 IStream* pStream = NULL;
446 if ((CreateStreamOnHGlobal(hGlobal, true, &pStream) == S_OK)&&(pStream))
448 HRESULT hr = OleLoadPicture(pStream, nSize, false, IID_IPicture, (LPVOID *)&m_IPicture);
449 pStream->Release();
450 pStream = NULL;
452 bResult = hr == S_OK;
455 FreeResource(hGlobal); // 16Bit Windows Needs This (32Bit - Automatic Release)
457 return(bResult);
460 bool CPicture::Show(HDC hDC, RECT DrawRect)
462 if (hDC == NULL)
463 return false;
464 if (bIsIcon && lpIcons)
466 ::DrawIconEx(hDC, DrawRect.left, DrawRect.top, hIcons[nCurrentIcon], DrawRect.right-DrawRect.left, DrawRect.bottom-DrawRect.top, 0, NULL, DI_NORMAL);
467 return true;
469 if ((m_IPicture == NULL)&&(pBitmap == NULL))
470 return false;
472 if (m_IPicture)
474 long Width = 0;
475 long Height = 0;
476 m_IPicture->get_Width(&Width);
477 m_IPicture->get_Height(&Height);
479 HRESULT hr = m_IPicture->Render(hDC,
480 DrawRect.left, // Left
481 DrawRect.top, // Top
482 DrawRect.right - DrawRect.left, // Right
483 DrawRect.bottom - DrawRect.top, // Bottom
485 Height,
486 Width,
487 -Height,
488 &DrawRect);
490 if (SUCCEEDED(hr))
491 return(true);
493 else if (pBitmap)
495 Graphics graphics(hDC);
496 graphics.SetInterpolationMode(m_ip);
497 graphics.SetPixelOffsetMode(PixelOffsetModeHighQuality);
498 ImageAttributes attr;
499 attr.SetWrapMode(WrapModeTileFlipXY);
500 Rect rect(DrawRect.left, DrawRect.top, DrawRect.right-DrawRect.left, DrawRect.bottom-DrawRect.top);
501 graphics.DrawImage(pBitmap, rect, 0, 0, m_Width, m_Height, UnitPixel, &attr);
502 return true;
505 return(false);
508 bool CPicture::UpdateSizeOnDC(HDC hDC)
510 if(hDC == NULL || m_IPicture == NULL) { m_Height = 0; m_Width = 0; return(false); };
512 m_IPicture->get_Height(&m_Height);
513 m_IPicture->get_Width(&m_Width);
515 // Get Current DPI - Dot Per Inch
516 int CurrentDPI_X = GetDeviceCaps(hDC, LOGPIXELSX);
517 int CurrentDPI_Y = GetDeviceCaps(hDC, LOGPIXELSY);
519 m_Height = MulDiv(m_Height, CurrentDPI_Y, HIMETRIC_INCH);
520 m_Width = MulDiv(m_Width, CurrentDPI_X, HIMETRIC_INCH);
522 return(true);
525 UINT CPicture::GetColorDepth() const
527 if (bIsIcon && lpIcons)
529 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
530 return lpIconDir->idEntries[nCurrentIcon].wBitCount;
532 switch (GetPixelFormat())
534 case PixelFormat1bppIndexed:
535 return 1;
536 case PixelFormat4bppIndexed:
537 return 4;
538 case PixelFormat8bppIndexed:
539 return 8;
540 case PixelFormat16bppARGB1555:
541 case PixelFormat16bppGrayScale:
542 case PixelFormat16bppRGB555:
543 case PixelFormat16bppRGB565:
544 return 16;
545 case PixelFormat24bppRGB:
546 return 24;
547 case PixelFormat32bppARGB:
548 case PixelFormat32bppPARGB:
549 case PixelFormat32bppRGB:
550 return 32;
551 case PixelFormat48bppRGB:
552 return 48;
553 case PixelFormat64bppARGB:
554 case PixelFormat64bppPARGB:
555 return 64;
557 return 0;
560 UINT CPicture::GetNumberOfFrames(int dimension)
562 if (bIsIcon && lpIcons)
564 return 1;
566 if (pBitmap == NULL)
567 return 0;
568 UINT count = pBitmap->GetFrameDimensionsCount();
569 GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
571 pBitmap->GetFrameDimensionsList(pDimensionIDs, count);
573 UINT frameCount = pBitmap->GetFrameCount(&pDimensionIDs[dimension]);
575 free(pDimensionIDs);
576 return frameCount;
579 UINT CPicture::GetNumberOfDimensions()
581 if (bIsIcon && lpIcons)
583 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
584 return lpIconDir->idCount;
586 return pBitmap ? pBitmap->GetFrameDimensionsCount() : 0;
589 long CPicture::SetActiveFrame(UINT frame)
591 if (bIsIcon && lpIcons)
593 nCurrentIcon = frame-1;
594 m_Height = GetHeight();
595 m_Width = GetWidth();
596 return 0;
598 if (pBitmap == NULL)
599 return 0;
600 UINT count = 0;
601 count = pBitmap->GetFrameDimensionsCount();
602 GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
604 pBitmap->GetFrameDimensionsList(pDimensionIDs, count);
606 UINT frameCount = pBitmap->GetFrameCount(&pDimensionIDs[0]);
608 free(pDimensionIDs);
610 if (frame > frameCount)
611 return 0;
613 GUID pageGuid = FrameDimensionTime;
614 if (bIsTiff)
615 pageGuid = FrameDimensionPage;
616 pBitmap->SelectActiveFrame(&pageGuid, frame);
618 // Assume that the image has a property item of type PropertyItemEquipMake.
619 // Get the size of that property item.
620 int nSize = pBitmap->GetPropertyItemSize(PropertyTagFrameDelay);
622 // Allocate a buffer to receive the property item.
623 PropertyItem* pPropertyItem = (PropertyItem*) malloc(nSize);
625 Status s = pBitmap->GetPropertyItem(PropertyTagFrameDelay, nSize, pPropertyItem);
627 UINT prevframe = frame;
628 if (prevframe > 0)
629 prevframe--;
630 long delay = 0;
631 if (s == Ok)
633 delay = ((long*)pPropertyItem->value)[prevframe] * 10;
635 free(pPropertyItem);
636 m_Height = GetHeight();
637 m_Width = GetWidth();
638 return delay;
641 UINT CPicture::GetHeight() const
643 if ((bIsIcon)&&(lpIcons))
645 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
646 return lpIconDir->idEntries[nCurrentIcon].bHeight;
648 return pBitmap ? pBitmap->GetHeight() : 0;
651 UINT CPicture::GetWidth() const
653 if ((bIsIcon)&&(lpIcons))
655 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
656 return lpIconDir->idEntries[nCurrentIcon].bWidth;
658 return pBitmap ? pBitmap->GetWidth() : 0;
661 PixelFormat CPicture::GetPixelFormat() const
663 if ((bIsIcon)&&(lpIcons))
665 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
666 if (lpIconDir->idEntries[nCurrentIcon].wPlanes == 1)
668 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 1)
669 return PixelFormat1bppIndexed;
670 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 4)
671 return PixelFormat4bppIndexed;
672 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 8)
673 return PixelFormat8bppIndexed;
675 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 32)
677 return PixelFormat32bppARGB;
680 return pBitmap ? pBitmap->GetPixelFormat() : 0;