- 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:
spx 2011-03-08 19:39:04 +00:00
parent f19e76e6ec
commit 8ddc383ed1
3 changed files with 208 additions and 86 deletions

View File

@ -2122,23 +2122,26 @@ bool CMeterWindow::ResizeWindow(bool reset)
if (m_BackgroundMode == BGMODE_IMAGE) if (m_BackgroundMode == BGMODE_IMAGE)
{ {
PixelFormat format = tempBackground->GetPixelFormat(); w = m_BackgroundSize.cx;
if (format == PixelFormat32bppARGB) h = m_BackgroundSize.cy;
{
format = PixelFormat32bppPARGB;
}
m_Background = tempBackground->Clone(0, 0, m_BackgroundSize.cx, m_BackgroundSize.cy, format);
} }
else else
{ {
w = max(w, m_BackgroundSize.cx); w = max(w, m_BackgroundSize.cx);
h = max(h, m_BackgroundSize.cy); 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 // Scale the background to fill the whole window
Bitmap* background = new Bitmap(w, h, PixelFormat32bppPARGB);
Graphics graphics(background);
if (m_BackgroundMode == BGMODE_SCALED_IMAGE) if (m_BackgroundMode == BGMODE_SCALED_IMAGE)
{ {
const RECT m = m_BackgroundMargins; const RECT m = m_BackgroundMargins;
@ -2210,10 +2213,10 @@ bool CMeterWindow::ResizeWindow(bool reset)
Rect r(0, 0, w, h); Rect r(0, 0, w, h);
graphics.DrawImage(tempBackground, r, 0, 0, w, h, UnitPixel, &imgAttr); graphics.DrawImage(tempBackground, r, 0, 0, w, h, UnitPixel, &imgAttr);
} }
m_Background = background;
} }
m_Background = background;
// Get the size form the background bitmap // Get the size form the background bitmap
m_WindowW = m_Background->GetWidth(); m_WindowW = m_Background->GetWidth();
m_WindowH = m_Background->GetHeight(); m_WindowH = m_Background->GetHeight();

View File

@ -19,11 +19,101 @@
#include "StdAfx.h" #include "StdAfx.h"
#include "TintedImage.h" #include "TintedImage.h"
#include "ConfigParser.h" #include "ConfigParser.h"
#include "System.h"
#include "Error.h" #include "Error.h"
#include "Litestep.h" #include "Litestep.h"
using namespace Gdiplus; 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 PI (3.14159265f)
#define CONVERT_TO_RADIANS(X) ((X) * (PI / 180.0f)) #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_Bitmap(),
m_BitmapTint(), m_BitmapTint(),
m_hBuffer(),
m_Modified(),
m_NeedsCrop(false), m_NeedsCrop(false),
m_NeedsTinting(false), m_NeedsTinting(false),
m_NeedsTransform(false), m_NeedsTransform(false),
@ -98,20 +186,16 @@ CTintedImage::~CTintedImage()
*/ */
void CTintedImage::DisposeImage() void CTintedImage::DisposeImage()
{ {
delete m_Bitmap;
m_Bitmap = NULL;
delete m_BitmapTint; delete m_BitmapTint;
m_BitmapTint = NULL; m_BitmapTint = NULL;
if (m_hBuffer) m_Bitmap = NULL;
{
::GlobalFree(m_hBuffer);
m_hBuffer = NULL;
}
m_Modified.dwHighDateTime = 0; if (!m_CacheKey.empty())
m_Modified.dwLowDateTime = 0; {
ImageCachePool::RemoveCache(m_CacheKey);
m_CacheKey.clear();
}
} }
/* /*
@ -120,46 +204,60 @@ void CTintedImage::DisposeImage()
** Loads the image from file handle ** 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); HGLOBAL hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, fileSize);
if (hBuffer)
if (imageSize != INVALID_FILE_SIZE)
{ {
HGLOBAL hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize); void* pBuffer = ::GlobalLock(hBuffer);
if (hBuffer) if (pBuffer)
{ {
void* pBuffer = ::GlobalLock(hBuffer); DWORD readBytes;
if (pBuffer) ReadFile(fileHandle, pBuffer, fileSize, &readBytes, NULL);
::GlobalUnlock(hBuffer);
IStream* pStream = NULL;
if (::CreateStreamOnHGlobal(hBuffer, FALSE, &pStream) == S_OK)
{ {
DWORD readBytes; Bitmap* bitmap = Bitmap::FromStream(pStream);
ReadFile(fileHandle, pBuffer, imageSize, &readBytes, NULL); pStream->Release();
::GlobalUnlock(hBuffer);
IStream* pStream = NULL; if (Ok == bitmap->GetLastStatus())
if (::CreateStreamOnHGlobal(hBuffer, FALSE, &pStream) == S_OK)
{ {
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()) ::GlobalFree(hBuffer);
{ hBuffer = NULL;
*pBitmap = bitmap; }
*phBuffer = hBuffer;
return true;
}
else
{
delete bitmap;
} }
////////////////////////////////////////////
*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+ // 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); 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; FILETIME tmpTime;
GetFileTime(fileHandle, NULL, NULL, &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(); 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) // Check whether the new image needs tinting (or cropping, flipping, rotating)
if (!m_NeedsCrop) 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) // We need a copy of the image if has tinting (or flipping, rotating)
if (m_NeedsCrop || m_NeedsTinting || m_NeedsTransform) 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) 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) 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 else
{ {
@ -319,7 +425,7 @@ void CTintedImage::ApplyCrop()
} }
Rect r(0, 0, m_Crop.Width, m_Crop.Height); 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 graphics(m_BitmapTint);
graphics.DrawImage(m_Bitmap, r, x, y, r.Width, r.Height, UnitPixel); graphics.DrawImage(m_Bitmap, r, x, y, r.Width, r.Height, UnitPixel);
@ -335,28 +441,38 @@ void CTintedImage::ApplyCrop()
*/ */
void CTintedImage::ApplyTint() 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* original = GetImage();
Bitmap* tint;
ImageAttributes ImgAttr; if (m_GreyScale && !useColorMatrix)
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)
{ {
Bitmap* gray = TurnGreyscale(original); tint = TurnGreyscale(original);
graphics.DrawImage(gray, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
delete gray;
} }
else 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; 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 // We need a blank bitmap to paint our greyscale to in case of alpha
Rect r(0, 0, source->GetWidth(), source->GetHeight()); 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 graphics(bitmap);
graphics.DrawImage(source, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr); graphics.DrawImage(source, r, 0, 0, r.Width, r.Height, UnitPixel, &ImgAttr);
@ -429,7 +545,7 @@ void CTintedImage::ApplyTransform()
if (m_Flip != RotateNoneFlipNone) if (m_Flip != RotateNoneFlipNone)
{ {
original->RotateFlip(RotateNoneFlipNone); original->RotateFlip(m_Flip);
} }
delete m_BitmapTint; delete m_BitmapTint;
@ -440,7 +556,7 @@ void CTintedImage::ApplyTransform()
Bitmap* original = GetImage(); Bitmap* original = GetImage();
Rect r(0, 0, original->GetWidth(), original->GetHeight()); 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); Graphics graphics(transform);
@ -448,7 +564,7 @@ void CTintedImage::ApplyTransform()
graphics.DrawImage(original, r, 0, 0, r.Width, r.Height, UnitPixel); graphics.DrawImage(original, r, 0, 0, r.Width, r.Height, UnitPixel);
original->RotateFlip(RotateNoneFlipNone); original->RotateFlip(m_Flip);
delete m_BitmapTint; delete m_BitmapTint;
m_BitmapTint = transform; m_BitmapTint = transform;

View File

@ -46,6 +46,7 @@
}; };
class CConfigParser; class CConfigParser;
class ImageCache;
class CTintedImage class CTintedImage
{ {
@ -84,6 +85,9 @@ public:
void DisposeImage(); void DisposeImage();
void LoadImage(const std::wstring& imageName, bool bLoadAlways); void LoadImage(const std::wstring& imageName, bool bLoadAlways);
static Gdiplus::PixelFormat AdjustNonAlphaPixelFormat(Gdiplus::Bitmap* bitmap)
{ return (bitmap->GetPixelFormat() == PixelFormat24bppRGB) ? PixelFormat24bppRGB : PixelFormat32bppPARGB; }
protected: protected:
enum CROPMODE enum CROPMODE
{ {
@ -98,7 +102,7 @@ protected:
void ApplyTint(); void ApplyTint();
void ApplyTransform(); 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 Gdiplus::Bitmap* TurnGreyscale(Gdiplus::Bitmap* source);
static bool CompareColorMatrix(const Gdiplus::ColorMatrix* a, const Gdiplus::ColorMatrix* b); 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_Bitmap; // The bitmap
Gdiplus::Bitmap* m_BitmapTint; // The tinted bitmap Gdiplus::Bitmap* m_BitmapTint; // The tinted bitmap
HGLOBAL m_hBuffer;
FILETIME m_Modified;
const std::wstring m_ConfigName; const std::wstring m_ConfigName;
const WCHAR** m_ConfigArray; const WCHAR** m_ConfigArray;
const bool m_DisableTransform; const bool m_DisableTransform;
@ -124,6 +125,8 @@ protected:
Gdiplus::RotateFlipType m_Flip; Gdiplus::RotateFlipType m_Flip;
Gdiplus::REAL m_Rotate; Gdiplus::REAL m_Rotate;
std::wstring m_CacheKey;
static const Gdiplus::ColorMatrix c_GreyScaleMatrix; static const Gdiplus::ColorMatrix c_GreyScaleMatrix;
static const Gdiplus::ColorMatrix c_IdentifyMatrix; static const Gdiplus::ColorMatrix c_IdentifyMatrix;