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.
25 #include "SmartHandle.h"
28 #pragma comment(lib, "shlwapi.lib")
29 #pragma comment(lib, "gdiplus.lib")
31 #define HIMETRIC_INCH 2540
40 , m_ip(InterpolationModeDefault
)
55 FreePictureData(); // Important - Avoid Leaks...
58 GdiplusShutdown(gdiplusToken
);
62 void CPicture::FreePictureData()
64 if (m_IPicture
!= NULL
)
66 m_IPicture
->Release();
75 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
78 for (int i
=0; i
<lpIconDir
->idCount
; ++i
)
80 DestroyIcon(hIcons
[i
]);
89 // Util function to ease loading of FreeImage library
90 static FARPROC
s_GetProcAddressEx(HMODULE hDll
, const char* procName
, bool& valid
)
96 proc
= GetProcAddress(hDll
, procName
);
105 tstring
CPicture::GetFileSizeAsText(bool bAbbrev
/* = true */)
107 TCHAR buf
[100] = {0};
109 StrFormatByteSize(m_nSize
, buf
, _countof(buf
));
111 _stprintf_s(buf
, _T("%ld Bytes"), m_nSize
);
116 bool CPicture::Load(tstring sFilePathName
)
118 bool bResult
= false;
123 FreePictureData(); // Important - Avoid Leaks...
125 // No-op if no file specified
126 if (sFilePathName
.empty())
129 // Load & initialize the GDI+ library if available
130 HMODULE hGdiPlusLib
= AtlLoadSystemLibraryUsingFullPath(_T("gdiplus.dll"));
131 if (hGdiPlusLib
&& GdiplusStartup(&gdiplusToken
, &gdiplusStartupInput
, NULL
) == Ok
)
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()
139 FreeLibrary(hGdiPlusLib
);
141 // Attempt to load using GDI+ if available
144 pBitmap
= new Bitmap(sFilePathName
.c_str(), FALSE
);
146 pBitmap
->GetRawFormat(&guid
);
148 if (pBitmap
->GetLastStatus() != Ok
)
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
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
);
171 // Icon file, get special treatment...
180 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
183 BY_HANDLE_FILE_INFORMATION fileinfo
;
184 if (GetFileInformationByHandle(hFile
, &fileinfo
))
186 lpIcons
= new BYTE
[fileinfo
.nFileSizeLow
];
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
196 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
197 if ((lpIconDir
->idCount
)&&(lpIconDir
->idCount
* sizeof(ICONDIR
) <= fileinfo
.nFileIndexLow
))
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
,
243 else if (pBitmap
) // Image loaded successfully with GDI+
245 m_Height
= pBitmap
->GetHeight();
246 m_Width
= pBitmap
->GetWidth();
250 // If still failed to load the file...
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
;
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
);
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
);
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
);
300 //const char* version = FreeImage_GetVersion();
302 // Check the DLL is using compatible exports
305 // Derive file type from file header.
306 int fileType
= FreeImage_GetFileType(sFilePathName
.c_str(), 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
316 void* dib
= FreeImage_Load(fileType
, sFilePathName
.c_str(), 0);
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
);
341 else // Failed to lock the destination Bitmap
347 else // Bitmap allocation failed
353 FreeImage_Unload(dib
);
359 FreeLibrary(hFreeImageLib
);
360 hFreeImageLib
= NULL
;
364 else // GDI+ Unavailable...
368 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_HIDDEN
, NULL
);
371 BY_HANDLE_FILE_INFORMATION fileinfo
;
372 if (GetFileInformationByHandle(hFile
, &fileinfo
))
374 BYTE
* buffer
= new BYTE
[fileinfo
.nFileSizeLow
];
376 if (ReadFile(hFile
, buffer
, fileinfo
.nFileSizeLow
, &readbytes
, NULL
))
378 if (LoadPictureData(buffer
, readbytes
))
380 m_nSize
= fileinfo
.nFileSizeLow
;
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
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
);
414 BY_HANDLE_FILE_INFORMATION fileinfo
;
415 if (GetFileInformationByHandle(hFile
, &fileinfo
))
417 m_nSize
= fileinfo
.nFileSizeLow
;
422 m_ColorDepth
= GetColorDepth();
427 bool CPicture::LoadPictureData(BYTE
*pBuffer
, int nSize
)
429 bool bResult
= false;
431 HGLOBAL hGlobal
= GlobalAlloc(GMEM_MOVEABLE
, nSize
);
438 void* pData
= GlobalLock(hGlobal
);
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
);
452 bResult
= hr
== S_OK
;
455 FreeResource(hGlobal
); // 16Bit Windows Needs This (32Bit - Automatic Release)
460 bool CPicture::Show(HDC hDC
, RECT DrawRect
)
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
);
469 if ((m_IPicture
== NULL
)&&(pBitmap
== NULL
))
476 m_IPicture
->get_Width(&Width
);
477 m_IPicture
->get_Height(&Height
);
479 HRESULT hr
= m_IPicture
->Render(hDC
,
480 DrawRect
.left
, // Left
482 DrawRect
.right
- DrawRect
.left
, // Right
483 DrawRect
.bottom
- DrawRect
.top
, // Bottom
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
);
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
);
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
:
536 case PixelFormat4bppIndexed
:
538 case PixelFormat8bppIndexed
:
540 case PixelFormat16bppARGB1555
:
541 case PixelFormat16bppGrayScale
:
542 case PixelFormat16bppRGB555
:
543 case PixelFormat16bppRGB565
:
545 case PixelFormat24bppRGB
:
547 case PixelFormat32bppARGB
:
548 case PixelFormat32bppPARGB
:
549 case PixelFormat32bppRGB
:
551 case PixelFormat48bppRGB
:
553 case PixelFormat64bppARGB
:
554 case PixelFormat64bppPARGB
:
560 UINT
CPicture::GetNumberOfFrames(int dimension
)
562 if (bIsIcon
&& lpIcons
)
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
]);
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();
601 count
= pBitmap
->GetFrameDimensionsCount();
602 GUID
* pDimensionIDs
= (GUID
*)malloc(sizeof(GUID
)*count
);
604 pBitmap
->GetFrameDimensionsList(pDimensionIDs
, count
);
606 UINT frameCount
= pBitmap
->GetFrameCount(&pDimensionIDs
[0]);
610 if (frame
> frameCount
)
613 GUID pageGuid
= FrameDimensionTime
;
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
;
633 delay
= ((long*)pPropertyItem
->value
)[prevframe
] * 10;
636 m_Height
= GetHeight();
637 m_Width
= GetWidth();
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;