Fixed Deadlocking Issues In ImageFactory

This commit is contained in:
Alan Moon 2025-12-13 13:23:28 -08:00
parent 8e53e37ce2
commit 404092b484
5 changed files with 72 additions and 68 deletions

View File

@ -210,7 +210,7 @@ namespace QtCNETAPI.Services.ApiService
{
var restRequest = new RestRequest($"users/profile-pic/{userId}")
.AddHeader("Authorization", $"Bearer {SessionToken}");
var response = await _client.GetAsync(restRequest).ConfigureAwait(false);
var response = await _client.GetAsync(restRequest);
if (response != null)
{

View File

@ -15,68 +15,47 @@ namespace qtcnet_client.Factories
_apiService = apiService;
}
public async Task<Bitmap> GetAndCreateProfileImage(string userId, int status = -1, int cosmeticStoreId = 0)
public Bitmap GetAndCreateProfileImage(string userId, int status = -1, int cosmeticStoreId = 0)
{
var pfpTask = _apiService.GetUserProfilePic(userId);
var cosmeticTask = cosmeticStoreId >= 0 ? _apiService.GetStoreItem(cosmeticStoreId) : Task.FromResult<ServiceResponse<StoreItem>?>(null)!;
// get images from api
return await Task.Run(async () =>
// profile pic
Bitmap img1 = Resources.DefaultPfp;
var _pfpTask = Task.Run(() => _apiService.GetUserProfilePic(userId));
var _pfpRes = _pfpTask.Result;
if(_pfpRes.Success && _pfpRes.Data != null)
{
Bitmap img1 = Resources.DefaultPfp;
Bitmap? img2 = null;
Bitmap img3 = Resources.OfflineIcon;
using var ms = new MemoryStream(_pfpRes.Data);
img1 = new Bitmap(ms);
}
var pfpRes = await pfpTask;
if (pfpRes.Success && pfpRes.Data != null)
// cosmetic
Bitmap? img2 = null;
var _storeItemTask = Task.Run(() => _apiService.GetStoreItem(cosmeticStoreId));
var _storeItem = _storeItemTask.Result;
if(_storeItem.Success && _storeItem.Data != null)
{
var _webTask = Task.Run(() => _http.GetAsync(_storeItem.Data.AssetUrl));
using var _webRes = _webTask.Result;
if (_webRes.IsSuccessStatusCode)
img2 = new Bitmap(_webRes.Content.ReadAsStream());
}
// status
Bitmap? img3 = null;
if(!int.IsNegative(status))
{
img3 = status switch
{
using var ms = new MemoryStream(pfpRes.Data);
img1 = (Bitmap)Image.FromStream(ms);
}
1 => Resources.OnlineIcon,
2 => Resources.AwayIcon,
3 => Resources.DNDIcon,
_ => Resources.OfflineIcon,
};
}
if (cosmeticStoreId > 0)
{
var storeRes = await cosmeticTask;
if (storeRes?.Success == true)
{
using var httpRes = await _http.GetAsync(storeRes.Data?.AssetUrl);
if (httpRes.IsSuccessStatusCode)
{
var bytes = await httpRes.Content.ReadAsByteArrayAsync();
using var ms = new MemoryStream(bytes);
img2 = (Bitmap)Image.FromStream(ms);
}
}
}
Bitmap combined = new(139, 138);
using var g = Graphics.FromImage(combined);
g.Clear(Color.Transparent);
g.CompositingMode = CompositingMode.SourceOver;
g.DrawImage(img1, 4, 6, 128, 128);
if (img2 != null)
{
img2.MakeTransparent();
g.DrawImage(img2, 0, 0, 139, 138);
}
if (!int.IsNegative(status))
{
img3 = status switch
{
1 => Resources.OnlineIcon,
2 => Resources.AwayIcon,
3 => Resources.DNDIcon,
_ => Resources.OfflineIcon
};
img3.MakeTransparent();
g.DrawImage(img3, 104, 0, 35, 35);
}
return combined;
});
// return combined
return CombineImages(img1, img2, img3);
}
public async Task<Bitmap> GetStoreItemThumb(int id)
@ -88,10 +67,35 @@ namespace qtcnet_client.Factories
using var _http = new HttpClient();
using var _res = await _http.GetAsync(_item.Data.ThumbnailUrl);
if (_res.IsSuccessStatusCode)
return (Bitmap)Image.FromStream(_res.Content.ReadAsStream());
return new Bitmap(await _res.Content.ReadAsStreamAsync());
}
return Resources.QtCNETIcon; // temp no image icon
}
private static Bitmap CombineImages(Bitmap img1, Bitmap? img2, Bitmap? img3)
{
Bitmap combined = new(139, 138);
Graphics g = Graphics.FromImage(combined);
g.Clear(Color.Transparent);
g.CompositingMode = CompositingMode.SourceOver;
g.DrawImage(img1, 4, 6, 128, 128);
if(img2 != null)
{
img2.MakeTransparent();
g.DrawImage(img2, 0, 0, 139, 138);
}
if(img3 != null)
{
img3.MakeTransparent();
g.DrawImage(img3, 104, 0, 35, 35);
}
return combined;
}
}
}

View File

@ -160,7 +160,7 @@ namespace qtcnet_client.Forms
return image;
else
{
var img = await _imgFactory.GetAndCreateProfileImage(user.Id, -1, user.ActiveProfileCosmetic);
var img = _imgFactory.GetAndCreateProfileImage(user.Id, -1, user.ActiveProfileCosmetic);
UserProfileImages.Add(user.Id, img);
return img;
}

View File

@ -337,7 +337,7 @@ namespace qtcnet_client
_profile.ContactStatus = Contact.ContactStatus.NoRelation;
// get profile image
_profile.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId);
_profile.ProfileImage = _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId);
_profile.OnClose += ProfileForm_OnClose;
@ -382,7 +382,7 @@ namespace qtcnet_client
_profile.ContactStatus = Contact.ContactStatus.NoRelation;
// get profile image
_profile.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId);
_profile.ProfileImage = _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId);
_profile.OnClose += ProfileForm_OnClose;
@ -409,7 +409,7 @@ namespace qtcnet_client
CurrentProfileControl?.Username = _apiService.CurrentUser?.Username ?? "Username";
CurrentProfileControl?.CurrencyCount = _apiService.CurrentUser?.CurrencyAmount ?? 0;
CurrentProfileControl?.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _apiService.CurrentUser!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic);
CurrentProfileControl?.ProfileImage = _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _apiService.CurrentUser!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic);
CurrentProfileControl?.RefreshInfo();
}
@ -620,7 +620,7 @@ namespace qtcnet_client
CurrentProfileControl.OnStatusChanged += CurrentProfileControl_OnStatusChanged;
// get profile image for the current user
CurrentProfileControl.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser.Id, _apiService.CurrentUser.Status, _apiService.CurrentUser.ActiveProfileCosmetic);
CurrentProfileControl.ProfileImage = _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser.Id, _apiService.CurrentUser.Status, _apiService.CurrentUser.ActiveProfileCosmetic);
// setup main tab control
MainTabControl = new(_imgFactory)
@ -729,7 +729,7 @@ namespace qtcnet_client
{
if(e is UserStatusUpdatedEventArgs _args)
{
CurrentProfileControl?.ProfileImage = await _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _args.StatusDto!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic);
CurrentProfileControl?.ProfileImage = _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _args.StatusDto!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic);
}
}
@ -891,7 +891,7 @@ namespace qtcnet_client
Username = _user.Data.Username,
Bio = _user.Data.Bio,
Status = _user.Data.Status,
ProfileImage = await _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId)
ProfileImage = _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId)
};
_profile.OnClose += ProfileForm_OnClose;
@ -1040,7 +1040,7 @@ namespace qtcnet_client
}
// get profile image
ctrl.ProfilePic = await _imgFactory.GetAndCreateProfileImage(user.Data.Id, user.Data.Status, user.Data.ProfileCosmeticId);
ctrl.ProfilePic = _imgFactory.GetAndCreateProfileImage(user.Data.Id, user.Data.Status, user.Data.ProfileCosmeticId);
}
else if (contact.UserId == _apiService.CurrentUser.Id)
{
@ -1056,7 +1056,7 @@ namespace qtcnet_client
}
// get profile image
ctrl.ProfilePic = await _imgFactory.GetAndCreateProfileImage(user.Data.Id, user.Data.Status, user.Data.ProfileCosmeticId);
ctrl.ProfilePic = _imgFactory.GetAndCreateProfileImage(user.Data.Id, user.Data.Status, user.Data.ProfileCosmeticId);
}
// return the control

View File

@ -126,7 +126,7 @@ namespace qtcnet_client.Forms
ProfileCosmeticId = _itemId,
});
if (_res.Success)
pbProfileImage.Image = await _imgFactory.GetAndCreateProfileImage(UserId, Status, _itemId);
pbProfileImage.Image = _imgFactory.GetAndCreateProfileImage(UserId, Status, _itemId);
}
}