fatal: Implement basic text rendering.

This commit is contained in:
Michael Scire 2018-11-13 06:03:30 -08:00
parent 9f6ff2ed6e
commit 8550f722ca
7 changed files with 1541 additions and 10 deletions

View file

@ -34,14 +34,14 @@ ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CFLAGS += $(INCLUDE) -D__SWITCH__ `freetype-config --cflags`
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lstratosphere -lnx
LIBS := `freetype-config --libs` -lstratosphere -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include "fatal_types.hpp"
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "fatal_config.hpp"
#include "fatal_font.hpp"
static u16 *g_fb = nullptr;
static u32 (*g_unswizzle_func)(u32, u32) = nullptr;
static u16 g_font_color = 0xFFFF;
static PlFontData g_font;
static PlFontData g_fonts[PlSharedFontType_Total];
static FT_Library g_library;
static FT_Face g_face;
static FT_Error g_ft_err = 0;
static u16 Blend(u16 color, u16 bg, u8 alpha) {
const u32 c_r = RGB565_GET_R8(color);
const u32 c_g = RGB565_GET_G8(color);
const u32 c_b = RGB565_GET_B8(color);
const u32 b_r = RGB565_GET_R8(bg);
const u32 b_g = RGB565_GET_G8(bg);
const u32 b_b = RGB565_GET_B8(bg);
const u32 r = ((alpha * c_r) + ((0xFF - alpha) * b_r)) / 0xFF;
const u32 g = ((alpha * c_g) + ((0xFF - alpha) * b_g)) / 0xFF;
const u32 b = ((alpha * c_b) + ((0xFF - alpha) * b_b)) / 0xFF;
return RGB888_TO_RGB565(r, g, b);
}
static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) {
u8* imageptr = bitmap->buffer;
if (bitmap->pixel_mode!=FT_PIXEL_MODE_GRAY) return;
for (u32 tmpy = 0; tmpy < bitmap->rows; tmpy++) {
for (u32 tmpx = 0; tmpx < bitmap->width; tmpx++) {
/* Implement very simple blending, as the bitmap value is an alpha value. */
u16 *ptr = &g_fb[g_unswizzle_func(x + tmpx, y + tmpy)];
*ptr = Blend(g_font_color, *ptr, imageptr[tmpx]);
}
imageptr += bitmap->pitch;
}
}
void FontManager::DrawString(u32 x, u32 y, const char *str) {
FT_UInt glyph_index;
FT_GlyphSlot slot = g_face->glyph;
const size_t len = strlen(str);
u32 cur_x = x, cur_y = y;
for (u32 i = 0; i < len; ) {
u32 cur_char;
ssize_t unit_count = decode_utf8(&cur_char, reinterpret_cast<const u8 *>(&str[i]));
if (unit_count <= 0) break;
i += unit_count;
if (cur_char == '\n') {
cur_x = x;
cur_y += g_face->size->metrics.height >> 6;
continue;
}
glyph_index = FT_Get_Char_Index(g_face, cur_char);
g_ft_err = FT_Load_Glyph(g_face, glyph_index, FT_LOAD_DEFAULT);
if (g_ft_err == 0) {
g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL);
}
if (g_ft_err) {
return;
}
DrawGlyph(&slot->bitmap, cur_x + slot->bitmap_left, cur_y - slot->bitmap_top);
cur_x += slot->advance.x >> 6;
cur_y += slot->advance.y >> 6;
}
}
void FontManager::DrawFormat(u32 x, u32 y, const char *format, ...) {
va_list va_arg;
va_start(va_arg, format);
char char_buf[0x400];
vsnprintf(char_buf, sizeof(char_buf), format, va_arg);
DrawString(x, y, char_buf);
}
void FontManager::SetFontColor(u16 color) {
g_font_color = color;
}
void FontManager::ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)) {
g_fb = fb;
g_unswizzle_func = unswizzle_func;
}
Result FontManager::InitializeSharedFont() {
Result rc;
size_t total_fonts = 0;
if (R_FAILED((rc = plGetSharedFont(GetFatalConfig()->language_code, g_fonts, PlSharedFontType_Total, &total_fonts)))) {
return rc;
}
if (R_FAILED((rc = plGetSharedFontByType(&g_font, PlSharedFontType_Standard)))) {
return rc;
}
g_ft_err = FT_Init_FreeType(&g_library);
if (g_ft_err) return g_ft_err;
g_ft_err = FT_New_Memory_Face(g_library, reinterpret_cast<const FT_Byte *>(g_font.address), g_font.size, 0, &g_face);
if (g_ft_err) return g_ft_err;
g_ft_err = FT_Set_Char_Size(g_face, 0, 8*64, 300, 300);
return g_ft_err;
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdarg>
#include <switch.h>
#include <stratosphere.hpp>
#define RGB888_TO_RGB565(r, g, b) ((((r >> 3) << 11) & 0xF800) | (((g >> 2) << 5) & 0x7E0) | ((b >> 3) & 0x1F))
#define RGB565_GET_R8(c) (((c >> 11) & 0x1F) << 3)
#define RGB565_GET_G8(c) (((c >> 5) & 0x3F) << 2)
#define RGB565_GET_B8(c) (((c >> 0) & 0x1F) << 3)
class FontManager {
public:
static Result InitializeSharedFont();
static void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32));
static void SetFontColor(u16 color);
static void DrawString(u32 x, u32 y, const char *str);
static void DrawFormat(u32 x, u32 y, const char *format, ...);
};

View file

@ -28,13 +28,14 @@
#include "fatal_user.hpp"
#include "fatal_config.hpp"
#include "fatal_repair.hpp"
#include "fatal_font.hpp"
extern "C" {
extern u32 __start__;
u32 __nx_applet_type = AppletType_None;
#define INNER_HEAP_SIZE 0x3C0000
#define INNER_HEAP_SIZE 0x280000
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE];
@ -112,11 +113,17 @@ void __appInit(void) {
std::abort();
}
rc = plInitialize();
if (R_FAILED(rc)) {
std::abort();
}
/* fatal cannot throw fatal, so don't do: CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); */
}
void __appExit(void) {
/* Cleanup services. */
plExit();
spsmExit();
psmExit();
lblExit();
@ -134,7 +141,10 @@ int main(int argc, char **argv)
/* Load settings from set:sys. */
InitializeFatalConfig();
/* TODO: Load shared font. */
/* Load shared font. */
if (R_FAILED(FontManager::InitializeSharedFont())) {
std::abort();
}
/* Check whether we should throw fatal due to repair process. */
CheckRepairStatus();

View file

@ -42,21 +42,21 @@ static void RunTaskThreadFunc(void *arg) {
svcExitThread();
}
static void RunTask(IFatalTask *task) {
static void RunTask(IFatalTask *task, u32 stack_size = 0x4000) {
if (g_num_threads >= MaxTasks) {
std::abort();
}
HosThread *cur_thread = &g_task_threads[g_num_threads++];
cur_thread->Initialize(&RunTaskThreadFunc, task, 0x4000, 15);
cur_thread->Initialize(&RunTaskThreadFunc, task, stack_size, 15);
cur_thread->Start();
}
void RunFatalTasks(FatalContext *ctx, u64 title_id, bool error_report, Event *erpt_event, Event *battery_event) {
RunTask(new ErrorReportTask(ctx, title_id, error_report, erpt_event));
RunTask(new PowerControlTask(ctx, title_id, erpt_event, battery_event));
RunTask(new ShowFatalTask(ctx, title_id, battery_event));
RunTask(new ShowFatalTask(ctx, title_id, battery_event), 0x10000);
RunTask(new StopSoundTask(ctx, title_id));
RunTask(new BacklightControlTask(ctx, title_id));
RunTask(new AdjustClockTask(ctx, title_id));

View file

@ -17,6 +17,27 @@
#include <switch.h>
#include "fatal_task_screen.hpp"
#include "fatal_config.hpp"
#include "fatal_font.hpp"
#include "ams_logo.hpp"
static constexpr u32 FatalScreenWidth = 1280;
static constexpr u32 FatalScreenHeight = 720;
static constexpr u32 FatalScreenBpp = 2;
static constexpr u32 FatalScreenWidthAlignedBytes = (FatalScreenWidth * FatalScreenBpp + 63) & ~63;
static constexpr u32 FatalScreenWidthAligned = FatalScreenWidthAlignedBytes / FatalScreenBpp;
u32 GetPixelOffset(uint32_t x, uint32_t y)
{
u32 tmp_pos;
tmp_pos = ((y & 127) / 16) + (x/32*8) + ((y/16/8)*(((FatalScreenWidthAligned/2)/16*8)));
tmp_pos *= 16*16 * 4;
tmp_pos += ((y%16)/8)*512 + ((x%32)/16)*256 + ((y%8)/2)*64 + ((x%16)/8)*32 + (y%2)*16 + (x%8)*2;//This line is a modified version of code from the Tegra X1 datasheet.
return tmp_pos / 2;
}
Result ShowFatalTask::SetupDisplayInternal() {
Result rc;
@ -107,8 +128,8 @@ Result ShowFatalTask::PrepareScreenForDrawing() {
/* Display a layer of 1280 x 720 at 1.5x magnification */
/* NOTE: N uses 2 (770x400) RGBA4444 buffers (tiled buffer + linear). */
/* We use a single 1280x720 tiled RGB565 buffer. */
constexpr u32 raw_width = 1280;
constexpr u32 raw_height = 720;
constexpr u32 raw_width = FatalScreenWidth;
constexpr u32 raw_height = FatalScreenHeight;
constexpr u32 layer_width = ((raw_width) * 3) / 2;
constexpr u32 layer_height = ((raw_height) * 3) / 2;
@ -160,13 +181,25 @@ Result ShowFatalTask::ShowFatal() {
return FatalResult_NullGfxBuffer;
}
/* Let the font manager know about our framebuffer. */
FontManager::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset);
FontManager::SetFontColor(0xFFFF);
/* Draw a background. */
for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) {
tiled_buf[i] = 0x39C9;
}
/* TODO: Actually draw meaningful shit here. */
/* Draw the atmosphere logo in the bottom right corner. */
for (size_t y = 0; y < AMS_LOGO_HEIGHT; y++) {
for (size_t x = 0; x < AMS_LOGO_WIDTH; x++) {
tiled_buf[GetPixelOffset(FatalScreenWidth - AMS_LOGO_WIDTH - 32 + x, FatalScreenHeight - AMS_LOGO_HEIGHT - 32 + y)] = AMS_LOGO_BIN[y * AMS_LOGO_WIDTH + x];
}
}
/* TODO: Actually draw meaningful shit here. */
FontManager::DrawFormat(32, 64, u8"A fatal error occurred: 2%03d-%04d\n", R_MODULE(this->ctx->error_code), R_DESCRIPTION(this->ctx->error_code));
/* Enqueue the buffer. */
framebufferEnd(&fb);