mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
- Added image caching system internally.
It would reduce memory usage in case that you use the same image file repeatedly on some meters. (Eg. Crop a part from same image by using ImageCrop.) - Fixed an issue that Background image isn't drawn correctly when BackgroundMode=0 and tint option is set.
This commit is contained in:
parent
f19e76e6ec
commit
8ddc383ed1
@ -2122,23 +2122,26 @@ bool CMeterWindow::ResizeWindow(bool reset)
|
||||
|
||||
if (m_BackgroundMode == BGMODE_IMAGE)
|
||||
{
|
||||
PixelFormat format = tempBackground->GetPixelFormat();
|
||||
if (format == PixelFormat32bppARGB)
|
||||
{
|
||||
format = PixelFormat32bppPARGB;
|
||||
}
|
||||
m_Background = tempBackground->Clone(0, 0, m_BackgroundSize.cx, m_BackgroundSize.cy, format);
|
||||
w = m_BackgroundSize.cx;
|
||||
h = m_BackgroundSize.cy;
|
||||
}
|
||||
else
|
||||
{
|
||||
w = max(w, m_BackgroundSize.cx);
|
||||
h = max(h, m_BackgroundSize.cy);
|
||||
}
|
||||
|
||||
Bitmap* background = new Bitmap(w, h, CTintedImage::AdjustNonAlphaPixelFormat(tempBackground));
|
||||
Graphics graphics(background);
|
||||
|
||||
if (m_BackgroundMode == BGMODE_IMAGE)
|
||||
{
|
||||
Rect r(0, 0, w, h);
|
||||
graphics.DrawImage(tempBackground, r, 0, 0, w, h, UnitPixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scale the background to fill the whole window
|
||||
Bitmap* background = new Bitmap(w, h, PixelFormat32bppPARGB);
|
||||
|
||||
Graphics graphics(background);
|
||||
|
||||
if (m_BackgroundMode == BGMODE_SCALED_IMAGE)
|
||||
{
|
||||
const RECT m = m_BackgroundMargins;
|
||||
@ -2210,10 +2213,10 @@ bool CMeterWindow::ResizeWindow(bool reset)
|
||||
Rect r(0, 0, w, h);
|
||||
graphics.DrawImage(tempBackground, r, 0, 0, w, h, UnitPixel, &imgAttr);
|
||||
}
|
||||
|
||||
m_Background = background;
|
||||
}
|
||||
|
||||
m_Background = background;
|
||||
|
||||
// Get the size form the background bitmap
|
||||
m_WindowW = m_Background->GetWidth();
|
||||
m_WindowH = m_Background->GetHeight();
|
||||
|
@ -19,11 +19,101 @@
|
||||
#include "StdAfx.h"
|
||||
#include "TintedImage.h"
|
||||
#include "ConfigParser.h"
|
||||
#include "System.h"
|
||||
#include "Error.h"
|
||||
#include "Litestep.h"
|
||||
|
||||
using namespace Gdiplus;
|
||||
|
||||
class ImageCache
|
||||
{
|
||||
public:
|
||||
ImageCache(Bitmap* bitmap, HGLOBAL hBuffer) : m_Bitmap(bitmap), m_hBuffer(hBuffer), m_Ref(1) {}
|
||||
~ImageCache() { Dispose(); }
|
||||
|
||||
void AddRef() { ++m_Ref; }
|
||||
void Release() { if (m_Ref > 0) { --m_Ref; } if (m_Ref == 0) { Dispose(); } }
|
||||
|
||||
bool IsInvalid() { return m_Ref == 0; }
|
||||
//int GetRef() { return m_Ref; }
|
||||
Bitmap* GetCache() { return m_Bitmap; }
|
||||
|
||||
private:
|
||||
ImageCache() {}
|
||||
ImageCache(const ImageCache& cache) {}
|
||||
|
||||
void Dispose() { delete m_Bitmap; m_Bitmap = NULL; if (m_hBuffer) { ::GlobalFree(m_hBuffer); m_hBuffer = NULL; } }
|
||||
|
||||
Bitmap* m_Bitmap;
|
||||
HGLOBAL m_hBuffer;
|
||||
int m_Ref;
|
||||
};
|
||||
|
||||
class ImageCachePool
|
||||
{
|
||||
public:
|
||||
static std::wstring CreateKey(const std::wstring& fname, FILETIME ftime, DWORD fileSize)
|
||||
{
|
||||
WCHAR buffer[MAX_PATH];
|
||||
|
||||
std::wstring key = (PathCanonicalize(buffer, fname.c_str())) ? buffer : fname;
|
||||
|
||||
_snwprintf_s(buffer, _TRUNCATE, L":%x%08x:%x", ftime.dwHighDateTime, ftime.dwLowDateTime, fileSize);
|
||||
key += buffer;
|
||||
|
||||
std::transform(key.begin(), key.end(), key.begin(), ::towlower);
|
||||
return key;
|
||||
}
|
||||
|
||||
static Bitmap* GetCache(const std::wstring& key)
|
||||
{
|
||||
std::unordered_map<std::wstring, ImageCache*>::const_iterator iter = c_CacheMap.find(key);
|
||||
if (iter != c_CacheMap.end())
|
||||
{
|
||||
return (*iter).second->GetCache();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void AddCache(const std::wstring& key, ImageCache* cache)
|
||||
{
|
||||
std::unordered_map<std::wstring, ImageCache*>::const_iterator iter = c_CacheMap.find(key);
|
||||
if (iter != c_CacheMap.end())
|
||||
{
|
||||
(*iter).second->AddRef();
|
||||
//LogWithArgs(LOG_DEBUG, L"* ADD: key=%s, ref=%i", key.c_str(), (*iter).second->GetRef());
|
||||
}
|
||||
else if (cache)
|
||||
{
|
||||
c_CacheMap[key] = cache;
|
||||
//LogWithArgs(LOG_DEBUG, L"* ADD: key=%s, ref=%i", key.c_str(), cache->GetRef());
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveCache(const std::wstring& key)
|
||||
{
|
||||
std::unordered_map<std::wstring, ImageCache*>::const_iterator iter = c_CacheMap.find(key);
|
||||
if (iter != c_CacheMap.end())
|
||||
{
|
||||
ImageCache* cache = (*iter).second;
|
||||
cache->Release();
|
||||
//LogWithArgs(LOG_DEBUG, L"* REMOVE: key=%s, ref=%i", key.c_str(), cache->GetRef());
|
||||
|
||||
if (cache->IsInvalid())
|
||||
{
|
||||
//LogWithArgs(LOG_DEBUG, L"* EMPTY-ERASE: key=%s", key.c_str());
|
||||
c_CacheMap.erase(iter);
|
||||
delete cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::wstring, ImageCache*> c_CacheMap;
|
||||
};
|
||||
std::unordered_map<std::wstring, ImageCache*> ImageCachePool::c_CacheMap;
|
||||
|
||||
|
||||
#define PI (3.14159265f)
|
||||
#define CONVERT_TO_RADIANS(X) ((X) * (PI / 180.0f))
|
||||
|
||||
@ -62,8 +152,6 @@ CTintedImage::CTintedImage(const WCHAR* name, const WCHAR** configArray, bool di
|
||||
|
||||
m_Bitmap(),
|
||||
m_BitmapTint(),
|
||||
m_hBuffer(),
|
||||
m_Modified(),
|
||||
m_NeedsCrop(false),
|
||||
m_NeedsTinting(false),
|
||||
m_NeedsTransform(false),
|
||||
@ -98,20 +186,16 @@ CTintedImage::~CTintedImage()
|
||||
*/
|
||||
void CTintedImage::DisposeImage()
|
||||
{
|
||||
delete m_Bitmap;
|
||||
m_Bitmap = NULL;
|
||||
|
||||
delete m_BitmapTint;
|
||||
m_BitmapTint = NULL;
|
||||
|
||||
if (m_hBuffer)
|
||||
{
|
||||
::GlobalFree(m_hBuffer);
|
||||
m_hBuffer = NULL;
|
||||
}
|
||||
m_Bitmap = NULL;
|
||||
|
||||
m_Modified.dwHighDateTime = 0;
|
||||
m_Modified.dwLowDateTime = 0;
|
||||
if (!m_CacheKey.empty())
|
||||
{
|
||||
ImageCachePool::RemoveCache(m_CacheKey);
|
||||
m_CacheKey.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -120,46 +204,60 @@ void CTintedImage::DisposeImage()
|
||||
** Loads the image from file handle
|
||||
**
|
||||
*/
|
||||
bool CTintedImage::LoadImageFromFileHandle(HANDLE fileHandle, Bitmap** pBitmap, HGLOBAL* phBuffer)
|
||||
Bitmap* CTintedImage::LoadImageFromFileHandle(HANDLE fileHandle, DWORD fileSize, ImageCache** ppCache)
|
||||
{
|
||||
DWORD imageSize = GetFileSize(fileHandle, NULL);
|
||||
|
||||
if (imageSize != INVALID_FILE_SIZE)
|
||||
HGLOBAL hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, fileSize);
|
||||
if (hBuffer)
|
||||
{
|
||||
HGLOBAL hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
|
||||
if (hBuffer)
|
||||
void* pBuffer = ::GlobalLock(hBuffer);
|
||||
if (pBuffer)
|
||||
{
|
||||
void* pBuffer = ::GlobalLock(hBuffer);
|
||||
if (pBuffer)
|
||||
DWORD readBytes;
|
||||
ReadFile(fileHandle, pBuffer, fileSize, &readBytes, NULL);
|
||||
::GlobalUnlock(hBuffer);
|
||||
|
||||
IStream* pStream = NULL;
|
||||
if (::CreateStreamOnHGlobal(hBuffer, FALSE, &pStream) == S_OK)
|
||||
{
|
||||
DWORD readBytes;
|
||||
ReadFile(fileHandle, pBuffer, imageSize, &readBytes, NULL);
|
||||
::GlobalUnlock(hBuffer);
|
||||
Bitmap* bitmap = Bitmap::FromStream(pStream);
|
||||
pStream->Release();
|
||||
|
||||
IStream* pStream = NULL;
|
||||
if (::CreateStreamOnHGlobal(hBuffer, FALSE, &pStream) == S_OK)
|
||||
if (Ok == bitmap->GetLastStatus())
|
||||
{
|
||||
Bitmap* bitmap = Bitmap::FromStream(pStream);
|
||||
pStream->Release();
|
||||
////////////////////////////////////////////
|
||||
// Workaround to avoid image corruption with JPEG in some cases
|
||||
if (CSystem::GetOSPlatform() < OSPLATFORM_7)
|
||||
{
|
||||
GUID guid;
|
||||
bitmap->GetRawFormat(&guid);
|
||||
if (guid == ImageFormatJPEG)
|
||||
{
|
||||
Rect r(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
|
||||
Bitmap* clone = new Bitmap(r.Width, r.Height, PixelFormat24bppRGB);
|
||||
{
|
||||
Graphics graphics(clone);
|
||||
graphics.DrawImage(bitmap, r, 0, 0, r.Width, r.Height, UnitPixel);
|
||||
}
|
||||
delete bitmap;
|
||||
bitmap = clone;
|
||||
|
||||
if (bitmap && Ok == bitmap->GetLastStatus())
|
||||
{
|
||||
*pBitmap = bitmap;
|
||||
*phBuffer = hBuffer;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete bitmap;
|
||||
::GlobalFree(hBuffer);
|
||||
hBuffer = NULL;
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////////
|
||||
*ppCache = new ImageCache(bitmap, hBuffer);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
::GlobalFree(hBuffer);
|
||||
delete bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
::GlobalFree(hBuffer);
|
||||
}
|
||||
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -184,19 +282,30 @@ void CTintedImage::LoadImage(const std::wstring& imageName, bool bLoadAlways)
|
||||
}
|
||||
|
||||
// Read the bitmap to memory so that it's not locked by GDI+
|
||||
DWORD fileSize;
|
||||
HANDLE fileHandle = CreateFile(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
||||
if (fileHandle != INVALID_HANDLE_VALUE)
|
||||
if (fileHandle != INVALID_HANDLE_VALUE && (fileSize = GetFileSize(fileHandle, NULL)) != INVALID_FILE_SIZE)
|
||||
{
|
||||
// Compare the timestamp and filename to check if the file has been changed (don't load if it's not)
|
||||
// Compare the filename/timestamp/filesize to check if the file has been changed (don't load if it's not)
|
||||
FILETIME tmpTime;
|
||||
GetFileTime(fileHandle, NULL, NULL, &tmpTime);
|
||||
if (bLoadAlways || CompareFileTime(&tmpTime, &m_Modified) != 0)
|
||||
std::wstring key = ImageCachePool::CreateKey(filename, tmpTime, fileSize);
|
||||
|
||||
if (bLoadAlways || key != m_CacheKey)
|
||||
{
|
||||
DisposeImage();
|
||||
|
||||
if (LoadImageFromFileHandle(fileHandle, &m_Bitmap, &m_hBuffer))
|
||||
Bitmap* bitmap = ImageCachePool::GetCache(key);
|
||||
ImageCache* cache = NULL;
|
||||
|
||||
m_Bitmap = (bitmap) ?
|
||||
bitmap :
|
||||
LoadImageFromFileHandle(fileHandle, fileSize, &cache);
|
||||
|
||||
if (m_Bitmap)
|
||||
{
|
||||
m_Modified = tmpTime;
|
||||
m_CacheKey = key;
|
||||
ImageCachePool::AddCache(key, cache);
|
||||
|
||||
// Check whether the new image needs tinting (or cropping, flipping, rotating)
|
||||
if (!m_NeedsCrop)
|
||||
@ -233,11 +342,8 @@ void CTintedImage::LoadImage(const std::wstring& imageName, bool bLoadAlways)
|
||||
// We need a copy of the image if has tinting (or flipping, rotating)
|
||||
if (m_NeedsCrop || m_NeedsTinting || m_NeedsTransform)
|
||||
{
|
||||
if (m_BitmapTint)
|
||||
{
|
||||
delete m_BitmapTint;
|
||||
m_BitmapTint = NULL;
|
||||
}
|
||||
delete m_BitmapTint;
|
||||
m_BitmapTint = NULL;
|
||||
|
||||
if (m_Bitmap->GetWidth() > 0 && m_Bitmap->GetHeight() > 0)
|
||||
{
|
||||
@ -280,7 +386,7 @@ void CTintedImage::ApplyCrop()
|
||||
{
|
||||
if (m_Crop.Width == 0 || m_Crop.Height == 0)
|
||||
{
|
||||
m_BitmapTint = new Bitmap(0, 0, PixelFormat32bppPARGB); // create dummy bitmap
|
||||
m_BitmapTint = new Bitmap(0, 0, PixelFormat24bppRGB); // create dummy bitmap
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -319,7 +425,7 @@ void CTintedImage::ApplyCrop()
|
||||
}
|
||||
|
||||
Rect r(0, 0, m_Crop.Width, m_Crop.Height);
|
||||
m_BitmapTint = new Bitmap(r.Width, r.Height, PixelFormat32bppPARGB);
|
||||
m_BitmapTint = new Bitmap(r.Width, r.Height, AdjustNonAlphaPixelFormat(m_Bitmap));
|
||||
|
||||
Graphics graphics(m_BitmapTint);
|
||||
graphics.DrawImage(m_Bitmap, r, x, y, r.Width, r.Height, UnitPixel);
|
||||
@ -335,28 +441,38 @@ void CTintedImage::ApplyCrop()
|
||||
*/
|
||||
void CTintedImage::ApplyTint()
|
||||
{
|
||||
if (m_GreyScale || !CompareColorMatrix(m_ColorMatrix, &c_IdentifyMatrix))
|
||||
bool useColorMatrix = !CompareColorMatrix(m_ColorMatrix, &c_IdentifyMatrix);
|
||||
|
||||
if (m_GreyScale || useColorMatrix)
|
||||
{
|
||||
Bitmap* original = GetImage();
|
||||
Bitmap* tint;
|
||||
|
||||
ImageAttributes ImgAttr;
|
||||
ImgAttr.SetColorMatrix(m_ColorMatrix, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap);
|
||||
|
||||
Rect r(0, 0, original->GetWidth(), original->GetHeight());
|
||||
|
||||
Bitmap* tint = new Bitmap(r.Width, r.Height, PixelFormat32bppPARGB);
|
||||
|
||||
Graphics graphics(tint);
|
||||
|
||||
if (m_GreyScale)
|
||||
if (m_GreyScale && !useColorMatrix)
|
||||
{
|
||||
Bitmap* gray = TurnGreyscale(original);
|
||||
graphics.DrawImage(gray, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
|
||||
delete gray;
|
||||
tint = TurnGreyscale(original);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.DrawImage(original, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
|
||||
ImageAttributes ImgAttr;
|
||||
ImgAttr.SetColorMatrix(m_ColorMatrix, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap);
|
||||
|
||||
Rect r(0, 0, original->GetWidth(), original->GetHeight());
|
||||
|
||||
tint = new Bitmap(r.Width, r.Height, PixelFormat32bppPARGB);
|
||||
|
||||
Graphics graphics(tint);
|
||||
|
||||
if (m_GreyScale)
|
||||
{
|
||||
Bitmap* gray = TurnGreyscale(original);
|
||||
graphics.DrawImage(gray, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
|
||||
delete gray;
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.DrawImage(original, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
|
||||
}
|
||||
}
|
||||
|
||||
delete m_BitmapTint;
|
||||
@ -378,7 +494,7 @@ Bitmap* CTintedImage::TurnGreyscale(Bitmap* source)
|
||||
|
||||
// We need a blank bitmap to paint our greyscale to in case of alpha
|
||||
Rect r(0, 0, source->GetWidth(), source->GetHeight());
|
||||
Bitmap* bitmap = new Bitmap(r.Width, r.Height, PixelFormat32bppPARGB);
|
||||
Bitmap* bitmap = new Bitmap(r.Width, r.Height, AdjustNonAlphaPixelFormat(source));
|
||||
|
||||
Graphics graphics(bitmap);
|
||||
graphics.DrawImage(source, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
|
||||
@ -429,7 +545,7 @@ void CTintedImage::ApplyTransform()
|
||||
|
||||
if (m_Flip != RotateNoneFlipNone)
|
||||
{
|
||||
original->RotateFlip(RotateNoneFlipNone);
|
||||
original->RotateFlip(m_Flip);
|
||||
}
|
||||
|
||||
delete m_BitmapTint;
|
||||
@ -440,7 +556,7 @@ void CTintedImage::ApplyTransform()
|
||||
Bitmap* original = GetImage();
|
||||
|
||||
Rect r(0, 0, original->GetWidth(), original->GetHeight());
|
||||
Bitmap* transform = new Bitmap(r.Width, r.Height, PixelFormat32bppPARGB);
|
||||
Bitmap* transform = new Bitmap(r.Width, r.Height, AdjustNonAlphaPixelFormat(original));
|
||||
|
||||
Graphics graphics(transform);
|
||||
|
||||
@ -448,7 +564,7 @@ void CTintedImage::ApplyTransform()
|
||||
|
||||
graphics.DrawImage(original, r, 0, 0, r.Width, r.Height, UnitPixel);
|
||||
|
||||
original->RotateFlip(RotateNoneFlipNone);
|
||||
original->RotateFlip(m_Flip);
|
||||
|
||||
delete m_BitmapTint;
|
||||
m_BitmapTint = transform;
|
||||
|
@ -46,6 +46,7 @@
|
||||
};
|
||||
|
||||
class CConfigParser;
|
||||
class ImageCache;
|
||||
|
||||
class CTintedImage
|
||||
{
|
||||
@ -84,6 +85,9 @@ public:
|
||||
void DisposeImage();
|
||||
void LoadImage(const std::wstring& imageName, bool bLoadAlways);
|
||||
|
||||
static Gdiplus::PixelFormat AdjustNonAlphaPixelFormat(Gdiplus::Bitmap* bitmap)
|
||||
{ return (bitmap->GetPixelFormat() == PixelFormat24bppRGB) ? PixelFormat24bppRGB : PixelFormat32bppPARGB; }
|
||||
|
||||
protected:
|
||||
enum CROPMODE
|
||||
{
|
||||
@ -98,7 +102,7 @@ protected:
|
||||
void ApplyTint();
|
||||
void ApplyTransform();
|
||||
|
||||
static bool LoadImageFromFileHandle(HANDLE fileHandle, Gdiplus::Bitmap** pBitmap, HGLOBAL* phBuffer);
|
||||
Gdiplus::Bitmap* LoadImageFromFileHandle(HANDLE fileHandle, DWORD fileSize, ImageCache** ppCache);
|
||||
|
||||
static Gdiplus::Bitmap* TurnGreyscale(Gdiplus::Bitmap* source);
|
||||
static bool CompareColorMatrix(const Gdiplus::ColorMatrix* a, const Gdiplus::ColorMatrix* b);
|
||||
@ -106,9 +110,6 @@ protected:
|
||||
Gdiplus::Bitmap* m_Bitmap; // The bitmap
|
||||
Gdiplus::Bitmap* m_BitmapTint; // The tinted bitmap
|
||||
|
||||
HGLOBAL m_hBuffer;
|
||||
FILETIME m_Modified;
|
||||
|
||||
const std::wstring m_ConfigName;
|
||||
const WCHAR** m_ConfigArray;
|
||||
const bool m_DisableTransform;
|
||||
@ -124,6 +125,8 @@ protected:
|
||||
Gdiplus::RotateFlipType m_Flip;
|
||||
Gdiplus::REAL m_Rotate;
|
||||
|
||||
std::wstring m_CacheKey;
|
||||
|
||||
static const Gdiplus::ColorMatrix c_GreyScaleMatrix;
|
||||
static const Gdiplus::ColorMatrix c_IdentifyMatrix;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user