mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2026-03-21 05:49:26 +00:00
300 lines
9.0 KiB
C
300 lines
9.0 KiB
C
/**
|
|
* Copyright (c) 2017-2018 Tara Keeling
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#include "gds_private.h"
|
|
#include "gds.h"
|
|
#include "gds_font.h"
|
|
#include "gds_draw.h"
|
|
#include "gds_err.h"
|
|
#include "esp_spiffs.h"
|
|
#include "esp_log.h"
|
|
#include "esp_heap_caps.h"
|
|
#include "tools.h"
|
|
static const char* TAG = "gds_font";
|
|
struct GDS_FontDef* Font_droid_sans_fallback_11x13 = NULL;
|
|
struct GDS_FontDef* Font_line_1 = NULL;
|
|
struct GDS_FontDef* Font_line_2 = NULL;
|
|
|
|
// struct GDS_FontDef * Font_droid_sans_fallback_15x17 = NULL;
|
|
// struct GDS_FontDef * Font_droid_sans_fallback_24x28 = NULL;
|
|
// struct GDS_FontDef * Font_droid_sans_mono_7x13 = NULL;
|
|
// struct GDS_FontDef * Font_droid_sans_mono_13x24 = NULL;
|
|
// struct GDS_FontDef * Font_droid_sans_mono_16x31 = NULL;
|
|
// struct GDS_FontDef * Font_liberation_mono_9x15 = NULL;
|
|
// struct GDS_FontDef * Font_liberation_mono_13x21 = NULL;
|
|
// struct GDS_FontDef * Font_liberation_mono_17x30 = NULL;
|
|
// struct GDS_FontDef * Font_Tarable7Seg_16x32 = NULL;
|
|
// struct GDS_FontDef * Font_Tarable7Seg_32x64 = NULL;
|
|
|
|
static bool LoadFont(struct GDS_FontDef** fontPtr, const char* fileName) {
|
|
if(!fontPtr) {
|
|
ESP_LOGE(TAG, "Invalid pointer for LoadFont");
|
|
return false;
|
|
}
|
|
char font_file_name[CONFIG_SPIFFS_OBJ_NAME_LEN + 1] = {0};
|
|
snprintf(font_file_name, sizeof(font_file_name), "/spiffs/fonts/%s", fileName);
|
|
// Allocate DMA-capable memory for the font
|
|
struct GDS_FontDef* loadedFont = load_file_dma(NULL, font_file_name);
|
|
|
|
// Check if allocation succeeded
|
|
if(loadedFont == NULL) {
|
|
ESP_LOGE(TAG, "Failed to load font");
|
|
return false;
|
|
}
|
|
// Update the pointer
|
|
*fontPtr = loadedFont;
|
|
|
|
ESP_LOGI(TAG, "Successfully loaded font: %s", fileName);
|
|
return true;
|
|
}
|
|
bool gds_init_fonts() {
|
|
bool success = true;
|
|
|
|
// Load the Font_droid_sans_fallback_11x13
|
|
if(!LoadFont(&Font_droid_sans_fallback_11x13, "droid_sans_fb_11x13.bin")) { success = false; }
|
|
|
|
// Load the Font_line_1
|
|
if(!LoadFont(&Font_line_1, "line_1.bin")) { success = false; }
|
|
|
|
// Load the Font_line_2
|
|
if(!LoadFont(&Font_line_2, "line_2.bin")) { success = false; }
|
|
|
|
return success;
|
|
}
|
|
static int RoundUpFontHeight(const struct GDS_FontDef* Font) {
|
|
int Height = Font->Height;
|
|
|
|
if((Height % 8) != 0) { return ((Height + 7) / 8) * 8; }
|
|
|
|
return Height;
|
|
}
|
|
|
|
static const uint8_t* GetCharPtr(const struct GDS_FontDef* Font, char Character) {
|
|
return &Font->FontData[(Character - Font->StartChar) * ((Font->Width * (RoundUpFontHeight(Font) / 8)) + 1)];
|
|
}
|
|
|
|
void GDS_FontDrawChar(struct GDS_Device* Device, char Character, int x, int y, int Color) {
|
|
const uint8_t* GlyphData = NULL;
|
|
int GlyphColumnLen = 0;
|
|
int CharStartX = 0;
|
|
int CharStartY = 0;
|
|
int CharWidth = 0;
|
|
int CharHeight = 0;
|
|
int CharEndX = 0;
|
|
int CharEndY = 0;
|
|
int OffsetX = 0;
|
|
int OffsetY = 0;
|
|
int YByte = 0;
|
|
int YBit = 0;
|
|
int i = 0;
|
|
|
|
NullCheck((GlyphData = GetCharPtr(Device->Font, Character)), return);
|
|
|
|
if(Character >= Device->Font->StartChar && Character <= Device->Font->EndChar) {
|
|
/* The first byte in the glyph data is the width of the character in pixels, skip over */
|
|
GlyphData++;
|
|
GlyphColumnLen = RoundUpFontHeight(Device->Font) / 8;
|
|
|
|
CharWidth = GDS_FontGetCharWidth(Device, Character);
|
|
CharHeight = GDS_FontGetHeight(Device);
|
|
|
|
CharStartX = x;
|
|
CharStartY = y;
|
|
|
|
CharEndX = CharStartX + CharWidth;
|
|
CharEndY = CharStartY + CharHeight;
|
|
|
|
/* If the character is partially offscreen offset the end by
|
|
* distance between (coord) and 0.
|
|
*/
|
|
OffsetX = (CharStartX < 0) ? abs(CharStartX) : 0;
|
|
OffsetY = (CharStartY < 0) ? abs(CharStartY) : 0;
|
|
|
|
/* This skips into the proper column within the glyph data */
|
|
GlyphData += (OffsetX * GlyphColumnLen);
|
|
|
|
CharStartX += OffsetX;
|
|
CharStartY += OffsetY;
|
|
|
|
/* Do not attempt to draw if this character is entirely offscreen */
|
|
if(CharEndX < 0 || CharStartX >= Device->TextWidth || CharEndY < 0 || CharStartY >= Device->Height) {
|
|
ClipDebug(x, y);
|
|
return;
|
|
}
|
|
|
|
/* Do not attempt to draw past the end of the screen */
|
|
CharEndX = (CharEndX >= Device->TextWidth) ? Device->TextWidth - 1 : CharEndX;
|
|
CharEndY = (CharEndY >= Device->Height) ? Device->Height - 1 : CharEndY;
|
|
Device->Dirty = true;
|
|
|
|
for(x = CharStartX; x < CharEndX; x++) {
|
|
for(y = CharStartY, i = 0; y < CharEndY && i < CharHeight; y++, i++) {
|
|
YByte = (i + OffsetY) / 8;
|
|
YBit = (i + OffsetY) & 0x07;
|
|
|
|
if(GlyphData[YByte] & BIT(YBit)) { DrawPixel(Device, x, y, Color); }
|
|
}
|
|
|
|
GlyphData += GlyphColumnLen;
|
|
}
|
|
}
|
|
}
|
|
|
|
const struct GDS_FontDef* GDS_SetFont(struct GDS_Device* Display, const struct GDS_FontDef* Font) {
|
|
const struct GDS_FontDef* OldFont = Display->Font;
|
|
|
|
Display->FontForceProportional = false;
|
|
Display->FontForceMonospace = false;
|
|
Display->Font = Font;
|
|
|
|
return OldFont;
|
|
}
|
|
|
|
void GDS_FontForceProportional(struct GDS_Device* Display, bool Force) { Display->FontForceProportional = Force; }
|
|
|
|
void GDS_FontForceMonospace(struct GDS_Device* Display, bool Force) { Display->FontForceMonospace = Force; }
|
|
|
|
int GDS_FontGetWidth(struct GDS_Device* Display) { return Display->Font->Width; }
|
|
|
|
int GDS_FontGetHeight(struct GDS_Device* Display) { return Display->Font->Height; }
|
|
|
|
int GDS_FontGetCharWidth(struct GDS_Device* Display, char Character) {
|
|
const uint8_t* CharPtr = NULL;
|
|
int Width = 0;
|
|
|
|
if(Character >= Display->Font->StartChar && Character <= Display->Font->EndChar) {
|
|
CharPtr = GetCharPtr(Display->Font, Character);
|
|
Width = (Display->Font->Monospace == true) ? Display->Font->Width : *CharPtr;
|
|
if(Display->FontForceMonospace == true) { Width = Display->Font->Width; }
|
|
|
|
if(Display->FontForceProportional == true) { Width = *CharPtr; }
|
|
}
|
|
|
|
return Width;
|
|
}
|
|
|
|
int GDS_FontGetMaxCharsPerRow(struct GDS_Device* Display) { return Display->TextWidth / Display->Font->Width; }
|
|
|
|
int GDS_FontGetMaxCharsPerColumn(struct GDS_Device* Display) { return Display->Height / Display->Font->Height; }
|
|
|
|
int GDS_FontGetCharHeight(struct GDS_Device* Display) { return Display->Font->Height; }
|
|
|
|
int GDS_FontMeasureString(struct GDS_Device* Display, const char* Text) {
|
|
int Width = 0;
|
|
int Len = 0;
|
|
|
|
NullCheck(Text, return 0);
|
|
|
|
for(Len = strlen(Text); Len >= 0; Len--, Text++) {
|
|
if(*Text >= Display->Font->StartChar && *Text <= Display->Font->EndChar) { Width += GDS_FontGetCharWidth(Display, *Text); }
|
|
}
|
|
return Width;
|
|
}
|
|
|
|
void GDS_FontDrawString(struct GDS_Device* Display, int x, int y, const char* Text, int Color) {
|
|
int Len = 0;
|
|
int i = 0;
|
|
|
|
NullCheck(Text, return);
|
|
|
|
for(Len = strlen(Text), i = 0; i < Len; i++) {
|
|
GDS_FontDrawChar(Display, *Text, x, y, Color);
|
|
|
|
x += GDS_FontGetCharWidth(Display, *Text);
|
|
Text++;
|
|
}
|
|
}
|
|
|
|
void GDS_FontDrawAnchoredString(struct GDS_Device* Display, TextAnchor Anchor, const char* Text, int Color) {
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
NullCheck(Text, return);
|
|
|
|
GDS_FontGetAnchoredStringCoords(Display, &x, &y, Anchor, Text);
|
|
GDS_FontDrawString(Display, x, y, Text, Color);
|
|
}
|
|
|
|
void GDS_FontGetAnchoredStringCoords(struct GDS_Device* Display, int* OutX, int* OutY, TextAnchor Anchor, const char* Text) {
|
|
int StringWidth = 0;
|
|
int StringHeight = 0;
|
|
|
|
NullCheck(OutX, return);
|
|
NullCheck(OutY, return);
|
|
NullCheck(Text, return);
|
|
|
|
StringWidth = GDS_FontMeasureString(Display, Text);
|
|
StringHeight = GDS_FontGetCharHeight(Display);
|
|
|
|
switch(Anchor) {
|
|
case TextAnchor_East: {
|
|
*OutY = (Display->Height / 2) - (StringHeight / 2);
|
|
*OutX = (Display->TextWidth - StringWidth);
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_West: {
|
|
*OutY = (Display->Height / 2) - (StringHeight / 2);
|
|
*OutX = 0;
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_North: {
|
|
*OutX = (Display->TextWidth / 2) - (StringWidth / 2);
|
|
*OutY = 0;
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_South: {
|
|
*OutX = (Display->TextWidth / 2) - (StringWidth / 2);
|
|
*OutY = (Display->Height - StringHeight);
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_NorthEast: {
|
|
*OutX = (Display->TextWidth - StringWidth);
|
|
*OutY = 0;
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_NorthWest: {
|
|
*OutY = 0;
|
|
*OutX = 0;
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_SouthEast: {
|
|
*OutY = (Display->Height - StringHeight);
|
|
*OutX = (Display->TextWidth - StringWidth);
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_SouthWest: {
|
|
*OutY = (Display->Height - StringHeight);
|
|
*OutX = 0;
|
|
|
|
break;
|
|
}
|
|
case TextAnchor_Center: {
|
|
*OutY = (Display->Height / 2) - (StringHeight / 2);
|
|
*OutX = (Display->TextWidth / 2) - (StringWidth / 2);
|
|
|
|
break;
|
|
}
|
|
default: {
|
|
*OutX = 128;
|
|
*OutY = 64;
|
|
|
|
break;
|
|
}
|
|
};
|
|
}
|