WX: HiDPI: MemcardManager

Required a partial rewrite of the image loading code because it was working in
unscaled wxBitmaps. Needed to make it produce wxImages and scale them instead.
This commit is contained in:
EmptyChaos 2016-08-02 06:21:01 +00:00
parent 3b11581aff
commit b6fb56e978
2 changed files with 110 additions and 95 deletions

View File

@ -17,7 +17,6 @@
#include <wx/listbase.h>
#include <wx/menu.h>
#include <wx/msgdlg.h>
#include <wx/mstream.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
@ -34,37 +33,24 @@
#define FIRSTPAGE 0
#define ARROWS slot ? "" : ARROW[slot], slot ? ARROW[slot] : ""
static wxBitmap wxBitmapFromMemoryRGBA(const unsigned char* data, u32 width, u32 height)
static wxImage wxImageFromMemoryRGBA(const u32* data, u32 width, u32 height)
{
static const std::array<u8, 54> header = {
{0x42, 0x4D, 0x38, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // Width
0x20, 0x00, 0x00, 0x00, // Height
0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, // Data size
0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}};
u32 stride = (4 * width);
u32 bytes = (stride * height) + header.size();
bytes = (bytes + 3) & ~3;
u32 data_length = bytes - header.size();
std::vector<u8> pdata(bytes);
std::copy(header.begin(), header.end(), pdata.begin());
u8* const pixelData = &pdata[header.size()];
for (u32 y = 0; y < height; y++)
std::memcpy(&pixelData[y * stride], &data[(height - y - 1) * stride], stride);
std::memcpy(&pdata[18], &width, sizeof(u32));
std::memcpy(&pdata[22], &height, sizeof(u32));
std::memcpy(&pdata[34], &data_length, sizeof(u32));
wxMemoryInputStream is(pdata.data(), bytes);
return wxBitmap(wxImage(is, wxBITMAP_TYPE_BMP));
wxImage image(width, height, false);
image.InitAlpha();
u8* rgb = image.GetData();
u8* alpha = image.GetAlpha();
for (u32 y = 0; y < height; ++y)
{
for (u32 x = 0; x < width; ++x)
{
u32 pixel = data[y * width + x];
*rgb++ = (pixel & 0x00FF0000) >> 16; // Red
*rgb++ = (pixel & 0x0000FF00) >> 8; // Green
*rgb++ = (pixel & 0x000000FF) >> 0; // Blue
*alpha++ = (pixel & 0xFF000000) >> 24;
}
}
return image;
}
BEGIN_EVENT_TABLE(CMemcardManager, wxDialog)
@ -94,7 +80,8 @@ END_EVENT_TABLE()
CMemcardManager::CMemcardManager(wxWindow* parent)
: wxDialog(parent, wxID_ANY, _("Memory Card Manager"), wxDefaultPosition, wxDefaultSize,
wxCAPTION | wxSYSTEM_MENU | wxDIALOG_NO_PARENT | wxCLOSE_BOX | wxRESIZE_BORDER |
wxMAXIMIZE_BOX)
wxMAXIMIZE_BOX),
m_image_list_size(FromDIP(wxSize(96, 32)))
{
memoryCard[SLOT_A] = nullptr;
memoryCard[SLOT_B] = nullptr;
@ -179,6 +166,7 @@ bool CMemcardManager::SaveSettings()
void CMemcardManager::CreateGUIControls()
{
// Create the controls for both memcards
const int space5 = FromDIP(5);
const char* ARROW[2] = {"<-", "->"};
@ -203,11 +191,11 @@ void CMemcardManager::CreateGUIControls()
t_Status[slot] = new wxStaticText(this, 0, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0,
wxEmptyString);
wxBoxSizer* const sPages = new wxBoxSizer(wxHORIZONTAL);
sPages->Add(m_PrevPage[slot], 0, wxEXPAND | wxALL, 1);
sPages->Add(t_Status[slot], 0, wxEXPAND | wxALL, 5);
sPages->Add(0, 0, 1, wxEXPAND | wxALL, 0);
sPages->Add(m_NextPage[slot], 0, wxEXPAND | wxALL, 1);
wxFlexGridSizer* const paging_sizer = new wxFlexGridSizer(3, wxSize(space5, space5));
paging_sizer->AddGrowableCol(1);
paging_sizer->Add(m_PrevPage[slot], 0, wxEXPAND);
paging_sizer->Add(t_Status[slot], 0, wxALIGN_CENTER);
paging_sizer->Add(m_NextPage[slot], 0, wxEXPAND);
m_MemcardPath[slot] = new wxFilePickerCtrl(
this, ID_MEMCARDPATH_A + slot, StrToWxStr(File::GetUserPath(D_GCUSER_IDX)),
@ -216,41 +204,51 @@ void CMemcardManager::CreateGUIControls()
wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_OPEN);
m_MemcardList[slot] = new CMemcardListCtrl(
this, ID_MEMCARDLIST_A + slot, wxDefaultPosition, wxSize(350, 400),
this, ID_MEMCARDLIST_A + slot, wxDefaultPosition, FromDIP(wxSize(350, 400)),
wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL, mcmSettings);
m_MemcardList[slot]->AssignImageList(new wxImageList(96, 32), wxIMAGE_LIST_SMALL);
m_MemcardList[slot]->AssignImageList(
new wxImageList(m_image_list_size.GetWidth(), m_image_list_size.GetHeight()),
wxIMAGE_LIST_SMALL);
sMemcard[slot] = new wxStaticBoxSizer(wxVERTICAL, this,
_("Memory Card") + wxString::Format(" %c", 'A' + slot));
sMemcard[slot]->Add(m_MemcardPath[slot], 0, wxEXPAND | wxALL, 5);
sMemcard[slot]->Add(m_MemcardList[slot], 1, wxEXPAND | wxALL, 5);
sMemcard[slot]->Add(sPages, 0, wxEXPAND | wxALL, 1);
sMemcard[slot]->AddSpacer(space5);
sMemcard[slot]->Add(m_MemcardPath[slot], 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
sMemcard[slot]->AddSpacer(space5);
sMemcard[slot]->Add(m_MemcardList[slot], 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
sMemcard[slot]->AddSpacer(space5);
sMemcard[slot]->Add(paging_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
sMemcard[slot]->AddSpacer(space5);
}
wxBoxSizer* const sButtons = new wxBoxSizer(wxVERTICAL);
sButtons->AddStretchSpacer(2);
sButtons->Add(m_CopyFrom[SLOT_B], 0, wxEXPAND, 5);
sButtons->Add(m_CopyFrom[SLOT_A], 0, wxEXPAND, 5);
sButtons->AddStretchSpacer(1);
sButtons->Add(m_SaveImport[SLOT_A], 0, wxEXPAND, 5);
sButtons->Add(m_SaveExport[SLOT_A], 0, wxEXPAND, 5);
sButtons->AddStretchSpacer(1);
sButtons->Add(m_ConvertToGci, 0, wxEXPAND, 5);
sButtons->AddStretchSpacer(1);
sButtons->Add(m_SaveImport[SLOT_B], 0, wxEXPAND, 5);
sButtons->Add(m_SaveExport[SLOT_B], 0, wxEXPAND, 5);
sButtons->AddStretchSpacer(1);
sButtons->Add(m_Delete[SLOT_A], 0, wxEXPAND, 5);
sButtons->Add(m_Delete[SLOT_B], 0, wxEXPAND, 5);
sButtons->Add(m_CopyFrom[SLOT_B], 0, wxEXPAND);
sButtons->Add(m_CopyFrom[SLOT_A], 0, wxEXPAND);
sButtons->AddStretchSpacer();
sButtons->Add(new wxButton(this, wxID_OK, _("Close")), 0, wxEXPAND, 5);
sButtons->Add(m_SaveImport[SLOT_A], 0, wxEXPAND);
sButtons->Add(m_SaveExport[SLOT_A], 0, wxEXPAND);
sButtons->AddStretchSpacer();
sButtons->Add(m_ConvertToGci, 0, wxEXPAND);
sButtons->AddStretchSpacer();
sButtons->Add(m_SaveImport[SLOT_B], 0, wxEXPAND);
sButtons->Add(m_SaveExport[SLOT_B], 0, wxEXPAND);
sButtons->AddStretchSpacer();
sButtons->Add(m_Delete[SLOT_A], 0, wxEXPAND);
sButtons->Add(m_Delete[SLOT_B], 0, wxEXPAND);
sButtons->AddStretchSpacer();
sButtons->Add(new wxButton(this, wxID_OK, _("Close")), 0, wxEXPAND);
sButtons->AddStretchSpacer();
wxBoxSizer* const sMain = new wxBoxSizer(wxHORIZONTAL);
sMain->Add(sMemcard[SLOT_A], 1, wxEXPAND | wxALL, 5);
sMain->Add(sButtons, 0, wxEXPAND, 0);
sMain->Add(sMemcard[SLOT_B], 1, wxEXPAND | wxALL, 5);
sMain->AddSpacer(space5);
sMain->Add(sMemcard[SLOT_A], 1, wxEXPAND | wxTOP | wxBOTTOM, space5);
sMain->AddSpacer(space5);
sMain->Add(sButtons, 0, wxEXPAND | wxTOP | wxBOTTOM, space5);
sMain->AddSpacer(space5);
sMain->Add(sMemcard[SLOT_B], 1, wxEXPAND | wxTOP | wxBOTTOM, space5);
sMain->AddSpacer(space5);
SetSizerAndFit(sMain);
Center();
@ -653,57 +651,73 @@ bool CMemcardManager::ReloadMemcard(const std::string& fileName, int card)
u8 nFiles = memoryCard[card]->GetNumFiles();
std::vector<int> images(nFiles * 2);
static constexpr unsigned int BANNER_WIDTH = 96;
static constexpr unsigned int ANIM_FRAME_WIDTH = 32;
static constexpr unsigned int IMAGE_HEIGHT = 32;
static constexpr unsigned int ANIM_MAX_FRAMES = 8;
std::vector<u32> pxdata(BANNER_WIDTH * IMAGE_HEIGHT);
std::vector<u8> anim_delay(ANIM_MAX_FRAMES);
std::vector<u32> anim_data(ANIM_FRAME_WIDTH * IMAGE_HEIGHT * ANIM_MAX_FRAMES);
for (u8 i = 0; i < nFiles; i++)
{
static u32 pxdata[96 * 32];
static u8 animDelay[8];
static u32 animData[32 * 32 * 8];
u8 file_index = memoryCard[card]->GetFileIndex(i);
u32 num_frames =
memoryCard[card]->ReadAnimRGBA8(file_index, anim_data.data(), anim_delay.data());
u8 fileIndex = memoryCard[card]->GetFileIndex(i);
int numFrames = memoryCard[card]->ReadAnimRGBA8(fileIndex, animData, animDelay);
if (!memoryCard[card]->ReadBannerRGBA8(fileIndex, pxdata))
// Decode Save File Animation
wxImage anim_img_strip;
if (num_frames > 0)
{
memset(pxdata, 0, 96 * 32 * 4);
unsigned int frames = BANNER_WIDTH / ANIM_FRAME_WIDTH;
if (numFrames > 0) // Just use the first one
if (num_frames < frames)
{
u32* icdata = animData;
frames = num_frames;
for (int y = 0; y < 32; y++)
// Clear unused frame's pixels from the buffer.
std::fill(pxdata.begin(), pxdata.end(), 0);
}
for (unsigned int f = 0; f < frames; ++f)
{
for (unsigned int y = 0; y < IMAGE_HEIGHT; ++y)
{
for (int x = 0; x < 32; x++)
for (unsigned int x = 0; x < ANIM_FRAME_WIDTH; ++x)
{
pxdata[y * 96 + x + 32] = icdata[y * 32 + x]; // | 0xFF000000
// NOTE: pxdata is stacked horizontal, anim_data is stacked vertical
pxdata[y * BANNER_WIDTH + f * ANIM_FRAME_WIDTH + x] =
anim_data[f * ANIM_FRAME_WIDTH * IMAGE_HEIGHT + y * IMAGE_HEIGHT + x];
}
}
}
anim_img_strip = wxImageFromMemoryRGBA(pxdata.data(), BANNER_WIDTH, IMAGE_HEIGHT);
}
wxBitmap map = wxBitmapFromMemoryRGBA((u8*)pxdata, 96, 32);
images[i * 2] = list->Add(map);
if (numFrames > 0)
else
{
memset(pxdata, 0, 96 * 32 * 4);
int frames = 3;
if (numFrames < frames)
frames = numFrames;
for (int f = 0; f < frames; f++)
{
for (int y = 0; y < 32; y++)
{
for (int x = 0; x < 32; x++)
{
pxdata[y * 96 + x + 32 * f] = animData[f * 32 * 32 + y * 32 + x];
}
}
}
wxBitmap icon = wxBitmapFromMemoryRGBA((u8*)pxdata, 96, 32);
images[i * 2 + 1] = list->Add(icon);
// No Animation found, use an empty placeholder instead.
anim_img_strip.Create(BANNER_WIDTH, IMAGE_HEIGHT, false);
anim_img_strip.Clear(0xFF);
anim_img_strip.SetMaskColour(0xFF, 0xFF, 0xFF);
}
// Decode Banner if it exists
{
wxImage image;
if (memoryCard[card]->ReadBannerRGBA8(file_index, pxdata.data()))
{
image = wxImageFromMemoryRGBA(pxdata.data(), BANNER_WIDTH, IMAGE_HEIGHT);
}
else
{
// Use first frame of animation instead.
image = anim_img_strip.Size(wxSize(ANIM_FRAME_WIDTH, IMAGE_HEIGHT), wxPoint(0, 0));
}
images[i * 2] = list->Add(WxUtils::ScaleImageToBitmap(image, this, m_image_list_size));
}
images[i * 2 + 1] =
list->Add(WxUtils::ScaleImageToBitmap(anim_img_strip, this, m_image_list_size));
}
int pagesMax = (mcmSettings.usePages) ? (page[card] + 1) * itemsPerPage : 128;

View File

@ -131,5 +131,6 @@ private:
void OnRightClick(wxMouseEvent& event);
};
wxSize m_image_list_size;
CMemcardListCtrl* m_MemcardList[2];
};