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"
27 #pragma comment(lib, "shlwapi.lib")
28 #pragma comment(lib, "gdiplus.lib")
30 #define HIMETRIC_INCH 2540
39 , m_ip(InterpolationModeDefault
)
54 FreePictureData(); // Important - Avoid Leaks...
57 GdiplusShutdown(gdiplusToken
);
61 void CPicture::FreePictureData()
63 if (m_IPicture
!= NULL
)
65 m_IPicture
->Release();
74 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
77 for (int i
=0; i
<lpIconDir
->idCount
; ++i
)
79 DestroyIcon(hIcons
[i
]);
88 // Util function to ease loading of FreeImage library
89 static FARPROC
s_GetProcAddressEx(HMODULE hDll
, const char* procName
, bool& valid
)
95 proc
= GetProcAddress(hDll
, procName
);
104 tstring
CPicture::GetFileSizeAsText(bool bAbbrev
/* = true */)
106 TCHAR buf
[100] = {0};
108 StrFormatByteSize(m_nSize
, buf
, _countof(buf
));
110 _stprintf_s(buf
, _T("%ld Bytes"), m_nSize
);
115 bool CPicture::Load(tstring sFilePathName
)
117 bool bResult
= false;
122 FreePictureData(); // Important - Avoid Leaks...
124 // No-op if no file specified
125 if (sFilePathName
.empty())
128 // Load & initialize the GDI+ library if available
129 HMODULE hGdiPlusLib
= LoadLibrary(_T("gdiplus.dll"));
130 if (hGdiPlusLib
&& GdiplusStartup(&gdiplusToken
, &gdiplusStartupInput
, NULL
) == Ok
)
134 // Since we loaded the gdiplus.dll only to check if it's available, we
135 // can safely free the library here again - GdiplusStartup() loaded it too
136 // and reference counting will make sure that it stays loaded until GdiplusShutdown()
138 FreeLibrary(hGdiPlusLib
);
140 // Attempt to load using GDI+ if available
143 pBitmap
= new Bitmap(sFilePathName
.c_str(), FALSE
);
145 pBitmap
->GetRawFormat(&guid
);
147 if (pBitmap
->GetLastStatus() != Ok
)
153 // gdiplus only loads the first icon found in an icon file
154 // so we have to handle icon files ourselves :(
156 // Even though gdiplus can load icons, it can't load the new
157 // icons from Vista - in Vista, the icon format changed slightly.
158 // But the LoadIcon/LoadImage API still can load those icons,
159 // at least those dimensions which are also used on pre-Vista
161 // For that reason, we don't rely on gdiplus telling us if
162 // the image format is "icon" or not, we also check the
163 // file extension for ".ico".
164 std::transform(sFilePathName
.begin(), sFilePathName
.end(), sFilePathName
.begin(), ::tolower
);
165 bIsIcon
= (guid
== ImageFormatIcon
) || (_tcsstr(sFilePathName
.c_str(), _T(".ico")) != NULL
);
166 bIsTiff
= (guid
== ImageFormatTIFF
) || (_tcsstr(sFilePathName
.c_str(), _T(".tiff")) != NULL
);
170 // Icon file, get special treatment...
179 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
182 BY_HANDLE_FILE_INFORMATION fileinfo
;
183 if (GetFileInformationByHandle(hFile
, &fileinfo
))
185 lpIcons
= new BYTE
[fileinfo
.nFileSizeLow
];
187 if (ReadFile(hFile
, lpIcons
, fileinfo
.nFileSizeLow
, &readbytes
, NULL
))
189 // we have the icon. Now gather the information we need later
190 if (readbytes
>= sizeof(ICONDIR
))
192 // we are going to open same file second time so we have to close the file now
195 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
196 if ((lpIconDir
->idCount
)&&(lpIconDir
->idCount
* sizeof(ICONDIR
) <= fileinfo
.nFileIndexLow
))
201 hIcons
= new HICON
[lpIconDir
->idCount
];
202 m_Width
= lpIconDir
->idEntries
[0].bWidth
;
203 m_Height
= lpIconDir
->idEntries
[0].bHeight
;
204 for (int i
=0; i
<lpIconDir
->idCount
; ++i
)
206 hIcons
[i
] = (HICON
)LoadImage(NULL
, sFilePathName
.c_str(), IMAGE_ICON
,
207 lpIconDir
->idEntries
[i
].bWidth
,
208 lpIconDir
->idEntries
[i
].bHeight
,
242 else if (pBitmap
) // Image loaded successfully with GDI+
244 m_Height
= pBitmap
->GetHeight();
245 m_Width
= pBitmap
->GetWidth();
249 // If still failed to load the file...
252 // Attempt to load the FreeImage library as an optional DLL to support additional formats
254 // NOTE: Currently just loading via FreeImage & using GDI+ for drawing.
255 // It might be nice to remove this dependency in the future.
256 HMODULE hFreeImageLib
= LoadLibrary(_T("FreeImage.dll"));
258 // FreeImage DLL functions
259 typedef const char* (__stdcall
*FreeImage_GetVersion_t
)(void);
260 typedef int (__stdcall
*FreeImage_GetFileType_t
)(const TCHAR
*filename
, int size
);
261 typedef int (__stdcall
*FreeImage_GetFIFFromFilename_t
)(const TCHAR
*filename
);
262 typedef void* (__stdcall
*FreeImage_Load_t
)(int format
, const TCHAR
*filename
, int flags
);
263 typedef void (__stdcall
*FreeImage_Unload_t
)(void* dib
);
264 typedef int (__stdcall
*FreeImage_GetColorType_t
)(void* dib
);
265 typedef unsigned (__stdcall
*FreeImage_GetWidth_t
)(void* dib
);
266 typedef unsigned (__stdcall
*FreeImage_GetHeight_t
)(void* dib
);
267 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
);
269 //FreeImage_GetVersion_t FreeImage_GetVersion = NULL;
270 FreeImage_GetFileType_t FreeImage_GetFileType
= NULL
;
271 FreeImage_GetFIFFromFilename_t FreeImage_GetFIFFromFilename
= NULL
;
272 FreeImage_Load_t FreeImage_Load
= NULL
;
273 FreeImage_Unload_t FreeImage_Unload
= NULL
;
274 //FreeImage_GetColorType_t FreeImage_GetColorType = NULL;
275 FreeImage_GetWidth_t FreeImage_GetWidth
= NULL
;
276 FreeImage_GetHeight_t FreeImage_GetHeight
= NULL
;
277 FreeImage_ConvertToRawBits_t FreeImage_ConvertToRawBits
= NULL
;
281 bool exportsValid
= true;
283 //FreeImage_GetVersion = (FreeImage_GetVersion_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetVersion@0", valid);
284 FreeImage_GetWidth
= (FreeImage_GetWidth_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetWidth@4", exportsValid
);
285 FreeImage_GetHeight
= (FreeImage_GetHeight_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetHeight@4", exportsValid
);
286 FreeImage_Unload
= (FreeImage_Unload_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_Unload@4", exportsValid
);
287 FreeImage_ConvertToRawBits
= (FreeImage_ConvertToRawBits_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_ConvertToRawBits@32", exportsValid
);
290 FreeImage_GetFileType
= (FreeImage_GetFileType_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFileTypeU@8", exportsValid
);
291 FreeImage_GetFIFFromFilename
= (FreeImage_GetFIFFromFilename_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFIFFromFilenameU@4", exportsValid
);
292 FreeImage_Load
= (FreeImage_Load_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_LoadU@12", exportsValid
);
294 FreeImage_GetFileType
= (FreeImage_GetFileType_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFileType@8", exportsValid
);
295 FreeImage_GetFIFFromFilename
= (FreeImage_GetFIFFromFilename_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFIFFromFilename@4", exportsValid
);
296 FreeImage_Load
= (FreeImage_Load_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_Load@12", exportsValid
);
299 //const char* version = FreeImage_GetVersion();
301 // Check the DLL is using compatible exports
304 // Derive file type from file header.
305 int fileType
= FreeImage_GetFileType(sFilePathName
.c_str(), 0);
308 // No file header available, attempt to parse file name for extension.
309 fileType
= FreeImage_GetFIFFromFilename(sFilePathName
.c_str());
312 // If we have a valid file type
315 void* dib
= FreeImage_Load(fileType
, sFilePathName
.c_str(), 0);
319 unsigned width
= FreeImage_GetWidth(dib
);
320 unsigned height
= FreeImage_GetHeight(dib
);
322 // Create a GDI+ bitmap to load into...
323 pBitmap
= new Bitmap(width
, height
, PixelFormat32bppARGB
);
325 if (pBitmap
&& pBitmap
->GetLastStatus() == Ok
)
327 // Write & convert the loaded data into the GDI+ Bitmap
328 Rect
rect(0, 0, width
, height
);
329 BitmapData bitmapData
;
330 if (pBitmap
->LockBits(&rect
, ImageLockModeWrite
, PixelFormat32bppARGB
, &bitmapData
) == Ok
)
332 FreeImage_ConvertToRawBits((BYTE
*)bitmapData
.Scan0
, dib
, bitmapData
.Stride
, 32, 0xff << RED_SHIFT
, 0xff << GREEN_SHIFT
, 0xff << BLUE_SHIFT
, FALSE
);
334 pBitmap
->UnlockBits(&bitmapData
);
340 else // Failed to lock the destination Bitmap
346 else // Bitmap allocation failed
352 FreeImage_Unload(dib
);
358 FreeLibrary(hFreeImageLib
);
359 hFreeImageLib
= NULL
;
363 else // GDI+ Unavailable...
367 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_HIDDEN
, NULL
);
370 BY_HANDLE_FILE_INFORMATION fileinfo
;
371 if (GetFileInformationByHandle(hFile
, &fileinfo
))
373 BYTE
* buffer
= new BYTE
[fileinfo
.nFileSizeLow
];
375 if (ReadFile(hFile
, buffer
, fileinfo
.nFileSizeLow
, &readbytes
, NULL
))
377 if (LoadPictureData(buffer
, readbytes
))
379 m_nSize
= fileinfo
.nFileSizeLow
;
389 m_Name
= sFilePathName
;
390 m_Weight
= nSize
; // Update Picture Size Info...
392 if(m_IPicture
!= NULL
) // Do Not Try To Read From Memory That Does Not Exist...
394 m_IPicture
->get_Height(&m_Height
);
395 m_IPicture
->get_Width(&m_Width
);
396 // Calculate Its Size On a "Standard" (96 DPI) Device Context
397 m_Height
= MulDiv(m_Height
, 96, HIMETRIC_INCH
);
398 m_Width
= MulDiv(m_Width
, 96, HIMETRIC_INCH
);
400 else // Picture Data Is Not a Known Picture Type
408 if ((bResult
)&&(m_nSize
== 0))
410 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_HIDDEN
, NULL
);
413 BY_HANDLE_FILE_INFORMATION fileinfo
;
414 if (GetFileInformationByHandle(hFile
, &fileinfo
))
416 m_nSize
= fileinfo
.nFileSizeLow
;
421 m_ColorDepth
= GetColorDepth();
426 bool CPicture::LoadPictureData(BYTE
*pBuffer
, int nSize
)
428 bool bResult
= false;
430 HGLOBAL hGlobal
= GlobalAlloc(GMEM_MOVEABLE
, nSize
);
437 void* pData
= GlobalLock(hGlobal
);
440 memcpy(pData
, pBuffer
, nSize
);
441 GlobalUnlock(hGlobal
);
443 IStream
* pStream
= NULL
;
445 if ((CreateStreamOnHGlobal(hGlobal
, true, &pStream
) == S_OK
)&&(pStream
))
447 HRESULT hr
= OleLoadPicture(pStream
, nSize
, false, IID_IPicture
, (LPVOID
*)&m_IPicture
);
451 bResult
= hr
== S_OK
;
454 FreeResource(hGlobal
); // 16Bit Windows Needs This (32Bit - Automatic Release)
459 bool CPicture::Show(HDC hDC
, RECT DrawRect
)
463 if (bIsIcon
&& lpIcons
)
465 ::DrawIconEx(hDC
, DrawRect
.left
, DrawRect
.top
, hIcons
[nCurrentIcon
], DrawRect
.right
-DrawRect
.left
, DrawRect
.bottom
-DrawRect
.top
, 0, NULL
, DI_NORMAL
);
468 if ((m_IPicture
== NULL
)&&(pBitmap
== NULL
))
475 m_IPicture
->get_Width(&Width
);
476 m_IPicture
->get_Height(&Height
);
478 HRESULT hr
= m_IPicture
->Render(hDC
,
479 DrawRect
.left
, // Left
481 DrawRect
.right
- DrawRect
.left
, // Right
482 DrawRect
.bottom
- DrawRect
.top
, // Bottom
494 Graphics
graphics(hDC
);
495 graphics
.SetInterpolationMode(m_ip
);
496 graphics
.SetPixelOffsetMode(PixelOffsetModeHighQuality
);
497 ImageAttributes attr
;
498 attr
.SetWrapMode(WrapModeTileFlipXY
);
499 Rect
rect(DrawRect
.left
, DrawRect
.top
, DrawRect
.right
-DrawRect
.left
, DrawRect
.bottom
-DrawRect
.top
);
500 graphics
.DrawImage(pBitmap
, rect
, 0, 0, m_Width
, m_Height
, UnitPixel
, &attr
);
507 bool CPicture::UpdateSizeOnDC(HDC hDC
)
509 if(hDC
== NULL
|| m_IPicture
== NULL
) { m_Height
= 0; m_Width
= 0; return(false); };
511 m_IPicture
->get_Height(&m_Height
);
512 m_IPicture
->get_Width(&m_Width
);
514 // Get Current DPI - Dot Per Inch
515 int CurrentDPI_X
= GetDeviceCaps(hDC
, LOGPIXELSX
);
516 int CurrentDPI_Y
= GetDeviceCaps(hDC
, LOGPIXELSY
);
518 m_Height
= MulDiv(m_Height
, CurrentDPI_Y
, HIMETRIC_INCH
);
519 m_Width
= MulDiv(m_Width
, CurrentDPI_X
, HIMETRIC_INCH
);
524 UINT
CPicture::GetColorDepth() const
526 if (bIsIcon
&& lpIcons
)
528 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
529 return lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
;
531 switch (GetPixelFormat())
533 case PixelFormat1bppIndexed
:
535 case PixelFormat4bppIndexed
:
537 case PixelFormat8bppIndexed
:
539 case PixelFormat16bppARGB1555
:
540 case PixelFormat16bppGrayScale
:
541 case PixelFormat16bppRGB555
:
542 case PixelFormat16bppRGB565
:
544 case PixelFormat24bppRGB
:
546 case PixelFormat32bppARGB
:
547 case PixelFormat32bppPARGB
:
548 case PixelFormat32bppRGB
:
550 case PixelFormat48bppRGB
:
552 case PixelFormat64bppARGB
:
553 case PixelFormat64bppPARGB
:
559 UINT
CPicture::GetNumberOfFrames(int dimension
)
561 if (bIsIcon
&& lpIcons
)
567 UINT count
= pBitmap
->GetFrameDimensionsCount();
568 GUID
* pDimensionIDs
= (GUID
*)malloc(sizeof(GUID
)*count
);
570 pBitmap
->GetFrameDimensionsList(pDimensionIDs
, count
);
572 UINT frameCount
= pBitmap
->GetFrameCount(&pDimensionIDs
[dimension
]);
578 UINT
CPicture::GetNumberOfDimensions()
580 if (bIsIcon
&& lpIcons
)
582 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
583 return lpIconDir
->idCount
;
585 return pBitmap
? pBitmap
->GetFrameDimensionsCount() : 0;
588 long CPicture::SetActiveFrame(UINT frame
)
590 if (bIsIcon
&& lpIcons
)
592 nCurrentIcon
= frame
-1;
593 m_Height
= GetHeight();
594 m_Width
= GetWidth();
600 count
= pBitmap
->GetFrameDimensionsCount();
601 GUID
* pDimensionIDs
= (GUID
*)malloc(sizeof(GUID
)*count
);
603 pBitmap
->GetFrameDimensionsList(pDimensionIDs
, count
);
605 UINT frameCount
= pBitmap
->GetFrameCount(&pDimensionIDs
[0]);
609 if (frame
> frameCount
)
612 GUID pageGuid
= FrameDimensionTime
;
614 pageGuid
= FrameDimensionPage
;
615 pBitmap
->SelectActiveFrame(&pageGuid
, frame
);
617 // Assume that the image has a property item of type PropertyItemEquipMake.
618 // Get the size of that property item.
619 int nSize
= pBitmap
->GetPropertyItemSize(PropertyTagFrameDelay
);
621 // Allocate a buffer to receive the property item.
622 PropertyItem
* pPropertyItem
= (PropertyItem
*) malloc(nSize
);
624 Status s
= pBitmap
->GetPropertyItem(PropertyTagFrameDelay
, nSize
, pPropertyItem
);
626 UINT prevframe
= frame
;
632 delay
= ((long*)pPropertyItem
->value
)[prevframe
] * 10;
635 m_Height
= GetHeight();
636 m_Width
= GetWidth();
640 UINT
CPicture::GetHeight() const
642 if ((bIsIcon
)&&(lpIcons
))
644 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
645 return lpIconDir
->idEntries
[nCurrentIcon
].bHeight
;
647 return pBitmap
? pBitmap
->GetHeight() : 0;
650 UINT
CPicture::GetWidth() const
652 if ((bIsIcon
)&&(lpIcons
))
654 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
655 return lpIconDir
->idEntries
[nCurrentIcon
].bWidth
;
657 return pBitmap
? pBitmap
->GetWidth() : 0;
660 PixelFormat
CPicture::GetPixelFormat() const
662 if ((bIsIcon
)&&(lpIcons
))
664 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
665 if (lpIconDir
->idEntries
[nCurrentIcon
].wPlanes
== 1)
667 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 1)
668 return PixelFormat1bppIndexed
;
669 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 4)
670 return PixelFormat4bppIndexed
;
671 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 8)
672 return PixelFormat8bppIndexed
;
674 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 32)
676 return PixelFormat32bppARGB
;
679 return pBitmap
? pBitmap
->GetPixelFormat() : 0;