From 48540940b6c28bb4378abfeb500ec45a625b37b6 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 15 May 2012 10:38:20 +0000 Subject: initial commit git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/smileyadd/imagecache.cpp | 777 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 777 insertions(+) create mode 100644 plugins/smileyadd/imagecache.cpp (limited to 'plugins/smileyadd/imagecache.cpp') diff --git a/plugins/smileyadd/imagecache.cpp b/plugins/smileyadd/imagecache.cpp new file mode 100644 index 0000000000..b771c4941b --- /dev/null +++ b/plugins/smileyadd/imagecache.cpp @@ -0,0 +1,777 @@ +/* +Miranda SmileyAdd Plugin +Copyright (C) 2008 - 2011 Boris Krasnovskiy + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "imagecache.h" +#include "options.h" + +typedef BOOL (WINAPI *tAlphaBlend)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION); +static tAlphaBlend pAlphaBlend; + +static FI_INTERFACE *fei; + +static HANDLE g_hMutexIm; +static OBJLIST g_imagecache(25, ImageType::CompareImg); + +static bkstring lastdllname; +static HMODULE lastmodule; +static time_t laststamp; +static UINT_PTR timerId; + +static void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD) +{ + WaitForSingleObject(g_hMutexIm, 3000); + const time_t ts = time(NULL) - 10; + if ( lastmodule && ts > laststamp) + { + FreeLibrary(lastmodule); + lastmodule = NULL; + lastdllname.clear(); + } + + for (int i=g_imagecache.getCount(); i--; ) + g_imagecache[i].ProcessTimerTick(ts); + + if (g_imagecache.getCount() == 0) + { + g_imagecache.destroy(); + if (timerId && (timerId+1) && lastmodule == NULL) + { + KillTimer(NULL, timerId); + timerId = 0; + } + } + + ReleaseMutex(g_hMutexIm); +} + + +static void CALLBACK sttMainThreadCallback( PVOID ) +{ + if (timerId == 0xffffffff) + timerId = SetTimer(NULL, 0, 10000, (TIMERPROC)timerProc); +} + + +static HMODULE LoadDll(const bkstring& file) +{ + WaitForSingleObject(g_hMutexIm, 3000); + + if (lastdllname != file) + { + FreeLibrary(lastmodule); + lastdllname = file; +#if (defined _UNICODE || defined UNICODE) + lastmodule = LoadLibraryEx(file.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE); +#else + lastmodule = LoadLibraryEx(file.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES); +#endif + } + laststamp = time(NULL); + + ReleaseMutex(g_hMutexIm); + return lastmodule; +} + + + +ImageBase::ImageBase(unsigned id) +{ + m_id = id; + m_lRefCount = 1; + m_timestamp = 0; +} + +long ImageBase::AddRef(void) +{ + WaitForSingleObject(g_hMutexIm, 3000); + long cnt = ++m_lRefCount; + ReleaseMutex(g_hMutexIm); + return cnt; +} + +long ImageBase::Release(void) +{ + WaitForSingleObject(g_hMutexIm, 3000); + + long cnt = m_lRefCount; + if (cnt) m_lRefCount = --cnt; + if (cnt == 0) m_timestamp = time(NULL); + + ReleaseMutex(g_hMutexIm); + return cnt; +} + +void ImageBase::ProcessTimerTick(time_t ts) +{ + WaitForSingleObject(g_hMutexIm, 3000); + if (m_lRefCount == 0 && m_timestamp < ts ) + { + if (!g_imagecache.remove(this)) + delete this; + } + ReleaseMutex(g_hMutexIm); +} + +int ImageBase::CompareImg(const ImageBase* p1, const ImageBase* p2) +{ + unsigned id1 = p1->m_id; + unsigned id2 = p2->m_id; + + if (id1 == id2) return 0; + else return id1 < id2 ? -1 : 1; +} + +void ImageBase::Draw(HDC hdc, RECT& rc, bool clip) +{ + HRGN hrgn = NULL; + if (clip) + { + hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); + SelectClipRgn(hdc, hrgn); + } + + SIZE iSize; + GetSize(iSize); + + const int sizeX = rc.right - rc.left; + const int sizeY = rc.bottom - rc.top; + + const int x = rc.left + (sizeX > iSize.cx || clip ? (sizeX - iSize.cx) / 2 : 0); + const int y = rc.top + (sizeY > iSize.cy || clip ? (sizeY - iSize.cy) / 2 : 0); + + const int scaleX = sizeX > iSize.cx || clip ? iSize.cx : sizeX; + const int scaleY = sizeY > iSize.cy || clip ? iSize.cy : sizeY; + + DrawInternal(hdc, x, y, scaleX, scaleY); + + if (clip) + { + SelectClipRgn(hdc, NULL); + DeleteObject(hrgn); + } +} + + +HBITMAP ImageBase::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY) +{ + RECT rc = { 0, 0, sizeX, sizeY }; + + if (sizeX == 0 || sizeY == 0) + { + SIZE iSize; + GetSize(iSize); + + if (sizeX == 0) rc.right = iSize.cx; + if (sizeY == 0) rc.bottom = iSize.cy; + } + + HBRUSH hBkgBrush = CreateSolidBrush(bkgClr); + HDC hdc = GetDC(NULL); + HBITMAP hBmp = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); + HDC hdcMem = CreateCompatibleDC(hdc); + SelectObject(hdcMem, hBmp); + + FillRect(hdcMem, &rc, hBkgBrush); + + Draw(hdcMem, rc, false); + + DeleteDC(hdcMem); + ReleaseDC(NULL, hdc); + DeleteObject(hBkgBrush); + + return hBmp; +} + +int ImageBase::SelectNextFrame(const int frame) +{ + int res = (frame + 1) % GetFrameCount(); + SelectFrame(res); + return res; +} + + + +IconType::IconType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type) + : ImageBase(id) +{ + m_SmileyIcon = NULL; + + switch (type) + { + case icoDll: + { + const HMODULE hModule = LoadDll(file); + if (hModule != NULL) + m_SmileyIcon = (HICON) LoadImage(hModule, MAKEINTRESOURCE(-index), IMAGE_ICON, 0, 0, 0); + } + break; + + case icoFile: + m_SmileyIcon = (HICON) LoadImage(NULL, file.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + break; + + default: + ExtractIconEx(file.c_str(), index, NULL, &m_SmileyIcon, 1); + break; + } +} + +IconType::~IconType() +{ + DestroyIcon(m_SmileyIcon); +} + +void IconType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + if (m_SmileyIcon != NULL) + DrawIconEx(hdc, x, y, m_SmileyIcon, sizeX, sizeY, 0, NULL, DI_NORMAL); +} + +HICON IconType::GetIcon(void) +{ + return (HICON)CopyImage(m_SmileyIcon, IMAGE_ICON, 0, 0, 0); +} + +void IconType::GetSize(SIZE& size) +{ + if (m_SmileyIcon != NULL) + { + ICONINFO ii; + BITMAP bm; + GetIconInfo(m_SmileyIcon, &ii); + GetObject(ii.hbmColor, sizeof(bm), &bm); + size.cx = bm.bmWidth; + size.cy = bm.bmHeight; + DeleteObject(ii.hbmMask); + DeleteObject(ii.hbmColor); + } +} + + + +ImageListItemType::ImageListItemType(const unsigned id, HIMAGELIST hImList, int index) + : ImageBase(id) +{ + m_index = index; + m_hImList = hImList; +} + +void ImageListItemType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + SIZE iSize; + GetSize(iSize); + + if (sizeX >= iSize.cx && sizeY >= iSize.cy) + ImageList_Draw(m_hImList, m_index, hdc, x, y, ILD_TRANSPARENT); + else + { + HICON hIcon = ImageList_GetIconFixed(m_hImList, m_index, ILD_TRANSPARENT); + DrawIconEx(hdc, x, y, hIcon, sizeX, sizeY, 0, NULL, DI_NORMAL); + DestroyIcon(hIcon); + } +} + +HICON ImageListItemType::GetIcon(void) +{ + return ImageList_GetIconFixed(m_hImList, m_index, ILD_TRANSPARENT); +} + +void ImageListItemType::GetSize(SIZE& size) +{ + ImageList_GetIconSize(m_hImList, (int*)&size.cx, (int*)&size.cy); +} + +ImageType::ImageType(const unsigned id, const bkstring& file, IStream* pStream) + : ImageBase(id) +{ + m_bmp = NULL; + m_pPropertyItem = NULL; + m_nCurrentFrame = 0; + m_nFrameCount = 0; + + if (!InitGdiPlus()) return; + + if (pStream) + m_bmp = new Gdiplus::Bitmap(pStream); + else + m_bmp = new Gdiplus::Bitmap(T2W_SM(file.c_str())); + + if (m_bmp->GetLastStatus() != Gdiplus::Ok) + { + delete m_bmp; + m_bmp = NULL; + return; + } + + GUID pageGuid = Gdiplus::FrameDimensionTime; + m_nFrameCount = m_bmp->GetFrameCount(&pageGuid); + + if (IsAnimated()) + { + int nSize = m_bmp->GetPropertyItemSize(PropertyTagFrameDelay); + m_pPropertyItem = (Gdiplus::PropertyItem*) new char[nSize]; + m_bmp->GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem); + } +} + +ImageType::ImageType(const unsigned id, const bkstring& file, const int index, const IcoTypeEnum type) + : ImageBase(id) +{ + m_bmp = NULL; + m_pPropertyItem = NULL; + m_nCurrentFrame = 0; + m_nFrameCount = 0; + + if (!InitGdiPlus()) return; + + switch (type) + { + case icoDll: + { + const HMODULE hModule = LoadDll(file); + if (hModule != NULL) + { + HICON hIcon = (HICON) LoadImage(hModule, MAKEINTRESOURCE(-index), IMAGE_ICON, 0, 0, 0); + m_bmp = new Gdiplus::Bitmap(hIcon); + DestroyIcon(hIcon); + } + } + break; + + case icoFile: + m_bmp = new Gdiplus::Bitmap(T2W_SM(file.c_str())); + break; + + default: + HICON hIcon = NULL; + ExtractIconEx(file.c_str(), index, NULL, &hIcon, 1); + m_bmp = new Gdiplus::Bitmap(hIcon); + DestroyIcon(hIcon); + break; + } + + if (m_bmp->GetLastStatus() != Gdiplus::Ok) + { + delete m_bmp; + m_bmp = NULL; + return; + } +} + + +ImageType::~ImageType(void) +{ + if (m_pPropertyItem) delete[] m_pPropertyItem; + if (m_bmp) delete m_bmp; +} + +void ImageType::SelectFrame(int frame) +{ + if ((unsigned)frame >= (unsigned)m_nFrameCount) frame = 0; + if (IsAnimated() && frame != m_nCurrentFrame) + { + m_nCurrentFrame = frame; + GUID pageGuid = Gdiplus::FrameDimensionTime; + m_bmp->SelectActiveFrame(&pageGuid, frame); + } +} + + +void ImageType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + if (m_bmp == NULL) return; + + WaitForSingleObject(g_hMutexIm, 3000); + { + Gdiplus::Graphics grp(hdc); +// if (opt.HQScaling) grp.SetInterpolationMode(Gdiplus::InterpolationModeBicubic); + grp.DrawImage(m_bmp, x, y, sizeX, sizeY); + } + ReleaseMutex(g_hMutexIm); +} + +int ImageType::GetFrameDelay(void) const +{ + return ((long*) m_pPropertyItem->value)[m_nCurrentFrame]; +} + +HICON ImageType::GetIcon(void) +{ + if (m_bmp == NULL) return NULL; + + HICON hIcon = NULL; + WaitForSingleObject(g_hMutexIm, 3000); + + m_bmp->GetHICON(&hIcon); + + ReleaseMutex(g_hMutexIm); + return hIcon; +} + + +void ImageType::GetSize(SIZE& size) +{ + if (m_bmp) + { + size.cx = m_bmp->GetWidth(); + size.cy = m_bmp->GetHeight(); + } + else + { + size.cx = 0; + size.cy = 0; + } +} + + +ImageFType::ImageFType(const unsigned id) + : ImageBase(id) +{ + m_bmp = NULL; +} + +ImageFType::ImageFType(const unsigned id, const bkstring& file) + : ImageBase(id) +{ + m_bmp = NULL; + + FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(file.c_str(), 0); + if (fif == FIF_UNKNOWN) + fif = fei->FI_GetFIFFromFilenameT(file.c_str()); + if (fif == FIF_UNKNOWN) return; + + FIBITMAP *dib = fei->FI_LoadT(fif, file.c_str(), 0); + if (dib == NULL) return; + + bool transp = fei->FI_IsTransparent(dib) != 0; + FREE_IMAGE_TYPE imt = fei->FI_GetImageType(dib); + unsigned bpp = fei->FI_GetBPP(dib); + + if (transp && bpp != 32 || imt == FIT_RGBA16) + { + FIBITMAP *tdib = fei->FI_ConvertTo32Bits(dib); + fei->FI_Unload(dib); + dib = tdib; + } + else if (!transp && bpp > 24) + { + FIBITMAP *tdib = fei->FI_ConvertTo24Bits(dib); + fei->FI_Unload(dib); + dib = tdib; + } + + m_bmp = fei->FI_CreateHBITMAPFromDIB(dib); + fei->FI_Unload(dib); + + if (transp) + fei->FI_Premultiply(m_bmp); +} + +ImageFType::~ImageFType() +{ + DeleteObject(m_bmp); +} + +void ImageFType::DrawInternal(HDC hdc, int x, int y, int sizeX, int sizeY) +{ + if (m_bmp == NULL) return; + + HDC hdcImg = CreateCompatibleDC(hdc); + HBITMAP oldBmp = (HBITMAP) SelectObject(hdcImg, m_bmp); + + BITMAP bm; + GetObject(m_bmp, sizeof(bm), &bm); + + if (bm.bmBitsPixel == 32 && pAlphaBlend) + { + BLENDFUNCTION bf = {0}; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + pAlphaBlend(hdc, x, y, sizeX, sizeY, hdcImg, 0, 0, bm.bmWidth, bm.bmHeight, bf); + } + else + { + BitBlt(hdc, x, y, sizeX, sizeY, hdcImg, 0, 0, SRCCOPY); + } + + SelectObject(hdcImg, oldBmp); + DeleteDC(hdcImg); +} + +HICON ImageFType::GetIcon(void) +{ + if (m_bmp == NULL) return NULL; + + HICON hIcon; + + BITMAP bm; + GetObject(m_bmp, sizeof(bm), &bm); + + ICONINFO ii; + ii.fIcon = TRUE; + ii.xHotspot = 0; + ii.yHotspot = 0; + + if (bm.bmBitsPixel == 32 && GetWinVer() < 0x0501) + { + int slen = bm.bmWidth * 4; + int len = bm.bmHeight * slen; + + BYTE* p = (BYTE*)mir_alloc(len); + BYTE* maskBits = (BYTE*)mir_calloc(len); + BYTE* colorBits = (BYTE*)mir_calloc(len); + + GetBitmapBits(m_bmp, len, p); + + for (int y = 0; y < bm.bmHeight; ++y) + { + int shift = y * slen; + BYTE *px = p + shift; + BYTE *color = colorBits + shift; + BYTE *mask = maskBits + shift; + + for (int x = 0; x < bm.bmWidth; ++x) + { + for(int i = 0; i < 4; i++) + { + mask[i] = px[3]; + color[i] = px[i]; + } + + px += 4; + mask += 4; + color += 4; + } + } + + ii.hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 32, maskBits); + ii.hbmColor = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 32, colorBits); + + hIcon = CreateIconIndirect(&ii); + + DeleteObject(ii.hbmMask); + DeleteObject(ii.hbmColor); + + mir_free(p); + mir_free(maskBits); + mir_free(colorBits); + } + else + { + ii.hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL); + ii.hbmColor = m_bmp; + hIcon = CreateIconIndirect(&ii); + DeleteObject(ii.hbmMask); + } + return hIcon; +} + +void ImageFType::GetSize(SIZE& size) +{ + if (m_bmp) + { + BITMAP bm; + GetObject(m_bmp, sizeof(bm), &bm); + size.cx = bm.bmWidth; + size.cy = bm.bmHeight; + } + else + { + size.cx = 0; + size.cy = 0; + } +} +/* +ImageFAniType::ImageFAniType(const unsigned id, const bkstring& file) +: ImageFType(id) +{ + m_fmbmp = NULL; + m_nCurrentFrame = -1; + m_FrameDelay = NULL; + + FREE_IMAGE_FORMAT fif = fei->FI_GetFileTypeT(file.c_str(), 0); + if (fif == FIF_UNKNOWN) + fif = fei->FI_GetFIFFromFilenameT(file.c_str()); + + m_fmbmp = fei->FI_OpenMultiBitmap(fif, T2A_SM(file.c_str()), FALSE, TRUE, TRUE, GIF_PLAYBACK); + if (m_fmbmp == NULL) return; + + m_nFrameCount = fei->FI_GetPageCount(m_fmbmp); + m_bmpl = (HBITMAP*)mir_calloc(m_nFrameCount*sizeof(HBITMAP)); + m_FrameDelay = (int*)mir_calloc(m_nFrameCount*sizeof(int)); + SelectFrame(0); +} + +ImageFAniType::~ImageFAniType() +{ + if (m_fmbmp) fei->FI_CloseMultiBitmap(m_fmbmp, 0); + for (int i=0; i= (unsigned)m_nFrameCount) frame = 0; + if (frame == m_nCurrentFrame) return; + m_nCurrentFrame = frame; + + if (m_bmpl[frame]) + { + m_bmp = m_bmpl[frame]; + return; + } + + FITAG *tag = NULL; + + FIBITMAP *dib = fei->FI_LockPage(m_fmbmp, frame); + if (dib == NULL) + return; + + if (fei->FI_GetMetadata(FIMD_ANIMATION, dib, "FrameTime", &tag)) + m_FrameDelay[frame] = *(LONG *)fei->FI_GetTagValue(tag) / 10; + + m_bmpl[frame] = m_bmp = fei->FI_CreateHBITMAPFromDIB(dib); + + if (fei->FI_IsTransparent(dib)) + fei->FI_Premultiply(m_bmp); + + fei->FI_UnlockPage(m_fmbmp, dib, FALSE); +} +*/ + +#pragma optimize("gt", on) + +// MurmurHash2, by Austin Appleby +unsigned int __fastcall hash( const void * key, unsigned int len ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + const unsigned int m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + unsigned int h = len; + + // Mix 4 bytes at a time into the hash + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + unsigned int k = *(unsigned int *)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} +#pragma optimize("", on) + + +void InitImageCache(void) +{ + g_hMutexIm = CreateMutex(NULL, FALSE, NULL); + CallService(MS_IMG_GETINTERFACE, FI_IF_VERSION, (LPARAM) &fei); + + pAlphaBlend = (tAlphaBlend) GetProcAddress(GetModuleHandleA("gdi32"), "GdiAlphaBlend"); + if (pAlphaBlend == NULL) + pAlphaBlend = (tAlphaBlend) GetProcAddress(LoadLibraryA("msimg32"), "AlphaBlend"); +} + +void DestroyImageCache(void) +{ + WaitForSingleObject(g_hMutexIm, 3000); + + if (timerId) KillTimer(NULL, timerId); + if (lastmodule) FreeLibrary(lastmodule); + + g_imagecache.destroy(); + + ReleaseMutex(g_hMutexIm); + CloseHandle(g_hMutexIm); +} + + +ImageBase* AddCacheImage(const bkstring& file, int index) +{ + bkstring tmpfile(file); tmpfile.appendfmt(_T("#%d"), index); + unsigned id = hash(tmpfile.c_str(), (unsigned int)tmpfile.size() * sizeof(TCHAR)); + + WaitForSingleObject(g_hMutexIm, 3000); + + ImageBase srch(id); + ImageBase *img = g_imagecache.find(&srch); + if (img == NULL) + { + bkstring::size_type ind = file.find_last_of('.'); + if (ind == file.npos) return NULL; + bkstring ext = file.substr(ind+1); + + if (ext.comparei(_T("dll")) == 0 || ext.comparei(_T("exe")) == 0) + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, index, icoDll) : (ImageBase*)new IconType(id, file, index, icoDll); + else if (ext.comparei(_T("ico")) == 0) + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, 0, icoFile) : (ImageBase*)new IconType(id, file, 0, icoFile); + else if (ext.comparei(_T("icl")) == 0) + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, index, icoIcl) : (ImageBase*)new IconType(id, file, index, icoIcl); + else if (ext.comparei(_T("gif")) == 0) + img = new ImageType(id, file, NULL); + else if (fei == NULL || ext.comparei(_T("tif")) == 0 || ext.comparei(_T("tiff")) == 0) + img = new ImageType(id, file, NULL); + else + img = opt.HQScaling ? (ImageBase*)new ImageType(id, file, NULL) : (ImageBase*)new ImageFType(id, file); + + g_imagecache.insert(img); + + if (timerId == 0) + { + timerId = 0xffffffff; + CallFunctionAsync(sttMainThreadCallback, NULL); + } + } + else + img->AddRef(); + + ReleaseMutex(g_hMutexIm); + + return img; +} -- cgit v1.2.3