From 3a009f6089bb09b0bf470d57b0d6f13dba285b1e Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sat, 21 Sep 2024 10:43:38 +0300 Subject: [PATCH] * AvatarCache refactor. --- src/windows/AvatarCache.cpp | 88 +++--- src/windows/AvatarCache.hpp | 21 +- src/windows/ChannelView.cpp | 2 +- src/windows/Frontend_Win32.cpp | 21 +- src/windows/GuildLister.cpp | 4 +- src/windows/ImageLoader.cpp | 398 +++++++++++++++++++--------- src/windows/ImageLoader.hpp | 42 ++- src/windows/ImageViewer.cpp | 39 ++- src/windows/Main.cpp | 8 + src/windows/MemberList.cpp | 2 +- src/windows/MessageList.cpp | 24 +- src/windows/OptionsDialog.cpp | 2 +- src/windows/ProfilePopout.cpp | 2 +- src/windows/ProfileView.cpp | 2 +- src/windows/TextInterface_Win32.cpp | 2 +- 15 files changed, 425 insertions(+), 232 deletions(-) diff --git a/src/windows/AvatarCache.cpp b/src/windows/AvatarCache.cpp index 7ab5b7c..46cbcf5 100644 --- a/src/windows/AvatarCache.cpp +++ b/src/windows/AvatarCache.cpp @@ -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); @@ -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) @@ -93,9 +93,10 @@ 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); @@ -103,15 +104,15 @@ HBITMAP AvatarCache::GetBitmapSpecial(const std::string& resource, bool& hasAlph 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; @@ -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); @@ -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(); @@ -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); @@ -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(); @@ -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); } @@ -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; } @@ -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; @@ -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 diff --git a/src/windows/AvatarCache.hpp b/src/windows/AvatarCache.hpp index 4e45d00..2d3b6ab 100644 --- a/src/windows/AvatarCache.hpp +++ b/src/windows/AvatarCache.hpp @@ -6,6 +6,7 @@ #include #include "../discord/Snowflake.hpp" +#include "ImageLoader.hpp" enum class eImagePlace { @@ -33,8 +34,8 @@ 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 @@ -42,12 +43,12 @@ 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() { } @@ -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 @@ -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(); @@ -117,8 +118,8 @@ class AvatarCache // A list of resources pending load. std::set 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(); diff --git a/src/windows/ChannelView.cpp b/src/windows/ChannelView.cpp index ecd8b5e..b0403d4 100644 --- a/src/windows/ChannelView.cpp +++ b/src/windows/ChannelView.cpp @@ -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 diff --git a/src/windows/Frontend_Win32.cpp b/src/windows/Frontend_Win32.cpp index c612c39..b6b0b63 100644 --- a/src/windows/Frontend_Win32.cpp +++ b/src/windows/Frontend_Win32.cpp @@ -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.. @@ -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); } diff --git a/src/windows/GuildLister.cpp b/src/windows/GuildLister.cpp index 80ef7b0..a429b36 100644 --- a/src/windows/GuildLister.cpp +++ b/src/windows/GuildLister.cpp @@ -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; @@ -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); diff --git a/src/windows/ImageLoader.cpp b/src/windows/ImageLoader.cpp index 4152c16..e860d78 100644 --- a/src/windows/ImageLoader.cpp +++ b/src/windows/ImageLoader.cpp @@ -2,6 +2,58 @@ #include "ImageLoader.hpp" typedef void(*FreeFunction)(void*); +struct ImageFrame +{ + int FrameTime; + uint8_t* Data; + FreeFunction DataFree; + + ImageFrame(uint8_t* data, FreeFunction freeFunc, int frameTime = 0) : + FrameTime(frameTime), + Data(data), + DataFree(freeFunc) + {} + + ImageFrame(const ImageFrame& f) = delete; + + ImageFrame(ImageFrame&& f) noexcept { + Data = f.Data; + DataFree = f.DataFree; + FrameTime = f.FrameTime; + f.Data = nullptr; + f.DataFree = nullptr; + } + + ~ImageFrame() + { + if (Data) { + assert(DataFree); + DataFree(Data); + } + + Data = nullptr; + DataFree = nullptr; + } +}; + +struct Image +{ + std::vector Frames; + int Width = 0; + int Height = 0; + + Image() {} + Image(ImageFrame&& imf, int width, int height) : + Width(width), + Height(height) + { + Frames.push_back(std::move(imf)); + } + + bool IsValid() const { + return !Frames.empty(); + } +}; void NopFree(void* unused) {} @@ -9,18 +61,21 @@ void NopFree(void* unused) {} #include -static uint8_t* DecodeWebp(FreeFunction* pFreeFunc, const uint8_t* pData, size_t size, int* width, int* height) +static Image DecodeWebp(const uint8_t* pData, size_t size) { - *pFreeFunc = WebPFree; - return WebPDecodeBGRA(pData, size, width, height); + int width = 0, height = 0; + uint8_t* Data = WebPDecodeBGRA(pData, size, &width, &height); + if (!Data) + return Image(); + + return Image(ImageFrame(Data, WebPFree), width, height); } #else -static uint8_t* DecodeWebp(FreeFunction* pFreeFunc, const uint8_t* pData, size_t size, int* width, int* height) +static Image DecodeWebp(const uint8_t* pData, size_t size, int* width, int* height) { - *pFreeFunc = NopFree; - return nullptr; + return {}; } #endif @@ -32,14 +87,26 @@ static uint8_t* DecodeWebp(FreeFunction* pFreeFunc, const uint8_t* pData, size_t #include #include -static uint8_t* DecodeWithStbImage(FreeFunction* pFreeFunc, const uint8_t* pData, size_t size, int* width, int* height) +static Image DecodeWithStbImage(const uint8_t* pData, size_t size) { - int x; - stbi_uc* dataStbi = stbi_load_from_memory(pData, int(size), width, height, &x, 4); - if (!*width || !*height) - return nullptr; + if (size > INT_MAX) + return Image(); + + // note: doing some work with raw STBI contexts here. don't mind it. + /* + stbi__context s; + stbi__start_mem(&s, pData, (int) size); // why do you have to be an INT here... + + if (stbi__gif_test(&s)) { + + } + */ + + int x = 0, w = 0, h = 0; + stbi_uc* dataStbi = stbi_load_from_memory(pData, int(size), &w, &h, &x, 4); + if (!dataStbi) + return Image(); - int w = *width, h = *height; // byte swap because stbi is annoying for (int i = 0; i < w * h; i++) { @@ -49,35 +116,31 @@ static uint8_t* DecodeWithStbImage(FreeFunction* pFreeFunc, const uint8_t* pData pd[2] = tmp; } - *pFreeFunc = stbi_image_free; - return dataStbi; + return Image(ImageFrame(dataStbi, stbi_image_free, 0), w, h); } #else -static uint8_t* DecodeWithStbImage(FreeFunction* pFreeFunc, const uint8_t* pData, size_t size, int* width, int* height) +static Image DecodeWithStbImage(const uint8_t* pData, size_t size, int* width, int* height) { - *pFreeFunc = NopFree; - return nullptr; + return {}; } #endif -HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& outHasAlphaChannel, int newWidth, int newHeight) +HImage* ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& outHasAlphaChannel, bool loadAllFrames, int newWidth, int newHeight) { outHasAlphaChannel = false; - FreeFunction freeFunc = NopFree; - int width = 0, height = 0; - uint8_t* pNewData = DecodeWebp(&freeFunc, pData, size, &width, &height); + Image img = DecodeWebp(pData, size); - if (!pNewData) + if (!img.IsValid()) { // try using stb_image instead, probably a png/gif/jpg - pNewData = DecodeWithStbImage(&freeFunc, pData, size, &width, &height); + img = DecodeWithStbImage(pData, size); - if (!pNewData) - return NULL; + if (!img.IsValid()) + return nullptr; } if (newWidth < 0) @@ -86,37 +149,22 @@ HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& ou newHeight = GetProfilePictureSize(); if (!newWidth) - newWidth = width; + newWidth = img.Width; if (!newHeight) - newHeight = height; + newHeight = img.Height; - // pre-multiply alpha - size_t pixelCount = width * height; - uint32_t* pixels = (uint32_t*)pNewData; + size_t loadedImageCount = loadAllFrames ? img.Frames.size() : 1; - bool hasAlpha = false; - for (size_t i = 0; i < pixelCount; i++) - { - union { - uint8_t c[4]; - uint32_t x; - } u; - - u.x = pixels[i]; - if (u.c[3] != 0xFF) { - hasAlpha = true; - u.c[0] = uint8_t(int(u.c[0]) * int(u.c[3]) / 255); - u.c[1] = uint8_t(int(u.c[1]) * int(u.c[3]) / 255); - u.c[2] = uint8_t(int(u.c[2]) * int(u.c[3]) / 255); - pixels[i] = u.x; - } - } + HImage* himg = new HImage; + himg->Frames.resize(loadedImageCount); + himg->Width = newWidth; + himg->Height = newHeight; BITMAPINFO bmi{}; BITMAPINFOHEADER& hdr = bmi.bmiHeader; hdr.biSizeImage = 0; - hdr.biWidth = width; - hdr.biHeight = -height; + hdr.biWidth = img.Width; + hdr.biHeight = -img.Height; hdr.biPlanes = 1; hdr.biBitCount = 32; hdr.biCompression = BI_RGB; @@ -124,84 +172,113 @@ HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& ou HDC wndHdc = GetDC(g_Hwnd); HDC hdc = CreateCompatibleDC(wndHdc); - if (!hasAlpha) - { - HBITMAP hbm = CreateCompatibleBitmap(wndHdc, newWidth, newHeight); - HGDIOBJ old = SelectObject(hdc, hbm); - outHasAlphaChannel = false; - int x = 0; - if (newWidth != width || newHeight != height) { - int oldMode = SetStretchBltMode(hdc, HALFTONE); - x = StretchDIBits(hdc, 0, 0, newWidth, newHeight, 0, 0, width, height, pNewData, &bmi, DIB_RGB_COLORS, SRCCOPY); - SetStretchBltMode(hdc, oldMode); - } - else { - x = SetDIBits(hdc, hbm, 0, height, pNewData, &bmi, DIB_RGB_COLORS); + for (size_t i = 0; i < loadedImageCount; i++) + { + auto& frm = img.Frames[i]; + + // pre-multiply alpha + size_t pixelCount = img.Width * img.Height; + uint32_t* pixels = (uint32_t*)frm.Data; + + bool hasAlpha = false; + for (size_t i = 0; i < pixelCount; i++) + { + union { + uint8_t c[4]; + uint32_t x; + } u; + + u.x = pixels[i]; + if (u.c[3] != 0xFF) { + hasAlpha = true; + u.c[0] = uint8_t(int(u.c[0]) * int(u.c[3]) / 255); + u.c[1] = uint8_t(int(u.c[1]) * int(u.c[3]) / 255); + u.c[2] = uint8_t(int(u.c[2]) * int(u.c[3]) / 255); + pixels[i] = u.x; + } } - if (x == GDI_ERROR) { - DbgPrintW("ConvertToBitmap failed to convert opaque image!"); - DeleteObject(hbm); - hbm = NULL; + HBITMAP hbm = NULL; + + if (!hasAlpha) + { + hbm = CreateCompatibleBitmap(wndHdc, newWidth, newHeight); + HGDIOBJ old = SelectObject(hdc, hbm); + + int x = 0; + if (newWidth != img.Width || newHeight != img.Height) { + int oldMode = SetStretchBltMode(hdc, HALFTONE); + x = StretchDIBits(hdc, 0, 0, newWidth, newHeight, 0, 0, img.Width, img.Height, frm.Data, &bmi, DIB_RGB_COLORS, SRCCOPY); + SetStretchBltMode(hdc, oldMode); + } + else { + x = SetDIBits(hdc, hbm, 0, img.Height, frm.Data, &bmi, DIB_RGB_COLORS); + } + + if (x == GDI_ERROR) { + DbgPrintW("ConvertToBitmap failed to convert opaque image!"); + DeleteObject(hbm); + hbm = NULL; + } + + SelectObject(hdc, old); + goto DoneHBM; } - freeFunc(pNewData); - SelectObject(hdc, old); - DeleteDC(hdc); - ReleaseDC(g_Hwnd, wndHdc); - return hbm; - } + bool needDeletePixels = false; + int width = img.Width, height = img.Height; - // Check if we need to stretch the bitmap. - if (newWidth == width && newHeight == height) - { - // Nope, so we can apply a highly optimized scheme. - _UnstretchedScheme: - void* pvBits = NULL; - HBITMAP hbm = CreateDIBSection(wndHdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0); + // Check if we need to stretch the bitmap. + if (newWidth == width && newHeight == height) + { + // Nope, so we can apply a highly optimized scheme. + UnstretchedScheme: + void* pvBits = NULL; + hbm = CreateDIBSection(wndHdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0); - if (!hbm) { - DbgPrintW("ConvertToBitmap failed to convert transparent unstretched image!"); - hbm = NULL; + if (!hbm) { + DbgPrintW("ConvertToBitmap failed to convert transparent unstretched image!"); + hbm = NULL; + } + else { + memcpy(pvBits, pixels, sizeof(uint32_t) * pixelCount); + } + + if (needDeletePixels) + delete[] pixels; + + outHasAlphaChannel = true; + goto DoneHBM; } - else { - memcpy(pvBits, pixels, sizeof(uint32_t) * pixelCount); + + // Yeah, need to stretch the bitmap. Because of that, we need to perform two stretch + // operations and combine the bits together. + uint32_t* alphaPixels = new uint32_t[pixelCount]; + for (size_t i = 0; i < pixelCount; i++) { + uint32_t alpha = (pixels[i]) >> 24; + alphaPixels[i] = (255 << 24) | (alpha << 16) | (alpha << 8) | alpha; } - outHasAlphaChannel = true; - freeFunc(pNewData); - DeleteDC(hdc); - ReleaseDC(g_Hwnd, wndHdc); - return hbm; - } + // Create the alpha channel bitmap. + hbm = CreateCompatibleBitmap(wndHdc, newWidth, newHeight); + HGDIOBJ old = SelectObject(hdc, hbm); + int oldMode = SetStretchBltMode(hdc, HALFTONE); + int x = StretchDIBits(hdc, 0, 0, newWidth, newHeight, 0, 0, width, height, alphaPixels, &bmi, DIB_RGB_COLORS, SRCCOPY); + SetStretchBltMode(hdc, oldMode); + SelectObject(hdc, old); + delete[] alphaPixels; + + BITMAPINFO bmi2; + size_t newPixelCount; + uint32_t *pvAlphaBits, *pvColorBits; - // Yeah, need to stretch the bitmap. Because of that, we need to perform two stretch - // operations and combine the bits together. - uint32_t* alphaPixels = new uint32_t[pixelCount]; - for (size_t i = 0; i < pixelCount; i++) { - uint32_t alpha = (pixels[i]) >> 24; - alphaPixels[i] = (255 << 24) | (alpha << 16) | (alpha << 8) | alpha; - } + if (x == GDI_ERROR) { + DbgPrintW("ConvertToBitmap failed to convert transparent stretched image!"); + hbm = NULL; + goto DoneHBM; + } - // Create the alpha channel bitmap. - HBITMAP hbm = CreateCompatibleBitmap(wndHdc, newWidth, newHeight); - HGDIOBJ old = SelectObject(hdc, hbm); - int oldMode = SetStretchBltMode(hdc, HALFTONE); - int x = StretchDIBits(hdc, 0, 0, newWidth, newHeight, 0, 0, width, height, alphaPixels, &bmi, DIB_RGB_COLORS, SRCCOPY); - SetStretchBltMode(hdc, oldMode); - SelectObject(hdc, old); - delete[] alphaPixels; - - BITMAPINFO bmi2; - size_t newPixelCount; - uint32_t *pvAlphaBits, *pvColorBits; - - if (x == GDI_ERROR) { - DbgPrintW("ConvertToBitmap failed to convert transparent stretched image!"); - hbm = NULL; - } - else { ZeroMemory(&bmi2, sizeof bmi2); BITMAPINFOHEADER& hdr2 = bmi2.bmiHeader; hdr2.biSizeImage = 0; @@ -227,7 +304,7 @@ HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& ou delete[] pvAlphaBits; DeleteBitmap(hbm); hbm = NULL; - goto _error; + goto DoneHBM; } // Done with the alpha channel stretch. @@ -244,7 +321,7 @@ HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& ou DbgPrintW("ConvertToBitmap failed to convert transparent stretched image - StretchDIBits in alpha returned 0"); DeleteBitmap(hbm); hbm = NULL; - goto _error; + goto DoneHBM; } SelectObject(hdc, old); @@ -271,17 +348,15 @@ HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& ou delete[] pvColorBits; DeleteBitmap(hbm); hbm = NULL; - goto _error; + goto DoneHBM; } // Done with the color channel stretch. DeleteBitmap(hbm); // Clean up the original image, and perform a replacement: - freeFunc(pNewData); pixels = pvColorBits; - pNewData = (uint8_t*)pvColorBits; - freeFunc = &::free; + needDeletePixels = true; width = newWidth; height = newHeight; pixelCount = width * height; @@ -296,18 +371,16 @@ HBITMAP ImageLoader::ConvertToBitmap(const uint8_t* pData, size_t size, bool& ou delete[] pvAlphaBits; // Finally, apply the unstretched scheme. - goto _UnstretchedScheme; - } + goto UnstretchedScheme; -_error: - // note: you can't actually reach this part with a valid bitmap - assert(!hbm); + DoneHBM: + himg->Frames[i].Bitmap = hbm; + himg->Frames[i].FrameTime = frm.FrameTime; + } - outHasAlphaChannel = false; - freeFunc(pNewData); DeleteDC(hdc); ReleaseDC(g_Hwnd, wndHdc); - return hbm; + return himg; } static void ConvertToPNGWriteFunc(void* context, void* data, int size) @@ -398,10 +471,71 @@ HBITMAP ImageLoader::LoadFromFile(const char* pFileName, bool& hasAlphaOut) fclose(f); bool hasAlpha = false; - HBITMAP hbmp = ImageLoader::ConvertToBitmap(pData, size_t(sz), hasAlpha, 0, 0); - if (!hbmp) + HImage* himg = ImageLoader::ConvertToBitmap(pData, size_t(sz), hasAlpha, false, 0, 0); + assert(himg->Frames.size() == 1); + + if (!himg->Frames[0].Bitmap) { + delete himg; return NULL; + } + + // steal the resource for ourselves (don't let the HImage destructor delete it) + HBITMAP hbm = himg->Frames[0].Bitmap; + himg->Frames[0].Bitmap = NULL; + delete himg; hasAlphaOut = hasAlpha; - return hbmp; + return hbm; +} + +HImageFrame::~HImageFrame() +{ + if (Bitmap) + DeleteBitmap(Bitmap); + + Bitmap = NULL; +} + +extern HImage g_defaultImage; + +HImage::HImage() +{ + DbgPrintW("HImage %p constructed", this); +} + +HImage::~HImage() +{ + // note: this assertion will not work when exiting ! + //assert(this != &g_defaultImage); + DbgPrintW("HImage %p destructed", this); +} + +HBITMAP HImage::WithdrawImage(size_t frameNo, int* frameLengthOut) +{ + if (frameNo >= Frames.size()) + return NULL; + + HBITMAP hbm = Frames[frameNo].Bitmap; + if (frameLengthOut) + *frameLengthOut = Frames[frameNo].FrameTime; + + Frames[frameNo].Bitmap = NULL; + return hbm; +} + +HBITMAP HImage::GetImage(size_t frameNo, int* frameLengthOut) const +{ + if (frameNo >= Frames.size()) + return NULL; + + if (frameLengthOut) + *frameLengthOut = Frames[frameNo].FrameTime; + + return Frames[frameNo].Bitmap; +} + +HBITMAP HImage::GetFirstFrame() const +{ + if (Frames.empty()) return NULL; + return Frames[0].Bitmap; } diff --git a/src/windows/ImageLoader.hpp b/src/windows/ImageLoader.hpp index d349c9b..16f9bc0 100644 --- a/src/windows/ImageLoader.hpp +++ b/src/windows/ImageLoader.hpp @@ -4,12 +4,52 @@ #include #include +struct HImageFrame +{ + HBITMAP Bitmap = NULL; + int FrameTime = 0; + + ~HImageFrame(); +}; + +struct HImage +{ + std::vector Frames; + int Width = 0; + int Height = 0; + + HImage(); + ~HImage(); + HImage(const HImage&) = delete; + HImage(HImage&&) = default; + + bool IsValid() const noexcept { + return !Frames.empty(); + } + + // Withdraws a bitmap handle based on frame number. This removes it from + // the management of HImage, so deleting the HImage instance won't delete + // the returned bitmap. + HBITMAP WithdrawImage(size_t frameNo, int* frameLengthOut); + + // Gets a bitmap based on frame number. This doesn't remove it from the + // management of HImage. As such, deleting the HImage instance won't delete + // the returned image. + HBITMAP GetImage(size_t frameNo, int* frameLengthOut) const; + + // Gets the first frame of an image. + // Use when you have no intention of animating the image. + HBITMAP GetFirstFrame() const; +}; + class ImageLoader { public: // outHasAlphaChannel - If HBITMAP is valid, this value means whether or not the alpha channel is valid. If // false, you shouldn't use AlphaBlend because the channel is most likely filled with zero, which is bad. - static HBITMAP ConvertToBitmap(const uint8_t* pData, size_t size, bool& outHasAlphaChannel, int width = 0, int height = 0); + // + // loadAllFrames - If false, loads only the first frame. + static HImage* ConvertToBitmap(const uint8_t* pData, size_t size, bool& outHasAlphaChannel, bool loadAllFrames = true, int width = 0, int height = 0); static HBITMAP LoadFromFile(const char* pFileName, bool& outHasAlphaChannel); diff --git a/src/windows/ImageViewer.cpp b/src/windows/ImageViewer.cpp index b3a4db1..0d87633 100644 --- a/src/windows/ImageViewer.cpp +++ b/src/windows/ImageViewer.cpp @@ -19,19 +19,19 @@ static int g_ivPreviewWidth, g_ivPreviewHeight; static int g_ivPreviewImageWidth, g_ivPreviewImageHeight; static bool g_ivbHaveScrollBars; static HWND g_ivChildHwnd, g_ivButtonHwnd; -static HBITMAP g_hBitmapPreview, g_hBitmapFull; +static HImage* g_hBitmapPreview, *g_hBitmapFull; static HCURSOR g_curZoomIn, g_curZoomOut; void KillImageBitmaps() { - if (g_hBitmapFull) DeleteObject(g_hBitmapFull); - if (g_hBitmapPreview) DeleteObject(g_hBitmapPreview); + if (g_hBitmapFull) delete g_hBitmapFull; + if (g_hBitmapPreview) delete g_hBitmapPreview; g_hBitmapFull = g_hBitmapPreview = NULL; } static bool g_bChildZoomedIn = false; -static HBITMAP GetBitmap() +static HImage* GetBitmap() { if (g_bChildZoomedIn) return g_hBitmapFull; @@ -103,21 +103,14 @@ LRESULT CALLBACK ImageViewerChildWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP } case WM_UPDATEBITMAP: { - HBITMAP hbm = GetBitmap(); - if (!hbm) + HImage* him = GetBitmap(); + if (!him) return 0; - BITMAP bm; - memset(&bm, 0, sizeof(bm)); - if (!GetObject(hbm, sizeof(BITMAP), &bm)) { - MessageBox(hWnd, TmGetTString(IDS_CANT_SHOW_BITMAP), TmGetTString(IDS_PROGRAM_NAME), MB_ICONERROR); - break; - } - bool bzi = g_bChildZoomedIn; - int prevWidth = abs(bm.bmWidth); - int prevHeight = abs(bm.bmHeight); + int prevWidth = abs(him->Width); + int prevHeight = abs(him->Height); RECT rect = {}; GetClientRect(hWnd, &rect); @@ -241,8 +234,8 @@ LRESULT CALLBACK ImageViewerChildWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); - HBITMAP hbm = GetBitmap(); - if (!hbm) { + HImage* him = GetBitmap(); + if (!him) { COLORREF oldBk = SetBkColor(hdc, GetSysColor (COLOR_3DDKSHADOW)); COLORREF oldText = SetTextColor(hdc, RGB(255, 255, 255)); HGDIOBJ oldObject = SelectObject(hdc, g_MessageTextFont); @@ -276,7 +269,10 @@ LRESULT CALLBACK ImageViewerChildWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP } // draw the image - DrawBitmap(hdc, hbm, -xOffs, -yOffs, NULL); + + // TODO: if animated, go animate it! + assert(him->Frames.size() != 0); + DrawBitmap(hdc, him->Frames[0].Bitmap, -xOffs, -yOffs, NULL); EndPaint(hWnd, &ps); break; @@ -327,8 +323,11 @@ void ImageViewerOnLoad(NetRequest* pRequest) bool unusedHasAlpha1 = false; bool unusedHasAlpha2 = false; - g_hBitmapFull = ImageLoader::ConvertToBitmap(pData, nSize, unusedHasAlpha1, 0, 0); - g_hBitmapPreview = ImageLoader::ConvertToBitmap(pData, nSize, unusedHasAlpha2, g_ivPreviewImageWidth, g_ivPreviewImageHeight); + HImage* imFull = ImageLoader::ConvertToBitmap(pData, nSize, unusedHasAlpha1, false, 0, 0); + HImage* imPreview = ImageLoader::ConvertToBitmap(pData, nSize, unusedHasAlpha2, false, g_ivPreviewImageWidth, g_ivPreviewImageHeight); + + g_hBitmapFull = imFull; + g_hBitmapPreview = imPreview; g_bChildZoomedIn = false; SendMessage(g_ivChildHwnd, WM_UPDATEBITMAP, 0, 0); diff --git a/src/windows/Main.cpp b/src/windows/Main.cpp index 040a9db..409028c 100644 --- a/src/windows/Main.cpp +++ b/src/windows/Main.cpp @@ -152,12 +152,16 @@ HFONT g_TypingBoldFont; HBITMAP g_DefaultProfilePicture; +HImage g_defaultImage; UINT_PTR g_HeartbeatTimer; int g_HeartbeatTimerInterval; HBITMAP GetDefaultBitmap() { return g_DefaultProfilePicture; } +HImage* GetDefaultImage() { + return &g_defaultImage; +} void WantQuit() { @@ -1699,6 +1703,10 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLin g_ProfileBorderIcon = (HICON) LoadImage(hInstance, MAKEINTRESOURCE(DMIC(IDI_PROFILE_BORDER)), IMAGE_ICON, 0, 0, LR_CREATEDIBSECTION | LR_SHARED); g_ProfileBorderIconGold = (HICON) LoadImage(hInstance, MAKEINTRESOURCE(DMIC(IDI_PROFILE_BORDER_GOLD)), IMAGE_ICON, 0, 0, LR_CREATEDIBSECTION | LR_SHARED); + g_defaultImage.Frames.resize(1); + g_defaultImage.Frames[0].Bitmap = g_DefaultProfilePicture; + g_defaultImage.Width = g_defaultImage.Height = GetProfilePictureSize(); + InitializeStatusIcons(); // Register the class. diff --git a/src/windows/MemberList.cpp b/src/windows/MemberList.cpp index 550ee33..f90634e 100644 --- a/src/windows/MemberList.cpp +++ b/src/windows/MemberList.cpp @@ -401,7 +401,7 @@ LRESULT MemberList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // draw profile picture bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmap(pf->m_avatarlnk, hasAlpha), hbmmask = NULL; + HBITMAP hbm = GetAvatarCache()->GetImage(pf->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 diff --git a/src/windows/MessageList.cpp b/src/windows/MessageList.cpp index 7abd941..b5ca832 100644 --- a/src/windows/MessageList.cpp +++ b/src/windows/MessageList.cpp @@ -28,14 +28,14 @@ static int GetProfileBorderRenderSize() return ScaleByDPI(Supports32BitIcons() ? (PROFILE_PICTURE_SIZE_DEF + 12) : 64); } -static void DrawImageSpecial(HDC hdc, HBITMAP hbm, RECT rect, bool hasAlpha) +static void DrawImageSpecial(HDC hdc, HImage* him, RECT rect, bool hasAlpha) { - if (hbm == HBITMAP_LOADING) + if (him == HIMAGE_LOADING) DrawLoadingBox(hdc, rect); - else if (hbm == HBITMAP_ERROR || !hbm) + else if (him == HIMAGE_ERROR || !him) DrawErrorBox(hdc, rect); else - DrawBitmap(hdc, hbm, rect.left, rect.top, NULL, CLR_NONE, rect.right - rect.left, rect.bottom - rect.top, hasAlpha); + DrawBitmap(hdc, him->GetFirstFrame(), rect.left, rect.top, NULL, CLR_NONE, rect.right - rect.left, rect.bottom - rect.top, hasAlpha); } WNDCLASS MessageList::g_MsgListClass; @@ -343,20 +343,20 @@ void RichEmbedItem::Draw(HDC hdc, RECT& messageRect, MessageList* pList) m_thumbnailResourceID = GetAvatarCache()->MakeIdentifier(m_pEmbed->m_thumbnailUrl); GetAvatarCache()->AddImagePlace(m_thumbnailResourceID, eImagePlace::ATTACHMENTS, m_pEmbed->m_thumbnailProxiedUrl); bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmapSpecial(m_pEmbed->m_thumbnailUrl, hasAlpha); + HImage* him = GetAvatarCache()->GetImageSpecial(m_pEmbed->m_thumbnailUrl, hasAlpha); if (sizeY) sizeY += gap; m_thumbnailRect = { rc.left, rc.top + sizeY, rc.left + m_thumbnailSize.cx, rc.top + sizeY + m_thumbnailSize.cy }; - DrawImageSpecial(hdc, hbm, m_thumbnailRect, hasAlpha); + DrawImageSpecial(hdc, him, m_thumbnailRect, hasAlpha); sizeY += m_thumbnailSize.cy; } if (m_imageSize.cy) { m_imageResourceID = GetAvatarCache()->MakeIdentifier(m_pEmbed->m_imageUrl); GetAvatarCache()->AddImagePlace(m_imageResourceID, eImagePlace::ATTACHMENTS, m_pEmbed->m_imageProxiedUrl); bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmapSpecial(m_pEmbed->m_imageUrl, hasAlpha); + HImage* him = GetAvatarCache()->GetImageSpecial(m_pEmbed->m_imageUrl, hasAlpha); if (sizeY) sizeY += gap; m_imageRect = { rc.left, rc.top + sizeY, rc.left + m_imageSize.cx, rc.top + sizeY + m_imageSize.cy }; - DrawImageSpecial(hdc, hbm, m_imageRect, hasAlpha); + DrawImageSpecial(hdc, him, m_imageRect, hasAlpha); sizeY += m_imageSize.cy; } } @@ -1447,8 +1447,8 @@ void MessageList::DrawImageAttachment(HDC hdc, RECT& paintRect, AttachmentItem& GetAvatarCache()->AddImagePlace(attachItem.m_resourceID, eImagePlace::ATTACHMENTS, url, pAttach->m_id); bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmapSpecial(attachItem.m_resourceID, hasAlpha); - DrawImageSpecial(hdc, hbm, childAttachRect, hasAlpha); + HImage* him = GetAvatarCache()->GetImageSpecial(attachItem.m_resourceID, hasAlpha); + DrawImageSpecial(hdc, him, childAttachRect, hasAlpha); } void MessageList::DrawDefaultAttachment(HDC hdc, RECT& paintRect, AttachmentItem& attachItem, RECT& attachRect) @@ -1942,7 +1942,7 @@ int MessageList::DrawMessageReply(HDC hdc, MessageItem& item, RECT& rc) DrawIconEx(hdc, pfpX - pfpBordOffX, pfpY - pfpBordOffY, g_ProfileBorderIcon, pfpBordSize, pfpBordSize, 0, NULL, DI_COMPAT | DI_NORMAL); bool hasAlpha = false; GetAvatarCache()->AddImagePlace(refMsg.m_avatar, eImagePlace::AVATARS, refMsg.m_avatar, refMsg.m_author_snowflake); - HBITMAP hbm = GetAvatarCache()->GetBitmap(refMsg.m_avatar, hasAlpha); + HBITMAP hbm = GetAvatarCache()->GetImage(refMsg.m_avatar, hasAlpha)->GetFirstFrame(); RECT& raRect = item.m_refAvatarRect; raRect.left = pfpX; @@ -2689,7 +2689,7 @@ void MessageList::DrawMessage(HDC hdc, MessageItem& item, RECT& msgRect, RECT& c // draw the avatar bool hasAlpha = false; GetAvatarCache()->AddImagePlace(item.m_msg.m_avatar, eImagePlace::AVATARS, item.m_msg.m_avatar, item.m_msg.m_author_snowflake); - HBITMAP hbm = GetAvatarCache()->GetBitmap(item.m_msg.m_avatar, hasAlpha); + HBITMAP hbm = GetAvatarCache()->GetImage(item.m_msg.m_avatar, hasAlpha)->GetFirstFrame(); DrawBitmap(hdc, hbm, pfRect.left, pfRect.top, &pfRect, CLR_NONE, GetProfilePictureSize(), 0, hasAlpha); } } diff --git a/src/windows/OptionsDialog.cpp b/src/windows/OptionsDialog.cpp index 46ee402..8369745 100644 --- a/src/windows/OptionsDialog.cpp +++ b/src/windows/OptionsDialog.cpp @@ -148,7 +148,7 @@ void WINAPI OnChildDialogInit(HWND hwndDlg) hwnd = GetDlgItem(hwndDlg, IDC_STATIC_PROFILE_IMAGE); bool unusedHasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmap(pProfile->m_avatarlnk, unusedHasAlpha); + HBITMAP hbm = GetAvatarCache()->GetImage(pProfile->m_avatarlnk, unusedHasAlpha)->GetFirstFrame(); SendMessage(hwnd, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbm); eExplicitFilter filter = GetSettingsManager()->GetExplicitFilter(); diff --git a/src/windows/ProfilePopout.cpp b/src/windows/ProfilePopout.cpp index c00b80e..ed9fbae 100644 --- a/src/windows/ProfilePopout.cpp +++ b/src/windows/ProfilePopout.cpp @@ -318,7 +318,7 @@ bool ProfilePopout::Layout(HWND hWnd, SIZE& fullSize) ShowWindow(hChild, SW_SHOW); MoveWindow(hChild, pos.x, pos.y, joinedAtIconSize, joinedAtIconSize, TRUE); bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmap(gld->m_avatarlnk, hasAlpha); + HBITMAP hbm = GetAvatarCache()->GetImage(gld->m_avatarlnk, hasAlpha)->GetFirstFrame(); if (hbm) { hdc = GetDC(hWnd); int pps = GetProfilePictureSize(); diff --git a/src/windows/ProfileView.cpp b/src/windows/ProfileView.cpp index 2374de6..b3461d2 100644 --- a/src/windows/ProfileView.cpp +++ b/src/windows/ProfileView.cpp @@ -98,7 +98,7 @@ void ProfileView::Paint(HDC hdc) } bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmap(avatarLink, hasAlpha); + HBITMAP hbm = GetAvatarCache()->GetImage(avatarLink, hasAlpha)->GetFirstFrame(); int pfpSize = GetProfilePictureSize(); int pfpBorderSize = ScaleByDPI(PROFILE_PICTURE_SIZE_DEF + 12); int pfpBorderSizeDrawn = GetProfileBorderSize(); diff --git a/src/windows/TextInterface_Win32.cpp b/src/windows/TextInterface_Win32.cpp index d798f65..ee9b37c 100644 --- a/src/windows/TextInterface_Win32.cpp +++ b/src/windows/TextInterface_Win32.cpp @@ -147,7 +147,7 @@ void MdDrawString(DrawingContext* context, const Rect& rect, const String& str, std::string name = GetAvatarCache()->AddImagePlace(nameRaw, eImagePlace::EMOJIS, nameRaw, parsed, height); bool hasAlpha = false; - HBITMAP hbm = GetAvatarCache()->GetBitmap(nameRaw, hasAlpha); + HBITMAP hbm = GetAvatarCache()->GetImage(nameRaw, hasAlpha)->GetFirstFrame(); bool shouldResize = GetProfilePictureSize() != height;