1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
21 #include "shlwapi.h"
\r
23 #include <algorithm>
\r
24 #include "Picture.h"
\r
26 #pragma comment(lib, "shlwapi.lib")
\r
27 #pragma comment(lib, "gdiplus.lib")
\r
29 #define HIMETRIC_INCH 2540
\r
31 CPicture::CPicture()
\r
38 bHaveGDIPlus = false;
\r
39 m_ip = InterpolationModeDefault;
\r
48 CPicture::~CPicture()
\r
50 FreePictureData(); // Important - Avoid Leaks...
\r
54 GdiplusShutdown(gdiplusToken);
\r
58 void CPicture::FreePictureData()
\r
60 if (m_IPicture != NULL)
\r
62 m_IPicture->Release();
\r
71 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
74 for (int i=0; i<lpIconDir->idCount; ++i)
\r
76 DestroyIcon(hIcons[i]);
\r
86 // Util function to ease loading of FreeImage library
\r
87 static FARPROC s_GetProcAddressEx(HMODULE hDll, const char* procName, bool& valid)
\r
89 FARPROC proc = NULL;
\r
93 proc = GetProcAddress(hDll, procName);
\r
102 stdstring CPicture::GetFileSizeAsText(bool bAbbrev /* = true */)
\r
104 TCHAR buf[100] = {0};
\r
106 StrFormatByteSize(m_nSize, buf, 100);
\r
108 _stprintf_s(buf, _T("%ld Bytes"), m_nSize);
\r
110 return stdstring(buf);
\r
113 bool CPicture::Load(stdstring sFilePathName)
\r
115 bool bResult = false;
\r
118 //CFile PictureFile;
\r
119 //CFileException e;
\r
121 FreePictureData(); // Important - Avoid Leaks...
\r
123 // No-op if no file specified
\r
124 if (sFilePathName.empty())
\r
127 // Load & initialize the GDI+ library if available
\r
128 HMODULE hGdiPlusLib = LoadLibrary(_T("gdiplus.dll"));
\r
129 if (hGdiPlusLib && GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) == Ok)
\r
131 bHaveGDIPlus = true;
\r
133 // Since we loaded the gdiplus.dll only to check if it's available, we
\r
134 // can safely free the library here again - GdiplusStartup() loaded it too
\r
135 // and reference counting will make sure that it stays loaded until GdiplusShutdown()
\r
137 FreeLibrary(hGdiPlusLib);
\r
139 // Attempt to load using GDI+ if available
\r
142 pBitmap = new Bitmap(sFilePathName.c_str(), FALSE);
\r
144 pBitmap->GetRawFormat(&guid);
\r
146 if (pBitmap->GetLastStatus() != Ok)
\r
152 // gdiplus only loads the first icon found in an icon file
\r
153 // so we have to handle icon files ourselves :(
\r
155 // Even though gdiplus can load icons, it can't load the new
\r
156 // icons from Vista - in Vista, the icon format changed slightly.
\r
157 // But the LoadIcon/LoadImage API still can load those icons,
\r
158 // at least those dimensions which are also used on pre-Vista
\r
160 // For that reason, we don't rely on gdiplus telling us if
\r
161 // the image format is "icon" or not, we also check the
\r
162 // file extension for ".ico".
\r
163 std::transform(sFilePathName.begin(), sFilePathName.end(), sFilePathName.begin(), ::tolower);
\r
164 bIsIcon = (guid == ImageFormatIcon) || (_tcsstr(sFilePathName.c_str(), _T(".ico")) != NULL);
\r
168 // Icon file, get special treatment...
\r
171 // Cleanup first...
\r
177 HANDLE hFile = CreateFile(sFilePathName.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
\r
178 if (hFile != INVALID_HANDLE_VALUE)
\r
180 BY_HANDLE_FILE_INFORMATION fileinfo;
\r
181 if (GetFileInformationByHandle(hFile, &fileinfo))
\r
183 lpIcons = new BYTE[fileinfo.nFileSizeLow];
\r
185 if (ReadFile(hFile, lpIcons, fileinfo.nFileSizeLow, &readbytes, NULL))
\r
187 // we have the icon. Now gather the information we need later
\r
188 CloseHandle(hFile);
\r
189 if (readbytes >= sizeof(ICONDIR))
\r
192 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
193 hIcons = new HICON[lpIconDir->idCount];
\r
194 m_Width = lpIconDir->idEntries[0].bWidth;
\r
195 m_Height = lpIconDir->idEntries[0].bHeight;
\r
196 for (int i=0; i<lpIconDir->idCount; ++i)
\r
198 hIcons[i] = (HICON)LoadImage(NULL, sFilePathName.c_str(), IMAGE_ICON,
\r
199 lpIconDir->idEntries[i].bWidth,
\r
200 lpIconDir->idEntries[i].bHeight,
\r
216 CloseHandle(hFile);
\r
220 CloseHandle(hFile);
\r
223 else if (pBitmap) // Image loaded successfully with GDI+
\r
225 m_Height = pBitmap->GetHeight();
\r
226 m_Width = pBitmap->GetWidth();
\r
230 // If still failed to load the file...
\r
233 // Attempt to load the FreeImage library as an optional DLL to support additional formats
\r
235 // NOTE: Currently just loading via FreeImage & using GDI+ for drawing.
\r
236 // It might be nice to remove this dependency in the future.
\r
237 HMODULE hFreeImageLib = LoadLibrary(_T("FreeImage.dll"));
\r
239 // FreeImage DLL functions
\r
240 typedef const char* (__stdcall *FreeImage_GetVersion_t)(void);
\r
241 typedef int (__stdcall *FreeImage_GetFileType_t)(const TCHAR *filename, int size);
\r
242 typedef int (__stdcall *FreeImage_GetFIFFromFilename_t)(const TCHAR *filename);
\r
243 typedef void* (__stdcall *FreeImage_Load_t)(int format, const TCHAR *filename, int flags);
\r
244 typedef void (__stdcall *FreeImage_Unload_t)(void* dib);
\r
245 typedef int (__stdcall *FreeImage_GetColorType_t)(void* dib);
\r
246 typedef unsigned (__stdcall *FreeImage_GetWidth_t)(void* dib);
\r
247 typedef unsigned (__stdcall *FreeImage_GetHeight_t)(void* dib);
\r
248 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);
\r
250 FreeImage_GetVersion_t FreeImage_GetVersion = NULL;
\r
251 FreeImage_GetFileType_t FreeImage_GetFileType = NULL;
\r
252 FreeImage_GetFIFFromFilename_t FreeImage_GetFIFFromFilename = NULL;
\r
253 FreeImage_Load_t FreeImage_Load = NULL;
\r
254 FreeImage_Unload_t FreeImage_Unload = NULL;
\r
255 FreeImage_GetColorType_t FreeImage_GetColorType = NULL;
\r
256 FreeImage_GetWidth_t FreeImage_GetWidth = NULL;
\r
257 FreeImage_GetHeight_t FreeImage_GetHeight = NULL;
\r
258 FreeImage_ConvertToRawBits_t FreeImage_ConvertToRawBits = NULL;
\r
262 bool exportsValid = true;
\r
264 //FreeImage_GetVersion = (FreeImage_GetVersion_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetVersion@0", valid);
\r
265 FreeImage_GetWidth = (FreeImage_GetWidth_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetWidth@4", exportsValid);
\r
266 FreeImage_GetHeight = (FreeImage_GetHeight_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetHeight@4", exportsValid);
\r
267 FreeImage_Unload = (FreeImage_Unload_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_Unload@4", exportsValid);
\r
268 FreeImage_ConvertToRawBits = (FreeImage_ConvertToRawBits_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_ConvertToRawBits@32", exportsValid);
\r
271 FreeImage_GetFileType = (FreeImage_GetFileType_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFileTypeU@8", exportsValid);
\r
272 FreeImage_GetFIFFromFilename = (FreeImage_GetFIFFromFilename_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFIFFromFilenameU@4", exportsValid);
\r
273 FreeImage_Load = (FreeImage_Load_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_LoadU@12", exportsValid);
\r
275 FreeImage_GetFileType = (FreeImage_GetFileType_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFileType@8", exportsValid);
\r
276 FreeImage_GetFIFFromFilename = (FreeImage_GetFIFFromFilename_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_GetFIFFromFilename@4", exportsValid);
\r
277 FreeImage_Load = (FreeImage_Load_t)s_GetProcAddressEx(hFreeImageLib, "_FreeImage_Load@12", exportsValid);
\r
280 //const char* version = FreeImage_GetVersion();
\r
282 // Check the DLL is using compatible exports
\r
285 // Derive file type from file header.
\r
286 int fileType = FreeImage_GetFileType(sFilePathName.c_str(), 0);
\r
289 // No file header available, attempt to parse file name for extension.
\r
290 fileType = FreeImage_GetFIFFromFilename(sFilePathName.c_str());
\r
293 // If we have a valid file type
\r
296 void* dib = FreeImage_Load(fileType, sFilePathName.c_str(), 0);
\r
300 unsigned width = FreeImage_GetWidth(dib);
\r
301 unsigned height = FreeImage_GetHeight(dib);
\r
303 // Create a GDI+ bitmap to load into...
\r
304 pBitmap = new Bitmap(width, height, PixelFormat32bppARGB);
\r
306 if (pBitmap && pBitmap->GetLastStatus() == Ok)
\r
308 void* imageData = NULL;
\r
310 // Write & convert the loaded data into the GDI+ Bitmap
\r
311 Rect rect(0, 0, width, height);
\r
312 BitmapData bitmapData;
\r
313 if (pBitmap->LockBits(&rect, ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData) == Ok)
\r
315 FreeImage_ConvertToRawBits((BYTE*)bitmapData.Scan0, dib, bitmapData.Stride, 32, 0xff << RED_SHIFT, 0xff << GREEN_SHIFT, 0xff << BLUE_SHIFT, FALSE);
\r
317 pBitmap->UnlockBits(&bitmapData);
\r
323 else // Failed to lock the destination Bitmap
\r
329 else // Bitmap allocation failed
\r
338 FreeImage_Unload(dib);
\r
344 FreeLibrary(hFreeImageLib);
\r
345 hFreeImageLib = NULL;
\r
349 else // GDI+ Unavailable...
\r
352 HANDLE hFile = CreateFile(sFilePathName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
\r
353 if (hFile != INVALID_HANDLE_VALUE)
\r
355 BY_HANDLE_FILE_INFORMATION fileinfo;
\r
356 if (GetFileInformationByHandle(hFile, &fileinfo))
\r
358 BYTE * buffer = new BYTE[fileinfo.nFileSizeLow];
\r
360 if (ReadFile(hFile, buffer, fileinfo.nFileSizeLow, &readbytes, NULL))
\r
362 if (LoadPictureData(buffer, readbytes))
\r
364 m_nSize = fileinfo.nFileSizeLow;
\r
370 CloseHandle(hFile);
\r
375 m_Name = sFilePathName;
\r
376 m_Weight = nSize; // Update Picture Size Info...
\r
378 if(m_IPicture != NULL) // Do Not Try To Read From Memory That Does Not Exist...
\r
380 m_IPicture->get_Height(&m_Height);
\r
381 m_IPicture->get_Width(&m_Width);
\r
382 // Calculate Its Size On a "Standard" (96 DPI) Device Context
\r
383 m_Height = MulDiv(m_Height, 96, HIMETRIC_INCH);
\r
384 m_Width = MulDiv(m_Width, 96, HIMETRIC_INCH);
\r
386 else // Picture Data Is Not a Known Picture Type
\r
394 if ((bResult)&&(m_nSize == 0))
\r
396 HANDLE hFile = CreateFile(sFilePathName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
\r
397 if (hFile != INVALID_HANDLE_VALUE)
\r
399 BY_HANDLE_FILE_INFORMATION fileinfo;
\r
400 if (GetFileInformationByHandle(hFile, &fileinfo))
\r
402 m_nSize = fileinfo.nFileSizeLow;
\r
404 CloseHandle(hFile);
\r
408 m_ColorDepth = GetColorDepth();
\r
413 bool CPicture::LoadPictureData(BYTE *pBuffer, int nSize)
\r
415 bool bResult = false;
\r
417 HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nSize);
\r
419 if(hGlobal == NULL)
\r
424 void* pData = GlobalLock(hGlobal);
\r
427 memcpy(pData, pBuffer, nSize);
\r
428 GlobalUnlock(hGlobal);
\r
430 IStream* pStream = NULL;
\r
432 if ((CreateStreamOnHGlobal(hGlobal, true, &pStream) == S_OK)&&(pStream))
\r
435 if((hr = OleLoadPicture(pStream, nSize, false, IID_IPicture, (LPVOID *)&m_IPicture)) == S_OK)
\r
437 pStream->Release();
\r
447 FreeResource(hGlobal); // 16Bit Windows Needs This (32Bit - Automatic Release)
\r
452 bool CPicture::Show(HDC hDC, RECT DrawRect)
\r
456 if (bIsIcon && lpIcons)
\r
458 ::DrawIconEx(hDC, DrawRect.left, DrawRect.top, hIcons[nCurrentIcon], DrawRect.right-DrawRect.left, DrawRect.bottom-DrawRect.top, 0, NULL, DI_NORMAL);
\r
461 if ((m_IPicture == NULL)&&(pBitmap == NULL))
\r
468 m_IPicture->get_Width(&Width);
\r
469 m_IPicture->get_Height(&Height);
\r
471 HRESULT hrP = NULL;
\r
473 hrP = m_IPicture->Render(hDC,
\r
474 DrawRect.left, // Left
\r
475 DrawRect.top, // Top
\r
476 DrawRect.right - DrawRect.left, // Right
\r
477 DrawRect.bottom - DrawRect.top, // Bottom
\r
484 if (SUCCEEDED(hrP))
\r
489 Graphics graphics(hDC);
\r
490 graphics.SetInterpolationMode(m_ip);
\r
491 graphics.SetPixelOffsetMode(PixelOffsetModeHighQuality);
\r
492 ImageAttributes attr;
\r
493 attr.SetWrapMode(WrapModeTileFlipXY);
\r
494 Rect rect(DrawRect.left, DrawRect.top, DrawRect.right-DrawRect.left, DrawRect.bottom-DrawRect.top);
\r
495 graphics.DrawImage(pBitmap, rect, 0, 0, m_Width, m_Height, UnitPixel, &attr);
\r
502 bool CPicture::UpdateSizeOnDC(HDC hDC)
\r
504 if(hDC == NULL || m_IPicture == NULL) { m_Height = 0; m_Width = 0; return(false); };
\r
506 m_IPicture->get_Height(&m_Height);
\r
507 m_IPicture->get_Width(&m_Width);
\r
509 // Get Current DPI - Dot Per Inch
\r
510 int CurrentDPI_X = GetDeviceCaps(hDC, LOGPIXELSX);
\r
511 int CurrentDPI_Y = GetDeviceCaps(hDC, LOGPIXELSY);
\r
513 m_Height = MulDiv(m_Height, CurrentDPI_Y, HIMETRIC_INCH);
\r
514 m_Width = MulDiv(m_Width, CurrentDPI_X, HIMETRIC_INCH);
\r
519 UINT CPicture::GetColorDepth() const
\r
521 if (bIsIcon && lpIcons)
\r
523 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
524 return lpIconDir->idEntries[nCurrentIcon].wBitCount;
\r
526 switch (GetPixelFormat())
\r
528 case PixelFormat1bppIndexed:
\r
530 case PixelFormat4bppIndexed:
\r
532 case PixelFormat8bppIndexed:
\r
534 case PixelFormat16bppARGB1555:
\r
535 case PixelFormat16bppGrayScale:
\r
536 case PixelFormat16bppRGB555:
\r
537 case PixelFormat16bppRGB565:
\r
539 case PixelFormat24bppRGB:
\r
541 case PixelFormat32bppARGB:
\r
542 case PixelFormat32bppPARGB:
\r
543 case PixelFormat32bppRGB:
\r
545 case PixelFormat48bppRGB:
\r
547 case PixelFormat64bppARGB:
\r
548 case PixelFormat64bppPARGB:
\r
554 UINT CPicture::GetNumberOfFrames(int dimension)
\r
556 if (bIsIcon && lpIcons)
\r
560 if (pBitmap == NULL)
\r
563 count = pBitmap->GetFrameDimensionsCount();
\r
564 GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
\r
566 pBitmap->GetFrameDimensionsList(pDimensionIDs, count);
\r
568 UINT frameCount = pBitmap->GetFrameCount(&pDimensionIDs[dimension]);
\r
570 free(pDimensionIDs);
\r
574 UINT CPicture::GetNumberOfDimensions()
\r
576 if (bIsIcon && lpIcons)
\r
578 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
579 return lpIconDir->idCount;
\r
581 return pBitmap ? pBitmap->GetFrameDimensionsCount() : 0;
\r
584 long CPicture::SetActiveFrame(UINT frame)
\r
586 if (bIsIcon && lpIcons)
\r
588 nCurrentIcon = frame-1;
\r
589 m_Height = GetHeight();
\r
590 m_Width = GetWidth();
\r
593 if (pBitmap == NULL)
\r
596 count = pBitmap->GetFrameDimensionsCount();
\r
597 GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);
\r
599 pBitmap->GetFrameDimensionsList(pDimensionIDs, count);
\r
601 UINT frameCount = pBitmap->GetFrameCount(&pDimensionIDs[0]);
\r
603 free(pDimensionIDs);
\r
605 if (frame > frameCount)
\r
608 GUID pageGuid = FrameDimensionTime;
\r
609 pBitmap->SelectActiveFrame(&pageGuid, frame);
\r
611 // Assume that the image has a property item of type PropertyItemEquipMake.
\r
612 // Get the size of that property item.
\r
613 int nSize = pBitmap->GetPropertyItemSize(PropertyTagFrameDelay);
\r
615 // Allocate a buffer to receive the property item.
\r
616 PropertyItem* pPropertyItem = (PropertyItem*) malloc(nSize);
\r
618 pBitmap->GetPropertyItem(PropertyTagFrameDelay, nSize, pPropertyItem);
\r
620 UINT prevframe = frame;
\r
624 long delay = ((long*)pPropertyItem->value)[prevframe] * 10;
\r
625 free(pPropertyItem);
\r
626 m_Height = GetHeight();
\r
627 m_Width = GetWidth();
\r
631 UINT CPicture::GetHeight() const
\r
633 if ((bIsIcon)&&(lpIcons))
\r
635 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
636 return lpIconDir->idEntries[nCurrentIcon].bHeight;
\r
638 return pBitmap ? pBitmap->GetHeight() : 0;
\r
641 UINT CPicture::GetWidth() const
\r
643 if ((bIsIcon)&&(lpIcons))
\r
645 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
646 return lpIconDir->idEntries[nCurrentIcon].bWidth;
\r
648 return pBitmap ? pBitmap->GetWidth() : 0;
\r
651 PixelFormat CPicture::GetPixelFormat() const
\r
653 if ((bIsIcon)&&(lpIcons))
\r
655 LPICONDIR lpIconDir = (LPICONDIR)lpIcons;
\r
656 if (lpIconDir->idEntries[nCurrentIcon].wPlanes == 1)
\r
658 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 1)
\r
659 return PixelFormat1bppIndexed;
\r
660 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 4)
\r
661 return PixelFormat4bppIndexed;
\r
662 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 8)
\r
663 return PixelFormat8bppIndexed;
\r
665 if (lpIconDir->idEntries[nCurrentIcon].wBitCount == 32)
\r
667 return PixelFormat32bppARGB;
\r
670 return pBitmap ? pBitmap->GetPixelFormat() : 0;
\r