Skip to content

Commit

Permalink
* AvatarCache refactor.
Browse files Browse the repository at this point in the history
  • Loading branch information
iProgramMC committed Sep 21, 2024
1 parent 32fd5b2 commit 3a009f6
Show file tree
Hide file tree
Showing 15 changed files with 425 additions and 232 deletions.
88 changes: 45 additions & 43 deletions src/windows/AvatarCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ std::string AvatarCache::AddImagePlace(const std::string& resource, eImagePlace
return id;
}

void AvatarCache::SetBitmap(const std::string& resource, HBITMAP hbm, bool hasAlpha)
void AvatarCache::SetImage(const std::string& resource, HImage* him, bool hasAlpha)
{
std::string id = MakeIdentifier(resource);

Expand All @@ -66,14 +66,14 @@ void AvatarCache::SetBitmap(const std::string& resource, HBITMAP hbm, bool hasAl
auto iter = m_profileToBitmap.find(id);
if (iter != m_profileToBitmap.end())
{
DeleteBitmapIfNeeded(iter->second.m_bitmap);
iter->second.m_bitmap = hbm;
DeleteImageIfNeeded(iter->second.m_image);
iter->second.m_image = him;
iter->second.m_age = 0;
iter->second.m_bHasAlpha = hasAlpha;
return;
}

m_profileToBitmap[id] = BitmapObject(hbm, 0, hasAlpha);
m_profileToBitmap[id] = BitmapObject(him, 0, hasAlpha);
}

ImagePlace AvatarCache::GetPlace(const std::string& resource)
Expand All @@ -93,25 +93,26 @@ void AvatarCache::LoadedResource(const std::string& resource)
#include "Main.hpp"
#include "NetworkerThread.hpp"

extern HBITMAP GetDefaultBitmap(); // main.cpp
//extern HBITMAP GetDefaultBitmap(); // main.cpp
extern HImage* GetDefaultImage(); // main.cpp

HBITMAP AvatarCache::GetBitmapSpecial(const std::string& resource, bool& hasAlphaOut)
HImage* AvatarCache::GetImageSpecial(const std::string& resource, bool& hasAlphaOut)
{
std::string id = MakeIdentifier(resource);

auto iter = m_profileToBitmap.find(id);
if (iter != m_profileToBitmap.end()) {
iter->second.m_age = 0;
hasAlphaOut = iter->second.m_bHasAlpha;
return iter->second.m_bitmap;
return iter->second.m_image;
}

auto iterIP = m_imagePlaces.find(id);
if (iterIP == m_imagePlaces.end()) {
// this shouldn't happen. Just set to default
DbgPrintW("Could not load resource %s, no image place was registered", id.c_str());
SetBitmap(id, HBITMAP_LOADING, false);
return GetBitmapSpecial(id, hasAlphaOut);
SetImage(id, HIMAGE_LOADING, false);
return GetImageSpecial(id, hasAlphaOut);
}

eImagePlace pla = iterIP->second.type;
Expand All @@ -125,8 +126,8 @@ HBITMAP AvatarCache::GetBitmapSpecial(const std::string& resource, bool& hasAlph
// load that instead.
FILE* f = fopen(final_path.c_str(), "rb");
if (!f) {
SetBitmap(id, HBITMAP_ERROR, false);
return GetBitmapSpecial(id, hasAlphaOut);
SetImage(id, HIMAGE_ERROR, false);
return GetImageSpecial(id, hasAlphaOut);
}

fseek(f, 0, SEEK_END);
Expand All @@ -140,26 +141,30 @@ HBITMAP AvatarCache::GetBitmapSpecial(const std::string& resource, bool& hasAlph

int nsz = pla == eImagePlace::ATTACHMENTS ? 0 : -1;
bool hasAlpha = false;
HBITMAP hbmp = ImageLoader::ConvertToBitmap(pData, size_t(sz), hasAlpha, nsz, nsz);
if (hbmp) {
SetBitmap(id, hbmp, hasAlpha);
HImage* himg = ImageLoader::ConvertToBitmap(pData, size_t(sz), hasAlpha, false, nsz, nsz);

if (himg && himg->IsValid())
{
SetImage(id, himg, hasAlpha);
hasAlphaOut = hasAlpha;
return hbmp;
return himg;
}

SAFE_DELETE(himg);

// just return the default...
DbgPrintW("Image %s could not be decoded!", id.c_str());
#endif
SetBitmap(id, HBITMAP_ERROR, false);
return GetBitmapSpecial(id, hasAlphaOut);
SetImage(id, HIMAGE_ERROR, false);
return GetImageSpecial(id, hasAlphaOut);
}

// Could not find it in the cache, so request it from discord
SetBitmap(id, HBITMAP_LOADING, false);
SetImage(id, HIMAGE_LOADING, false);

if (iterIP->second.place.empty()) {
DbgPrintW("Image %s could not be fetched! Place is empty", id.c_str());
return GetBitmapSpecial(id, hasAlphaOut);
return GetImageSpecial(id, hasAlphaOut);
}

std::string url = iterIP->second.GetURL();
Expand All @@ -168,7 +173,7 @@ HBITMAP AvatarCache::GetBitmapSpecial(const std::string& resource, bool& hasAlph
{
// if not inserted already
if (!m_loadingResources.insert(url).second)
return GetBitmapSpecial(id, hasAlphaOut);
return GetImageSpecial(id, hasAlphaOut);

#ifdef DISABLE_AVATAR_LOADING_FOR_DEBUGGING
GetFrontend()->OnAttachmentFailed(!iterIP->second.IsAttachment(), id);
Expand All @@ -191,34 +196,34 @@ HBITMAP AvatarCache::GetBitmapSpecial(const std::string& resource, bool& hasAlph
DbgPrintW("Image %s could not be downloaded! URL is empty!", id.c_str());
}

return GetBitmapSpecial(id, hasAlphaOut);
return GetImageSpecial(id, hasAlphaOut);
}

HBITMAP AvatarCache::GetBitmapNullable(const std::string& resource, bool& hasAlphaOut)
HImage* AvatarCache::GetImageNullable(const std::string& resource, bool& hasAlphaOut)
{
HBITMAP hbm = GetBitmapSpecial(resource, hasAlphaOut);
HImage* him = GetImageSpecial(resource, hasAlphaOut);

if (hbm == HBITMAP_ERROR || hbm == HBITMAP_LOADING)
hbm = NULL;
if (him == HIMAGE_ERROR || him == HIMAGE_LOADING)
him = NULL;

return hbm;
return him;
}

HBITMAP AvatarCache::GetBitmap(const std::string& resource, bool& hasAlphaOut)
HImage* AvatarCache::GetImage(const std::string& resource, bool& hasAlphaOut)
{
hasAlphaOut = false;
HBITMAP hbm = GetBitmapSpecial(resource, hasAlphaOut);
HImage* him = GetImageSpecial(resource, hasAlphaOut);

if (hbm == HBITMAP_ERROR || hbm == HBITMAP_LOADING || !hbm)
hbm = GetDefaultBitmap();
if (him == HIMAGE_ERROR || him == HIMAGE_LOADING || !him)
him = GetDefaultImage();

return hbm;
return him;
}

void AvatarCache::WipeBitmaps()
{
for (auto b : m_profileToBitmap)
DeleteBitmapIfNeeded(b.second.m_bitmap);
DeleteImageIfNeeded(b.second.m_image);

m_profileToBitmap.clear();
m_imagePlaces.clear();
Expand All @@ -230,7 +235,7 @@ void AvatarCache::EraseBitmap(const std::string& resource)
if (iter == m_profileToBitmap.end())
return;

DeleteBitmapIfNeeded(iter->second.m_bitmap);
DeleteImageIfNeeded(iter->second.m_image);
m_profileToBitmap.erase(iter);
}

Expand All @@ -241,9 +246,9 @@ bool AvatarCache::TrimBitmap()

for (auto &b : m_profileToBitmap) {
if (maxAge < b.second.m_age &&
b.second.m_bitmap != GetDefaultBitmap() &&
b.second.m_bitmap != HBITMAP_ERROR &&
b.second.m_bitmap != HBITMAP_LOADING) {
b.second.m_image != GetDefaultImage() &&
b.second.m_image != HIMAGE_ERROR &&
b.second.m_image != HIMAGE_LOADING) {
maxAge = b.second.m_age;
rid = b.first;
}
Expand All @@ -260,7 +265,7 @@ bool AvatarCache::TrimBitmap()
m_loadingResources.erase(iter2);

DbgPrintW("Deleting bitmap %s", rid.c_str());
DeleteBitmapIfNeeded(iter->second.m_bitmap);
DeleteImageIfNeeded(iter->second.m_image);
m_profileToBitmap.erase(iter);

return true;
Expand Down Expand Up @@ -289,13 +294,10 @@ void AvatarCache::ClearProcessingRequests()
m_loadingResources.clear();
}

void AvatarCache::DeleteBitmapIfNeeded(HBITMAP hbm)
void AvatarCache::DeleteImageIfNeeded(HImage* him)
{
if (hbm && hbm != HBITMAP_LOADING && hbm != HBITMAP_ERROR && hbm != GetDefaultBitmap())
{
BOOL result = DeleteObject(hbm);
assert(result);
}
if (him && him != HIMAGE_LOADING && him != HIMAGE_ERROR && him != GetDefaultImage())
delete him;
}

std::string ImagePlace::GetURL() const
Expand Down
21 changes: 11 additions & 10 deletions src/windows/AvatarCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <set>

#include "../discord/Snowflake.hpp"
#include "ImageLoader.hpp"

enum class eImagePlace
{
Expand Down Expand Up @@ -33,21 +34,21 @@ struct ImagePlace {
std::string GetURL() const;
};

#define HBITMAP_LOADING ((HBITMAP) 0xDDCCBBAA)
#define HBITMAP_ERROR ((HBITMAP) 0xDDCCBBAB)
#define HIMAGE_LOADING ((HImage*) 0xDDCCBBAA)
#define HIMAGE_ERROR ((HImage*) 0xDDCCBBAB)

// Kind of a misnomer as it also handles attachment resources.
class AvatarCache
{
private:
struct BitmapObject
{
HBITMAP m_bitmap = NULL;
HImage* m_image = nullptr;
int m_age = 0;
bool m_bHasAlpha = false;

BitmapObject() {}
BitmapObject(HBITMAP hbm, int age, bool hasAlpha) : m_bitmap(hbm), m_age(age), m_bHasAlpha(hasAlpha) {}
BitmapObject(HImage* him, int age, bool hasAlpha) : m_image(him), m_age(age), m_bHasAlpha(hasAlpha) {}

~BitmapObject() {
}
Expand All @@ -58,7 +59,7 @@ class AvatarCache

// Set the bitmap associated with the resource ID.
// Note, after this, hbm is owned by the profile bitmap handler, so you shouldn't delete it
void SetBitmap(const std::string& resource, HBITMAP hbm, bool hasAlpha);
void SetImage(const std::string& resource, HImage* him, bool hasAlpha);

public:
// Create a 32-character identifier based on the resource name. If a 32 character
Expand All @@ -76,13 +77,13 @@ class AvatarCache
void LoadedResource(const std::string& resource);

// Get the bitmap associated with the resource. If it isn't loaded, request it, and return special bitmap handles.
HBITMAP GetBitmapSpecial(const std::string& resource, bool& hasAlphaOut);
HImage* GetImageSpecial(const std::string& resource, bool& hasAlphaOut);

// Get the bitmap associated with the resource. If it isn't loaded, request it, and return NULL.
HBITMAP GetBitmapNullable(const std::string& resource, bool& hasAlphaOut);
HImage* GetImageNullable(const std::string& resource, bool& hasAlphaOut);

// Get the bitmap associated with the resource. If it isn't loaded, request it, and return a default.
HBITMAP GetBitmap(const std::string& resource, bool& hasAlphaOut);
HImage* GetImage(const std::string& resource, bool& hasAlphaOut);

// Delete all bitmaps.
void WipeBitmaps();
Expand Down Expand Up @@ -117,8 +118,8 @@ class AvatarCache
// A list of resources pending load.
std::set<std::string> m_loadingResources;

// Delete the bitmap if it isn't the default one.
static void DeleteBitmapIfNeeded(HBITMAP hbm);
// Delete the image if it isn't the default one.
static void DeleteImageIfNeeded(HImage* hbm);
};

AvatarCache* GetAvatarCache();
2 changes: 1 addition & 1 deletion src/windows/ChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ LRESULT ChannelView::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

// draw profile picture
bool hasAlpha = false;
HBITMAP hbm = GetAvatarCache()->GetBitmap(pChan->m_avatarLnk, hasAlpha), hbmmask = NULL;
HBITMAP hbm = GetAvatarCache()->GetImage(pChan->m_avatarLnk, hasAlpha)->GetFirstFrame(), hbmmask = NULL;
DrawBitmap(hdc, hbm, rcItem.left + ScaleByDPI(6), rcItem.top + ScaleByDPI(4), NULL, CLR_NONE, GetProfilePictureSize(), 0, hasAlpha);

// draw status indicator
Expand Down
21 changes: 15 additions & 6 deletions src/windows/Frontend_Win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,21 @@ void Frontend_Win32::OnAttachmentDownloaded(bool bIsProfilePicture, const uint8_
{
int nImSize = bIsProfilePicture ? -1 : 0;
bool bHasAlpha = false;
HBITMAP bmp = ImageLoader::ConvertToBitmap(pData, nSize, bHasAlpha, nImSize, nImSize);
if (bmp)
HImage* himg = ImageLoader::ConvertToBitmap(pData, nSize, bHasAlpha, false, nImSize, nImSize);

if (himg)
{
GetAvatarCache()->LoadedResource(additData);
GetAvatarCache()->SetBitmap(additData, bmp, bHasAlpha);
OnUpdateAvatar(additData);
if (himg->IsValid())
{
GetAvatarCache()->LoadedResource(additData);
GetAvatarCache()->SetImage(additData, himg, bHasAlpha);
OnUpdateAvatar(additData);
// note: stole the resource so that the HImage destructor doesn't delete it.
}
else
{
delete himg;
}
}

// store the cached data..
Expand All @@ -246,7 +255,7 @@ void Frontend_Win32::OnAttachmentFailed(bool bIsProfilePicture, const std::strin
}

GetAvatarCache()->LoadedResource(additData);
GetAvatarCache()->SetBitmap(additData, HBITMAP_ERROR, false);
GetAvatarCache()->SetImage(additData, HIMAGE_ERROR, false);
OnUpdateAvatar(additData);
}

Expand Down
4 changes: 2 additions & 2 deletions src/windows/GuildLister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ LRESULT CALLBACK GuildLister::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
else
{
hbm = GetAvatarCache()->GetBitmap(pGuild->m_avatarlnk, hasAlpha);
hbm = GetAvatarCache()->GetImage(pGuild->m_avatarlnk, hasAlpha)->GetFirstFrame();
}

int oldY = y;
Expand Down Expand Up @@ -1035,7 +1035,7 @@ BOOL GuildLister::ChooserDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPa

if (!guild.m_avatarlnk.empty()) {
bool unusedHasAlpha = false;
hbm = GetAvatarCache()->GetBitmap(guild.m_avatarlnk, unusedHasAlpha);
hbm = GetAvatarCache()->GetImage(guild.m_avatarlnk, unusedHasAlpha)->GetFirstFrame();
}

int im = ImageList_Add(hImgList, hbm, NULL);
Expand Down
Loading

0 comments on commit 3a009f6

Please sign in to comment.