mirror of
https://github.com/wiiu-env/EnvironmentLoader.git
synced 2024-10-31 20:55:04 +01:00
Improve environment selection menu, add option to set a default environment, show menu if no default environment is set.
This commit is contained in:
parent
6cbe000c49
commit
a2f04afd09
4
Makefile
4
Makefile
@ -46,7 +46,7 @@ endif
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lwut -lz
|
||||
LIBS := -lfreetype -lpng -lbz2 -lwut -lz
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
@ -95,7 +95,7 @@ export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
-I$(CURDIR)/$(BUILD) -I$(DEVKITPRO)/portlibs/ppc/include/freetype2
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
|
215
source/main.cpp
215
source/main.cpp
@ -18,6 +18,7 @@
|
||||
#include <malloc.h>
|
||||
#include <vpad/input.h>
|
||||
#include <coreinit/debug.h>
|
||||
#include <fcntl.h>
|
||||
#include "utils/StringTools.h"
|
||||
|
||||
#include "fs/DirList.h"
|
||||
@ -25,11 +26,14 @@
|
||||
#include "ElfUtils.h"
|
||||
#include "kernel.h"
|
||||
#include "common/module_defines.h"
|
||||
#include "utils/DrawUtils.h"
|
||||
|
||||
#define MEMORY_REGION_START 0x00900000
|
||||
#define MEMORY_REGION_SIZE 0x00700000
|
||||
#define MEMORY_REGION_END (MEMORY_REGION_START + MEMORY_REGION_SIZE)
|
||||
|
||||
#define AUTOBOOT_CONFIG_PATH "fs:/vol/external01/wiiu/environments/autoboot.cfg"
|
||||
|
||||
bool CheckRunning() {
|
||||
switch (ProcUIProcessMessages(true)) {
|
||||
case PROCUI_STATUS_EXITING: {
|
||||
@ -52,7 +56,32 @@ bool CheckRunning() {
|
||||
extern "C" uint32_t textStart();
|
||||
|
||||
|
||||
std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads);
|
||||
std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads, int32_t autobootIndex);
|
||||
|
||||
std::optional<std::string> getFileContent(const std::string &path) {
|
||||
DEBUG_FUNCTION_LINE("Read %s", path.c_str());
|
||||
FILE *f = fopen(path.c_str(), "r");
|
||||
if (f) {
|
||||
char buf[128]{};
|
||||
fgets(buf, sizeof(buf), f);
|
||||
fclose(f);
|
||||
|
||||
return std::string(buf);
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("Failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
bool writeFileContent(const std::string &path, const std::string &content) {
|
||||
DEBUG_FUNCTION_LINE("Write to file %s: %s", path.c_str(), content.c_str());
|
||||
FILE *f = fopen(path.c_str(), "w");
|
||||
if (f) {
|
||||
fputs(content.c_str(), f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifdef DEBUG
|
||||
@ -86,6 +115,26 @@ int main(int argc, char **argv) {
|
||||
if (strncmp(environmentPath, "fs:/vol/external01/wiiu/environments/", strlen("fs:/vol/external01/wiiu/environments/")) != 0) {
|
||||
DirList environmentDirs("fs:/vol/external01/wiiu/environments/", nullptr, DirList::Dirs, 1);
|
||||
|
||||
bool foundFromConfig = false;
|
||||
bool forceMenu = true;
|
||||
auto res = getFileContent(AUTOBOOT_CONFIG_PATH);
|
||||
auto autobootIndex = -1;
|
||||
if (res) {
|
||||
DEBUG_FUNCTION_LINE("Got result %s", res->c_str());
|
||||
for (int i = 0; i < environmentDirs.GetFilecount(); i++) {
|
||||
if (environmentDirs.GetFilename(i) == res.value()) {
|
||||
DEBUG_FUNCTION_LINE("Found environment %s from config at index %d", res.value().c_str(), i);
|
||||
autobootIndex = i;
|
||||
environment_path = environmentDirs.GetFilepath(i);
|
||||
foundFromConfig = true;
|
||||
forceMenu = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("No config found");
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> environmentPaths;
|
||||
for (int i = 0; i < environmentDirs.GetFilecount(); i++) {
|
||||
environmentPaths[environmentDirs.GetFilename(i)] = environmentDirs.GetFilepath(i);
|
||||
@ -100,10 +149,9 @@ int main(int argc, char **argv) {
|
||||
btn = vpad_data.hold | vpad_data.trigger;
|
||||
}
|
||||
|
||||
environment_path = "fs:/vol/external01/wiiu/environments/default";
|
||||
|
||||
if ((btn & VPAD_BUTTON_X) == VPAD_BUTTON_X) {
|
||||
environment_path = EnvironmentSelectionScreen(environmentPaths);
|
||||
if (forceMenu || (btn & VPAD_BUTTON_X) == VPAD_BUTTON_X) {
|
||||
DEBUG_FUNCTION_LINE("Open menu!");
|
||||
environment_path = EnvironmentSelectionScreen(environmentPaths, autobootIndex);
|
||||
DEBUG_FUNCTION_LINE("Selected %s", environment_path.c_str());
|
||||
}
|
||||
}
|
||||
@ -152,80 +200,131 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads) {
|
||||
// Init screen and screen buffers
|
||||
#define COLOR_WHITE Color(0xffffffff)
|
||||
#define COLOR_BLACK Color(0, 0, 0, 255)
|
||||
#define COLOR_BACKGROUND Color(0, 40, 100, 255)
|
||||
#define COLOR_TEXT COLOR_WHITE
|
||||
#define COLOR_TEXT2 Color(0xB3ffffff)
|
||||
#define COLOR_AUTOBOOT Color(0xaeea00ff)
|
||||
#define COLOR_BORDER Color(204, 204, 204, 255)
|
||||
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff)
|
||||
|
||||
|
||||
std::string EnvironmentSelectionScreen(const std::map<std::string, std::string> &payloads, int32_t autobootIndex) {
|
||||
OSScreenInit();
|
||||
uint32_t screen_buf0_size = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t screen_buf1_size = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
auto *screenBuffer = (uint8_t *) memalign(0x100, screen_buf0_size + screen_buf1_size);
|
||||
OSScreenSetBufferEx(SCREEN_TV, (void *) screenBuffer);
|
||||
OSScreenSetBufferEx(SCREEN_DRC, (void *) (screenBuffer + screen_buf0_size));
|
||||
|
||||
OSScreenEnableEx(SCREEN_TV, 1);
|
||||
OSScreenEnableEx(SCREEN_DRC, 1);
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
|
||||
// Clear screens
|
||||
OSScreenClearBufferEx(SCREEN_TV, 0);
|
||||
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
||||
uint8_t *screenBuffer = (uint8_t *) memalign(0x100, tvBufferSize + drcBufferSize);
|
||||
|
||||
OSScreenFlipBuffersEx(SCREEN_TV);
|
||||
OSScreenFlipBuffersEx(SCREEN_DRC);
|
||||
OSScreenSetBufferEx(SCREEN_TV, screenBuffer);
|
||||
OSScreenSetBufferEx(SCREEN_DRC, screenBuffer + tvBufferSize);
|
||||
|
||||
VPADStatus vpad_data;
|
||||
VPADReadError error;
|
||||
int selected = 0;
|
||||
std::string header = "Please choose your environment:";
|
||||
OSScreenEnableEx(SCREEN_TV, TRUE);
|
||||
OSScreenEnableEx(SCREEN_DRC, TRUE);
|
||||
|
||||
DrawUtils::initBuffers(screenBuffer, tvBufferSize, screenBuffer + tvBufferSize, drcBufferSize);
|
||||
DrawUtils::initFont();
|
||||
|
||||
uint32_t selected = 0;
|
||||
int autoBoot = autobootIndex;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Time to draw");
|
||||
|
||||
bool redraw = true;
|
||||
while (true) {
|
||||
// Clear screens
|
||||
OSScreenClearBufferEx(SCREEN_TV, 0);
|
||||
OSScreenClearBufferEx(SCREEN_DRC, 0);
|
||||
VPADStatus vpad{};
|
||||
VPADRead(VPAD_CHAN_0, &vpad, 1, NULL);
|
||||
|
||||
int pos = 0;
|
||||
if (vpad.trigger & VPAD_BUTTON_UP) {
|
||||
if (selected > 0) {
|
||||
selected--;
|
||||
redraw = true;
|
||||
}
|
||||
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (selected < payloads.size() - 1) {
|
||||
selected++;
|
||||
redraw = true;
|
||||
}
|
||||
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
||||
break;
|
||||
} else if (vpad.trigger & VPAD_BUTTON_X) {
|
||||
autoBoot = -1;
|
||||
redraw = true;
|
||||
} else if (vpad.trigger & VPAD_BUTTON_Y) {
|
||||
autoBoot = selected;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
OSScreenPutFontEx(SCREEN_TV, 0, pos, header.c_str());
|
||||
OSScreenPutFontEx(SCREEN_DRC, 0, pos, header.c_str());
|
||||
if (redraw) {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
|
||||
pos += 2;
|
||||
// draw buttons
|
||||
uint32_t index = 8 + 24 + 8 + 4;
|
||||
uint32_t i = 0;
|
||||
for (auto const&[key, val]: payloads) {
|
||||
if (i == selected) {
|
||||
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
|
||||
} else {
|
||||
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, (i == autoBoot) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
||||
}
|
||||
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::setFontColor((i == autoBoot) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
||||
DrawUtils::print(16 * 2, index + 8 + 24, key.c_str());
|
||||
index += 42 + 8;
|
||||
i++;
|
||||
}
|
||||
|
||||
DrawUtils::setFontColor(COLOR_TEXT);
|
||||
|
||||
// draw top bar
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::print(16, 6 + 24, "Environment Loader");
|
||||
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
|
||||
// draw bottom bar
|
||||
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
DrawUtils::setFontSize(18);
|
||||
DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
|
||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue000 Choose", true);
|
||||
const char *autobootHints = "\ue002 Clear Default / \ue003 Select Default";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, true);
|
||||
|
||||
DrawUtils::endDraw();
|
||||
|
||||
redraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
DrawUtils::clear(COLOR_BLACK);
|
||||
DrawUtils::endDraw();
|
||||
|
||||
DrawUtils::deinitFont();
|
||||
|
||||
free(screenBuffer);
|
||||
|
||||
if (autoBoot != autobootIndex) {
|
||||
int i = 0;
|
||||
for (auto const&[key, val]: payloads) {
|
||||
std::string text = StringTools::strfmt("%s %s", i == selected ? "> " : " ", key.c_str());
|
||||
OSScreenPutFontEx(SCREEN_TV, 0, pos, text.c_str());
|
||||
OSScreenPutFontEx(SCREEN_DRC, 0, pos, text.c_str());
|
||||
if (i == autoBoot) {
|
||||
DEBUG_FUNCTION_LINE("Save config");
|
||||
writeFileContent(AUTOBOOT_CONFIG_PATH, key);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
pos++;
|
||||
}
|
||||
|
||||
VPADRead(VPAD_CHAN_0, &vpad_data, 1, &error);
|
||||
if (vpad_data.trigger == VPAD_BUTTON_A) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (vpad_data.trigger == VPAD_BUTTON_UP) {
|
||||
selected--;
|
||||
if (selected < 0) {
|
||||
selected = 0;
|
||||
}
|
||||
} else if (vpad_data.trigger == VPAD_BUTTON_DOWN) {
|
||||
selected++;
|
||||
if ((uint32_t) selected >= payloads.size()) {
|
||||
selected = payloads.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
OSScreenFlipBuffersEx(SCREEN_TV);
|
||||
OSScreenFlipBuffersEx(SCREEN_DRC);
|
||||
|
||||
OSSleepTicks(OSMillisecondsToTicks(16));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (auto const&[key, val]: payloads) {
|
||||
if (i == selected) {
|
||||
free(screenBuffer);
|
||||
return val;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
free(screenBuffer);
|
||||
|
||||
return "";
|
||||
}
|
332
source/utils/DrawUtils.cpp
Normal file
332
source/utils/DrawUtils.cpp
Normal file
@ -0,0 +1,332 @@
|
||||
#include "DrawUtils.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <coreinit/memory.h>
|
||||
#include <coreinit/screen.h>
|
||||
#include <coreinit/cache.h>
|
||||
#include <png.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
// buffer width
|
||||
#define TV_WIDTH 0x500
|
||||
#define DRC_WIDTH 0x380
|
||||
|
||||
bool DrawUtils::isBackBuffer;
|
||||
|
||||
uint8_t* DrawUtils::tvBuffer = nullptr;
|
||||
uint32_t DrawUtils::tvSize = 0;
|
||||
uint8_t* DrawUtils::drcBuffer = nullptr;
|
||||
uint32_t DrawUtils::drcSize = 0;
|
||||
|
||||
// Don't put those into the class or we have to include ft everywhere
|
||||
static FT_Library ft_lib = nullptr;
|
||||
static FT_Face ft_face = nullptr;
|
||||
static Color font_col = {0xFFFFFFFF};
|
||||
|
||||
void DrawUtils::initBuffers(void* tvBuffer, uint32_t tvSize, void* drcBuffer, uint32_t drcSize)
|
||||
{
|
||||
DrawUtils::tvBuffer = (uint8_t*) tvBuffer;
|
||||
DrawUtils::tvSize = tvSize;
|
||||
DrawUtils::drcBuffer = (uint8_t*) drcBuffer;
|
||||
DrawUtils::drcSize = drcSize;
|
||||
}
|
||||
|
||||
void DrawUtils::beginDraw()
|
||||
{
|
||||
uint32_t pixel = *(uint32_t*) tvBuffer;
|
||||
|
||||
// check which buffer is currently used
|
||||
OSScreenPutPixelEx(SCREEN_TV, 0, 0, 0xABCDEF90);
|
||||
if (*(uint32_t*) tvBuffer == 0xABCDEF90) {
|
||||
isBackBuffer = false;
|
||||
} else {
|
||||
isBackBuffer = true;
|
||||
}
|
||||
|
||||
// restore the pixel we used for checking
|
||||
*(uint32_t*) tvBuffer = pixel;
|
||||
}
|
||||
|
||||
void DrawUtils::endDraw()
|
||||
{
|
||||
// OSScreenFlipBuffersEx already flushes the cache?
|
||||
// DCFlushRange(tvBuffer, tvSize);
|
||||
// DCFlushRange(drcBuffer, drcSize);
|
||||
|
||||
OSScreenFlipBuffersEx(SCREEN_DRC);
|
||||
OSScreenFlipBuffersEx(SCREEN_TV);
|
||||
}
|
||||
|
||||
void DrawUtils::clear(Color col)
|
||||
{
|
||||
OSScreenClearBufferEx(SCREEN_TV, col.color);
|
||||
OSScreenClearBufferEx(SCREEN_DRC, col.color);
|
||||
}
|
||||
|
||||
void DrawUtils::drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
float opacity = a / 255.0f;
|
||||
|
||||
// put pixel in the drc buffer
|
||||
uint32_t i = (x + y * DRC_WIDTH) * 4;
|
||||
if (i + 3 < drcSize / 2) {
|
||||
if (isBackBuffer) {
|
||||
i += drcSize / 2;
|
||||
}
|
||||
if (a == 0xFF) {
|
||||
drcBuffer[i ] = r;
|
||||
drcBuffer[i + 1] = g;
|
||||
drcBuffer[i + 2] = b;
|
||||
}
|
||||
else {
|
||||
drcBuffer[i ] = r * opacity + drcBuffer[i ] * (1 - opacity);
|
||||
drcBuffer[i + 1] = g * opacity + drcBuffer[i + 1] * (1 - opacity);
|
||||
drcBuffer[i + 2] = b * opacity + drcBuffer[i + 2] * (1 - opacity);
|
||||
}
|
||||
}
|
||||
|
||||
// scale and put pixel in the tv buffer
|
||||
for (uint32_t yy = (y * 1.5); yy < ((y * 1.5) + 1); yy++) {
|
||||
for (uint32_t xx = (x * 1.5); xx < ((x * 1.5) + 1); xx++) {
|
||||
uint32_t i = (xx + yy * TV_WIDTH) * 4;
|
||||
if (i + 3 < tvSize / 2) {
|
||||
if (isBackBuffer) {
|
||||
i += tvSize / 2;
|
||||
}
|
||||
if (a == 0xFF) {
|
||||
tvBuffer[i ] = r;
|
||||
tvBuffer[i + 1] = g;
|
||||
tvBuffer[i + 2] = b;
|
||||
}
|
||||
else {
|
||||
tvBuffer[i ] = r * opacity + tvBuffer[i ] * (1 - opacity);
|
||||
tvBuffer[i + 1] = g * opacity + tvBuffer[i + 1] * (1 - opacity);
|
||||
tvBuffer[i + 2] = b * opacity + tvBuffer[i + 2] * (1 - opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtils::drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col)
|
||||
{
|
||||
for (uint32_t yy = y; yy < y + h; yy++) {
|
||||
for (uint32_t xx = x; xx < x + w; xx++) {
|
||||
drawPixel(xx, yy, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtils::drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col)
|
||||
{
|
||||
drawRectFilled(x, y, w, borderSize, col);
|
||||
drawRectFilled(x, y + h - borderSize, w, borderSize, col);
|
||||
drawRectFilled(x, y, borderSize, h, col);
|
||||
drawRectFilled(x + w - borderSize, y, borderSize, h, col);
|
||||
}
|
||||
|
||||
void DrawUtils::drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t* data)
|
||||
{
|
||||
if ( data[0] != 'B' || data[1] != 'M' ) {
|
||||
// invalid header
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t dataPos = __builtin_bswap32(*(uint32_t*)&(data[0x0A]));
|
||||
uint32_t width = __builtin_bswap32(*(uint32_t*)&(data[0x12]));
|
||||
uint32_t height = __builtin_bswap32(*(uint32_t*)&(data[0x16]));
|
||||
|
||||
if (dataPos == 0) {
|
||||
dataPos = 54;
|
||||
}
|
||||
|
||||
data += dataPos;
|
||||
|
||||
// TODO flip image since bitmaps are stored upside down
|
||||
|
||||
for (uint32_t yy = y; yy < y + target_height; yy++) {
|
||||
for (uint32_t xx = x; xx < x + target_width; xx++) {
|
||||
uint32_t i = (((xx - x) * width / target_width) + ((yy - y) * height / target_height) * width) * 3;
|
||||
drawPixel(xx, yy, data[i + 2], data[i + 1], data[i], 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void png_read_data(png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead)
|
||||
{
|
||||
void** data = (void**) png_get_io_ptr(png_ptr);
|
||||
|
||||
memcpy(outBytes, *data, byteCountToRead);
|
||||
*((uint8_t**) data) += byteCountToRead;
|
||||
}
|
||||
|
||||
void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t* data)
|
||||
{
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if(png_ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if(info_ptr == NULL) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
png_set_read_fn(png_ptr, (void*) &data, png_read_data);
|
||||
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
int bitDepth = 0;
|
||||
int colorType = -1;
|
||||
uint32_t retval = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
|
||||
if(retval != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t bytesPerRow = png_get_rowbytes(png_ptr, info_ptr);
|
||||
uint8_t* rowData = new uint8_t[bytesPerRow];
|
||||
|
||||
for(uint32_t yy = y; yy < y + height; yy++) {
|
||||
png_read_row(png_ptr, (png_bytep)rowData, NULL);
|
||||
|
||||
for (uint32_t xx = x; xx < x + width; xx++) {
|
||||
if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||
uint32_t i = (xx - x) * 4;
|
||||
drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], rowData[i + 3]);
|
||||
}
|
||||
else if (colorType == PNG_COLOR_TYPE_RGB) {
|
||||
uint32_t i = (xx - x) * 3;
|
||||
drawPixel(xx, yy, rowData[i], rowData[i + 1], rowData[i + 2], 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] rowData;
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
}
|
||||
|
||||
void DrawUtils::initFont()
|
||||
{
|
||||
void* font = NULL;
|
||||
uint32_t size = 0;
|
||||
OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size);
|
||||
|
||||
if (font && size) {
|
||||
FT_Init_FreeType(&ft_lib);
|
||||
FT_New_Memory_Face(ft_lib, (FT_Byte*) font, size, 0, &ft_face);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtils::deinitFont()
|
||||
{
|
||||
FT_Done_Face(ft_face);
|
||||
FT_Done_FreeType(ft_lib);
|
||||
}
|
||||
|
||||
void DrawUtils::setFontSize(uint32_t size)
|
||||
{
|
||||
FT_Set_Pixel_Sizes(ft_face, 0, size);
|
||||
}
|
||||
|
||||
void DrawUtils::setFontColor(Color col)
|
||||
{
|
||||
font_col = col;
|
||||
}
|
||||
|
||||
static void draw_freetype_bitmap(FT_Bitmap* bitmap, FT_Int x, FT_Int y)
|
||||
{
|
||||
FT_Int i, j, p, q;
|
||||
FT_Int x_max = x + bitmap->width;
|
||||
FT_Int y_max = y + bitmap->rows;
|
||||
|
||||
for (i = x, p = 0; i < x_max; i++, p++) {
|
||||
for (j = y, q = 0; j < y_max; j++, q++) {
|
||||
if (i < 0 || j < 0 || i >= SCREEN_WIDTH || j >= SCREEN_HEIGHT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float opacity = bitmap->buffer[q * bitmap->pitch + p] / 255.0f;
|
||||
DrawUtils::drawPixel(i, j, font_col.r, font_col.g, font_col.b, font_col.a * opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtils::print(uint32_t x, uint32_t y, const char* string, bool alignRight)
|
||||
{
|
||||
wchar_t* buffer = new wchar_t[strlen(string) + 1];
|
||||
|
||||
size_t num = mbstowcs(buffer, string, strlen(string));
|
||||
if (num > 0) {
|
||||
buffer[num] = 0;
|
||||
}
|
||||
else {
|
||||
wchar_t* tmp = buffer;
|
||||
while ((*tmp++ = *string++));
|
||||
}
|
||||
|
||||
print(x, y, buffer, alignRight);
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
void DrawUtils::print(uint32_t x, uint32_t y, const wchar_t* string, bool alignRight)
|
||||
{
|
||||
FT_GlyphSlot slot = ft_face->glyph;
|
||||
FT_Vector pen = {(int) x, (int) y};
|
||||
|
||||
if (alignRight) {
|
||||
pen.x -= getTextWidth(string);
|
||||
}
|
||||
|
||||
for (; *string; string++) {
|
||||
uint32_t charcode = *string;
|
||||
|
||||
if (charcode == '\n') {
|
||||
pen.y += ft_face->size->metrics.height >> 6;
|
||||
pen.x = x;
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, charcode), FT_LOAD_DEFAULT);
|
||||
FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
|
||||
|
||||
draw_freetype_bitmap(&slot->bitmap, pen.x + slot->bitmap_left, pen.y - slot->bitmap_top);
|
||||
pen.x += slot->advance.x >> 6;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DrawUtils::getTextWidth(const char* string)
|
||||
{
|
||||
wchar_t* buffer = new wchar_t[strlen(string) + 1];
|
||||
|
||||
size_t num = mbstowcs(buffer, string, strlen(string));
|
||||
if (num > 0) {
|
||||
buffer[num] = 0;
|
||||
}
|
||||
else {
|
||||
wchar_t* tmp = buffer;
|
||||
while ((*tmp++ = *string++));
|
||||
}
|
||||
|
||||
uint32_t width = getTextWidth(buffer);
|
||||
delete[] buffer;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
uint32_t DrawUtils::getTextWidth(const wchar_t* string)
|
||||
{
|
||||
FT_GlyphSlot slot = ft_face->glyph;
|
||||
uint32_t width = 0;
|
||||
|
||||
for (; *string; string++) {
|
||||
FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, *string), FT_LOAD_BITMAP_METRICS_ONLY);
|
||||
|
||||
width += slot->advance.x >> 6;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
57
source/utils/DrawUtils.h
Normal file
57
source/utils/DrawUtils.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// visible screen sizes
|
||||
#define SCREEN_WIDTH 854
|
||||
#define SCREEN_HEIGHT 480
|
||||
|
||||
union Color {
|
||||
Color(uint32_t color) {
|
||||
this->color = color;
|
||||
}
|
||||
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
this->r = r; this->g = g; this->b = b; this->a = a;
|
||||
}
|
||||
|
||||
uint32_t color;
|
||||
struct {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
};
|
||||
|
||||
class DrawUtils {
|
||||
public:
|
||||
static void initBuffers(void* tvBuffer, uint32_t tvSize, void* drcBuffer, uint32_t drcSize);
|
||||
static void beginDraw();
|
||||
static void endDraw();
|
||||
static void clear(Color col);
|
||||
static void drawPixel(uint32_t x, uint32_t y, Color col) { drawPixel(x, y, col.r, col.g, col.b, col.a); }
|
||||
static void drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
|
||||
static void drawRectFilled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, Color col);
|
||||
static void drawRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, Color col);
|
||||
|
||||
static void drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32_t target_height, const uint8_t* data);
|
||||
static void drawPNG(uint32_t x, uint32_t y, const uint8_t* data);
|
||||
|
||||
static void initFont();
|
||||
static void deinitFont();
|
||||
static void setFontSize(uint32_t size);
|
||||
static void setFontColor(Color col);
|
||||
static void print(uint32_t x, uint32_t y, const char* string, bool alignRight = false);
|
||||
static void print(uint32_t x, uint32_t y, const wchar_t* string, bool alignRight = false);
|
||||
static uint32_t getTextWidth(const char* string);
|
||||
static uint32_t getTextWidth(const wchar_t* string);
|
||||
|
||||
private:
|
||||
static bool isBackBuffer;
|
||||
|
||||
static uint8_t* tvBuffer;
|
||||
static uint32_t tvSize;
|
||||
static uint8_t* drcBuffer;
|
||||
static uint32_t drcSize;
|
||||
};
|
Loading…
Reference in New Issue
Block a user