/* * Copyright (c) 2009-2021 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*============================ INCLUDES ======================================*/ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <stdarg.h> #include "arm_extra_lcd_printf.h" #include "arm_2d.h" #include "__common.h" #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunknown-warning-option" # pragma clang diagnostic ignored "-Wreserved-identifier" # pragma clang diagnostic ignored "-Wdeclaration-after-statement" # pragma clang diagnostic ignored "-Wformat-nonliteral" # pragma clang diagnostic ignored "-Wsign-compare" # pragma clang diagnostic ignored "-Wmissing-prototypes" # pragma clang diagnostic ignored "-Wcast-qual" # pragma clang diagnostic ignored "-Wsign-conversion" # pragma clang diagnostic ignored "-Wpedantic" # pragma clang diagnostic ignored "-Wimplicit-int-conversion" # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wunreachable-code" #endif /*============================ MACROS ========================================*/ #ifndef __LCD_PRINTF_CFG_TEXT_BUFFER_SIZE__ # define __LCD_PRINTF_CFG_TEXT_BUFFER_SIZE__ 256 #endif #undef this #define this (*ptThis) /*============================ MACROFIED FUNCTIONS ===========================*/ /*============================ TYPES =========================================*/ /*============================ GLOBAL VARIABLES ==============================*/ extern const uint8_t Font_6x8_h[(144-32)*8]; /*============================ PROTOTYPES ====================================*/ /*============================ LOCAL VARIABLES ===============================*/ static struct { arm_2d_region_t tScreen; arm_2d_region_t tRegion; arm_2d_location_t tDrawOffset; struct { COLOUR_INT_TYPE tForeground; COLOUR_INT_TYPE tBackground; } tColour; uint8_t chOpacity; arm_2d_tile_t *ptTargetFB; uint32_t wMode; const arm_2d_font_t *ptFont; } s_tLCDTextControl = { .tScreen = { .tSize = { .iWidth = __GLCD_CFG_SCEEN_WIDTH__, .iHeight = __GLCD_CFG_SCEEN_HEIGHT__, }, }, .tRegion = { .tSize = { .iWidth = __GLCD_CFG_SCEEN_WIDTH__, .iHeight = __GLCD_CFG_SCEEN_HEIGHT__, }, }, .tColour = { .tForeground = GLCD_COLOR_GREEN, .tBackground = GLCD_COLOR_BLACK, }, .ptTargetFB = (arm_2d_tile_t *)(-1), .wMode = ARM_2D_DRW_PATN_MODE_COPY, //| ARM_2D_DRW_PATN_MODE_NO_FG_COLOR //| ARM_2D_DRW_PATH_MODE_COMP_FG_COLOUR .ptFont = &ARM_2D_FONT_6x8.use_as__arm_2d_font_t, .chOpacity = 255, }; /*============================ IMPLEMENTATION ================================*/ void arm_lcd_text_init(arm_2d_region_t *ptScreen) { assert(NULL != ptScreen); s_tLCDTextControl.tScreen = *ptScreen; } void arm_lcd_text_set_colour(COLOUR_INT_TYPE wForeground, COLOUR_INT_TYPE wBackground) { s_tLCDTextControl.tColour.tForeground = wForeground; s_tLCDTextControl.tColour.tBackground = wBackground; } void arm_lcd_text_set_target_framebuffer(const arm_2d_tile_t *ptFrameBuffer) { s_tLCDTextControl.ptTargetFB = (arm_2d_tile_t *)ptFrameBuffer; if (NULL == ptFrameBuffer) { // use default framebuffer s_tLCDTextControl.ptTargetFB = (arm_2d_tile_t *)(-1); } } void arm_lcd_text_set_display_mode(uint32_t wMode) { s_tLCDTextControl.wMode = wMode; } void arm_lcd_text_set_opacity(uint8_t chOpacity) { s_tLCDTextControl.chOpacity = chOpacity; } void arm_lcd_text_set_draw_region(arm_2d_region_t *ptRegion) { if (NULL == ptRegion) { ptRegion = &s_tLCDTextControl.tScreen; } s_tLCDTextControl.tRegion = *ptRegion; /* reset draw pointer */ arm_lcd_text_location(0,0); } void arm_lcd_text_location(uint8_t chY, uint8_t chX) { arm_2d_size_t tSize = s_tLCDTextControl.ptFont->tCharSize; s_tLCDTextControl.tDrawOffset.iX = tSize.iWidth * chX % s_tLCDTextControl.tRegion.tSize.iWidth; s_tLCDTextControl.tDrawOffset.iY = tSize.iHeight * ( chY + tSize.iWidth * chX / s_tLCDTextControl.tRegion.tSize.iWidth); s_tLCDTextControl.tDrawOffset.iY %= s_tLCDTextControl.tRegion.tSize.iHeight; } arm_2d_err_t arm_lcd_text_set_font(const arm_2d_font_t *ptFont) { if (NULL == ptFont) { ptFont = &ARM_2D_FONT_6x8.use_as__arm_2d_font_t; /* use default font */ } if (s_tLCDTextControl.ptFont == ptFont) { return ARM_2D_ERR_NONE; } do { if (NULL == ptFont->tileFont.pchBuffer) { break; } if ( (0 == ptFont->tCharSize.iHeight) || (0 == ptFont->tCharSize.iWidth) || (0 == ptFont->nCount)) { break; } s_tLCDTextControl.ptFont = ptFont; /* reset draw pointer */ arm_lcd_text_location(0,0); return ARM_2D_ERR_NONE; } while(0); return ARM_2D_ERR_INVALID_PARAM; } arm_2d_char_descriptor_t * ARM_2D_A1_FONT_GET_CHAR_DESCRIPTOR_HANDLER( const arm_2d_font_t *ptFont, arm_2d_char_descriptor_t *ptDescriptor, uint8_t *pchCharCode) { assert(NULL != ptFont); assert(NULL != ptDescriptor); assert(NULL != pchCharCode); arm_2d_a1_font_t *ptThis = (arm_2d_a1_font_t *)ptFont; memset(ptDescriptor, 0, sizeof(arm_2d_char_descriptor_t)); ptDescriptor->tileChar.tRegion.tSize = ptFont->tCharSize; ptDescriptor->tileChar.ptParent = (arm_2d_tile_t *)&ptFont->tileFont; ptDescriptor->tileChar.tInfo.bDerivedResource = true; ptDescriptor->iAdvance = ptFont->tCharSize.iWidth; ptDescriptor->iBearingX = 0; ptDescriptor->iBearingY = ptFont->tCharSize.iHeight; ptDescriptor->tileChar.tRegion.tLocation.iY = (*pchCharCode - (int16_t)this.nOffset) * ptFont->tCharSize.iHeight; ptDescriptor->chCodeLength = 1; return ptDescriptor; } static arm_2d_char_descriptor_t *__arm_lcd_get_char_descriptor(const arm_2d_font_t *ptFont, arm_2d_char_descriptor_t *ptDescriptor, uint8_t **ppchCharCode) { assert(NULL != ppchCharCode); assert(NULL != (*ppchCharCode)); if (NULL == ARM_2D_INVOKE(ptFont->fnGetCharDescriptor, ARM_2D_PARAM( ptFont, ptDescriptor, (uint8_t *)(*ppchCharCode)))) { assert(false); return NULL; } return ptDescriptor; } int8_t lcd_draw_char(int16_t iX, int16_t iY, uint8_t **ppchCharCode, uint_fast8_t chOpacity) { arm_2d_char_descriptor_t tCharDescriptor; if (NULL == __arm_lcd_get_char_descriptor( s_tLCDTextControl.ptFont, &tCharDescriptor, ppchCharCode)){ (*ppchCharCode) += 1; return 0; } (*ppchCharCode) += tCharDescriptor.chCodeLength; arm_2d_size_t tBBoxSize = s_tLCDTextControl.ptFont->tCharSize; arm_2d_region_t tDrawRegion = { .tLocation = { .iX = iX + tCharDescriptor.iBearingX, .iY = iY + (tBBoxSize.iHeight - tCharDescriptor.iBearingY), }, .tSize = tCharDescriptor.tileChar.tRegion.tSize, }; if (NULL != s_tLCDTextControl.ptFont->fnDrawChar) { s_tLCDTextControl.ptFont->fnDrawChar( s_tLCDTextControl.ptTargetFB, &tDrawRegion, &tCharDescriptor.tileChar, s_tLCDTextControl.tColour.tForeground, chOpacity); } else { arm_2d_draw_pattern(&tCharDescriptor.tileChar, s_tLCDTextControl.ptTargetFB, &tDrawRegion, s_tLCDTextControl.wMode, s_tLCDTextControl.tColour.tForeground, s_tLCDTextControl.tColour.tBackground); } arm_2d_op_wait_async(NULL); return tCharDescriptor.iAdvance; } static void __arm_lcd_draw_region_line_wrapping(arm_2d_size_t *ptCharSize, arm_2d_size_t *ptDrawRegionSize) { if (s_tLCDTextControl.tDrawOffset.iX >= ptDrawRegionSize->iWidth) { s_tLCDTextControl.tDrawOffset.iX = 0; s_tLCDTextControl.tDrawOffset.iY += ptCharSize->iHeight; if (s_tLCDTextControl.tDrawOffset.iY >= ptDrawRegionSize->iHeight) { s_tLCDTextControl.tDrawOffset.iY = 0; } } } arm_2d_size_t __arm_lcd_get_string_line_box(const char *str, const arm_2d_font_t *ptFont) { if (NULL == ptFont) { ptFont = s_tLCDTextControl.ptFont; } arm_2d_size_t tCharSize = ptFont->tCharSize; arm_2d_region_t tDrawBox = { .tSize.iHeight = tCharSize.iHeight, }; while(*str) { if (*str == '\r') { tDrawBox.tLocation.iX = 0; } else if (*str == '\n') { tDrawBox.tLocation.iX = 0; tDrawBox.tLocation.iY += tCharSize.iHeight; tDrawBox.tSize.iHeight = MAX(tDrawBox.tSize.iHeight, tDrawBox.tLocation.iY); } else if (*str == '\t') { tDrawBox.tLocation.iX += tCharSize.iWidth * 4; tDrawBox.tLocation.iX -= tDrawBox.tLocation.iX % tCharSize.iWidth; tDrawBox.tSize.iWidth = MAX(tDrawBox.tSize.iWidth, tDrawBox.tLocation.iX); }else if (*str == '\b') { if (tDrawBox.tLocation.iX >= tCharSize.iWidth) { tDrawBox.tLocation.iX -= tCharSize.iWidth; } else { tDrawBox.tLocation.iX = 0; } } else { int16_t iAdvance = tCharSize.iWidth; arm_2d_char_descriptor_t tDescriptor; if (NULL == __arm_lcd_get_char_descriptor( ptFont, &tDescriptor, (uint8_t **)&str)) { str++; } else{ iAdvance = tDescriptor.iAdvance; str += tDescriptor.chCodeLength; } tDrawBox.tLocation.iX += iAdvance; tDrawBox.tSize.iWidth = MAX(tDrawBox.tSize.iWidth, tDrawBox.tLocation.iX); continue; } str++; } return tDrawBox.tSize; } void arm_lcd_puts(const char *str) { arm_2d_size_t tCharSize = s_tLCDTextControl.ptFont->tCharSize; arm_2d_size_t tDrawRegionSize = s_tLCDTextControl.tRegion.tSize; while(*str) { if (*str == '\r') { s_tLCDTextControl.tDrawOffset.iX = 0; } else if (*str == '\n') { s_tLCDTextControl.tDrawOffset.iX = 0; s_tLCDTextControl.tDrawOffset.iY += tCharSize.iHeight; if (s_tLCDTextControl.tDrawOffset.iY >= tDrawRegionSize.iHeight) { s_tLCDTextControl.tDrawOffset.iY = 0; } } else if (*str == '\t') { s_tLCDTextControl.tDrawOffset.iX += tCharSize.iWidth * 4; s_tLCDTextControl.tDrawOffset.iX -= s_tLCDTextControl.tDrawOffset.iX % tCharSize.iWidth; __arm_lcd_draw_region_line_wrapping(&tCharSize, &tDrawRegionSize); }else if (*str == '\b') { if (s_tLCDTextControl.tDrawOffset.iX >= tCharSize.iWidth) { s_tLCDTextControl.tDrawOffset.iX -= tCharSize.iWidth; } else { s_tLCDTextControl.tDrawOffset.iX = 0; } } else { int16_t iX = s_tLCDTextControl.tDrawOffset.iX + s_tLCDTextControl.tRegion.tLocation.iX; int16_t iY = s_tLCDTextControl.tDrawOffset.iY + s_tLCDTextControl.tRegion.tLocation.iY; s_tLCDTextControl.tDrawOffset.iX += lcd_draw_char( iX, iY, (uint8_t **)&str, s_tLCDTextControl.chOpacity);; __arm_lcd_draw_region_line_wrapping(&tCharSize, &tDrawRegionSize); continue; } str++; } } #if defined(__IS_COMPILER_IAR__) && __IS_COMPILER_IAR__ #define __va_list va_list #endif int arm_lcd_printf(const char *format, ...) { int real_size; static char s_chBuffer[__LCD_PRINTF_CFG_TEXT_BUFFER_SIZE__ + 1]; __va_list ap; va_start(ap, format); real_size = vsnprintf(s_chBuffer, sizeof(s_chBuffer)-1, format, ap); va_end(ap); real_size = MIN(sizeof(s_chBuffer)-1, real_size); s_chBuffer[real_size] = '\0'; arm_lcd_puts(s_chBuffer); return real_size; } #if defined(__clang__) # pragma clang diagnostic pop #endif