1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2014 - 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"
29 #pragma comment(lib, "shlwapi.lib")
30 #pragma comment(lib, "gdiplus.lib")
31 // note: linking with Windowscodecs.lib does not make the exe require the dll
32 // which means it still runs on XP even if the WIC isn't installed - WIC won't work
33 // but the exe still runs
34 #pragma comment(lib, "Windowscodecs.lib")
36 #define HIMETRIC_INCH 2540
45 , m_ip(InterpolationModeDefault
)
60 FreePictureData(); // Important - Avoid Leaks...
63 GdiplusShutdown(gdiplusToken
);
67 void CPicture::FreePictureData()
69 if (m_IPicture
!= NULL
)
71 m_IPicture
->Release();
80 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
83 for (int i
=0; i
<lpIconDir
->idCount
; ++i
)
85 DestroyIcon(hIcons
[i
]);
94 // Util function to ease loading of FreeImage library
95 static FARPROC
s_GetProcAddressEx(HMODULE hDll
, const char* procName
, bool& valid
)
101 proc
= GetProcAddress(hDll
, procName
);
110 tstring
CPicture::GetFileSizeAsText(bool bAbbrev
/* = true */)
112 TCHAR buf
[100] = {0};
114 StrFormatByteSize(m_nSize
, buf
, _countof(buf
));
116 _stprintf_s(buf
, _T("%ld Bytes"), m_nSize
);
121 bool CPicture::Load(tstring sFilePathName
)
123 bool bResult
= false;
128 FreePictureData(); // Important - Avoid Leaks...
130 // No-op if no file specified
131 if (sFilePathName
.empty())
134 // Load & initialize the GDI+ library if available
135 HMODULE hGdiPlusLib
= AtlLoadSystemLibraryUsingFullPath(_T("gdiplus.dll"));
136 if (hGdiPlusLib
&& GdiplusStartup(&gdiplusToken
, &gdiplusStartupInput
, NULL
) == Ok
)
140 // Since we loaded the gdiplus.dll only to check if it's available, we
141 // can safely free the library here again - GdiplusStartup() loaded it too
142 // and reference counting will make sure that it stays loaded until GdiplusShutdown()
144 FreeLibrary(hGdiPlusLib
);
146 // Attempt to load using GDI+ if available
149 pBitmap
= new Bitmap(sFilePathName
.c_str(), FALSE
);
151 pBitmap
->GetRawFormat(&guid
);
153 if (pBitmap
->GetLastStatus() != Ok
)
159 // gdiplus only loads the first icon found in an icon file
160 // so we have to handle icon files ourselves :(
162 // Even though gdiplus can load icons, it can't load the new
163 // icons from Vista - in Vista, the icon format changed slightly.
164 // But the LoadIcon/LoadImage API still can load those icons,
165 // at least those dimensions which are also used on pre-Vista
167 // For that reason, we don't rely on gdiplus telling us if
168 // the image format is "icon" or not, we also check the
169 // file extension for ".ico".
170 std::transform(sFilePathName
.begin(), sFilePathName
.end(), sFilePathName
.begin(), ::tolower
);
171 bIsIcon
= (guid
== ImageFormatIcon
) || (wcsstr(sFilePathName
.c_str(), L
".ico") != NULL
) || (wcsstr(sFilePathName
.c_str(), L
".cur") != NULL
);
172 bIsTiff
= (guid
== ImageFormatTIFF
) || (_tcsstr(sFilePathName
.c_str(), _T(".tiff")) != NULL
);
173 m_Name
= sFilePathName
;
177 // Icon file, get special treatment...
186 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
189 BY_HANDLE_FILE_INFORMATION fileinfo
;
190 if (GetFileInformationByHandle(hFile
, &fileinfo
))
192 lpIcons
= new BYTE
[fileinfo
.nFileSizeLow
];
194 if (ReadFile(hFile
, lpIcons
, fileinfo
.nFileSizeLow
, &readbytes
, NULL
))
196 // we have the icon. Now gather the information we need later
197 if (readbytes
>= sizeof(ICONDIR
))
199 // we are going to open same file second time so we have to close the file now
202 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
203 if ((lpIconDir
->idCount
) && ((lpIconDir
->idCount
* sizeof(ICONDIR
)) <= fileinfo
.nFileSizeLow
))
209 hIcons
= new HICON
[lpIconDir
->idCount
];
210 // check that the pointers point to data that we just loaded
211 if (((BYTE
*)lpIconDir
->idEntries
> (BYTE
*)lpIconDir
) &&
212 (((BYTE
*)lpIconDir
->idEntries
) + (lpIconDir
->idCount
* sizeof(ICONDIRENTRY
)) < ((BYTE
*)lpIconDir
) + fileinfo
.nFileSizeLow
))
214 m_Width
= lpIconDir
->idEntries
[0].bWidth
;
215 m_Height
= lpIconDir
->idEntries
[0].bHeight
;
217 for (int i
=0; i
<lpIconDir
->idCount
; ++i
)
219 hIcons
[i
] = (HICON
)LoadImage(NULL
, sFilePathName
.c_str(), IMAGE_ICON
,
220 lpIconDir
->idEntries
[i
].bWidth
,
221 lpIconDir
->idEntries
[i
].bHeight
,
223 if (hIcons
[i
] == NULL
)
225 // if the icon couldn't be loaded, the data is most likely corrupt
263 else if (pBitmap
) // Image loaded successfully with GDI+
265 m_Height
= pBitmap
->GetHeight();
266 m_Width
= pBitmap
->GetWidth();
270 // If still failed to load the file...
273 // Attempt to load the FreeImage library as an optional DLL to support additional formats
275 // NOTE: Currently just loading via FreeImage & using GDI+ for drawing.
276 // It might be nice to remove this dependency in the future.
277 HMODULE hFreeImageLib
= LoadLibrary(_T("FreeImage.dll"));
279 // FreeImage DLL functions
280 typedef const char* (__stdcall
*FreeImage_GetVersion_t
)(void);
281 typedef int (__stdcall
*FreeImage_GetFileType_t
)(const TCHAR
*filename
, int size
);
282 typedef int (__stdcall
*FreeImage_GetFIFFromFilename_t
)(const TCHAR
*filename
);
283 typedef void* (__stdcall
*FreeImage_Load_t
)(int format
, const TCHAR
*filename
, int flags
);
284 typedef void (__stdcall
*FreeImage_Unload_t
)(void* dib
);
285 typedef int (__stdcall
*FreeImage_GetColorType_t
)(void* dib
);
286 typedef unsigned (__stdcall
*FreeImage_GetWidth_t
)(void* dib
);
287 typedef unsigned (__stdcall
*FreeImage_GetHeight_t
)(void* dib
);
288 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
);
290 //FreeImage_GetVersion_t FreeImage_GetVersion = NULL;
291 FreeImage_GetFileType_t FreeImage_GetFileType
= NULL
;
292 FreeImage_GetFIFFromFilename_t FreeImage_GetFIFFromFilename
= NULL
;
293 FreeImage_Load_t FreeImage_Load
= NULL
;
294 FreeImage_Unload_t FreeImage_Unload
= NULL
;
295 //FreeImage_GetColorType_t FreeImage_GetColorType = NULL;
296 FreeImage_GetWidth_t FreeImage_GetWidth
= NULL
;
297 FreeImage_GetHeight_t FreeImage_GetHeight
= NULL
;
298 FreeImage_ConvertToRawBits_t FreeImage_ConvertToRawBits
= NULL
;
302 bool exportsValid
= true;
304 //FreeImage_GetVersion = (FreeImage_GetVersion_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetVersion@0", valid);
305 FreeImage_GetWidth
= (FreeImage_GetWidth_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetWidth@4", exportsValid
);
306 FreeImage_GetHeight
= (FreeImage_GetHeight_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetHeight@4", exportsValid
);
307 FreeImage_Unload
= (FreeImage_Unload_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_Unload@4", exportsValid
);
308 FreeImage_ConvertToRawBits
= (FreeImage_ConvertToRawBits_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_ConvertToRawBits@32", exportsValid
);
311 FreeImage_GetFileType
= (FreeImage_GetFileType_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFileTypeU@8", exportsValid
);
312 FreeImage_GetFIFFromFilename
= (FreeImage_GetFIFFromFilename_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFIFFromFilenameU@4", exportsValid
);
313 FreeImage_Load
= (FreeImage_Load_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_LoadU@12", exportsValid
);
315 FreeImage_GetFileType
= (FreeImage_GetFileType_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFileType@8", exportsValid
);
316 FreeImage_GetFIFFromFilename
= (FreeImage_GetFIFFromFilename_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_GetFIFFromFilename@4", exportsValid
);
317 FreeImage_Load
= (FreeImage_Load_t
)s_GetProcAddressEx(hFreeImageLib
, "_FreeImage_Load@12", exportsValid
);
320 //const char* version = FreeImage_GetVersion();
322 // Check the DLL is using compatible exports
325 // Derive file type from file header.
326 int fileType
= FreeImage_GetFileType(sFilePathName
.c_str(), 0);
329 // No file header available, attempt to parse file name for extension.
330 fileType
= FreeImage_GetFIFFromFilename(sFilePathName
.c_str());
333 // If we have a valid file type
336 void* dib
= FreeImage_Load(fileType
, sFilePathName
.c_str(), 0);
340 unsigned width
= FreeImage_GetWidth(dib
);
341 unsigned height
= FreeImage_GetHeight(dib
);
343 // Create a GDI+ bitmap to load into...
344 pBitmap
= new Bitmap(width
, height
, PixelFormat32bppARGB
);
346 if (pBitmap
&& pBitmap
->GetLastStatus() == Ok
)
348 // Write & convert the loaded data into the GDI+ Bitmap
349 Rect
rect(0, 0, width
, height
);
350 BitmapData bitmapData
;
351 if (pBitmap
->LockBits(&rect
, ImageLockModeWrite
, PixelFormat32bppARGB
, &bitmapData
) == Ok
)
353 FreeImage_ConvertToRawBits((BYTE
*)bitmapData
.Scan0
, dib
, bitmapData
.Stride
, 32, 0xff << RED_SHIFT
, 0xff << GREEN_SHIFT
, 0xff << BLUE_SHIFT
, FALSE
);
355 pBitmap
->UnlockBits(&bitmapData
);
361 else // Failed to lock the destination Bitmap
367 else // Bitmap allocation failed
373 FreeImage_Unload(dib
);
379 FreeLibrary(hFreeImageLib
);
380 hFreeImageLib
= NULL
;
384 else // GDI+ Unavailable...
388 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_HIDDEN
, NULL
);
391 BY_HANDLE_FILE_INFORMATION fileinfo
;
392 if (GetFileInformationByHandle(hFile
, &fileinfo
))
394 BYTE
* buffer
= new BYTE
[fileinfo
.nFileSizeLow
];
396 if (ReadFile(hFile
, buffer
, fileinfo
.nFileSizeLow
, &readbytes
, NULL
))
398 if (LoadPictureData(buffer
, readbytes
))
400 m_nSize
= fileinfo
.nFileSizeLow
;
410 m_Name
= sFilePathName
;
411 m_Weight
= nSize
; // Update Picture Size Info...
413 if(m_IPicture
!= NULL
) // Do Not Try To Read From Memory That Does Not Exist...
415 m_IPicture
->get_Height(&m_Height
);
416 m_IPicture
->get_Width(&m_Width
);
417 // Calculate Its Size On a "Standard" (96 DPI) Device Context
418 m_Height
= MulDiv(m_Height
, 96, HIMETRIC_INCH
);
419 m_Width
= MulDiv(m_Width
, 96, HIMETRIC_INCH
);
421 else // Picture Data Is Not a Known Picture Type
429 if ((bResult
)&&(m_nSize
== 0))
431 CAutoFile hFile
= CreateFile(sFilePathName
.c_str(), GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_HIDDEN
, NULL
);
434 BY_HANDLE_FILE_INFORMATION fileinfo
;
435 if (GetFileInformationByHandle(hFile
, &fileinfo
))
437 m_nSize
= fileinfo
.nFileSizeLow
;
442 m_ColorDepth
= GetColorDepth();
447 bool CPicture::LoadPictureData(BYTE
*pBuffer
, int nSize
)
449 bool bResult
= false;
451 HGLOBAL hGlobal
= GlobalAlloc(GMEM_MOVEABLE
, nSize
);
458 void* pData
= GlobalLock(hGlobal
);
461 memcpy(pData
, pBuffer
, nSize
);
462 GlobalUnlock(hGlobal
);
464 IStream
* pStream
= NULL
;
466 if ((CreateStreamOnHGlobal(hGlobal
, true, &pStream
) == S_OK
)&&(pStream
))
468 HRESULT hr
= OleLoadPicture(pStream
, nSize
, false, IID_IPicture
, (LPVOID
*)&m_IPicture
);
472 bResult
= hr
== S_OK
;
475 FreeResource(hGlobal
); // 16Bit Windows Needs This (32Bit - Automatic Release)
480 bool CPicture::Show(HDC hDC
, RECT DrawRect
)
484 if (bIsIcon
&& lpIcons
)
486 ::DrawIconEx(hDC
, DrawRect
.left
, DrawRect
.top
, hIcons
[nCurrentIcon
], DrawRect
.right
-DrawRect
.left
, DrawRect
.bottom
-DrawRect
.top
, 0, NULL
, DI_NORMAL
);
489 if ((m_IPicture
== NULL
)&&(pBitmap
== NULL
))
496 m_IPicture
->get_Width(&Width
);
497 m_IPicture
->get_Height(&Height
);
499 HRESULT hr
= m_IPicture
->Render(hDC
,
500 DrawRect
.left
, // Left
502 DrawRect
.right
- DrawRect
.left
, // Right
503 DrawRect
.bottom
- DrawRect
.top
, // Bottom
515 Graphics
graphics(hDC
);
516 graphics
.SetInterpolationMode(m_ip
);
517 graphics
.SetPixelOffsetMode(PixelOffsetModeHighQuality
);
518 graphics
.SetPixelOffsetMode(PixelOffsetModeHalf
);
519 ImageAttributes attr
;
520 attr
.SetWrapMode(WrapModeTileFlipXY
);
521 Rect
rect(DrawRect
.left
, DrawRect
.top
, DrawRect
.right
-DrawRect
.left
, DrawRect
.bottom
-DrawRect
.top
);
522 graphics
.DrawImage(pBitmap
, rect
, 0, 0, m_Width
, m_Height
, UnitPixel
, &attr
);
529 bool CPicture::UpdateSizeOnDC(HDC hDC
)
531 if(hDC
== NULL
|| m_IPicture
== NULL
) { m_Height
= 0; m_Width
= 0; return(false); };
533 m_IPicture
->get_Height(&m_Height
);
534 m_IPicture
->get_Width(&m_Width
);
536 // Get Current DPI - Dot Per Inch
537 int CurrentDPI_X
= GetDeviceCaps(hDC
, LOGPIXELSX
);
538 int CurrentDPI_Y
= GetDeviceCaps(hDC
, LOGPIXELSY
);
540 m_Height
= MulDiv(m_Height
, CurrentDPI_Y
, HIMETRIC_INCH
);
541 m_Width
= MulDiv(m_Width
, CurrentDPI_X
, HIMETRIC_INCH
);
546 UINT
CPicture::GetColorDepth() const
548 if (bIsIcon
&& lpIcons
)
550 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
551 return lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
;
554 // try first with WIC to get the pixel format since GDI+ often returns 32-bit even if it's not
555 // Create the image factory.
557 IWICImagingFactory
* pFactory
= NULL
;
558 HRESULT hr
= CoCreateInstance(CLSID_WICImagingFactory
,
560 CLSCTX_INPROC_SERVER
,
561 IID_IWICImagingFactory
,
562 (LPVOID
*) &pFactory
);
564 // Create a decoder from the file.
567 IWICBitmapDecoder
* pDecoder
= NULL
;
568 hr
= pFactory
->CreateDecoderFromFilename(m_Name
.c_str(),
571 WICDecodeMetadataCacheOnDemand
,
575 IWICBitmapFrameDecode
* pBitmapFrameDecode
= NULL
;
576 hr
= pDecoder
->GetFrame(0, &pBitmapFrameDecode
);
579 IWICBitmapSource
* pSource
= NULL
;
580 pSource
= pBitmapFrameDecode
;
582 WICPixelFormatGUID pixelFormat
;
583 hr
= pSource
->GetPixelFormat(&pixelFormat
);
586 IWICComponentInfo
* piCompInfo
= NULL
;
587 hr
= pFactory
->CreateComponentInfo(pixelFormat
, &piCompInfo
);
590 IWICPixelFormatInfo
* piPixelFormatInfo
= NULL
;
591 hr
= piCompInfo
->QueryInterface(IID_IWICPixelFormatInfo
,(LPVOID
*) &piPixelFormatInfo
);
594 hr
= piPixelFormatInfo
->GetBitsPerPixel(&bpp
);
595 piPixelFormatInfo
->Release();
597 piCompInfo
->Release();
601 pBitmapFrameDecode
->Release();
610 switch (GetPixelFormat())
612 case PixelFormat1bppIndexed
:
614 case PixelFormat4bppIndexed
:
616 case PixelFormat8bppIndexed
:
618 case PixelFormat16bppARGB1555
:
619 case PixelFormat16bppGrayScale
:
620 case PixelFormat16bppRGB555
:
621 case PixelFormat16bppRGB565
:
623 case PixelFormat24bppRGB
:
625 case PixelFormat32bppARGB
:
626 case PixelFormat32bppPARGB
:
627 case PixelFormat32bppRGB
:
629 case PixelFormat48bppRGB
:
630 // note: GDI+ converts images with bit depths > 32 automatically
631 // on loading, so PixelFormat48bppRGB, PixelFormat64bppARGB and
632 // PixelFormat64bppPARGB will never be used here.
634 case PixelFormat64bppARGB
:
635 case PixelFormat64bppPARGB
:
641 UINT
CPicture::GetNumberOfFrames(int dimension
)
643 if (bIsIcon
&& lpIcons
)
649 UINT count
= pBitmap
->GetFrameDimensionsCount();
650 GUID
* pDimensionIDs
= (GUID
*)malloc(sizeof(GUID
)*count
);
652 pBitmap
->GetFrameDimensionsList(pDimensionIDs
, count
);
654 UINT frameCount
= pBitmap
->GetFrameCount(&pDimensionIDs
[dimension
]);
660 UINT
CPicture::GetNumberOfDimensions()
662 if (bIsIcon
&& lpIcons
)
664 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
665 return lpIconDir
->idCount
;
667 return pBitmap
? pBitmap
->GetFrameDimensionsCount() : 0;
670 long CPicture::SetActiveFrame(UINT frame
)
672 if (bIsIcon
&& lpIcons
)
674 nCurrentIcon
= frame
-1;
675 m_Height
= GetHeight();
676 m_Width
= GetWidth();
682 count
= pBitmap
->GetFrameDimensionsCount();
683 GUID
* pDimensionIDs
= (GUID
*)malloc(sizeof(GUID
)*count
);
685 pBitmap
->GetFrameDimensionsList(pDimensionIDs
, count
);
687 UINT frameCount
= pBitmap
->GetFrameCount(&pDimensionIDs
[0]);
691 if (frame
> frameCount
)
694 GUID pageGuid
= FrameDimensionTime
;
696 pageGuid
= FrameDimensionPage
;
697 pBitmap
->SelectActiveFrame(&pageGuid
, frame
);
699 // Assume that the image has a property item of type PropertyItemEquipMake.
700 // Get the size of that property item.
701 int nSize
= pBitmap
->GetPropertyItemSize(PropertyTagFrameDelay
);
703 // Allocate a buffer to receive the property item.
704 PropertyItem
* pPropertyItem
= (PropertyItem
*) malloc(nSize
);
706 Status s
= pBitmap
->GetPropertyItem(PropertyTagFrameDelay
, nSize
, pPropertyItem
);
708 UINT prevframe
= frame
;
714 delay
= ((long*)pPropertyItem
->value
)[prevframe
] * 10;
717 m_Height
= GetHeight();
718 m_Width
= GetWidth();
722 UINT
CPicture::GetHeight() const
724 if ((bIsIcon
)&&(lpIcons
))
726 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
727 return lpIconDir
->idEntries
[nCurrentIcon
].bHeight
;
729 return pBitmap
? pBitmap
->GetHeight() : 0;
732 UINT
CPicture::GetWidth() const
734 if ((bIsIcon
)&&(lpIcons
))
736 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
737 return lpIconDir
->idEntries
[nCurrentIcon
].bWidth
;
739 return pBitmap
? pBitmap
->GetWidth() : 0;
742 PixelFormat
CPicture::GetPixelFormat() const
744 if ((bIsIcon
)&&(lpIcons
))
746 LPICONDIR lpIconDir
= (LPICONDIR
)lpIcons
;
747 if (lpIconDir
->idEntries
[nCurrentIcon
].wPlanes
== 1)
749 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 1)
750 return PixelFormat1bppIndexed
;
751 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 4)
752 return PixelFormat4bppIndexed
;
753 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 8)
754 return PixelFormat8bppIndexed
;
756 if (lpIconDir
->idEntries
[nCurrentIcon
].wBitCount
== 32)
758 return PixelFormat32bppARGB
;
761 return pBitmap
? pBitmap
->GetPixelFormat() : 0;