move lvgl files to src folder

This commit is contained in:
Gabor Kiss-Vamosi
2019-03-17 08:33:03 +01:00
parent 2db846d5e0
commit f51bf17e39
186 changed files with 145 additions and 144 deletions

598
src/lv_conf_checker.h Normal file
View File

@@ -0,0 +1,598 @@
/**
* GENERATED FILE, DO NOT EDIT IT!
* @file lv_conf_checker.h
* Make sure all the defines of lv_conf.h have a default value
**/
#ifndef LV_CONF_CHECKER_H
#define LV_CONF_CHECKER_H
/*===================
Graphical settings
*===================*/
/* Horizontal and vertical resolution of the library.*/
#ifndef LV_HOR_RES_MAX
#define LV_HOR_RES_MAX (480)
#endif
#ifndef LV_VER_RES_MAX
#define LV_VER_RES_MAX (320)
#endif
/*Color settings*/
#ifndef LV_COLOR_DEPTH
#define LV_COLOR_DEPTH 16 /*Color depth: 1/8/16/32*/
#endif
#ifndef LV_COLOR_16_SWAP
#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/
#endif
#ifndef LV_COLOR_SCREEN_TRANSP
#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/
#endif
#ifndef LV_COLOR_TRANSP
#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/
#endif
/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
#ifndef LV_ANTIALIAS
#define LV_ANTIALIAS 1 /*1: Enable anti-aliasing*/
#endif
/*Screen refresh period in milliseconds. LittlevGL will redraw the screen with this period*/
#ifndef LV_REFR_PERIOD
#define LV_REFR_PERIOD 30 /*[ms]*/
#endif
/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide
* (Not so important, you can adjust it to modify default sizes and spaces)*/
#ifndef LV_DPI
#define LV_DPI 100 /*[px]*/
#endif
/*===================
Dynamic memory
*===================*/
/* Memory size which will be used by the library
* to store the graphical objects and other data */
#ifndef LV_MEM_CUSTOM
#define LV_MEM_CUSTOM 0 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/
#endif
#if LV_MEM_CUSTOM == 0
#ifndef LV_MEM_SIZE
# define LV_MEM_SIZE (32U * 1024U) /*Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
#endif
#ifndef LV_MEM_ATTR
# define LV_MEM_ATTR /*Complier prefix for big array declaration*/
#endif
#ifndef LV_MEM_ADR
# define LV_MEM_ADR 0 /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/
#endif
#ifndef LV_MEM_AUTO_DEFRAG
# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/
#endif
#else /*LV_MEM_CUSTOM*/
#ifndef LV_MEM_CUSTOM_INCLUDE
# define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
#endif
#ifndef LV_MEM_CUSTOM_ALLOC
# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
#endif
#ifndef LV_MEM_CUSTOM_FREE
# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
#endif
#endif /*LV_MEM_CUSTOM*/
/* Garbage Collector settings
* Used if lvgl is binded to higher language and the memory is managed by that language */
#ifndef LV_ENABLE_GC
#define LV_ENABLE_GC 0
#endif
#if LV_ENABLE_GC != 0
#ifndef LV_MEM_CUSTOM_REALLOC
# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
#endif
#ifndef LV_MEM_CUSTOM_GET_SIZE
# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
#endif
#ifndef LV_GC_INCLUDE
# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif
#endif /* LV_ENABLE_GC */
/*=================
Misc. setting
*=================*/
/*Input device settings*/
#ifndef LV_INDEV_READ_PERIOD
#define LV_INDEV_READ_PERIOD 30 /*Input device read period in milliseconds*/
#endif
#ifndef LV_INDEV_POINT_MARKER
#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: LV_USE_REAL_DRAW = 1)*/
#endif
#ifndef LV_INDEV_DRAG_LIMIT
#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */
#endif
#ifndef LV_INDEV_DRAG_THROW
#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */
#endif
#ifndef LV_INDEV_LONG_PRESS_TIME
#define LV_INDEV_LONG_PRESS_TIME 400 /*Long press time in milliseconds*/
#endif
#ifndef LV_INDEV_LONG_PRESS_REP_TIME
#define LV_INDEV_LONG_PRESS_REP_TIME 100 /*Repeated trigger period in long press [ms] */
#endif
/*Text settings*/
#ifndef LV_TXT_UTF8
#define LV_TXT_UTF8 1 /*Enable UTF-8 coded Unicode character usage */
#endif
#ifndef LV_TXT_BREAK_CHARS
#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/
#endif
#ifndef LV_TXT_LINE_BREAK_LONG_LEN
#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */
#endif
#ifndef LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */
#endif
#ifndef LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */
#endif
/*Feature usage*/
#ifndef LV_USE_ANIMATION
#define LV_USE_ANIMATION 1 /*1: Enable all animations*/
#endif
#ifndef LV_USE_SHADOW
#define LV_USE_SHADOW 1 /*1: Enable shadows*/
#endif
#ifndef LV_USE_GROUP
#define LV_USE_GROUP 1 /*1: Enable object groups (for keyboards)*/
#endif
#if LV_USE_GROUP
#endif /*LV_USE_GROUP*/
#ifndef LV_USE_GPU
#define LV_USE_GPU 1 /*1: Enable GPU interface*/
#endif
#ifndef LV_USE_FILESYSTEM
#define LV_USE_FILESYSTEM 1 /*1: Enable file system (might be required for images*/
#endif
#ifndef LV_USE_USER_DATA_SINGLE
#define LV_USE_USER_DATA_SINGLE 1 /*1: Add a `user_data` to drivers and objects*/
#endif
#ifndef LV_USE_USER_DATA_MULTI
#define LV_USE_USER_DATA_MULTI 0 /*1: Add separate `user_data` for every callback*/
#endif
/*Compiler settings*/
#ifndef LV_ATTRIBUTE_TICK_INC
#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */
#endif
#ifndef LV_ATTRIBUTE_TASK_HANDLER
#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */
#endif
#ifndef LV_COMPILER_VLA_SUPPORTED
#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/
#endif
#ifndef LV_COMPILER_NON_CONST_INIT_SUPPORTED
#define LV_COMPILER_NON_CONST_INIT_SUPPORTED 1 /* 1: Initialization with non constant values are supported */
#endif
/*HAL settings*/
#ifndef LV_TICK_CUSTOM
#define LV_TICK_CUSTOM 0 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */
#endif
#if LV_TICK_CUSTOM == 1
#ifndef LV_TICK_CUSTOM_INCLUDE
#define LV_TICK_CUSTOM_INCLUDE "something.h" /*Header for the sys time function*/
#endif
#ifndef LV_TICK_CUSTOM_SYS_TIME_EXPR
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current systime in ms*/
#endif
#endif /*LV_TICK_CUSTOM*/
/*Log settings*/
#ifndef LV_USE_LOG
#define LV_USE_LOG 1 /*Enable/disable the log module*/
#endif
#if LV_USE_LOG
/* How important log should be added:
* LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
* LV_LOG_LEVEL_INFO Log important events
* LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
* LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*/
#ifndef LV_LOG_LEVEL
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
#endif
/* 1: Print the log with 'printf'; 0: user need to register a callback*/
#ifndef LV_LOG_PRINTF
# define LV_LOG_PRINTF 0
#endif
#endif /*LV_USE_LOG*/
/*================
* THEME USAGE
*================*/
#ifndef LV_THEME_LIVE_UPDATE
#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
#endif
#ifndef LV_USE_THEME_TEMPL
#define LV_USE_THEME_TEMPL 0 /*Just for test*/
#endif
#ifndef LV_USE_THEME_DEFAULT
#define LV_USE_THEME_DEFAULT 0 /*Built mainly from the built-in styles. Consumes very few RAM*/
#endif
#ifndef LV_USE_THEME_ALIEN
#define LV_USE_THEME_ALIEN 0 /*Dark futuristic theme*/
#endif
#ifndef LV_USE_THEME_NIGHT
#define LV_USE_THEME_NIGHT 0 /*Dark elegant theme*/
#endif
#ifndef LV_USE_THEME_MONO
#define LV_USE_THEME_MONO 0 /*Mono color theme for monochrome displays*/
#endif
#ifndef LV_USE_THEME_MATERIAL
#define LV_USE_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/
#endif
#ifndef LV_USE_THEME_ZEN
#define LV_USE_THEME_ZEN 0 /*Peaceful, mainly light theme */
#endif
#ifndef LV_USE_THEME_NEMO
#define LV_USE_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/
#endif
/*==================
* FONT USAGE
*===================*/
/* More info about fonts: https://docs.littlevgl.com/#Fonts
* To enable a built-in font use 1,2,4 or 8 values
* which will determine the bit-per-pixel. Higher value means smoother fonts */
#ifndef LV_USE_FONT_DEJAVU_10
#define LV_USE_FONT_DEJAVU_10 0
#endif
#ifndef LV_USE_FONT_DEJAVU_10_LATIN_SUP
#define LV_USE_FONT_DEJAVU_10_LATIN_SUP 0
#endif
#ifndef LV_USE_FONT_DEJAVU_10_CYRILLIC
#define LV_USE_FONT_DEJAVU_10_CYRILLIC 0
#endif
#ifndef LV_USE_FONT_SYMBOL_10
#define LV_USE_FONT_SYMBOL_10 0
#endif
#ifndef LV_USE_FONT_DEJAVU_20
#define LV_USE_FONT_DEJAVU_20 4
#endif
#ifndef LV_USE_FONT_DEJAVU_20_LATIN_SUP
#define LV_USE_FONT_DEJAVU_20_LATIN_SUP 4
#endif
#ifndef LV_USE_FONT_DEJAVU_20_CYRILLIC
#define LV_USE_FONT_DEJAVU_20_CYRILLIC 4
#endif
#ifndef LV_USE_FONT_SYMBOL_20
#define LV_USE_FONT_SYMBOL_20 4
#endif
#ifndef LV_USE_FONT_DEJAVU_30
#define LV_USE_FONT_DEJAVU_30 0
#endif
#ifndef LV_USE_FONT_DEJAVU_30_LATIN_SUP
#define LV_USE_FONT_DEJAVU_30_LATIN_SUP 0
#endif
#ifndef LV_USE_FONT_DEJAVU_30_CYRILLIC
#define LV_USE_FONT_DEJAVU_30_CYRILLIC 0
#endif
#ifndef LV_USE_FONT_SYMBOL_30
#define LV_USE_FONT_SYMBOL_30 0
#endif
#ifndef LV_USE_FONT_DEJAVU_40
#define LV_USE_FONT_DEJAVU_40 0
#endif
#ifndef LV_USE_FONT_DEJAVU_40_LATIN_SUP
#define LV_USE_FONT_DEJAVU_40_LATIN_SUP 0
#endif
#ifndef LV_USE_FONT_DEJAVU_40_CYRILLIC
#define LV_USE_FONT_DEJAVU_40_CYRILLIC 0
#endif
#ifndef LV_USE_FONT_SYMBOL_40
#define LV_USE_FONT_SYMBOL_40 0
#endif
#ifndef LV_USE_FONT_MONOSPACE_8
#define LV_USE_FONT_MONOSPACE_8 0
#endif
/* Optionally declare your custom fonts here.
* You can use these fonts as default font too
* and they will be available globally. E.g.
* #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
* LV_FONT_DECLARE(my_font_2) \
*/
#ifndef LV_FONT_CUSTOM_DECLARE
#define LV_FONT_CUSTOM_DECLARE
#endif
#ifndef LV_FONT_DEFAULT
#define LV_FONT_DEFAULT &lv_font_dejavu_20 /*Always set a default font from the built-in fonts*/
#endif
/*===================
* LV_OBJ SETTINGS
*==================*/
#ifndef LV_OBJ_REALIGN
#define LV_OBJ_REALIGN 0 /*Enable `lv_obj_realign()` based on `lv_obj_align()` parameters*/
#endif
/*==================
* LV OBJ X USAGE
*================*/
/*
* Documentation of the object types: https://docs.littlevgl.com/#Object-types
*/
/*****************
* Simple object
*****************/
/*Label (dependencies: -*/
#ifndef LV_USE_LABEL
#define LV_USE_LABEL 1
#endif
#if LV_USE_LABEL != 0
#ifndef LV_LABEL_SCROLL_SPEED
# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
#endif
#endif
/*Image (dependencies: lv_label*/
#ifndef LV_USE_IMG
#define LV_USE_IMG 1
#endif
#if LV_USE_IMG != 0
#ifndef LV_IMG_CF_INDEXED
# define LV_IMG_CF_INDEXED 1 /*Enable indexed (palette) images*/
#endif
#ifndef LV_IMG_CF_ALPHA
# define LV_IMG_CF_ALPHA 1 /*Enable alpha indexed images*/
#endif
#endif
/*Line (dependencies: -*/
#ifndef LV_USE_LINE
#define LV_USE_LINE 1
#endif
/*Arc (dependencies: -)*/
#ifndef LV_USE_ARC
#define LV_USE_ARC 1
#endif
/*******************
* Container objects
*******************/
/*Container (dependencies: -*/
#ifndef LV_USE_CONT
#define LV_USE_CONT 1
#endif
/*Page (dependencies: lv_cont)*/
#ifndef LV_USE_PAGE
#define LV_USE_PAGE 1
#endif
/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
#ifndef LV_USE_WIN
#define LV_USE_WIN 1
#endif
/*Tab (dependencies: lv_page, lv_btnm)*/
#ifndef LV_USE_TABVIEW
#define LV_USE_TABVIEW 1
#endif
# if LV_USE_TABVIEW != 0
#ifndef LV_TABVIEW_ANIM_TIME
# define LV_TABVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/
#endif
#endif
/*Tileview (dependencies: lv_page) */
#ifndef LV_USE_TILEVIEW
#define LV_USE_TILEVIEW 1
#endif
#if LV_USE_TILEVIEW
#ifndef LV_TILEVIEW_ANIM_TIME
# define LV_TILEVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/
#endif
#endif
/*************************
* Data visualizer objects
*************************/
/*Bar (dependencies: -)*/
#ifndef LV_USE_BAR
#define LV_USE_BAR 1
#endif
/*Line meter (dependencies: *;)*/
#ifndef LV_USE_LMETER
#define LV_USE_LMETER 1
#endif
/*Gauge (dependencies:lv_bar, lv_lmeter)*/
#ifndef LV_USE_GAUGE
#define LV_USE_GAUGE 1
#endif
/*Chart (dependencies: -)*/
#ifndef LV_USE_CHART
#define LV_USE_CHART 1
#endif
/*Table (dependencies: lv_label)*/
#ifndef LV_USE_TABLE
#define LV_USE_TABLE 1
#endif
#if LV_USE_TABLE
#ifndef LV_TABLE_COL_MAX
# define LV_TABLE_COL_MAX 12
#endif
#endif
/*LED (dependencies: -)*/
#ifndef LV_USE_LED
#define LV_USE_LED 1
#endif
/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
#ifndef LV_USE_MBOX
#define LV_USE_MBOX 1
#endif
/*Text area (dependencies: lv_label, lv_page)*/
#ifndef LV_USE_TA
#define LV_USE_TA 1
#endif
#if LV_USE_TA != 0
#ifndef LV_TA_CURSOR_BLINK_TIME
# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/
#endif
#ifndef LV_TA_PWD_SHOW_TIME
# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/
#endif
#endif
/*Spinbox (dependencies: lv_ta)*/
#ifndef LV_USE_SPINBOX
#define LV_USE_SPINBOX 1
#endif
/*Calendar (dependencies: -)*/
#ifndef LV_USE_CALENDAR
#define LV_USE_CALENDAR 1
#endif
/*Preload (dependencies: lv_arc)*/
#ifndef LV_USE_PRELOAD
#define LV_USE_PRELOAD 1
#endif
#if LV_USE_PRELOAD != 0
#ifndef LV_PRELOAD_DEF_ARC_LENGTH
# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
#endif
#ifndef LV_PRELOAD_DEF_SPIN_TIME
# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
#endif
#ifndef LV_PRELOAD_DEF_ANIM
# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC
#endif
#endif
/*Canvas (dependencies: lv_img)*/
#ifndef LV_USE_CANVAS
#define LV_USE_CANVAS 1
#endif
/*************************
* User input objects
*************************/
/*Button (dependencies: lv_cont*/
#ifndef LV_USE_BTN
#define LV_USE_BTN 1
#endif
#if LV_USE_BTN != 0
#ifndef LV_BTN_INK_EFFECT
# define LV_BTN_INK_EFFECT 1 /*Enable button-state animations - draw a circle on click (dependencies: LV_USE_ANIMATION)*/
#endif
#endif
/*Image Button (dependencies: lv_btn*/
#ifndef LV_USE_IMGBTN
#define LV_USE_IMGBTN 1
#endif
#if LV_USE_IMGBTN
#ifndef LV_IMGBTN_TILED
# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
#endif
#endif
/*Button matrix (dependencies: -)*/
#ifndef LV_USE_BTNM
#define LV_USE_BTNM 1
#endif
/*Keyboard (dependencies: lv_btnm)*/
#ifndef LV_USE_KB
#define LV_USE_KB 1
#endif
/*Check box (dependencies: lv_btn, lv_label)*/
#ifndef LV_USE_CB
#define LV_USE_CB 1
#endif
/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
#ifndef LV_USE_LIST
#define LV_USE_LIST 1
#endif
#if LV_USE_LIST != 0
#ifndef LV_LIST_FOCUS_TIME
# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */
#endif
#endif
/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
#ifndef LV_USE_DDLIST
#define LV_USE_DDLIST 1
#endif
#if LV_USE_DDLIST != 0
#ifndef LV_DDLIST_ANIM_TIME
# define LV_DDLIST_ANIM_TIME 200 /*Open and close default animation time [ms] (0: no animation)*/
#endif
#endif
/*Roller (dependencies: lv_ddlist)*/
#ifndef LV_USE_ROLLER
#define LV_USE_ROLLER 1
#endif
#if LV_USE_ROLLER != 0
#ifndef LV_ROLLER_ANIM_TIME
# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/
#endif
#ifndef LV_ROLLER_INF_PAGES
# define LV_ROLLER_INF_PAGES 7 /*With inifinite rollers the options are added multiple times (on select positioned back to the center)*/
#endif
#endif
/*Slider (dependencies: lv_bar)*/
#ifndef LV_USE_SLIDER
#define LV_USE_SLIDER 1
#endif
/*Switch (dependencies: lv_slider)*/
#ifndef LV_USE_SW
#define LV_USE_SW 1
#endif
/*************************
* Non-user section
*************************/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#endif
#endif /*LV_CONF_CHECKER_H*/

361
src/lv_conf_templ.h Normal file
View File

@@ -0,0 +1,361 @@
/**
* @file lv_conf.h
*
*/
/*
* COPY THIS FILE AS lv_conf.h
*/
#if 0 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
/*===================
Graphical settings
*===================*/
/* Horizontal and vertical resolution of the library.*/
#define LV_HOR_RES_MAX (480)
#define LV_VER_RES_MAX (320)
/*Color settings*/
#define LV_COLOR_DEPTH 16 /*Color depth: 1/8/16/32*/
#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/
#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/
#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/
/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
#define LV_ANTIALIAS 1 /*1: Enable anti-aliasing*/
/*Screen refresh period in milliseconds. LittlevGL will redraw the screen with this period*/
#define LV_REFR_PERIOD 30 /*[ms]*/
/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide
* (Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI 100 /*[px]*/
/*===================
Dynamic memory
*===================*/
/* Memory size which will be used by the library
* to store the graphical objects and other data */
#define LV_MEM_CUSTOM 0 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/
#if LV_MEM_CUSTOM == 0
# define LV_MEM_SIZE (32U * 1024U) /*Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/
# define LV_MEM_ATTR /*Complier prefix for big array declaration*/
# define LV_MEM_ADR 0 /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/
# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/
#else /*LV_MEM_CUSTOM*/
# define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/
# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/
#endif /*LV_MEM_CUSTOM*/
/* Garbage Collector settings
* Used if lvgl is binded to higher language and the memory is managed by that language */
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/
# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/
# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /* LV_ENABLE_GC */
/*=================
Misc. setting
*=================*/
/*Input device settings*/
#define LV_INDEV_READ_PERIOD 30 /*Input device read period in milliseconds*/
#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: LV_USE_REAL_DRAW = 1)*/
#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */
#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */
#define LV_INDEV_LONG_PRESS_TIME 400 /*Long press time in milliseconds*/
#define LV_INDEV_LONG_PRESS_REP_TIME 100 /*Repeated trigger period in long press [ms] */
/*Text settings*/
#define LV_TXT_UTF8 1 /*Enable UTF-8 coded Unicode character usage */
#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/
#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */
/*Feature usage*/
#define LV_USE_ANIMATION 1 /*1: Enable all animations*/
#define LV_USE_SHADOW 1 /*1: Enable shadows*/
#define LV_USE_GROUP 1 /*1: Enable object groups (for keyboards)*/
#if LV_USE_GROUP
typedef void * lv_group_user_data_t;
#endif /*LV_USE_GROUP*/
#define LV_USE_GPU 1 /*1: Enable GPU interface*/
#define LV_USE_FILESYSTEM 1 /*1: Enable file system (might be required for images*/
#define LV_USE_USER_DATA_SINGLE 1 /*1: Add a `user_data` to drivers and objects*/
#define LV_USE_USER_DATA_MULTI 0 /*1: Add separate `user_data` for every callback*/
/*Compiler settings*/
#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */
#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */
#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/
#define LV_COMPILER_NON_CONST_INIT_SUPPORTED 1 /* 1: Initialization with non constant values are supported */
/*HAL settings*/
#define LV_TICK_CUSTOM 0 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */
#if LV_TICK_CUSTOM == 1
#define LV_TICK_CUSTOM_INCLUDE "something.h" /*Header for the sys time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current systime in ms*/
#endif /*LV_TICK_CUSTOM*/
typedef void * lv_disp_drv_user_data_t; /*Type of user data in the display driver*/
typedef void * lv_indev_drv_user_data_t; /*Type of user data in the display driver*/
/*Log settings*/
#define LV_USE_LOG 1 /*Enable/disable the log module*/
#if LV_USE_LOG
/* How important log should be added:
* LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
* LV_LOG_LEVEL_INFO Log important events
* LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
* LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*/
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/* 1: Print the log with 'printf'; 0: user need to register a callback*/
# define LV_LOG_PRINTF 0
#endif /*LV_USE_LOG*/
/*================
* THEME USAGE
*================*/
#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/
#define LV_USE_THEME_TEMPL 0 /*Just for test*/
#define LV_USE_THEME_DEFAULT 0 /*Built mainly from the built-in styles. Consumes very few RAM*/
#define LV_USE_THEME_ALIEN 0 /*Dark futuristic theme*/
#define LV_USE_THEME_NIGHT 0 /*Dark elegant theme*/
#define LV_USE_THEME_MONO 0 /*Mono color theme for monochrome displays*/
#define LV_USE_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/
#define LV_USE_THEME_ZEN 0 /*Peaceful, mainly light theme */
#define LV_USE_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/
/*==================
* FONT USAGE
*===================*/
/* More info about fonts: https://docs.littlevgl.com/#Fonts
* To enable a built-in font use 1,2,4 or 8 values
* which will determine the bit-per-pixel. Higher value means smoother fonts */
#define LV_USE_FONT_DEJAVU_10 0
#define LV_USE_FONT_DEJAVU_10_LATIN_SUP 0
#define LV_USE_FONT_DEJAVU_10_CYRILLIC 0
#define LV_USE_FONT_SYMBOL_10 0
#define LV_USE_FONT_DEJAVU_20 4
#define LV_USE_FONT_DEJAVU_20_LATIN_SUP 4
#define LV_USE_FONT_DEJAVU_20_CYRILLIC 4
#define LV_USE_FONT_SYMBOL_20 4
#define LV_USE_FONT_DEJAVU_30 0
#define LV_USE_FONT_DEJAVU_30_LATIN_SUP 0
#define LV_USE_FONT_DEJAVU_30_CYRILLIC 0
#define LV_USE_FONT_SYMBOL_30 0
#define LV_USE_FONT_DEJAVU_40 0
#define LV_USE_FONT_DEJAVU_40_LATIN_SUP 0
#define LV_USE_FONT_DEJAVU_40_CYRILLIC 0
#define LV_USE_FONT_SYMBOL_40 0
#define LV_USE_FONT_MONOSPACE_8 0
/* Optionally declare your custom fonts here.
* You can use these fonts as default font too
* and they will be available globally. E.g.
* #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \
* LV_FONT_DECLARE(my_font_2) \
*/
#define LV_FONT_CUSTOM_DECLARE
#define LV_FONT_DEFAULT &lv_font_dejavu_20 /*Always set a default font from the built-in fonts*/
/*===================
* LV_OBJ SETTINGS
*==================*/
typedef void * lv_obj_user_data_t; /*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/
#define LV_OBJ_REALIGN 0 /*Enable `lv_obj_realign()` based on `lv_obj_align()` parameters*/
/*==================
* LV OBJ X USAGE
*================*/
/*
* Documentation of the object types: https://docs.littlevgl.com/#Object-types
*/
/*****************
* Simple object
*****************/
/*Label (dependencies: -*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL != 0
# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
#endif
/*Image (dependencies: lv_label*/
#define LV_USE_IMG 1
#if LV_USE_IMG != 0
# define LV_IMG_CF_INDEXED 1 /*Enable indexed (palette) images*/
# define LV_IMG_CF_ALPHA 1 /*Enable alpha indexed images*/
#endif
/*Line (dependencies: -*/
#define LV_USE_LINE 1
/*Arc (dependencies: -)*/
#define LV_USE_ARC 1
/*******************
* Container objects
*******************/
/*Container (dependencies: -*/
#define LV_USE_CONT 1
/*Page (dependencies: lv_cont)*/
#define LV_USE_PAGE 1
/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/
#define LV_USE_WIN 1
/*Tab (dependencies: lv_page, lv_btnm)*/
#define LV_USE_TABVIEW 1
# if LV_USE_TABVIEW != 0
# define LV_TABVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/
#endif
/*Tileview (dependencies: lv_page) */
#define LV_USE_TILEVIEW 1
#if LV_USE_TILEVIEW
# define LV_TILEVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/
#endif
/*************************
* Data visualizer objects
*************************/
/*Bar (dependencies: -)*/
#define LV_USE_BAR 1
/*Line meter (dependencies: *;)*/
#define LV_USE_LMETER 1
/*Gauge (dependencies:lv_bar, lv_lmeter)*/
#define LV_USE_GAUGE 1
/*Chart (dependencies: -)*/
#define LV_USE_CHART 1
/*Table (dependencies: lv_label)*/
#define LV_USE_TABLE 1
#if LV_USE_TABLE
# define LV_TABLE_COL_MAX 12
#endif
/*LED (dependencies: -)*/
#define LV_USE_LED 1
/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/
#define LV_USE_MBOX 1
/*Text area (dependencies: lv_label, lv_page)*/
#define LV_USE_TA 1
#if LV_USE_TA != 0
# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/
# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/
#endif
/*Spinbox (dependencies: lv_ta)*/
#define LV_USE_SPINBOX 1
/*Calendar (dependencies: -)*/
#define LV_USE_CALENDAR 1
/*Preload (dependencies: lv_arc)*/
#define LV_USE_PRELOAD 1
#if LV_USE_PRELOAD != 0
# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC
#endif
/*Canvas (dependencies: lv_img)*/
#define LV_USE_CANVAS 1
/*************************
* User input objects
*************************/
/*Button (dependencies: lv_cont*/
#define LV_USE_BTN 1
#if LV_USE_BTN != 0
# define LV_BTN_INK_EFFECT 1 /*Enable button-state animations - draw a circle on click (dependencies: LV_USE_ANIMATION)*/
#endif
/*Image Button (dependencies: lv_btn*/
#define LV_USE_IMGBTN 1
#if LV_USE_IMGBTN
# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/
#endif
/*Button matrix (dependencies: -)*/
#define LV_USE_BTNM 1
/*Keyboard (dependencies: lv_btnm)*/
#define LV_USE_KB 1
/*Check box (dependencies: lv_btn, lv_label)*/
#define LV_USE_CB 1
/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/
#define LV_USE_LIST 1
#if LV_USE_LIST != 0
# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */
#endif
/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/
#define LV_USE_DDLIST 1
#if LV_USE_DDLIST != 0
# define LV_DDLIST_ANIM_TIME 200 /*Open and close default animation time [ms] (0: no animation)*/
#endif
/*Roller (dependencies: lv_ddlist)*/
#define LV_USE_ROLLER 1
#if LV_USE_ROLLER != 0
# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/
# define LV_ROLLER_INF_PAGES 7 /*With inifinite rollers the options are added multiple times (on select positioned back to the center)*/
#endif
/*Slider (dependencies: lv_bar)*/
#define LV_USE_SLIDER 1
/*Switch (dependencies: lv_slider)*/
#define LV_USE_SW 1
/*************************
* Non-user section
*************************/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/
# define _CRT_SECURE_NO_WARNINGS
#endif
/*--END OF LV_CONF_H--*/
/*Be sure every define has a default value*/
#include "lvgl/src/lv_conf_checker.h"
#endif /*LV_CONF_H*/
#endif /*End of "Content enable"*/

11
src/lv_core/lv_core.mk Normal file
View File

@@ -0,0 +1,11 @@
CSRCS += lv_group.c
CSRCS += lv_indev.c
CSRCS += lv_disp.c
CSRCS += lv_obj.c
CSRCS += lv_refr.c
CSRCS += lv_style.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/src/lv_core
VPATH += :$(LVGL_DIR)/lvgl/src/lv_core
CFLAGS += "-I$(LVGL_DIR)/lvgl/src/lv_core"

117
src/lv_core/lv_disp.c Normal file
View File

@@ -0,0 +1,117 @@
/**
* @file lv_disp.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_disp.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Return with a pointer to the active screen
* @param disp pointer to display which active screen should be get. (NULL to use the default screen)
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_scr_act: no display registered to get its top layer");
return NULL;
}
return disp->act_scr;
}
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_set_scr_act(lv_obj_t * scr)
{
lv_disp_t * d = lv_obj_get_disp(scr);
d->act_scr = scr;
lv_obj_invalidate(scr);
}
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
return NULL;
}
return disp->top_layer;
}
/**
* Return with the sys. layer. (Same on every screen and it is above the normal screen and the top layer)
* @param disp pointer to display which sys. layer should be get. (NULL to use the default screen)
* @return pointer to the sys layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) {
LV_LOG_WARN("lv_layer_sys: no display registered to get its top layer");
return NULL;
}
return disp->sys_layer;
}
/**
* Assign a screen to a display.
* @param disp pointer to a display where to assign the screen
* @param scr pointer to a screen object to assign
*/
void lv_disp_assign_screen(lv_disp_t * disp, lv_obj_t * scr)
{
if(lv_obj_get_parent(scr) != NULL) {
LV_LOG_WARN("lv_disp_assign_screen: try to assign a non-screen object");
return;
}
lv_disp_t * old_disp = lv_obj_get_disp(scr);
if(old_disp == disp) return;
lv_ll_chg_list(&old_disp->scr_ll, &disp->scr_ll, scr);
}
/**********************
* STATIC FUNCTIONS
**********************/

120
src/lv_core/lv_disp.h Normal file
View File

@@ -0,0 +1,120 @@
/**
* @file lv_disp.h
*
*/
#ifndef LV_DISP_H
#define LV_DISP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_hal/lv_hal.h"
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Return with a pointer to the active screen
* @param disp pointer to display which active screen should be get. (NULL to use the default screen)
* @return pointer to the active screen object (loaded by 'lv_scr_load()')
*/
lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp);
/**
* Make a screen active
* @param scr pointer to a screen
*/
void lv_disp_set_scr_act(lv_obj_t * scr);
/**
* Return with the top layer. (Same on every screen and it is above the normal screen layer)
* @param disp pointer to display which top layer should be get. (NULL to use the default screen)
* @return pointer to the top layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp);
/**
* Return with the sys. layer. (Same on every screen and it is above the normal screen and the top layer)
* @param disp pointer to display which sys. layer should be get. (NULL to use the default screen)
* @return pointer to the sys layer object (transparent screen sized lv_obj)
*/
lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp);
/**
* Assign a screen to a display.
* @param disp pointer to a display where to assign the screen
* @param scr pointer to a screen object to assign
*/
void lv_disp_assign_screen(lv_disp_t * disp, lv_obj_t * scr);
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
/**
* Get the active screen of the default display
* @return pointer to the active screen
*/
static inline lv_obj_t * lv_scr_act(void)
{
return lv_disp_get_scr_act(lv_disp_get_default());
}
/**
* Get the top layer of the default display
* @return pointer to the top layer
*/
static inline lv_obj_t * lv_layer_top(void)
{
return lv_disp_get_layer_top(lv_disp_get_default());
}
/**
* Get the active screen of the deafult display
* @return pointer to the sys layer
*/
static inline lv_obj_t * lv_layer_sys(void)
{
return lv_disp_get_layer_sys(lv_disp_get_default());
}
static inline void lv_scr_load(lv_obj_t * scr)
{
lv_disp_set_scr_act(scr);
}
/**********************
* MACROS
**********************/
/*------------------------------------------------
* To improve backward compatibility
* Recommended only if you have one display
*------------------------------------------------*/
#define LV_HOR_RES lv_disp_get_hor_res(lv_disp_get_default())
#define LV_VER_RES lv_disp_get_ver_res(lv_disp_get_default())
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TEMPL_H*/

663
src/lv_core/lv_group.c Normal file
View File

@@ -0,0 +1,663 @@
/**
* @file lv_group.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_group.h"
#if LV_USE_GROUP != 0
#include "../lv_themes/lv_theme.h"
#include <stddef.h>
#include "../lv_misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void style_mod_def(lv_group_t * group, lv_style_t * style);
static void style_mod_edit_def(lv_group_t * group, lv_style_t * style);
static void refresh_theme(lv_group_t * g, lv_theme_t * th);
static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *), void * (*move)(const lv_ll_t *, const void *));
static void lv_group_refocus(lv_group_t * g);
static void obj_to_foreground(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Init. the group module
*/
void lv_group_init(void)
{
lv_ll_init(&LV_GC_ROOT(_lv_group_ll), sizeof(lv_group_t));
}
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void)
{
lv_group_t * group = lv_ll_ins_head(&LV_GC_ROOT(_lv_group_ll));
lv_mem_assert(group);
if(group == NULL) return NULL;
lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
group->obj_focus = NULL;
group->frozen = 0;
group->focus_cb = NULL;
group->click_focus = 1;
group->editing = 0;
group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
group->wrap = 1;
#if LV_USE_USER_DATA_SINGLE
memset(&group->user_data, 0, sizeof(lv_group_user_data_t));
#endif
#if LV_USE_USER_DATA_MULTI
memset(&group->focus_user_data, 0, sizeof(lv_group_user_data_t));
memset(&group->style_mod_user_data, 0, sizeof(lv_group_user_data_t));
memset(&group->style_mod_edit_user_data, 0, sizeof(lv_group_user_data_t));
#endif
/*Initialize style modification callbacks from current theme*/
refresh_theme(group, lv_theme_get_current());
return group;
}
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group)
{
/*Defocus the the currently focused object*/
if(group->obj_focus != NULL) {
(*group->obj_focus)->signal_cb(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_obj_invalidate(*group->obj_focus);
}
/*Remove the objects from the group*/
lv_obj_t ** obj;
LV_LL_READ(group->obj_ll, obj) {
(*obj)->group_p = NULL;
}
lv_ll_clear(&(group->obj_ll));
lv_mem_free(group);
}
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
if(group == NULL) return;
/*Do not add the object twice*/
lv_obj_t ** obj_i;
LV_LL_READ(group->obj_ll, obj_i) {
if((*obj_i) == obj) {
LV_LOG_INFO("lv_group_add_obj: the object is already added to this group");
return;
}
}
/*If the object is already in a group and focused then defocus it*/
if(obj->group_p) {
if(lv_obj_is_focused(obj)) {
lv_group_refocus(obj->group_p);
LV_LOG_INFO("lv_group_add_obj: assign object to an other group");
}
}
obj->group_p = group;
lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
lv_mem_assert(next);
if(next == NULL) return;
*next = obj;
/* If the head and the tail is equal then there is only one object in the linked list.
* In this case automatically activate it*/
if(lv_ll_get_head(&group->obj_ll) == next) {
lv_group_refocus(group);
}
}
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(lv_obj_t * obj)
{
lv_group_t * g = obj->group_p;
if(g == NULL) return;
if(g->obj_focus == NULL) return; /*Just to be sure (Not possible if there is at least one object in the group)*/
/*Focus on the next object*/
if(*g->obj_focus == obj) {
/*If this is the only object in the group then focus to nothing.*/
if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
(*g->obj_focus)->signal_cb(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
}
/*If there more objects in the group then focus to the next/prev object*/
else {
lv_group_refocus(g);
}
}
/* If the focuses object is still the same then it was the only object in the group but it will be deleted.
* Set the `obj_focus` to NULL to get back to the initial state of the group with zero objects*/
if(*g->obj_focus == obj) {
g->obj_focus = NULL;
}
/*Search the object and remove it from its group */
lv_obj_t ** i;
LV_LL_READ(g->obj_ll, i) {
if(*i == obj) {
lv_ll_rem(&g->obj_ll, i);
lv_mem_free(i);
obj->group_p = NULL;
break;
}
}
}
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj)
{
if(obj == NULL) return;
lv_group_t * g = obj->group_p;
if(g == NULL) return;
if(g->frozen != 0) return;
/*On defocus edit mode must be leaved*/
lv_group_set_editing(g, false);
lv_obj_t ** i;
LV_LL_READ(g->obj_ll, i) {
if(*i == obj) {
if(g->obj_focus == i) return; /*Don't focus the already focused object again*/
if(g->obj_focus != NULL) {
(*g->obj_focus)->signal_cb(*g->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_res_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED);
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
}
g->obj_focus = i;
if(g->obj_focus != NULL) {
(*g->obj_focus)->signal_cb(*g->obj_focus, LV_SIGNAL_FOCUS, NULL);
if(g->focus_cb) g->focus_cb(g);
lv_res_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_FOCUSED);
if(res != LV_RES_OK) return;
lv_obj_invalidate(*g->obj_focus);
/*If the object or its parent has `top == true` bring it to the foregorund*/
obj_to_foreground(*g->obj_focus);
}
break;
}
}
}
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group)
{
focus_next_core(group, lv_ll_get_head, lv_ll_get_next);
}
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group)
{
focus_next_core(group, lv_ll_get_tail, lv_ll_get_prev);
}
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en)
{
if(en == false) group->frozen = 0;
else group->frozen = 1;
}
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_GROUP_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
{
lv_obj_t * act = lv_group_get_focused(group);
if(act == NULL) return LV_RES_OK;
return act->signal_cb(act, LV_SIGNAL_CONTROLL, &c);
}
/**
* Set a function for a group which will modify the object's style if it is in focus
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
{
group->style_mod = style_mod_func;
if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
}
/**
* Set a function for a group which will modify the object's style if it is in focus in edit mode
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func)
{
group->style_mod_edit = style_mod_func;
if(group->obj_focus != NULL) lv_obj_invalidate(*group->obj_focus);
}
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
{
group->focus_cb = focus_cb;
}
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit: true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit)
{
uint8_t en_val = edit ? 1 : 0;
if(en_val == group->editing) return; /*Do not set the same mode again*/
group->editing = en_val;
lv_obj_t * focused = lv_group_get_focused(group);
if(focused) {
focused->signal_cb(focused, LV_SIGNAL_FOCUS, NULL); /*Focus again to properly leave/open edit/navigate mode*/
lv_res_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED);
if(res != LV_RES_OK) return;
}
lv_obj_invalidate(focused);
}
/**
* Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_click_focus(lv_group_t * group, bool en)
{
group->click_focus = en ? 1 : 0;
}
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy) {
group->refocus_policy = policy & 0x01;
}
/**
* Set whether focus next/prev will allow wrapping from first->last or last->first.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_wrap(lv_group_t * group, bool en)
{
group->wrap = en ? 1 : 0;
}
/**
* Modify a style with the set 'style_mod' function. The input style remains unchanged.
* @param group pointer to group
* @param style pointer to a style to modify
* @return a copy of the input style but modified with the 'style_mod' function
*/
lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style)
{
/*Load the current style. It will be modified by the callback*/
lv_style_copy(&group->style_tmp, style);
if(group->editing) {
if(group->style_mod_edit) group->style_mod_edit(group, &group->style_tmp);
} else {
if(group->style_mod) group->style_mod(group, &group->style_tmp);
}
return &group->style_tmp;
}
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
lv_obj_t * lv_group_get_focused(const lv_group_t * group)
{
if(!group) return NULL;
if(group->obj_focus == NULL) return NULL;
return *group->obj_focus;
}
#if LV_USE_USER_DATA_SINGLE
/**
* Get a pointer to the group's user data
* @param group pointer to an group
* @return pointer to the user data
*/
lv_group_user_data_t * lv_group_get_user_data(lv_group_t * group)
{
return &group->user_data;
}
#endif
/**
* Get a the style modifier function of a group
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_cb(const lv_group_t * group)
{
if(!group) return false;
return group->style_mod ;
}
/**
* Get a the style modifier function of a group in edit mode
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(const lv_group_t * group)
{
if(!group) return false;
return group->style_mod_edit;
}
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
{
if(!group) return false;
return group->focus_cb;
}
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group)
{
if(!group) return false;
return group->editing ? true : false;
}
/**
* Get the `click_focus` attribute.
* @param group pointer to group
* @return true: `click_focus` is enabled; false: disabled
*/
bool lv_group_get_click_focus(const lv_group_t * group)
{
if(!group) return false;
return group->click_focus ? true : false;
}
/**
* Get whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en: true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group)
{
if(!group) return false;
return group->wrap ? true : false;
}
/**
* Notify the group that current theme changed and style modification callbacks need to be refreshed.
* @param group pointer to group. If NULL then all groups are notified.
*/
void lv_group_report_style_mod(lv_group_t * group)
{
lv_theme_t * th = lv_theme_get_current();
if(group != NULL) {
refresh_theme(group, th);
return;
}
lv_group_t * i;
LV_LL_READ(LV_GC_ROOT(_lv_group_ll), i) {
refresh_theme(i, th);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_group_refocus(lv_group_t *g) {
/*Refocus must temporarily allow wrapping to work correctly*/
uint8_t temp_wrap = g->wrap;
g->wrap = 1;
if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
lv_group_focus_next(g);
else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
lv_group_focus_prev(g);
/*Restore wrap property*/
g->wrap = temp_wrap;
}
/**
* Default style modifier function
* @param group pointer to the caller group
* @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
*/
static void style_mod_def(lv_group_t * group, lv_style_t * style)
{
(void)group; /*Unused*/
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_ORANGE;
/*If not empty or has border then emphasis the border*/
if(style->body.opa != LV_OPA_TRANSP || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_ORANGE, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_ORANGE, LV_OPA_60);
style->text.color = lv_color_mix(style->text.color, LV_COLOR_ORANGE, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 2;
#endif
}
/**
* Default style modifier function
* @param group pointer to the caller group
* @param style pointer to a style to modify. (Typically group.style_tmp) It will be OVERWRITTEN.
*/
static void style_mod_edit_def(lv_group_t * group, lv_style_t * style)
{
(void)group; /*Unused*/
#if LV_COLOR_DEPTH != 1
/*Make the style to be a little bit orange*/
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_GREEN;
/*If not empty or has border then emphasis the border*/
if(style->body.opa != LV_OPA_TRANSP || style->body.border.width != 0) style->body.border.width = LV_DPI / 20;
style->body.main_color = lv_color_mix(style->body.main_color, LV_COLOR_GREEN, LV_OPA_70);
style->body.grad_color = lv_color_mix(style->body.grad_color, LV_COLOR_GREEN, LV_OPA_70);
style->body.shadow.color = lv_color_mix(style->body.shadow.color, LV_COLOR_GREEN, LV_OPA_60);
style->text.color = lv_color_mix(style->text.color, LV_COLOR_GREEN, LV_OPA_70);
#else
style->body.border.opa = LV_OPA_COVER;
style->body.border.color = LV_COLOR_BLACK;
style->body.border.width = 3;
#endif
}
static void refresh_theme(lv_group_t * g, lv_theme_t * th)
{
g->style_mod = style_mod_def;
g->style_mod_edit = style_mod_edit_def;
if(th) {
if(th->group.style_mod)
g->style_mod = th->group.style_mod;
if(th->group.style_mod_edit)
g->style_mod_edit = th->group.style_mod_edit;
}
}
static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *), void * (*move)(const lv_ll_t *, const void *))
{
if (group->frozen) return;
lv_obj_t ** obj_next = group->obj_focus;
lv_obj_t ** obj_sentinel = NULL;
bool can_move = true;
bool can_begin = true;
for(;;) {
if(obj_next == NULL) {
if(group->wrap || obj_sentinel == NULL) {
if(!can_begin) return;
obj_next = begin(&group->obj_ll);
can_move = false;
can_begin = false;
} else {
/*Currently focused object is the last/first in the group, keep it that way*/
return;
}
}
if(obj_sentinel == NULL) {
obj_sentinel = obj_next;
if(obj_sentinel == NULL) return; /*Group is empty*/
}
if(can_move) {
obj_next = move(&group->obj_ll, obj_next);
/*Give up if we walked the entire list and haven't found another visible object*/
if(obj_next == obj_sentinel) return;
}
can_move = true;
if(obj_next == NULL) continue;
/*Hidden objects don't receive focus*/
if(!lv_obj_get_hidden(*obj_next)) break;
}
if(obj_next == group->obj_focus) return; /*There's only one visible object and it's already focused*/
if(group->obj_focus) {
(*group->obj_focus)->signal_cb(*group->obj_focus, LV_SIGNAL_DEFOCUS, NULL);
lv_res_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED);
if(res != LV_RES_OK) return;
lv_obj_invalidate(*group->obj_focus);
}
group->obj_focus = obj_next;
(*group->obj_focus)->signal_cb(*group->obj_focus, LV_SIGNAL_FOCUS, NULL);
lv_res_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED);
if(res != LV_RES_OK) return;
/*If the object or its parent has `top == true` bring it to the foregorund*/
obj_to_foreground(*group->obj_focus);
lv_obj_invalidate(*group->obj_focus);
if(group->focus_cb) group->focus_cb(group);
}
static void obj_to_foreground(lv_obj_t * obj)
{
/*Search for 'top' attribute*/
lv_obj_t * i = obj;
lv_obj_t * last_top = NULL;
while(i != NULL) {
if(i->top != 0) last_top = i;
i = lv_obj_get_parent(i);
}
if(last_top != NULL) {
/*Move the last_top object to the foreground*/
lv_obj_t * par = lv_obj_get_parent(last_top);
/*After list change it will be the new head*/
lv_ll_chg_list(&par->child_ll, &par->child_ll, last_top);
lv_obj_invalidate(last_top);
}
}
#endif /*LV_USE_GROUP != 0*/

281
src/lv_core/lv_group.h Normal file
View File

@@ -0,0 +1,281 @@
/**
* @file lv_group.h
*
*/
#ifndef LV_GROUP_H
#define LV_GROUP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/*Predefined keys to control the focused object via lv_group_send(group, c)*/
/*For compatibility in signal function define the keys regardless to LV_GROUP*/
#define LV_GROUP_KEY_UP 17 /*0x11*/
#define LV_GROUP_KEY_DOWN 18 /*0x12*/
#define LV_GROUP_KEY_RIGHT 19 /*0x13*/
#define LV_GROUP_KEY_LEFT 20 /*0x14*/
#define LV_GROUP_KEY_ESC 27 /*0x1B*/
#define LV_GROUP_KEY_DEL 127 /*0x7F*/
#define LV_GROUP_KEY_BACKSPACE 8 /*0x08*/
#define LV_GROUP_KEY_ENTER 10 /*0x0A, '\n'*/
#define LV_GROUP_KEY_NEXT 9 /*0x09, '\t'*/
#define LV_GROUP_KEY_PREV 11 /*0x0B, '*/
#define LV_GROUP_KEY_HOME 2 /*0x02, STX*/
#define LV_GROUP_KEY_END 3 /*0x03, ETX*/
#if LV_USE_GROUP != 0
/**********************
* TYPEDEFS
**********************/
struct _lv_group_t;
typedef void (*lv_group_style_mod_func_t)(struct _lv_group_t *, lv_style_t *);
typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
typedef struct _lv_group_t
{
lv_ll_t obj_ll; /*Linked list to store the objects in the group */
lv_obj_t ** obj_focus; /*The object in focus*/
lv_group_style_mod_func_t style_mod; /*A function which modifies the style of the focused object*/
lv_group_style_mod_func_t style_mod_edit;/*A function which modifies the style of the focused object*/
lv_group_focus_cb_t focus_cb; /*A function to call when a new object is focused (optional)*/
lv_style_t style_tmp; /*Stores the modified style of the focused object */
#if LV_USE_USER_DATA_SINGLE
lv_group_user_data_t user_data;
#endif
#if LV_USE_USER_DATA_MULTI
lv_group_user_data_t focus_user_data;
lv_group_user_data_t style_mod_user_data;
lv_group_user_data_t style_mod_edit_user_data;
#endif
uint8_t frozen :1; /*1: can't focus to new object*/
uint8_t editing :1; /*1: Edit mode, 0: Navigate mode*/
uint8_t click_focus :1; /*1: If an object in a group is clicked by an indev then it will be focused */
uint8_t refocus_policy :1; /*1: Focus prev if focused on deletion. 0: Focus prev if focused on deletion.*/
uint8_t wrap :1; /*1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end of list.*/
} lv_group_t;
typedef enum _lv_group_refocus_policy_t {
LV_GROUP_REFOCUS_POLICY_NEXT = 0,
LV_GROUP_REFOCUS_POLICY_PREV = 1
} lv_group_refocus_policy_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the group module
* @remarks Internal function, do not call directly.
*/
void lv_group_init(void);
/**
* Create a new object group
* @return pointer to the new object group
*/
lv_group_t * lv_group_create(void);
/**
* Delete a group object
* @param group pointer to a group
*/
void lv_group_del(lv_group_t * group);
/**
* Add an object to a group
* @param group pointer to a group
* @param obj pointer to an object to add
*/
void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj);
/**
* Remove an object from its group
* @param obj pointer to an object to remove
*/
void lv_group_remove_obj(lv_obj_t * obj);
/**
* Focus on an object (defocus the current)
* @param obj pointer to an object to focus on
*/
void lv_group_focus_obj(lv_obj_t * obj);
/**
* Focus the next object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_next(lv_group_t * group);
/**
* Focus the previous object in a group (defocus the current)
* @param group pointer to a group
*/
void lv_group_focus_prev(lv_group_t * group);
/**
* Do not let to change the focus from the current object
* @param group pointer to a group
* @param en true: freeze, false: release freezing (normal mode)
*/
void lv_group_focus_freeze(lv_group_t * group, bool en);
/**
* Send a control character to the focuses object of a group
* @param group pointer to a group
* @param c a character (use LV_GROUP_KEY_.. to navigate)
* @return result of focused object in group.
*/
lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c);
/**
* Set a function for a group which will modify the object's style if it is in focus
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func);
/**
* Set a function for a group which will modify the object's style if it is in focus in edit mode
* @param group pointer to a group
* @param style_mod_func the style modifier function pointer
*/
void lv_group_set_style_mod_edit_cb(lv_group_t * group, lv_group_style_mod_func_t style_mod_func);
/**
* Set a function for a group which will be called when a new object is focused
* @param group pointer to a group
* @param focus_cb the call back function or NULL if unused
*/
void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
/**
* Set whether the next or previous item in a group is focused if the currently focussed obj is deleted.
* @param group pointer to a group
* @param new refocus policy enum
*/
void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
/**
* Manually set the current mode (edit or navigate).
* @param group pointer to group
* @param edit: true: edit mode; false: navigate mode
*/
void lv_group_set_editing(lv_group_t * group, bool edit);
/**
* Set the `click_focus` attribute. If enabled then the object will be focused then it is clicked.
* @param group pointer to group
* @param en: true: enable `click_focus`
*/
void lv_group_set_click_focus(lv_group_t * group, bool en);
/**
* Set whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en: true: wrapping enabled; false: wrapping disabled
*/
void lv_group_set_wrap(lv_group_t * group, bool en);
/**
* Modify a style with the set 'style_mod' function. The input style remains unchanged.
* @param group pointer to group
* @param style pointer to a style to modify
* @return a copy of the input style but modified with the 'style_mod' function
*/
lv_style_t * lv_group_mod_style(lv_group_t * group, const lv_style_t * style);
/**
* Get the focused object or NULL if there isn't one
* @param group pointer to a group
* @return pointer to the focused object
*/
lv_obj_t * lv_group_get_focused(const lv_group_t * group);
#if LV_USE_USER_DATA_SINGLE
/**
* Get a pointer to the group's user data
* @param group pointer to an group
* @return pointer to the user data
*/
lv_group_user_data_t * lv_group_get_user_data(lv_group_t * group);
#endif
/**
* Get a the style modifier function of a group
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_cb(const lv_group_t * group);
/**
* Get a the style modifier function of a group in edit mode
* @param group pointer to a group
* @return pointer to the style modifier function
*/
lv_group_style_mod_func_t lv_group_get_style_mod_edit_cb(const lv_group_t * group);
/**
* Get the focus callback function of a group
* @param group pointer to a group
* @return the call back function or NULL if not set
*/
lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
/**
* Get the current mode (edit or navigate).
* @param group pointer to group
* @return true: edit mode; false: navigate mode
*/
bool lv_group_get_editing(const lv_group_t * group);
/**
* Get the `click_focus` attribute.
* @param group pointer to group
* @return true: `click_focus` is enabled; false: disabled
*/
bool lv_group_get_click_focus(const lv_group_t * group);
/**
* Get whether focus next/prev will allow wrapping from first->last or last->first object.
* @param group pointer to group
* @param en: true: wrapping enabled; false: wrapping disabled
*/
bool lv_group_get_wrap(lv_group_t * group);
/**
* Notify the group that current theme changed and style modification callbacks need to be refreshed.
* @param group pointer to group. If NULL then all groups are notified.
*/
void lv_group_report_style_mod(lv_group_t * group);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GROUP != 0*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_GROUP_H*/

1125
src/lv_core/lv_indev.c Normal file

File diff suppressed because it is too large Load Diff

157
src/lv_core/lv_indev.h Normal file
View File

@@ -0,0 +1,157 @@
/**
* @file lv_indev_proc.h
*
*/
#ifndef LV_INDEV_H
#define LV_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../lv_hal/lv_hal_indev.h"
#include "../lv_core/lv_group.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the display input device subsystem
*/
void lv_indev_init(void);
/**
* Get the currently processed input device. Can be used in action functions too.
* @return pointer to the currently processed input device or NULL if no input device processing right now
*/
lv_indev_t * lv_indev_get_act(void);
/**
* Get the type of an input device
* @param indev pointer to an input device
* @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
*/
lv_hal_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
/**
* Reset one or all input devices
* @param indev pointer to an input device to reset or NULL to reset all of them
*/
void lv_indev_reset(lv_indev_t * indev);
/**
* Reset the long press state of an input device
* @param indev_proc pointer to an input device
*/
void lv_indev_reset_long_press(lv_indev_t * indev);
/**
* Enable or disable an input devices
* @param indev pointer to an input device
* @param en true: enable; false: disable
*/
void lv_indev_enable(lv_indev_t * indev, bool en);
/**
* Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
* @param indev pointer to an input device
* @param cur_obj pointer to an object to be used as cursor
*/
void lv_indev_set_cursor(lv_indev_t *indev, lv_obj_t *cur_obj);
#if LV_USE_GROUP
/**
* Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_group(lv_indev_t *indev, lv_group_t *group);
#endif
/**
* Set the an array of points for LV_INDEV_TYPE_BUTTON.
* These points will be assigned to the buttons to press a specific point on the screen
* @param indev pointer to an input device
* @param group point to a group
*/
void lv_indev_set_button_points(lv_indev_t *indev, const lv_point_t *points);
/**
* Set feedback callback for indev.
* @param indev pointer to an input device
* @param feedback feedback callback
*/
void lv_indev_set_feedback(lv_indev_t *indev, lv_indev_feedback_t feedback);
/**
* Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the result
*/
void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
/**
* Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
* @param indev pointer to an input device
* @return the last pressed key (0 on error)
*/
uint32_t lv_indev_get_key(const lv_indev_t * indev);
/**
* Check if there is dragging with an input device or not (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @return true: drag is in progress
*/
bool lv_indev_is_dragging(const lv_indev_t * indev);
/**
* Get the vector of dragging of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
* @param indev pointer to an input device
* @param point pointer to a point to store the vector
*/
void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
/**
* Get elapsed time since last press
* @param indev pointer to an input device (NULL to get the overall smallest inactivity)
* @return Elapsed ticks (milliseconds) since last press
*/
uint32_t lv_indev_get_inactive_time(const lv_indev_t * indev);
/**
* Get feedback callback for indev.
* @param indev pointer to an input device
* @return feedback callback
*/
lv_indev_feedback_t lv_indev_get_feedback(const lv_indev_t *indev);
/**
* Do nothing until the next release
* @param indev pointer to an input device
*/
void lv_indev_wait_release(lv_indev_t * indev);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_INDEV_H*/

2052
src/lv_core/lv_obj.c Normal file

File diff suppressed because it is too large Load Diff

845
src/lv_core/lv_obj.h Normal file
View File

@@ -0,0 +1,845 @@
/**
* @file lv_obj.h
*
*/
#ifndef LV_OBJ_H
#define LV_OBJ_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stddef.h>
#include <stdbool.h>
#include "lv_style.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_mem.h"
#include "../lv_misc/lv_ll.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_log.h"
#include "../lv_hal/lv_hal.h"
/*********************
* DEFINES
*********************/
/*Error check of lv_conf.h*/
#if LV_HOR_RES_MAX == 0 || LV_VER_RES_MAX == 0
#error "LittlevGL: LV_HOR_RES and LV_VER_RES must be greater than 0"
#endif
#if LV_ANTIALIAS > 1
#error "LittlevGL: LV_ANTIALIAS can be only 0 or 1"
#endif
#define LV_ANIM_IN 0x00 /*Animation to show an object. 'OR' it with lv_anim_builtin_t*/
#define LV_ANIM_OUT 0x80 /*Animation to hide an object. 'OR' it with lv_anim_builtin_t*/
#define LV_ANIM_DIR_MASK 0x80 /*ANIM_IN/ANIM_OUT mask*/
#define LV_MAX_ANCESTOR_NUM 8
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
enum
{
LV_DESIGN_DRAW_MAIN,
LV_DESIGN_DRAW_POST,
LV_DESIGN_COVER_CHK,
};
typedef uint8_t lv_design_mode_t;
typedef bool (* lv_design_cb_t) (struct _lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode);
enum
{
LV_RES_INV = 0, /*Typically indicates that the object is deleted (become invalid) in the action function or an operation was failed*/
LV_RES_OK, /*The object is valid (no deleted) after the action*/
};
typedef uint8_t lv_res_t;
typedef enum {
LV_EVENT_PRESSED, /*The object has been pressed*/
LV_EVENT_PRESSING, /*The object is being pressed (called continuously while pressing)*/
LV_EVENT_PRESS_LOST, /*Still pressing but slid from the objects*/
LV_EVENT_SHORT_CLICKED, /*Released before long press time. Not called if dragged.*/
LV_EVENT_LONG_PRESSED, /*Pressing for `LV_INDEV_LONG_PRESS_TIME` time. Not called if dragged.*/
LV_EVENT_LONG_PRESSED_REPEAT, /*Called after `LV_INDEV_LONG_PRESS_TIME` in every `LV_INDEV_LONG_PRESS_REP_TIME` ms. Not called if dragged.*/
LV_EVENT_CLICKED, /*Called on release if not dragged (regardless to long press)*/
LV_EVENT_RELEASED, /*Called in every cases when the object has been released*/
LV_EVENT_LONG_HOVER_IN, /*TODO*/
LV_EVENT_LONG_HOVER_OUT, /*TODO*/
LV_EVENT_DRAG_BEGIN,
LV_EVENT_DRAG_END,
LV_EVENT_DRAG_THROW_BEGIN,
LV_EVENT_FOCUSED,
LV_EVENT_DEFOCUSED,
LV_EVENT_VALUE_CHANGED,
LV_EVENT_REFRESH,
LV_EVENT_APPLY, /*"Ok", "Apply" or similar specific button has clicked*/
LV_EVENT_CANCEL, /*"Close", "Cancel" or similar specific button has clicked*/
}lv_event_t;
typedef void (*lv_event_cb_t)(struct _lv_obj_t * obj, lv_event_t event);
enum
{
/*General signals*/
LV_SIGNAL_CLEANUP,
LV_SIGNAL_CHILD_CHG,
LV_SIGNAL_CORD_CHG,
LV_SIGNAL_PARENT_SIZE_CHG,
LV_SIGNAL_STYLE_CHG,
LV_SIGNAL_REFR_EXT_SIZE,
LV_SIGNAL_GET_TYPE,
_LV_SIGNAL_FEEDBACK_SECTION_START,
/*Input device related*/
LV_SIGNAL_PRESSED,
LV_SIGNAL_PRESSING,
LV_SIGNAL_PRESS_LOST,
LV_SIGNAL_RELEASED,
LV_SIGNAL_LONG_PRESS,
LV_SIGNAL_LONG_PRESS_REP,
LV_SIGNAL_DRAG_BEGIN,
LV_SIGNAL_DRAG_END,
/*Group related*/
LV_SIGNAL_FOCUS,
LV_SIGNAL_DEFOCUS,
LV_SIGNAL_CONTROLL,
_LV_SIGNAL_FEEDBACK_SECTION_END,
LV_SIGNAL_GET_EDITABLE,
};
typedef uint8_t lv_signal_t;
typedef lv_res_t (* lv_signal_cb_t) (struct _lv_obj_t * obj, lv_signal_t sign, void * param);
enum
{
LV_ALIGN_CENTER = 0,
LV_ALIGN_IN_TOP_LEFT,
LV_ALIGN_IN_TOP_MID,
LV_ALIGN_IN_TOP_RIGHT,
LV_ALIGN_IN_BOTTOM_LEFT,
LV_ALIGN_IN_BOTTOM_MID,
LV_ALIGN_IN_BOTTOM_RIGHT,
LV_ALIGN_IN_LEFT_MID,
LV_ALIGN_IN_RIGHT_MID,
LV_ALIGN_OUT_TOP_LEFT,
LV_ALIGN_OUT_TOP_MID,
LV_ALIGN_OUT_TOP_RIGHT,
LV_ALIGN_OUT_BOTTOM_LEFT,
LV_ALIGN_OUT_BOTTOM_MID,
LV_ALIGN_OUT_BOTTOM_RIGHT,
LV_ALIGN_OUT_LEFT_TOP,
LV_ALIGN_OUT_LEFT_MID,
LV_ALIGN_OUT_LEFT_BOTTOM,
LV_ALIGN_OUT_RIGHT_TOP,
LV_ALIGN_OUT_RIGHT_MID,
LV_ALIGN_OUT_RIGHT_BOTTOM,
};
typedef uint8_t lv_align_t;
#if LV_OBJ_REALIGN
typedef struct {
const struct _lv_obj_t * base;
lv_coord_t xofs;
lv_coord_t yofs;
lv_align_t align;
uint8_t auto_realign :1;
uint8_t origo_align :1; /*1: the oigo (center of the object) was aligned with `lv_obj_align_origo`*/
}lv_reailgn_t;
#endif
typedef struct _lv_obj_t
{
struct _lv_obj_t * par; /*Pointer to the parent object*/
lv_ll_t child_ll; /*Linked list to store the children objects*/
lv_area_t coords; /*Coordinates of the object (x1, y1, x2, y2)*/
lv_event_cb_t event_cb;
lv_signal_cb_t signal_cb; /*Object type specific signal function*/
lv_design_cb_t design_cb; /*Object type specific design function*/
void * ext_attr; /*Object type specific extended data*/
lv_style_t * style_p; /*Pointer to the object's style*/
#if LV_USE_GROUP != 0
void * group_p; /*Pointer to the group of the object*/
#endif
/*Attributes and states*/
uint8_t click :1; /*1: Can be pressed by an input device*/
uint8_t drag :1; /*1: Enable the dragging*/
uint8_t drag_throw :1; /*1: Enable throwing with drag*/
uint8_t drag_parent :1; /*1: Parent will be dragged instead*/
uint8_t hidden :1; /*1: Object is hidden*/
uint8_t top :1; /*1: If the object or its children is clicked it goes to the foreground*/
uint8_t opa_scale_en :1; /*1: opa_scale is set*/
uint8_t parent_event :1; /*1: Send the object's events to the parent too. */
uint8_t protect; /*Automatically happening actions can be prevented. 'OR'ed values from `lv_protect_t`*/
lv_opa_t opa_scale; /*Scale down the opacity by this factor. Effects all children as well*/
lv_coord_t ext_size; /*EXTtend the size of the object in every direction. E.g. for shadow drawing*/
#if LV_OBJ_REALIGN
lv_reailgn_t realign;
#endif
#if LV_USE_USER_DATA_SINGLE
lv_obj_user_data_t user_data;
#endif
#if LV_USE_USER_DATA_MULTI
lv_obj_user_data_t event_user_data;
lv_obj_user_data_t signal_user_data;
lv_obj_user_data_t design_user_data;
#endif
} lv_obj_t;
/*Protect some attributes (max. 8 bit)*/
enum
{
LV_PROTECT_NONE = 0x00,
LV_PROTECT_CHILD_CHG = 0x01, /*Disable the child change signal. Used by the library*/
LV_PROTECT_PARENT = 0x02, /*Prevent automatic parent change (e.g. in lv_page)*/
LV_PROTECT_POS = 0x04, /*Prevent automatic positioning (e.g. in lv_cont layout)*/
LV_PROTECT_FOLLOW = 0x08, /*Prevent the object be followed in automatic ordering (e.g. in lv_cont PRETTY layout)*/
LV_PROTECT_PRESS_LOST= 0x10, /*If the `indev` was pressing this object but swiped out while pressing do not search other object.*/
LV_PROTECT_CLICK_FOCUS= 0x20,/*Prevent focusing the object by clicking on it*/
};
typedef uint8_t lv_protect_t;
/*Used by `lv_obj_get_type()`. The object's and its ancestor types are stored here*/
typedef struct {
const char * type[LV_MAX_ANCESTOR_NUM]; /*[0]: the actual type, [1]: ancestor, [2] #1's ancestor ... [x]: "lv_obj" */
} lv_obj_type_t;
enum
{
LV_ANIM_NONE = 0,
LV_ANIM_FLOAT_TOP, /*Float from/to the top*/
LV_ANIM_FLOAT_LEFT, /*Float from/to the left*/
LV_ANIM_FLOAT_BOTTOM, /*Float from/to the bottom*/
LV_ANIM_FLOAT_RIGHT, /*Float from/to the right*/
LV_ANIM_GROW_H, /*Grow/shrink horizontally*/
LV_ANIM_GROW_V, /*Grow/shrink vertically*/
};
typedef uint8_t lv_anim_builtin_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the 'lv' library.
*/
void lv_init(void);
/*--------------------
* Create and delete
*-------------------*/
/**
* Create a basic object
* @param parent pointer to a parent object.
* If NULL then a screen will be created
* @param copy pointer to a base object, if not NULL then the new object will be copied from it
* @return pointer to the new object
*/
lv_obj_t * lv_obj_create(lv_obj_t * parent,const lv_obj_t * copy);
/**
* Delete 'obj' and all of its children
* @param obj pointer to an object to delete
* @return LV_RES_INV because the object is deleted
*/
lv_res_t lv_obj_del(lv_obj_t * obj);
/**
* Delete all children of an object
* @param obj pointer to an object
*/
void lv_obj_clean(lv_obj_t *obj);
/**
* Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
* @param obj pointer to an object
*/
void lv_obj_invalidate(const lv_obj_t * obj);
/*=====================
* Setter functions
*====================*/
/*--------------------
* Parent/children set
*--------------------*/
/**
* Set a new parent for an object. Its relative position will be the same.
* @param obj pointer to an object. Can't be a screen.
* @param parent pointer to the new parent object. (Can't be NULL)
*/
void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent);
/*--------------------
* Coordinate set
* ------------------*/
/**
* Set relative the position of an object (relative to the parent)
* @param obj pointer to an object
* @param x new distance from the left side of the parent
* @param y new distance from the top of the parent
*/
void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
/**
* Set the x coordinate of a object
* @param obj pointer to an object
* @param x new distance from the left side from the parent
*/
void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x);
/**
* Set the y coordinate of a object
* @param obj pointer to an object
* @param y new distance from the top of the parent
*/
void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y);
/**
* Set the size of an object
* @param obj pointer to an object
* @param w new width
* @param h new height
*/
void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
/**
* Set the width of an object
* @param obj pointer to an object
* @param w new width
*/
void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w);
/**
* Set the height of an object
* @param obj pointer to an object
* @param h new height
*/
void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_mod x coordinate shift after alignment
* @param y_mod y coordinate shift after alignment
*/
void lv_obj_align(lv_obj_t * obj,const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod);
/**
* Align an object to an other object.
* @param obj pointer to an object to align
* @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
* @param align type of alignment (see 'lv_align_t' enum)
* @param x_mod x coordinate shift after alignment
* @param y_mod y coordinate shift after alignment
*/
void lv_obj_align_origo(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod);
/**
* Realign the object based on the last `lv_obj_align` parameters.
* @param obj pointer to an object
*/
void lv_obj_realign(lv_obj_t * obj);
/**
* Enable the automatic realign of the object when its size has changed based on the last `lv_obj_align` parameters.
* @param obj pointer to an object
* @param en true: enable auto realign; false: disable auto realign
*/
void lv_obj_set_auto_realign(lv_obj_t * obj, bool en);
/*---------------------
* Appearance set
*--------------------*/
/**
* Set a new style for an object
* @param obj pointer to an object
* @param style_p pointer to the new style
*/
void lv_obj_set_style(lv_obj_t * obj, lv_style_t * style);
/**
* Notify an object about its style is modified
* @param obj pointer to an object
*/
void lv_obj_refresh_style(lv_obj_t * obj);
/**
* Notify all object if a style is modified
* @param style pointer to a style. Only the objects with this style will be notified
* (NULL to notify all objects)
*/
void lv_obj_report_style_mod(lv_style_t * style);
/*-----------------
* Attribute set
*----------------*/
/**
* Hide an object. It won't be visible and clickable.
* @param obj pointer to an object
* @param en true: hide the object
*/
void lv_obj_set_hidden(lv_obj_t * obj, bool en);
/**
* Enable or disable the clicking of an object
* @param obj pointer to an object
* @param en true: make the object clickable
*/
void lv_obj_set_click(lv_obj_t * obj, bool en);
/**
* Enable to bring this object to the foreground if it
* or any of its children is clicked
* @param obj pointer to an object
* @param en true: enable the auto top feature
*/
void lv_obj_set_top(lv_obj_t * obj, bool en);
/**
* Enable the dragging of an object
* @param obj pointer to an object
* @param en true: make the object dragable
*/
void lv_obj_set_drag(lv_obj_t * obj, bool en);
/**
* Enable the throwing of an object after is is dragged
* @param obj pointer to an object
* @param en true: enable the drag throw
*/
void lv_obj_set_drag_throw(lv_obj_t * obj, bool en);
/**
* Enable to use parent for drag related operations.
* If trying to drag the object the parent will be moved instead
* @param obj pointer to an object
* @param en true: enable the 'drag parent' for the object
*/
void lv_obj_set_drag_parent(lv_obj_t * obj, bool en);
/**
* Propagate the events to the parent too
* @param obj pointer to an object
* @param en true: enable the event propagation
*/
void lv_obj_set_parent_event(lv_obj_t * obj, bool en);
/**
* Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`)
* @param obj pointer to an object
* @param en true: opa scaling is enabled for this object and all children; false: no opa scaling
*/
void lv_obj_set_opa_scale_enable(lv_obj_t * obj, bool en);
/**
* Set the opa scale of an object
* @param obj pointer to an object
* @param opa_scale a factor to scale down opacity [0..255]
*/
void lv_obj_set_opa_scale(lv_obj_t * obj, lv_opa_t opa_scale);
/**
* Set a bit or bits in the protect filed
* @param obj pointer to an object
* @param prot 'OR'-ed values from `lv_protect_t`
*/
void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot);
/**
* Clear a bit or bits in the protect filed
* @param obj pointer to an object
* @param prot 'OR'-ed values from `lv_protect_t`
*/
void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot);
/**
* Set a an event handler function for an object.
* Used by the user to react on event which happens with the object.
* @param obj pointer to an object
* @param cb the new event function
*/
void lv_obj_set_event_cb(lv_obj_t * obj, lv_event_cb_t cb);
/**
* Send an event to the object
* @param obj pointer to an object
* @param event the type of the event from `lv_event_t`.
* @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event
*/
lv_res_t lv_obj_send_event(lv_obj_t * obj, lv_event_t event);
/**
* Set the a signal function of an object. Used internally by the library.
* Always call the previous signal function in the new.
* @param obj pointer to an object
* @param cb the new signal function
*/
void lv_obj_set_signal_cb(lv_obj_t * obj, lv_signal_cb_t cb);
/**
* Send an event to the object
* @param obj pointer to an object
* @param event the type of the event from `lv_event_t`.
*/
void lv_obj_send_signal(lv_obj_t * obj, lv_signal_t signal, void * param);
/**
* Set a new design function for an object
* @param obj pointer to an object
* @param cb the new design function
*/
void lv_obj_set_design_cb(lv_obj_t * obj, lv_design_cb_t cb);
/*----------------
* Other set
*--------------*/
/**
* Allocate a new ext. data for an object
* @param obj pointer to an object
* @param ext_size the size of the new ext. data
* @return pointer to the allocated ext
*/
void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size);
/**
* Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object
* @param obj pointer to an object
*/
void lv_obj_refresh_ext_size(lv_obj_t * obj);
#if LV_USE_ANIMATION
/**
* Animate an object
* @param obj pointer to an object to animate
* @param type type of animation from 'lv_anim_builtin_t'. 'OR' it with ANIM_IN or ANIM_OUT
* @param time time of animation in milliseconds
* @param delay delay before the animation in milliseconds
* @param cb a function to call when the animation is ready
*/
void lv_obj_animate(lv_obj_t * obj, lv_anim_builtin_t type, uint16_t time, uint16_t delay, void (*cb) (lv_obj_t *));
#endif
/*=======================
* Getter functions
*======================*/
/**
* Return with the screen of an object
* @param obj pointer to an object
* @return pointer to a screen
*/
lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj);
/**
* Get the display of an object
* @param scr pointer to an object
* @return pointer the object's display
*/
lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj);
/*---------------------
* Parent/children get
*--------------------*/
/**
* Returns with the parent of an object
* @param obj pointer to an object
* @return pointer to the parent of 'obj'
*/
lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj);
/**
* Iterate through the children of an object (start from the "youngest, lastly created")
* @param obj pointer to an object
* @param child NULL at first call to get the next children
* and the previous return value later
* @return the child after 'act_child' or NULL if no more child
*/
lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child);
/**
* Iterate through the children of an object (start from the "oldest", firstly created)
* @param obj pointer to an object
* @param child NULL at first call to get the next children
* and the previous return value later
* @return the child after 'act_child' or NULL if no more child
*/
lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child);
/**
* Count the children of an object (only children directly on 'obj')
* @param obj pointer to an object
* @return children number of 'obj'
*/
uint16_t lv_obj_count_children(const lv_obj_t * obj);
/*---------------------
* Coordinate get
*--------------------*/
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
* @param cords_p pointer to an area to store the coordinates
*/
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p);
/**
* Get the x coordinate of object
* @param obj pointer to an object
* @return distance of 'obj' from the left side of its parent
*/
lv_coord_t lv_obj_get_x(const lv_obj_t * obj);
/**
* Get the y coordinate of object
* @param obj pointer to an object
* @return distance of 'obj' from the top of its parent
*/
lv_coord_t lv_obj_get_y(const lv_obj_t * obj);
/**
* Get the width of an object
* @param obj pointer to an object
* @return the width
*/
lv_coord_t lv_obj_get_width(const lv_obj_t * obj);
/**
* Get the height of an object
* @param obj pointer to an object
* @return the height
*/
lv_coord_t lv_obj_get_height(const lv_obj_t * obj);
/**
* Get that width reduced by the left and right padding.
* @param obj pointer to an object
* @return the width which still fits into the container
*/
lv_coord_t lv_obj_get_width_fit(lv_obj_t * obj);
/**
* Get that height reduced by the top an bottom padding.
* @param obj pointer to an object
* @return the height which still fits into the container
*/
lv_coord_t lv_obj_get_height_fit(lv_obj_t * obj);
/**
* Get the extended size attribute of an object
* @param obj pointer to an object
* @return the extended size attribute
*/
lv_coord_t lv_obj_get_ext_size(const lv_obj_t * obj);
/**
* Get the automatic realign property of the object.
* @param obj pointer to an object
* @return true: auto realign is enabled; false: auto realign is disabled
*/
bool lv_obj_get_auto_realign(lv_obj_t * obj);
/*-----------------
* Appearance get
*---------------*/
/**
* Get the style pointer of an object (if NULL get style of the parent)
* @param obj pointer to an object
* @return pointer to a style
*/
lv_style_t * lv_obj_get_style(const lv_obj_t * obj);
/*-----------------
* Attribute get
*----------------*/
/**
* Get the hidden attribute of an object
* @param obj pointer to an object
* @return true: the object is hidden
*/
bool lv_obj_get_hidden(const lv_obj_t * obj);
/**
* Get the click enable attribute of an object
* @param obj pointer to an object
* @return true: the object is clickable
*/
bool lv_obj_get_click(const lv_obj_t * obj);
/**
* Get the top enable attribute of an object
* @param obj pointer to an object
* @return true: the auto top feature is enabled
*/
bool lv_obj_get_top(const lv_obj_t * obj);
/**
* Get the drag enable attribute of an object
* @param obj pointer to an object
* @return true: the object is dragable
*/
bool lv_obj_get_drag(const lv_obj_t * obj);
/**
* Get the drag throw enable attribute of an object
* @param obj pointer to an object
* @return true: drag throw is enabled
*/
bool lv_obj_get_drag_throw(const lv_obj_t * obj);
/**
* Get the drag parent attribute of an object
* @param obj pointer to an object
* @return true: drag parent is enabled
*/
bool lv_obj_get_drag_parent(const lv_obj_t * obj);
/**
* Get the drag parent attribute of an object
* @param obj pointer to an object
* @return true: drag parent is enabled
*/
bool lv_obj_get_parent_event(const lv_obj_t * obj);
/**
* Get the opa scale enable parameter
* @param obj pointer to an object
* @return true: opa scaling is enabled for this object and all children; false: no opa scaling
*/
lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj);
/**
* Get the opa scale parameter of an object
* @param obj pointer to an object
* @return opa scale [0..255]
*/
lv_opa_t lv_obj_get_opa_scale(const lv_obj_t * obj);
/**
* Get the protect field of an object
* @param obj pointer to an object
* @return protect field ('OR'ed values of `lv_protect_t`)
*/
uint8_t lv_obj_get_protect(const lv_obj_t * obj);
/**
* Check at least one bit of a given protect bitfield is set
* @param obj pointer to an object
* @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
* @return false: none of the given bits are set, true: at least one bit is set
*/
bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot);
/**
* Get the signal function of an object
* @param obj pointer to an object
* @return the signal function
*/
lv_signal_cb_t lv_obj_get_signal_func(const lv_obj_t * obj);
/**
* Get the design function of an object
* @param obj pointer to an object
* @return the design function
*/
lv_design_cb_t lv_obj_get_design_func(const lv_obj_t * obj);
/*------------------
* Other get
*-----------------*/
/**
* Get the ext pointer
* @param obj pointer to an object
* @return the ext pointer but not the dynamic version
* Use it as ext->data1, and NOT da(ext)->data1
*/
void * lv_obj_get_ext_attr(const lv_obj_t * obj);
/**
* Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
* E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
* @param obj pointer to an object which type should be get
* @param buf pointer to an `lv_obj_type_t` buffer to store the types
*/
void lv_obj_get_type(lv_obj_t * obj, lv_obj_type_t * buf);
#if LV_USE_USER_DATA_SINGLE
/**
* Get a pointer to the object's user data
* @param obj pointer to an object
* @return pointer to the user data
*/
lv_obj_user_data_t * lv_obj_get_user_data(lv_obj_t * obj);
#endif
#if LV_USE_GROUP
/**
* Get the group of the object
* @param obj pointer to an object
* @return the pointer to group of the object
*/
void * lv_obj_get_group(const lv_obj_t * obj);
/**
* Tell whether the object is the focused object of a group or not.
* @param obj pointer to an object
* @return true: the object is focused, false: the object is not focused or not in a group
*/
bool lv_obj_is_focused(const lv_obj_t * obj);
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_OBJ_H*/

554
src/lv_core/lv_refr.c Normal file
View File

@@ -0,0 +1,554 @@
/**
* @file lv_refr.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_refr.h"
#include "lv_disp.h"
#include "../lv_hal/lv_hal_tick.h"
#include "../lv_hal/lv_hal_disp.h"
#include "../lv_misc/lv_task.h"
#include "../lv_misc/lv_mem.h"
#include "../lv_misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_refr_task(void * param);
static void lv_refr_join_area(void);
static void lv_refr_areas(void);
static void lv_refr_area(const lv_area_t * area_p);
static void lv_refr_area_part(const lv_area_t * area_p);
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p);
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p);
static void lv_refr_vdb_flush(void);
/**********************
* STATIC VARIABLES
**********************/
static uint32_t px_num;
static lv_disp_t * disp_refr; /*Display being refreshed*/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void lv_refr_init(void)
{
lv_task_t * task;
task = lv_task_create(lv_refr_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);
lv_task_ready(task); /*Be sure the screen will be refreshed immediately on start up*/
}
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process can
* prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process (e.g. progress bar)
* this function can be called when the screen should be updated.
*/
void lv_refr_now(void)
{
lv_refr_task(NULL);
}
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is only one display)
*/
void lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) return;
/*Clear the invalidate buffer if the parameter is NULL*/
if(area_p == NULL) {
disp->inv_p = 0;
return;
}
lv_area_t scr_area;
scr_area.x1 = 0;
scr_area.y1 = 0;
scr_area.x2 = disp->driver.hor_res - 1;
scr_area.y2 = disp->driver.ver_res - 1;
lv_area_t com_area;
bool suc;
suc = lv_area_intersect(&com_area, area_p, &scr_area);
/*The area is truncated to the screen*/
if(suc != false) {
if(disp->driver.rounder_cb) disp->driver.rounder_cb(&disp_refr->driver, &com_area);
/*Save only if this area is not in one of the saved areas*/
uint16_t i;
for(i = 0; i < disp->inv_p; i++) {
if(lv_area_is_in(&com_area, &disp->inv_areas[i]) != false) return;
}
/*Save the area*/
if(disp->inv_p < LV_INV_BUF_SIZE) {
lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
} else {/*If no place for the area add the screen*/
disp->inv_p = 0;
lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
}
disp->inv_p ++;
}
}
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * lv_refr_get_disp_refreshing(void)
{
return disp_refr;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Called periodically to handle the refreshing
* @param param unused
*/
static void lv_refr_task(void * param)
{
(void)param;
LV_LOG_TRACE("lv_refr_task: started");
uint32_t start = lv_tick_get();
LV_LL_READ(LV_GC_ROOT(_lv_disp_ll), disp_refr) {
LV_LOG_TRACE("lv_refr_task: refreshing a display");
lv_refr_join_area();
lv_refr_areas();
/*If refresh happened ...*/
if(disp_refr->inv_p != 0) {
/*In true double buffered mode copy the refreshed areas to the new VDB to keep it up to date*/
if(lv_disp_is_true_double_buf(disp_refr)) {
lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
/*Flush the content of the VDB*/
lv_refr_vdb_flush();
/* With true double buffering the flushing should be only the address change of the current frame buffer.
* Wait until the address change is ready and copy the changed content to the other frame buffer (new active VDB)
* to keep the buffers synchronized*/
while(vdb->flushing);
uint8_t * buf_act = (uint8_t *) vdb->buf_act;
uint8_t * buf_ina = (uint8_t *) vdb->buf_act == vdb->buf1 ? vdb->buf2 : vdb->buf1;
lv_coord_t hres = lv_disp_get_hor_res(disp_refr);
uint16_t a;
for(a = 0; a < disp_refr->inv_p; a++) {
if(disp_refr->inv_area_joined[a] == 0) {
lv_coord_t y;
uint32_t start_offs = (hres * disp_refr->inv_areas[a].y1 + disp_refr->inv_areas[a].x1) * sizeof(lv_color_t);
uint32_t line_length = lv_area_get_width(&disp_refr->inv_areas[a]) * sizeof(lv_color_t);
for(y = disp_refr->inv_areas[a].y1; y <= disp_refr->inv_areas[a].y2; y++) {
memcpy(buf_act + start_offs, buf_ina + start_offs, line_length);
start_offs += hres * sizeof(lv_color_t);
}
}
}
} /*End of true double buffer handling*/
/*Clean up*/
memset(disp_refr->inv_areas, 0, sizeof(disp_refr->inv_areas));
memset(disp_refr->inv_area_joined, 0, sizeof(disp_refr->inv_area_joined));
disp_refr->inv_p = 0;
/*Call monitor cb if present*/
if(disp_refr->driver.monitor_cb) {
disp_refr->driver.monitor_cb(&disp_refr->driver, lv_tick_elaps(start), px_num);
}
}
}
LV_LOG_TRACE("lv_refr_task: ready");
}
/**
* Join the areas which has got common parts
*/
static void lv_refr_join_area(void)
{
uint32_t join_from;
uint32_t join_in;
lv_area_t joined_area;
for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
if(disp_refr->inv_area_joined[join_in] != 0) continue;
/*Check all areas to join them in 'join_in'*/
for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
/*Handle only unjoined areas and ignore itself*/
if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
continue;
}
/*Check if the areas are on each other*/
if(lv_area_is_on(&disp_refr->inv_areas[join_in],
&disp_refr->inv_areas[join_from]) == false) {
continue;
}
lv_area_join(&joined_area, &disp_refr->inv_areas[join_in],
&disp_refr->inv_areas[join_from]);
/*Join two area only if the joined area size is smaller*/
if(lv_area_get_size(&joined_area) <
(lv_area_get_size(&disp_refr->inv_areas[join_in]) + lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
/*Mark 'join_form' is joined into 'join_in'*/
disp_refr->inv_area_joined[join_from] = 1;
}
}
}
}
/**
* Refresh the joined areas
*/
static void lv_refr_areas(void)
{
px_num = 0;
uint32_t i;
for(i = 0; i < disp_refr->inv_p; i++) {
/*Refresh the unjoined areas*/
if(disp_refr->inv_area_joined[i] == 0) {
lv_refr_area(&disp_refr->inv_areas[i]);
if(disp_refr->driver.monitor_cb) px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
}
}
}
/**
* Refresh an area if there is Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area(const lv_area_t * area_p)
{
/*True double buffering: there are two screen sized buffers. Just redraw directly into a buffer*/
if(lv_disp_is_true_double_buf(disp_refr)) {
lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
vdb->area.x1 = 0;
vdb->area.x2 = lv_disp_get_hor_res(disp_refr) - 1;
vdb->area.y1 = 0;
vdb->area.y2 = lv_disp_get_ver_res(disp_refr) - 1;
lv_refr_area_part(area_p);
}
/*The buffer is smaller: refresh the area in parts*/
else {
lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
/*Calculate the max row num*/
lv_coord_t w = lv_area_get_width(area_p);
lv_coord_t h = lv_area_get_height(area_p);
lv_coord_t y2 = area_p->y2 >= lv_disp_get_ver_res(disp_refr) ? y2 = lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
int32_t max_row = (uint32_t) vdb->size / w;
if(max_row > h) max_row = h;
/*Round down the lines of VDB if rounding is added*/
if(disp_refr->driver.rounder_cb) {
lv_area_t tmp;
tmp.x1 = 0;
tmp.x2 = 0;
tmp.y1 = 0;
tmp.y2 = max_row;
lv_coord_t y_tmp = max_row;
do {
tmp.y2 = y_tmp;
disp_refr->driver.rounder_cb(&disp_refr->driver, &tmp);
y_tmp --; /*Decrement the number of line until it is rounded to a smaller (or equal) value then the original. */
} while(lv_area_get_height(&tmp) > max_row && y_tmp != 0);
if(y_tmp == 0) {
LV_LOG_WARN("Can't set VDB height using the round function. (Wrong round_cb or to small VDB)");
return;
} else {
max_row = tmp.y2 + 1;
}
}
/*Always use the full row*/
lv_coord_t row;
lv_coord_t row_last = 0;
for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
/*Calc. the next y coordinates of VDB*/
vdb->area.x1 = area_p->x1;
vdb->area.x2 = area_p->x2;
vdb->area.y1 = row;
vdb->area.y2 = row + max_row - 1;
if(vdb->area.y2 > y2) vdb->area.y2 = y2;
row_last = vdb->area.y2;
lv_refr_area_part(area_p);
}
/*If the last y coordinates are not handled yet ...*/
if(y2 != row_last) {
/*Calc. the next y coordinates of VDB*/
vdb->area.x1 = area_p->x1;
vdb->area.x2 = area_p->x2;
vdb->area.y1 = row;
vdb->area.y2 = y2;
/*Refresh this part too*/
lv_refr_area_part(area_p);
}
}
}
/**
* Refresh a part of an area which is on the actual Virtual Display Buffer
* @param area_p pointer to an area to refresh
*/
static void lv_refr_area_part(const lv_area_t * area_p)
{
lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
/*In non double buffered mode, before rendering the next part wait until the previous image is flushed*/
if(lv_disp_is_double_buf(disp_refr) == false) {
while(vdb->flushing);
}
lv_obj_t * top_p;
/*Get the new mask from the original area and the act. VDB
It will be a part of 'area_p'*/
lv_area_t start_mask;
lv_area_intersect(&start_mask, area_p, &vdb->area);
/*Get the most top object which is not covered by others*/
top_p = lv_refr_get_top_obj(&start_mask, lv_disp_get_scr_act(disp_refr));
/*Do the refreshing from the top object*/
lv_refr_obj_and_children(top_p, &start_mask);
/*Also refresh top and sys layer unconditionally*/
lv_refr_obj_and_children(lv_disp_get_layer_top(disp_refr), &start_mask);
lv_refr_obj_and_children(lv_disp_get_layer_sys(disp_refr), &start_mask);
/* In true double buffered mode flush only once when all areas were rendered.
* In normal mode flush after every area */
if(lv_disp_is_true_double_buf(disp_refr) == false) {
lv_refr_vdb_flush();
}
}
/**
* Search the most top object which fully covers an area
* @param area_p pointer to an area
* @param obj the first object to start the searching (typically a screen)
* @return
*/
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
{
lv_obj_t * i;
lv_obj_t * found_p = NULL;
/*If this object is fully cover the draw area check the children too */
if(lv_area_is_in(area_p, &obj->coords) && obj->hidden == 0) {
LV_LL_READ(obj->child_ll, i) {
found_p = lv_refr_get_top_obj(area_p, i);
/*If a children is ok then break*/
if(found_p != NULL) {
break;
}
}
/*If no better children check this object*/
if(found_p == NULL) {
lv_style_t * style = lv_obj_get_style(obj);
if(style->body.opa == LV_OPA_COVER &&
obj->design_cb(obj, area_p, LV_DESIGN_COVER_CHK) != false &&
lv_obj_get_opa_scale(obj) == LV_OPA_COVER) {
found_p = obj;
}
}
}
return found_p;
}
/**
* Make the refreshing from an object. Draw all its children and the youngers too.
* @param top_p pointer to an objects. Start the drawing from it.
* @param mask_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
{
/* Normally always will be a top_obj (at least the screen)
* but in special cases (e.g. if the screen has alpha) it won't.
* In this case use the screen directly */
if(top_p == NULL) top_p = lv_disp_get_scr_act(disp_refr);
/*Refresh the top object and its children*/
lv_refr_obj(top_p, mask_p);
/*Draw the 'younger' sibling objects because they can be on top_obj */
lv_obj_t * par;
lv_obj_t * i;
lv_obj_t * border_p = top_p;
par = lv_obj_get_parent(top_p);
/*Do until not reach the screen*/
while(par != NULL) {
/*object before border_p has to be redrawn*/
i = lv_ll_get_prev(&(par->child_ll), border_p);
while(i != NULL) {
/*Refresh the objects*/
lv_refr_obj(i, mask_p);
i = lv_ll_get_prev(&(par->child_ll), i);
}
/*The new border will be there last parents,
*so the 'younger' brothers of parent will be refreshed*/
border_p = par;
/*Go a level deeper*/
par = lv_obj_get_parent(par);
}
/*Call the post draw design function of the parents of the to object*/
par = lv_obj_get_parent(top_p);
while(par != NULL) {
par->design_cb(par, mask_p, LV_DESIGN_DRAW_POST);
par = lv_obj_get_parent(par);
}
}
/**
* Refresh an object an all of its children. (Called recursively)
* @param obj pointer to an object to refresh
* @param mask_ori_p pointer to an area, the objects will be drawn only here
*/
static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
{
/*Do not refresh hidden objects*/
if(obj->hidden != 0) return;
bool union_ok; /* Store the return value of area_union */
/* Truncate the original mask to the coordinates of the parent
* because the parent and its children are visible only here */
lv_area_t obj_mask;
lv_area_t obj_ext_mask;
lv_area_t obj_area;
lv_coord_t ext_size = obj->ext_size;
lv_obj_get_coords(obj, &obj_area);
obj_area.x1 -= ext_size;
obj_area.y1 -= ext_size;
obj_area.x2 += ext_size;
obj_area.y2 += ext_size;
union_ok = lv_area_intersect(&obj_ext_mask, mask_ori_p, &obj_area);
/*Draw the parent and its children only if they ore on 'mask_parent'*/
if(union_ok != false) {
/* Redraw the object */
obj->design_cb(obj, &obj_ext_mask, LV_DESIGN_DRAW_MAIN);
//usleep(5 * 1000); /*DEBUG: Wait after every object draw to see the order of drawing*/
/*Create a new 'obj_mask' without 'ext_size' because the children can't be visible there*/
lv_obj_get_coords(obj, &obj_area);
union_ok = lv_area_intersect(&obj_mask, mask_ori_p, &obj_area);
if(union_ok != false) {
lv_area_t mask_child; /*Mask from obj and its child*/
lv_obj_t * child_p;
lv_area_t child_area;
LV_LL_READ_BACK(obj->child_ll, child_p) {
lv_obj_get_coords(child_p, &child_area);
ext_size = child_p->ext_size;
child_area.x1 -= ext_size;
child_area.y1 -= ext_size;
child_area.x2 += ext_size;
child_area.y2 += ext_size;
/* Get the union (common parts) of original mask (from obj)
* and its child */
union_ok = lv_area_intersect(&mask_child, &obj_mask, &child_area);
/*If the parent and the child has common area then refresh the child */
if(union_ok) {
/*Refresh the next children*/
lv_refr_obj(child_p, &mask_child);
}
}
}
/* If all the children are redrawn make 'post draw' design */
obj->design_cb(obj, &obj_ext_mask, LV_DESIGN_DRAW_POST);
}
}
/**
* Flush the content of the VDB
*/
static void lv_refr_vdb_flush(void)
{
lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
/*In double buffered mode wait until the other buffer is flushed before flushing the current one*/
if(lv_disp_is_double_buf(disp_refr)) {
while(vdb->flushing);
}
vdb->flushing = 1;
/*Flush the rendered content to the display*/
lv_disp_t * disp = lv_refr_get_disp_refreshing();
if(disp->driver.flush_cb) disp->driver.flush_cb(&disp->driver, &vdb->area, vdb->buf_act);
if(vdb->buf1 && vdb->buf2) {
if(vdb->buf_act == vdb->buf1) vdb->buf_act = vdb->buf2;
else vdb->buf_act = vdb->buf1;
/*If the screen is transparent initialize it when the new VDB is selected*/
# if LV_COLOR_SCREEN_TRANSP
memset(vdb[vdb_active].buf, 0x00, LV_VDB_SIZE_IN_BYTES);
# endif /*LV_COLOR_SCREEN_TRANSP*/
}
}

90
src/lv_core/lv_refr.h Normal file
View File

@@ -0,0 +1,90 @@
/**
* @file lv_refr.h
*
*/
#ifndef LV_REFR_H
#define LV_REFR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the screen refresh subsystem
*/
void lv_refr_init(void);
/**
* Redraw the invalidated areas now.
* Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process can
* prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process (e.g. progress bar)
* this function can be called when the screen should be updated.
*/
void lv_refr_now(void);
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is only one display)
*/
void lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p);
/**
* Get the number of areas in the buffer
* @return number of invalid areas
*/
uint16_t lv_refr_get_buf_size(void);
/**
* Pop (delete) the last 'num' invalidated areas from the buffer
* @param num number of areas to delete
*/
void lv_refr_pop_from_buf(uint16_t num);
/**
* Get the display which is being refreshed
* @return the display being refreshed
*/
lv_disp_t * lv_refr_get_disp_refreshing(void);
/**********************
* STATIC FUNCTIONS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_REFR_H*/

356
src/lv_core/lv_style.c Normal file
View File

@@ -0,0 +1,356 @@
/**
* @file lv_style.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "../lv_misc/lv_mem.h"
/*********************
* DEFINES
*********************/
#define STYLE_MIX_MAX 256
#define STYLE_MIX_SHIFT 8 /*log2(STYLE_MIX_MAX)*/
#define VAL_PROP(v1, v2, r) v1 + (((v2-v1) * r) >> STYLE_MIX_SHIFT)
#define STYLE_ATTR_MIX(attr, r) if(start->attr != end->attr) {res->attr = VAL_PROP(start->attr, end->attr, r);} else {res->attr = start->attr;}
/**********************
* TYPEDEFS
**********************/
#if LV_USE_ANIMATION
typedef struct {
lv_style_t style_start; /*Save not only pointers because can be same as 'style_anim' then it will be modified too*/
lv_style_t style_end;
lv_style_t * style_anim;
void (*end_cb)(void *);
} lv_style_anim_dsc_t;
#endif
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_USE_ANIMATION
static void style_animator(lv_style_anim_dsc_t * dsc, int32_t val);
static void style_animation_common_end_cb(void * ptr);
#endif
/**********************
* STATIC VARIABLES
**********************/
lv_style_t lv_style_scr;
lv_style_t lv_style_transp;
lv_style_t lv_style_transp_fit;
lv_style_t lv_style_transp_tight;
lv_style_t lv_style_plain;
lv_style_t lv_style_plain_color;
lv_style_t lv_style_pretty;
lv_style_t lv_style_pretty_color;
lv_style_t lv_style_btn_rel;
lv_style_t lv_style_btn_pr;
lv_style_t lv_style_btn_tgl_rel;
lv_style_t lv_style_btn_tgl_pr;
lv_style_t lv_style_btn_ina;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Init the basic styles
*/
void lv_style_init(void)
{
/* Not White/Black/Gray colors are created by HSV model with
* HUE = 210*/
/*Screen style*/
lv_style_scr.glass = 0;
lv_style_scr.body.opa = LV_OPA_COVER;
lv_style_scr.body.main_color = LV_COLOR_WHITE;
lv_style_scr.body.grad_color = LV_COLOR_WHITE;
lv_style_scr.body.radius = 0;
lv_style_scr.body.padding.left = 0;
lv_style_scr.body.padding.right = 0;
lv_style_scr.body.padding.top = 0;
lv_style_scr.body.padding.bottom = 0;
lv_style_scr.body.padding.inner = LV_DPI / 12;
lv_style_scr.body.border.color = LV_COLOR_BLACK;
lv_style_scr.body.border.opa = LV_OPA_COVER;
lv_style_scr.body.border.width = 0;
lv_style_scr.body.border.part = LV_BORDER_FULL;
lv_style_scr.body.shadow.color = LV_COLOR_GRAY;
lv_style_scr.body.shadow.type = LV_SHADOW_FULL;
lv_style_scr.body.shadow.width = 0;
lv_style_scr.text.opa = LV_OPA_COVER;
lv_style_scr.text.color = lv_color_make(0x30, 0x30, 0x30);
lv_style_scr.text.font = LV_FONT_DEFAULT;
lv_style_scr.text.letter_space = 2;
lv_style_scr.text.line_space = 2;
lv_style_scr.image.opa = LV_OPA_COVER;
lv_style_scr.image.color = lv_color_make(0x20, 0x20, 0x20);
lv_style_scr.image.intense = LV_OPA_TRANSP;
lv_style_scr.line.opa = LV_OPA_COVER;
lv_style_scr.line.color = lv_color_make(0x20, 0x20, 0x20);
lv_style_scr.line.width = 2;
lv_style_scr.line.rounded = 0;
/*Plain style (by default near the same as the screen style)*/
memcpy(&lv_style_plain, &lv_style_scr, sizeof(lv_style_t));
lv_style_plain.body.padding.left = LV_DPI / 12;
lv_style_plain.body.padding.right = LV_DPI / 12;
lv_style_plain.body.padding.top = LV_DPI / 12;
lv_style_plain.body.padding.bottom = LV_DPI / 12;
/*Plain color style*/
memcpy(&lv_style_plain_color, &lv_style_plain, sizeof(lv_style_t));
lv_style_plain_color.text.color = lv_color_make(0xf0, 0xf0, 0xf0);
lv_style_plain_color.image.color = lv_color_make(0xf0, 0xf0, 0xf0);
lv_style_plain_color.line.color = lv_color_make(0xf0, 0xf0, 0xf0);
lv_style_plain_color.body.main_color = lv_color_make(0x55, 0x96, 0xd8);
lv_style_plain_color.body.grad_color = lv_style_plain_color.body.main_color;
/*Pretty style */
memcpy(&lv_style_pretty, &lv_style_plain, sizeof(lv_style_t));
lv_style_pretty.text.color = lv_color_make(0x20, 0x20, 0x20);
lv_style_pretty.image.color = lv_color_make(0x20, 0x20, 0x20);
lv_style_pretty.line.color = lv_color_make(0x20, 0x20, 0x20);
lv_style_pretty.body.main_color = LV_COLOR_WHITE;
lv_style_pretty.body.grad_color = LV_COLOR_SILVER;
lv_style_pretty.body.radius = LV_DPI / 15;
lv_style_pretty.body.border.color = lv_color_make(0x40, 0x40, 0x40);
lv_style_pretty.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
lv_style_pretty.body.border.opa = LV_OPA_30;
/*Pretty color style*/
memcpy(&lv_style_pretty_color, &lv_style_pretty, sizeof(lv_style_t));
lv_style_pretty_color.text.color = lv_color_make(0xe0, 0xe0, 0xe0);
lv_style_pretty_color.image.color = lv_color_make(0xe0, 0xe0, 0xe0);
lv_style_pretty_color.line.color = lv_color_make(0xc0, 0xc0, 0xc0);
lv_style_pretty_color.body.main_color = lv_color_make(0x6b, 0x9a, 0xc7);
lv_style_pretty_color.body.grad_color = lv_color_make(0x2b, 0x59, 0x8b);
lv_style_pretty_color.body.border.color = lv_color_make(0x15, 0x2c, 0x42);
/*Transparent style*/
memcpy(&lv_style_transp, &lv_style_plain, sizeof(lv_style_t));
lv_style_transp.glass = 1;
lv_style_transp.body.border.width = 0;
lv_style_transp.body.opa = LV_OPA_TRANSP;
/*Transparent fitting size*/
memcpy(&lv_style_transp_fit, &lv_style_transp, sizeof(lv_style_t));
lv_style_transp_fit.body.padding.left = 0;
lv_style_transp_fit.body.padding.right = 0;
lv_style_transp_fit.body.padding.top = 0;
lv_style_transp_fit.body.padding.bottom = 0;
/*Transparent tight style*/
memcpy(&lv_style_transp_tight, &lv_style_transp_fit, sizeof(lv_style_t));
lv_style_transp_tight.body.padding.inner = 0;
/*Button released style*/
memcpy(&lv_style_btn_rel, &lv_style_plain, sizeof(lv_style_t));
lv_style_btn_rel.body.main_color = lv_color_make(0x76, 0xa2, 0xd0);
lv_style_btn_rel.body.grad_color = lv_color_make(0x19, 0x3a, 0x5d);
lv_style_btn_rel.body.radius = LV_DPI / 15;
lv_style_btn_rel.body.padding.left= LV_DPI / 4;
lv_style_btn_rel.body.padding.right = LV_DPI / 4;
lv_style_btn_rel.body.padding.top = LV_DPI / 6;
lv_style_btn_rel.body.padding.bottom = LV_DPI / 6;
lv_style_btn_rel.body.padding.inner = LV_DPI / 10;
lv_style_btn_rel.body.border.color = lv_color_make(0x0b, 0x19, 0x28);
lv_style_btn_rel.body.border.width = LV_DPI / 50 >= 1 ? LV_DPI / 50 : 1;
lv_style_btn_rel.body.border.opa = LV_OPA_70;
lv_style_btn_rel.body.shadow.color = LV_COLOR_GRAY;
lv_style_btn_rel.body.shadow.width = 0;
lv_style_btn_rel.text.color = lv_color_make(0xff, 0xff, 0xff);
lv_style_btn_rel.image.color = lv_color_make(0xff, 0xff, 0xff);
/*Button pressed style*/
memcpy(&lv_style_btn_pr, &lv_style_btn_rel, sizeof(lv_style_t));
lv_style_btn_pr.body.main_color = lv_color_make(0x33, 0x62, 0x94);
lv_style_btn_pr.body.grad_color = lv_color_make(0x10, 0x26, 0x3c);
lv_style_btn_pr.text.color = lv_color_make(0xa4, 0xb5, 0xc6);
lv_style_btn_pr.image.color = lv_color_make(0xa4, 0xb5, 0xc6);
lv_style_btn_pr.line.color = lv_color_make(0xa4, 0xb5, 0xc6);
/*Button toggle released style*/
memcpy(&lv_style_btn_tgl_rel, &lv_style_btn_rel, sizeof(lv_style_t));
lv_style_btn_tgl_rel.body.main_color = lv_color_make(0x0a, 0x11, 0x22);
lv_style_btn_tgl_rel.body.grad_color = lv_color_make(0x37, 0x62, 0x90);
lv_style_btn_tgl_rel.body.border.color = lv_color_make(0x01, 0x07, 0x0d);
lv_style_btn_tgl_rel.text.color = lv_color_make(0xc8, 0xdd, 0xf4);
lv_style_btn_tgl_rel.image.color = lv_color_make(0xc8, 0xdd, 0xf4);
lv_style_btn_tgl_rel.line.color = lv_color_make(0xc8, 0xdd, 0xf4);
/*Button toggle pressed style*/
memcpy(&lv_style_btn_tgl_pr, &lv_style_btn_tgl_rel, sizeof(lv_style_t));
lv_style_btn_tgl_pr.body.main_color = lv_color_make(0x02, 0x14, 0x27);
lv_style_btn_tgl_pr.body.grad_color = lv_color_make(0x2b, 0x4c, 0x70);
lv_style_btn_tgl_pr.text.color = lv_color_make(0xa4, 0xb5, 0xc6);
lv_style_btn_tgl_pr.image.color = lv_color_make(0xa4, 0xb5, 0xc6);
lv_style_btn_tgl_pr.line.color = lv_color_make(0xa4, 0xb5, 0xc6);
/*Button inactive style*/
memcpy(&lv_style_btn_ina, &lv_style_btn_rel, sizeof(lv_style_t));
lv_style_btn_ina.body.main_color = lv_color_make(0xd8, 0xd8, 0xd8);
lv_style_btn_ina.body.grad_color = lv_color_make(0xd8, 0xd8, 0xd8);
lv_style_btn_ina.body.border.color = lv_color_make(0x90, 0x90, 0x90);
lv_style_btn_ina.text.color = lv_color_make(0x70, 0x70, 0x70);
lv_style_btn_ina.image.color = lv_color_make(0x70, 0x70, 0x70);
lv_style_btn_ina.line.color = lv_color_make(0x70, 0x70, 0x70);
}
/**
* Copy a style to an other
* @param dest pointer to the destination style
* @param src pointer to the source style
*/
void lv_style_copy(lv_style_t * dest, const lv_style_t * src)
{
memcpy(dest, src, sizeof(lv_style_t));
}
/**
* Mix two styles according to a given ratio
* @param start start style
* @param end end style
* @param res store the result style here
* @param ratio the ratio of mix [0..256]; 0: `start` style; 256: `end` style
*/
void lv_style_mix(const lv_style_t * start, const lv_style_t * end, lv_style_t * res, uint16_t ratio)
{
STYLE_ATTR_MIX(body.opa, ratio);
STYLE_ATTR_MIX(body.radius, ratio);
STYLE_ATTR_MIX(body.border.width, ratio);
STYLE_ATTR_MIX(body.border.opa, ratio);
STYLE_ATTR_MIX(body.shadow.width, ratio);
STYLE_ATTR_MIX(body.padding.left, ratio);
STYLE_ATTR_MIX(body.padding.right, ratio);
STYLE_ATTR_MIX(body.padding.top, ratio);
STYLE_ATTR_MIX(body.padding.bottom, ratio);
STYLE_ATTR_MIX(body.padding.inner, ratio);
STYLE_ATTR_MIX(text.line_space, ratio);
STYLE_ATTR_MIX(text.letter_space, ratio);
STYLE_ATTR_MIX(text.opa, ratio);
STYLE_ATTR_MIX(line.width, ratio);
STYLE_ATTR_MIX(line.opa, ratio);
STYLE_ATTR_MIX(image.intense, ratio);
STYLE_ATTR_MIX(image.opa, ratio);
lv_opa_t opa = ratio == STYLE_MIX_MAX ? LV_OPA_COVER : ratio;
res->body.main_color = lv_color_mix(end->body.main_color, start->body.main_color, opa);
res->body.grad_color = lv_color_mix(end->body.grad_color, start->body.grad_color, opa);
res->body.border.color = lv_color_mix(end->body.border.color, start->body.border.color, opa);
res->body.shadow.color = lv_color_mix(end->body.shadow.color, start->body.shadow.color, opa);
res->text.color = lv_color_mix(end->text.color, start->text.color, opa);
res->image.color = lv_color_mix(end->image.color, start->image.color, opa);
res->line.color = lv_color_mix(end->line.color, start->line.color, opa);
if(ratio < (STYLE_MIX_MAX >> 1)) {
res->body.border.part = start->body.border.part;
res->glass = start->glass;
res->text.font = start->text.font;
res->body.shadow.type = start->body.shadow.type;
res->line.rounded = start->line.rounded;
} else {
res->body.border.part = end->body.border.part;
res->glass = end->glass;
res->text.font = end->text.font;
res->body.shadow.type = end->body.shadow.type;
res->line.rounded = end->line.rounded;
}
}
#if LV_USE_ANIMATION
/**
* Create an animation from a pre-configured 'lv_style_anim_t' variable
* @param anim pointer to a pre-configured 'lv_style_anim_t' variable (will be copied)
* @return pointer to a descriptor. Really this variable will be animated. (Can be used in `lv_anim_del(dsc, NULL)`)
*/
void * lv_style_anim_create(lv_style_anim_t * anim)
{
lv_style_anim_dsc_t * dsc;
dsc = lv_mem_alloc(sizeof(lv_style_anim_dsc_t));
lv_mem_assert(dsc);
if(dsc == NULL) return NULL;
dsc->style_anim = anim->style_anim;
memcpy(&dsc->style_start, anim->style_start, sizeof(lv_style_t));
memcpy(&dsc->style_end, anim->style_end, sizeof(lv_style_t));
memcpy(dsc->style_anim, anim->style_start, sizeof(lv_style_t));
dsc->end_cb = anim->end_cb;
lv_anim_t a;
a.var = (void *)dsc;
a.start = 0;
a.end = STYLE_MIX_MAX;
a.fp = (lv_anim_fp_t)style_animator;
a.path = lv_anim_path_linear;
a.end_cb = style_animation_common_end_cb;
a.act_time = anim->act_time;
a.time = anim->time;
a.playback = anim->playback;
a.playback_pause = anim->playback_pause;
a.repeat = anim->repeat;
a.repeat_pause = anim->repeat_pause;
lv_anim_create(&a);
return dsc;
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_USE_ANIMATION
/**
* Used by the style animations to set the values of a style according to start and end style.
* @param dsc the 'animated variable' set by lv_style_anim_create()
* @param val the current state of the animation between 0 and LV_STYLE_ANIM_RES
*/
static void style_animator(lv_style_anim_dsc_t * dsc, int32_t val)
{
const lv_style_t * start = &dsc->style_start;
const lv_style_t * end = &dsc->style_end;
lv_style_t * act = dsc->style_anim;
lv_style_mix(start, end, act, val);
lv_obj_report_style_mod(dsc->style_anim);
}
/**
* Called when a style animation is ready
* It called the user defined call back and free the allocated memories
* @param ptr the 'animated variable' set by lv_style_anim_create()
*/
static void style_animation_common_end_cb(void * ptr)
{
lv_style_anim_dsc_t * dsc = ptr; /*To avoid casting*/
if(dsc->end_cb) dsc->end_cb(dsc);
lv_mem_free(dsc);
}
#endif

199
src/lv_core/lv_style.h Normal file
View File

@@ -0,0 +1,199 @@
/**
* @file lv_style.h
*
*/
#ifndef LV_STYLE_H
#define LV_STYLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdbool.h>
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_font.h"
#include "../lv_misc/lv_anim.h"
/*********************
* DEFINES
*********************/
#define LV_RADIUS_CIRCLE (LV_COORD_MAX) /*A very big radius to always draw as circle*/
/**********************
* TYPEDEFS
**********************/
/*Border types (Use 'OR'ed values)*/
enum
{
LV_BORDER_NONE = 0x00,
LV_BORDER_BOTTOM = 0x01,
LV_BORDER_TOP = 0x02,
LV_BORDER_LEFT = 0x04,
LV_BORDER_RIGHT = 0x08,
LV_BORDER_FULL = 0x0F,
LV_BORDER_INTERNAL = 0x10, /*FOR matrix-like objects (e.g. Button matrix)*/
};
typedef uint8_t lv_border_part_t;
/*Shadow types*/
enum
{
LV_SHADOW_BOTTOM = 0,
LV_SHADOW_FULL,
};
typedef uint8_t lv_shadow_type_t;
typedef struct
{
uint8_t glass :1; /*1: Do not inherit this style*/
struct {
lv_color_t main_color;
lv_color_t grad_color; /*`grad_color` will be removed in v6.0, use `aux_color` instead*/
lv_coord_t radius;
lv_opa_t opa;
struct {
lv_color_t color;
lv_coord_t width;
lv_border_part_t part;
lv_opa_t opa;
} border;
struct {
lv_color_t color;
lv_coord_t width;
lv_shadow_type_t type;
} shadow;
struct {
lv_coord_t top;
lv_coord_t bottom;
lv_coord_t left;
lv_coord_t right;
lv_coord_t inner;
} padding;
} body;
struct {
lv_color_t color;
const lv_font_t * font;
lv_coord_t letter_space;
lv_coord_t line_space;
lv_opa_t opa;
} text;
struct {
lv_color_t color;
lv_opa_t intense;
lv_opa_t opa;
} image;
struct {
lv_color_t color;
lv_coord_t width;
lv_opa_t opa;
uint8_t rounded :1; /*1: rounded line endings*/
} line;
} lv_style_t;
#if LV_USE_ANIMATION
typedef struct {
const lv_style_t * style_start; /*Pointer to the starting style*/
const lv_style_t * style_end; /*Pointer to the destination style*/
lv_style_t * style_anim; /*Pointer to a style to animate*/
lv_anim_cb_t end_cb; /*Call it when the animation is ready (NULL if unused)*/
int16_t time; /*Animation time in ms*/
int16_t act_time; /*Current time in animation. Set to negative to make delay.*/
uint16_t playback_pause; /*Wait before play back*/
uint16_t repeat_pause; /*Wait before repeat*/
uint8_t playback :1; /*When the animation is ready play it back*/
uint8_t repeat :1; /*Repeat the animation infinitely*/
} lv_style_anim_t;
/* Example initialization
lv_style_anim_t a;
a.style_anim = &style_to_anim;
a.style_start = &style_1;
a.style_end = &style_2;
a.act_time = 0;
a.time = 1000;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
a.end_cb = NULL;
lv_style_anim_create(&a);
*/
#endif
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init the basic styles
*/
void lv_style_init (void);
/**
* Copy a style to an other
* @param dest pointer to the destination style
* @param src pointer to the source style
*/
void lv_style_copy(lv_style_t * dest, const lv_style_t * src);
/**
* Mix two styles according to a given ratio
* @param start start style
* @param end end style
* @param res store the result style here
* @param ratio the ratio of mix [0..256]; 0: `start` style; 256: `end` style
*/
void lv_style_mix(const lv_style_t * start, const lv_style_t * end, lv_style_t * res, uint16_t ratio);
#if LV_USE_ANIMATION
/**
* Create an animation from a pre-configured 'lv_style_anim_t' variable
* @param anim pointer to a pre-configured 'lv_style_anim_t' variable (will be copied)
* @return pointer to a descriptor. Really this variable will be animated. (Can be used in `lv_anim_del(dsc, NULL)`)
*/
void * lv_style_anim_create(lv_style_anim_t * anim);
#endif
/*************************
* GLOBAL VARIABLES
*************************/
extern lv_style_t lv_style_scr;
extern lv_style_t lv_style_transp;
extern lv_style_t lv_style_transp_fit;
extern lv_style_t lv_style_transp_tight;
extern lv_style_t lv_style_plain;
extern lv_style_t lv_style_plain_color;
extern lv_style_t lv_style_pretty;
extern lv_style_t lv_style_pretty_color;
extern lv_style_t lv_style_btn_rel;
extern lv_style_t lv_style_btn_pr;
extern lv_style_t lv_style_btn_tgl_rel;
extern lv_style_t lv_style_btn_tgl_pr;
extern lv_style_t lv_style_btn_ina;
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_STYLE_H*/

143
src/lv_draw/lv_draw.c Normal file
View File

@@ -0,0 +1,143 @@
/**
* @file lv_draw.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdio.h>
#include <stdbool.h>
#include "lv_draw.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_ANTIALIAS != 0
/**
* Get the opacity of a pixel based it's position in a line segment
* @param seg segment length
* @param px_id position of of a pixel which opacity should be get [0..seg-1]
* @param base_opa the base opacity
* @return the opacity of the given pixel
*/
lv_opa_t lv_draw_aa_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t base_opa)
{
/* How to calculate the opacity of pixels on the edges which makes the anti-aliasing?
* For example we have a line like this (y = -0.5 * x):
*
* | _ _
* * * |
*
* Anti-aliased pixels come to the '*' characters
* Calculate what percentage of the pixels should be covered if real line (not rasterized) would be drawn:
* 1. A real line should start on (0;0) and end on (2;1)
* 2. So the line intersection coordinates on the first pixel: (0;0) (1;0.5) -> 25% covered pixel in average
* 3. For the second pixel: (1;0.5) (2;1) -> 75% covered pixel in average
* 4. The equation: (px_id * 2 + 1) / (segment_width * 2)
* segment_width: the line segment which is being anti-aliased (was 2 in the example)
* px_id: pixel ID from 0 to (segment_width - 1)
* result: [0..1] coverage of the pixel
*/
/*Accelerate the common segment sizes to avoid division*/
static const lv_opa_t seg1[1] = {128};
static const lv_opa_t seg2[2] = {64, 192};
static const lv_opa_t seg3[3] = {42, 128, 212};
static const lv_opa_t seg4[4] = {32, 96, 159, 223};
static const lv_opa_t seg5[5] = {26, 76, 128, 178, 230};
static const lv_opa_t seg6[6] = {21, 64, 106, 148, 191, 234};
static const lv_opa_t seg7[7] = {18, 55, 91, 128, 164, 200, 237};
static const lv_opa_t seg8[8] = {16, 48, 80, 112, 143, 175, 207, 239};
static const lv_opa_t * seg_map[] = {seg1, seg2, seg3, seg4,
seg5, seg6, seg7, seg8
};
if(seg == 0) return LV_OPA_TRANSP;
else if(seg < 8) return (uint32_t)((uint32_t)seg_map[seg - 1][px_id] * base_opa) >> 8;
else {
return ((px_id * 2 + 1) * base_opa) / (2 * seg);
}
}
/**
* Add a vertical anti-aliasing segment (pixels with decreasing opacity)
* @param x start point x coordinate
* @param y start point y coordinate
* @param length length of segment (negative value to start from 0 opacity)
* @param mask draw only in this area
* @param color color of pixels
* @param opa maximum opacity
*/
void lv_draw_aa_ver_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa)
{
bool aa_inv = false;
if(length < 0) {
aa_inv = true;
length = -length;
}
lv_coord_t i;
for(i = 0; i < length; i++) {
lv_opa_t px_opa = lv_draw_aa_get_opa(length, i, opa);
if(aa_inv) px_opa = opa - px_opa;
lv_draw_px(x, y + i, mask, color, px_opa);
}
}
/**
* Add a horizontal anti-aliasing segment (pixels with decreasing opacity)
* @param x start point x coordinate
* @param y start point y coordinate
* @param length length of segment (negative value to start from 0 opacity)
* @param mask draw only in this area
* @param color color of pixels
* @param opa maximum opacity
*/
void lv_draw_aa_hor_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa)
{
bool aa_inv = false;
if(length < 0) {
aa_inv = true;
length = -length;
}
lv_coord_t i;
for(i = 0; i < length; i++) {
lv_opa_t px_opa = lv_draw_aa_get_opa(length, i, opa);
if(aa_inv) px_opa = opa - px_opa;
lv_draw_px(x + i, y, mask, color, px_opa);
}
}
#endif

110
src/lv_draw/lv_draw.h Normal file
View File

@@ -0,0 +1,110 @@
/**
* @file lv_draw.h
*
*/
#ifndef LV_DRAW_H
#define LV_DRAW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include "../lv_core/lv_style.h"
#include "../lv_misc/lv_txt.h"
/*********************
* DEFINES
*********************/
/*If image pixels contains alpha we need to know how much byte is a pixel*/
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
# define LV_IMG_PX_SIZE_ALPHA_BYTE 2
#elif LV_COLOR_DEPTH == 16
# define LV_IMG_PX_SIZE_ALPHA_BYTE 3
#elif LV_COLOR_DEPTH == 32
# define LV_IMG_PX_SIZE_ALPHA_BYTE 4
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_IMG_SRC_VARIABLE,
LV_IMG_SRC_FILE,
LV_IMG_SRC_SYMBOL,
LV_IMG_SRC_UNKNOWN,
};
typedef uint8_t lv_img_src_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_ANTIALIAS != 0
/**
* Get the opacity of a pixel based it's position in a line segment
* @param seg segment length
* @param px_id position of of a pixel which opacity should be get [0..seg-1]
* @param base_opa the base opacity
* @return the opacity of the given pixel
*/
lv_opa_t lv_draw_aa_get_opa(lv_coord_t seg, lv_coord_t px_id, lv_opa_t base_opa);
/**
* Add a vertical anti-aliasing segment (pixels with decreasing opacity)
* @param x start point x coordinate
* @param y start point y coordinate
* @param length length of segment (negative value to start from 0 opacity)
* @param mask draw only in this area
* @param color color of pixels
* @param opa maximum opacity
*/
void lv_draw_aa_ver_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa);
/**
* Add a horizontal anti-aliasing segment (pixels with decreasing opacity)
* @param x start point x coordinate
* @param y start point y coordinate
* @param length length of segment (negative value to start from 0 opacity)
* @param mask draw only in this area
* @param color color of pixels
* @param opa maximum opacity
*/
void lv_draw_aa_hor_seg(lv_coord_t x, lv_coord_t y, lv_coord_t length, const lv_area_t * mask, lv_color_t color, lv_opa_t opa);
#endif
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* POST INCLUDES
*********************/
#include "lv_draw_basic.h"
#include "lv_draw_rect.h"
#include "lv_draw_label.h"
#include "lv_draw_img.h"
#include "lv_draw_line.h"
#include "lv_draw_triangle.h"
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_H*/

13
src/lv_draw/lv_draw.mk Normal file
View File

@@ -0,0 +1,13 @@
CSRCS += lv_draw_basic.c
CSRCS += lv_draw.c
CSRCS += lv_draw_rect.c
CSRCS += lv_draw_label.c
CSRCS += lv_draw_line.c
CSRCS += lv_draw_img.c
CSRCS += lv_draw_arc.c
CSRCS += lv_draw_triangle.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/src/lv_draw
VPATH += :$(LVGL_DIR)/lvgl/src/lv_draw
CFLAGS += "-I$(LVGL_DIR)lvgl/src/lv_draw"

264
src/lv_draw/lv_draw_arc.c Normal file
View File

@@ -0,0 +1,264 @@
/**
* @file lv_draw_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_arc.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static uint16_t fast_atan2(int x, int y);
static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa);
static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa);
static bool deg_test_norm(uint16_t deg, uint16_t start, uint16_t end);
static bool deg_test_inv(uint16_t deg, uint16_t start, uint16_t end);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param mask the arc will be drawn only in this mask
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param style style of the arc (`body.thickness`, `body.main_color`, `body.opa` is used)
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, const lv_area_t * mask,
uint16_t start_angle, uint16_t end_angle, const lv_style_t * style, lv_opa_t opa_scale)
{
lv_coord_t thickness = style->line.width;
if(thickness > radius) thickness = radius;
lv_coord_t r_out = radius;
lv_coord_t r_in = r_out - thickness;
int16_t deg_base;
int16_t deg;
lv_coord_t x_start[4];
lv_coord_t x_end[4];
lv_color_t color = style->line.color;
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->body.opa : (uint16_t)((uint16_t) style->body.opa * opa_scale) >> 8;
bool (*deg_test)(uint16_t, uint16_t, uint16_t);
if(start_angle <= end_angle) deg_test = deg_test_norm;
else deg_test = deg_test_inv;
if(deg_test(270, start_angle, end_angle)) hor_line(center_x - r_out + 1, center_y, mask, thickness - 1, color, opa); // Left Middle
if(deg_test(90, start_angle, end_angle)) hor_line(center_x + r_in, center_y, mask, thickness - 1, color, opa); // Right Middle
if(deg_test(180, start_angle, end_angle)) ver_line(center_x, center_y - r_out + 1, mask, thickness - 1, color, opa); // Top Middle
if(deg_test(0, start_angle, end_angle)) ver_line(center_x, center_y + r_in, mask, thickness - 1, color, opa); // Bottom middle
uint32_t r_out_sqr = r_out * r_out;
uint32_t r_in_sqr = r_in * r_in;
int16_t xi;
int16_t yi;
for(yi = -r_out; yi < 0; yi++) {
x_start[0] = LV_COORD_MIN;
x_start[1] = LV_COORD_MIN;
x_start[2] = LV_COORD_MIN;
x_start[3] = LV_COORD_MIN;
x_end[0] = LV_COORD_MIN;
x_end[1] = LV_COORD_MIN;
x_end[2] = LV_COORD_MIN;
x_end[3] = LV_COORD_MIN;
for(xi = -r_out; xi < 0; xi++) {
uint32_t r_act_sqr = xi * xi + yi * yi;
if(r_act_sqr > r_out_sqr) continue;
deg_base = fast_atan2(xi, yi) - 180;
deg = 180 + deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[0] == LV_COORD_MIN) x_start[0] = xi;
} else if(x_start[0] != LV_COORD_MIN && x_end[0] == LV_COORD_MIN) {
x_end[0] = xi - 1;
}
deg = 360 - deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[1] == LV_COORD_MIN) x_start[1] = xi;
} else if(x_start[1] != LV_COORD_MIN && x_end[1] == LV_COORD_MIN) {
x_end[1] = xi - 1;
}
deg = 180 - deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[2] == LV_COORD_MIN) x_start[2] = xi;
} else if(x_start[2] != LV_COORD_MIN && x_end[2] == LV_COORD_MIN) {
x_end[2] = xi - 1;
}
deg = deg_base;
if(deg_test(deg, start_angle, end_angle)) {
if(x_start[3] == LV_COORD_MIN) x_start[3] = xi;
} else if(x_start[3] != LV_COORD_MIN && x_end[3] == LV_COORD_MIN) {
x_end[3] = xi - 1;
}
if(r_act_sqr < r_in_sqr) break; /*No need to continue the iteration in x once we found the inner edge of the arc*/
}
if(x_start[0] != LV_COORD_MIN) {
if(x_end[0] == LV_COORD_MIN) x_end[0] = xi - 1;
hor_line(center_x + x_start[0], center_y + yi, mask, x_end[0] - x_start[0], color, opa);
}
if(x_start[1] != LV_COORD_MIN) {
if(x_end[1] == LV_COORD_MIN) x_end[1] = xi - 1;
hor_line(center_x + x_start[1], center_y - yi, mask, x_end[1] - x_start[1], color, opa);
}
if(x_start[2] != LV_COORD_MIN) {
if(x_end[2] == LV_COORD_MIN) x_end[2] = xi - 1;
hor_line(center_x - x_end[2], center_y + yi, mask, LV_MATH_ABS(x_end[2] - x_start[2]), color, opa);
}
if(x_start[3] != LV_COORD_MIN) {
if(x_end[3] == LV_COORD_MIN) x_end[3] = xi - 1;
hor_line(center_x - x_end[3], center_y - yi, mask, LV_MATH_ABS(x_end[3] - x_start[3]), color, opa);
}
#if LV_ANTIALIAS
/*TODO*/
#endif
}
}
static uint16_t fast_atan2(int x, int y)
{
// Fast XY vector to integer degree algorithm - Jan 2011 www.RomanBlack.com
// Converts any XY values including 0 to a degree value that should be
// within +/- 1 degree of the accurate value without needing
// large slow trig functions like ArcTan() or ArcCos().
// NOTE! at least one of the X or Y values must be non-zero!
// This is the full version, for all 4 quadrants and will generate
// the angle in integer degrees from 0-360.
// Any values of X and Y are usable including negative values provided
// they are between -1456 and 1456 so the 16bit multiply does not overflow.
unsigned char negflag;
unsigned char tempdegree;
unsigned char comp;
unsigned int degree; // this will hold the result
//signed int x; // these hold the XY vector at the start
//signed int y; // (and they will be destroyed)
unsigned int ux;
unsigned int uy;
// Save the sign flags then remove signs and get XY as unsigned ints
negflag = 0;
if(x < 0) {
negflag += 0x01; // x flag bit
x = (0 - x); // is now +
}
ux = x; // copy to unsigned var before multiply
if(y < 0) {
negflag += 0x02; // y flag bit
y = (0 - y); // is now +
}
uy = y; // copy to unsigned var before multiply
// 1. Calc the scaled "degrees"
if(ux > uy) {
degree = (uy * 45) / ux; // degree result will be 0-45 range
negflag += 0x10; // octant flag bit
} else {
degree = (ux * 45) / uy; // degree result will be 0-45 range
}
// 2. Compensate for the 4 degree error curve
comp = 0;
tempdegree = degree; // use an unsigned char for speed!
if(tempdegree > 22) { // if top half of range
if(tempdegree <= 44) comp++;
if(tempdegree <= 41) comp++;
if(tempdegree <= 37) comp++;
if(tempdegree <= 32) comp++; // max is 4 degrees compensated
} else { // else is lower half of range
if(tempdegree >= 2) comp++;
if(tempdegree >= 6) comp++;
if(tempdegree >= 10) comp++;
if(tempdegree >= 15) comp++; // max is 4 degrees compensated
}
degree += comp; // degree is now accurate to +/- 1 degree!
// Invert degree if it was X>Y octant, makes 0-45 into 90-45
if(negflag & 0x10) degree = (90 - degree);
// 3. Degree is now 0-90 range for this quadrant,
// need to invert it for whichever quadrant it was in
if(negflag & 0x02) { // if -Y
if(negflag & 0x01) // if -Y -X
degree = (180 + degree);
else // else is -Y +X
degree = (180 - degree);
} else { // else is +Y
if(negflag & 0x01) // if +Y -X
degree = (360 - degree);
}
return degree;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void ver_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa)
{
lv_area_t area;
lv_area_set(&area, x, y, x, y + len);
lv_draw_fill(&area, mask, color, opa);
}
static void hor_line(lv_coord_t x, lv_coord_t y, const lv_area_t * mask, lv_coord_t len, lv_color_t color, lv_opa_t opa)
{
lv_area_t area;
lv_area_set(&area, x, y, x + len, y);
lv_draw_fill(&area, mask, color, opa);
}
static bool deg_test_norm(uint16_t deg, uint16_t start, uint16_t end)
{
if(deg >= start && deg <= end) return true;
else return false;
}
static bool deg_test_inv(uint16_t deg, uint16_t start, uint16_t end)
{
if(deg >= start || deg <= end) {
return true;
} else return false;
}

53
src/lv_draw/lv_draw_arc.h Normal file
View File

@@ -0,0 +1,53 @@
/**
* @file lv_draw_arc.h
*
*/
#ifndef LV_DRAW_ARC_H
#define LV_DRAW_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw an arc. (Can draw pie too with great thickness.)
* @param center_x the x coordinate of the center of the arc
* @param center_y the y coordinate of the center of the arc
* @param radius the radius of the arc
* @param mask the arc will be drawn only in this mask
* @param start_angle the start angle of the arc (0 deg on the bottom, 90 deg on the right)
* @param end_angle the end angle of the arc
* @param style style of the arc (`body.thickness`, `body.main_color`, `body.opa` is used)
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_arc(lv_coord_t center_x, lv_coord_t center_y, uint16_t radius, const lv_area_t * mask,
uint16_t start_angle, uint16_t end_angle, const lv_style_t * style, lv_opa_t opa_scale);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_ARC*/

675
src/lv_draw/lv_draw_basic.c Normal file
View File

@@ -0,0 +1,675 @@
/**
* @file lv_draw_basic.c
*
*/
#include "lv_draw_basic.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "../lv_core/lv_refr.h"
#include "../lv_hal/lv_hal.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_font.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_log.h"
#include <stddef.h>
#include "lv_draw.h"
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
#define VFILL_HW_ACC_SIZE_LIMIT 50 /*Always fill < 50 px with 'sw_color_fill' because of the hw. init overhead*/
#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
static void sw_color_fill(lv_area_t * mem_area, lv_color_t * mem, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa);
#if LV_COLOR_SCREEN_TRANSP
static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Put a pixel in the Virtual Display Buffer
* @param x pixel x coordinate
* @param y pixel y coordinate
* @param mask_p fill only on this mask (truncated to VDB area)
* @param color pixel color
* @param opa opacity of the area (0..255)
*/
void lv_draw_px(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
{
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
/*Pixel out of the mask*/
if(x < mask_p->x1 || x > mask_p->x2 ||
y < mask_p->y1 || y > mask_p->y2) {
return;
}
lv_disp_t * disp = lv_refr_get_disp_refreshing();
lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
uint32_t vdb_width = lv_area_get_width(&vdb->area);
/*Make the coordinates relative to VDB*/
x -= vdb->area.x1;
y -= vdb->area.y1;
if(disp->driver.set_px_cb) {
disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, x, y, color, opa);
} else {
lv_color_t * vdb_px_p = vdb->buf_act;
vdb_px_p += y * vdb_width + x;
#if LV_COLOR_SCREEN_TRANSP == 0
if(opa == LV_OPA_COVER) {
*vdb_px_p = color;
} else {
*vdb_px_p = lv_color_mix(color, *vdb_px_p, opa);
}
#else
*vdb_px_p = color_mix_2_alpha(*vdb_px_p, (*vdb_px_p).alpha, color, opa);
#endif
}
}
/**
* Fill an area in the Virtual Display Buffer
* @param cords_p coordinates of the area to fill
* @param mask_p fill only o this mask (truncated to VDB area)
* @param color fill color
* @param opa opacity of the area (0..255)
*/
void lv_draw_fill(const lv_area_t * cords_p, const lv_area_t * mask_p,
lv_color_t color, lv_opa_t opa)
{
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
lv_area_t res_a;
bool union_ok;
/*Get the union of cord and mask*/
/* The mask is already truncated to the vdb size
* in 'lv_refr_area_with_vdb' function */
union_ok = lv_area_intersect(&res_a, cords_p, mask_p);
/*If there are common part of the three area then draw to the vdb*/
if(union_ok == false) return;
lv_disp_t * disp = lv_refr_get_disp_refreshing();
lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
lv_area_t vdb_rel_a; /*Stores relative coordinates on vdb*/
vdb_rel_a.x1 = res_a.x1 - vdb->area.x1;
vdb_rel_a.y1 = res_a.y1 - vdb->area.y1;
vdb_rel_a.x2 = res_a.x2 - vdb->area.x1;
vdb_rel_a.y2 = res_a.y2 - vdb->area.y1;
lv_color_t * vdb_buf_tmp = vdb->buf_act;
uint32_t vdb_width = lv_area_get_width(&vdb->area);
/*Move the vdb_tmp to the first row*/
vdb_buf_tmp += vdb_width * vdb_rel_a.y1;
#if LV_USE_GPU
static LV_ATTRIBUTE_MEM_ALIGN lv_color_t color_array_tmp[LV_HOR_RES_MAX]; /*Used by 'lv_disp_mem_blend'*/
static lv_coord_t last_width = -1;
lv_coord_t w = lv_area_get_width(&vdb_rel_a);
/*Don't use hw. acc. for every small fill (because of the init overhead)*/
if(w < VFILL_HW_ACC_SIZE_LIMIT) {
sw_color_fill(&vdb->area, vdb->buf_act, &vdb_rel_a, color, opa);
}
/*Not opaque fill*/
else if(opa == LV_OPA_COVER) {
/*Use hw fill if present*/
if(disp->driver.mem_fill) {
lv_coord_t row;
for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
disp->driver.mem_fill(&vdb_buf_tmp[vdb_rel_a.x1], w, color);
vdb_buf_tmp += vdb_width;
}
}
/*Use hw blend if present and the area is not too small*/
else if(lv_area_get_height(&vdb_rel_a) > VFILL_HW_ACC_SIZE_LIMIT &&
disp->driver.mem_blend) {
/*Fill a one line sized buffer with a color and blend this later*/
if(color_array_tmp[0].full != color.full || last_width != w) {
uint16_t i;
for(i = 0; i < w; i++) {
color_array_tmp[i].full = color.full;
}
last_width = w;
}
/*Blend the filled line to every line VDB line-by-line*/
lv_coord_t row;
for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
disp->driver.mem_blend(&vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
vdb_buf_tmp += vdb_width;
}
}
/*Else use sw fill if no better option*/
else {
sw_color_fill(&vdb->area, vdb->buf_act, &vdb_rel_a, color, opa);
}
}
/*Fill with opacity*/
else {
/*Use hw blend if present*/
if(disp->driver.mem_blend) {
if(color_array_tmp[0].full != color.full || last_width != w) {
uint16_t i;
for(i = 0; i < w; i++) {
color_array_tmp[i].full = color.full;
}
last_width = w;
}
lv_coord_t row;
for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
disp->driver.mem_blend(&vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
vdb_buf_tmp += vdb_width;
}
}
/*Use sw fill with opa if no better option*/
else {
sw_color_fill(&vdb->area, vdb->buf_act, &vdb_rel_a, color, opa);
}
}
#else
sw_color_fill(&vdb->area, vdb->buf_act, &vdb_rel_a, color, opa);
#endif
}
/**
* Draw a letter in the Virtual Display Buffer
* @param pos_p left-top coordinate of the latter
* @param mask_p the letter will be drawn only on this area (truncated to VDB area)
* @param font_p pointer to font
* @param letter a letter to draw
* @param color color of letter
* @param opa opacity of letter (0..255)
*/
void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * mask_p,
const lv_font_t * font_p, uint32_t letter,
lv_color_t color, lv_opa_t opa)
{
const uint8_t bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const uint8_t bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const uint8_t bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119,
136, 153, 170, 187,
204, 221, 238, 255
};
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
if(font_p == NULL) {
LV_LOG_WARN("Font: character's bitmap not found");
return;
}
lv_coord_t pos_x = pos_p->x;
lv_coord_t pos_y = pos_p->y;
uint8_t letter_w = lv_font_get_real_width(font_p, letter);
uint8_t letter_h = lv_font_get_height(font_p);
uint8_t bpp = lv_font_get_bpp(font_p, letter); /*Bit per pixel (1,2, 4 or 8)*/
const uint8_t * bpp_opa_table;
uint8_t mask_init;
uint8_t mask;
if(lv_font_is_monospace(font_p, letter)) {
pos_x += (lv_font_get_width(font_p, letter) - letter_w) / 2;
}
switch(bpp) {
case 1:
bpp_opa_table = bpp1_opa_table;
mask_init = 0x80;
break;
case 2:
bpp_opa_table = bpp2_opa_table;
mask_init = 0xC0;
break;
case 4:
bpp_opa_table = bpp4_opa_table;
mask_init = 0xF0;
break;
case 8:
bpp_opa_table = NULL;
mask_init = 0xFF;
break; /*No opa table, pixel value will be used directly*/
default:
return; /*Invalid bpp. Can't render the letter*/
}
const uint8_t * map_p = lv_font_get_bitmap(font_p, letter);
if(map_p == NULL) return;
/*If the letter is completely out of mask don't draw it */
if(pos_x + letter_w < mask_p->x1 || pos_x > mask_p->x2 ||
pos_y + letter_h < mask_p->y1 || pos_y > mask_p->y2) return;
lv_disp_t * disp = lv_refr_get_disp_refreshing();
lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
lv_coord_t vdb_width = lv_area_get_width(&vdb->area);
lv_color_t * vdb_buf_tmp = vdb->buf_act;
lv_coord_t col, row;
uint8_t col_bit;
uint8_t col_byte_cnt;
uint8_t width_byte_scr = letter_w >> 3; /*Width in bytes (on the screen finally) (e.g. w = 11 -> 2 bytes wide)*/
if(letter_w & 0x7) width_byte_scr++;
uint8_t width_byte_bpp = (letter_w * bpp) >> 3; /*Letter width in byte. Real width in the font*/
if((letter_w * bpp) & 0x7) width_byte_bpp++;
/* Calculate the col/row start/end on the map*/
lv_coord_t col_start = pos_x >= mask_p->x1 ? 0 : mask_p->x1 - pos_x;
lv_coord_t col_end = pos_x + letter_w <= mask_p->x2 ? letter_w : mask_p->x2 - pos_x + 1;
lv_coord_t row_start = pos_y >= mask_p->y1 ? 0 : mask_p->y1 - pos_y;
lv_coord_t row_end = pos_y + letter_h <= mask_p->y2 ? letter_h : mask_p->y2 - pos_y + 1;
/*Set a pointer on VDB to the first pixel of the letter*/
vdb_buf_tmp += ((pos_y - vdb->area.y1) * vdb_width)
+ pos_x - vdb->area.x1;
/*If the letter is partially out of mask the move there on VDB*/
vdb_buf_tmp += (row_start * vdb_width) + col_start;
/*Move on the map too*/
map_p += (row_start * width_byte_bpp) + ((col_start * bpp) >> 3);
uint8_t letter_px;
lv_opa_t px_opa;
for(row = row_start; row < row_end; row ++) {
col_byte_cnt = 0;
col_bit = (col_start * bpp) % 8;
mask = mask_init >> col_bit;
for(col = col_start; col < col_end; col ++) {
letter_px = (*map_p & mask) >> (8 - col_bit - bpp);
if(letter_px != 0) {
if(opa == LV_OPA_COVER) {
px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px];
} else {
px_opa = bpp == 8 ?
(uint16_t)((uint16_t)letter_px * opa) >> 8 :
(uint16_t)((uint16_t)bpp_opa_table[letter_px] * opa) >> 8;
}
if(disp->driver.set_px_cb) {
disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width,
(col + pos_x) - vdb->area.x1, (row + pos_y) - vdb->area.y1,
color, px_opa);
} else {
#if LV_COLOR_SCREEN_TRANSP == 0
*vdb_buf_tmp = lv_color_mix(color, *vdb_buf_tmp, px_opa);
#else
*vdb_buf_tmp = color_mix_2_alpha(*vdb_buf_tmp, (*vdb_buf_tmp).alpha, color, px_opa);
#endif
}
}
vdb_buf_tmp++;
if(col_bit < 8 - bpp) {
col_bit += bpp;
mask = mask >> bpp;
} else {
col_bit = 0;
col_byte_cnt ++;
mask = mask_init;
map_p ++;
}
}
map_p += (width_byte_bpp) - col_byte_cnt;
vdb_buf_tmp += vdb_width - (col_end - col_start); /*Next row in VDB*/
}
}
/**
* Draw a color map to the display (image)
* @param cords_p coordinates the color map
* @param mask_p the map will drawn only on this area (truncated to VDB area)
* @param map_p pointer to a lv_color_t array
* @param opa opacity of the map
* @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
* @param alpha_byte true: extra alpha byte is inserted for every pixel
* @param recolor mix the pixels with this color
* @param recolor_opa the intense of recoloring
*/
void lv_draw_map(const lv_area_t * cords_p, const lv_area_t * mask_p,
const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
lv_color_t recolor, lv_opa_t recolor_opa)
{
if(opa < LV_OPA_MIN) return;
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
lv_area_t masked_a;
bool union_ok;
/*Get the union of map size and mask*/
/* The mask is already truncated to the vdb size
* in 'lv_refr_area_with_vdb' function */
union_ok = lv_area_intersect(&masked_a, cords_p, mask_p);
/*If there are common part of the three area then draw to the vdb*/
if(union_ok == false) return;
/*The pixel size in byte is different if an alpha byte is added too*/
uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
/*If the map starts OUT of the masked area then calc. the first pixel*/
lv_coord_t map_width = lv_area_get_width(cords_p);
if(cords_p->y1 < masked_a.y1) {
map_p += (uint32_t) map_width * ((masked_a.y1 - cords_p->y1)) * px_size_byte;
}
if(cords_p->x1 < masked_a.x1) {
map_p += (masked_a.x1 - cords_p->x1) * px_size_byte;
}
lv_disp_t * disp = lv_refr_get_disp_refreshing();
lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
/*Stores coordinates relative to the current VDB*/
masked_a.x1 = masked_a.x1 - vdb->area.x1;
masked_a.y1 = masked_a.y1 - vdb->area.y1;
masked_a.x2 = masked_a.x2 - vdb->area.x1;
masked_a.y2 = masked_a.y2 - vdb->area.y1;
lv_coord_t vdb_width = lv_area_get_width(&vdb->area);
lv_color_t * vdb_buf_tmp = vdb->buf_act;
vdb_buf_tmp += (uint32_t) vdb_width * masked_a.y1; /*Move to the first row*/
vdb_buf_tmp += (uint32_t) masked_a.x1; /*Move to the first col*/
lv_coord_t row;
lv_coord_t map_useful_w = lv_area_get_width(&masked_a);
/*The simplest case just copy the pixels into the VDB*/
if(chroma_key == false && alpha_byte == false && opa == LV_OPA_COVER && recolor_opa == LV_OPA_TRANSP) {
/*Use the custom VDB write function is exists*/
if(disp->driver.set_px_cb) {
lv_coord_t col;
for(row = masked_a.y1; row <= masked_a.y2; row++) {
for(col = 0; col < map_useful_w; col++) {
lv_color_t px_color = *((lv_color_t *)&map_p[(uint32_t)col * px_size_byte]);
disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, col + masked_a.x1, row, px_color, opa);
}
map_p += map_width * px_size_byte; /*Next row on the map*/
}
}
/*Normal native VDB*/
else {
for(row = masked_a.y1; row <= masked_a.y2; row++) {
#if LV_USE_GPU
if(disp->driver.mem_blend == false) {
sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
} else {
disp->driver.mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
}
#else
sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
#endif
map_p += map_width * px_size_byte; /*Next row on the map*/
vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
}
}
}
/*In the other cases every pixel need to be checked one-by-one*/
else {
lv_color_t chroma_key_color = LV_COLOR_TRANSP;
lv_coord_t col;
lv_color_t last_img_px = LV_COLOR_BLACK;
lv_color_t recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
for(row = masked_a.y1; row <= masked_a.y2; row++) {
for(col = 0; col < map_useful_w; col++) {
lv_opa_t opa_result = opa;
uint8_t * px_color_p = (uint8_t *) &map_p[(uint32_t)col * px_size_byte];
lv_color_t px_color;
/*Calculate with the pixel level alpha*/
if(alpha_byte) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
px_color.full = px_color_p[0];
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
px_color.full = px_color_p[0] + (px_color_p[1] << 8);
#elif LV_COLOR_DEPTH == 32
px_color = *((lv_color_t *)px_color_p);
#endif
lv_opa_t px_opa = *(px_color_p + LV_IMG_PX_SIZE_ALPHA_BYTE - 1);
if(px_opa == LV_OPA_TRANSP) continue;
else if(px_opa != LV_OPA_COVER) opa_result = (uint32_t)((uint32_t)px_opa * opa_result) >> 8;
} else {
px_color = *((lv_color_t *)px_color_p);
}
/*Handle chroma key*/
if(chroma_key && px_color.full == chroma_key_color.full) continue;
/*Re-color the pixel if required*/
if(recolor_opa != LV_OPA_TRANSP) {
if(last_img_px.full != px_color.full) { /*Minor acceleration: calculate only for new colors (save the last)*/
last_img_px = px_color;
recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
}
/*Handle custom VDB write is present*/
if(disp->driver.set_px_cb) {
disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, col + masked_a.x1, row, recolored_px, opa_result);
}
/*Normal native VDB write*/
else {
if(opa_result == LV_OPA_COVER) vdb_buf_tmp[col].full = recolored_px.full;
else vdb_buf_tmp[col] = lv_color_mix(recolored_px, vdb_buf_tmp[col], opa_result);
}
} else {
/*Handle custom VDB write is present*/
if(disp->driver.set_px_cb) {
disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, col + masked_a.x1, row, px_color, opa_result);
}
/*Normal native VDB write*/
else {
if(opa_result == LV_OPA_COVER) vdb_buf_tmp[col] = px_color;
else {
#if LV_COLOR_SCREEN_TRANSP == 0
vdb_buf_tmp[col] = lv_color_mix(px_color, vdb_buf_tmp[col], opa_result);
#else
vdb_buf_tmp[col] = color_mix_2_alpha(vdb_buf_tmp[col], vdb_buf_tmp[col].alpha, px_color, opa_result);
#endif
}
}
}
}
map_p += map_width * px_size_byte; /*Next row on the map*/
vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Blend pixels to destination memory using opacity
* @param dest a memory address. Copy 'src' here.
* @param src pointer to pixel map. Copy it to 'dest'.
* @param length number of pixels in 'src'
* @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
*/
static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
{
if(opa == LV_OPA_COVER) {
memcpy(dest, src, length * sizeof(lv_color_t));
} else {
uint32_t col;
for(col = 0; col < length; col++) {
dest[col] = lv_color_mix(src[col], dest[col], opa);
}
}
}
/**
*
* @param mem_area coordinates of 'mem' memory area
* @param mem a memory address. Considered to a rectangular window according to 'mem_area'
* @param fill_area coordinates of an area to fill. Relative to 'mem_area'.
* @param color fill color
* @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
*/
static void sw_color_fill(lv_area_t * mem_area, lv_color_t * mem, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
{
/*Set all row in vdb to the given color*/
lv_coord_t row;
lv_coord_t col;
lv_coord_t mem_width = lv_area_get_width(mem_area);
lv_disp_t * disp = lv_refr_get_disp_refreshing();
if(disp->driver.set_px_cb) {
for(col = fill_area->x1; col <= fill_area->x2; col++) {
for(row = fill_area->y1; row <= fill_area->y2; row++) {
disp->driver.set_px_cb(&disp->driver, (uint8_t *)mem, mem_width, col, row, color, opa);
}
}
} else {
mem += fill_area->y1 * mem_width; /*Go to the first row*/
/*Run simpler function without opacity*/
if(opa == LV_OPA_COVER) {
/*Fill the first row with 'color'*/
for(col = fill_area->x1; col <= fill_area->x2; col++) {
mem[col] = color;
}
/*Copy the first row to all other rows*/
lv_color_t * mem_first = &mem[fill_area->x1];
lv_coord_t copy_size = (fill_area->x2 - fill_area->x1 + 1) * sizeof(lv_color_t);
mem += mem_width;
for(row = fill_area->y1 + 1; row <= fill_area->y2; row++) {
memcpy(&mem[fill_area->x1], mem_first, copy_size);
mem += mem_width;
}
}
/*Calculate with alpha too*/
else {
#if LV_COLOR_SCREEN_TRANSP == 0
lv_color_t bg_tmp = LV_COLOR_BLACK;
lv_color_t opa_tmp = lv_color_mix(color, bg_tmp, opa);
#endif
for(row = fill_area->y1; row <= fill_area->y2; row++) {
for(col = fill_area->x1; col <= fill_area->x2; col++) {
#if LV_COLOR_SCREEN_TRANSP == 0
/*If the bg color changed recalculate the result color*/
if(mem[col].full != bg_tmp.full) {
bg_tmp = mem[col];
opa_tmp = lv_color_mix(color, bg_tmp, opa);
}
mem[col] = opa_tmp;
#else
mem[col] = color_mix_2_alpha(mem[col], mem[col].alpha, color, opa);
#endif
}
mem += mem_width;
}
}
}
}
#if LV_COLOR_SCREEN_TRANSP
/**
* Mix two colors. Both color can have alpha value. It requires ARGB888 colors.
* @param bg_color background color
* @param bg_opa alpha of the background color
* @param fg_color foreground color
* @param fg_opa alpha of the foreground color
* @return the mixed color. the alpha channel (color.alpha) contains the result alpha
*/
static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa)
{
/* Pick the foreground if it's fully opaque or the Background is fully transparent*/
if(fg_opa == LV_OPA_COVER && bg_opa <= LV_OPA_MIN) {
fg_color.alpha = fg_opa;
return fg_color;
}
/*Transparent foreground: use the Background*/
else if(fg_opa <= LV_OPA_MIN) {
return bg_color;
}
/*Opaque background: use simple mix*/
else if(bg_opa >= LV_OPA_MAX) {
return lv_color_mix(fg_color, bg_color, fg_opa);
}
/*Both colors have alpha. Expensive calculation need to be applied*/
else {
/*Save the parameters and the result. If they will be asked again don't compute again*/
static lv_opa_t fg_opa_save = 0;
static lv_opa_t bg_opa_save = 0;
static lv_color_t c = {{0}};
if(fg_opa != fg_opa_save || bg_opa != bg_opa_save) {
fg_opa_save = fg_opa;
bg_opa_save = bg_opa;
/*Info: https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
lv_opa_t alpha_res = 255 - ((uint16_t)((uint16_t)(255 - fg_opa) * (255 - bg_opa)) >> 8);
if(alpha_res == 0) {
while(1);
}
lv_opa_t ratio = (uint16_t)((uint16_t) fg_opa * 255) / alpha_res;
c = lv_color_mix(fg_color, bg_color, ratio);
c.alpha = alpha_res;
}
return c;
}
}
#endif /*LV_COLOR_SCREEN_TRANSP*/

View File

@@ -0,0 +1,85 @@
/**
* @file lv_draw_basic.h
*
*/
#ifndef LV_DRAW_BASIC_H
#define LV_DRAW_BASIC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_font.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_draw_px(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa);
/**
* Fill an area in the Virtual Display Buffer
* @param cords_p coordinates of the area to fill
* @param mask_p fill only o this mask
* @param color fill color
* @param opa opacity of the area (0..255)
*/
void lv_draw_fill(const lv_area_t * cords_p, const lv_area_t * mask_p,
lv_color_t color, lv_opa_t opa);
/**
* Draw a letter in the Virtual Display Buffer
* @param pos_p left-top coordinate of the latter
* @param mask_p the letter will be drawn only on this area
* @param font_p pointer to font
* @param letter a letter to draw
* @param color color of letter
* @param opa opacity of letter (0..255)
*/
void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * mask_p,
const lv_font_t * font_p, uint32_t letter,
lv_color_t color, lv_opa_t opa);
/**
* Draw a color map to the display (image)
* @param cords_p coordinates the color map
* @param mask_p the map will drawn only on this area (truncated to VDB area)
* @param map_p pointer to a lv_color_t array
* @param opa opacity of the map
* @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
* @param alpha_byte true: extra alpha byte is inserted for every pixel
* @param recolor mix the pixels with this color
* @param recolor_opa the intense of recoloring
*/
void lv_draw_map(const lv_area_t * cords_p, const lv_area_t * mask_p,
const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
lv_color_t recolor, lv_opa_t recolor_opa);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_BASIC_H*/

759
src/lv_draw/lv_draw_img.c Normal file
View File

@@ -0,0 +1,759 @@
/**
* @file lv_draw_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_img.h"
#include "../lv_misc/lv_fs.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mask,
const void * src, const lv_style_t * style, lv_opa_t opa_scale);
static const uint8_t * lv_img_decoder_open(const void * src, const lv_style_t * style);
static lv_res_t lv_img_decoder_read_line(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
static void lv_img_decoder_close(void);
static lv_res_t lv_img_built_in_decoder_line_alpha(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
static lv_res_t lv_img_built_in_decoder_line_indexed(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**********************
* STATIC VARIABLES
**********************/
static bool decoder_custom;
static const void * decoder_src;
static lv_img_src_t decoder_src_type;
static lv_img_header_t decoder_header;
static const lv_style_t * decoder_style;
#if LV_USE_FILESYSTEM
static lv_fs_file_t decoder_file;
#endif
#if LV_IMG_CF_INDEXED
static lv_color_t decoder_index_map[256];
#endif
static lv_img_decoder_info_f_t lv_img_decoder_info_custom;
static lv_img_decoder_open_f_t lv_img_decoder_open_custom;
static lv_img_decoder_read_line_f_t lv_img_decoder_read_line_custom;
static lv_img_decoder_close_f_t lv_img_decoder_close_custom;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param style style of the image
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask,
const void * src, const lv_style_t * style, lv_opa_t opa_scale)
{
if(src == NULL) {
LV_LOG_WARN("Image draw: src is NULL");
lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL);
return;
}
lv_res_t res;
res = lv_img_draw_core(coords, mask, src, style, opa_scale);
if(res == LV_RES_INV) {
LV_LOG_WARN("Image draw error");
lv_draw_rect(coords, mask, &lv_style_plain, LV_OPA_COVER);
lv_draw_label(coords, mask, &lv_style_plain, LV_OPA_COVER, "No\ndata", LV_TXT_FLAG_NONE, NULL);
return;
}
}
/**
*
* @param src
* @param header
* @param style
* @return
*/
lv_res_t lv_img_dsc_get_info(const char * src, lv_img_header_t * header)
{
header->always_zero = 0;
/*Try to get info with the custom functions first*/
if(lv_img_decoder_info_custom) {
lv_res_t custom_res;
custom_res = lv_img_decoder_info_custom(src, header);
if(custom_res == LV_RES_OK) return LV_RES_OK; /*Custom info has supported this source*/
}
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_VARIABLE) {
header->w = ((lv_img_dsc_t *)src)->header.w;
header->h = ((lv_img_dsc_t *)src)->header.h;
header->cf = ((lv_img_dsc_t *)src)->header.cf;
}
#if LV_USE_FILESYSTEM
else if(src_type == LV_IMG_SRC_FILE) {
lv_fs_file_t file;
lv_fs_res_t res;
uint32_t rn;
res = lv_fs_open(&file, src, LV_FS_MODE_RD);
if(res == LV_FS_RES_OK) {
res = lv_fs_read(&file, header, sizeof(lv_img_header_t), &rn);
}
/*Create a dummy header on fs error*/
if(res != LV_FS_RES_OK || rn != sizeof(lv_img_header_t)) {
header->w = LV_DPI;
header->h = LV_DPI;
header->cf = LV_IMG_CF_UNKNOWN;
}
lv_fs_close(&file);
}
#endif
else if(src_type == LV_IMG_SRC_SYMBOL) {
/*The size depend on the font but it is unknown here. It should be handled outside of the function*/
header->w = 1;
header->h = 1;
/* Symbols always have transparent parts. Important because of cover check in the design function.
* The actual value doesn't matter because lv_draw_label will draw it*/
header->cf = LV_IMG_CF_ALPHA_1BIT;
} else {
LV_LOG_WARN("Image get info found unknown src type");
return false;
}
return true;
}
uint8_t lv_img_color_format_get_px_size(lv_img_cf_t cf)
{
uint8_t px_size = 0;
switch(cf) {
case LV_IMG_CF_UNKNOWN:
case LV_IMG_CF_RAW:
px_size = 0;
break;
case LV_IMG_CF_TRUE_COLOR:
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
px_size = LV_COLOR_SIZE;
break;
case LV_IMG_CF_TRUE_COLOR_ALPHA:
px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
break;
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_ALPHA_1BIT:
px_size = 1;
break;
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_ALPHA_2BIT:
px_size = 2;
break;
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_ALPHA_4BIT:
px_size = 4;
break;
case LV_IMG_CF_INDEXED_8BIT:
case LV_IMG_CF_ALPHA_8BIT:
px_size = 8;
break;
default:
px_size = 0;
break;
}
return px_size;
}
bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf)
{
bool is_chroma_keyed = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
case LV_IMG_CF_RAW_CHROMA_KEYED:
case LV_IMG_CF_INDEXED_1BIT:
case LV_IMG_CF_INDEXED_2BIT:
case LV_IMG_CF_INDEXED_4BIT:
case LV_IMG_CF_INDEXED_8BIT:
is_chroma_keyed = true;
break;
default:
is_chroma_keyed = false;
break;
}
return is_chroma_keyed;
}
bool lv_img_color_format_has_alpha(lv_img_cf_t cf)
{
bool has_alpha = false;
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_RAW_ALPHA:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
has_alpha = true;
break;
default:
has_alpha = false;
break;
}
return has_alpha;
}
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src)
{
lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
if(src == NULL) return img_src_type;
const uint8_t * u8_p = src;
/*The first byte shows the type of the image source*/
if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
} else if(u8_p[0] >= 0x80) {
img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
} else {
img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is design to the first byte < 0x20*/
}
if (LV_IMG_SRC_UNKNOWN == img_src_type) {
LV_LOG_WARN("lv_img_src_get_type: unknown image type");
}
return img_src_type;
}
/**
* Set custom decoder functions. See the typdefs of the function typed above for more info about them
* @param info_fp info get function
* @param open_fp open function
* @param read_fp read line function
* @param close_fp clode function
*/
void lv_img_decoder_set_custom(lv_img_decoder_info_f_t info_fp, lv_img_decoder_open_f_t open_fp,
lv_img_decoder_read_line_f_t read_fp, lv_img_decoder_close_f_t close_fp)
{
lv_img_decoder_info_custom = info_fp;
lv_img_decoder_open_custom = open_fp;
lv_img_decoder_read_line_custom = read_fp;
lv_img_decoder_close_custom = close_fp;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * mask,
const void * src, const lv_style_t * style, lv_opa_t opa_scale)
{
lv_area_t mask_com; /*Common area of mask and coords*/
bool union_ok;
union_ok = lv_area_intersect(&mask_com, mask, coords);
if(union_ok == false) {
return LV_RES_OK; /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
}
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->image.opa : (uint16_t)((uint16_t) style->image.opa * opa_scale) >> 8;
lv_img_header_t header;
lv_res_t header_res;
header_res = lv_img_dsc_get_info(src, &header);
if(header_res != LV_RES_OK) {
LV_LOG_WARN("Image draw can't get image info");
lv_img_decoder_close();
return LV_RES_INV;
}
bool chroma_keyed = lv_img_color_format_is_chroma_keyed(header.cf);
bool alpha_byte = lv_img_color_format_has_alpha(header.cf);
const uint8_t * img_data = lv_img_decoder_open(src, style);
if(img_data == LV_IMG_DECODER_OPEN_FAIL) {
LV_LOG_WARN("Image draw cannot open the image resource");
lv_img_decoder_close();
return LV_RES_INV;
}
/* The decoder open could open the image and gave the entire uncompressed image.
* Just draw it!*/
if(img_data) {
lv_draw_map(coords, mask, img_data, opa, chroma_keyed, alpha_byte, style->image.color, style->image.intense);
}
/* The whole uncompressed image is not available. Try to read it line-by-line*/
else {
lv_coord_t width = lv_area_get_width(&mask_com);
#if LV_COMPILER_VLA_SUPPORTED
uint8_t buf[(lv_area_get_width(&mask_com) * ((LV_COLOR_DEPTH >> 3) + 1))];
#else
uint8_t buf[LV_HOR_RES_MAX * ((LV_COLOR_DEPTH >> 3) + 1)]; /*+1 because of the possible alpha byte*/
#endif
lv_area_t line;
lv_area_copy(&line, &mask_com);
lv_area_set_height(&line, 1);
lv_coord_t x = mask_com.x1 - coords->x1;
lv_coord_t y = mask_com.y1 - coords->y1;
lv_coord_t row;
lv_res_t read_res;
for(row = mask_com.y1; row <= mask_com.y2; row++) {
read_res = lv_img_decoder_read_line(x, y, width, buf);
if(read_res != LV_RES_OK) {
lv_img_decoder_close();
LV_LOG_WARN("Image draw can't read the line");
return LV_RES_INV;
}
lv_draw_map(&line, mask, buf, opa, chroma_keyed, alpha_byte, style->image.color, style->image.intense);
line.y1++;
line.y2++;
y++;
}
}
lv_img_decoder_close();
return LV_RES_OK;
}
static const uint8_t * lv_img_decoder_open(const void * src, const lv_style_t * style)
{
decoder_custom = false;
/*Try to open with the custom functions first*/
if(lv_img_decoder_open_custom) {
const uint8_t * custom_res;
custom_res = lv_img_decoder_open_custom(src, style);
if(custom_res != LV_IMG_DECODER_OPEN_FAIL) {
decoder_custom = true; /*Mark that custom decoder function should be used for this img source.*/
return custom_res; /*Custom open supported this source*/
}
}
decoder_src = src;
decoder_style = style;
decoder_src_type = lv_img_src_get_type(src);
lv_res_t header_res;
header_res = lv_img_dsc_get_info(src, &decoder_header);
if(header_res == LV_RES_INV) {
decoder_src = NULL;
decoder_src_type = LV_IMG_SRC_UNKNOWN;
LV_LOG_WARN("Built-in image decoder can't get the header info");
return LV_IMG_DECODER_OPEN_FAIL;
}
/*Open the file if it's a file*/
if(decoder_src_type == LV_IMG_SRC_FILE) {
#if LV_USE_FILESYSTEM
lv_fs_res_t res = lv_fs_open(&decoder_file, src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder can't open the file");
return LV_IMG_DECODER_OPEN_FAIL;
}
#else
LV_LOG_WARN("Image built-in decoder can read file because LV_USE_FILESYSTEM = 0");
return LV_IMG_DECODER_OPEN_FAIL;
#endif
}
/*Process the different color formats*/
lv_img_cf_t cf = decoder_header.cf;
if(cf == LV_IMG_CF_TRUE_COLOR ||
cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
/*In case of uncompressed formats if the image stored in the ROM/RAM simply give it's pointer*/
return ((lv_img_dsc_t *)decoder_src)->data;
} else {
/*If it's file it need to be read line by line later*/
return NULL;
}
} else if(cf == LV_IMG_CF_INDEXED_1BIT ||
cf == LV_IMG_CF_INDEXED_2BIT ||
cf == LV_IMG_CF_INDEXED_4BIT ||
cf == LV_IMG_CF_INDEXED_8BIT) {
#if LV_IMG_CF_INDEXED
#if LV_USE_FILESYSTEM
lv_color32_t palette_file[256];
#endif
lv_color32_t * palette_p = NULL;
uint8_t px_size = lv_img_color_format_get_px_size(cf);
uint32_t palette_size = 1 << px_size;
if(decoder_src_type == LV_IMG_SRC_FILE) {
/*Read the palette from file*/
#if LV_USE_FILESYSTEM
lv_fs_seek(&decoder_file, 4); /*Skip the header*/
lv_fs_read(&decoder_file, palette_file, palette_size * sizeof(lv_color32_t), NULL);
palette_p = palette_file;
#else
LV_LOG_WARN("Image built-in decoder can read the palette because LV_USE_FILESYSTEM = 0");
return LV_IMG_DECODER_OPEN_FAIL;
#endif
} else {
/*The palette begins in the beginning of the image data. Just point to it.*/
palette_p = (lv_color32_t *)((lv_img_dsc_t *)decoder_src)->data;
}
uint32_t i;
for(i = 0; i < palette_size; i++) {
decoder_index_map[i] = lv_color_make(palette_p[i].ch.red, palette_p[i].ch.green, palette_p[i].ch.blue);
}
return NULL;
#else
LV_LOG_WARN("Indexed (palette) images are not enabled in lv_conf.h. See LV_IMG_CF_INDEXED");
return LV_IMG_DECODER_OPEN_FAIL;
#endif
} else if(cf == LV_IMG_CF_ALPHA_1BIT ||
cf == LV_IMG_CF_ALPHA_2BIT ||
cf == LV_IMG_CF_ALPHA_4BIT ||
cf == LV_IMG_CF_ALPHA_8BIT) {
#if LV_IMG_CF_ALPHA
return NULL; /*Nothing to process*/
#else
LV_LOG_WARN("Alpha indexed images are not enabled in lv_conf.h. See LV_IMG_CF_ALPHA");
return LV_IMG_DECODER_OPEN_FAIL;
#endif
} else {
LV_LOG_WARN("Image decoder open: unknown color format")
return LV_IMG_DECODER_OPEN_FAIL;
}
}
static lv_res_t lv_img_decoder_read_line(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
/*Try to read the line with the custom functions*/
if(decoder_custom) {
if(lv_img_decoder_read_line_custom) {
lv_res_t custom_res;
custom_res = lv_img_decoder_read_line_custom(x, y, len, buf);
return custom_res;
} else {
LV_LOG_WARN("Image open with custom decoder but read not supported")
}
return LV_RES_INV; /*It"s an error if not returned earlier*/
}
if(decoder_src_type == LV_IMG_SRC_FILE) {
#if LV_USE_FILESYSTEM
uint8_t px_size = lv_img_color_format_get_px_size(decoder_header.cf);
lv_fs_res_t res;
if(decoder_header.cf == LV_IMG_CF_TRUE_COLOR ||
decoder_header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA ||
decoder_header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) {
uint32_t pos = ((y * decoder_header.w + x) * px_size) >> 3;
pos += 4; /*Skip the header*/
res = lv_fs_seek(&decoder_file, pos);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Built-in image decoder seek failed");
return false;
}
uint32_t btr = len * (px_size >> 3);
uint32_t br = 0;
lv_fs_read(&decoder_file, buf, btr, &br);
if(res != LV_FS_RES_OK || btr != br) {
LV_LOG_WARN("Built-in image decoder read failed");
return false;
}
} else if(decoder_header.cf == LV_IMG_CF_ALPHA_1BIT ||
decoder_header.cf == LV_IMG_CF_ALPHA_2BIT ||
decoder_header.cf == LV_IMG_CF_ALPHA_4BIT ||
decoder_header.cf == LV_IMG_CF_ALPHA_8BIT) {
lv_img_built_in_decoder_line_alpha(x, y, len, buf);
} else if(decoder_header.cf == LV_IMG_CF_INDEXED_1BIT ||
decoder_header.cf == LV_IMG_CF_INDEXED_2BIT ||
decoder_header.cf == LV_IMG_CF_INDEXED_4BIT ||
decoder_header.cf == LV_IMG_CF_INDEXED_8BIT) {
lv_img_built_in_decoder_line_indexed(x, y, len, buf);
} else {
LV_LOG_WARN("Built-in image decoder read not supports the color format");
return false;
}
#else
LV_LOG_WARN("Image built-in decoder can't read file because LV_USE_FILESYSTEM = 0");
return false;
#endif
} else if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = decoder_src;
if(img_dsc->header.cf == LV_IMG_CF_ALPHA_1BIT ||
img_dsc->header.cf == LV_IMG_CF_ALPHA_2BIT ||
img_dsc->header.cf == LV_IMG_CF_ALPHA_4BIT ||
img_dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) {
lv_img_built_in_decoder_line_alpha(x, y, len, buf);
} else if(img_dsc->header.cf == LV_IMG_CF_INDEXED_1BIT ||
img_dsc->header.cf == LV_IMG_CF_INDEXED_2BIT ||
img_dsc->header.cf == LV_IMG_CF_INDEXED_4BIT ||
img_dsc->header.cf == LV_IMG_CF_INDEXED_8BIT) {
lv_img_built_in_decoder_line_indexed(x, y, len, buf);
} else {
LV_LOG_WARN("Built-in image decoder not supports the color format");
return false;
}
}
return true;
}
static void lv_img_decoder_close(void)
{
/*Try to close with the custom functions*/
if(decoder_custom) {
if(lv_img_decoder_close_custom) lv_img_decoder_close_custom();
return;
}
/*It was opened with built-in decoder*/
if(decoder_src) {
#if LV_USE_FILESYSTEM
if(decoder_src_type == LV_IMG_SRC_FILE) {
lv_fs_close(&decoder_file);
}
#endif
decoder_src_type = LV_IMG_SRC_UNKNOWN;
decoder_src = NULL;
}
}
static lv_res_t lv_img_built_in_decoder_line_alpha(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
#if LV_IMG_CF_ALPHA
const lv_opa_t alpha1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
const lv_opa_t alpha2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
const lv_opa_t alpha4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
68, 85, 102, 119,
136, 153, 170, 187,
204, 221, 238, 255
};
/*Simply fill the buffer with the color. Later only the alpha value will be modified.*/
lv_color_t bg_color = decoder_style->image.color;
lv_coord_t i;
for(i = 0; i < len; i++) {
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full;
#elif LV_COLOR_DEPTH == 16
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE] = bg_color.full & 0xFF;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + 1] = (bg_color.full >> 8) & 0xFF;
#elif LV_COLOR_DEPTH == 32
*((uint32_t *)&buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE]) = bg_color.full;
#else
#error "Invalid LV_COLOR_DEPTH. Check it in lv_conf.h"
#endif
}
const lv_opa_t * opa_table = NULL;
uint8_t px_size = lv_img_color_format_get_px_size(decoder_header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
uint32_t ofs = 0;
int8_t pos = 0;
switch(decoder_header.cf) {
case LV_IMG_CF_ALPHA_1BIT:
w = (decoder_header.w >> 3); /*E.g. w = 20 -> w = 2 + 1*/
if(decoder_header.w & 0x7) w++;
ofs += w * y + (x >> 3); /*First pixel*/
pos = 7 - (x & 0x7);
opa_table = alpha1_opa_table;
break;
case LV_IMG_CF_ALPHA_2BIT:
w = (decoder_header.w >> 2); /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
if(decoder_header.w & 0x3) w++;
ofs += w * y + (x >> 2); /*First pixel*/
pos = 6 - ((x & 0x3) * 2);
opa_table = alpha2_opa_table;
break;
case LV_IMG_CF_ALPHA_4BIT:
w = (decoder_header.w >> 1); /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
if(decoder_header.w & 0x1) w++;
ofs += w * y + (x >> 1); /*First pixel*/
pos = 4 - ((x & 0x1) * 4);
opa_table = alpha4_opa_table;
break;
case LV_IMG_CF_ALPHA_8BIT:
w = decoder_header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
pos = 0;
break;
}
#if LV_USE_FILESYSTEM
# if LV_COMPILER_VLA_SUPPORTED
uint8_t fs_buf[w];
# else
uint8_t fs_buf[LV_HOR_RES];
# endif
#endif
const uint8_t * data_tmp = NULL;
if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = decoder_src;
data_tmp = img_dsc->data + ofs;
} else {
#if LV_USE_FILESYSTEM
lv_fs_seek(&decoder_file, ofs + 4); /*+4 to skip the header*/
lv_fs_read(&decoder_file, fs_buf, w, NULL);
data_tmp = fs_buf;
#else
LV_LOG_WARN("Image built-in alpha line reader can't read file because LV_USE_FILESYSTEM = 0");
data_tmp = NULL; /*To avoid warnings*/
return LV_RES_INV;
#endif
}
uint8_t byte_act = 0;
uint8_t val_act;
for(i = 0; i < len; i ++) {
val_act = (data_tmp[byte_act] & (mask << pos)) >> pos;
buf[i * LV_IMG_PX_SIZE_ALPHA_BYTE + LV_IMG_PX_SIZE_ALPHA_BYTE - 1] =
decoder_header.cf == LV_IMG_CF_ALPHA_8BIT ? val_act : opa_table[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
return LV_RES_OK;
#else
LV_LOG_WARN("Image built-in alpha line reader failed because LV_IMG_CF_ALPHA is 0 in lv_conf.h");
return LV_RES_INV;
#endif
}
static lv_res_t lv_img_built_in_decoder_line_indexed(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
#if LV_IMG_CF_INDEXED
uint8_t px_size = lv_img_color_format_get_px_size(decoder_header.cf);
uint16_t mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
lv_coord_t w = 0;
int8_t pos = 0;
uint32_t ofs = 0;
switch(decoder_header.cf) {
case LV_IMG_CF_INDEXED_1BIT:
w = (decoder_header.w >> 3); /*E.g. w = 20 -> w = 2 + 1*/
if(decoder_header.w & 0x7) w++;
ofs += w * y + (x >> 3); /*First pixel*/
ofs += 8; /*Skip the palette*/
pos = 7 - (x & 0x7);
break;
case LV_IMG_CF_INDEXED_2BIT:
w = (decoder_header.w >> 2); /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
if(decoder_header.w & 0x3) w++;
ofs += w * y + (x >> 2); /*First pixel*/
ofs += 16; /*Skip the palette*/
pos = 6 - ((x & 0x3) * 2);
break;
case LV_IMG_CF_INDEXED_4BIT:
w = (decoder_header.w >> 1); /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
if(decoder_header.w & 0x1) w++;
ofs += w * y + (x >> 1); /*First pixel*/
ofs += 64; /*Skip the palette*/
pos = 4 - ((x & 0x1) * 4);
break;
case LV_IMG_CF_INDEXED_8BIT:
w = decoder_header.w; /*E.g. x = 7 -> w = 7 (bytes)*/
ofs += w * y + x; /*First pixel*/
ofs += 1024; /*Skip the palette*/
pos = 0;
break;
}
#if LV_USE_FILESYSTEM
# if LV_COMPILER_VLA_SUPPORTED
uint8_t fs_buf[w];
# else
uint8_t fs_buf[LV_HOR_RES];
# endif
#endif
const uint8_t * data_tmp = NULL;
if(decoder_src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = decoder_src;
data_tmp = img_dsc->data + ofs;
} else {
#if LV_USE_FILESYSTEM
lv_fs_seek(&decoder_file, ofs + 4); /*+4 to skip the header*/
lv_fs_read(&decoder_file, fs_buf, w, NULL);
data_tmp = fs_buf;
#else
LV_LOG_WARN("Image built-in indexed line reader can't read file because LV_USE_FILESYSTEM = 0");
data_tmp = NULL; /*To avoid warnings*/
return LV_RES_INV;
#endif
}
uint8_t byte_act = 0;
uint8_t val_act;
lv_coord_t i;
lv_color_t * cbuf = (lv_color_t *) buf;
for(i = 0; i < len; i ++) {
val_act = (data_tmp[byte_act] & (mask << pos)) >> pos;
cbuf[i] = decoder_index_map[val_act];
pos -= px_size;
if(pos < 0) {
pos = 8 - px_size;
data_tmp++;
}
}
return LV_RES_OK;
#else
LV_LOG_WARN("Image built-in indexed line reader failed because LV_IMG_CF_INDEXED is 0 in lv_conf.h");
return LV_RES_INV;
#endif
}

167
src/lv_draw/lv_draw_img.h Normal file
View File

@@ -0,0 +1,167 @@
/**
* @file lv_draw_img.h
*
*/
#ifndef LV_DRAW_IMG_H
#define LV_DRAW_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
#define LV_IMG_DECODER_OPEN_FAIL ((void*)(-1))
/**********************
* TYPEDEFS
**********************/
struct _lv_img_t;
typedef struct {
/* The first 8 bit is very important to distinguish the different source types.
* For more info see `lv_img_get_src_type()` in lv_img.c */
uint32_t cf :5; /* Color format: See `lv_img_color_format_t`*/
uint32_t always_zero :3; /*It the upper bits of the first byte. Always zero to look like a non-printable character*/
uint32_t reserved :2; /*Reserved to be used later*/
uint32_t w:11; /*Width of the image map*/
uint32_t h:11; /*Height of the image map*/
} lv_img_header_t;
/*Image color format*/
enum {
LV_IMG_CF_UNKNOWN = 0,
LV_IMG_CF_RAW, /*Contains the file as it is. Needs custom decoder function*/
LV_IMG_CF_RAW_ALPHA, /*Contains the file as it is. The image has alpha. Needs custom decoder function*/
LV_IMG_CF_RAW_CHROMA_KEYED, /*Contains the file as it is. The image is chroma keyed. Needs custom decoder function*/
LV_IMG_CF_TRUE_COLOR, /*Color format and depth should match with LV_COLOR settings*/
LV_IMG_CF_TRUE_COLOR_ALPHA, /*Same as `LV_IMG_CF_TRUE_COLOR` but every pixel has an alpha byte*/
LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /*Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels will be transparent*/
LV_IMG_CF_INDEXED_1BIT, /*Can have 2 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_2BIT, /*Can have 4 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_4BIT, /*Can have 16 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_INDEXED_8BIT, /*Can have 256 different colors in a palette (always chroma keyed)*/
LV_IMG_CF_ALPHA_1BIT, /*Can have one color and it can be drawn or not*/
LV_IMG_CF_ALPHA_2BIT, /*Can have one color but 4 different alpha value*/
LV_IMG_CF_ALPHA_4BIT, /*Can have one color but 16 different alpha value*/
LV_IMG_CF_ALPHA_8BIT, /*Can have one color but 256 different alpha value*/
};
typedef uint8_t lv_img_cf_t;
/* Image header it is compatible with
* the result image converter utility*/
typedef struct
{
lv_img_header_t header;
uint32_t data_size;
const uint8_t * data;
} lv_img_dsc_t;
/* Decoder function definitions */
/**
* Get info from an image and store in the `header`
* @param src the image source. Can be a pointer to a C array or a file name (Use `lv_img_src_get_type` to determine the type)
* @param header store the info here
* @return LV_RES_OK: info written correctly; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_info_f_t)(const void * src, lv_img_header_t * header);
/**
* Open an image for decoding. Prepare it as it is required to read it later
* @param src the image source. Can be a pointer to a C array or a file name (Use `lv_img_src_get_type` to determine the type)
* @param style the style of image (maybe it will be required to determine a color or something)
* @return there are 3 possible return values:
* 1) buffer with the decoded image
* 2) if can decode the whole image NULL. decoder_read_line will be called to read the image line-by-line
* 3) LV_IMG_DECODER_OPEN_FAIL if the image format is unknown to the decoder or an error occurred
*/
typedef const uint8_t * (*lv_img_decoder_open_f_t)(const void * src, const lv_style_t * style);
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't return with the whole decoded pixel array.
* @param x start x coordinate
* @param y startt y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
typedef lv_res_t (*lv_img_decoder_read_line_f_t)(lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
/**
* Close the pending decoding. Free resources etc.
*/
typedef void (*lv_img_decoder_close_f_t)(void);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw an image
* @param coords the coordinates of the image
* @param mask the image will be drawn only in this area
* @param src pointer to a lv_color_t array which contains the pixels of the image
* @param style style of the image
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_img(const lv_area_t * coords, const lv_area_t * mask,
const void * src, const lv_style_t * style, lv_opa_t opa_scale);
/**
* Get the type of an image source
* @param src pointer to an image source:
* - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
* - a path to a file (e.g. "S:/folder/image.bin")
* - or a symbol (e.g. LV_SYMBOL_CLOSE)
* @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
*/
lv_img_src_t lv_img_src_get_type(const void * src);
/**
* Set custom decoder functions. See the typdefs of the function typed above for more info about them
* @param info_fp info get function
* @param open_fp open function
* @param read_fp read line function
* @param close_fp clode function
*/
void lv_img_decoder_set_custom(lv_img_decoder_info_f_t info_fp, lv_img_decoder_open_f_t open_fp,
lv_img_decoder_read_line_f_t read_fp, lv_img_decoder_close_f_t close_fp);
lv_res_t lv_img_dsc_get_info(const char * src, lv_img_header_t * header);
uint8_t lv_img_color_format_get_px_size(lv_img_cf_t cf);
bool lv_img_color_format_is_chroma_keyed(lv_img_cf_t cf);
bool lv_img_color_format_has_alpha(lv_img_cf_t cf);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TEMPL_H*/

257
src/lv_draw/lv_draw_label.c Normal file
View File

@@ -0,0 +1,257 @@
/**
* @file lv_draw_label.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_label.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LABEL_RECOLOR_PAR_LENGTH 6
/**********************
* TYPEDEFS
**********************/
enum {
CMD_STATE_WAIT,
CMD_STATE_PAR,
CMD_STATE_IN,
};
typedef uint8_t cmd_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static uint8_t hex_char_to_num(char hex);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param style pointer to a style
* @param opa_scale scale down all opacities by the factor
* @param txt 0 terminated text to write
* @param flag settings for the text from 'txt_flag_t' enum
* @param offset text offset in x and y direction (NULL if unused)
*
*/
void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale,
const char * txt, lv_txt_flag_t flag, lv_point_t * offset)
{
const lv_font_t * font = style->text.font;
lv_coord_t w;
if((flag & LV_TXT_FLAG_EXPAND) == 0) {
/*Normally use the label's width as width*/
w = lv_area_get_width(coords);
} else {
/*If EXAPND is enabled then not limit the text's width to the object's width*/
lv_point_t p;
lv_txt_get_size(&p, txt, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag);
w = p.x;
}
lv_coord_t line_height = lv_font_get_height(font) + style->text.line_space;
/*Init variables for the first line*/
lv_coord_t line_width = 0;
lv_point_t pos;
pos.x = coords->x1;
pos.y = coords->y1;
lv_coord_t x_ofs = 0;
lv_coord_t y_ofs = 0;
if(offset != NULL) {
x_ofs = offset->x;
y_ofs = offset->y;
pos.y += y_ofs;
}
uint32_t line_start = 0;
uint32_t line_end = lv_txt_get_next_line(txt, font, style->text.letter_space, w, flag);
/*Go the first visible line*/
while(pos.y + line_height < mask->y1) {
/*Go to next line*/
line_start = line_end;
line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
pos.y += line_height;
if(txt[line_start] == '\0') return;
}
/*Align to middle*/
if(flag & LV_TXT_FLAG_CENTER) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
font, style->text.letter_space, flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(flag & LV_TXT_FLAG_RIGHT) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
font, style->text.letter_space, flag);
pos.x += lv_area_get_width(coords) - line_width;
}
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->text.opa : (uint16_t)((uint16_t) style->text.opa * opa_scale) >> 8;
cmd_state_t cmd_state = CMD_STATE_WAIT;
uint32_t i;
uint16_t par_start = 0;
lv_color_t recolor;
lv_coord_t letter_w;
/*Write out all lines*/
while(txt[line_start] != '\0') {
if(offset != NULL) {
pos.x += x_ofs;
}
/*Write all letter of a line*/
cmd_state = CMD_STATE_WAIT;
i = line_start;
uint32_t letter;
while(i < line_end) {
letter = lv_txt_encoded_next(txt, &i);
/*Handle the re-color command*/
if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
if(letter == (uint32_t)LV_TXT_COLOR_CMD[0]) {
if(cmd_state == CMD_STATE_WAIT) { /*Start char*/
par_start = i;
cmd_state = CMD_STATE_PAR;
continue;
} else if(cmd_state == CMD_STATE_PAR) { /*Other start char in parameter escaped cmd. char */
cmd_state = CMD_STATE_WAIT;
} else if(cmd_state == CMD_STATE_IN) { /*Command end */
cmd_state = CMD_STATE_WAIT;
continue;
}
}
/*Skip the color parameter and wait the space after it*/
if(cmd_state == CMD_STATE_PAR) {
if(letter == ' ') {
/*Get the parameter*/
if(i - par_start == LABEL_RECOLOR_PAR_LENGTH + 1) {
char buf[LABEL_RECOLOR_PAR_LENGTH + 1];
memcpy(buf, &txt[par_start], LABEL_RECOLOR_PAR_LENGTH);
buf[LABEL_RECOLOR_PAR_LENGTH] = '\0';
int r, g, b;
r = (hex_char_to_num(buf[0]) << 4) + hex_char_to_num(buf[1]);
g = (hex_char_to_num(buf[2]) << 4) + hex_char_to_num(buf[3]);
b = (hex_char_to_num(buf[4]) << 4) + hex_char_to_num(buf[5]);
recolor = lv_color_make(r, g, b);
} else {
recolor.full = style->text.color.full;
}
cmd_state = CMD_STATE_IN; /*After the parameter the text is in the command*/
}
continue;
}
}
lv_color_t color = style->text.color;
if(cmd_state == CMD_STATE_IN) color = recolor;
lv_draw_letter(&pos, mask, font, letter, color, opa);
letter_w = lv_font_get_width(font, letter);
if(letter_w > 0){
pos.x += letter_w + style->text.letter_space;
}
}
/*Go to next line*/
line_start = line_end;
line_end += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, w, flag);
pos.x = coords->x1;
/*Align to middle*/
if(flag & LV_TXT_FLAG_CENTER) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
font, style->text.letter_space, flag);
pos.x += (lv_area_get_width(coords) - line_width) / 2;
}
/*Align to the right*/
else if(flag & LV_TXT_FLAG_RIGHT) {
line_width = lv_txt_get_width(&txt[line_start], line_end - line_start,
font, style->text.letter_space, flag);
pos.x += lv_area_get_width(coords) - line_width;
}
/*Go the next line position*/
pos.y += line_height;
if(pos.y > mask->y2) return;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Convert a hexadecimal characters to a number (0..15)
* @param hex Pointer to a hexadecimal character (0..9, A..F)
* @return the numerical value of `hex` or 0 on error
*/
static uint8_t hex_char_to_num(char hex)
{
uint8_t result = 0;
if(hex >= '0' && hex <= '9') {
result = hex - '0';
}
else {
if(hex >= 'a') hex -= 'a' - 'A'; /*Convert to upper case*/
switch(hex) {
case 'A':
result = 10;
break;
case 'B':
result = 11;
break;
case 'C':
result = 12;
break;
case 'D':
result = 13;
break;
case 'E':
result = 14;
break;
case 'F':
result = 15;
break;
default:
result = 0;
break;
}
}
return result;
}

View File

@@ -0,0 +1,53 @@
/**
* @file lv_draw_label.h
*
*/
#ifndef LV_DRAW_LABEL_H
#define LV_DRAW_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Write a text
* @param coords coordinates of the label
* @param mask the label will be drawn only in this area
* @param style pointer to a style
* @param opa_scale scale down all opacities by the factor
* @param txt 0 terminated text to write
* @param flag settings for the text from 'txt_flag_t' enum
* @param offset text offset in x and y direction (NULL if unused)
*
*/
void lv_draw_label(const lv_area_t * coords,const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale,
const char * txt, lv_txt_flag_t flag, lv_point_t * offset);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_LABEL_H*/

608
src/lv_draw/lv_draw_line.c Normal file
View File

@@ -0,0 +1,608 @@
/**
* @file lv_draw_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stdio.h>
#include <stdbool.h>
#include "lv_draw.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#if LV_COMPILER_VLA_SUPPORTED == 0
#define LINE_MAX_WIDTH 64
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_point_t p1;
lv_point_t p2;
lv_point_t p_act;
lv_coord_t dx;
lv_coord_t sx; /*-1: x1 < x2; 1: x2 >= x1*/
lv_coord_t dy;
lv_coord_t sy; /*-1: y1 < y2; 1: y2 >= y1*/
lv_coord_t err;
lv_coord_t e2;
bool hor; /*Rather horizontal or vertical*/
} line_draw_t;
typedef struct {
lv_coord_t width;
lv_coord_t width_1;
lv_coord_t width_half;
} line_width_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
static void line_draw_skew(line_draw_t * main_line, bool dir_ori, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2);
static bool line_next(line_draw_t * line);
static bool line_next_y(line_draw_t * line);
static bool line_next_x(line_draw_t * line);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param mask the line will be drawn only on this area
* @param style pointer to a line's style
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask,
const lv_style_t * style, lv_opa_t opa_scale)
{
if(style->line.width == 0) return;
if(point1->x == point2->x && point1->y == point2->y) return;
line_draw_t main_line;
lv_point_t p1;
lv_point_t p2;
/*If the line if rather vertical then be sure y1 < y2 else x1 < x2*/
if(LV_MATH_ABS(point1->x - point2->x) > LV_MATH_ABS(point1->y - point2->y)) {
/*Steps less in y then x -> rather horizontal*/
if(point1->x < point2->x) {
p1.x = point1->x;
p1.y = point1->y;
p2.x = point2->x;
p2.y = point2->y;
} else {
p1.x = point2->x;
p1.y = point2->y;
p2.x = point1->x;
p2.y = point1->y;
}
} else {
/*Steps less in x then y -> rather vertical*/
if(point1->y < point2->y) {
p1.x = point1->x;
p1.y = point1->y;
p2.x = point2->x;
p2.y = point2->y;
} else {
p1.x = point2->x;
p1.y = point2->y;
p2.x = point1->x;
p2.y = point1->y;
}
}
line_init(&main_line, &p1, &p2);
/*Special case draw a horizontal line*/
if(main_line.p1.y == main_line.p2.y) {
line_draw_hor(&main_line, mask, style, opa_scale);
}
/*Special case draw a vertical line*/
else if(main_line.p1.x == main_line.p2.x) {
line_draw_ver(&main_line, mask, style, opa_scale);
}
/*Arbitrary skew line*/
else {
bool dir_ori = false;
#if LV_ANTIALIAS
lv_point_t p_tmp;
if(main_line.hor) {
if(main_line.p1.y < main_line.p2.y) {
dir_ori = true;
p_tmp.x = main_line.p2.x;
p_tmp.y = main_line.p2.y - 1;
line_init(&main_line, &p1, &p_tmp);
main_line.sy = LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
}
else if(main_line.p1.y > main_line.p2.y) {
dir_ori = false;
p_tmp.x = main_line.p2.x;
p_tmp.y = main_line.p2.y + 1;
line_init(&main_line, &p1, &p_tmp);
main_line.sy = -LV_MATH_ABS(main_line.sy); /*The sign can change if the line becomes horizontal*/
}
}
else {
if(main_line.p1.x < main_line.p2.x) {
dir_ori = true;
p_tmp.x = main_line.p2.x - 1;
p_tmp.y = main_line.p2.y;
line_init(&main_line, &p1, &p_tmp);
main_line.sx = LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
}
else if(main_line.p1.x > main_line.p2.x) {
dir_ori = false;
p_tmp.x = main_line.p2.x + 1;
p_tmp.y = main_line.p2.y;
line_init(&main_line, &p1, &p_tmp);
main_line.sx = -LV_MATH_ABS(main_line.sx); /*The sign can change if the line becomes vertical*/
}
}
#endif
line_draw_skew(&main_line, dir_ori, mask, style, opa_scale);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void line_draw_hor(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
{
lv_coord_t width = style->line.width - 1;
lv_coord_t width_half = width >> 1;
lv_coord_t width_1 = width & 0x1;
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
lv_area_t act_area;
act_area.x1 = main_line->p1.x;
act_area.x2 = main_line->p2.x;
act_area.y1 = main_line->p1.y - width_half - width_1;
act_area.y2 = main_line->p2.y + width_half ;
lv_area_t draw_area;
draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
lv_draw_fill(&draw_area, mask, style->line.color, opa);
}
static void line_draw_ver(line_draw_t * main_line, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
{
lv_coord_t width = style->line.width - 1;
lv_coord_t width_half = width >> 1;
lv_coord_t width_1 = width & 0x1;
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
lv_area_t act_area;
act_area.x1 = main_line->p1.x - width_half;
act_area.x2 = main_line->p2.x + width_half + width_1;
act_area.y1 = main_line->p1.y;
act_area.y2 = main_line->p2.y;
lv_area_t draw_area;
draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
lv_draw_fill(&draw_area, mask, style->line.color, opa);
}
static void line_draw_skew(line_draw_t * main_line, bool dir_ori, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale)
{
lv_opa_t opa = opa_scale == LV_OPA_COVER ? style->line.opa : (uint16_t)((uint16_t) style->line.opa * opa_scale) >> 8;
lv_point_t vect_main, vect_norm;
vect_main.x = main_line->p2.x - main_line->p1.x;
vect_main.y = main_line->p2.y - main_line->p1.y;
if(main_line->hor) {
if(main_line->p1.y < main_line->p2.y + dir_ori) {
vect_norm.x = - vect_main.y;
vect_norm.y = vect_main.x;
} else {
vect_norm.x = vect_main.y;
vect_norm.y = -vect_main.x;
}
} else {
if(main_line->p1.x < main_line->p2.x + dir_ori) {
vect_norm.x = vect_main.y;
vect_norm.y = - vect_main.x;
} else {
vect_norm.x = - vect_main.y;
vect_norm.y = vect_main.x;
}
}
/* In case of a short but tick line the perpendicular ending is longer then the real line.
* it would break the calculations so make the normal vector larger*/
vect_norm.x = vect_norm.x << 4;
vect_norm.y = vect_norm.y << 4;
lv_coord_t width;
width = style->line.width;
/* The pattern stores the points of the line ending. It has the good direction and length.
* The worth case is the 45° line where pattern can have 1.41 x `width` points*/
#if LV_COMPILER_VLA_SUPPORTED
lv_point_t pattern[width * 2];
#else
lv_point_t pattern[LINE_MAX_WIDTH];
#endif
lv_coord_t i = 0;
/*Create a perpendicular pattern (a small line)*/
if(width != 0) {
line_draw_t pattern_line;
lv_point_t p0 = {0, 0};
line_init(&pattern_line, &p0, &vect_norm);
uint32_t width_sqr = width * width;
/* Run for a lot of times. Meanwhile the real width will be determined as well */
for(i = 0; i < (lv_coord_t)sizeof(pattern); i ++) {
pattern[i].x = pattern_line.p_act.x;
pattern[i].y = pattern_line.p_act.y;
/*Finish the pattern line if it's length equal to the desired width (Use Pythagoras theorem)*/
uint32_t sqr = pattern_line.p_act.x * pattern_line.p_act.x + pattern_line.p_act.y * pattern_line.p_act.y;
if(sqr >= width_sqr) {
width = i;
#if LV_ANTIALIAS
width--;
#endif
break;
}
line_next(&pattern_line);
}
}
#if LV_ANTIALIAS
lv_coord_t width_safe = width;
if(width == 0) width_safe = 1;
lv_coord_t aa_last_corner;
aa_last_corner = 0;
#endif
lv_coord_t x_center_ofs = 0;
lv_coord_t y_center_ofs = 0;
if(width != 0) {
x_center_ofs = pattern[width - 1].x / 2;
y_center_ofs = pattern[width - 1].y / 2;
}
else {
if(main_line->hor && main_line->p1.y >= main_line->p2.y + dir_ori) pattern[0].y --;
if(!main_line->hor && main_line->p1.x >= main_line->p2.x + dir_ori) pattern[0].x --;
}
/* Make the coordinates relative to the center */
for(i = 0; i < width; i++) {
pattern[i].x -= x_center_ofs;
pattern[i].y -= y_center_ofs;
#if LV_ANTIALIAS
if(i != 0) {
if(main_line->hor) {
if(pattern[i - 1].x != pattern[i].x) {
lv_coord_t seg_w = pattern[i].y - pattern[aa_last_corner].y;
if(main_line->sy < 0) {
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y + seg_w + 1,
seg_w, mask, style->line.color, opa);
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y + seg_w + 1,
-seg_w, mask, style->line.color, opa);
} else {
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y,
seg_w, mask, style->line.color, opa);
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y,
-seg_w, mask, style->line.color, opa);
}
aa_last_corner = i;
}
} else {
if(pattern[i - 1].y != pattern[i].y) {
lv_coord_t seg_w = pattern[i].x - pattern[aa_last_corner].x;
if(main_line->sx < 0) {
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w + 1, main_line->p1.y + pattern[aa_last_corner].y - 1,
seg_w, mask, style->line.color, opa);
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w + 1, main_line->p2.y + pattern[aa_last_corner].y + 1,
-seg_w, mask, style->line.color, opa);
} else {
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x, main_line->p1.y + pattern[aa_last_corner].y - 1,
seg_w, mask, style->line.color, opa);
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x, main_line->p2.y + pattern[aa_last_corner].y + 1,
-seg_w, mask, style->line.color, opa);
}
aa_last_corner = i;
}
}
}
#endif
}
#if LV_ANTIALIAS
/*Add the last part of anti-aliasing for the perpendicular ending*/
if(width != 0) { /*Due to rounding error with very thin lines it looks ugly*/
if(main_line->hor) {
lv_coord_t seg_w = pattern[width_safe - 1].y - pattern[aa_last_corner].y;
if(main_line->sy < 0) {
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y + seg_w,
seg_w + main_line->sy, mask, style->line.color, opa);
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y + seg_w,
-(seg_w + main_line->sy), mask, style->line.color, opa);
} else {
lv_draw_aa_ver_seg(main_line->p1.x + pattern[aa_last_corner].x - 1, main_line->p1.y + pattern[aa_last_corner].y,
seg_w + main_line->sy, mask, style->line.color, opa);
lv_draw_aa_ver_seg(main_line->p2.x + pattern[aa_last_corner].x + 1, main_line->p2.y + pattern[aa_last_corner].y,
-(seg_w + main_line->sy), mask, style->line.color, opa);
}
} else {
lv_coord_t seg_w = pattern[width_safe - 1].x - pattern[aa_last_corner].x;
if(main_line->sx < 0) {
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x + seg_w, main_line->p1.y + pattern[aa_last_corner].y - 1,
seg_w + main_line->sx, mask, style->line.color, opa);
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x + seg_w, main_line->p2.y + pattern[aa_last_corner].y + 1,
-(seg_w + main_line->sx), mask, style->line.color, opa);
} else {
lv_draw_aa_hor_seg(main_line->p1.x + pattern[aa_last_corner].x, main_line->p1.y + pattern[aa_last_corner].y - 1,
seg_w + main_line->sx, mask, style->line.color, opa);
lv_draw_aa_hor_seg(main_line->p2.x + pattern[aa_last_corner].x, main_line->p2.y + pattern[aa_last_corner].y + 1,
-(seg_w + main_line->sx), mask, style->line.color, opa);
}
}
}
#endif
#if LV_ANTIALIAS
/*Shift the anti aliasing on the edges (-1, 1 or 0 (zero only in case width == 0))*/
lv_coord_t aa_shift1;
lv_coord_t aa_shift2;
if(main_line->hor == false) {
if(main_line->sx < 0) {
aa_shift1 = -1;
aa_shift2 = width == 0 ? 0 : aa_shift1;
} else {
aa_shift2 = 1;
aa_shift1 = width == 0 ? 0 : aa_shift2;
}
} else {
if(main_line->sy < 0) {
aa_shift1 = -1;
aa_shift2 = width == 0 ? 0 : aa_shift1;
} else {
aa_shift2 = 1;
aa_shift1 = width == 0 ? 0 : aa_shift2;
}
}
#endif
volatile lv_point_t prev_p;
prev_p.x = main_line->p1.x;
prev_p.y = main_line->p1.y;
lv_area_t draw_area;
bool first_run = true;
if(main_line->hor) {
while(line_next_y(main_line)) {
for(i = 0; i < width; i++) {
draw_area.x1 = prev_p.x + pattern[i].x;
draw_area.y1 = prev_p.y + pattern[i].y;
draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x - 1;
draw_area.y2 = draw_area.y1;
lv_draw_fill(&draw_area, mask, style->line.color, opa);
/* Fill the gaps
* When stepping in y one pixel remains empty on every corner (don't do this on the first segment ) */
if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
lv_draw_px(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
}
}
#if LV_ANTIALIAS
lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
-(main_line->p_act.x - prev_p.x), mask, style->line.color, opa);
lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
main_line->p_act.x - prev_p.x, mask, style->line.color, opa);
#endif
first_run = false;
prev_p.x = main_line->p_act.x;
prev_p.y = main_line->p_act.y;
}
for(i = 0; i < width; i++) {
draw_area.x1 = prev_p.x + pattern[i].x;
draw_area.y1 = prev_p.y + pattern[i].y;
draw_area.x2 = draw_area.x1 + main_line->p_act.x - prev_p.x;
draw_area.y2 = draw_area.y1;
lv_draw_fill(&draw_area, mask, style->line.color, opa);
/* Fill the gaps
* When stepping in y one pixel remains empty on every corner */
if(i != 0 && pattern[i].x != pattern[i - 1].x && !first_run) {
lv_draw_px(draw_area.x1, draw_area.y1 - main_line->sy, mask, style->line.color, opa);
}
}
#if LV_ANTIALIAS
lv_draw_aa_hor_seg(prev_p.x + pattern[0].x, prev_p.y + pattern[0].y - aa_shift1,
-(main_line->p_act.x - prev_p.x + 1), mask, style->line.color, opa);
lv_draw_aa_hor_seg(prev_p.x + pattern[width_safe - 1].x, prev_p.y + pattern[width_safe - 1].y + aa_shift2,
main_line->p_act.x - prev_p.x + 1, mask, style->line.color, opa);
#endif
}
/*Rather a vertical line*/
else {
while(line_next_x(main_line)) {
for(i = 0; i < width; i++) {
draw_area.x1 = prev_p.x + pattern[i].x;
draw_area.y1 = prev_p.y + pattern[i].y;
draw_area.x2 = draw_area.x1;
draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y - 1;
lv_draw_fill(&draw_area, mask, style->line.color, opa);
/* Fill the gaps
* When stepping in x one pixel remains empty on every corner (don't do this on the first segment ) */
if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
lv_draw_px(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
}
}
#if LV_ANTIALIAS
lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
-(main_line->p_act.y - prev_p.y), mask, style->line.color, opa);
lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
main_line->p_act.y - prev_p.y, mask, style->line.color, opa);
#endif
first_run = false;
prev_p.x = main_line->p_act.x;
prev_p.y = main_line->p_act.y;
}
/*Draw the last part*/
for(i = 0; i < width; i++) {
draw_area.x1 = prev_p.x + pattern[i].x;
draw_area.y1 = prev_p.y + pattern[i].y;
draw_area.x2 = draw_area.x1;
draw_area.y2 = draw_area.y1 + main_line->p_act.y - prev_p.y;
lv_draw_fill(&draw_area, mask, style->line.color, opa);
/* Fill the gaps
* When stepping in x one pixel remains empty on every corner */
if(i != 0 && pattern[i].y != pattern[i - 1].y && !first_run) {
lv_draw_px(draw_area.x1 - main_line->sx, draw_area.y1, mask, style->line.color, opa);
}
}
#if LV_ANTIALIAS
lv_draw_aa_ver_seg(prev_p.x + pattern[0].x - aa_shift1, prev_p.y + pattern[0].y,
-(main_line->p_act.y - prev_p.y + 1), mask, style->line.color, opa);
lv_draw_aa_ver_seg(prev_p.x + pattern[width_safe - 1].x + aa_shift2, prev_p.y + pattern[width_safe - 1].y,
main_line->p_act.y - prev_p.y + 1, mask, style->line.color, opa);
#endif
}
}
static void line_init(line_draw_t * line, const lv_point_t * p1, const lv_point_t * p2)
{
line->p1.x = p1->x;
line->p1.y = p1->y;
line->p2.x = p2->x;
line->p2.y = p2->y;
line->dx = LV_MATH_ABS(line->p2.x - line->p1.x);
line->sx = line->p1.x < line->p2.x ? 1 : -1;
line->dy = LV_MATH_ABS(line->p2.y - line->p1.y);
line->sy = line->p1.y < line->p2.y ? 1 : -1;
line->err = (line->dx > line->dy ? line->dx : -line->dy) / 2;
line->e2 = 0;
line->hor = line->dx > line->dy ? true : false; /*Rather horizontal or vertical*/
line->p_act.x = line->p1.x;
line->p_act.y = line->p1.y;
}
static bool line_next(line_draw_t * line)
{
if(line->p_act.x == line->p2.x && line->p_act.y == line->p2.y) return false;
line->e2 = line->err;
if(line->e2 > -line->dx) {
line->err -= line->dy;
line->p_act.x += line->sx;
}
if(line->e2 < line->dy) {
line->err += line->dx;
line->p_act.y += line->sy;
}
return true;
}
/**
* Iterate until step one in y direction.
* @param line
* @return
*/
static bool line_next_y(line_draw_t * line)
{
lv_coord_t last_y = line->p_act.y;
do {
if(!line_next(line)) return false;
} while(last_y == line->p_act.y);
return true;
}
/**
* Iterate until step one in x direction.
* @param line
* @return
*/
static bool line_next_x(line_draw_t * line)
{
lv_coord_t last_x = line->p_act.x;
do {
if(!line_next(line)) return false;
} while(last_x == line->p_act.x);
return true;
}

View File

@@ -0,0 +1,49 @@
/**
* @file lv_draw_line.h
*
*/
#ifndef LV_DRAW_LINE_H
#define LV_DRAW_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw a line
* @param point1 first point of the line
* @param point2 second point of the line
* @param mask the line will be drawn only on this area
* @param style pointer to a line's style
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_line(const lv_point_t * point1, const lv_point_t * point2, const lv_area_t * mask,
const lv_style_t * style, lv_opa_t opa_scale);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_LINE_H*/

1435
src/lv_draw/lv_draw_rect.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
/**
* @file lv_draw_rect.h
*
*/
#ifndef LV_DRAW_RECT_H
#define LV_DRAW_RECT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Draw a rectangle
* @param coords the coordinates of the rectangle
* @param mask the rectangle will be drawn only in this mask
* @param style pointer to a style
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_rect(const lv_area_t * coords, const lv_area_t * mask, const lv_style_t * style, lv_opa_t opa_scale);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_RECT_H*/

View File

@@ -0,0 +1,164 @@
/**
* @file lv_draw_triangle.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_triangle.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static void point_swap(lv_point_t * p1, lv_point_t * p2);
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
*
* @param points pointer to an array with 3 points
* @param mask the triangle will be drawn only in this mask
* @param color color of the triangle
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, lv_color_t color, lv_opa_t opa_scale)
{
lv_point_t tri[3];
memcpy(tri, points, sizeof(tri));
/*Sort the vertices according to their y coordinate (0: y max, 1: y mid, 2:y min)*/
if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
if(tri[2].y < tri[1].y) point_swap(&tri[2], &tri[1]);
if(tri[1].y < tri[0].y) point_swap(&tri[1], &tri[0]);
/*Return is the triangle is degenerated*/
if(tri[0].x == tri[1].x && tri[0].y == tri[1].y) return;
if(tri[1].x == tri[2].x && tri[1].y == tri[2].y) return;
if(tri[0].x == tri[2].x && tri[0].y == tri[2].y) return;
if(tri[0].x == tri[1].x && tri[1].x == tri[2].x) return;
if(tri[0].y == tri[1].y && tri[1].y == tri[2].y) return;
/*Draw the triangle*/
lv_point_t edge1;
lv_coord_t dx1 = LV_MATH_ABS(tri[0].x - tri[1].x);
lv_coord_t sx1 = tri[0].x < tri[1].x ? 1 : -1;
lv_coord_t dy1 = LV_MATH_ABS(tri[0].y - tri[1].y);
lv_coord_t sy1 = tri[0].y < tri[1].y ? 1 : -1;
lv_coord_t err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
lv_coord_t err_tmp1;
lv_point_t edge2;
lv_coord_t dx2 = LV_MATH_ABS(tri[0].x - tri[2].x);
lv_coord_t sx2 = tri[0].x < tri[2].x ? 1 : -1;
lv_coord_t dy2 = LV_MATH_ABS(tri[0].y - tri[2].y);
lv_coord_t sy2 = tri[0].y < tri[2].y ? 1 : -1;
lv_coord_t err2 = (dx1 > dy2 ? dx2 : -dy2) / 2;
lv_coord_t err_tmp2;
lv_coord_t y1_tmp;
lv_coord_t y2_tmp;
edge1.x = tri[0].x;
edge1.y = tri[0].y;
edge2.x = tri[0].x;
edge2.y = tri[0].y;
lv_area_t act_area;
lv_area_t draw_area;
while(1) {
act_area.x1 = edge1.x;
act_area.x2 = edge2.x ;
act_area.y1 = edge1.y;
act_area.y2 = edge2.y ;
draw_area.x1 = LV_MATH_MIN(act_area.x1, act_area.x2);
draw_area.x2 = LV_MATH_MAX(act_area.x1, act_area.x2);
draw_area.y1 = LV_MATH_MIN(act_area.y1, act_area.y2);
draw_area.y2 = LV_MATH_MAX(act_area.y1, act_area.y2);
draw_area.x2--; /*Do not draw most right pixel because it will be drawn by the adjacent triangle*/
lv_draw_fill(&draw_area, mask, color, LV_OPA_COVER);
/*Calc. the next point of edge1*/
y1_tmp = edge1.y;
do {
if(edge1.x == tri[1].x && edge1.y == tri[1].y) {
dx1 = LV_MATH_ABS(tri[1].x - tri[2].x);
sx1 = tri[1].x < tri[2].x ? 1 : -1;
dy1 = LV_MATH_ABS(tri[1].y - tri[2].y);
sy1 = tri[1].y < tri[2].y ? 1 : -1;
err1 = (dx1 > dy1 ? dx1 : -dy1) / 2;
} else if(edge1.x == tri[2].x && edge1.y == tri[2].y) return;
err_tmp1 = err1;
if(err_tmp1 > -dx1) {
err1 -= dy1;
edge1.x += sx1;
}
if(err_tmp1 < dy1) {
err1 += dx1;
edge1.y += sy1;
}
} while(edge1.y == y1_tmp);
/*Calc. the next point of edge2*/
y2_tmp = edge2.y;
do {
if(edge2.x == tri[2].x && edge2.y == tri[2].y) return;
err_tmp2 = err2;
if(err_tmp2 > -dx2) {
err2 -= dy2;
edge2.x += sx2;
}
if(err_tmp2 < dy2) {
err2 += dx2;
edge2.y += sy2;
}
} while(edge2.y == y2_tmp);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Swap two points
* p1 pointer to the first point
* p2 pointer to the second point
*/
static void point_swap(lv_point_t * p1, lv_point_t * p2)
{
lv_point_t tmp;
tmp.x = p1->x;
tmp.y = p1->y;
p1->x = p2->x;
p1->y = p2->y;
p2->x = tmp.x;
p2->y = tmp.y;
}

View File

@@ -0,0 +1,48 @@
/**
* @file lv_draw_triangle.h
*
*/
#ifndef LV_DRAW_TRIANGLE_H
#define LV_DRAW_TRIANGLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
*
* @param points pointer to an array with 3 points
* @param mask the triangle will be drawn only in this mask
* @param color color of the triangle
* @param opa_scale scale down all opacities by the factor
*/
void lv_draw_triangle(const lv_point_t * points, const lv_area_t * mask, lv_color_t color, lv_opa_t opa_scale);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DRAW_TRIANGLE_H*/

View File

@@ -0,0 +1,165 @@
/**
* @file lv_font_built_in.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_font_builtin.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the built-in fonts
*/
void lv_font_builtin_init(void)
{
/*DEJAVU 10*/
#if LV_USE_FONT_DEJAVU_10 != 0
lv_font_add(&lv_font_dejavu_10, NULL);
#endif
#if LV_USE_FONT_DEJAVU_10_LATIN_SUP != 0
#if LV_USE_FONT_DEJAVU_10 != 0
lv_font_add(&lv_font_dejavu_10_latin_sup, &lv_font_dejavu_10);
#else
lv_font_add(&lv_font_dejavu_10_latin_sup, NULL);
#endif
#endif
#if LV_USE_FONT_DEJAVU_10_CYRILLIC != 0
#if LV_USE_FONT_DEJAVU_10 != 0
lv_font_add(&lv_font_dejavu_10_cyrillic, &lv_font_dejavu_10);
#else
lv_font_add(&lv_font_dejavu_10_cyrillic, NULL);
#endif
#endif
/*SYMBOL 10*/
#if LV_USE_FONT_SYMBOL_10 != 0
#if LV_USE_FONT_DEJAVU_10 != 0
lv_font_add(&lv_font_symbol_10, &lv_font_dejavu_10);
#else
lv_font_add(&lv_font_symbol_10, NULL);
#endif
#endif
/*DEJAVU 20*/
#if LV_USE_FONT_DEJAVU_20 != 0
lv_font_add(&lv_font_dejavu_20, NULL);
#endif
#if LV_USE_FONT_DEJAVU_20_LATIN_SUP != 0
#if LV_USE_FONT_DEJAVU_20 != 0
lv_font_add(&lv_font_dejavu_20_latin_sup, &lv_font_dejavu_20);
#else
lv_font_add(&lv_font_symbol_20_latin_sup, NULL);
#endif
#endif
#if LV_USE_FONT_DEJAVU_20_CYRILLIC != 0
#if LV_USE_FONT_DEJAVU_20 != 0
lv_font_add(&lv_font_dejavu_20_cyrillic, &lv_font_dejavu_20);
#else
lv_font_add(&lv_font_dejavu_20_cyrillic, NULL);
#endif
#endif
/*SYMBOL 20*/
#if LV_USE_FONT_SYMBOL_20 != 0
#if LV_USE_FONT_DEJAVU_20 != 0
lv_font_add(&lv_font_symbol_20, &lv_font_dejavu_20);
#else
lv_font_add(&lv_font_symbol_20, NULL);
#endif
#endif
/*DEJAVU 30*/
#if LV_USE_FONT_DEJAVU_30 != 0
lv_font_add(&lv_font_dejavu_30, NULL);
#endif
#if LV_USE_FONT_DEJAVU_30_LATIN_SUP != 0
#if LV_USE_FONT_DEJAVU_30 != 0
lv_font_add(&lv_font_dejavu_30_latin_sup, &lv_font_dejavu_30);
#else
lv_font_add(&lv_font_dejavu_30_latin_sup, NULL);
#endif
#endif
#if LV_USE_FONT_DEJAVU_30_CYRILLIC != 0
#if LV_USE_FONT_DEJAVU_30 != 0
lv_font_add(&lv_font_dejavu_30_cyrillic, &lv_font_dejavu_30);
#else
lv_font_add(&lv_font_dejavu_30_cyrillic, NULL);
#endif
#endif
/*SYMBOL 30*/
#if LV_USE_FONT_SYMBOL_30 != 0
#if LV_USE_FONT_DEJAVU_30 != 0
lv_font_add(&lv_font_symbol_30, &lv_font_dejavu_30);
#else
lv_font_add(&lv_font_symbol_30_basic, NULL);
#endif
#endif
/*DEJAVU 40*/
#if LV_USE_FONT_DEJAVU_40 != 0
lv_font_add(&lv_font_dejavu_40, NULL);
#endif
#if LV_USE_FONT_DEJAVU_40_LATIN_SUP != 0
#if LV_USE_FONT_DEJAVU_40 != 0
lv_font_add(&lv_font_dejavu_40_latin_sup, &lv_font_dejavu_40);
#else
lv_font_add(&lv_font_dejavu_40_latin_sup, NULL);
#endif
#endif
#if LV_USE_FONT_DEJAVU_40_CYRILLIC != 0
#if LV_USE_FONT_DEJAVU_40 != 0
lv_font_add(&lv_font_dejavu_40_cyrillic, &lv_font_dejavu_40);
#else
lv_font_add(&lv_font_dejavu_40_cyrillic, NULL);
#endif
#endif
/*SYMBOL 40*/
#if LV_USE_FONT_SYMBOL_40 != 0
#if LV_USE_FONT_DEJAVU_40 != 0
lv_font_add(&lv_font_symbol_40, &lv_font_dejavu_40);
#else
lv_font_add(&lv_font_symbol_40, NULL);
#endif
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,125 @@
/**
* @file lv_font_builtin.h
*
*/
#ifndef LV_FONT_BUILTIN_H
#define LV_FONT_BUILTIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include "../lv_misc/lv_font.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the built-in fonts
*/
void lv_font_builtin_init(void);
/**********************
* MACROS
**********************/
/**********************
* FONT DECLARATIONS
**********************/
/*10 px */
#if LV_USE_FONT_DEJAVU_10
LV_FONT_DECLARE(lv_font_dejavu_10)
#endif
#if LV_USE_FONT_DEJAVU_10_LATIN_SUP
LV_FONT_DECLARE(lv_font_dejavu_10_latin_sup)
#endif
#if LV_USE_FONT_DEJAVU_10_CYRILLIC
LV_FONT_DECLARE(lv_font_dejavu_10_cyrillic)
#endif
#if LV_USE_FONT_SYMBOL_10
LV_FONT_DECLARE(lv_font_symbol_10)
#endif
/*20 px */
#if LV_USE_FONT_DEJAVU_20
LV_FONT_DECLARE(lv_font_dejavu_20)
#endif
#if LV_USE_FONT_DEJAVU_20_LATIN_SUP
LV_FONT_DECLARE(lv_font_dejavu_20_latin_sup)
#endif
#if LV_USE_FONT_DEJAVU_20_CYRILLIC
LV_FONT_DECLARE(lv_font_dejavu_20_cyrillic)
#endif
#if LV_USE_FONT_SYMBOL_20
LV_FONT_DECLARE(lv_font_symbol_20)
#endif
/*30 px */
#if LV_USE_FONT_DEJAVU_30
LV_FONT_DECLARE(lv_font_dejavu_30)
#endif
#if LV_USE_FONT_DEJAVU_30_LATIN_SUP
LV_FONT_DECLARE(lv_font_dejavu_30_latin_sup)
#endif
#if LV_USE_FONT_DEJAVU_30_CYRILLIC
LV_FONT_DECLARE(lv_font_dejavu_30_cyrillic)
#endif
#if LV_USE_FONT_SYMBOL_30
LV_FONT_DECLARE(lv_font_symbol_30)
#endif
/*40 px */
#if LV_USE_FONT_DEJAVU_40
LV_FONT_DECLARE(lv_font_dejavu_40)
#endif
#if LV_USE_FONT_DEJAVU_40_LATIN_SUP
LV_FONT_DECLARE(lv_font_dejavu_40_latin_sup)
#endif
#if LV_USE_FONT_DEJAVU_40_CYRILLIC
LV_FONT_DECLARE(lv_font_dejavu_40_cyrillic)
#endif
#if LV_USE_FONT_SYMBOL_40
LV_FONT_DECLARE(lv_font_symbol_40)
#endif
#if LV_USE_FONT_MONOSPACE_8
LV_FONT_DECLARE(lv_font_monospace_8)
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_FONT_BUILTIN_H*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

23
src/lv_fonts/lv_fonts.mk Normal file
View File

@@ -0,0 +1,23 @@
CSRCS += lv_font_builtin.c
CSRCS += lv_font_dejavu_10.c
CSRCS += lv_font_dejavu_20.c
CSRCS += lv_font_dejavu_30.c
CSRCS += lv_font_dejavu_40.c
CSRCS += lv_font_dejavu_10_cyrillic.c
CSRCS += lv_font_dejavu_20_cyrillic.c
CSRCS += lv_font_dejavu_30_cyrillic.c
CSRCS += lv_font_dejavu_40_cyrillic.c
CSRCS += lv_font_dejavu_10_latin_sup.c
CSRCS += lv_font_dejavu_20_latin_sup.c
CSRCS += lv_font_dejavu_30_latin_sup.c
CSRCS += lv_font_dejavu_40_latin_sup.c
CSRCS += lv_font_symbol_10.c
CSRCS += lv_font_symbol_20.c
CSRCS += lv_font_symbol_30.c
CSRCS += lv_font_symbol_40.c
CSRCS += lv_font_monospace_8.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/src/lv_fonts
VPATH += :$(LVGL_DIR)/lvgl/src/lv_fonts
CFLAGS += "-I$(LVGL_DIR)/lvgl/src/lv_fonts"

40
src/lv_hal/lv_hal.h Normal file
View File

@@ -0,0 +1,40 @@
/**
* @file lv_hal.h
*
*/
#ifndef LV_HAL_H
#define LV_HAL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_hal_disp.h"
#include "lv_hal_indev.h"
#include "lv_hal_tick.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

8
src/lv_hal/lv_hal.mk Normal file
View File

@@ -0,0 +1,8 @@
CSRCS += lv_hal_disp.c
CSRCS += lv_hal_indev.c
CSRCS += lv_hal_tick.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/src/lv_hal
VPATH += :$(LVGL_DIR)/lvgl/src/lv_hal
CFLAGS += "-I$(LVGL_DIR)/lvgl/src/lv_hal"

296
src/lv_hal/lv_hal_disp.c Normal file
View File

@@ -0,0 +1,296 @@
/**
* @file hal_disp.c
*
* @description HAL layer for display driver
*
*/
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stddef.h>
#include "lv_hal.h"
#include "../lv_misc/lv_mem.h"
#include "../lv_core/lv_obj.h"
#include "../lv_misc/lv_gc.h"
#if defined(LV_GC_INCLUDE)
# include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static lv_disp_t * disp_def;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize a display driver with default values.
* It is used to surly have known values in the fields ant not memory junk.
* After it you can set the fields.
* @param driver pointer to driver variable to initialize
*/
void lv_disp_drv_init(lv_disp_drv_t * driver)
{
memset(driver, 0, sizeof(lv_disp_drv_t));
driver->flush_cb = NULL;
driver->hor_res = LV_HOR_RES_MAX;
driver->ver_res = LV_VER_RES_MAX;
driver->buffer = NULL;
#if LV_USE_GPU
driver->mem_blend = NULL;
driver->mem_fill = NULL;
#endif
driver->set_px_cb = NULL;
}
/**
* Initialize a display buffer
* @param disp_buf pointer `lv_disp_buf_t` variable to initialize
* @param buf1 A buffer to be used by LittlevGL to draw the image.
* Always has to specified and can't be NULL.
* Can be an array allocated by the user. E.g. `static lv_color_t disp_buf1[1024 * 10]`
* Or a memory address e.g. in external SRAM
* @param buf2 Optionally specify a second buffer to make image rendering and image flushing
* (sending to the display) parallel.
* In the `disp_drv->flush` you should use DMA or similar hardware to send
* the image to the display in the background.
* It lets LittlevGL to render next frame into the other buffer while previous is being sent.
* Set to `NULL` if unused.
* @param size size of the `buf1` and `buf2` in pixel count.
*/
void lv_disp_buf_init(lv_disp_buf_t * disp_buf, void * buf1, void * buf2, uint32_t size)
{
memset(disp_buf, 0, sizeof(lv_disp_buf_t));
disp_buf->buf1 = buf1;
disp_buf->buf2 = buf2;
disp_buf->buf_act = disp_buf->buf1;
disp_buf->size = size;
}
/**
* Register an initialized display driver.
* Automatically set the first display as active.
* @param driver pointer to an initialized 'lv_disp_drv_t' variable (can be local variable)
* @return pointer to the new display or NULL on error
*/
lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
{
lv_disp_t * disp = lv_ll_ins_head(&LV_GC_ROOT(_lv_disp_ll));
if(!disp) {
lv_mem_assert(disp);
return NULL;
}
memcpy(&disp->driver, driver, sizeof(lv_disp_drv_t));
memset(&disp->inv_area_joined, 0, sizeof(disp->inv_area_joined));
memset(&disp->inv_areas, 0, sizeof(disp->inv_areas));
lv_ll_init(&disp->scr_ll, sizeof(lv_obj_t));
if(disp_def == NULL) disp_def = disp;
lv_disp_t * disp_def_tmp = disp_def;
disp_def = disp; /*Temporarily change the default screen to create the default screens on the new display*/
disp->act_scr = lv_obj_create(NULL, NULL); /*Create a default screen on the display*/
disp->top_layer = lv_obj_create(NULL, NULL); /*Create top layer on the display*/
disp->sys_layer = lv_obj_create(NULL, NULL); /*Create top layer on the display*/
lv_obj_set_style(disp->top_layer, &lv_style_transp);
lv_obj_set_style(disp->sys_layer, &lv_style_transp);
disp->inv_p = 0;
lv_obj_invalidate(disp->act_scr);
disp_def = disp_def_tmp; /*Revert the default display*/
return disp;
}
/**
* Remove a display
* @param disp pointer to display
*/
void lv_disp_remove(lv_disp_t * disp)
{
bool was_default = false;
if(disp == lv_disp_get_default()) was_default = true;
/*Detach the input devices */
lv_indev_t * indev;
indev = lv_indev_next(NULL);
while(indev) {
if(indev->driver.disp == disp) {
indev->driver.disp = NULL;
}
indev = lv_indev_next(indev);
}
lv_ll_rem(&LV_GC_ROOT(_lv_disp_ll), disp);
lv_mem_free(disp);
if(was_default) lv_disp_set_default(lv_ll_get_head(&LV_GC_ROOT(_lv_disp_ll)));
}
/**
* Set a default screen. The new screens will be created on it by default.
* @param disp pointer to a display
*/
void lv_disp_set_default(lv_disp_t * disp)
{
disp_def = disp;
}
/**
* Get the default display
* @return pointer to the default display
*/
lv_disp_t * lv_disp_get_default(void)
{
return disp_def;
}
/**
* Get the horizontal resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal resolution of the display
*/
lv_coord_t lv_disp_get_hor_res(lv_disp_t * disp)
{
if(disp == NULL) disp = lv_disp_get_default();
if(disp == NULL) return LV_HOR_RES_MAX;
else return disp->driver.hor_res;
}
/**
* Get the vertical resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the vertical resolution of the display
*/
lv_coord_t lv_disp_get_ver_res(lv_disp_t * disp)
{
if(disp == NULL) disp = lv_disp_get_default();
if(disp == NULL) return LV_VER_RES_MAX;
else return disp->driver.ver_res;
}
/**
* Call in the display driver's `flush_cb` function when the flushing is finished
* @param disp_drv pointer to display driver in `flush_cb` where this function is called
*/
LV_ATTRIBUTE_FLUSH_READY void lv_disp_flush_ready(lv_disp_drv_t * disp_drv)
{
disp_drv->buffer->flushing = 0;
/*If the screen is transparent initialize it when the flushing is ready*/
#if LV_COLOR_SCREEN_TRANSP
memset(vdb_buf, 0x00, LV_VDB_SIZE_IN_BYTES);
#endif
}
/**
* Get the next display.
* @param disp pointer to the current display. NULL to initialize.
* @return the next display or NULL if no more. Give the first display when the parameter is NULL
*/
lv_disp_t * lv_disp_get_next(lv_disp_t * disp)
{
if(disp == NULL) return lv_ll_get_head(&LV_GC_ROOT(_lv_disp_ll));
else return lv_ll_get_next(&LV_GC_ROOT(_lv_disp_ll), disp);
}
/**
* Get the internal buffer of a display
* @param disp pointer to a display
* @return pointer to the internal buffers
*/
lv_disp_buf_t * lv_disp_get_buf(lv_disp_t * disp)
{
return disp->driver.buffer;
}
/**
* Get the number of areas in the buffer
* @return number of invalid areas
*/
uint16_t lv_disp_get_inv_buf_size(lv_disp_t * disp)
{
return disp->inv_p;
}
/**
* Pop (delete) the last 'num' invalidated areas from the buffer
* @param num number of areas to delete
*/
void lv_disp_pop_from_inv_buf(lv_disp_t * disp, uint16_t num)
{
if(disp->inv_p < num) disp->inv_p = 0;
else disp->inv_p -= num;
}
/**
* Check the driver configuration if it's double buffered (both `buf1` and `buf2` are set)
* @param disp pointer to to display to check
* @return true: double buffered; false: not double buffered
*/
bool lv_disp_is_double_buf(lv_disp_t * disp)
{
if(disp->driver.buffer->buf1 && disp->driver.buffer->buf2) return true;
else return false;
}
/**
* Check the driver configuration if it's TRUE double buffered (both `buf1` and `buf2` are set and `size` is screen sized)
* @param disp pointer to to display to check
* @return true: double buffered; false: not double buffered
*/
bool lv_disp_is_true_double_buf(lv_disp_t * disp)
{
uint32_t scr_size = disp->driver.hor_res * disp->driver.ver_res;
if(lv_disp_is_double_buf(disp) &&
disp->driver.buffer->size == scr_size) {
return true;
}
else {
return false;
}
}
/**********************
* STATIC FUNCTIONS
**********************/

250
src/lv_hal/lv_hal_disp.h Normal file
View File

@@ -0,0 +1,250 @@
/**
* @file lv_hal_disp.h
*
* @description Display Driver HAL interface header file
*
*/
#ifndef LV_HAL_DISP_H
#define LV_HAL_DISP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stdbool.h>
#include "lv_hal.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_ll.h"
/*********************
* DEFINES
*********************/
#ifndef LV_INV_BUF_SIZE
#define LV_INV_BUF_SIZE 32 /*Buffer size for invalid areas */
#endif
#ifndef LV_ATTRIBUTE_FLUSH_READY
# define LV_ATTRIBUTE_FLUSH_READY
#endif
/**********************
* TYPEDEFS
**********************/
struct _disp_t;
struct _disp_drv_t;
typedef struct
{
void * buf1;
void * buf2;
/*Used by the library*/
void * buf_act;
uint32_t size; /*In pixel count*/
lv_area_t area;
volatile uint32_t flushing :1;
}lv_disp_buf_t;
/**
* Display Driver structure to be registered by HAL
*/
typedef struct _disp_drv_t {
/*Horizontal and vertical resolution*/
lv_coord_t hor_res;
lv_coord_t ver_res;
/* Pointer to a buffer initialized with `lv_disp_buf_init()`.
* LittlevGL will use this buffer(s) to draw the screens contents */
lv_disp_buf_t * buffer;
/* MANDATORY: Write the internal buffer (VDB) to the display. 'lv_flush_ready()' has to be called when finished */
void (*flush_cb)(struct _disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
/* OPTIONAL: Extend the invalidated areas to match with the display drivers requirements
* E.g. round `y` to, 8, 16 ..) on a monochrome display*/
void (*rounder_cb)(struct _disp_drv_t * disp_drv, lv_area_t * area);
/* OPTIONAL: Set a pixel in a buffer according to the special requirements of the display
* Can be used for color format not supported in LittelvGL. E.g. 2 bit -> 4 gray scales
* Note: Much slower then drawing with supported color formats. */
void (*set_px_cb)(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
/* OPTIONAL: Called after every refresh cycle to tell the rendering and flushing time + the number of flushed pixels */
void (*monitor_cb)(struct _disp_drv_t * disp_drv, uint32_t time, uint32_t px);
#if LV_USE_USER_DATA_SINGLE
lv_disp_drv_user_data_t user_data;
#endif
#if LV_USE_USER_DATA_MULTI
lv_disp_drv_user_data_t flush_user_data;
lv_disp_drv_user_data_t rounder_user_data;
lv_disp_drv_user_data_t set_px_user_data;
lv_disp_drv_user_data_t monitor_user_data;
#endif
#if LV_USE_GPU
/*OPTIONAL: Blend two memories using opacity (GPU only)*/
void (*mem_blend)(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
/*OPTIONAL: Fill a memory with a color (GPU only)*/
void (*mem_fill)(lv_color_t * dest, uint32_t length, lv_color_t color);
#endif
} lv_disp_drv_t;
struct _lv_obj_t;
typedef struct _disp_t {
/*Driver to the display*/
lv_disp_drv_t driver;
/*Screens of the display*/
lv_ll_t scr_ll;
struct _lv_obj_t * act_scr;
struct _lv_obj_t * top_layer;
struct _lv_obj_t * sys_layer;
/*Invalidated (marked to redraw) areas*/
lv_area_t inv_areas[LV_INV_BUF_SIZE];
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
uint32_t inv_p :10;
} lv_disp_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a display driver with default values.
* It is used to surly have known values in the fields ant not memory junk.
* After it you can set the fields.
* @param driver pointer to driver variable to initialize
*/
void lv_disp_drv_init(lv_disp_drv_t * driver);
/**
* Initialize a display buffer
* @param disp_buf pointer `lv_disp_buf_t` variable to initialize
* @param buf1 A buffer to be used by LittlevGL to draw the image.
* Always has to specified and can't be NULL.
* Can be an array allocated by the user. E.g. `static lv_color_t disp_buf1[1024 * 10]`
* Or a memory address e.g. in external SRAM
* @param buf2 Optionally specify a second buffer to make image rendering and image flushing
* (sending to the display) parallel.
* In the `disp_drv->flush` you should use DMA or similar hardware to send
* the image to the display in the background.
* It lets LittlevGL to render next frame into the other buffer while previous is being sent.
* Set to `NULL` if unused.
* @param size size of the `buf1` and `buf2` in pixel count.
*/
void lv_disp_buf_init(lv_disp_buf_t * disp_buf, void * buf1, void * buf2, uint32_t size);
/**
* Register an initialized display driver.
* Automatically set the first display as active.
* @param driver pointer to an initialized 'lv_disp_drv_t' variable (can be local variable)
* @return pointer to the new display or NULL on error
*/
lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver);
/**
* Remove a display
* @param disp pointer to display
*/
void lv_disp_remove(lv_disp_t * disp);
/**
* Set a default screen. The new screens will be created on it by default.
* @param disp pointer to a display
*/
void lv_disp_set_default(lv_disp_t * disp);
/**
* Get the default display
* @return pointer to the default display
*/
lv_disp_t * lv_disp_get_default(void);
/**
* Get the horizontal resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the horizontal resolution of the display
*/
lv_coord_t lv_disp_get_hor_res(lv_disp_t * disp);
/**
* Get the vertical resolution of a display
* @param disp pointer to a display (NULL to use the default display)
* @return the vertical resolution of the display
*/
lv_coord_t lv_disp_get_ver_res(lv_disp_t * disp);
/**
* Call in the display driver's `flush_cb` function when the flushing is finished
* @param disp_drv pointer to display driver in `flush_cb` where this function is called
*/
LV_ATTRIBUTE_FLUSH_READY void lv_disp_flush_ready(lv_disp_drv_t * disp_drv);
/**
* Get the next display.
* @param disp pointer to the current display. NULL to initialize.
* @return the next display or NULL if no more. Give the first display when the parameter is NULL
*/
lv_disp_t * lv_disp_get_next(lv_disp_t * disp);
/**
* Get the internal buffer of a display
* @param disp pointer to a display
* @return pointer to the internal buffers
*/
lv_disp_buf_t * lv_disp_get_buf(lv_disp_t * disp);
/**
* Get the number of areas in the buffer
* @return number of invalid areas
*/
uint16_t lv_disp_get_inv_buf_size(lv_disp_t * disp);
/**
* Pop (delete) the last 'num' invalidated areas from the buffer
* @param num number of areas to delete
*/
void lv_disp_pop_from_inv_buf(lv_disp_t * disp, uint16_t num);
/**
* Check the driver configuration if it's double buffered (both `buf1` and `buf2` are set)
* @param disp pointer to to display to check
* @return true: double buffered; false: not double buffered
*/
bool lv_disp_is_double_buf(lv_disp_t * disp);
/**
* Check the driver configuration if it's TRUE double buffered (both `buf1` and `buf2` are set and `size` is screen sized)
* @param disp pointer to to display to check
* @return true: double buffered; false: not double buffered
*/
bool lv_disp_is_true_double_buf(lv_disp_t * disp);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

137
src/lv_hal/lv_hal_indev.c Normal file
View File

@@ -0,0 +1,137 @@
/**
* @file hal_indev.c
*
* @description Input device HAL interface
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_hal/lv_hal_indev.h"
#include "../lv_misc/lv_mem.h"
#include "../lv_misc/lv_gc.h"
#include "lv_hal_disp.h"
#if defined(LV_GC_INCLUDE)
# include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize an input device driver with default values.
* It is used to surly have known values in the fields ant not memory junk.
* After it you can set the fields.
* @param driver pointer to driver variable to initialize
*/
void lv_indev_drv_init(lv_indev_drv_t * driver)
{
memset(driver, 0, sizeof(lv_indev_drv_t));
driver->type = LV_INDEV_TYPE_NONE;
}
/**
* Register an initialized input device driver.
* @param driver pointer to an initialized 'lv_indev_drv_t' variable (can be local variable)
* @return pointer to the new input device or NULL on error
*/
lv_indev_t * lv_indev_drv_register(lv_indev_drv_t * driver)
{
if(driver->disp == NULL) driver->disp = lv_disp_get_default();
if(driver->disp == NULL) {
LV_LOG_WARN("lv_indev_drv_register: no display registered hence can't attache the indev to a display");
return NULL;
}
lv_indev_t * node = lv_ll_ins_head(&LV_GC_ROOT(_lv_indev_ll));
if(!node) {
lv_mem_assert(node);
return NULL;
}
memset(node, 0, sizeof(lv_indev_t));
memcpy(&node->driver, driver, sizeof(lv_indev_drv_t));
node->proc.reset_query = 1;
node->cursor = NULL;
node->group = NULL;
node->btn_points = NULL;
return node;
}
/**
* Get the next input device.
* @param indev pointer to the current input device. NULL to initialize.
* @return the next input devise or NULL if no more. Give the first input device when the parameter is NULL
*/
lv_indev_t * lv_indev_next(lv_indev_t * indev)
{
if(indev == NULL) return lv_ll_get_head(LV_GC_ROOT(&_lv_indev_ll));
else return lv_ll_get_next(LV_GC_ROOT(&_lv_indev_ll), indev);
}
/**
* Read data from an input device.
* @param indev pointer to an input device
* @param data input device will write its data here
* @return false: no more data; true: there more data to read (buffered)
*/
bool lv_indev_read(lv_indev_t * indev, lv_indev_data_t * data)
{
bool cont = false;
memset(data, 0, sizeof(lv_indev_data_t));
/* For touchpad sometimes users don't the last pressed coordinate on release.
* So be sure a coordinates are initialized to the last point */
if(indev->driver.type == LV_INDEV_TYPE_POINTER) {
data->point.x = indev->proc.types.pointer.act_point.x;
data->point.y = indev->proc.types.pointer.act_point.y;
}
/*Similarly set at least the last key in case of the the user doesn't set it on release*/
else if(indev->driver.type == LV_INDEV_TYPE_KEYPAD) {
data->key = indev->proc.types.keypad.last_key;
}
if(indev->driver.read_cb) {
LV_LOG_TRACE("idnev read started");
cont = indev->driver.read_cb(&indev->driver, data);
LV_LOG_TRACE("idnev read finished");
} else {
LV_LOG_WARN("indev function registered");
}
return cont;
}
/**********************
* STATIC FUNCTIONS
**********************/

177
src/lv_hal/lv_hal_indev.h Normal file
View File

@@ -0,0 +1,177 @@
/**
* @file lv_hal_indev.h
*
* @description Input Device HAL interface layer header file
*
*/
#ifndef LV_HAL_INDEV_H
#define LV_HAL_INDEV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdbool.h>
#include <stdint.h>
#include "../lv_misc/lv_area.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_obj_t;
struct _disp_t;
struct _lv_indev_t;
struct _lv_indev_drv_t;
/*Possible input device types*/
enum {
LV_INDEV_TYPE_NONE, /*Show uninitialized state*/
LV_INDEV_TYPE_POINTER, /*Touch pad, mouse, external button*/
LV_INDEV_TYPE_KEYPAD, /*Keypad or keyboard*/
LV_INDEV_TYPE_BUTTON, /*External (hardware button) which is assinged to a specific point of the screen*/
LV_INDEV_TYPE_ENCODER, /*Encoder with only Left, Right turn and a Button*/
};
typedef uint8_t lv_hal_indev_type_t;
/*States for input devices*/
enum {
LV_INDEV_STATE_REL = 0,
LV_INDEV_STATE_PR
};
typedef uint8_t lv_indev_state_t;
/*Data type when an input device is read */
typedef struct {
lv_point_t point; /*For LV_INDEV_TYPE_POINTER the currently pressed point*/
uint32_t key; /*For LV_INDEV_TYPE_KEYPAD the currently pressed key*/
uint32_t btn_id; /*For LV_INDEV_TYPE_BUTTON the currently pressed button*/
int16_t enc_diff; /*For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/
lv_indev_state_t state; /*LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/
} lv_indev_data_t;
/*Initialized by the user and registered by 'lv_indev_add()'*/
typedef struct _lv_indev_drv_t {
lv_hal_indev_type_t type; /*Input device type*/
bool (*read_cb)(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t *data); /*Function pointer to read_cb data. Return 'true' if there is still data to be read_cb (buffered)*/
#if LV_USE_USER_DATA_MULTI
lv_indev_drv_user_data_t read_user_data; /*Pointer to user defined data, passed in 'lv_indev_data_t' on read*/
#endif
#if LV_USE_USER_DATA_SINGLE
lv_indev_drv_user_data_t user_data;
#endif
struct _disp_t * disp; /*Pointer to the assigned display*/
} lv_indev_drv_t;
/*Run time data of input devices*/
typedef struct _lv_indev_proc_t {
lv_indev_state_t state;
union {
struct { /*Pointer and button data*/
lv_point_t act_point;
lv_point_t last_point;
lv_point_t vect;
lv_point_t drag_sum; /*Count the dragged pixels to check LV_INDEV_DRAG_LIMIT*/
lv_point_t drag_throw_vect;
struct _lv_obj_t * act_obj; /*The object being pressed*/
struct _lv_obj_t * last_obj; /*The last obejct which was pressed (used by dragthrow and other post-release event)*/
struct _lv_obj_t * last_pressed; /*The lastly pressed object*/
/*Flags*/
uint8_t drag_limit_out :1;
uint8_t drag_in_prog :1;
uint8_t wait_until_release :1;
}pointer;
struct { /*Keypad data*/
lv_indev_state_t last_state;
uint32_t last_key;
}keypad;
}types;
uint32_t pr_timestamp; /*Pressed time stamp*/
uint32_t longpr_rep_timestamp; /*Long press repeat time stamp*/
/*Flags*/
uint8_t long_pr_sent :1;
uint8_t reset_query :1;
uint8_t disabled :1;
} lv_indev_proc_t;
typedef void (*lv_indev_feedback_t)(struct _lv_indev_t *, uint8_t);
struct _lv_obj_t;
struct _lv_group_t;
/*The main input device descriptor with driver, runtime data ('proc') and some additional information*/
typedef struct _lv_indev_t {
lv_indev_drv_t driver;
lv_indev_proc_t proc;
lv_indev_feedback_t feedback;
uint32_t last_activity_time;
struct _lv_obj_t *cursor; /*Cursor for LV_INPUT_TYPE_POINTER*/
struct _lv_group_t *group; /*Keypad destination group*/
const lv_point_t * btn_points; /*Array points assigned to the button ()screen will be pressed here by the buttons*/
} lv_indev_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize an input device driver with default values.
* It is used to surly have known values in the fields ant not memory junk.
* After it you can set the fields.
* @param driver pointer to driver variable to initialize
*/
void lv_indev_drv_init(lv_indev_drv_t *driver);
/**
* Register an initialized input device driver.
* @param driver pointer to an initialized 'lv_indev_drv_t' variable (can be local variable)
* @return pointer to the new input device or NULL on error
*/
lv_indev_t * lv_indev_drv_register(lv_indev_drv_t *driver);
/**
* Get the next input device.
* @param indev pointer to the current input device. NULL to initialize.
* @return the next input devise or NULL if no more. Give the first input device when the parameter is NULL
*/
lv_indev_t * lv_indev_next(lv_indev_t * indev);
/**
* Read data from an input device.
* @param indev pointer to an input device
* @param data input device will write its data here
* @return false: no more data; true: there more data to read (buffered)
*/
bool lv_indev_read(lv_indev_t * indev, lv_indev_data_t *data);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

100
src/lv_hal/lv_hal_tick.c Normal file
View File

@@ -0,0 +1,100 @@
/**
* @file systick.c
* Provide access to the system tick with 1 millisecond resolution
*/
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include "lv_hal_tick.h"
#include <stddef.h>
#if LV_TICK_CUSTOM == 1
#include LV_TICK_CUSTOM_INCLUDE
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static uint32_t sys_time = 0;
static volatile uint8_t tick_irq_flag;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* You have to call this function periodically
* @param tick_period the call period of this function in milliseconds
*/
LV_ATTRIBUTE_TICK_INC void lv_tick_inc(uint32_t tick_period)
{
tick_irq_flag = 0;
sys_time += tick_period;
}
/**
* Get the elapsed milliseconds since start up
* @return the elapsed milliseconds
*/
uint32_t lv_tick_get(void)
{
#if LV_TICK_CUSTOM == 0
uint32_t result;
do {
tick_irq_flag = 1;
result = sys_time;
} while(!tick_irq_flag); /*'lv_tick_inc()' clears this flag which can be in an interrupt. Continue until make a non interrupted cycle */
return result;
#else
return LV_TICK_CUSTOM_SYS_TIME_EXPR;
#endif
}
/**
* Get the elapsed milliseconds since a previous time stamp
* @param prev_tick a previous time stamp (return value of systick_get() )
* @return the elapsed milliseconds since 'prev_tick'
*/
uint32_t lv_tick_elaps(uint32_t prev_tick)
{
uint32_t act_time = lv_tick_get();
/*If there is no overflow in sys_time simple subtract*/
if(act_time >= prev_tick) {
prev_tick = act_time - prev_tick;
} else {
prev_tick = UINT32_MAX - prev_tick + 1;
prev_tick += act_time;
}
return prev_tick;
}
/**********************
* STATIC FUNCTIONS
**********************/

66
src/lv_hal/lv_hal_tick.h Normal file
View File

@@ -0,0 +1,66 @@
/**
* @file lv_hal_tick.h
* Provide access to the system tick with 1 millisecond resolution
*/
#ifndef LV_HAL_TICK_H
#define LV_HAL_TICK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdint.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
#ifndef LV_ATTRIBUTE_TICK_INC
#define LV_ATTRIBUTE_TICK_INC
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* You have to call this function periodically
* @param tick_period the call period of this function in milliseconds
*/
LV_ATTRIBUTE_TICK_INC void lv_tick_inc(uint32_t tick_period);
/**
* Get the elapsed milliseconds since start up
* @return the elapsed milliseconds
*/
uint32_t lv_tick_get(void);
/**
* Get the elapsed milliseconds since a previous time stamp
* @param prev_tick a previous time stamp (return value of systick_get() )
* @return the elapsed milliseconds since 'prev_tick'
*/
uint32_t lv_tick_elaps(uint32_t prev_tick);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_HAL_TICK_H*/

445
src/lv_misc/lv_anim.c Normal file
View File

@@ -0,0 +1,445 @@
/**
* @file anim.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_anim.h"
#if LV_USE_ANIMATION
#include <stddef.h>
#include <string.h>
#include "../lv_hal/lv_hal_tick.h"
#include "lv_task.h"
#include "lv_math.h"
#include "lv_gc.h"
#if defined(LV_GC_INCLUDE)
# include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
/*********************
* DEFINES
*********************/
#define LV_ANIM_RESOLUTION 1024
#define LV_ANIM_RES_SHIFT 10
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void anim_task(void * param);
static bool anim_ready_handler(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
static uint32_t last_task_run;
static bool anim_list_changed;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Init. the animation module
*/
void lv_anim_init(void)
{
lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t));
last_task_run = lv_tick_get();
lv_task_create(anim_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL);
}
/**
* Create an animation
* @param anim_p an initialized 'anim_t' variable. Not required after call.
*/
void lv_anim_create(lv_anim_t * anim_p)
{
LV_LOG_TRACE("animation create started")
/* Do not let two animations for the same 'var' with the same 'fp'*/
if(anim_p->fp != NULL) lv_anim_del(anim_p->var, anim_p->fp); /*fp == NULL would delete all animations of var*/
/*Add the new animation to the animation linked list*/
lv_anim_t * new_anim = lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll));
lv_mem_assert(new_anim);
if(new_anim == NULL) return;
/*Initialize the animation descriptor*/
anim_p->playback_now = 0;
memcpy(new_anim, anim_p, sizeof(lv_anim_t));
/*Set the start value*/
if(new_anim->fp != NULL) new_anim->fp(new_anim->var, new_anim->start);
/* Creating an animation changed the linked list.
* It's important if it happens in a ready callback. (see `anim_task`)*/
anim_list_changed = true;
LV_LOG_TRACE("animation created")
}
/**
* Delete an animation for a variable with a given animator function
* @param var pointer to variable
* @param fp a function pointer which is animating 'var',
* or NULL to delete all animations of 'var'
* @return true: at least 1 animation is deleted, false: no animation is deleted
*/
bool lv_anim_del(void * var, lv_anim_fp_t fp)
{
lv_anim_t * a;
lv_anim_t * a_next;
bool del = false;
a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
while(a != NULL) {
/*'a' might be deleted, so get the next object while 'a' is valid*/
a_next = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
if(a->var == var && (a->fp == fp || fp == NULL)) {
lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);
lv_mem_free(a);
anim_list_changed = true; /*Read by `anim_task`. It need to know if a delete occurred in the linked list*/
del = true;
}
a = a_next;
}
return del;
}
/**
* Get the number of currently running animations
* @return the number of running animations
*/
uint16_t lv_anim_count_running(void)
{
uint16_t cnt = 0;
lv_anim_t * a;
LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a) cnt++;
return cnt++;
}
/**
* Calculate the time of an animation with a given speed and the start and end values
* @param speed speed of animation in unit/sec
* @param start start value of the animation
* @param end end value of the animation
* @return the required time [ms] for the animation with the given parameters
*/
uint16_t lv_anim_speed_to_time(uint16_t speed, int32_t start, int32_t end)
{
int32_t d = LV_MATH_ABS((int32_t) start - end);
uint32_t time = (int32_t)((int32_t)(d * 1000) / speed);
if(time > UINT16_MAX) time = UINT16_MAX;
if(time == 0) {
time++;
}
return time;
}
/**
* Calculate the current value of an animation applying linear characteristic
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_linear(const lv_anim_t * a)
{
/*Calculate the current step*/
uint16_t step;
if(a->time == a->act_time) step = LV_ANIM_RESOLUTION; /*Use the last value if the time fully elapsed*/
else step = (a->act_time * LV_ANIM_RESOLUTION) / a->time;
/* Get the new value which will be proportional to `step`
* and the `start` and `end` values*/
int32_t new_value;
new_value = (int32_t) step * (a->end - a->start);
new_value = new_value >> LV_ANIM_RES_SHIFT;
new_value += a->start;
return new_value;
}
/**
* Calculate the current value of an animation slowing down the start phase
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_ease_in(const lv_anim_t * a)
{
/*Calculate the current step*/
uint32_t t;
if(a->time == a->act_time) t = 1024;
else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
int32_t step = lv_bezier3(t, 0, 1, 1, 1024);
int32_t new_value;
new_value = (int32_t) step * (a->end - a->start);
new_value = new_value >> 10;
new_value += a->start;
return new_value;
}
/**
* Calculate the current value of an animation slowing down the end phase
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_ease_out(const lv_anim_t * a)
{
/*Calculate the current step*/
uint32_t t;
if(a->time == a->act_time) t = 1024;
else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
int32_t step = lv_bezier3(t, 0, 1023, 1023, 1024);
int32_t new_value;
new_value = (int32_t) step * (a->end - a->start);
new_value = new_value >> 10;
new_value += a->start;
return new_value;
}
/**
* Calculate the current value of an animation applying an "S" characteristic (cosine)
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_ease_in_out(const lv_anim_t * a)
{
/*Calculate the current step*/
uint32_t t;
if(a->time == a->act_time) t = 1024;
else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
int32_t step = lv_bezier3(t, 0, 100, 924, 1024);
int32_t new_value;
new_value = (int32_t) step * (a->end - a->start);
new_value = new_value >> 10;
new_value += a->start;
return new_value;
}
/**
* Calculate the current value of an animation with overshoot at the end
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_overshoot(const lv_anim_t * a)
{
/*Calculate the current step*/
uint32_t t;
if(a->time == a->act_time) t = 1024;
else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
int32_t step = lv_bezier3(t, 0, 600, 1300, 1024);
int32_t new_value;
new_value = (int32_t) step * (a->end - a->start);
new_value = new_value >> 10;
new_value += a->start;
return new_value;
}
/**
* Calculate the current value of an animation with 3 bounces
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_bounce(const lv_anim_t * a)
{
/*Calculate the current step*/
uint32_t t;
if(a->time == a->act_time) t = 1024;
else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time;
int32_t diff = (a->end - a->start);
/*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/
if(t < 408){
/*Go down*/
t = (t * 2500) >> 10; /*[0..1024] range*/
}
else if(t >= 408 && t < 614) {
/*First bounce back*/
t -= 408;
t = t * 5; /*to [0..1024] range*/
t = 1024 - t;
diff = diff / 6;
}
else if(t >= 614 && t < 819) {
/*Fall back*/
t -= 614;
t = t * 5; /*to [0..1024] range*/
diff = diff / 6;
}
else if(t >= 819 && t < 921) {
/*Second bounce back*/
t -= 819;
t = t * 10; /*to [0..1024] range*/
t = 1024 - t;
diff = diff / 16;
}
else if(t >= 921 && t <= 1024) {
/*Fall back*/
t -= 921;
t = t * 10; /*to [0..1024] range*/
diff = diff / 16;
}
if(t > 1024) t = 1024;
int32_t step = lv_bezier3(t, 1024, 1024, 800, 0);
int32_t new_value;
new_value = (int32_t) step * diff;
new_value = new_value >> 10;
new_value = a->end - new_value;
return new_value;
}
/**
* Calculate the current value of an animation applying step characteristic.
* (Set end value on the end of the animation)
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_step(const lv_anim_t * a)
{
if(a->act_time >= a->time) return a->end;
else return a->start;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Periodically handle the animations.
* @param param unused
*/
static void anim_task(void * param)
{
(void)param;
lv_anim_t * a;
LV_LL_READ(LV_GC_ROOT(_lv_anim_ll), a) {
a->has_run = 0;
}
uint32_t elaps = lv_tick_elaps(last_task_run);
a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
while(a != NULL) {
/*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete happened in `anim_ready_handler`
* which could make this linked list reading corrupt because the list is changed meanwhile
*/
anim_list_changed = false;
if(!a->has_run) {
a->has_run = 1; /*The list readying might be reseted so need to know which anim has run already*/
a->act_time += elaps;
if(a->act_time >= 0) {
if(a->act_time > a->time) a->act_time = a->time;
int32_t new_value;
new_value = a->path(a);
if(a->fp != NULL) a->fp(a->var, new_value); /*Apply the calculated value*/
/*If the time is elapsed the animation is ready*/
if(a->act_time >= a->time) {
anim_ready_handler(a);
}
}
}
/* If the linked list changed due to anim. delete then it's not safe to continue
* the reading of the list from here -> start from the head*/
if(anim_list_changed) a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll));
else a = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a);
}
last_task_run = lv_tick_get();
}
/**
* Called when an animation is ready to do the necessary thinks
* e.g. repeat, play back, delete etc.
* @param a pointer to an animation descriptor
* @return true: animation delete occurred nnd the `LV_GC_ROOT(_lv_anim_ll)` has changed
* */
static bool anim_ready_handler(lv_anim_t * a)
{
/*Delete the animation if
* - no repeat and no play back (simple one shot animation)
* - no repeat, play back is enabled and play back is ready */
if((a->repeat == 0 && a->playback == 0) ||
(a->repeat == 0 && a->playback == 1 && a->playback_now == 1)) {
void (*cb)(void *) = a->end_cb;
void * p = a->var;
lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a);
lv_mem_free(a);
anim_list_changed = true;
/* Call the callback function at the end*/
/* Check if an animation is deleted in the cb function
* if yes then the caller function has to know this*/
if(cb != NULL) cb(p);
}
/*If the animation is not deleted then restart it*/
else {
a->act_time = - a->repeat_pause; /*Restart the animation*/
/*Swap the start and end values in play back mode*/
if(a->playback != 0) {
/*If now turning back use the 'playback_pause*/
if(a->playback_now == 0) a->act_time = - a->playback_pause;
/*Toggle the play back state*/
a->playback_now = a->playback_now == 0 ? 1 : 0;
/*Swap the start and end values*/
int32_t tmp;
tmp = a->start;
a->start = a->end;
a->end = tmp;
}
}
return anim_list_changed;
}
#endif

177
src/lv_misc/lv_anim.h Normal file
View File

@@ -0,0 +1,177 @@
/**
* @file anim.h
*
*/
#ifndef ANIM_H
#define ANIM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_ANIMATION
#include <stdint.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_anim_t;
typedef int32_t(*lv_anim_path_t)(const struct _lv_anim_t*);
typedef void (*lv_anim_fp_t)(void *, int32_t);
typedef void (*lv_anim_cb_t)(void *);
typedef struct _lv_anim_t
{
void * var; /*Variable to animate*/
lv_anim_fp_t fp; /*Animator function*/
lv_anim_cb_t end_cb; /*Call it when the animation is ready*/
lv_anim_path_t path; /*An array with the steps of animations*/
int32_t start; /*Start value*/
int32_t end; /*End value*/
uint16_t time; /*Animation time in ms*/
int16_t act_time; /*Current time in animation. Set to negative to make delay.*/
uint16_t playback_pause; /*Wait before play back*/
uint16_t repeat_pause; /*Wait before repeat*/
uint8_t playback :1; /*When the animation is ready play it back*/
uint8_t repeat :1; /*Repeat the animation infinitely*/
/*Animation system use these - user shouldn't set*/
uint8_t playback_now :1; /*Play back is in progress*/
uint32_t has_run :1; /*Indicates the animation has run it this round*/
} lv_anim_t;
/*Example initialization
lv_anim_t a;
a.var = obj;
a.start = lv_obj_get_height(obj);
a.end = new_height;
a.fp = (lv_anim_fp_t)lv_obj_set_height;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = 200;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
*/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init. the animation module
*/
void lv_anim_init(void);
/**
* Create an animation
* @param anim_p an initialized 'anim_t' variable. Not required after call.
*/
void lv_anim_create(lv_anim_t * anim_p);
/**
* Delete an animation for a variable with a given animatior function
* @param var pointer to variable
* @param fp a function pointer which is animating 'var',
* or NULL to ignore it and delete all animation with 'var
* @return true: at least 1 animation is deleted, false: no animation is deleted
*/
bool lv_anim_del(void * var, lv_anim_fp_t fp);
/**
* Get the number of currently running animations
* @return the number of running animations
*/
uint16_t lv_anim_count_running(void);
/**
* Calculate the time of an animation with a given speed and the start and end values
* @param speed speed of animation in unit/sec
* @param start start value of the animation
* @param end end value of the animation
* @return the required time [ms] for the animation with the given parameters
*/
uint16_t lv_anim_speed_to_time(uint16_t speed, int32_t start, int32_t end);
/**
* Calculate the current value of an animation applying linear characteristic
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_linear(const lv_anim_t *a);
/**
* Calculate the current value of an animation slowing down the start phase
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_ease_in(const lv_anim_t * a);
/**
* Calculate the current value of an animation slowing down the end phase
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_ease_out(const lv_anim_t * a);
/**
* Calculate the current value of an animation applying an "S" characteristic (cosine)
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_ease_in_out(const lv_anim_t *a);
/**
* Calculate the current value of an animation with overshoot at the end
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_overshoot(const lv_anim_t * a);
/**
* Calculate the current value of an animation with 3 bounces
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_bounce(const lv_anim_t * a);
/**
* Calculate the current value of an animation applying step characteristic.
* (Set end value on the end of the animation)
* @param a pointer to an animation
* @return the current value to set
*/
int32_t lv_anim_path_step(const lv_anim_t *a);
/**********************
* MACROS
**********************/
#endif /*LV_USE_ANIMATION == 0*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ANIM_H*/

200
src/lv_misc/lv_area.c Normal file
View File

@@ -0,0 +1,200 @@
/**
* @file lv_area.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_area.h"
#include "lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize an area
* @param area_p pointer to an area
* @param x1 left coordinate of the area
* @param y1 top coordinate of the area
* @param x2 right coordinate of the area
* @param y2 bottom coordinate of the area
*/
void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2)
{
area_p->x1 = x1;
area_p->y1 = y1;
area_p->x2 = x2;
area_p->y2 = y2;
}
/**
* Set the width of an area
* @param area_p pointer to an area
* @param w the new width of the area (w == 1 makes x1 == x2)
*/
void lv_area_set_width(lv_area_t * area_p, lv_coord_t w)
{
area_p->x2 = area_p->x1 + w - 1;
}
/**
* Set the height of an area
* @param area_p pointer to an area
* @param h the new height of the area (h == 1 makes y1 == y2)
*/
void lv_area_set_height(lv_area_t * area_p, lv_coord_t h)
{
area_p->y2 = area_p->y1 + h - 1;
}
/**
* Set the position of an area (width and height will be kept)
* @param area_p pointer to an area
* @param x the new x coordinate of the area
* @param y the new y coordinate of the area
*/
void lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y)
{
lv_coord_t w = lv_area_get_width(area_p);
lv_coord_t h = lv_area_get_height(area_p);
area_p->x1 = x;
area_p->y1 = y;
lv_area_set_width(area_p, w);
lv_area_set_height(area_p, h);
}
/**
* Return with area of an area (x * y)
* @param area_p pointer to an area
* @return size of area
*/
uint32_t lv_area_get_size(const lv_area_t * area_p)
{
uint32_t size;
size = (uint32_t)(area_p->x2 - area_p->x1 + 1) *
(area_p->y2 - area_p->y1 + 1);
return size;
}
/**
* Get the common parts of two areas
* @param res_p pointer to an area, the result will be stored here
* @param a1_p pointer to the first area
* @param a2_p pointer to the second area
* @return false: the two area has NO common parts, res_p is invalid
*/
bool lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
{
/* Get the smaller area from 'a1_p' and 'a2_p' */
res_p->x1 = LV_MATH_MAX(a1_p->x1, a2_p->x1);
res_p->y1 = LV_MATH_MAX(a1_p->y1, a2_p->y1);
res_p->x2 = LV_MATH_MIN(a1_p->x2, a2_p->x2);
res_p->y2 = LV_MATH_MIN(a1_p->y2, a2_p->y2);
/*If x1 or y1 greater then x2 or y2 then the areas union is empty*/
bool union_ok = true;
if((res_p->x1 > res_p->x2) ||
(res_p->y1 > res_p->y2)) {
union_ok = false;
}
return union_ok;
}
/**
* Join two areas into a third which involves the other two
* @param res_p pointer to an area, the result will be stored here
* @param a1_p pointer to the first area
* @param a2_p pointer to the second area
*/
void lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
{
a_res_p->x1 = LV_MATH_MIN(a1_p->x1, a2_p->x1);
a_res_p->y1 = LV_MATH_MIN(a1_p->y1, a2_p->y1);
a_res_p->x2 = LV_MATH_MAX(a1_p->x2, a2_p->x2);
a_res_p->y2 = LV_MATH_MAX(a1_p->y2, a2_p->y2);
}
/**
* Check if a point is on an area
* @param a_p pointer to an area
* @param p_p pointer to a point
* @return false:the point is out of the area
*/
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p)
{
bool is_on = false;
if((p_p->x >= a_p->x1 && p_p->x <= a_p->x2) &&
((p_p->y >= a_p->y1 && p_p->y <= a_p->y2))) {
is_on = true;
}
return is_on;
}
/**
* Check if two area has common parts
* @param a1_p pointer to an area.
* @param a2_p pointer to an other area
* @return false: a1_p and a2_p has no common parts
*/
bool lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p)
{
if((a1_p->x1 <= a2_p->x2) &&
(a1_p->x2 >= a2_p->x1) &&
(a1_p->y1 <= a2_p->y2) &&
(a1_p->y2 >= a2_p->y1)) {
return true;
} else {
return false;
}
}
/**
* Check if an area is fully on an other
* @param ain_p pointer to an area which could be in 'aholder_p'
* @param aholder pointer to an area which could involve 'ain_p'
* @return
*/
bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p)
{
bool is_in = false;
if(ain_p->x1 >= aholder_p->x1 &&
ain_p->y1 >= aholder_p->y1 &&
ain_p->x2 <= aholder_p->x2 &&
ain_p->y2 <= aholder_p->y2) {
is_in = true;
}
return is_in;
}
/**********************
* STATIC FUNCTIONS
**********************/

169
src/lv_misc/lv_area.h Normal file
View File

@@ -0,0 +1,169 @@
/**
* @file lv_area.h
*
*/
#ifndef LV_AREA_H
#define LV_AREA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
/*********************
* DEFINES
*********************/
#define LV_COORD_MAX (16383) /*To avoid overflow don't let the max [-32,32k] range */
#define LV_COORD_MIN (-16384)
/**********************
* TYPEDEFS
**********************/
typedef int16_t lv_coord_t;
typedef struct
{
lv_coord_t x;
lv_coord_t y;
} lv_point_t;
typedef struct
{
lv_coord_t x1;
lv_coord_t y1;
lv_coord_t x2;
lv_coord_t y2;
} lv_area_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize an area
* @param area_p pointer to an area
* @param x1 left coordinate of the area
* @param y1 top coordinate of the area
* @param x2 right coordinate of the area
* @param y2 bottom coordinate of the area
*/
void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2);
/**
* Copy an area
* @param dest pointer to the destination area
* @param src pointer to the source area
*/
inline static void lv_area_copy(lv_area_t * dest, const lv_area_t * src)
{
memcpy(dest, src, sizeof(lv_area_t));
}
/**
* Get the width of an area
* @param area_p pointer to an area
* @return the width of the area (if x1 == x2 -> width = 1)
*/
static inline lv_coord_t lv_area_get_width(const lv_area_t * area_p)
{
return area_p->x2 - area_p->x1 + 1;
}
/**
* Get the height of an area
* @param area_p pointer to an area
* @return the height of the area (if y1 == y2 -> height = 1)
*/
static inline lv_coord_t lv_area_get_height(const lv_area_t * area_p)
{
return area_p->y2 - area_p->y1 + 1;
}
/**
* Set the width of an area
* @param area_p pointer to an area
* @param w the new width of the area (w == 1 makes x1 == x2)
*/
void lv_area_set_width(lv_area_t * area_p, lv_coord_t w);
/**
* Set the height of an area
* @param area_p pointer to an area
* @param h the new height of the area (h == 1 makes y1 == y2)
*/
void lv_area_set_height(lv_area_t * area_p, lv_coord_t h);
/**
* Set the position of an area (width and height will be kept)
* @param area_p pointer to an area
* @param x the new x coordinate of the area
* @param y the new y coordinate of the area
*/
void lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y);
/**
* Return with area of an area (x * y)
* @param area_p pointer to an area
* @return size of area
*/
uint32_t lv_area_get_size(const lv_area_t * area_p);
/**
* Get the common parts of two areas
* @param res_p pointer to an area, the result will be stored her
* @param a1_p pointer to the first area
* @param a2_p pointer to the second area
* @return false: the two area has NO common parts, res_p is invalid
*/
bool lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p);
/**
* Join two areas into a third which involves the other two
* @param res_p pointer to an area, the result will be stored here
* @param a1_p pointer to the first area
* @param a2_p pointer to the second area
*/
void lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p);
/**
* Check if a point is on an area
* @param a_p pointer to an area
* @param p_p pointer to a point
* @return false:the point is out of the area
*/
bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p);
/**
* Check if two area has common parts
* @param a1_p pointer to an area.
* @param a2_p pointer to an other area
* @return false: a1_p and a2_p has no common parts
*/
bool lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p);
/**
* Check if an area is fully on an other
* @param ain_p pointer to an area which could be on aholder_p
* @param aholder pointer to an area which could involve ain_p
* @return
*/
bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

79
src/lv_misc/lv_circ.c Normal file
View File

@@ -0,0 +1,79 @@
/**
* @file lv_circ.c
* Circle drawing algorithm (with Bresenham)
* Only a 1/8 circle is calculated. Use CIRC_OCT1_X, CIRC_OCT1_Y macros to get
* the other octets.
*/
/*********************
* INCLUDES
*********************/
#include "lv_circ.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the circle drawing
* @param c pointer to a point. The coordinates will be calculated here
* @param tmp point to a variable. It will store temporary data
* @param radius radius of the circle
*/
void lv_circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius)
{
c->x = radius;
c->y = 0;
*tmp = 1 - radius;
}
/**
* Test the circle drawing is ready or not
* @param c same as in circ_init
* @return true if the circle is not ready yet
*/
bool lv_circ_cont(lv_point_t * c)
{
return c->y <= c->x ? true : false;
}
/**
* Get the next point from the circle
* @param c same as in circ_init. The next point stored here.
* @param tmp same as in circ_init.
*/
void lv_circ_next(lv_point_t * c, lv_coord_t * tmp)
{
c->y++;
if(*tmp <= 0) {
(*tmp) += 2 * c->y + 1; // Change in decision criterion for y -> y+1
} else {
c->x--;
(*tmp) += 2 * (c->y - c->x) + 1; // Change for y -> y+1, x -> x-1
}
}
/**********************
* STATIC FUNCTIONS
**********************/

79
src/lv_misc/lv_circ.h Normal file
View File

@@ -0,0 +1,79 @@
/**
* @file lv_circ.h
*
*/
#ifndef LV_CIRC_H
#define LV_CIRC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_area.h"
/*********************
* DEFINES
*********************/
#define LV_CIRC_OCT1_X(p) (p.x)
#define LV_CIRC_OCT1_Y(p) (p.y)
#define LV_CIRC_OCT2_X(p) (p.y)
#define LV_CIRC_OCT2_Y(p) (p.x)
#define LV_CIRC_OCT3_X(p) (-p.y)
#define LV_CIRC_OCT3_Y(p) (p.x)
#define LV_CIRC_OCT4_X(p) (-p.x)
#define LV_CIRC_OCT4_Y(p) (p.y)
#define LV_CIRC_OCT5_X(p) (-p.x)
#define LV_CIRC_OCT5_Y(p) (-p.y)
#define LV_CIRC_OCT6_X(p) (-p.y)
#define LV_CIRC_OCT6_Y(p) (-p.x)
#define LV_CIRC_OCT7_X(p) (p.y)
#define LV_CIRC_OCT7_Y(p) (-p.x)
#define LV_CIRC_OCT8_X(p) (p.x)
#define LV_CIRC_OCT8_Y(p) (-p.y)
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the circle drawing
* @param c pointer to a point. The coordinates will be calculated here
* @param tmp point to a variable. It will store temporary data
* @param radius radius of the circle
*/
void lv_circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius);
/**
* Test the circle drawing is ready or not
* @param c same as in circ_init
* @return true if the circle is not ready yet
*/
bool lv_circ_cont(lv_point_t * c);
/**
* Get the next point from the circle
* @param c same as in circ_init. The next point stored here.
* @param tmp same as in circ_init.
*/
void lv_circ_next(lv_point_t * c, lv_coord_t * tmp);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

143
src/lv_misc/lv_color.c Normal file
View File

@@ -0,0 +1,143 @@
/**
* @file lv_color.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_color.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Convert a HSV color to RGB
* @param h hue [0..359]
* @param s saturation [0..100]
* @param v value [0..100]
* @return the given RGB color in RGB (with LV_COLOR_DEPTH depth)
*/
lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v)
{
h = (uint32_t)((uint32_t)h * 255) / 360;
s = (uint16_t)((uint16_t)s * 255) / 100;
v = (uint16_t)((uint16_t)v * 255) / 100;
uint8_t r, g, b;
uint8_t region, remainder, p, q, t;
if(s == 0) {
r = v;
g = v;
b = v;
return lv_color_make(v, v, v);
}
region = h / 43;
remainder = (h - (region * 43)) * 6;
p = (v * (255 - s)) >> 8;
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
switch(region) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default:
r = v;
g = p;
b = q;
break;
}
lv_color_t result = lv_color_make(r, g, b);
return result;
}
/**
* Convert an RGB color to HSV
* @param r red
* @param g green
* @param b blue
* @return the given RGB color n HSV
*/
lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b)
{
lv_color_hsv_t hsv;
uint8_t rgbMin, rgbMax;
rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b);
rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b);
hsv.v = rgbMax;
if(hsv.v == 0) {
hsv.h = 0;
hsv.s = 0;
return hsv;
}
hsv.s = 255 * (long)(rgbMax - rgbMin) / hsv.v;
if(hsv.s == 0) {
hsv.h = 0;
return hsv;
}
if(rgbMax == r)
hsv.h = 0 + 43 * (g - b) / (rgbMax - rgbMin);
else if(rgbMax == g)
hsv.h = 85 + 43 * (b - r) / (rgbMax - rgbMin);
else
hsv.h = 171 + 43 * (r - g) / (rgbMax - rgbMin);
return hsv;
}

456
src/lv_misc/lv_color.h Normal file
View File

@@ -0,0 +1,456 @@
/**
* @file lv_color.h
*
*/
#ifndef LV_COLOR_H
#define LV_COLOR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
/*Error checking*/
#if LV_COLOR_DEPTH == 24
#error "LV_COLOR_DEPTH 24 is deprecated. Use LV_COLOR_DEPTH 32 instead (lv_conf.h)"
#endif
#if LV_COLOR_DEPTH != 32 && LV_COLOR_SCREEN_TRANSP != 0
#error "LV_COLOR_SCREEN_TRANSP requires LV_COLOR_DEPTH == 32. Set it in lv_conf.h"
#endif
#if LV_COLOR_DEPTH != 16 && LV_COLOR_16_SWAP != 0
#error "LV_COLOR_16_SWAP requires LV_COLOR_DEPTH == 16. Set it in lv_conf.h"
#endif
#include <stdint.h>
/*********************
* DEFINES
*********************/
#define LV_COLOR_WHITE LV_COLOR_MAKE(0xFF,0xFF,0xFF)
#define LV_COLOR_SILVER LV_COLOR_MAKE(0xC0,0xC0,0xC0)
#define LV_COLOR_GRAY LV_COLOR_MAKE(0x80,0x80,0x80)
#define LV_COLOR_BLACK LV_COLOR_MAKE(0x00,0x00,0x00)
#define LV_COLOR_RED LV_COLOR_MAKE(0xFF,0x00,0x00)
#define LV_COLOR_MAROON LV_COLOR_MAKE(0x80,0x00,0x00)
#define LV_COLOR_YELLOW LV_COLOR_MAKE(0xFF,0xFF,0x00)
#define LV_COLOR_OLIVE LV_COLOR_MAKE(0x80,0x80,0x00)
#define LV_COLOR_LIME LV_COLOR_MAKE(0x00,0xFF,0x00)
#define LV_COLOR_GREEN LV_COLOR_MAKE(0x00,0x80,0x00)
#define LV_COLOR_CYAN LV_COLOR_MAKE(0x00,0xFF,0xFF)
#define LV_COLOR_AQUA LV_COLOR_CYAN
#define LV_COLOR_TEAL LV_COLOR_MAKE(0x00,0x80,0x80)
#define LV_COLOR_BLUE LV_COLOR_MAKE(0x00,0x00,0xFF)
#define LV_COLOR_NAVY LV_COLOR_MAKE(0x00,0x00,0x80)
#define LV_COLOR_MAGENTA LV_COLOR_MAKE(0xFF,0x00,0xFF)
#define LV_COLOR_PURPLE LV_COLOR_MAKE(0x80,0x00,0x80)
#define LV_COLOR_ORANGE LV_COLOR_MAKE(0xFF,0xA5,0x00)
enum {
LV_OPA_TRANSP = 0,
LV_OPA_0 = 0,
LV_OPA_10 = 25,
LV_OPA_20 = 51,
LV_OPA_30 = 76,
LV_OPA_40 = 102,
LV_OPA_50 = 127,
LV_OPA_60 = 153,
LV_OPA_70 = 178,
LV_OPA_80 = 204,
LV_OPA_90 = 229,
LV_OPA_100 = 255,
LV_OPA_COVER = 255,
};
#define LV_OPA_MIN 16 /*Opacities below this will be transparent*/
#define LV_OPA_MAX 251 /*Opacities above this will fully cover*/
#if LV_COLOR_DEPTH == 1
#define LV_COLOR_SIZE 8
#elif LV_COLOR_DEPTH == 8
#define LV_COLOR_SIZE 8
#elif LV_COLOR_DEPTH == 16
#define LV_COLOR_SIZE 16
#elif LV_COLOR_DEPTH == 32
#define LV_COLOR_SIZE 32
#else
#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!"
#endif
/**********************
* TYPEDEFS
**********************/
typedef union
{
uint8_t blue :1;
uint8_t green :1;
uint8_t red :1;
uint8_t full :1;
} lv_color1_t;
typedef union
{
struct
{
uint8_t blue :2;
uint8_t green :3;
uint8_t red :3;
}ch;
uint8_t full;
} lv_color8_t;
typedef union
{
struct
{
#if LV_COLOR_16_SWAP == 0
uint16_t blue :5;
uint16_t green :6;
uint16_t red :5;
#else
uint16_t green_h :3;
uint16_t red :5;
uint16_t blue :5;
uint16_t green_l :3;
#endif
}ch;
uint16_t full;
} lv_color16_t;
typedef union
{
struct
{
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
}ch;
uint32_t full;
} lv_color32_t;
#if LV_COLOR_DEPTH == 1
typedef uint8_t lv_color_int_t;
typedef lv_color1_t lv_color_t;
#elif LV_COLOR_DEPTH == 8
typedef uint8_t lv_color_int_t;
typedef lv_color8_t lv_color_t;
#elif LV_COLOR_DEPTH == 16
typedef uint16_t lv_color_int_t;
typedef lv_color16_t lv_color_t;
#elif LV_COLOR_DEPTH == 32
typedef uint32_t lv_color_int_t;
typedef lv_color32_t lv_color_t;
#else
#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!"
#endif
typedef uint8_t lv_opa_t;
typedef struct
{
uint16_t h;
uint8_t s;
uint8_t v;
} lv_color_hsv_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/*In color conversations:
* - When converting to bigger color type the LSB weight of 1 LSB is calculated
* E.g. 16 bit Red has 5 bits
* 8 bit Red has 2 bits
* ----------------------
* 8 bit red LSB = (2^5 - 1) / (2^2 - 1) = 31 / 3 = 10
*
* - When calculating to smaller color type simply shift out the LSBs
* E.g. 8 bit Red has 2 bits
* 16 bit Red has 5 bits
* ----------------------
* Shift right with 5 - 3 = 2
*/
static inline uint8_t lv_color_to1(lv_color_t color)
{
#if LV_COLOR_DEPTH == 1
return color.full;
#elif LV_COLOR_DEPTH == 8
if((color.ch.red & 0x4) ||
(color.ch.green & 0x4) ||
(color.ch.blue & 0x2)) {
return 1;
} else {
return 0;
}
#elif LV_COLOR_DEPTH == 16
# if LV_COLOR_16_SWAP == 0
if((color.ch.red & 0x10) ||
(color.ch.green & 0x20) ||
(color.ch.blue & 0x10)) {
return 1;
# else
if((color.ch.red & 0x10) ||
(color.ch.green_h & 0x20) ||
(color.ch.blue & 0x10)) {
return 1;
# endif
} else {
return 0;
}
#elif LV_COLOR_DEPTH == 32
if((color.ch.red & 0x80) ||
(color.ch.green & 0x80) ||
(color.ch.blue & 0x80)) {
return 1;
} else {
return 0;
}
#endif
}
static inline uint8_t lv_color_to8(lv_color_t color)
{
#if LV_COLOR_DEPTH == 1
if(color.full == 0) return 0;
else return 0xFF;
#elif LV_COLOR_DEPTH == 8
return color.full;
#elif LV_COLOR_DEPTH == 16
# if LV_COLOR_16_SWAP == 0
lv_color8_t ret;
ret.ch.red = color.ch.red >> 2; /* 5 - 3 = 2*/
ret.ch.green = color.ch.green >> 3; /* 6 - 3 = 3*/
ret.ch.blue = color.ch.blue >> 3; /* 5 - 2 = 3*/
return ret.full;
# else
lv_color8_t ret;
ret.ch.red = color.ch.red >> 2; /* 5 - 3 = 2*/
ret.ch.green = color.ch.green_h; /* 6 - 3 = 3*/
ret.ch.blue = color.ch.blue >> 3; /* 5 - 2 = 3*/
return ret.full;
# endif
#elif LV_COLOR_DEPTH == 32
lv_color8_t ret;
ret.ch.red = color.ch.red >> 5; /* 8 - 3 = 5*/
ret.ch.green = color.ch.green >> 5; /* 8 - 3 = 5*/
ret.ch.blue = color.ch.blue >> 6; /* 8 - 2 = 6*/
return ret.full;
#endif
}
static inline uint16_t lv_color_to16(lv_color_t color)
{
#if LV_COLOR_DEPTH == 1
if(color.full == 0) return 0;
else return 0xFFFF;
#elif LV_COLOR_DEPTH == 8
lv_color16_t ret;
# if LV_COLOR_16_SWAP == 0
ret.ch.red = color.ch.red * 4; /*(2^5 - 1)/(2^3 - 1) = 31/7 = 4*/
ret.ch.green = color.ch.green * 9; /*(2^6 - 1)/(2^3 - 1) = 63/7 = 9*/
ret.ch.blue = color.ch.blue * 10; /*(2^5 - 1)/(2^2 - 1) = 31/3 = 10*/
# else
ret.red = color.ch.red * 4;
uint8_t g_tmp = color.ch.green * 9;
ret.ch.green_h = (g_tmp & 0x1F) >> 3;
ret.ch.green_l = g_tmp & 0x07;
ret.ch.blue = color.ch.blue * 10;
# endif
return ret.full;
#elif LV_COLOR_DEPTH == 16
return color.full;
#elif LV_COLOR_DEPTH == 32
lv_color16_t ret;
# if LV_COLOR_16_SWAP == 0
ret.ch.red = color.ch.red >> 3; /* 8 - 5 = 3*/
ret.ch.green = color.ch.green >> 2; /* 8 - 6 = 2*/
ret.ch.blue = color.ch.blue >> 3; /* 8 - 5 = 3*/
# else
ret.ch.red = color.ch.red >> 3;
ret.ch.green_h = (color.ch.green & 0xE0) >> 5;
ret.ch.green_l = (color.ch.green & 0x1C) >> 2;
ret.ch.blue = color.ch.blue >> 3;
# endif
return ret.full;
#endif
}
static inline uint32_t lv_color_to32(lv_color_t color)
{
#if LV_COLOR_DEPTH == 1
if(color.full == 0) return 0;
else return 0xFFFFFFFF;
#elif LV_COLOR_DEPTH == 8
lv_color32_t ret;
ret.ch.red = color.ch.red * 36; /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/
ret.ch.green = color.ch.green * 36; /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/
ret.ch.blue = color.ch.blue * 85; /*(2^8 - 1)/(2^2 - 1) = 255/3 = 85*/
ret.ch.alpha = 0xFF;
return ret.full;
#elif LV_COLOR_DEPTH == 16
# if LV_COLOR_16_SWAP == 0
lv_color32_t ret;
ret.ch.red = color.ch.red * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/
ret.ch.green = color.ch.green * 4; /*(2^8 - 1)/(2^6 - 1) = 255/63 = 4*/
ret.ch.blue = color.ch.blue * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/
ret.ch.alpha = 0xFF;
return ret.full;
# else
lv_color32_t ret;
ret.ch.red = color.ch.red * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/
ret.ch.green = ((color.ch.green_h << 3) + color.ch.green_l) * 4; /*(2^8 - 1)/(2^6 - 1) = 255/63 = 4*/
ret.ch.blue = color.ch.blue * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/
ret.ch.alpha = 0xFF;
return ret.full;
# endif
#elif LV_COLOR_DEPTH == 32
return color.full;
#endif
}
static inline lv_color_t lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix)
{
lv_color_t ret;
#if LV_COLOR_DEPTH != 1
/*LV_COLOR_DEPTH == 8, 16 or 32*/
ret.ch.red = (uint16_t)((uint16_t) c1.ch.red * mix + (c2.ch.red * (255 - mix))) >> 8;
# if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
/*If swapped Green is in 2 parts*/
uint16_t g_1 = (c1.ch.green_h << 3) + c1.ch.green_l;
uint16_t g_2 = (c2.ch.green_h << 3) + c2.ch.green_l;
uint16_t g_out = (uint16_t)((uint16_t) g_1 * mix + (g_2 * (255 - mix))) >> 8;
ret.ch.green_h = g_out >> 3;
ret.ch.green_l = g_out & 0x7;
# else
ret.ch.green = (uint16_t)((uint16_t) c1.ch.green * mix + (c2.ch.green * (255 - mix))) >> 8;
# endif
ret.ch.blue = (uint16_t)((uint16_t) c1.ch.blue * mix + (c2.ch.blue * (255 - mix))) >> 8;
# if LV_COLOR_DEPTH == 32
ret.ch.alpha = 0xFF;
# endif
#else
/*LV_COLOR_DEPTH == 1*/
ret.full = mix > LV_OPA_50 ? c1.full : c2.full;
#endif
return ret;
}
/**
* Get the brightness of a color
* @param color a color
* @return the brightness [0..255]
*/
static inline uint8_t lv_color_brightness(lv_color_t color)
{
lv_color32_t c32;
c32.full = lv_color_to32(color);
uint16_t bright = 3 * c32.ch.red + c32.ch.blue + 4 * c32.ch.green;
return (uint16_t) bright >> 3;
}
/* The most simple macro to create a color from R,G and B values */
#if LV_COLOR_DEPTH == 1
#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){(b8 >> 7 | g8 >> 7 | r8 >> 7)})
static inline lv_color_t lv_color_make(int r8, int g8, int b8){
lv_color_t color;
color.full = (b8 >> 7 | g8 >> 7 | r8 >> 7);
return color;
}
#elif LV_COLOR_DEPTH == 8
#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8 >> 6, g8 >> 5, r8 >> 5}})
static inline lv_color_t lv_color_make(uint8_t r8, int g8, int b8){
lv_color_t color;
color.ch.blue = b8 >> 6;
color.ch.green = g8 >> 5;
color.ch.red = r8 >> 5;
return color;
}
#elif LV_COLOR_DEPTH == 16
# if LV_COLOR_16_SWAP == 0
# define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8 >> 3, g8 >> 2, r8 >> 3}})
static inline lv_color_t lv_color_make(uint8_t r8, uint8_t g8, uint8_t b8){
lv_color_t color;
color.ch.blue = (uint16_t)(b8 >> 3);
color.ch.green = (uint16_t)(g8 >> 2);
color.ch.red = (uint16_t)(r8 >> 3);
return color;
}
# else
# define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{g8 >> 5, r8 >> 3, b8 >> 3, (g8 >> 2) & 0x7}})
static inline lv_color_t lv_color_make(uint8_t r8, uint8_t g8, uint8_t b8){
lv_color_t color;
color.ch.green_h = (uint16_t)(g8 >> 5);
color.ch.red = (uint16_t)(r8 >> 3);
color.ch.blue = (uint16_t)(b8 >> 3);
color.ch.green_l = (uint16_t)((g8 >> 2) & 0x7);
return color;
}
# endif
#elif LV_COLOR_DEPTH == 32
#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8, g8, r8, 0xff}}) /*Fix 0xff alpha*/
static inline lv_color_t lv_color_make(uint8_t r8, uint8_t g8, uint8_t b8){
lv_color_t color;
color.ch.blue = b8;
color.ch.green = g8;
color.ch.red = r8;
color.ch.alpha = 0xff;
return color;
}
#endif
static inline lv_color_t lv_color_hex(uint32_t c) {
return lv_color_make((uint8_t) ((c >> 16) & 0xFF),
(uint8_t) ((c >> 8) & 0xFF),
(uint8_t) (c & 0xFF));
}
static inline lv_color_t lv_color_hex3(uint32_t c) {
return lv_color_make((uint8_t) (((c >> 4) & 0xF0) | ((c >> 8) & 0xF)),
(uint8_t) ((c & 0xF0) | ((c & 0xF0) >> 4)),
(uint8_t) ((c & 0xF) | ((c & 0xF) << 4)));
}
/**
* Convert a HSV color to RGB
* @param h hue [0..359]
* @param s saturation [0..100]
* @param v value [0..100]
* @return the given RGB color in RGB (with LV_COLOR_DEPTH depth)
*/
lv_color_t lv_color_hsv_to_rgb(uint16_t h, uint8_t s, uint8_t v);
/**
* Convert an RGB color to HSV
* @param r red
* @param g green
* @param b blue
* @return the given RGB color n HSV
*/
lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*USE_COLOR*/

269
src/lv_misc/lv_font.c Normal file
View File

@@ -0,0 +1,269 @@
/**
* @file lv_font.c
*
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_font.h"
#include "lv_log.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the fonts
*/
void lv_font_init(void)
{
lv_font_builtin_init();
}
/**
* Add a font to an other to extend the character set.
* @param child the font to add
* @param parent this font will be extended. Using it later will contain the characters from `child`
*/
void lv_font_add(lv_font_t * child, lv_font_t * parent)
{
if(parent == NULL) return;
while(parent->next_page != NULL) {
parent = parent->next_page; /*Got to the last page and add the new font there*/
}
parent->next_page = child;
}
/**
* Remove a font from a character set.
* @param child the font to remove
* @param parent remove `child` from here
*/
void lv_font_remove(lv_font_t * child, lv_font_t * parent)
{
if(parent == NULL) return;
if(child == NULL) return;
while(parent->next_page != child) {
parent = parent->next_page; /*Got to the last page and add the new font there*/
}
parent->next_page = child->next_page;
}
/**
* Tells if font which contains `letter` is monospace or not
* @param font_p point to font
* @param letter an UNICODE character code
* @return true: the letter is monospace; false not monospace
*/
bool lv_font_is_monospace(const lv_font_t * font_p, uint32_t letter)
{
const lv_font_t * font_i = font_p;
int16_t w;
while(font_i != NULL) {
w = font_i->get_width(font_i, letter);
if(w >= 0) {
/*Glyph found*/
if(font_i->monospace) return true;
return false;
}
font_i = font_i->next_page;
}
return 0;
}
/**
* Return with the bitmap of a font.
* @param font_p pointer to a font
* @param letter an UNICODE character code
* @return pointer to the bitmap of the letter
*/
const uint8_t * lv_font_get_bitmap(const lv_font_t * font_p, uint32_t letter)
{
const lv_font_t * font_i = font_p;
while(font_i != NULL) {
const uint8_t * bitmap = font_i->get_bitmap(font_i, letter);
if(bitmap) return bitmap;
font_i = font_i->next_page;
}
return NULL;
}
/**
* Get the width of a letter in a font. If `monospace` is set then return with it.
* @param font_p pointer to a font
* @param letter an UNICODE character code
* @return the width of a letter
*/
uint8_t lv_font_get_width(const lv_font_t * font_p, uint32_t letter)
{
const lv_font_t * font_i = font_p;
int16_t w;
while(font_i != NULL) {
w = font_i->get_width(font_i, letter);
if(w >= 0) {
/*Glyph found*/
uint8_t m = font_i->monospace;
if(m) w = m;
return w;
}
font_i = font_i->next_page;
}
return 0;
}
/**
* Get the width of the letter without overwriting it with the `monospace` attribute
* @param font_p pointer to a font
* @param letter an UNICODE character code
* @return the width of a letter
*/
uint8_t lv_font_get_real_width(const lv_font_t * font_p, uint32_t letter)
{
const lv_font_t * font_i = font_p;
int16_t w;
while(font_i != NULL) {
w = font_i->get_width(font_i, letter);
if(w >= 0) return w;
font_i = font_i->next_page;
}
return 0;
}
/**
* Get the bit-per-pixel of font
* @param font pointer to font
* @param letter a letter from font (font extensions can have different bpp)
* @return bpp of the font (or font extension)
*/
uint8_t lv_font_get_bpp(const lv_font_t * font, uint32_t letter)
{
const lv_font_t * font_i = font;
while(font_i != NULL) {
if(letter >= font_i->unicode_first && letter <= font_i->unicode_last) {
return font_i->bpp;
}
font_i = font_i->next_page;
}
return 0;
}
/**
* Generic bitmap get function used in 'font->get_bitmap' when the font contains all characters in the range
* @param font pointer to font
* @param unicode_letter an unicode letter which bitmap should be get
* @return pointer to the bitmap or NULL if not found
*/
const uint8_t * lv_font_get_bitmap_continuous(const lv_font_t * font, uint32_t unicode_letter)
{
/*Check the range*/
if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return NULL;
uint32_t index = (unicode_letter - font->unicode_first);
return &font->glyph_bitmap[font->glyph_dsc[index].glyph_index];
}
/**
* Generic bitmap get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse)
* @param font pointer to font
* @param unicode_letter an unicode letter which bitmap should be get
* @return pointer to the bitmap or NULL if not found
*/
const uint8_t * lv_font_get_bitmap_sparse(const lv_font_t * font, uint32_t unicode_letter)
{
/*Check the range*/
if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return NULL;
uint32_t i;
for(i = 0; font->unicode_list[i] != 0; i++) {
if(font->unicode_list[i] == unicode_letter) {
return &font->glyph_bitmap[font->glyph_dsc[i].glyph_index];
}
}
return NULL;
}
/**
* Generic glyph width get function used in 'font->get_width' when the font contains all characters in the range
* @param font pointer to font
* @param unicode_letter an unicode letter which width should be get
* @return width of the gylph or -1 if not found
*/
int16_t lv_font_get_width_continuous(const lv_font_t * font, uint32_t unicode_letter)
{
/*Check the range*/
if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) {
return -1;
}
uint32_t index = (unicode_letter - font->unicode_first);
return font->glyph_dsc[index].w_px;
}
/**
* Generic glyph width get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse)
* @param font pointer to font
* @param unicode_letter an unicode letter which width should be get
* @return width of the glyph or -1 if not found
*/
int16_t lv_font_get_width_sparse(const lv_font_t * font, uint32_t unicode_letter)
{
/*Check the range*/
if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return -1;
uint32_t i;
for(i = 0; font->unicode_list[i] != 0; i++) {
if(font->unicode_list[i] == unicode_letter) {
return font->glyph_dsc[i].w_px;
}
}
return -1;
}
/**********************
* STATIC FUNCTIONS
**********************/

192
src/lv_misc/lv_font.h Normal file
View File

@@ -0,0 +1,192 @@
/**
* @file lv_font.h
*
*/
#ifndef LV_FONT_H
#define LV_FONT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "lv_symbol_def.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct
{
uint32_t w_px :8;
uint32_t glyph_index :24;
} lv_font_glyph_dsc_t;
typedef struct
{
uint32_t unicode :21;
uint32_t glyph_dsc_index :11;
} lv_font_unicode_map_t;
typedef struct _lv_font_struct
{
uint32_t unicode_first;
uint32_t unicode_last;
const uint8_t * glyph_bitmap;
const lv_font_glyph_dsc_t * glyph_dsc;
const uint32_t * unicode_list;
const uint8_t * (*get_bitmap)(const struct _lv_font_struct *,uint32_t); /*Get a glyph's bitmap from a font*/
int16_t (*get_width)(const struct _lv_font_struct *,uint32_t); /*Get a glyph's with with a given font*/
struct _lv_font_struct * next_page; /*Pointer to a font extension*/
uint32_t h_px :8;
uint32_t bpp :4; /*Bit per pixel: 1, 2 or 4*/
uint32_t monospace :8; /*Fix width (0: normal width)*/
uint16_t glyph_cnt; /*Number of glyphs (letters) in the font*/
} lv_font_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the fonts
*/
void lv_font_init(void);
/**
* Add a font to an other to extend the character set.
* @param child the font to add
* @param parent this font will be extended. Using it later will contain the characters from `child`
*/
void lv_font_add(lv_font_t *child, lv_font_t *parent);
/**
* Remove a font from a character set.
* @param child the font to remove
* @param parent remove `child` from here
*/
void lv_font_remove(lv_font_t * child, lv_font_t * parent);
/**
* Tells if font which contains `letter` is monospace or not
* @param font_p point to font
* @param letter an UNICODE character code
* @return true: the letter is monospace; false not monospace
*/
bool lv_font_is_monospace(const lv_font_t * font_p, uint32_t letter);
/**
* Return with the bitmap of a font.
* @param font_p pointer to a font
* @param letter an UNICODE character code
* @return pointer to the bitmap of the letter
*/
const uint8_t * lv_font_get_bitmap(const lv_font_t * font_p, uint32_t letter);
/**
* Get the width of a letter in a font. If `monospace` is set then return with it.
* @param font_p pointer to a font
* @param letter an UNICODE character code
* @return the width of a letter
*/
uint8_t lv_font_get_width(const lv_font_t * font_p, uint32_t letter);
/**
* Get the width of the letter without overwriting it with the `monospace` attribute
* @param font_p pointer to a font
* @param letter an UNICODE character code
* @return the width of a letter
*/
uint8_t lv_font_get_real_width(const lv_font_t * font_p, uint32_t letter);
/**
* Get the height of a font
* @param font_p pointer to a font
* @return the height of a font
*/
static inline uint8_t lv_font_get_height(const lv_font_t * font_p)
{
return font_p->h_px;
}
/**
* Get the bit-per-pixel of font
* @param font pointer to font
* @param letter a letter from font (font extensions can have different bpp)
* @return bpp of the font (or font extension)
*/
uint8_t lv_font_get_bpp(const lv_font_t * font, uint32_t letter);
/**
* Generic bitmap get function used in 'font->get_bitmap' when the font contains all characters in the range
* @param font pointer to font
* @param unicode_letter an unicode letter which bitmap should be get
* @return pointer to the bitmap or NULL if not found
*/
const uint8_t * lv_font_get_bitmap_continuous(const lv_font_t * font, uint32_t unicode_letter);
/**
* Generic bitmap get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse)
* @param font pointer to font
* @param unicode_letter an unicode letter which bitmap should be get
* @return pointer to the bitmap or NULL if not found
*/
const uint8_t * lv_font_get_bitmap_sparse(const lv_font_t * font, uint32_t unicode_letter);
/**
* Generic glyph width get function used in 'font->get_width' when the font contains all characters in the range
* @param font pointer to font
* @param unicode_letter an unicode letter which width should be get
* @return width of the gylph or -1 if not found
*/
int16_t lv_font_get_width_continuous(const lv_font_t * font, uint32_t unicode_letter);
/**
* Generic glyph width get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse)
* @param font pointer to font
* @param unicode_letter an unicode letter which width should be get
* @return width of the glyph or -1 if not found
*/
int16_t lv_font_get_width_sparse(const lv_font_t * font, uint32_t unicode_letter);
/**********************
* MACROS
**********************/
#define LV_FONT_DECLARE(font_name) extern lv_font_t font_name;
/**********************
* ADD BUILT IN FONTS
**********************/
#include "../lv_fonts/lv_font_builtin.h"
/*Declare the custom (user defined) fonts*/
#ifdef LV_FONT_CUSTOM_DECLARE
LV_FONT_CUSTOM_DECLARE
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*USE_FONT*/

627
src/lv_misc/lv_fs.c Normal file
View File

@@ -0,0 +1,627 @@
/**
* @file lv_fs.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fs.h"
#if LV_USE_FILESYSTEM
#include "lv_ll.h"
#include <string.h>
#include "lv_gc.h"
#if defined(LV_GC_INCLUDE)
# include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
/*********************
* DEFINES
*********************/
/* "free" is used as a function pointer (in lv_fs_drv_t).
* We must make sure "free" was not defined to a platform specific
* free function, otherwise compilation would fail.
*/
#ifdef free
#undef free
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static const char * lv_fs_get_real_path(const char * path);
static lv_fs_drv_t * lv_fs_get_drv(char letter);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize the File system interface
*/
void lv_fs_init(void)
{
lv_ll_init(&LV_GC_ROOT(_lv_drv_ll), sizeof(lv_fs_drv_t));
}
/**
* Test if a drive is rady or not. If the `ready` function was not initialized `true` will be returned.
* @param letter letter of the drive
* @return true: drive is ready; false: drive is not ready
*/
bool lv_fs_is_ready(char letter)
{
lv_fs_drv_t * drv = lv_fs_get_drv(letter);
if(drv == NULL) return false; /*An unknown driver in not ready*/
if(drv->ready == NULL) return true; /*Assume the driver is always ready if no handler provided*/
return drv->ready();
}
/**
* Open a file
* @param file_p pointer to a lv_fs_file_t variable
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode)
{
file_p->drv = NULL;
file_p->file_d = NULL;
if(path == NULL) return LV_FS_RES_INV_PARAM;
char letter = path[0];
file_p->drv = lv_fs_get_drv(letter);
if(file_p->drv == NULL) {
file_p->file_d = NULL;
return LV_FS_RES_NOT_EX;
}
if(file_p->drv->ready != NULL) {
if(file_p->drv->ready() == false) {
file_p->drv = NULL;
file_p->file_d = NULL;
return LV_FS_RES_HW_ERR;
}
}
file_p->file_d = lv_mem_alloc(file_p->drv->file_size);
lv_mem_assert(file_p->file_d);
if(file_p->file_d == NULL) {
file_p->drv = NULL;
return LV_FS_RES_OUT_OF_MEM; /* Out of memory */
}
if(file_p->drv->open == NULL) {
return LV_FS_RES_NOT_IMP;
}
const char * real_path = lv_fs_get_real_path(path);
lv_fs_res_t res = file_p->drv->open(file_p->file_d, real_path, mode);
if(res != LV_FS_RES_OK) {
lv_mem_free(file_p->file_d);
file_p->file_d = NULL;
file_p->drv = NULL;
}
return res;
}
/**
* Close an already opened file
* @param file_p pointer to a lv_fs_file_t variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p)
{
if(file_p->drv == NULL) {
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->close == NULL) {
return LV_FS_RES_NOT_IMP;
}
lv_fs_res_t res = file_p->drv->close(file_p->file_d);
lv_mem_free(file_p->file_d); /*Clean up*/
file_p->file_d = NULL;
file_p->drv = NULL;
file_p->file_d = NULL;
return res;
}
/**
* Delete a file
* @param path path of the file to delete
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_remove(const char * path)
{
if(path == NULL) return LV_FS_RES_INV_PARAM;
lv_fs_drv_t * drv = NULL;
char letter = path[0];
drv = lv_fs_get_drv(letter);
if(drv == NULL) return LV_FS_RES_NOT_EX;
if(drv->ready != NULL) {
if(drv->ready() == false) return LV_FS_RES_HW_ERR;
}
if(drv->remove == NULL) return LV_FS_RES_NOT_IMP;
const char * real_path = lv_fs_get_real_path(path);
lv_fs_res_t res = drv->remove(real_path);
return res;
}
/**
* Read from a file
* @param file_p pointer to a lv_fs_file_t variable
* @param buf pointer to a buffer where the read bytes are stored
* @param btr Bytes To Read
* @param br the number of real read bytes (Bytes Read). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
{
if(br != NULL) *br = 0;
if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM;
if(file_p->drv->read == NULL) return LV_FS_RES_NOT_IMP;
uint32_t br_tmp = 0;
lv_fs_res_t res = file_p->drv->read(file_p->file_d, buf, btr, &br_tmp);
if(br != NULL) *br = br_tmp;
return res;
}
/**
* Write into a file
* @param file_p pointer to a lv_fs_file_t variable
* @param buf pointer to a buffer with the bytes to write
* @param btr Bytes To Write
* @param br the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
if(bw != NULL) *bw = 0;
if(file_p->drv == NULL) {
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->write == NULL) {
return LV_FS_RES_NOT_IMP;
}
uint32_t bw_tmp = 0;
lv_fs_res_t res = file_p->drv->write(file_p->file_d, buf, btw, &bw_tmp);
if(bw != NULL) *bw = bw_tmp;
return res;
}
/**
* Set the position of the 'cursor' (read write pointer) in a file
* @param file_p pointer to a lv_fs_file_t variable
* @param pos the new position expressed in bytes index (0: start of file)
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos)
{
if(file_p->drv == NULL) {
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->seek == NULL) {
return LV_FS_RES_NOT_IMP;
}
lv_fs_res_t res = file_p->drv->seek(file_p->file_d, pos);
return res;
}
/**
* Give the position of the read write pointer
* @param file_p pointer to a lv_fs_file_t variable
* @param pos_p pointer to store the position of the read write pointer
* @return LV_FS_RES_OK or any error from 'fs_res_t'
*/
lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos)
{
if(file_p->drv == NULL) {
pos = 0;
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->tell == NULL) {
pos = 0;
return LV_FS_RES_NOT_IMP;
}
lv_fs_res_t res = file_p->drv->tell(file_p->file_d, pos);
return res;
}
/**
* Truncate the file size to the current position of the read write pointer
* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open )
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_trunc(lv_fs_file_t * file_p)
{
if(file_p->drv == NULL) {
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->tell == NULL) {
return LV_FS_RES_NOT_IMP;
}
lv_fs_res_t res = file_p->drv->trunc(file_p->file_d);
return res;
}
/**
* Give the size of a file bytes
* @param file_p pointer to a lv_fs_file_t variable
* @param size pointer to a variable to store the size
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_size(lv_fs_file_t * file_p, uint32_t * size)
{
if(file_p->drv == NULL) {
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->size == NULL) return LV_FS_RES_NOT_IMP;
if(size == NULL) return LV_FS_RES_INV_PARAM;
lv_fs_res_t res = file_p->drv->size(file_p->file_d, size);
return res;
}
/**
* Rename a file
* @param oldname path to the file
* @param newname path with the new name
* @return LV_FS_RES_OK or any error from 'fs_res_t'
*/
lv_fs_res_t lv_fs_rename(const char * oldname, const char * newname)
{
if(!oldname || !newname) return LV_FS_RES_INV_PARAM;
char letter = oldname[0];
lv_fs_drv_t * drv = lv_fs_get_drv(letter);
if(!drv) {
return LV_FS_RES_NOT_EX;
}
if(drv->ready != NULL) {
if(drv->ready() == false) {
return LV_FS_RES_HW_ERR;
}
}
if(drv->rename == NULL) return LV_FS_RES_NOT_IMP;
const char * old_real = lv_fs_get_real_path(oldname);
const char * new_real = lv_fs_get_real_path(newname);
lv_fs_res_t res = drv->rename(old_real, new_real);
return res;
}
/**
* Initialize a 'fs_read_dir_t' variable for directory reading
* @param rddir_p pointer to a 'fs_read_dir_t' variable
* @param path path to a directory
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path)
{
if(path == NULL) return LV_FS_RES_INV_PARAM;
char letter = path[0];
rddir_p->drv = lv_fs_get_drv(letter);
if(rddir_p->drv == NULL) {
rddir_p->dir_d = NULL;
return LV_FS_RES_NOT_EX;
}
rddir_p->dir_d = lv_mem_alloc(rddir_p->drv->rddir_size);
lv_mem_assert(rddir_p->dir_d);
if(rddir_p->dir_d == NULL) {
rddir_p->dir_d = NULL;
return LV_FS_RES_OUT_OF_MEM; /* Out of memory */
}
if(rddir_p->drv->dir_open == NULL) {
return LV_FS_RES_NOT_IMP;
}
const char * real_path = lv_fs_get_real_path(path);
lv_fs_res_t res = rddir_p->drv->dir_open(rddir_p->dir_d, real_path);
return res;
}
/**
* Read the next filename form a directory.
* The name of the directories will begin with '/'
* @param rddir_p pointer to an initialized 'fs_read_dir_t' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn)
{
if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
fn[0] = '\0';
return LV_FS_RES_INV_PARAM;
}
if(rddir_p->drv->dir_read == NULL) {
return LV_FS_RES_NOT_IMP;
}
lv_fs_res_t res = rddir_p->drv->dir_read(rddir_p->dir_d, fn);
return res;
}
/**
* Close the directory reading
* @param rddir_p pointer to an initialized 'fs_read_dir_t' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p)
{
if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
return LV_FS_RES_INV_PARAM;
}
lv_fs_res_t res;
if(rddir_p->drv->dir_close == NULL) {
res = LV_FS_RES_NOT_IMP;
} else {
res = rddir_p->drv->dir_close(rddir_p->dir_d);
}
lv_mem_free(rddir_p->dir_d); /*Clean up*/
rddir_p->dir_d = NULL;
rddir_p->drv = NULL;
rddir_p->dir_d = NULL;
return res;
}
/**
* Get the free and total size of a driver in kB
* @param letter the driver letter
* @param total_p pointer to store the total size [kB]
* @param free_p pointer to store the free size [kB]
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_free_space(char letter, uint32_t * total_p, uint32_t * free_p)
{
lv_fs_drv_t * drv = lv_fs_get_drv(letter);
if(drv == NULL) {
return LV_FS_RES_INV_PARAM;
}
lv_fs_res_t res;
if(drv->free_space == NULL) {
res = LV_FS_RES_NOT_IMP;
} else {
uint32_t total_tmp = 0;
uint32_t free_tmp = 0;
res = drv->free_space(&total_tmp, &free_tmp);
if(total_p != NULL) *total_p = total_tmp;
if(free_p != NULL) *free_p = free_tmp;
}
return res;
}
/**
* Add a new drive
* @param drv_p pointer to an lv_fs_drv_t structure which is inited with the
* corresponding function pointers. The data will be copied so the variable can be local.
*/
void lv_fs_add_drv(lv_fs_drv_t * drv_p)
{
/*Save the new driver*/
lv_fs_drv_t * new_drv;
new_drv = lv_ll_ins_head(&LV_GC_ROOT(_lv_drv_ll));
lv_mem_assert(new_drv);
if(new_drv == NULL) return;
memcpy(new_drv, drv_p, sizeof(lv_fs_drv_t));
}
/**
* Fill a buffer with the letters of existing drivers
* @param buf buffer to store the letters ('\0' added after the last letter)
* @return the buffer
*/
char * lv_fs_get_letters(char * buf)
{
lv_fs_drv_t * drv;
uint8_t i = 0;
LV_LL_READ(LV_GC_ROOT(_lv_drv_ll), drv) {
buf[i] = drv->letter;
i++;
}
buf[i] = '\0';
return buf;
}
/**
* Return with the extension of the filename
* @param fn string with a filename
* @return pointer to the beginning extension or empty string if no extension
*/
const char * lv_fs_get_ext(const char * fn)
{
uint16_t i;
for(i = strlen(fn); i > 0; i --) {
if(fn[i] == '.') {
return &fn[i + 1];
} else if(fn[i] == '/' || fn[i] == '\\') {
return ""; /*No extension if a '\' or '/' found*/
}
}
return ""; /*Empty string if no '.' in the file name. */
}
/**
* Step up one level
* @param path pointer to a file name
* @return the truncated file name
*/
char * lv_fs_up(char * path)
{
uint16_t len = strlen(path);
if(len == 0) return path;
len --; /*Go before the trailing '\0'*/
/*Ignore trailing '/' or '\'*/
while(path[len] == '/' || path[len] == '\\') {
path[len] = '\0';
if(len > 0) len --;
else return path;
}
uint16_t i;
for(i = len; i > 0; i --) {
if(path[i] == '/' || path[i] == '\\') break;
}
if(i > 0) path[i] = '\0';
return path;
}
/**
* Get the last element of a path (e.g. U:/folder/file -> file)
* @param path a character sting with the path to search in
* @return pointer to the beginning of the last element in the path
*/
const char * lv_fs_get_last(const char * path)
{
uint16_t len = strlen(path);
if(len == 0) return path;
len --; /*Go before the trailing '\0'*/
/*Ignore trailing '/' or '\'*/
while(path[len] == '/' || path[len] == '\\') {
if(len > 0) len --;
else return path;
}
uint16_t i;
for(i = len; i > 0; i --) {
if(path[i] == '/' || path[i] == '\\') break;
}
/*No '/' or '\' in the path so return with path itself*/
if(i == 0) return path;
return &path[i + 1];
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Leave the driver letters and / or \ letters from beginning of the path
* @param path path string (E.g. S:/folder/file.txt)
* @return pointer to the beginning of the real path (E.g. folder/file.txt)
*/
static const char * lv_fs_get_real_path(const char * path)
{
/* Example path: "S:/folder/file.txt"
* Leave the letter and the : / \ characters*/
path ++; /*Ignore the driver letter*/
while(*path != '\0') {
if(*path == ':' || *path == '\\' || *path == '/') {
path ++;
} else {
break;
}
}
return path;
}
/**
* Give a pointer to a driver from its letter
* @param letter the driver letter
* @return pointer to a driver or NULL if not found
*/
static lv_fs_drv_t * lv_fs_get_drv(char letter)
{
lv_fs_drv_t * drv;
LV_LL_READ(LV_GC_ROOT(_lv_drv_ll), drv) {
if(drv->letter == letter) {
return drv;
}
}
return NULL;
}
#endif /*LV_USE_FILESYSTEM*/

277
src/lv_misc/lv_fs.h Normal file
View File

@@ -0,0 +1,277 @@
/**
* @file lv_fs.h
*
*/
#ifndef LV_FS_H
#define LV_FS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_FILESYSTEM
#include <stdint.h>
#include <stdbool.h>
#include "lv_mem.h"
/*********************
* DEFINES
*********************/
#define LV_FS_MAX_FN_LENGTH 64
/**********************
* TYPEDEFS
**********************/
enum
{
LV_FS_RES_OK = 0,
LV_FS_RES_HW_ERR, /*Low level hardware error*/
LV_FS_RES_FS_ERR, /*Error in the file system structure */
LV_FS_RES_NOT_EX, /*Driver, file or directory is not exists*/
LV_FS_RES_FULL, /*Disk full*/
LV_FS_RES_LOCKED, /*The file is already opened*/
LV_FS_RES_DENIED, /*Access denied. Check 'fs_open' modes and write protect*/
LV_FS_RES_BUSY, /*The file system now can't handle it, try later*/
LV_FS_RES_TOUT, /*Process time outed*/
LV_FS_RES_NOT_IMP, /*Requested function is not implemented*/
LV_FS_RES_OUT_OF_MEM, /*Not enough memory for an internal operation*/
LV_FS_RES_INV_PARAM, /*Invalid parameter among arguments*/
LV_FS_RES_UNKNOWN, /*Other unknown error*/
};
typedef uint8_t lv_fs_res_t;
struct __lv_fs_drv_t;
typedef struct
{
void * file_d;
struct __lv_fs_drv_t* drv;
} lv_fs_file_t;
typedef struct
{
void * dir_d;
struct __lv_fs_drv_t * drv;
} lv_fs_dir_t;
enum
{
LV_FS_MODE_WR = 0x01,
LV_FS_MODE_RD = 0x02,
};
typedef uint8_t lv_fs_mode_t;
typedef struct __lv_fs_drv_t
{
char letter;
uint16_t file_size;
uint16_t rddir_size;
bool (*ready) (void);
lv_fs_res_t (*open) (void * file_p, const char * path, lv_fs_mode_t mode);
lv_fs_res_t (*close) (void * file_p);
lv_fs_res_t (*remove) (const char * fn);
lv_fs_res_t (*read) (void * file_p, void * buf, uint32_t btr, uint32_t * br);
lv_fs_res_t (*write) (void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
lv_fs_res_t (*seek) (void * file_p, uint32_t pos);
lv_fs_res_t (*tell) (void * file_p, uint32_t * pos_p);
lv_fs_res_t (*trunc) (void * file_p);
lv_fs_res_t (*size) (void * file_p, uint32_t * size_p);
lv_fs_res_t (*rename) (const char * oldname, const char * newname);
lv_fs_res_t (*free_space) (uint32_t * total_p, uint32_t * free_p);
lv_fs_res_t (*dir_open) (void * rddir_p, const char * path);
lv_fs_res_t (*dir_read) (void * rddir_p, char * fn);
lv_fs_res_t (*dir_close) (void * rddir_p);
} lv_fs_drv_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the File system interface
*/
void lv_fs_init(void);
/**
* Add a new drive
* @param drv_p pointer to an lv_fs_drv_t structure which is inited with the
* corresponding function pointers. The data will be copied so the variable can be local.
*/
void lv_fs_add_drv(lv_fs_drv_t * drv_p);
/**
* Test if a drive is rady or not. If the `ready` function was not initialized `true` will be returned.
* @param letter letter of the drive
* @return true: drive is ready; false: drive is not ready
*/
bool lv_fs_is_ready(char letter);
/**
* Open a file
* @param file_p pointer to a lv_fs_file_t variable
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_open (lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode);
/**
* Close an already opened file
* @param file_p pointer to a lv_fs_file_t variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_close (lv_fs_file_t * file_p);
/**
* Delete a file
* @param path path of the file to delete
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_remove (const char * path);
/**
* Read from a file
* @param file_p pointer to a lv_fs_file_t variable
* @param buf pointer to a buffer where the read bytes are stored
* @param btr Bytes To Read
* @param br the number of real read bytes (Bytes Read). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_read (lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br);
/**
* Write into a file
* @param file_p pointer to a lv_fs_file_t variable
* @param buf pointer to a buffer with the bytes to write
* @param btr Bytes To Write
* @param br the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_write (lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw);
/**
* Set the position of the 'cursor' (read write pointer) in a file
* @param file_p pointer to a lv_fs_file_t variable
* @param pos the new position expressed in bytes index (0: start of file)
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_seek (lv_fs_file_t * file_p, uint32_t pos);
/**
* Give the position of the read write pointer
* @param file_p pointer to a lv_fs_file_t variable
* @param pos_p pointer to store the position of the read write pointer
* @return LV_FS_RES_OK or any error from 'fs_res_t'
*/
lv_fs_res_t lv_fs_tell (lv_fs_file_t * file_p, uint32_t * pos);
/**
* Truncate the file size to the current position of the read write pointer
* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open )
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_trunc (lv_fs_file_t * file_p);
/**
* Give the size of a file bytes
* @param file_p pointer to a lv_fs_file_t variable
* @param size pointer to a variable to store the size
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_size (lv_fs_file_t * file_p, uint32_t * size);
/**
* Rename a file
* @param oldname path to the file
* @param newname path with the new name
* @return LV_FS_RES_OK or any error from 'fs_res_t'
*/
lv_fs_res_t lv_fs_rename (const char * oldname, const char * newname);
/**
* Initialize a 'fs_dir_t' variable for directory reading
* @param rddir_p pointer to a 'fs_read_dir_t' variable
* @param path path to a directory
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path);
/**
* Read the next filename form a directory.
* The name of the directories will begin with '/'
* @param rddir_p pointer to an initialized 'fs_rdir_t' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_dir_read (lv_fs_dir_t * rddir_p, char * fn);
/**
* Close the directory reading
* @param rddir_p pointer to an initialized 'fs_dir_t' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_dir_close (lv_fs_dir_t * rddir_p);
/**
* Get the free and total size of a driver in kB
* @param letter the driver letter
* @param total_p pointer to store the total size [kB]
* @param free_p pointer to store the free size [kB]
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
lv_fs_res_t lv_fs_free_space (char letter, uint32_t * total_p, uint32_t * free_p);
/**
* Fill a buffer with the letters of existing drivers
* @param buf buffer to store the letters ('\0' added after the last letter)
* @return the buffer
*/
char * lv_fs_get_letters(char * buf);
/**
* Return with the extension of the filename
* @param fn string with a filename
* @return pointer to the beginning extension or empty string if no extension
*/
const char * lv_fs_get_ext(const char * fn);
/**
* Step up one level
* @param path pointer to a file name
* @return the truncated file name
*/
char * lv_fs_up(char * path);
/**
* Get the last element of a path (e.g. U:/folder/file -> file)
* @param buf buffer to store the letters ('\0' added after the last letter)
* @return pointer to the beginning of the last element in the path
*/
const char * lv_fs_get_last(const char * path);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FILESYSTEM*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_FS_H*/

40
src/lv_misc/lv_gc.c Normal file
View File

@@ -0,0 +1,40 @@
/**
* @file lv_gc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
#if (!defined(LV_ENABLE_GC)) || LV_ENABLE_GC == 0
LV_ROOTS
#endif /* LV_ENABLE_GC */
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/

73
src/lv_misc/lv_gc.h Normal file
View File

@@ -0,0 +1,73 @@
/**
* @file lv_gc.h
*
*/
#ifndef LV_GC_H
#define LV_GC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdint.h>
#include <stdbool.h>
#include "lv_mem.h"
#include "lv_ll.h"
/*********************
* DEFINES
*********************/
#define LV_GC_ROOTS(prefix) \
prefix lv_ll_t _lv_task_ll; /*Linked list to store the lv_tasks*/ \
prefix lv_ll_t _lv_disp_ll; /*Linked list of screens*/ \
prefix lv_ll_t _lv_indev_ll; /*Linked list of screens*/ \
prefix lv_ll_t _lv_drv_ll;\
prefix lv_ll_t _lv_file_ll;\
prefix lv_ll_t _lv_anim_ll;\
prefix lv_ll_t _lv_group_ll;\
prefix void * _lv_task_act;\
#define LV_NO_PREFIX
#define LV_ROOTS LV_GC_ROOTS(LV_NO_PREFIX)
#if LV_ENABLE_GC == 1
# if LV_MEM_CUSTOM != 1
# error "GC requires CUSTOM_MEM"
# endif /* LV_MEM_CUSTOM */
#else /* LV_ENABLE_GC */
# define LV_GC_ROOT(x) x
LV_GC_ROOTS(extern)
#endif /* LV_ENABLE_GC */
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_GC_H*/

391
src/lv_misc/lv_ll.c Normal file
View File

@@ -0,0 +1,391 @@
/**
* @file lv_ll.c
* Handle linked lists.
* The nodes are dynamically allocated by the 'lv_mem' module,
*/
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <string.h>
#include "lv_ll.h"
#include "lv_mem.h"
/*********************
* DEFINES
*********************/
#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t*) + sizeof(lv_ll_node_t*))
#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)
#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t*))
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev);
static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initialize linked list
* @param ll_dsc pointer to ll_dsc variable
* @param node_size the size of 1 node in bytes
*/
void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
{
ll_p->head = NULL;
ll_p->tail = NULL;
#ifdef LV_MEM_ENV64
/*Round the size up to 8*/
if(node_size & 0x7) {
node_size = node_size & (~0x7);
node_size += 8;
}
#else
/*Round the size up to 4*/
if(node_size & 0x3) {
node_size = node_size & (~0x3);
node_size += 4;
}
#endif
ll_p->n_size = node_size;
}
/**
* Add a new head to a linked list
* @param ll_p pointer to linked list
* @return pointer to the new head
*/
void * lv_ll_ins_head(lv_ll_t * ll_p)
{
lv_ll_node_t * n_new;
n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
if(n_new != NULL) {
node_set_prev(ll_p, n_new, NULL); /*No prev. before the new head*/
node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/
if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/
node_set_prev(ll_p, ll_p->head, n_new);
}
ll_p->head = n_new; /*Set the new head in the dsc.*/
if(ll_p->tail == NULL) {/*If there is no tail (1. node) set the tail too*/
ll_p->tail = n_new;
}
}
return n_new;
}
/**
* Insert a new node in front of the n_act node
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the new head
*/
void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act)
{
lv_ll_node_t * n_new;
lv_ll_node_t * n_prev;
if(NULL == ll_p || NULL == n_act) return NULL;
if(lv_ll_get_head(ll_p) == n_act) {
n_new = lv_ll_ins_head(ll_p);
if(n_new == NULL) return NULL;
} else {
n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
if(n_new == NULL) return NULL;
n_prev = lv_ll_get_prev(ll_p, n_act);
node_set_next(ll_p, n_prev, n_new);
node_set_prev(ll_p, n_new, n_prev);
node_set_prev(ll_p, n_act, n_new);
node_set_next(ll_p, n_new, n_act);
}
return n_new;
}
/**
* Add a new tail to a linked list
* @param ll_p pointer to linked list
* @return pointer to the new tail
*/
void * lv_ll_ins_tail(lv_ll_t * ll_p)
{
lv_ll_node_t * n_new;
n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);
if(n_new == NULL) return NULL;
if(n_new != NULL) {
node_set_next(ll_p, n_new, NULL); /*No next after the new tail*/
node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is tho old tail*/
if(ll_p->tail != NULL) { /*If there is old tail then the new comes after it*/
node_set_next(ll_p, ll_p->tail, n_new);
}
ll_p->tail = n_new; /*Set the new tail in the dsc.*/
if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/
ll_p->head = n_new;
}
}
return n_new;
}
/**
* Remove the node 'node_p' from 'll_p' linked list.
* It does not free the the memory of node.
* @param ll_p pointer to the linked list of 'node_p'
* @param node_p pointer to node in 'll_p' linked list
*/
void lv_ll_rem(lv_ll_t * ll_p, void * node_p)
{
if(lv_ll_get_head(ll_p) == node_p) {
/*The new head will be the node after 'n_act'*/
ll_p->head = lv_ll_get_next(ll_p, node_p);
if(ll_p->head == NULL) {
ll_p->tail = NULL;
} else {
node_set_prev(ll_p, ll_p->head, NULL);
}
} else if(lv_ll_get_tail(ll_p) == node_p) {
/*The new tail will be the node before 'n_act'*/
ll_p->tail = lv_ll_get_prev(ll_p, node_p);
if(ll_p->tail == NULL) {
ll_p->head = NULL;
} else {
node_set_next(ll_p, ll_p->tail, NULL);
}
} else {
lv_ll_node_t * n_prev = lv_ll_get_prev(ll_p, node_p);
lv_ll_node_t * n_next = lv_ll_get_next(ll_p, node_p);
node_set_next(ll_p, n_prev, n_next);
node_set_prev(ll_p, n_next, n_prev);
}
}
/**
* Remove and free all elements from a linked list. The list remain valid but become empty.
* @param ll_p pointer to linked list
*/
void lv_ll_clear(lv_ll_t * ll_p)
{
void * i;
void * i_next;
i = lv_ll_get_head(ll_p);
i_next = NULL;
while(i != NULL) {
i_next = lv_ll_get_next(ll_p, i);
lv_ll_rem(ll_p, i);
lv_mem_free(i);
i = i_next;
}
}
/**
* Move a node to a new linked list
* @param ll_ori_p pointer to the original (old) linked list
* @param ll_new_p pointer to the new linked list
* @param node pointer to a node
*/
void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node)
{
lv_ll_rem(ll_ori_p, node);
/*Set node as head*/
node_set_prev(ll_new_p, node, NULL);
node_set_next(ll_new_p, node, ll_new_p->head);
if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/
node_set_prev(ll_new_p, ll_new_p->head, node);
}
ll_new_p->head = node; /*Set the new head in the dsc.*/
if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/
ll_new_p->tail = node;
}
}
/**
* Return with head node of the linked list
* @param ll_p pointer to linked list
* @return pointer to the head of 'll_p'
*/
void * lv_ll_get_head(const lv_ll_t * ll_p)
{
void * head = NULL;
if(ll_p != NULL) {
head = ll_p->head;
}
return head;
}
/**
* Return with tail node of the linked list
* @param ll_p pointer to linked list
* @return pointer to the head of 'll_p'
*/
void * lv_ll_get_tail(const lv_ll_t * ll_p)
{
void * tail = NULL;
if(ll_p != NULL) {
tail = ll_p->tail;
}
return tail;
}
/**
* Return with the pointer of the next node after 'n_act'
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the next node
*/
void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act)
{
void * next = NULL;
if(ll_p != NULL) {
const lv_ll_node_t * n_act_d = n_act;
memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));
}
return next;
}
/**
* Return with the pointer of the previous node after 'n_act'
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the previous node
*/
void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act)
{
void * prev = NULL;
if(ll_p != NULL) {
const lv_ll_node_t * n_act_d = n_act;
memcpy(&prev, n_act_d + LL_PREV_P_OFFSET(ll_p), sizeof(void *));
}
return prev;
}
void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p)
{
(void)(ll_p);
(void)(n1_p);
(void)(n2_p);
/*TODO*/
}
/**
* Move a nodw before an other node in the same linked list
* @param ll_p pointer to a linked list
* @param n_act pointer to node to move
* @param n_after pointer to a node which should be after `n_act`
*/
void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after)
{
if(n_act == n_after) return; /*Can't move before itself*/
void * n_before;
if(n_after != NULL) n_before = lv_ll_get_prev(ll_p, n_after);
else n_before = lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/
if(n_act == n_before) return; /*Already before `n_after`*/
/*It's much easier to remove from the list and add again*/
lv_ll_rem(ll_p, n_act);
/*Add again by setting the prev. and next nodes*/
node_set_next(ll_p, n_before, n_act);
node_set_prev(ll_p, n_act, n_before);
node_set_prev(ll_p, n_after, n_act);
node_set_next(ll_p, n_act, n_after);
/*If `n_act` was moved before NULL then it become the new tail*/
if(n_after == NULL) ll_p->tail = n_act;
}
/**
* Check if a linked list is empty
* @param ll_p pointer to a linked list
* @return true: the linked list is empty; false: not empty
*/
bool lv_ll_is_empty(lv_ll_t * ll_p)
{
if(ll_p == NULL) return true;
if(ll_p->head == NULL && ll_p->tail == NULL) return true;
return false;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Set the 'pervious node pointer' of a node
* @param ll_p pointer to linked list
* @param act pointer to a node which prev. node pointer should be set
* @param prev pointer to a node which should be the previous node before 'act'
*/
static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
{
if(act == NULL) return; /*Can't set the prev node of `NULL`*/
uint32_t node_p_size = sizeof(lv_ll_node_t *);
if(prev) memcpy(act + LL_PREV_P_OFFSET(ll_p), &prev, node_p_size);
else memset(act + LL_PREV_P_OFFSET(ll_p), 0, node_p_size);
}
/**
* Set the 'next node pointer' of a node
* @param ll_p pointer to linked list
* @param act pointer to a node which next node pointer should be set
* @param next pointer to a node which should be the next node before 'act'
*/
static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next)
{
if(act == NULL) return; /*Can't set the next node of `NULL`*/
uint32_t node_p_size = sizeof(lv_ll_node_t *);
if(next) memcpy(act + LL_NEXT_P_OFFSET(ll_p), &next, node_p_size);
else memset(act + LL_NEXT_P_OFFSET(ll_p), 0, node_p_size);
}

152
src/lv_misc/lv_ll.h Normal file
View File

@@ -0,0 +1,152 @@
/**
* @file lv_ll.c
* Handle linked lists. The nodes are dynamically allocated by the 'lv_mem' module.
*/
#ifndef LV_LL_H
#define LV_LL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_mem.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Dummy type to make handling easier*/
typedef uint8_t lv_ll_node_t;
/*Description of a linked list*/
typedef struct
{
uint32_t n_size;
lv_ll_node_t* head;
lv_ll_node_t* tail;
} lv_ll_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize linked list
* @param ll_dsc pointer to ll_dsc variable
* @param node_size the size of 1 node in bytes
*/
void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size);
/**
* Add a new head to a linked list
* @param ll_p pointer to linked list
* @return pointer to the new head
*/
void * lv_ll_ins_head(lv_ll_t * ll_p);
/**
* Insert a new node in front of the n_act node
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the new head
*/
void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act);
/**
* Add a new tail to a linked list
* @param ll_p pointer to linked list
* @return pointer to the new tail
*/
void * lv_ll_ins_tail(lv_ll_t * ll_p);
/**
* Remove the node 'node_p' from 'll_p' linked list.
* It does not free the the memory of node.
* @param ll_p pointer to the linked list of 'node_p'
* @param node_p pointer to node in 'll_p' linked list
*/
void lv_ll_rem(lv_ll_t * ll_p, void * node_p);
/**
* Remove and free all elements from a linked list. The list remain valid but become empty.
* @param ll_p pointer to linked list
*/
void lv_ll_clear(lv_ll_t * ll_p);
/**
* Move a node to a new linked list
* @param ll_ori_p pointer to the original (old) linked list
* @param ll_new_p pointer to the new linked list
* @param node pointer to a node
*/
void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node);
/**
* Return with head node of the linked list
* @param ll_p pointer to linked list
* @return pointer to the head of 'll_p'
*/
void * lv_ll_get_head(const lv_ll_t * ll_p);
/**
* Return with tail node of the linked list
* @param ll_p pointer to linked list
* @return pointer to the head of 'll_p'
*/
void * lv_ll_get_tail(const lv_ll_t * ll_p);
/**
* Return with the pointer of the next node after 'n_act'
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the next node
*/
void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act);
/**
* Return with the pointer of the previous node after 'n_act'
* @param ll_p pointer to linked list
* @param n_act pointer a node
* @return pointer to the previous node
*/
void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act);
/**
* Move a nodw before an other node in the same linked list
* @param ll_p pointer to a linked list
* @param n_act pointer to node to move
* @param n_after pointer to a node which should be after `n_act`
*/
void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after);
/**
* Check if a linked list is empty
* @param ll_p pointer to a linked list
* @return true: the linked list is empty; false: not empty
*/
bool lv_ll_is_empty(lv_ll_t * ll_p);
/**********************
* MACROS
**********************/
#define LV_LL_READ(list, i) for(i = lv_ll_get_head(&list); i != NULL; i = lv_ll_get_next(&list, i))
#define LV_LL_READ_BACK(list, i) for(i = lv_ll_get_tail(&list); i != NULL; i = lv_ll_get_prev(&list, i))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

76
src/lv_misc/lv_log.c Normal file
View File

@@ -0,0 +1,76 @@
/**
* @file lv_log.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_log.h"
#if LV_USE_LOG
#if LV_LOG_PRINTF
#include <stdio.h>
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static void (*print_cb)(lv_log_level_t, const char *, uint32_t, const char *);
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register custom print (or anything else) function to call when log is added
* @param f a function pointer:
* `void my_print (lv_log_level_t level, const char * file, uint32_t line, const char * dsc)`
*/
void lv_log_register_print(void f(lv_log_level_t, const char *, uint32_t, const char *))
{
print_cb = f;
}
/**
* Add a log
* @param level the level of log. (From `lv_log_level_t` enum)
* @param file name of the file when the log added
* @param line line number in the source code where the log added
* @param dsc description of the log
*/
void lv_log_add(lv_log_level_t level, const char * file, int line, const char * dsc)
{
if(level >= _LV_LOG_LEVEL_NUM) return; /*Invalid level*/
if(level >= LV_LOG_LEVEL) {
#if LV_LOG_PRINTF
static const char * lvl_prefix[] = {"Trace", "Info", "Warn", "Error"};
printf("%s: %s \t(%s #%d)\n", lvl_prefix[level], dsc, file, line);
#else
if(print_cb) print_cb(level, file, line, dsc);
#endif
}
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_LOG*/

86
src/lv_misc/lv_log.h Normal file
View File

@@ -0,0 +1,86 @@
/**
* @file lv_log.h
*
*/
#ifndef LV_LOG_H
#define LV_LOG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdint.h>
/*********************
* DEFINES
*********************/
/*Possible log level. For compatibility declare it independently from `LV_USE_LOG`*/
#define LV_LOG_LEVEL_TRACE 0 /*A lot of logs to give detailed information*/
#define LV_LOG_LEVEL_INFO 1 /*Log important events*/
#define LV_LOG_LEVEL_WARN 2 /*Log if something unwanted happened but didn't caused problem*/
#define LV_LOG_LEVEL_ERROR 3 /*Only critical issue, when the system may fail*/
#define _LV_LOG_LEVEL_NUM 4
typedef int8_t lv_log_level_t;
#if LV_USE_LOG
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register custom print (or anything else) function to call when log is added
* @param f a function pointer:
* `void my_print (lv_log_level_t level, const char * file, uint32_t line, const char * dsc)`
*/
void lv_log_register_print(void f(lv_log_level_t, const char *, uint32_t, const char *));
/**
* Add a log
* @param level the level of log. (From `lv_log_level_t` enum)
* @param file name of the file when the log added
* @param line line number in the source code where the log added
* @param dsc description of the log
*/
void lv_log_add(lv_log_level_t level, const char * file, int line, const char * dsc);
/**********************
* MACROS
**********************/
#define LV_LOG_TRACE(dsc) lv_log_add(LV_LOG_LEVEL_TRACE, __FILE__, __LINE__, dsc);
#define LV_LOG_INFO(dsc) lv_log_add(LV_LOG_LEVEL_INFO, __FILE__, __LINE__, dsc);
#define LV_LOG_WARN(dsc) lv_log_add(LV_LOG_LEVEL_WARN, __FILE__, __LINE__, dsc);
#define LV_LOG_ERROR(dsc) lv_log_add(LV_LOG_LEVEL_ERROR, __FILE__, __LINE__, dsc);
#else /*LV_USE_LOG*/
/*Do nothing if `LV_USE_LOG 0`*/
#define lv_log_add(level, file, line, dsc) {;}
#define LV_LOG_TRACE(dsc) {;}
#define LV_LOG_INFO(dsc) {;}
#define LV_LOG_WARN(dsc) {;}
#define LV_LOG_ERROR(dsc) {;}
#endif /*LV_USE_LOG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LOG_H*/

146
src/lv_misc/lv_math.c Normal file
View File

@@ -0,0 +1,146 @@
/**
* @file lv_math.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_math.h"
#include <stdbool.h>
#include <stdlib.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static int16_t sin0_90_table[] = {
0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126,
5690, 6252, 6813, 7371, 7927, 8481, 9032, 9580, 10126, 10668,
11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886,
16383, 16876, 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621,
21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964, 24351, 24730,
25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087,
28377, 28659, 28932, 29196, 29451, 29697, 29934, 30162, 30381, 30591,
30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165,
32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762,
32767
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Convert a number to string
* @param num a number
* @param buf pointer to a `char` buffer. The result will be stored here (max 10 elements)
* @return same as `buf` (just for convenience)
*/
char * lv_math_num_to_str(int32_t num, char * buf)
{
if (num == 0) {
buf[0] = '0';
buf[1] = '\0';
return buf;
}
int8_t digitCount = 0;
int8_t i = 0;
if (num < 0) {
buf[digitCount++] = '-';
num = abs(num);
++i;
}
while (num) {
char digit = num % 10;
buf[digitCount++] = digit + 48;
num /= 10;
}
buf[digitCount] = '\0';
digitCount--;
while (digitCount > i) {
char temp = buf[i];
buf[i] = buf[digitCount];
buf[digitCount] = temp;
digitCount--;
i++;
}
return buf;
}
/**
* Return with sinus of an angle
* @param angle
* @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767
*/
int16_t lv_trigo_sin(int16_t angle)
{
int16_t ret = 0;
angle = angle % 360;
if(angle < 0) angle = 360 + angle;
if(angle < 90) {
ret = sin0_90_table[angle];
} else if(angle >= 90 && angle < 180) {
angle = 180 - angle;
ret = sin0_90_table[angle];
} else if(angle >= 180 && angle < 270) {
angle = angle - 180;
ret = - sin0_90_table[angle];
} else { /*angle >=270*/
angle = 360 - angle;
ret = - sin0_90_table[angle];
}
return ret;
}
/**
* Calculate a value of a Cubic Bezier function.
* @param t time in range of [0..LV_BEZIER_VAL_MAX]
* @param u0 start values in range of [0..LV_BEZIER_VAL_MAX]
* @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX]
* @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX]
* @param u3 end values in range of [0..LV_BEZIER_VAL_MAX]
* @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX]
*/
int32_t lv_bezier3(uint32_t t, int32_t u0, int32_t u1, int32_t u2, int32_t u3)
{
uint32_t t_rem = 1024 - t;
uint32_t t_rem2 = (t_rem * t_rem) >> 10;
uint32_t t_rem3 = (t_rem2 * t_rem) >> 10;
uint32_t t2 = (t * t) >> 10;
uint32_t t3 = (t2 * t) >> 10;
uint32_t v1 = ((uint32_t)t_rem3 * u0) >> 10;
uint32_t v2 = ((uint32_t)3 * t_rem2 * t * u1) >> 20;
uint32_t v3 = ((uint32_t)3 * t_rem * t2 * u2) >> 20;
uint32_t v4 = ((uint32_t)t3 * u3) >> 10;
return v1 + v2 + v3 + v4;
}
/**********************
* STATIC FUNCTIONS
**********************/

73
src/lv_misc/lv_math.h Normal file
View File

@@ -0,0 +1,73 @@
/**
* @file math_base.h
*
*/
#ifndef LV_MATH_H
#define LV_MATH_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
/*********************
* DEFINES
*********************/
#define LV_MATH_MIN(a,b) ((a) < (b) ? (a) : (b))
#define LV_MATH_MAX(a,b) ((a) > (b) ? (a) : (b))
#define LV_MATH_ABS(x) ((x) > 0 ? (x) : (-(x)))
#define LV_TRIGO_SIN_MAX 32767
#define LV_TRIGO_SHIFT 15 /* >> LV_TRIGO_SHIFT to normalize*/
#define LV_BEZIER_VAL_MAX 1024 /*Max time in Bezier functions (not [0..1] to use integers) */
#define LV_BEZIER_VAL_SHIFT 10 /*log2(LV_BEZIER_VAL_MAX): used to normalize up scaled values*/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Convert a number to string
* @param num a number
* @param buf pointer to a `char` buffer. The result will be stored here (max 10 elements)
* @return same as `buf` (just for convenience)
*/
char * lv_math_num_to_str(int32_t num, char * buf);
/**
* Return with sinus of an angle
* @param angle
* @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767
*/
int16_t lv_trigo_sin(int16_t angle);
/**
* Calculate a value of a Cubic Bezier function.
* @param t time in range of [0..LV_BEZIER_VAL_MAX]
* @param u0 start values in range of [0..LV_BEZIER_VAL_MAX]
* @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX]
* @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX]
* @param u3 end values in range of [0..LV_BEZIER_VAL_MAX]
* @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX]
*/
int32_t lv_bezier3(uint32_t t, int32_t u0, int32_t u1, int32_t u2, int32_t u3);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

464
src/lv_misc/lv_mem.c Normal file
View File

@@ -0,0 +1,464 @@
/**
* @file lv_mem.c
* General and portable implementation of malloc and free.
* The dynamic memory monitoring is also supported.
*/
/*********************
* INCLUDES
*********************/
#include "lv_mem.h"
#include "lv_math.h"
#include <string.h>
#if LV_MEM_CUSTOM != 0
#include LV_MEM_CUSTOM_INCLUDE
#endif
/*********************
* DEFINES
*********************/
#define LV_MEM_ADD_JUNK 0 /*Add memory junk on alloc (0xaa) and free(0xbb) (just for testing purposes)*/
#ifdef LV_MEM_ENV64
# define MEM_UNIT uint64_t
#else
# define MEM_UNIT uint32_t
#endif
/**********************
* TYPEDEFS
**********************/
#if LV_ENABLE_GC == 0 /*gc custom allocations must not include header*/
/*The size of this union must be 4 bytes (uint32_t)*/
typedef union {
struct {
MEM_UNIT used: 1; //1: if the entry is used
MEM_UNIT d_size: 31; //Size off the data (1 means 4 bytes)
} s;
MEM_UNIT header; //The header (used + d_size)
} lv_mem_header_t;
typedef struct {
lv_mem_header_t header;
uint8_t first_data; /*First data byte in the allocated data (Just for easily create a pointer)*/
} lv_mem_ent_t;
#endif /* LV_ENABLE_GC */
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_MEM_CUSTOM == 0
static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e);
static void * ent_alloc(lv_mem_ent_t * e, uint32_t size);
static void ent_trunc(lv_mem_ent_t * e, uint32_t size);
#endif
/**********************
* STATIC VARIABLES
**********************/
#if LV_MEM_CUSTOM == 0
static uint8_t * work_mem;
#endif
static uint32_t zero_mem; /*Give the address of this variable if 0 byte should be allocated*/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Initiaiize the dyn_mem module (work memory and other variables)
*/
void lv_mem_init(void)
{
#if LV_MEM_CUSTOM == 0
#if LV_MEM_ADR == 0
/*Allocate a large array to store the dynamically allocated data*/
static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
work_mem = (uint8_t *) work_mem_int;
#else
work_mem = (uint8_t *) LV_MEM_ADR;
#endif
lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem;
full->header.s.used = 0;
/*The total mem size id reduced by the first header and the close patterns */
full->header.s.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t);
#endif
}
/**
* Allocate a memory dynamically
* @param size size of the memory to allocate in bytes
* @return pointer to the allocated memory
*/
void * lv_mem_alloc(uint32_t size)
{
if(size == 0) {
return &zero_mem;
}
#ifdef LV_MEM_ENV64
/*Round the size up to 8*/
if(size & 0x7) {
size = size & (~0x7);
size += 8;
}
#else
/*Round the size up to 4*/
if(size & 0x3) {
size = size & (~0x3);
size += 4;
}
#endif
void * alloc = NULL;
#if LV_MEM_CUSTOM == 0 /*Use the allocation from dyn_mem*/
lv_mem_ent_t * e = NULL;
//Search for a appropriate entry
do {
//Get the next entry
e = ent_get_next(e);
/*If there is next entry then try to allocate there*/
if(e != NULL) {
alloc = ent_alloc(e, size);
}
//End if there is not next entry OR the alloc. is successful
} while(e != NULL && alloc == NULL);
#else /*Use custom, user defined malloc function*/
#if LV_ENABLE_GC == 1 /*gc must not include header*/
alloc = LV_MEM_CUSTOM_ALLOC(size);
#else /* LV_ENABLE_GC */
/*Allocate a header too to store the size*/
alloc = LV_MEM_CUSTOM_ALLOC(size + sizeof(lv_mem_header_t));
if(alloc != NULL) {
((lv_mem_ent_t *) alloc)->header.s.d_size = size;
((lv_mem_ent_t *) alloc)->header.s.used = 1;
alloc = &((lv_mem_ent_t *) alloc)->first_data;
}
#endif /* LV_ENABLE_GC */
#endif /* LV_MEM_CUSTOM */
#if LV_MEM_ADD_JUNK
if(alloc != NULL) memset(alloc, 0xaa, size);
#endif
if(alloc == NULL) LV_LOG_WARN("Couldn't allocate memory");
return alloc;
}
/**
* Free an allocated data
* @param data pointer to an allocated memory
*/
void lv_mem_free(const void * data)
{
if(data == &zero_mem) return;
if(data == NULL) return;
#if LV_MEM_ADD_JUNK
memset((void *)data, 0xbb, lv_mem_get_size(data));
#endif
#if LV_ENABLE_GC==0
/*e points to the header*/
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t));
e->header.s.used = 0;
#endif
#if LV_MEM_CUSTOM == 0
#if LV_MEM_AUTO_DEFRAG
/* Make a simple defrag.
* Join the following free entries after this*/
lv_mem_ent_t * e_next;
e_next = ent_get_next(e);
while(e_next != NULL) {
if(e_next->header.s.used == 0) {
e->header.s.d_size += e_next->header.s.d_size + sizeof(e->header);
} else {
break;
}
e_next = ent_get_next(e_next);
}
#endif
#else /*Use custom, user defined free function*/
#if LV_ENABLE_GC==0
LV_MEM_CUSTOM_FREE(e);
#else
LV_MEM_CUSTOM_FREE((void*)data);
#endif /*LV_ENABLE_GC*/
#endif
}
/**
* Reallocate a memory with a new size. The old content will be kept.
* @param data pointer to an allocated memory.
* Its content will be copied to the new memory block and freed
* @param new_size the desired new size in byte
* @return pointer to the new memory
*/
#if LV_ENABLE_GC==0
void * lv_mem_realloc(void * data_p, uint32_t new_size)
{
/*data_p could be previously freed pointer (in this case it is invalid)*/
if(data_p != NULL) {
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t));
if(e->header.s.used == 0) {
data_p = NULL;
}
}
uint32_t old_size = lv_mem_get_size(data_p);
if(old_size == new_size) return data_p; /*Also avoid reallocating the same memory*/
#if LV_MEM_CUSTOM == 0
/* Only truncate the memory is possible
* If the 'old_size' was extended by a header size in 'ent_trunc' it avoids reallocating this same memory */
if(new_size < old_size) {
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t));
ent_trunc(e, new_size);
return &e->first_data;
}
#endif
void * new_p;
new_p = lv_mem_alloc(new_size);
if(new_p != NULL && data_p != NULL) {
/*Copy the old data to the new. Use the smaller size*/
if(old_size != 0) {
memcpy(new_p, data_p, LV_MATH_MIN(new_size, old_size));
lv_mem_free(data_p);
}
}
if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");
return new_p;
}
#else /* LV_ENABLE_GC */
void * lv_mem_realloc(void * data_p, uint32_t new_size)
{
void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size);
if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");
return new_p;
}
#endif /* lv_enable_gc */
/**
* Join the adjacent free memory blocks
*/
void lv_mem_defrag(void)
{
#if LV_MEM_CUSTOM == 0
lv_mem_ent_t * e_free;
lv_mem_ent_t * e_next;
e_free = ent_get_next(NULL);
while(1) {
/*Search the next free entry*/
while(e_free != NULL) {
if(e_free->header.s.used != 0) {
e_free = ent_get_next(e_free);
} else {
break;
}
}
if(e_free == NULL) return;
/*Joint the following free entries to the free*/
e_next = ent_get_next(e_free);
while(e_next != NULL) {
if(e_next->header.s.used == 0) {
e_free->header.s.d_size += e_next->header.s.d_size + sizeof(e_next->header);
} else {
break;
}
e_next = ent_get_next(e_next);
}
if(e_next == NULL) return;
/*Continue from the lastly checked entry*/
e_free = e_next;
}
#endif
}
/**
* Give information about the work memory of dynamic allocation
* @param mon_p pointer to a dm_mon_p variable,
* the result of the analysis will be stored here
*/
void lv_mem_monitor(lv_mem_monitor_t * mon_p)
{
/*Init the data*/
memset(mon_p, 0, sizeof(lv_mem_monitor_t));
#if LV_MEM_CUSTOM == 0
lv_mem_ent_t * e;
e = NULL;
e = ent_get_next(e);
while(e != NULL) {
if(e->header.s.used == 0) {
mon_p->free_cnt++;
mon_p->free_size += e->header.s.d_size;
if(e->header.s.d_size > mon_p->free_biggest_size) {
mon_p->free_biggest_size = e->header.s.d_size;
}
} else {
mon_p->used_cnt++;
}
e = ent_get_next(e);
}
mon_p->total_size = LV_MEM_SIZE;
mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size;
mon_p->frag_pct = 100 - mon_p->frag_pct;
#endif
}
/**
* Give the size of an allocated memory
* @param data pointer to an allocated memory
* @return the size of data memory in bytes
*/
#if LV_ENABLE_GC==0
uint32_t lv_mem_get_size(const void * data)
{
if(data == NULL) return 0;
if(data == &zero_mem) return 0;
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t));
return e->header.s.d_size;
}
#else /* LV_ENABLE_GC */
uint32_t lv_mem_get_size(const void * data)
{
return LV_MEM_CUSTOM_GET_SIZE(data);
}
#endif /*LV_ENABLE_GC*/
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_MEM_CUSTOM == 0
/**
* Give the next entry after 'act_e'
* @param act_e pointer to an entry
* @return pointer to an entry after 'act_e'
*/
static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e)
{
lv_mem_ent_t * next_e = NULL;
if(act_e == NULL) { /*NULL means: get the first entry*/
next_e = (lv_mem_ent_t *) work_mem;
} else { /*Get the next entry */
uint8_t * data = &act_e->first_data;
next_e = (lv_mem_ent_t *)&data[act_e->header.s.d_size];
if(&next_e->first_data >= &work_mem[LV_MEM_SIZE]) next_e = NULL;
}
return next_e;
}
/**
* Try to do the real allocation with a given size
* @param e try to allocate to this entry
* @param size size of the new memory in bytes
* @return pointer to the allocated memory or NULL if not enough memory in the entry
*/
static void * ent_alloc(lv_mem_ent_t * e, uint32_t size)
{
void * alloc = NULL;
/*If the memory is free and big enough then use it */
if(e->header.s.used == 0 && e->header.s.d_size >= size) {
/*Truncate the entry to the desired size */
ent_trunc(e, size),
e->header.s.used = 1;
/*Save the allocated data*/
alloc = &e->first_data;
}
return alloc;
}
/**
* Truncate the data of entry to the given size
* @param e Pointer to an entry
* @param size new size in bytes
*/
static void ent_trunc(lv_mem_ent_t * e, uint32_t size)
{
#ifdef LV_MEM_ENV64
/*Round the size up to 8*/
if(size & 0x7) {
size = size & (~0x7);
size += 8;
}
#else
/*Round the size up to 4*/
if(size & 0x3) {
size = size & (~0x3);
size += 4;
}
#endif
/*Don't let empty space only for a header without data*/
if(e->header.s.d_size == size + sizeof(lv_mem_header_t)) {
size = e->header.s.d_size;
}
/* Create the new entry after the current if there is space for it */
if(e->header.s.d_size != size) {
uint8_t * e_data = &e->first_data;
lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size];
after_new_e->header.s.used = 0;
after_new_e->header.s.d_size = e->header.s.d_size - size - sizeof(lv_mem_header_t);
}
/* Set the new size for the original entry */
e->header.s.d_size = size;
}
#endif

127
src/lv_misc/lv_mem.h Normal file
View File

@@ -0,0 +1,127 @@
/**
* @file lv_mem.h
*
*/
#ifndef LV_MEM_H
#define LV_MEM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdint.h>
#include <stddef.h>
#include "lv_log.h"
/*********************
* DEFINES
*********************/
// Check windows
#ifdef __WIN64
# define LV_MEM_ENV64
#endif
// Check GCC
#ifdef __GNUC__
# if defined(__x86_64__) || defined(__ppc64__)
# define LV_MEM_ENV64
# endif
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct
{
uint32_t total_size;
uint32_t free_cnt;
uint32_t free_size;
uint32_t free_biggest_size;
uint32_t used_cnt;
uint8_t used_pct;
uint8_t frag_pct;
} lv_mem_monitor_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initiaize the dyn_mem module (work memory and other variables)
*/
void lv_mem_init(void);
/**
* Allocate a memory dynamically
* @param size size of the memory to allocate in bytes
* @return pointer to the allocated memory
*/
void * lv_mem_alloc(uint32_t size);
/**
* Free an allocated data
* @param data pointer to an allocated memory
*/
void lv_mem_free(const void * data);
/**
* Reallocate a memory with a new size. The old content will be kept.
* @param data pointer to an allocated memory.
* Its content will be copied to the new memory block and freed
* @param new_size the desired new size in byte
* @return pointer to the new memory
*/
void * lv_mem_realloc(void * data_p, uint32_t new_size);
/**
* Join the adjacent free memory blocks
*/
void lv_mem_defrag(void);
/**
* Give information about the work memory of dynamic allocation
* @param mon_p pointer to a dm_mon_p variable,
* the result of the analysis will be stored here
*/
void lv_mem_monitor(lv_mem_monitor_t * mon_p);
/**
* Give the size of an allocated memory
* @param data pointer to an allocated memory
* @return the size of data memory in bytes
*/
uint32_t lv_mem_get_size(const void * data);
/**********************
* MACROS
**********************/
/**
* Halt on NULL pointer
* p pointer to a memory
*/
#if LV_USE_LOG == 0
# define lv_mem_assert(p) {if(p == NULL) while(1); }
#else
# define lv_mem_assert(p) {if(p == NULL) {LV_LOG_ERROR("Out of memory!"); while(1); }}
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_MEM_H*/

18
src/lv_misc/lv_misc.mk Normal file
View File

@@ -0,0 +1,18 @@
CSRCS += lv_font.c
CSRCS += lv_circ.c
CSRCS += lv_area.c
CSRCS += lv_task.c
CSRCS += lv_fs.c
CSRCS += lv_anim.c
CSRCS += lv_mem.c
CSRCS += lv_ll.c
CSRCS += lv_color.c
CSRCS += lv_txt.c
CSRCS += lv_math.c
CSRCS += lv_log.c
CSRCS += lv_gc.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/src/lv_misc
VPATH += :$(LVGL_DIR)/lvgl/src/lv_misc
CFLAGS += "-I$(LVGL_DIR)/lvgl/src/lv_misc"

207
src/lv_misc/lv_symbol_def.h Normal file
View File

@@ -0,0 +1,207 @@
#ifndef LV_SYMBOL_DEF_H
#define LV_SYMBOL_DEF_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
/*
* With no UTF-8 support (192- 255) (192..241 is used)
*
* With UTF-8 support (in Supplemental Private Use Area-A): 0xF800 .. 0xF831
* - Basic symbols: 0xE000..0xE01F
* - File symbols: 0xE020..0xE03F
* - Feedback symbols: 0xE040..0xE05F
* - Reserved: 0xE060..0xE07F
*/
#if LV_TXT_UTF8 == 0
#define LV_SYMBOL_GLYPH_FIRST 0xC0
#define LV_SYMBOL_AUDIO _LV_SYMBOL_VALUE1(C0)
#define LV_SYMBOL_VIDEO _LV_SYMBOL_VALUE1(C1)
#define LV_SYMBOL_LIST _LV_SYMBOL_VALUE1(C2)
#define LV_SYMBOL_OK _LV_SYMBOL_VALUE1(C3)
#define LV_SYMBOL_CLOSE _LV_SYMBOL_VALUE1(C4)
#define LV_SYMBOL_POWER _LV_SYMBOL_VALUE1(C5)
#define LV_SYMBOL_SETTINGS _LV_SYMBOL_VALUE1(C6)
#define LV_SYMBOL_TRASH _LV_SYMBOL_VALUE1(C7)
#define LV_SYMBOL_HOME _LV_SYMBOL_VALUE1(C8)
#define LV_SYMBOL_DOWNLOAD _LV_SYMBOL_VALUE1(C9)
#define LV_SYMBOL_DRIVE _LV_SYMBOL_VALUE1(CA)
#define LV_SYMBOL_REFRESH _LV_SYMBOL_VALUE1(CB)
#define LV_SYMBOL_MUTE _LV_SYMBOL_VALUE1(CC)
#define LV_SYMBOL_VOLUME_MID _LV_SYMBOL_VALUE1(CD)
#define LV_SYMBOL_VOLUME_MAX _LV_SYMBOL_VALUE1(CE)
#define LV_SYMBOL_IMAGE _LV_SYMBOL_VALUE1(CF)
#define LV_SYMBOL_EDIT _LV_SYMBOL_VALUE1(D0)
#define LV_SYMBOL_PREV _LV_SYMBOL_VALUE1(D1)
#define LV_SYMBOL_PLAY _LV_SYMBOL_VALUE1(D2)
#define LV_SYMBOL_PAUSE _LV_SYMBOL_VALUE1(D3)
#define LV_SYMBOL_STOP _LV_SYMBOL_VALUE1(D4)
#define LV_SYMBOL_NEXT _LV_SYMBOL_VALUE1(D5)
#define LV_SYMBOL_EJECT _LV_SYMBOL_VALUE1(D6)
#define LV_SYMBOL_LEFT _LV_SYMBOL_VALUE1(D7)
#define LV_SYMBOL_RIGHT _LV_SYMBOL_VALUE1(D8)
#define LV_SYMBOL_PLUS _LV_SYMBOL_VALUE1(D9)
#define LV_SYMBOL_MINUS _LV_SYMBOL_VALUE1(DA)
#define LV_SYMBOL_WARNING _LV_SYMBOL_VALUE1(DB)
#define LV_SYMBOL_SHUFFLE _LV_SYMBOL_VALUE1(DC)
#define LV_SYMBOL_UP _LV_SYMBOL_VALUE1(DD)
#define LV_SYMBOL_DOWN _LV_SYMBOL_VALUE1(DE)
#define LV_SYMBOL_LOOP _LV_SYMBOL_VALUE1(DF)
#define LV_SYMBOL_DIRECTORY _LV_SYMBOL_VALUE1(E0)
#define LV_SYMBOL_UPLOAD _LV_SYMBOL_VALUE1(E1)
#define LV_SYMBOL_CALL _LV_SYMBOL_VALUE1(E2)
#define LV_SYMBOL_CUT _LV_SYMBOL_VALUE1(E3)
#define LV_SYMBOL_COPY _LV_SYMBOL_VALUE1(E4)
#define LV_SYMBOL_SAVE _LV_SYMBOL_VALUE1(E5)
#define LV_SYMBOL_CHARGE _LV_SYMBOL_VALUE1(E6)
#define LV_SYMBOL_BELL _LV_SYMBOL_VALUE1(E7)
#define LV_SYMBOL_KEYBOARD _LV_SYMBOL_VALUE1(E8)
#define LV_SYMBOL_GPS _LV_SYMBOL_VALUE1(E9)
#define LV_SYMBOL_FILE _LV_SYMBOL_VALUE1(EA)
#define LV_SYMBOL_WIFI _LV_SYMBOL_VALUE1(EB)
#define LV_SYMBOL_BATTERY_FULL _LV_SYMBOL_VALUE1(EC)
#define LV_SYMBOL_BATTERY_3 _LV_SYMBOL_VALUE1(ED)
#define LV_SYMBOL_BATTERY_2 _LV_SYMBOL_VALUE1(EE)
#define LV_SYMBOL_BATTERY_1 _LV_SYMBOL_VALUE1(EF)
#define LV_SYMBOL_BATTERY_EMPTY _LV_SYMBOL_VALUE1(F0)
#define LV_SYMBOL_BLUETOOTH _LV_SYMBOL_VALUE1(F1)
#define LV_SYMBOL_GLYPH_LAST 0xF1
#define LV_SYMBOL_DUMMY _LV_SYMBOL_VALUE1(FF) /*Invalid symbol. If written before a string then `lv_img` will show it as a label*/
#else
#define LV_SYMBOL_GLYPH_FIRST 0xF800
#define LV_SYMBOL_AUDIO _LV_SYMBOL_VALUE3(EF,A0,80)
#define LV_SYMBOL_VIDEO _LV_SYMBOL_VALUE3(EF,A0,81)
#define LV_SYMBOL_LIST _LV_SYMBOL_VALUE3(EF,A0,82)
#define LV_SYMBOL_OK _LV_SYMBOL_VALUE3(EF,A0,83)
#define LV_SYMBOL_CLOSE _LV_SYMBOL_VALUE3(EF,A0,84)
#define LV_SYMBOL_POWER _LV_SYMBOL_VALUE3(EF,A0,85)
#define LV_SYMBOL_SETTINGS _LV_SYMBOL_VALUE3(EF,A0,86)
#define LV_SYMBOL_TRASH _LV_SYMBOL_VALUE3(EF,A0,87)
#define LV_SYMBOL_HOME _LV_SYMBOL_VALUE3(EF,A0,88)
#define LV_SYMBOL_DOWNLOAD _LV_SYMBOL_VALUE3(EF,A0,89)
#define LV_SYMBOL_DRIVE _LV_SYMBOL_VALUE3(EF,A0,8A)
#define LV_SYMBOL_REFRESH _LV_SYMBOL_VALUE3(EF,A0,8B)
#define LV_SYMBOL_MUTE _LV_SYMBOL_VALUE3(EF,A0,8C)
#define LV_SYMBOL_VOLUME_MID _LV_SYMBOL_VALUE3(EF,A0,8D)
#define LV_SYMBOL_VOLUME_MAX _LV_SYMBOL_VALUE3(EF,A0,8E)
#define LV_SYMBOL_IMAGE _LV_SYMBOL_VALUE3(EF,A0,8F)
#define LV_SYMBOL_EDIT _LV_SYMBOL_VALUE3(EF,A0,90)
#define LV_SYMBOL_PREV _LV_SYMBOL_VALUE3(EF,A0,91)
#define LV_SYMBOL_PLAY _LV_SYMBOL_VALUE3(EF,A0,92)
#define LV_SYMBOL_PAUSE _LV_SYMBOL_VALUE3(EF,A0,93)
#define LV_SYMBOL_STOP _LV_SYMBOL_VALUE3(EF,A0,94)
#define LV_SYMBOL_NEXT _LV_SYMBOL_VALUE3(EF,A0,95)
#define LV_SYMBOL_EJECT _LV_SYMBOL_VALUE3(EF,A0,96)
#define LV_SYMBOL_LEFT _LV_SYMBOL_VALUE3(EF,A0,97)
#define LV_SYMBOL_RIGHT _LV_SYMBOL_VALUE3(EF,A0,98)
#define LV_SYMBOL_PLUS _LV_SYMBOL_VALUE3(EF,A0,99)
#define LV_SYMBOL_MINUS _LV_SYMBOL_VALUE3(EF,A0,9A)
#define LV_SYMBOL_WARNING _LV_SYMBOL_VALUE3(EF,A0,9B)
#define LV_SYMBOL_SHUFFLE _LV_SYMBOL_VALUE3(EF,A0,9C)
#define LV_SYMBOL_UP _LV_SYMBOL_VALUE3(EF,A0,9D)
#define LV_SYMBOL_DOWN _LV_SYMBOL_VALUE3(EF,A0,9E)
#define LV_SYMBOL_LOOP _LV_SYMBOL_VALUE3(EF,A0,9F)
#define LV_SYMBOL_DIRECTORY _LV_SYMBOL_VALUE3(EF,A0,A0)
#define LV_SYMBOL_UPLOAD _LV_SYMBOL_VALUE3(EF,A0,A1)
#define LV_SYMBOL_CALL _LV_SYMBOL_VALUE3(EF,A0,A2)
#define LV_SYMBOL_CUT _LV_SYMBOL_VALUE3(EF,A0,A3)
#define LV_SYMBOL_COPY _LV_SYMBOL_VALUE3(EF,A0,A4)
#define LV_SYMBOL_SAVE _LV_SYMBOL_VALUE3(EF,A0,A5)
#define LV_SYMBOL_CHARGE _LV_SYMBOL_VALUE3(EF,A0,A6)
#define LV_SYMBOL_BELL _LV_SYMBOL_VALUE3(EF,A0,A7)
#define LV_SYMBOL_KEYBOARD _LV_SYMBOL_VALUE3(EF,A0,A8)
#define LV_SYMBOL_GPS _LV_SYMBOL_VALUE3(EF,A0,A9)
#define LV_SYMBOL_FILE _LV_SYMBOL_VALUE3(EF,A0,AA)
#define LV_SYMBOL_WIFI _LV_SYMBOL_VALUE3(EF,A0,AB)
#define LV_SYMBOL_BATTERY_FULL _LV_SYMBOL_VALUE3(EF,A0,AC)
#define LV_SYMBOL_BATTERY_3 _LV_SYMBOL_VALUE3(EF,A0,AD)
#define LV_SYMBOL_BATTERY_2 _LV_SYMBOL_VALUE3(EF,A0,AE)
#define LV_SYMBOL_BATTERY_1 _LV_SYMBOL_VALUE3(EF,A0,AF)
#define LV_SYMBOL_BATTERY_EMPTY _LV_SYMBOL_VALUE3(EF,A0,B0)
#define LV_SYMBOL_BLUETOOTH _LV_SYMBOL_VALUE3(EF,A0,B1)
#define LV_SYMBOL_GLYPH_LAST 0xF831
#define LV_SYMBOL_DUMMY _LV_SYMBOL_VALUE3(EF,A3,BF) /*Invalid symbol at (U+F831). If written before a string then `lv_img` will show it as a label*/
#endif
#define _LV_SYMBOL_VALUE1(x) (0x ## x)
#define _LV_SYMBOL_VALUE3(x, y, z) (0x ## z ## y ## x)
#define _LV_SYMBOL_NUMSTR(sym) LV_ ## sym ## _NUMSTR = sym
enum
{
_LV_SYMBOL_NUMSTR(LV_SYMBOL_AUDIO),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_VIDEO),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_LIST),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_OK),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_CLOSE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_POWER),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_SETTINGS),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_TRASH),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_HOME),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_DOWNLOAD),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_DRIVE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_REFRESH),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_MUTE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_VOLUME_MID),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_VOLUME_MAX),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_IMAGE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_EDIT),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_PREV),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_PLAY),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_PAUSE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_STOP),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_NEXT),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_EJECT),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_LEFT),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_RIGHT),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_PLUS),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_MINUS),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_WARNING),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_SHUFFLE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_UP),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_DOWN),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_LOOP),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_DIRECTORY),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_UPLOAD),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_CALL),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_CUT),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_COPY),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_SAVE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_CHARGE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BELL),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_KEYBOARD),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_GPS),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_FILE),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_WIFI),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BATTERY_FULL),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BATTERY_3),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BATTERY_2),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BATTERY_1),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BATTERY_EMPTY),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_BLUETOOTH),
_LV_SYMBOL_NUMSTR(LV_SYMBOL_DUMMY),
};
#undef _LV_SYMBOL_VALUE1
#undef _LV_SYMBOL_VALUE3
#define _LV_SYMBOL_STR_(x) #x
#define _LV_SYMBOL_STR(x) _LV_SYMBOL_STR_(x)
#define _LV_SYMBOL_CHAR(c) \x ## c
#define _LV_SYMBOL_VALUE1(x) _LV_SYMBOL_STR(_LV_SYMBOL_CHAR(x))
#define _LV_SYMBOL_VALUE3(x, y, z) _LV_SYMBOL_STR(_LV_SYMBOL_CHAR(x)_LV_SYMBOL_CHAR(y)_LV_SYMBOL_CHAR(z))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LV_SYMBOL_DEF_H*/

332
src/lv_misc/lv_task.c Normal file
View File

@@ -0,0 +1,332 @@
/**
* @file lv_task.c
* An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically.
* A priority (5 levels + disable) can be assigned to lv_tasks.
*/
/*********************
* INCLUDES
*********************/
#include <stddef.h>
#include "lv_task.h"
#include "../lv_hal/lv_hal_tick.h"
#include "lv_gc.h"
#if defined(LV_GC_INCLUDE)
# include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
/*********************
* DEFINES
*********************/
#define IDLE_MEAS_PERIOD 500 /*[ms]*/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_task_exec(lv_task_t * lv_task_p);
/**********************
* STATIC VARIABLES
**********************/
static bool lv_task_run = false;
static uint8_t idle_last = 0;
static bool task_deleted;
static bool task_created;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Init the lv_task module
*/
void lv_task_init(void)
{
lv_ll_init(&LV_GC_ROOT(_lv_task_ll), sizeof(lv_task_t));
/*Initially enable the lv_task handling*/
lv_task_enable(true);
}
/**
* Call it periodically to handle lv_tasks.
*/
LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void)
{
LV_LOG_TRACE("lv_task_handler started");
/*Avoid concurrent running of the task handler*/
static bool task_handler_mutex = false;
if(task_handler_mutex) return;
task_handler_mutex = true;
static uint32_t idle_period_start = 0;
static uint32_t handler_start = 0;
static uint32_t busy_time = 0;
if(lv_task_run == false) return;
handler_start = lv_tick_get();
/* Run all task from the highest to the lowest priority
* If a lower priority task is executed check task again from the highest priority
* but on the priority of executed tasks don't run tasks before the executed*/
lv_task_t * task_interrupter = NULL;
lv_task_t * next;
bool end_flag;
do {
end_flag = true;
task_deleted = false;
task_created = false;
LV_GC_ROOT(_lv_task_act) = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll));
while(LV_GC_ROOT(_lv_task_act)) {
/* The task might be deleted if it runs only once ('once = 1')
* So get next element until the current is surely valid*/
next = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), LV_GC_ROOT(_lv_task_act));
/*We reach priority of the turned off task. There is nothing more to do.*/
if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_OFF) {
break;
}
/*Here is the interrupter task. Don't execute it again.*/
if(LV_GC_ROOT(_lv_task_act) == task_interrupter) {
task_interrupter = NULL; /*From this point only task after the interrupter comes, so the interrupter is not interesting anymore*/
LV_GC_ROOT(_lv_task_act) = next;
continue; /*Load the next task*/
}
/*Just try to run the tasks with highest priority.*/
if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_HIGHEST) {
lv_task_exec(LV_GC_ROOT(_lv_task_act));
}
/*Tasks with higher priority then the interrupted shall be run in every case*/
else if(task_interrupter) {
if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio > task_interrupter->prio) {
if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) {
task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */
end_flag = false;
break;
}
}
}
/* It is no interrupter task or we already reached it earlier.
* Just run the remaining tasks*/
else {
if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) {
task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */
end_flag = false;
break;
}
}
if(task_deleted) break; /*If a task was deleted then this or the next item might be corrupted*/
if(task_created) break; /*If a task was deleted then this or the next item might be corrupted*/
LV_GC_ROOT(_lv_task_act) = next; /*Load the next task*/
}
} while(!end_flag);
busy_time += lv_tick_elaps(handler_start);
uint32_t idle_period_time = lv_tick_elaps(idle_period_start);
if(idle_period_time >= IDLE_MEAS_PERIOD) {
idle_last = (uint32_t)((uint32_t)busy_time * 100) / IDLE_MEAS_PERIOD; /*Calculate the busy percentage*/
idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/
busy_time = 0;
idle_period_start = lv_tick_get();
}
task_handler_mutex = false; /*Release the mutex*/
LV_LOG_TRACE("lv_task_handler ready");
}
/**
* Create a new lv_task
* @param task a function which is the task itself
* @param period call period in ms unit
* @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped)
* @param param free parameter
* @return pointer to the new task
*/
lv_task_t * lv_task_create(void (*task)(void *), uint32_t period, lv_task_prio_t prio, void * param)
{
lv_task_t * new_lv_task = NULL;
lv_task_t * tmp;
/*Create task lists in order of priority from high to low*/
tmp = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll));
if(NULL == tmp) { /*First task*/
new_lv_task = lv_ll_ins_head(&LV_GC_ROOT(_lv_task_ll));
lv_mem_assert(new_lv_task);
if(new_lv_task == NULL) return NULL;
} else {
do {
if(tmp->prio <= prio) {
new_lv_task = lv_ll_ins_prev(&LV_GC_ROOT(_lv_task_ll), tmp);
lv_mem_assert(new_lv_task);
if(new_lv_task == NULL) return NULL;
break;
}
tmp = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), tmp);
} while(tmp != NULL);
if(tmp == NULL) { /*Only too high priority tasks were found*/
new_lv_task = lv_ll_ins_tail(&LV_GC_ROOT(_lv_task_ll));
lv_mem_assert(new_lv_task);
if(new_lv_task == NULL) return NULL;
}
}
new_lv_task->period = period;
new_lv_task->task = task;
new_lv_task->prio = prio;
new_lv_task->param = param;
new_lv_task->once = 0;
new_lv_task->last_run = lv_tick_get();
task_created = true;
return new_lv_task;
}
/**
* Delete a lv_task
* @param lv_task_p pointer to task created by lv_task_p
*/
void lv_task_del(lv_task_t * lv_task_p)
{
lv_ll_rem(&LV_GC_ROOT(_lv_task_ll), lv_task_p);
lv_mem_free(lv_task_p);
if(LV_GC_ROOT(_lv_task_act) == lv_task_p) task_deleted = true; /*The active task was deleted*/
}
/**
* Set new priority for a lv_task
* @param lv_task_p pointer to a lv_task
* @param prio the new priority
*/
void lv_task_set_prio(lv_task_t * lv_task_p, lv_task_prio_t prio)
{
/*Find the tasks with new priority*/
lv_task_t * i;
LV_LL_READ(LV_GC_ROOT(_lv_task_ll), i) {
if(i->prio <= prio) {
if(i != lv_task_p) lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), lv_task_p, i);
break;
}
}
/*There was no such a low priority so far then add the node to the tail*/
if(i == NULL) {
lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), lv_task_p, NULL);
}
lv_task_p->prio = prio;
}
/**
* Set new period for a lv_task
* @param lv_task_p pointer to a lv_task
* @param period the new period
*/
void lv_task_set_period(lv_task_t * lv_task_p, uint32_t period)
{
lv_task_p->period = period;
}
/**
* Make a lv_task ready. It will not wait its period.
* @param lv_task_p pointer to a lv_task.
*/
void lv_task_ready(lv_task_t * lv_task_p)
{
lv_task_p->last_run = lv_tick_get() - lv_task_p->period - 1;
}
/**
* Delete the lv_task after one call
* @param lv_task_p pointer to a lv_task.
*/
void lv_task_once(lv_task_t * lv_task_p)
{
lv_task_p->once = 1;
}
/**
* Reset a lv_task.
* It will be called the previously set period milliseconds later.
* @param lv_task_p pointer to a lv_task.
*/
void lv_task_reset(lv_task_t * lv_task_p)
{
lv_task_p->last_run = lv_tick_get();
}
/**
* Enable or disable the whole lv_task handling
* @param en: true: lv_task handling is running, false: lv_task handling is suspended
*/
void lv_task_enable(bool en)
{
lv_task_run = en;
}
/**
* Get idle percentage
* @return the lv_task idle in percentage
*/
uint8_t lv_task_get_idle(void)
{
return idle_last;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Execute task if its the priority is appropriate
* @param lv_task_p pointer to lv_task
* @return true: execute, false: not executed
*/
static bool lv_task_exec(lv_task_t * lv_task_p)
{
bool exec = false;
/*Execute if at least 'period' time elapsed*/
uint32_t elp = lv_tick_elaps(lv_task_p->last_run);
if(elp >= lv_task_p->period) {
lv_task_p->last_run = lv_tick_get();
task_deleted = false;
task_created = false;
lv_task_p->task(lv_task_p->param);
/*Delete if it was a one shot lv_task*/
if(task_deleted == false) { /*The task might be deleted by itself as well*/
if(lv_task_p->once != 0) {
lv_task_del(lv_task_p);
}
}
exec = true;
}
return exec;
}

149
src/lv_misc/lv_task.h Normal file
View File

@@ -0,0 +1,149 @@
/**
* @file lv_task.c
* An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically.
* A priority (5 levels + disable) can be assigned to lv_tasks.
*/
#ifndef LV_TASK_H
#define LV_TASK_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdint.h>
#include <stdbool.h>
#include "lv_mem.h"
#include "lv_ll.h"
/*********************
* DEFINES
*********************/
#ifndef LV_ATTRIBUTE_TASK_HANDLER
#define LV_ATTRIBUTE_TASK_HANDLER
#endif
/**********************
* TYPEDEFS
**********************/
/**
* Possible priorities for lv_tasks
*/
enum
{
LV_TASK_PRIO_OFF = 0,
LV_TASK_PRIO_LOWEST,
LV_TASK_PRIO_LOW,
LV_TASK_PRIO_MID,
LV_TASK_PRIO_HIGH,
LV_TASK_PRIO_HIGHEST,
LV_TASK_PRIO_NUM,
};
typedef uint8_t lv_task_prio_t;
/**
* Descriptor of a lv_task
*/
typedef struct
{
uint32_t period;
uint32_t last_run;
void (*task) (void*);
void * param;
uint8_t prio:3;
uint8_t once:1;
} lv_task_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Init the lv_task module
*/
void lv_task_init(void);
/**
* Call it periodically to handle lv_tasks.
*/
LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void);
/**
* Create a new lv_task
* @param task a function which is the task itself
* @param period call period in ms unit
* @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped)
* @param param free parameter
* @return pointer to the new task
*/
lv_task_t* lv_task_create(void (*task) (void *), uint32_t period, lv_task_prio_t prio, void * param);
/**
* Delete a lv_task
* @param lv_task_p pointer to task created by lv_task_p
*/
void lv_task_del(lv_task_t* lv_task_p);
/**
* Set new priority for a lv_task
* @param lv_task_p pointer to a lv_task
* @param prio the new priority
*/
void lv_task_set_prio(lv_task_t* lv_task_p, lv_task_prio_t prio);
/**
* Set new period for a lv_task
* @param lv_task_p pointer to a lv_task
* @param period the new period
*/
void lv_task_set_period(lv_task_t* lv_task_p, uint32_t period);
/**
* Make a lv_task ready. It will not wait its period.
* @param lv_task_p pointer to a lv_task.
*/
void lv_task_ready(lv_task_t* lv_task_p);
/**
* Delete the lv_task after one call
* @param lv_task_p pointer to a lv_task.
*/
void lv_task_once(lv_task_t * lv_task_p);
/**
* Reset a lv_task.
* It will be called the previously set period milliseconds later.
* @param lv_task_p pointer to a lv_task.
*/
void lv_task_reset(lv_task_t* lv_task_p);
/**
* Enable or disable the whole lv_task handling
* @param en: true: lv_task handling is running, false: lv_task handling is suspended
*/
void lv_task_enable(bool en);
/**
* Get idle percentage
* @return the lv_task idle in percentage
*/
uint8_t lv_task_get_idle(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

40
src/lv_misc/lv_templ.c Normal file
View File

@@ -0,0 +1,40 @@
/**
* @file lv_templ.c
*
*/
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* This typedef exists purely to keep -Wpedantic happy when the file is empty. */
/* It can be removed. */
typedef int keep_pedantic_happy;
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/

38
src/lv_misc/lv_templ.h Normal file
View File

@@ -0,0 +1,38 @@
/**
* @file lv_templ.h
*
*/
#ifndef LV_TEMPL_H
#define LV_TEMPL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TEMPL_H*/

792
src/lv_misc/lv_txt.c Normal file
View File

@@ -0,0 +1,792 @@
/**
* @file lv_text.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_txt.h"
#include "lv_math.h"
/*********************
* DEFINES
*********************/
#define NO_BREAK_FOUND UINT32_MAX
#ifndef LV_TXT_LINE_BREAK_LONG_LEN
#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */
#endif
#ifndef LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */
#endif
#ifndef LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool is_break_char(uint32_t letter);
#if LV_TXT_UTF8
static uint8_t lv_txt_utf8_size(const char * str);
static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni);
static uint32_t lv_txt_utf8_conv_wc(uint32_t c);
static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i);
static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i_start);
static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id);
static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id);
static uint32_t lv_txt_utf8_get_length(const char * txt);
#else
static uint8_t lv_txt_ascii_size(const char * str);
static uint32_t lv_txt_unicode_to_ascii(uint32_t letter_uni);
static uint32_t lv_txt_ascii_conv_wc(uint32_t c);
static uint32_t lv_txt_ascii_next(const char * txt, uint32_t * i);
static uint32_t lv_txt_ascii_prev(const char * txt, uint32_t * i_start);
static uint32_t lv_txt_ascii_get_byte_id(const char * txt, uint32_t utf8_id);
static uint32_t lv_txt_ascii_get_char_id(const char * txt, uint32_t byte_id);
static uint32_t lv_txt_ascii_get_length(const char * txt);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
#if LV_TXT_UTF8
uint8_t (*lv_txt_encoded_size)(const char *) = lv_txt_utf8_size;
uint32_t (*lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_utf8;
uint32_t (*lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_utf8_conv_wc;
uint32_t (*lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_utf8_next;
uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_utf8_prev;
uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_utf8_get_byte_id;
uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t) = lv_txt_utf8_get_char_id;
uint32_t (*lv_txt_get_encoded_length)(const char *) = lv_txt_utf8_get_length;
#else
uint8_t (*lv_txt_encoded_size)(const char *) = lv_txt_ascii_size;
uint32_t (*lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_ascii;
uint32_t (*lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_ascii_conv_wc;
uint32_t (*lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_ascii_next;
uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_ascii_prev;
uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_ascii_get_byte_id;
uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t) = lv_txt_ascii_get_char_id;
uint32_t (*lv_txt_get_encoded_length)(const char *) = lv_txt_ascii_get_length;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Get size of a text
* @param size_res pointer to a 'point_t' variable to store the result
* @param text pointer to a text
* @param font pinter to font of the text
* @param letter_space letter space of the text
* @param txt.line_space line space of the text
* @param flags settings for the text from 'txt_flag_t' enum
* @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks
*/
void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font,
lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t max_width, lv_txt_flag_t flag)
{
size_res->x = 0;
size_res->y = 0;
if(text == NULL) return;
if(font == NULL) return;
if(flag & LV_TXT_FLAG_EXPAND) max_width = LV_COORD_MAX;
uint32_t line_start = 0;
uint32_t new_line_start = 0;
lv_coord_t act_line_length;
uint8_t letter_height = lv_font_get_height(font);
/*Calc. the height and longest line*/
while(text[line_start] != '\0') {
new_line_start += lv_txt_get_next_line(&text[line_start], font, letter_space, max_width, flag);
size_res->y += letter_height ;
size_res->y += line_space;
/*Calculate the the longest line*/
act_line_length = lv_txt_get_width(&text[line_start], new_line_start - line_start,
font, letter_space, flag);
size_res->x = LV_MATH_MAX(act_line_length, size_res->x);
line_start = new_line_start;
}
/*Ma ke the text one line taller if the last character is '\n' or '\r'*/
if((line_start != 0) && (text[line_start - 1] == '\n' || text[line_start - 1] == '\r')) {
size_res->y += letter_height + line_space;
}
/*Correction with the last line space or set the height manually if the text is empty*/
if(size_res->y == 0) size_res->y = letter_height;
else size_res->y -= line_space;
}
/**
* Get the next line of text. Check line length and break chars too.
* @param txt a '\0' terminated string
* @param font pointer to a font
* @param letter_space letter space
* @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks
* @param flags settings for the text from 'txt_flag_type' enum
* @return the index of the first char of the new line (in byte index not letter index. With UTF-8 they are different)
*/
uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font,
lv_coord_t letter_space, lv_coord_t max_width, lv_txt_flag_t flag)
{
if(txt == NULL) return 0;
if(font == NULL) return 0;
if(flag & LV_TXT_FLAG_EXPAND) max_width = LV_COORD_MAX;
uint32_t i = 0;
lv_coord_t cur_w = 0;
lv_coord_t w_at_last_break = 0;
uint32_t n_char_since_last_break = 0; /* Used count word length of long words */
uint32_t last_break = NO_BREAK_FOUND;
lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT;
uint32_t letter = 0;
while(txt[i] != '\0') {
lv_coord_t letter_width;
letter = lv_txt_encoded_next(txt, &i);
/*Handle the recolor command*/
if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
if(lv_txt_is_cmd(&cmd_state, letter) != false) {
continue; /*Skip the letter is it is part of a command*/
}
}
/*Check for new line chars*/
if(letter == '\n' || letter == '\r') {
uint32_t i_tmp = i;
uint32_t letter_next = lv_txt_encoded_next(txt, &i_tmp);
if(letter == '\r' && letter_next == '\n') i = i_tmp;
return i; /*Return with the first letter of the next line*/
} else { /*Check the actual length*/
n_char_since_last_break++;
letter_width = lv_font_get_width(font, letter);
cur_w += letter_width;
/* Get the length of the current work and determine best place
* to break the line. */
if(cur_w > max_width) {
if( last_break != NO_BREAK_FOUND ) {
/* Continue searching for next breakable character to see if the next word will fit */
uint32_t n_char_fit = n_char_since_last_break - 1;
if( n_char_since_last_break <= LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN ) {
i = last_break;
}
else {
uint32_t i_tmp = i;
cur_w -= w_at_last_break + letter_space; /*ignore the first letter_space after the break char */
bool other = true;
while(txt[i_tmp] != '\0') {
letter = lv_txt_encoded_next(txt, &i_tmp);
/*Handle the recolor command*/
if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
if(lv_txt_is_cmd(&cmd_state, letter) != false) {
continue; /*Skip the letter is it is part of a command*/
}
}
/*Check for new line chars*/
if(letter == '\n' || letter == '\r' || is_break_char(letter)) {
if(n_char_since_last_break >= LV_TXT_LINE_BREAK_LONG_LEN) {
/* Figure out the prettiest place to break */
uint32_t char_remain;
lv_txt_encoded_prev(txt, &i);
for(char_remain=n_char_since_last_break - n_char_fit;
char_remain < LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN;
char_remain++) {
lv_txt_encoded_prev(txt, &i);
}
}
else{
i = last_break;
}
other = false;
break;
}
n_char_since_last_break++;
lv_coord_t letter_width2 = lv_font_get_width(font, letter);
cur_w += letter_width2;
if(cur_w > max_width) {
/* Current letter already exceeds, return previous */
lv_txt_encoded_prev(txt, &i);
other = false;
break;
}
if(letter_width2 > 0){
cur_w += letter_space;
}
}
if( other ) {
if(n_char_since_last_break >= LV_TXT_LINE_BREAK_LONG_LEN) {
/* Figure out the prettiest place to break */
uint32_t char_remain;
lv_txt_encoded_prev(txt, &i);
for(char_remain=n_char_since_last_break - n_char_fit;
char_remain < LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN;
char_remain++){
lv_txt_encoded_prev(txt, &i);
}
}
else{
i = last_break;
}
}
}
} else {
/* Now this character is out of the area so it will be first character of the next line*/
/* But 'i' already points to the next character (because of lv_txt_utf8_next) step beck one*/
lv_txt_encoded_prev(txt, &i);
}
/* Do not let to return without doing nothing.
* Find at least one character (Avoid infinite loop )*/
if(i == 0) lv_txt_encoded_next(txt, &i);
return i;
}
/*If this char still can fit to this line then check if
* txt can be broken here later */
else if(is_break_char(letter)) {
last_break = i; /*Save the first char index after break*/
w_at_last_break = cur_w;
if(letter_width > 0) {
w_at_last_break += letter_space;
}
n_char_since_last_break = 0;
}
}
if(letter_width > 0) {
cur_w += letter_space;
}
}
return i;
}
/**
* Give the length of a text with a given font
* @param txt a '\0' terminate string
* @param length length of 'txt' in byte count and not characters (Á is 1 character but 2 bytes in UTF-8)
* @param font pointer to a font
* @param letter_space letter space
* @param flags settings for the text from 'txt_flag_t' enum
* @return length of a char_num long text
*/
lv_coord_t lv_txt_get_width(const char * txt, uint16_t length,
const lv_font_t * font, lv_coord_t letter_space, lv_txt_flag_t flag)
{
if(txt == NULL) return 0;
if(font == NULL) return 0;
uint32_t i = 0;
lv_coord_t width = 0;
lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT;
uint32_t letter;
if(length != 0) {
while(i< length){
letter = lv_txt_encoded_next(txt, &i);
if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
if(lv_txt_is_cmd(&cmd_state, letter) != false) {
continue;
}
}
lv_coord_t char_width = lv_font_get_width(font, letter);
if(char_width > 0){
width += lv_font_get_width(font, letter);
width += letter_space;
}
}
if(width > 0) {
width -= letter_space; /*Trim the last letter space. Important if the text is center aligned */
}
}
return width;
}
/**
* Check next character in a string and decide if the character is part of the command or not
* @param state pointer to a txt_cmd_state_t variable which stores the current state of command processing
* (Initied. to TXT_CMD_STATE_WAIT )
* @param c the current character
* @return true: the character is part of a command and should not be written,
* false: the character should be written
*/
bool lv_txt_is_cmd(lv_txt_cmd_state_t * state, uint32_t c)
{
bool ret = false;
if(c == (uint32_t)LV_TXT_COLOR_CMD[0]) {
if(*state == LV_TXT_CMD_STATE_WAIT) { /*Start char*/
*state = LV_TXT_CMD_STATE_PAR;
ret = true;
} else if(*state == LV_TXT_CMD_STATE_PAR) { /*Other start char in parameter is escaped cmd. char */
*state = LV_TXT_CMD_STATE_WAIT;
} else if(*state == LV_TXT_CMD_STATE_IN) { /*Command end */
*state = LV_TXT_CMD_STATE_WAIT;
ret = true;
}
}
/*Skip the color parameter and wait the space after it*/
if(*state == LV_TXT_CMD_STATE_PAR) {
if(c == ' ') {
*state = LV_TXT_CMD_STATE_IN; /*After the parameter the text is in the command*/
}
ret = true;
}
return ret;
}
/**
* Insert a string into an other
* @param txt_buf the original text (must be big enough for the result text)
* @param pos position to insert. Expressed in character index and not byte index (Different in UTF-8)
* 0: before the original text, 1: after the first char etc.
* @param ins_txt text to insert
*/
void lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt)
{
uint32_t old_len = strlen(txt_buf);
uint32_t ins_len = strlen(ins_txt);
uint32_t new_len = ins_len + old_len;
#if LV_TXT_UTF8 != 0
pos = lv_txt_encoded_get_byte_id(txt_buf, pos); /*Convert to byte index instead of letter index*/
#endif
/*Copy the second part into the end to make place to text to insert*/
uint32_t i;
for(i = new_len; i >= pos + ins_len; i--) {
txt_buf[i] = txt_buf[i - ins_len];
}
/* Copy the text into the new space*/
memcpy(txt_buf + pos, ins_txt, ins_len);
}
/**
* Delete a part of a string
* @param txt string to modify
* @param pos position where to start the deleting (0: before the first char, 1: after the first char etc.)
* @param len number of characters to delete
*/
void lv_txt_cut(char * txt, uint32_t pos, uint32_t len)
{
uint32_t old_len = strlen(txt);
#if LV_TXT_UTF8 != 0
pos = lv_txt_encoded_get_byte_id(txt, pos); /*Convert to byte index instead of letter index*/
len = lv_txt_encoded_get_byte_id(&txt[pos], len);
#endif
/*Copy the second part into the end to make place to text to insert*/
uint32_t i;
for(i = pos; i <= old_len - len; i++) {
txt[i] = txt[i + len];
}
}
/*******************************
* UTF-8 ENCODER/DECOER
******************************/
#if LV_TXT_UTF8
/**
* Give the size of an UTF-8 coded character
* @param str pointer to a character in a string
* @return length of the UTF-8 character (1,2,3 or 4). O on invalid code
*/
static uint8_t lv_txt_utf8_size(const char * str)
{
if((str[0] & 0x80) == 0) return 1;
else if((str[0] & 0xE0) == 0xC0) return 2;
else if((str[0] & 0xF0) == 0xE0) return 3;
else if((str[0] & 0xF8) == 0xF0) return 4;
return 1; /*If the char was invalid step tell it's 1 byte long*/
}
/**
* Convert an Unicode letter to UTF-8.
* @param letter_uni an Unicode letter
* @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű')
*/
static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni)
{
if(letter_uni < 128) return letter_uni;
uint8_t bytes[4];
if(letter_uni < 0x0800) {
bytes[0] = ((letter_uni >> 6) & 0x1F) | 0xC0;
bytes[1] = ((letter_uni >> 0) & 0x3F) | 0x80;
bytes[2] = 0;
bytes[3] = 0;
} else if(letter_uni < 0x010000) {
bytes[0] = ((letter_uni >> 12) & 0x0F) | 0xE0;
bytes[1] = ((letter_uni >> 6) & 0x3F) | 0x80;
bytes[2] = ((letter_uni >> 0) & 0x3F) | 0x80;
bytes[3] = 0;
} else if(letter_uni < 0x110000) {
bytes[0] = ((letter_uni >> 18) & 0x07) | 0xF0;
bytes[1] = ((letter_uni >> 12) & 0x3F) | 0x80;
bytes[2] = ((letter_uni >> 6) & 0x3F) | 0x80;
bytes[3] = ((letter_uni >> 0) & 0x3F) | 0x80;
}
uint32_t * res_p = (uint32_t *)bytes;
return *res_p;
}
/**
* Convert a wide character, e.g. 'Á' little endian to be UTF-8 compatible
* @param c a wide character or a Little endian number
* @return `c` in big endian
*/
static uint32_t lv_txt_utf8_conv_wc(uint32_t c)
{
/*Swap the bytes (UTF-8 is big endian, but the MCUs are little endian)*/
if((c & 0x80) != 0) {
uint32_t swapped;
uint8_t c8[4];
memcpy(c8, &c, 4);
swapped = (c8[0] << 24) + (c8[1] << 16) + (c8[2] << 8) + (c8[3]);
uint8_t i;
for(i = 0; i < 4; i++) {
if((swapped & 0xFF) == 0) swapped = (swapped >> 8); /*Ignore leading zeros (they were in the end originally)*/
}
c = swapped;
}
return c;
}
/**
* Decode an UTF-8 character from a string.
* @param txt pointer to '\0' terminated string
* @param i start byte index in 'txt' where to start.
* After call it will point to the next UTF-8 char in 'txt'.
* NULL to use txt[0] as index
* @return the decoded Unicode character or 0 on invalid UTF-8 code
*/
static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i)
{
/* Unicode to UTF-8
* 00000000 00000000 00000000 0xxxxxxx -> 0xxxxxxx
* 00000000 00000000 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx
* 00000000 00000000 zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx
* 00000000 000wwwzz zzzzyyyy yyxxxxxx -> 11110www 10zzzzzz 10yyyyyy 10xxxxxx
* */
uint32_t result = 0;
/*Dummy 'i' pointer is required*/
uint32_t i_tmp = 0;
if(i == NULL) i = &i_tmp;
/*Normal ASCII*/
if((txt[*i] & 0x80) == 0) {
result = txt[*i];
(*i)++;
}
/*Real UTF-8 decode*/
else {
/*2 bytes UTF-8 code*/
if((txt[*i] & 0xE0) == 0xC0) {
result = (uint32_t)(txt[*i] & 0x1F) << 6;
(*i)++;
if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/
result += (txt[*i] & 0x3F);
(*i)++;
}
/*3 bytes UTF-8 code*/
else if((txt[*i] & 0xF0) == 0xE0) {
result = (uint32_t)(txt[*i] & 0x0F) << 12;
(*i)++;
if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/
result += (uint32_t)(txt[*i] & 0x3F) << 6;
(*i)++;
if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/
result += (txt[*i] & 0x3F);
(*i)++;
}
/*4 bytes UTF-8 code*/
else if((txt[*i] & 0xF8) == 0xF0) {
result = (uint32_t)(txt[*i] & 0x07) << 18;
(*i)++;
if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/
result += (uint32_t)(txt[*i] & 0x3F) << 12;
(*i)++;
if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/
result += (uint32_t)(txt[*i] & 0x3F) << 6;
(*i)++;
if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/
result += txt[*i] & 0x3F;
(*i)++;
} else {
(*i)++; /*Not UTF-8 char. Go the next.*/
}
}
return result;
}
/**
* Get previous UTF-8 character form a string.
* @param txt pointer to '\0' terminated string
* @param i start byte index in 'txt' where to start. After the call it will point to the previous UTF-8 char in 'txt'.
* @return the decoded Unicode character or 0 on invalid UTF-8 code
*/
static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i)
{
uint8_t c_size;
uint8_t cnt = 0;
/*Try to find a !0 long UTF-8 char by stepping one character back*/
(*i)--;
do {
if(cnt >= 4) return 0; /*No UTF-8 char found before the initial*/
c_size = lv_txt_encoded_size(&txt[*i]);
if(c_size == 0) {
if(*i != 0)(*i)--;
else return 0;
}
cnt++;
} while(c_size == 0);
uint32_t i_tmp = *i;
uint32_t letter = lv_txt_encoded_next(txt, &i_tmp); /*Character found, get it*/
return letter;
}
/**
* Convert a character index (in an UTF-8 text) to byte index.
* E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
* @param txt a '\0' terminated UTF-8 string
* @param utf8_id character index
* @return byte index of the 'utf8_id'th letter
*/
static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id)
{
uint32_t i;
uint32_t byte_cnt = 0;
for(i = 0; i < utf8_id; i++) {
byte_cnt += lv_txt_encoded_size(&txt[byte_cnt]);
}
return byte_cnt;
}
/**
* Convert a byte index (in an UTF-8 text) to character index.
* E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
* @param txt a '\0' terminated UTF-8 string
* @param byte_id byte index
* @return character index of the letter at 'byte_id'th position
*/
static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id)
{
uint32_t i = 0;
uint32_t char_cnt = 0;
while(i < byte_id) {
lv_txt_encoded_next(txt, &i); /*'i' points to the next letter so use the prev. value*/
char_cnt++;
}
return char_cnt;
}
/**
* Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled.
* E.g.: "ÁBC" is 3 characters (but 4 bytes)
* @param txt a '\0' terminated char string
* @return number of characters
*/
static uint32_t lv_txt_utf8_get_length(const char * txt)
{
#if LV_TXT_UTF8 == 0
return strlen(txt);
#else
uint32_t len = 0;
uint32_t i = 0;
while(txt[i] != '\0') {
lv_txt_encoded_next(txt, &i);
len++;
}
return len;
#endif
}
#else
/**
* Give the size of an UTF-8 coded character
* @param str pointer to a character in a string
* @return length of the UTF-8 character (1,2,3 or 4). O on invalid code
*/
static uint8_t lv_txt_ascii_size(const char * str)
{
return 1;
}
/**
* Convert an Unicode letter to UTF-8.
* @param letter_uni an Unicode letter
* @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű')
*/
static uint32_t lv_txt_unicode_to_ascii(uint32_t letter_uni)
{
if(letter_uni < 128) return letter_uni;
else return ' ';
}
/**
* Convert wide characters to ASCII, however wide characters in ASCII range (e.g. 'A') are ASCII compatible by default.
* So this function does nothing just returns with `c`.
* @param c a character, e.g. 'A'
* @return same as `c`
*/
static uint32_t lv_txt_ascii_conv_wc(uint32_t c)
{
return c;
}
/**
* Decode an UTF-8 character from a string.
* @param txt pointer to '\0' terminated string
* @param i start byte index in 'txt' where to start.
* After call it will point to the next UTF-8 char in 'txt'.
* NULL to use txt[0] as index
* @return the decoded Unicode character or 0 on invalid UTF-8 code
*/
static uint32_t lv_txt_ascii_next(const char * txt, uint32_t * i)
{
if(i == NULL) return txt[1]; /*Get the next char */
uint8_t letter = txt[*i] ;
(*i)++;
return letter;
}
/**
* Get previous UTF-8 character form a string.
* @param txt pointer to '\0' terminated string
* @param i start byte index in 'txt' where to start. After the call it will point to the previous UTF-8 char in 'txt'.
* @return the decoded Unicode character or 0 on invalid UTF-8 code
*/
static uint32_t lv_txt_ascii_prev(const char * txt, uint32_t * i)
{
if(i == NULL) return *(txt - 1); /*Get the prev. char */
(*i)--;
uint8_t letter = txt[*i] ;
return letter;
}
/**
* Convert a character index (in an UTF-8 text) to byte index.
* E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
* @param txt a '\0' terminated UTF-8 string
* @param utf8_id character index
* @return byte index of the 'utf8_id'th letter
*/
static uint32_t lv_txt_ascii_get_byte_id(const char * txt, uint32_t utf8_id)
{
return utf8_id; /*In Non encoded no difference*/
}
/**
* Convert a byte index (in an UTF-8 text) to character index.
* E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long
* @param txt a '\0' terminated UTF-8 string
* @param byte_id byte index
* @return character index of the letter at 'byte_id'th position
*/
static uint32_t lv_txt_ascii_get_char_id(const char * txt, uint32_t byte_id)
{
return byte_id; /*In Non encoded no difference*/
}
/**
* Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled.
* E.g.: "ÁBC" is 3 characters (but 4 bytes)
* @param txt a '\0' terminated char string
* @return number of characters
*/
static uint32_t lv_txt_ascii_get_length(const char * txt)
{
return strlen(txt);
}
#endif
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Test if char is break char or not (a text can broken here or not)
* @param letter a letter
* @return false: 'letter' is not break char
*/
static bool is_break_char(uint32_t letter)
{
uint8_t i;
bool ret = false;
/*Compare the letter to TXT_BREAK_CHARS*/
for(i = 0; LV_TXT_BREAK_CHARS[i] != '\0'; i++) {
if(letter == (uint32_t)LV_TXT_BREAK_CHARS[i]) {
ret = true; /*If match then it is break char*/
break;
}
}
return ret;
}

198
src/lv_misc/lv_txt.h Normal file
View File

@@ -0,0 +1,198 @@
/**
* @file lv_text.h
*
*/
#ifndef LV_TXT_H
#define LV_TXT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#include <stdbool.h>
#include "lv_area.h"
#include "lv_font.h"
#include "lv_area.h"
/*********************
* DEFINES
*********************/
#define LV_TXT_COLOR_CMD "#"
/**********************
* TYPEDEFS
**********************/
enum
{
LV_TXT_FLAG_NONE = 0x00,
LV_TXT_FLAG_RECOLOR = 0x01, /*Enable parsing of recolor command*/
LV_TXT_FLAG_EXPAND = 0x02, /*Ignore width to avoid automatic word wrapping*/
LV_TXT_FLAG_CENTER = 0x04, /*Align the text to the middle*/
LV_TXT_FLAG_RIGHT = 0x08, /*Align the text to the right*/
};
typedef uint8_t lv_txt_flag_t;
enum
{
LV_TXT_CMD_STATE_WAIT, /*Waiting for command*/
LV_TXT_CMD_STATE_PAR, /*Processing the parameter*/
LV_TXT_CMD_STATE_IN, /*Processing the command*/
};
typedef uint8_t lv_txt_cmd_state_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get size of a text
* @param size_res pointer to a 'point_t' variable to store the result
* @param text pointer to a text
* @param font pinter to font of the text
* @param letter_space letter space of the text
* @param line_space line space of the text
* @param flags settings for the text from 'txt_flag_t' enum
* @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks
*/
void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font,
lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t max_width, lv_txt_flag_t flag);
/**
* Get the next line of text. Check line length and break chars too.
* @param txt a '\0' terminated string
* @param font pointer to a font
* @param letter_space letter space
* @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks
* @param flags settings for the text from 'txt_flag_type' enum
* @return the index of the first char of the new line (in byte index not letter index. With UTF-8 they are different)
*/
uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font,
lv_coord_t letter_space, lv_coord_t max_width, lv_txt_flag_t flag);
/**
* Give the length of a text with a given font
* @param txt a '\0' terminate string
* @param length length of 'txt' in byte count and not characters (Á is 1 character but 2 bytes in UTF-8)
* @param font pointer to a font
* @param letter_space letter space
* @param flags settings for the text from 'txt_flag_t' enum
* @return length of a char_num long text
*/
lv_coord_t lv_txt_get_width(const char * txt, uint16_t length,
const lv_font_t * font, lv_coord_t letter_space, lv_txt_flag_t flag);
/**
* Check next character in a string and decide if te character is part of the command or not
* @param state pointer to a txt_cmd_state_t variable which stores the current state of command processing
* @param c the current character
* @return true: the character is part of a command and should not be written,
* false: the character should be written
*/
bool lv_txt_is_cmd(lv_txt_cmd_state_t * state, uint32_t c);
/**
* Insert a string into an other
* @param txt_buf the original text (must be big enough for the result text)
* @param pos position to insert (0: before the original text, 1: after the first char etc.)
* @param ins_txt text to insert
*/
void lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt);
/**
* Delete a part of a string
* @param txt string to modify
* @param pos position where to start the deleting (0: before the first char, 1: after the first char etc.)
* @param len number of characters to delete
*/
void lv_txt_cut(char * txt, uint32_t pos, uint32_t len);
/***************************************************************
* GLOBAL FUNCTION POINTERS FOR CAHRACTER ENCODING INTERFACE
***************************************************************/
/**
* Give the size of an encoded character
* @param str pointer to a character in a string
* @return length of the encoded character (1,2,3 ...). O in invalid
*/
extern uint8_t (*lv_txt_encoded_size)(const char *);
/**
* Convert an Unicode letter to encoded
* @param letter_uni an Unicode letter
* @return Encoded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ü')
*/
extern uint32_t (*lv_txt_unicode_to_encoded)(uint32_t );
/**
* Convert a wide character, e.g. 'Á' little endian to be compatible with the encoded format.
* @param c a wide character
* @return `c` in the encoded format
*/
extern uint32_t (*lv_txt_encoded_conv_wc) (uint32_t c);
/**
* Decode the next encoded character from a string.
* @param txt pointer to '\0' terminated string
* @param i start index in 'txt' where to start.
* After the call it will point to the next encoded char in 'txt'.
* NULL to use txt[0] as index
* @return the decoded Unicode character or 0 on invalid data code
*/
extern uint32_t (*lv_txt_encoded_next)(const char *, uint32_t * );
/**
* Get the previous encoded character form a string.
* @param txt pointer to '\0' terminated string
* @param i_start index in 'txt' where to start. After the call it will point to the previous encoded char in 'txt'.
* @return the decoded Unicode character or 0 on invalid data
*/
extern uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *);
/**
* Convert a letter index (in an the encoded text) to byte index.
* E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long
* @param txt a '\0' terminated UTF-8 string
* @param enc_id letter index
* @return byte index of the 'enc_id'th letter
*/
extern uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t);
/**
* Convert a byte index (in an encoded text) to character index.
* E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long
* @param txt a '\0' terminated UTF-8 string
* @param byte_id byte index
* @return character index of the letter at 'byte_id'th position
*/
extern uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t);
/**
* Get the number of characters (and NOT bytes) in a string.
* E.g. in UTF-8 "ÁBC" is 3 characters (but 4 bytes)
* @param txt a '\0' terminated char string
* @return number of characters
*/
extern uint32_t (*lv_txt_get_encoded_length)(const char *);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*USE_TXT*/

310
src/lv_objx/lv_arc.c Normal file
View File

@@ -0,0 +1,310 @@
/**
* @file lv_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_arc.h"
#if LV_USE_ARC != 0
#include "../lv_misc/lv_math.h"
#include "../lv_draw/lv_draw_arc.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_arc_design(lv_obj_t * arc, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_cb_t ancestor_signal;
static lv_design_cb_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a arc object
* @param par pointer to an object, it will be the parent of the new arc
* @param copy pointer to a arc object, if not NULL then the new object will be copied from it
* @return pointer to the created arc
*/
lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("arc create started");
/*Create the ancestor of arc*/
lv_obj_t * new_arc = lv_obj_create(par, copy);
lv_mem_assert(new_arc);
if(new_arc == NULL) return NULL;
/*Allocate the arc type specific extended data*/
lv_arc_ext_t * ext = lv_obj_allocate_ext_attr(new_arc, sizeof(lv_arc_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_arc);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_arc);
/*Initialize the allocated 'ext' */
ext->angle_start = 45;
ext->angle_end = 315;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_cb(new_arc, lv_arc_signal);
lv_obj_set_design_cb(new_arc, lv_arc_design);
/*Init the new arc arc*/
if(copy == NULL) {
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_arc_set_style(new_arc, LV_ARC_STYLE_MAIN, th->style.arc);
} else {
lv_arc_set_style(new_arc, LV_ARC_STYLE_MAIN, &lv_style_plain_color);
}
}
/*Copy an existing arc*/
else {
lv_arc_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->angle_start = copy_ext->angle_start;
ext->angle_end = copy_ext->angle_end;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_arc);
}
LV_LOG_INFO("arc created");
return new_arc;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
/**
* Set the start and end angles of an arc. 0 deg: bottom, 90 deg: right etc.
* @param arc pointer to an arc object
* @param start the start angle [0..360]
* @param end the end angle [0..360]
*/
void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end)
{
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
if(start > 360) start = 360;
if(end > 360) end = 360;
ext->angle_start = start;
ext->angle_end = end;
lv_obj_invalidate(arc);
}
/**
* Set a style of a arc.
* @param arc pointer to arc object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_arc_set_style(lv_obj_t * arc, lv_arc_style_t type, lv_style_t * style)
{
switch(type) {
case LV_ARC_STYLE_MAIN:
lv_obj_set_style(arc, style);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the start angle of an arc.
* @param arc pointer to an arc object
* @return the start angle [0..360]
*/
uint16_t lv_arc_get_angle_start(lv_obj_t * arc)
{
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
return ext->angle_start;
}
/**
* Get the end angle of an arc.
* @param arc pointer to an arc object
* @return the end angle [0..360]
*/
uint16_t lv_arc_get_angle_end(lv_obj_t * arc)
{
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
return ext->angle_end;
}
/**
* Get style of a arc.
* @param arc pointer to arc object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_arc_get_style(const lv_obj_t * arc, lv_arc_style_t type)
{
lv_style_t * style = NULL;
switch(type) {
case LV_ARC_STYLE_MAIN:
style = lv_obj_get_style(arc);
break;
default:
style = NULL;
break;
}
return style;
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the arcs
* @param arc pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_arc_design(lv_obj_t * arc, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
lv_style_t * style = lv_arc_get_style(arc, LV_ARC_STYLE_MAIN);
lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(arc), lv_obj_get_height(arc))) / 2;
lv_coord_t x = arc->coords.x1 + lv_obj_get_width(arc) / 2;
lv_coord_t y = arc->coords.y1 + lv_obj_get_height(arc) / 2;
lv_opa_t opa_scale = lv_obj_get_opa_scale(arc);
lv_draw_arc(x, y, r, mask, ext->angle_start, ext->angle_end, style, opa_scale);
/*Draw circle on the ends if enabled */
if(style->line.rounded) {
lv_coord_t thick_half = style->line.width / 2;
lv_coord_t cir_x = ((r - thick_half) * lv_trigo_sin(ext->angle_start) >> LV_TRIGO_SHIFT);
lv_coord_t cir_y = ((r - thick_half) * lv_trigo_sin(ext->angle_start + 90) >> LV_TRIGO_SHIFT);
lv_style_t cir_style;
lv_style_copy(&cir_style, &lv_style_plain);
cir_style.body.grad_color = style->line.color;
cir_style.body.main_color = cir_style.body.grad_color;
cir_style.body.radius = LV_RADIUS_CIRCLE;
lv_area_t cir_area;
cir_area.x1 = cir_x + x - thick_half;
cir_area.y1 = cir_y + y - thick_half;
cir_area.x2 = cir_x + x + thick_half;
cir_area.y2 = cir_y + y + thick_half;
lv_draw_rect(&cir_area, mask, &cir_style, opa_scale);
cir_x = ((r - thick_half) * lv_trigo_sin(ext->angle_end) >> LV_TRIGO_SHIFT);
cir_y = ((r - thick_half) * lv_trigo_sin(ext->angle_end + 90) >> LV_TRIGO_SHIFT);
cir_area.x1 = cir_x + x - thick_half;
cir_area.y1 = cir_y + y - thick_half;
cir_area.x2 = cir_x + x + thick_half;
cir_area.y2 = cir_y + y + thick_half;
lv_draw_rect(&cir_area, mask, &cir_style, opa_scale);
}
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the arc
* @param arc pointer to a arc object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(arc, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_arc";
}
return res;
}
#endif

127
src/lv_objx/lv_arc.h Normal file
View File

@@ -0,0 +1,127 @@
/**
* @file lv_arc.h
*
*/
#ifndef LV_ARC_H
#define LV_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_ARC != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of arc*/
typedef struct {
/*New data for this type */
lv_coord_t angle_start;
lv_coord_t angle_end;
} lv_arc_ext_t;
/*Styles*/
enum {
LV_ARC_STYLE_MAIN,
};
typedef uint8_t lv_arc_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a arc objects
* @param par pointer to an object, it will be the parent of the new arc
* @param copy pointer to a arc object, if not NULL then the new object will be copied from it
* @return pointer to the created arc
*/
lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the start and end angles of an arc. 0 deg: bottom, 90 deg: right etc.
* @param arc pointer to an arc object
* @param start the start angle [0..360]
* @param end the end angle [0..360]
*/
void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end);
/**
* Set a style of a arc.
* @param arc pointer to arc object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_arc_set_style(lv_obj_t * arc, lv_arc_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the start angle of an arc.
* @param arc pointer to an arc object
* @return the start angle [0..360]
*/
uint16_t lv_arc_get_angle_start(lv_obj_t * arc);
/**
* Get the end angle of an arc.
* @param arc pointer to an arc object
* @return the end angle [0..360]
*/
uint16_t lv_arc_get_angle_end(lv_obj_t * arc);
/**
* Get style of a arc.
* @param arc pointer to arc object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_arc_get_style(const lv_obj_t * arc, lv_arc_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_ARC*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ARC_H*/

479
src/lv_objx/lv_bar.c Normal file
View File

@@ -0,0 +1,479 @@
/**
* @file lv_bar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_bar.h"
#if LV_USE_BAR != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_anim.h"
#include <stdio.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param);
static void lv_bar_animate(void * bar, int32_t value);
static void lv_bar_anim_ready(void * bar);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_cb_t ancestor_design_f;
static lv_signal_cb_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a bar objects
* @param par pointer to an object, it will be the parent of the new bar
* @param copy pointer to a bar object, if not NULL then the new object will be copied from it
* @return pointer to the created bar
*/
lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("lv_bar create started");
/*Create the ancestor basic object*/
lv_obj_t * new_bar = lv_obj_create(par, copy);
lv_mem_assert(new_bar);
if(new_bar == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_bar);
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_bar);
/*Allocate the object type specific extended data*/
lv_bar_ext_t * ext = lv_obj_allocate_ext_attr(new_bar, sizeof(lv_bar_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->min_value = 0;
ext->max_value = 100;
ext->cur_value = 0;
ext->anim_time = 200;
ext->anim_start = 0;
ext->anim_end = 0;
ext->anim_state = LV_BAR_ANIM_STATE_INV;
ext->sym = 0;
ext->style_indic = &lv_style_pretty_color;
lv_obj_set_signal_cb(new_bar, lv_bar_signal);
lv_obj_set_design_cb(new_bar, lv_bar_design);
/*Init the new bar object*/
if(copy == NULL) {
lv_obj_set_click(new_bar, false);
lv_obj_set_size(new_bar, LV_DPI * 2, LV_DPI / 3);
lv_bar_set_value(new_bar, ext->cur_value, false);
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_bar_set_style(new_bar, LV_BAR_STYLE_BG, th->style.bar.bg);
lv_bar_set_style(new_bar, LV_BAR_STYLE_INDIC, th->style.bar.indic);
} else {
lv_obj_set_style(new_bar, &lv_style_pretty);
}
} else {
lv_bar_ext_t * ext_copy = lv_obj_get_ext_attr(copy);
ext->min_value = ext_copy->min_value;
ext->max_value = ext_copy->max_value;
ext->cur_value = ext_copy->cur_value;
ext->style_indic = ext_copy->style_indic;
ext->sym = ext_copy->sym;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_bar);
lv_bar_set_value(new_bar, ext->cur_value, false);
}
LV_LOG_INFO("bar created");
return new_bar;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the bar
* @param bar pointer to a bar object
* @param value new value
* @param anim true: set the value with an animation; false: change the value immediatelly
*/
void lv_bar_set_value(lv_obj_t * bar, int16_t value, bool anim)
{
#if LV_USE_ANIMATION == 0
anim = false;
#endif
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->cur_value == value) return;
int16_t new_value;
new_value = value > ext->max_value ? ext->max_value : value;
new_value = new_value < ext->min_value ? ext->min_value : new_value;
if(ext->cur_value == new_value) return;
if(anim == false) {
ext->cur_value = new_value;
lv_obj_invalidate(bar);
} else {
#if LV_USE_ANIMATION
/*No animation in progress -> simply set the values*/
if(ext->anim_state == LV_BAR_ANIM_STATE_INV) {
ext->anim_start = ext->cur_value;
ext->anim_end = new_value;
}
/*Animation in progress. Start from the animation end value*/
else {
ext->anim_start = ext->anim_end;
ext->anim_end = new_value;
}
lv_anim_t a;
a.var = bar;
a.start = LV_BAR_ANIM_STATE_START;
a.end = LV_BAR_ANIM_STATE_END;
a.fp = (lv_anim_fp_t)lv_bar_animate;
a.path = lv_anim_path_linear;
a.end_cb = lv_bar_anim_ready;
a.act_time = 0;
a.time = ext->anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
#endif
}
}
/**
* Set minimum and the maximum values of a bar
* @param bar pointer to the bar object
* @param min minimum value
* @param max maximum value
*/
void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->min_value == min && ext->max_value == max) return;
ext->max_value = max;
ext->min_value = min;
if(ext->cur_value > max) {
ext->cur_value = max;
lv_bar_set_value(bar, ext->cur_value, false);
}
if(ext->cur_value < min) {
ext->cur_value = min;
lv_bar_set_value(bar, ext->cur_value, false);
}
lv_obj_invalidate(bar);
}
/**
* Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum position.
* @param bar pointer to a bar object
* @param en true: enable disable symmetric behavior; false: disable
*/
void lv_bar_set_sym(lv_obj_t * bar, bool en)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
ext->sym = en ? 1 : 0;
}
/**
* Set a style of a bar
* @param bar pointer to a bar object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_bar_set_style(lv_obj_t * bar, lv_bar_style_t type, lv_style_t * style)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
switch(type) {
case LV_BAR_STYLE_BG:
lv_obj_set_style(bar, style);
break;
case LV_BAR_STYLE_INDIC:
ext->style_indic = style;
lv_obj_refresh_ext_size(bar);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a bar
* @param bar pointer to a bar object
* @return the value of the bar
*/
int16_t lv_bar_get_value(const lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
/*If animated tell that it's already at the end value*/
if(ext->anim_state != LV_BAR_ANIM_STATE_INV) return ext->anim_end;
/*No animation, simple return the current value*/
else return ext->cur_value;
}
/**
* Get the minimum value of a bar
* @param bar pointer to a bar object
* @return the minimum value of the bar
*/
int16_t lv_bar_get_min_value(const lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->min_value;
}
/**
* Get the maximum value of a bar
* @param bar pointer to a bar object
* @return the maximum value of the bar
*/
int16_t lv_bar_get_max_value(const lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->max_value;
}
/**
* Get whether the bar is symmetric or not.
* @param bar pointer to a bar object
* @return true: symmetric is enabled; false: disable
*/
bool lv_bar_get_sym(lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->sym ? true : false;
}
/**
* Get a style of a bar
* @param bar pointer to a bar object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_bar_get_style(const lv_obj_t * bar, lv_bar_style_t type)
{
lv_style_t * style = NULL;
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
switch(type) {
case LV_BAR_STYLE_BG:
style = lv_obj_get_style(bar);
break;
case LV_BAR_STYLE_INDIC:
style = ext->style_indic;
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the bars
* @param bar pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
/*Return false if the object is not covers the mask area*/
return ancestor_design_f(bar, mask, mode);
} else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_opa_t opa_scale = lv_obj_get_opa_scale(bar);
#if LV_USE_GROUP == 0
ancestor_design_f(bar, mask, mode);
#else
/* Draw the borders later if the bar is focused.
* At value = 100% the indicator can cover to whole background and the focused style won't be visible*/
if(lv_obj_is_focused(bar)) {
lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG);
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style_bg);
style_tmp.body.border.width = 0;
lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale);
} else {
ancestor_design_f(bar, mask, mode);
}
#endif
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->cur_value != ext->min_value || ext->sym || ext->anim_start != LV_BAR_ANIM_STATE_INV) {
lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC);
lv_area_t indic_area;
lv_area_copy(&indic_area, &bar->coords);
indic_area.x1 += style_indic->body.padding.left;
indic_area.x2 -= style_indic->body.padding.right;
indic_area.y1 += style_indic->body.padding.top;
indic_area.y2 -= style_indic->body.padding.bottom;
lv_coord_t w = lv_area_get_width(&indic_area);
lv_coord_t h = lv_area_get_height(&indic_area);
if(w >= h) {
/*Horizontal*/
if(ext->anim_state != LV_BAR_ANIM_STATE_INV) {
/*Calculate the coordinates of anim. start and end*/
lv_coord_t anim_start_x = (int32_t)((int32_t)w * (ext->anim_start - ext->min_value)) / (ext->max_value - ext->min_value);
lv_coord_t anim_end_x = (int32_t)((int32_t)w * (ext->anim_end - ext->min_value)) / (ext->max_value - ext->min_value);
/*Calculate the real position based on `anim_state` (between `anim_start` and `anim_end`)*/
indic_area.x2 = anim_start_x + (((anim_end_x - anim_start_x) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM);
} else {
indic_area.x2 = (int32_t)((int32_t)w * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value);
}
indic_area.x2 = indic_area.x1 + indic_area.x2 - 1;
if(ext->sym && ext->min_value < 0 && ext->max_value > 0) {
/*Calculate the coordinate of the zero point*/
lv_coord_t zero;
zero = indic_area.x1 + (-ext->min_value * w) / (ext->max_value - ext->min_value);
if(indic_area.x2 > zero) indic_area.x1 = zero;
else {
indic_area.x1 = indic_area.x2;
indic_area.x2 = zero;
}
}
} else {
if(ext->anim_state != LV_BAR_ANIM_STATE_INV) {
/*Calculate the coordinates of anim. start and end*/
lv_coord_t anim_start_y = (int32_t)((int32_t)h * (ext->anim_start - ext->min_value)) / (ext->max_value - ext->min_value);
lv_coord_t anim_end_y = (int32_t)((int32_t)h * (ext->anim_end - ext->min_value)) / (ext->max_value - ext->min_value);
/*Calculate the real position based on `anim_state` (between `anim_start` and `anim_end`)*/
indic_area.y1 = anim_start_y + (((anim_end_y - anim_start_y) * ext->anim_state) >> LV_BAR_ANIM_STATE_NORM);
} else {
indic_area.y1 = (int32_t)((int32_t)h * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value);
}
indic_area.y1 = indic_area.y2 - indic_area.y1 + 1;
if(ext->sym && ext->min_value < 0 && ext->max_value > 0) {
/*Calculate the coordinate of the zero point*/
lv_coord_t zero;
zero = indic_area.y2 - (-ext->min_value * h) / (ext->max_value - ext->min_value);
if(indic_area.y1 < zero) indic_area.y2 = zero;
else {
indic_area.y2 = indic_area.y1;
indic_area.y1 = zero;
}
}
}
/*Draw the indicator*/
lv_draw_rect(&indic_area, mask, style_indic, opa_scale);
}
} else if(mode == LV_DESIGN_DRAW_POST) {
#if LV_USE_GROUP
/*Draw the border*/
if(lv_obj_is_focused(bar)) {
lv_opa_t opa_scale = lv_obj_get_opa_scale(bar);
lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG);
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style_bg);
style_tmp.body.opa = LV_OPA_TRANSP;
style_tmp.body.shadow.width = 0;
lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale);
}
#endif
}
return true;
}
/**
* Signal function of the bar
* @param bar pointer to a bar object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(bar, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC);
if(style_indic->body.shadow.width > bar->ext_size) bar->ext_size = style_indic->body.shadow.width;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_bar";
}
return res;
}
static void lv_bar_animate(void * bar, int32_t value)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
ext->anim_state = value;
lv_obj_invalidate(bar);
}
static void lv_bar_anim_ready(void * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
ext->anim_state = LV_BAR_ANIM_STATE_INV;
lv_bar_set_value(bar, ext->anim_end, false);
}
#endif

160
src/lv_objx/lv_bar.h Normal file
View File

@@ -0,0 +1,160 @@
/**
* @file lv_bar.h
*
*/
#ifndef LV_BAR_H
#define LV_BAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_BAR != 0
#include "../lv_core/lv_obj.h"
#include "lv_cont.h"
#include "lv_btn.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
#define LV_BAR_ANIM_STATE_START 0
#define LV_BAR_ANIM_STATE_END 256
#define LV_BAR_ANIM_STATE_INV -1
#define LV_BAR_ANIM_STATE_NORM 8 /*log2(LV_BAR_ANIM_STATE_END) used to normalize data*/
/**********************
* TYPEDEFS
**********************/
/*Data of bar*/
typedef struct
{
/*No inherited ext*/ /*Ext. of ancestor*/
/*New data for this type */
int16_t cur_value; /*Current value of the bar*/
int16_t min_value; /*Minimum value of the bar*/
int16_t max_value; /*Maximum value of the bar*/
int16_t anim_start;
int16_t anim_end;
int16_t anim_state;
uint16_t anim_time;
uint8_t sym :1; /*Symmetric: means the center is around zero value*/
lv_style_t *style_indic; /*Style of the indicator*/
} lv_bar_ext_t;
enum {
LV_BAR_STYLE_BG,
LV_BAR_STYLE_INDIC,
};
typedef uint8_t lv_bar_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a bar objects
* @param par pointer to an object, it will be the parent of the new bar
* @param copy pointer to a bar object, if not NULL then the new object will be copied from it
* @return pointer to the created bar
*/
lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the bar
* @param bar pointer to a bar object
* @param value new value
* @param anim true: set the value with an animation; false: change the value immediatelly
*/
void lv_bar_set_value(lv_obj_t * bar, int16_t value, bool anim);
/**
* Set minimum and the maximum values of a bar
* @param bar pointer to the bar object
* @param min minimum value
* @param max maximum value
*/
void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max);
/**
* Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum position.
* @param bar pointer to a bar object
* @param en true: enable disable symmetric behavior; false: disable
*/
void lv_bar_set_sym(lv_obj_t * bar, bool en);
/**
* Set a style of a bar
* @param bar pointer to a bar object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_bar_set_style(lv_obj_t *bar, lv_bar_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a bar
* @param bar pointer to a bar object
* @return the value of the bar
*/
int16_t lv_bar_get_value(const lv_obj_t * bar);
/**
* Get the minimum value of a bar
* @param bar pointer to a bar object
* @return the minimum value of the bar
*/
int16_t lv_bar_get_min_value(const lv_obj_t * bar);
/**
* Get the maximum value of a bar
* @param bar pointer to a bar object
* @return the maximum value of the bar
*/
int16_t lv_bar_get_max_value(const lv_obj_t * bar);
/**
* Get whether the bar is symmetric or not.
* @param bar pointer to a bar object
* @return true: symmetric is enabled; false: disable
*/
bool lv_bar_get_sym(lv_obj_t * bar);
/**
* Get a style of a bar
* @param bar pointer to a bar object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_bar_get_style(const lv_obj_t *bar, lv_bar_style_t type);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BAR*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BAR_H*/

705
src/lv_objx/lv_btn.c Normal file
View File

@@ -0,0 +1,705 @@
/**
* @file lv_btn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_btn.h"
#if LV_USE_BTN != 0
#include <string.h>
#include "../lv_core/lv_group.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_BTN_INK_VALUE_MAX 256
#define LV_BTN_INK_VALUE_MAX_SHIFT 8
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param);
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
static void lv_btn_ink_effect_anim(lv_obj_t * btn, int32_t val);
static void lv_btn_ink_effect_anim_ready(void * p);
#endif
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_cb_t ancestor_signal;
static lv_design_cb_t ancestor_design;
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
static lv_coord_t ink_act_value;
static lv_obj_t * ink_obj;
static lv_btn_state_t ink_bg_state;
static lv_btn_state_t ink_top_state;
static bool ink_ready;
static bool ink_playback;
static lv_point_t ink_point;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a button objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
* @return pointer to the created button
*/
lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("button create started");
lv_obj_t * new_btn;
new_btn = lv_cont_create(par, copy);
lv_mem_assert(new_btn);
if(new_btn == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_btn);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_btn);
/*Allocate the extended data*/
lv_btn_ext_t * ext = lv_obj_allocate_ext_attr(new_btn, sizeof(lv_btn_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->state = LV_BTN_STATE_REL;
ext->styles[LV_BTN_STATE_REL] = &lv_style_btn_rel;
ext->styles[LV_BTN_STATE_PR] = &lv_style_btn_pr;
ext->styles[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel;
ext->styles[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr;
ext->styles[LV_BTN_STATE_INA] = &lv_style_btn_ina;
ext->toggle = 0;
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
ext->ink_in_time = 0;
ext->ink_wait_time = 0;
ext->ink_out_time = 0;
#endif
lv_obj_set_signal_cb(new_btn, lv_btn_signal);
lv_obj_set_design_cb(new_btn, lv_btn_design);
/*If no copy do the basic initialization*/
if(copy == NULL) {
/*Set layout if the button is not a screen*/
if(par != NULL) {
lv_btn_set_layout(new_btn, LV_LAYOUT_CENTER);
}
lv_obj_set_click(new_btn, true); /*Be sure the button is clickable*/
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_btn_set_style(new_btn, LV_BTN_STYLE_REL, th->style.btn.rel);
lv_btn_set_style(new_btn, LV_BTN_STYLE_PR, th->style.btn.pr);
lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_REL, th->style.btn.tgl_rel);
lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_PR, th->style.btn.tgl_pr);
lv_btn_set_style(new_btn, LV_BTN_STYLE_INA, th->style.btn.ina);
} else {
lv_obj_set_style(new_btn, ext->styles[LV_BTN_STATE_REL]);
}
}
/*Copy 'copy'*/
else {
lv_btn_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->state = copy_ext->state;
ext->toggle = copy_ext->toggle;
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
ext->ink_in_time = copy_ext->ink_in_time;
ext->ink_wait_time = copy_ext->ink_wait_time;
ext->ink_out_time = copy_ext->ink_out_time;
#endif
memcpy(ext->styles, copy_ext->styles, sizeof(ext->styles));
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_btn);
}
LV_LOG_INFO("button created");
return new_btn;
}
/*=====================
* Setter functions
*====================*/
/**
* Enable the toggled states
* @param btn pointer to a button object
* @param tgl true: enable toggled states, false: disable
*/
void lv_btn_set_toggle(lv_obj_t * btn, bool tgl)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->toggle = tgl != false ? 1 : 0;
}
/**
* Set the state of the button
* @param btn pointer to a button object
* @param state the new state of the button (from lv_btn_state_t enum)
*/
void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
if(ext->state != state) {
ext->state = state;
lv_obj_set_style(btn, ext->styles[state]);
}
}
/**
* Toggle the state of the button (ON->OFF, OFF->ON)
* @param btn pointer to a button object
*/
void lv_btn_toggle(lv_obj_t * btn)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
switch(ext->state) {
case LV_BTN_STATE_REL:
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
break;
case LV_BTN_STATE_PR:
lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
break;
case LV_BTN_STATE_TGL_REL:
lv_btn_set_state(btn, LV_BTN_STATE_REL);
break;
case LV_BTN_STATE_TGL_PR:
lv_btn_set_state(btn, LV_BTN_STATE_PR);
break;
default:
break;
}
}
/**
* Set time of the ink effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time)
{
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->ink_in_time = time;
#else
(void)btn; /*Unused*/
(void)time; /*Unused*/
LV_LOG_WARN("`lv_btn_set_ink_ink_time` has no effect if LV_BTN_INK_EFEFCT or LV_USE_ANIMATION is disabled")
#endif
}
/**
* Set the wait time before the ink disappears
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time)
{
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->ink_wait_time = time;
#else
(void)btn; /*Unused*/
(void)time; /*Unused*/
LV_LOG_WARN("`lv_btn_set_ink_wait_time` has no effect if LV_BTN_INK_EFEFCT or LV_USE_ANIMATION is disabled")
#endif
}
/**
* Set time of the ink out effect (animate to the released state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time)
{
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->ink_out_time = time;
#else
(void)btn; /*Unused*/
(void)time; /*Unused*/
LV_LOG_WARN("`lv_btn_set_ink_out_time` has no effect if LV_BTN_INK_EFEFCT or LV_USE_ANIMATION is disabled")
#endif
}
/**
* Set a style of a button
* @param btn pointer to a button object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, lv_style_t * style)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
switch(type) {
case LV_BTN_STYLE_REL:
ext->styles[LV_BTN_STATE_REL] = style;
break;
case LV_BTN_STYLE_PR:
ext->styles[LV_BTN_STATE_PR] = style;
break;
case LV_BTN_STYLE_TGL_REL:
ext->styles[LV_BTN_STATE_TGL_REL] = style;
break;
case LV_BTN_STYLE_TGL_PR:
ext->styles[LV_BTN_STATE_TGL_PR] = style;
break;
case LV_BTN_STYLE_INA:
ext->styles[LV_BTN_STATE_INA] = style;
break;
}
/*Refresh the object with the new style*/
lv_obj_set_style(btn, ext->styles[ext->state]);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current state of the button
* @param btn pointer to a button object
* @return the state of the button (from lv_btn_state_t enum)
*/
lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->state;
}
/**
* Get the toggle enable attribute of the button
* @param btn pointer to a button object
* @return ture: toggle enabled, false: disabled
*/
bool lv_btn_get_toggle(const lv_obj_t * btn)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->toggle != 0 ? true : false;
}
/**
* Get time of the ink in effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn)
{
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->ink_in_time;
#else
(void)btn; /*Unused*/
return 0;
#endif
}
/**
* Get the wait time before the ink disappears
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn)
{
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->ink_wait_time;
#else
(void)btn; /*Unused*/
return 0;
#endif
}
/**
* Get time of the ink out effect (animate to the releases state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn)
{
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->ink_in_time;
#else
(void)btn; /*Unused*/
return 0;
#endif
}
/**
* Get a style of a button
* @param btn pointer to a button object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type)
{
lv_style_t * style = NULL;
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
switch(type) {
case LV_BTN_STYLE_REL:
style = ext->styles[LV_BTN_STATE_REL];
break;
case LV_BTN_STYLE_PR:
style = ext->styles[LV_BTN_STATE_PR];
break;
case LV_BTN_STYLE_TGL_REL:
style = ext->styles[LV_BTN_STATE_TGL_REL];
break;
case LV_BTN_STYLE_TGL_PR:
style = ext->styles[LV_BTN_STATE_TGL_PR];
break;
case LV_BTN_STYLE_INA:
style = ext->styles[LV_BTN_STATE_INA];
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the drop down lists
* @param btn pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
return false;
} else if(mode == LV_DESIGN_DRAW_MAIN) {
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
if(btn != ink_obj) {
ancestor_design(btn, mask, mode);
} else {
lv_opa_t opa_scale = lv_obj_get_opa_scale(btn);
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
/*Draw the normal button*/
if(ink_playback == false) {
lv_style_t style_tmp;
lv_style_copy(&style_tmp, ext->styles[ink_bg_state]);
style_tmp.body.shadow.width = ext->styles[ink_top_state]->body.shadow.width;
lv_draw_rect(&btn->coords, mask, &style_tmp, opa_scale);
lv_coord_t w = lv_obj_get_width(btn);
lv_coord_t h = lv_obj_get_height(btn);
lv_coord_t r_max = LV_MATH_MIN(w, h) / 2;
/*In the first part of the animation increase the size of the circle (ink effect) */
lv_area_t cir_area;
lv_coord_t coord_state = ink_act_value < LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value : LV_BTN_INK_VALUE_MAX / 2;
lv_point_t p_act;
p_act.x = ink_point.x;
p_act.y = ink_point.y;
lv_coord_t x_err = (btn->coords.x1 + w / 2) - p_act.x;
lv_coord_t y_err = (btn->coords.y1 + h / 2) - p_act.y;
p_act.x += (x_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1);
p_act.y += (y_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1);
lv_coord_t half_side = LV_MATH_MAX(w, h) / 2;
cir_area.x1 = p_act.x - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
cir_area.y1 = p_act.y - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
cir_area.x2 = p_act.x + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
cir_area.y2 = p_act.y + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
lv_area_intersect(&cir_area, &btn->coords, &cir_area); /*Limit the area. (It might be too big on the smaller side)*/
/*In the second part animate the radius. Circle -> body.radius*/
lv_coord_t r_state = ink_act_value > LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value - LV_BTN_INK_VALUE_MAX / 2 : 0;
lv_style_copy(&style_tmp, ext->styles[ink_top_state]);
style_tmp.body.radius = r_max + (((ext->styles[ink_bg_state]->body.radius - r_max) * r_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
style_tmp.body.border.width = 0;
/*Draw the circle*/
lv_draw_rect(&cir_area, mask, &style_tmp, opa_scale);
} else {
lv_style_t res;
lv_style_copy(&res, ext->styles[ink_bg_state]);
lv_style_mix(ext->styles[ink_bg_state], ext->styles[ink_top_state], &res, ink_act_value);
lv_draw_rect(&btn->coords, mask, &res, opa_scale);
}
}
#else
ancestor_design(btn, mask, mode);
#endif
} else if(mode == LV_DESIGN_DRAW_POST) {
ancestor_design(btn, mask, mode);
}
return true;
}
/**
* Signal function of the button
* @param btn pointer to a button object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(btn, sign, param);
if(res != LV_RES_OK) return res;
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
bool tgl = lv_btn_get_toggle(btn);
if(sign == LV_SIGNAL_PRESSED) {
/*Refresh the state*/
if(ext->state == LV_BTN_STATE_REL) {
lv_btn_set_state(btn, LV_BTN_STATE_PR);
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
ink_bg_state = LV_BTN_STATE_REL;
ink_top_state = LV_BTN_STATE_PR;
#endif
} else if(ext->state == LV_BTN_STATE_TGL_REL) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
ink_bg_state = LV_BTN_STATE_TGL_REL;
ink_top_state = LV_BTN_STATE_TGL_PR;
#endif
}
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
/*Forget the old inked button*/
if(ink_obj != NULL && ink_obj != btn) {
lv_anim_del(ink_obj, (lv_anim_fp_t)lv_btn_ink_effect_anim);
lv_obj_invalidate(ink_obj);
ink_obj = NULL;
}
/*Save the new data for inking and start it's animation if enabled*/
if(ext->ink_in_time > 0) {
ink_obj = btn;
ink_playback = false;
ink_ready = false;
lv_indev_get_point(lv_indev_get_act(), &ink_point);
lv_anim_t a;
a.var = btn;
a.start = 0;
a.end = LV_BTN_INK_VALUE_MAX;
a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim;
a.path = lv_anim_path_linear;
a.end_cb = lv_btn_ink_effect_anim_ready;
a.act_time = 0;
a.time = ext->ink_in_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
}
#endif
} else if(sign == LV_SIGNAL_PRESS_LOST) {
/*Refresh the state*/
if(ext->state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
else if(ext->state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(sign == LV_SIGNAL_PRESSING) {
/*When the button begins to drag revert pressed states to released*/
if(lv_indev_is_dragging(param) != false) {
if(ext->state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
else if(ext->state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
}
} else if(sign == LV_SIGNAL_RELEASED) {
/*If not dragged and it was not long press action then
*change state and run the action*/
if(lv_indev_is_dragging(param) == false) {
if(ext->state == LV_BTN_STATE_PR && tgl == false) {
lv_btn_set_state(btn, LV_BTN_STATE_REL);
} else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == false) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(ext->state == LV_BTN_STATE_PR && tgl == true) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == true) {
lv_btn_set_state(btn, LV_BTN_STATE_REL);
}
if(tgl) {
res = lv_obj_send_event(btn, LV_EVENT_VALUE_CHANGED);
if(res != LV_RES_OK) return res;
}
} else { /*If dragged change back the state*/
if(ext->state == LV_BTN_STATE_PR) {
lv_btn_set_state(btn, LV_BTN_STATE_REL);
} else if(ext->state == LV_BTN_STATE_TGL_PR) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
}
}
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
/*Draw the toggled state in the inking instead*/
if(ext->toggle) {
ink_top_state = ext->state;
}
/*If not a toggle button and the "IN" inking is ready then start an "OUT" inking*/
else if(ink_ready && ext->ink_out_time > 0) {
ink_obj = btn;
ink_playback = true; /*It is the playback. If not set `lv_btn_ink_effect_anim_ready` will start its own playback*/
lv_indev_get_point(lv_indev_get_act(), &ink_point);
lv_anim_t a;
a.var = ink_obj;
a.start = LV_BTN_INK_VALUE_MAX;
a.end = 0;
a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim;
a.path = lv_anim_path_linear;
a.end_cb = lv_btn_ink_effect_anim_ready;
a.act_time = 0;
a.time = ext->ink_out_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
}
#endif
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) {
if(lv_btn_get_toggle(btn)) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) {
if(lv_btn_get_toggle(btn)) lv_btn_set_state(btn, LV_BTN_STATE_REL);
}
// else if(c == LV_GROUP_KEY_ENTER) {
// if(lv_btn_get_toggle(btn)) {
// if(state == LV_BTN_STATE_REL || state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
// else if(state == LV_BTN_STATE_TGL_REL || state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
// } else {
// if(state == LV_BTN_STATE_REL || state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
// else if(state == LV_BTN_STATE_TGL_REL || state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
// }
// res = lv_obj_send_event(btn, LV_EVENT_VALUE_CHANGED);
// if(res != LV_RES_OK) return res;
// }
} else if(sign == LV_SIGNAL_CLEANUP) {
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
if(btn == ink_obj) {
lv_anim_del(ink_obj, (lv_anim_fp_t)lv_btn_ink_effect_anim);
ink_obj = NULL;
}
#endif
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_btn";
}
return res;
}
#if LV_USE_ANIMATION && LV_BTN_INK_EFFECT
/**
* The animator function of inking. CAlled to increase the radius of ink
* @param btn pointer to the animated button
* @param val the new radius
*/
static void lv_btn_ink_effect_anim(lv_obj_t * btn, int32_t val)
{
if(btn) {
ink_act_value = val;
lv_obj_invalidate(btn);
}
}
/**
* Called to clean up when the ink animation is ready
* @param p unused
*/
static void lv_btn_ink_effect_anim_ready(void * p)
{
(void) p; /*Unused*/
lv_btn_ext_t * ext = lv_obj_get_ext_attr(ink_obj);
lv_btn_state_t state = lv_btn_get_state(ink_obj);
lv_obj_invalidate(ink_obj);
ink_ready = true;
if((state == LV_BTN_STATE_REL || state == LV_BTN_STATE_TGL_REL) && ext->toggle == 0 && ink_playback == false) {
lv_anim_t a;
a.var = ink_obj;
a.start = LV_BTN_INK_VALUE_MAX;
a.end = 0;
a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim;
a.path = lv_anim_path_linear;
a.end_cb = lv_btn_ink_effect_anim_ready;
a.act_time = -ext->ink_wait_time;
a.time = ext->ink_out_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
ink_playback = true;
} else {
ink_obj = NULL;
}
}
#endif /*LV_USE_ANIMATION*/
#endif

298
src/lv_objx/lv_btn.h Normal file
View File

@@ -0,0 +1,298 @@
/**
* @file lv_btn.h
*
*/
#ifndef LV_BTN_H
#define LV_BTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_BTN != 0
/*Testing of dependencies*/
#if LV_USE_CONT == 0
#error "lv_btn: lv_cont is required. Enable it in lv_conf.h (LV_USE_CONT 1) "
#endif
#include "lv_cont.h"
#include "../lv_core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* Button states
* It can be used not only by buttons but other button-like objects too*/
enum
{
LV_BTN_STATE_REL,
LV_BTN_STATE_PR,
LV_BTN_STATE_TGL_REL,
LV_BTN_STATE_TGL_PR,
LV_BTN_STATE_INA,
LV_BTN_STATE_NUM,
};
typedef uint8_t lv_btn_state_t;
/*Data of button*/
typedef struct
{
lv_cont_ext_t cont; /*Ext. of ancestor*/
/*New data for this type */
lv_style_t * styles[LV_BTN_STATE_NUM]; /*Styles in each state*/
lv_btn_state_t state; /*Current state of the button from 'lv_btn_state_t' enum*/
#if LV_BTN_INK_EFFECT
uint16_t ink_in_time; /*[ms] Time of ink fill effect (0: disable ink effect)*/
uint16_t ink_wait_time; /*[ms] Wait before the ink disappears */
uint16_t ink_out_time; /*[ms] Time of ink disappearing*/
#endif
uint8_t toggle :1; /*1: Toggle enabled*/
} lv_btn_ext_t;
/*Styles*/
enum {
LV_BTN_STYLE_REL,
LV_BTN_STYLE_PR,
LV_BTN_STYLE_TGL_REL,
LV_BTN_STYLE_TGL_PR,
LV_BTN_STYLE_INA,
};
typedef uint8_t lv_btn_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a button objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
* @return pointer to the created button
*/
lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Enable the toggled states. On release the button will change from/to toggled state.
* @param btn pointer to a button object
* @param tgl true: enable toggled states, false: disable
*/
void lv_btn_set_toggle(lv_obj_t * btn, bool tgl);
/**
* Set the state of the button
* @param btn pointer to a button object
* @param state the new state of the button (from lv_btn_state_t enum)
*/
void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state);
/**
* Toggle the state of the button (ON->OFF, OFF->ON)
* @param btn pointer to a button object
*/
void lv_btn_toggle(lv_obj_t * btn);
/**
* Set the layout on a button
* @param btn pointer to a button object
* @param layout a layout from 'lv_cont_layout_t'
*/
static inline void lv_btn_set_layout(lv_obj_t * btn, lv_layout_t layout)
{
lv_cont_set_layout(btn, layout);
}
/**
* Set the fit policy in all 4 directions separately.
* It tell how to change the button size automatically.
* @param btn pointer to a button object
* @param left left fit policy from `lv_fit_t`
* @param right right fit policy from `lv_fit_t`
* @param top bottom fit policy from `lv_fit_t`
* @param bottom bottom fit policy from `lv_fit_t`
*/
static inline void lv_btn_set_fit4(lv_obj_t * btn, lv_fit_t left, lv_fit_t right, lv_fit_t top, lv_fit_t bottom)
{
lv_cont_set_fit4(btn, left, right, top, bottom);
}
/**
* Set the fit policy horizontally and vertically separately.
* It tell how to change the button size automatically.
* @param btn pointer to a button object
* @param hot horizontal fit policy from `lv_fit_t`
* @param ver vertical fit policy from `lv_fit_t`
*/
static inline void lv_btn_set_fit2(lv_obj_t * btn, lv_fit_t hor, lv_fit_t ver)
{
lv_cont_set_fit2(btn, hor, ver);
}
/**
* Set the fit policy in all 4 direction at once.
* It tell how to change the button size automatically.
* @param btn pointer to a button object
* @param fit fit policy from `lv_fit_t`
*/
static inline void lv_btn_set_fit(lv_obj_t * cont, lv_fit_t fit)
{
lv_cont_set_fit(cont, fit);
}
/**
* Set time of the ink effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time);
/**
* Set the wait time before the ink disappears
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time);
/**
* Set time of the ink out effect (animate to the released state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time);
/**
* Set a style of a button.
* @param btn pointer to button object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the current state of the button
* @param btn pointer to a button object
* @return the state of the button (from lv_btn_state_t enum)
*/
lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn);
/**
* Get the toggle enable attribute of the button
* @param btn pointer to a button object
* @return ture: toggle enabled, false: disabled
*/
bool lv_btn_get_toggle(const lv_obj_t * btn);
/**
* Get the layout of a button
* @param btn pointer to button object
* @return the layout from 'lv_cont_layout_t'
*/
static inline lv_layout_t lv_btn_get_layout(const lv_obj_t * btn)
{
return lv_cont_get_layout(btn);
}
/**
* Get the left fit mode
* @param btn pointer to a button object
* @return an element of `lv_fit_t`
*/
static inline lv_fit_t lv_btn_get_fit_left(const lv_obj_t * btn)
{
return lv_cont_get_fit_left(btn);
}
/**
* Get the right fit mode
* @param btn pointer to a button object
* @return an element of `lv_fit_t`
*/
static inline lv_fit_t lv_btn_get_fit_right(const lv_obj_t * btn)
{
return lv_cont_get_fit_right(btn);
}
/**
* Get the top fit mode
* @param btn pointer to a button object
* @return an element of `lv_fit_t`
*/
static inline lv_fit_t lv_btn_get_fit_top(const lv_obj_t * btn)
{
return lv_cont_get_fit_top(btn);
}
/**
* Get the bottom fit mode
* @param btn pointer to a button object
* @return an element of `lv_fit_t`
*/
static inline lv_fit_t lv_btn_get_fit_bottom(const lv_obj_t * btn)
{
return lv_cont_get_fit_bottom(btn);
}
/**
* Get time of the ink in effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn);
/**
* Get the wait time before the ink disappears
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn);
/**
* Get time of the ink out effect (animate to the releases state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn);
/**
* Get style of a button.
* @param btn pointer to button object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BUTTON*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BTN_H*/

1135
src/lv_objx/lv_btnm.c Normal file

File diff suppressed because it is too large Load Diff

318
src/lv_objx/lv_btnm.h Normal file
View File

@@ -0,0 +1,318 @@
/**
* @file lv_btnm.h
*
*/
#ifndef LV_BTNM_H
#define LV_BTNM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_BTNM != 0
#include "../lv_core/lv_obj.h"
#include "lv_label.h"
#include "lv_btn.h"
/*********************
* DEFINES
*********************/
/*Control byte*/
#define LV_BTNM_WIDTH_MASK 0x07
#define LV_BTNM_BTN_HIDDEN 0x08
#define LV_BTNM_BTN_NO_REPEAT 0x10
#define LV_BTNM_BTN_INACTIVE 0x20
#define LV_BTNM_BTN_TOGGLE 0x40
#define LV_BTNM_BTN_TOGGLE_STATE 0x80
#define LV_BTNM_BTN_NONE 0xFFFF
/**********************
* TYPEDEFS
**********************/
/* Type to store button control bits (disabled, hidden etc.) */
typedef uint8_t lv_btnm_ctrl_t;
/*Data of button matrix*/
typedef struct
{
/*No inherited ext.*/ /*Ext. of ancestor*/
/*New data for this type */
const char ** map_p; /*Pointer to the current map*/
lv_area_t *button_areas; /*Array of areas of buttons*/
lv_btnm_ctrl_t *ctrl_bits; /*Array of control bytes*/
lv_style_t *styles_btn[LV_BTN_STATE_NUM]; /*Styles of buttons in each state*/
uint16_t btn_cnt; /*Number of button in 'map_p'(Handled by the library)*/
uint16_t btn_id_pr; /*Index of the currently pressed button or LV_BTNM_BTN_NONE*/
uint16_t btn_id_act; /*Index of the active button (being pressed/released etc) or LV_BTNM_BTN_NONE */
uint8_t recolor :1; /*Enable button recoloring*/
} lv_btnm_ext_t;
enum {
LV_BTNM_STYLE_BG,
LV_BTNM_STYLE_BTN_REL,
LV_BTNM_STYLE_BTN_PR,
LV_BTNM_STYLE_BTN_TGL_REL,
LV_BTNM_STYLE_BTN_TGL_PR,
LV_BTNM_STYLE_BTN_INA,
};
typedef uint8_t lv_btnm_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a button matrix objects
* @param par pointer to an object, it will be the parent of the new button matrix
* @param copy pointer to a button matrix object, if not NULL then the new object will be copied from it
* @return pointer to the created button matrix
*/
lv_obj_t * lv_btnm_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new map. Buttons will be created/deleted according to the map. The
* button matrix keeps a reference to the map and so the string array must not
* be deallocated during the life of the matrix.
* @param btnm pointer to a button matrix object
* @param map pointer a string array. The last string has to be: "". Use "\n" to make a line break.
*/
void lv_btnm_set_map(const lv_obj_t * btnm, const char ** map);
/**
* Set the button control map (hidden, disabled etc.) for a button matrix. The
* control map array will be copied and so may be deallocated after this
* function returns.
* @param btnm pointer to a button matrix object
* @param ctrl_map pointer to an array of `lv_btn_ctrl_t` control bytes. The
* length of the array and position of the elements must match
* the number and order of the individual buttons (i.e. excludes
* newline entries).
* The control bits are:
* - bit 5 : 1 = inactive (disabled)
* - bit 4 : 1 = no repeat (on long press)
* - bit 3 : 1 = hidden
* - bit 2..0: Relative width compared to the buttons in the
* same row. [1..7]
*/
void lv_btnm_set_ctrl_map(const lv_obj_t * btnm, const lv_btnm_ctrl_t * ctrl_map);
/**
* Set the pressed button i.e. visually highlight it.
* Mainly used a when the btnm is in a group to show the selected button
* @param btnm pointer to button matrix object
* @param id index of the currently pressed button (`LV_BTNM_BTN_NONE` to unpress)
*/
void lv_btnm_set_pressed(const lv_obj_t * btnm, uint16_t id);
/**
* Set a style of a button matrix
* @param btnm pointer to a button matrix object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_btnm_set_style(lv_obj_t * btnm, lv_btnm_style_t type, lv_style_t * style);
/**
* Enable recoloring of button's texts
* @param btnm pointer to button matrix object
* @param en true: enable recoloring; false: disable
*/
void lv_btnm_set_recolor(const lv_obj_t * btnm, bool en);
/**
* Show/hide a single button in the matrix
* @param btnm pointer to button matrix object
* @param btn_idx 0 based index of the button to modify.
* @param hidden true: hide the button
*/
void lv_btnm_set_btn_hidden(const lv_obj_t * btnm, uint16_t btn_idx, bool hidden);
/**
* Enable/disable a single button in the matrix
* @param btnm pointer to button matrix object
* @param btn_id 0 based index of the button to modify.
* @param ina true: make the button inactive
*/
void lv_btnm_set_btn_inactive(const lv_obj_t * btnm, uint16_t btn_id, bool ina);
/**
* Enable/disable long press for a single button in the matrix
* @param btnm pointer to button matrix object
* @param btn_id 0 based index of the button to modify.
* @param no_rep true: disable repeat
*/
void lv_btnm_set_btn_no_repeat(const lv_obj_t * btnm, uint16_t btn_id, bool no_rep);
/**
* Enable/disable toggling a single button in the matrix
* @param btnm pointer to button matrix object
* @param btn_id 0 based index of the button to modify.
* @param tgl true: toggle enable
*/
void lv_btnm_set_btn_toggle(const lv_obj_t * btnm, uint16_t btn_id, bool tgl);
/**
* Make the a single button button toggled or not toggled.
* @param btnm pointer to button matrix object
* @param btn_id index of button (not counting "\n")
* @param state true: toggled; false: not toggled
*/
void lv_btnm_set_btn_toggle_state(lv_obj_t * btnm, uint16_t btn_id, bool toggle);
/**
* Set hidden/disabled/repeat flags for a single button.
* @param btnm pointer to button matrix object
* @param btn_id 0 based index of the button to modify.
* @param hidden true: hide the button
* @param inactive true: disable the button
* @param no_repeat true: disable repeat
* @param toggle true: enable toggling
* @param toggled_state true: set toggled state
*/
void lv_btnm_set_btn_flags(const lv_obj_t * btnm, uint16_t btn_id, bool hidden, bool inactive, bool no_repeat, bool toggle, bool toggle_state);
/**
* Set a single buttons relative width.
* This method will cause the matrix be regenerated and is a relatively
* expensive operation. It is recommended that initial width be specified using
* `lv_btnm_set_ctrl_map` and this method only be used for dynamic changes.
* @param btnm pointer to button matrix object
* @param btn_id 0 based index of the button to modify.
* @param width Relative width compared to the buttons in the same row. [1..7]
*/
void lv_btnm_set_btn_width(const lv_obj_t * btnm, uint16_t btn_id, uint8_t width);
/**
* Set the toggle state of all buttons
* @param btnm pointer to a button matrix object
* @param state true: toggled; false: not toggled
*/
void lv_btnm_set_btn_toggle_state_all(lv_obj_t * btnm, bool state);
/*=====================
* Getter functions
*====================*/
/**
* Get the current map of a button matrix
* @param btnm pointer to a button matrix object
* @return the current map
*/
const char ** lv_btnm_get_map(const lv_obj_t * btnm);
/**
* Check whether the button's text can use recolor or not
* @param btnm pointer to button matrix object
* @return true: text recolor enable; false: disabled
*/
bool lv_btnm_get_recolor(const lv_obj_t * btnm);
/**
* Get the index of the lastly "activated" button by the user (pressed, released etc)
* Useful in the the `event_cb` to get the text of the button, check if hidden etc.
* @param btnm pointer to button matrix object
* @return index of the last released button (LV_BTNM_BTN_NONE: if unset)
*/
uint16_t lv_btnm_get_active_btn(const lv_obj_t * btnm);
/**
* Get the text of the lastly "activated" button by the user (pressed, released etc)
* Useful in the the `event_cb`
* @param btnm pointer to button matrix object
* @return text of the last released button (NULL: if unset)
*/
const char * lv_btnm_get_active_btn_text(const lv_obj_t * btnm);
/**
* Get the pressed button's index.
* The button be really pressed by the user or manually set to pressed with `lv_btnm_set_pressed`
* @param btnm pointer to button matrix object
* @return index of the pressed button (LV_BTNM_BTN_NONE: if unset)
*/
uint16_t lv_btnm_get_pressed_btn(const lv_obj_t * btnm);
/**
* Get the button's text
* @param btnm pointer to button matrix object
* @param btn_index the index a button not counting new line characters. (The return value of lv_btnm_get_pressed/released)
* @return text of btn_index` button
*/
const char * lv_btnm_get_btn_text(const lv_obj_t * btnm, uint16_t btn_id);
/**
* Check whether "no repeat" for a button is set or not.
* The `LV_EVENT_LONG_PRESS_REPEAT` will be sent anyway but it can be ignored by the user if this function returns `true`
* @param btnm pointer to a button matrix object
* @param btn_index the index a button not counting new line characters. (The return value of lv_btnm_get_pressed/released)
* @return true: long press repeat is disabled; false: long press repeat enabled
*/
bool lv_btnm_get_btn_no_repeate(lv_obj_t * btnm, uint16_t btn_id);
/**
* Check whether a button for a button is hidden or not.
* Events will be sent anyway but they can be ignored by the user if this function returns `true`
* @param btnm pointer to a button matrix object
* @param btn_id the index a button not counting new line characters. (The return value of lv_btnm_get_pressed/released)
* @return true: hidden; false: not hidden
*/
bool lv_btnm_get_btn_hidden(lv_obj_t * btnm, uint16_t btn_id);
/**
* Check whether a button for a button is inactive or not.
* Events will be sent anyway but they can be ignored by the user if this function returns `true`
* @param btnm pointer to a button matrix object
* @param btn_id the index a button not counting new line characters. (The return value of lv_btnm_get_pressed/released)
* @return true: inactive; false: not inactive
*/
bool lv_btnm_get_btn_inactive(lv_obj_t * btnm, uint16_t btn_id);
/**
* Check if the button can be toggled or not
* @param btnm pointer to button matrix object
* @return btn_id index a of a button not counting "\n". (The return value of lv_btnm_get_pressed/released)
*/
bool lv_btnm_get_btn_toggle(const lv_obj_t * btnm, int16_t btn_id);
/**
* Check if the button is toggled or not
* @param btnm pointer to button matrix object
* @return btn_id index a of a button not counting "\n". (The return value of lv_btnm_get_pressed/released)
*/
bool lv_btnm_get_btn_toggle_state(const lv_obj_t * btnm, int16_t btn_id);
/**
* Get a style of a button matrix
* @param btnm pointer to a button matrix object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_btnm_get_style(const lv_obj_t * btnm, lv_btnm_style_t type);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BTNM*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BTNM_H*/

976
src/lv_objx/lv_calendar.c Normal file
View File

@@ -0,0 +1,976 @@
/**
* @file lv_calendar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar.h"
#if LV_USE_CALENDAR != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_hal/lv_hal_indev.h"
#include "../lv_misc/lv_math.h"
#include "../lv_core/lv_indev.h"
#include "../lv_themes/lv_theme.h"
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
DAY_DRAW_PREV_MONTH,
DAY_DRAW_ACT_MONTH,
DAY_DRAW_NEXT_MONTH,
};
typedef uint8_t day_draw_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param);
static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point);
static lv_coord_t get_header_height(lv_obj_t * calendar);
static lv_coord_t get_day_names_height(lv_obj_t * calendar);
static void draw_header(lv_obj_t * calendar, const lv_area_t * mask);
static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask);
static void draw_days(lv_obj_t * calendar, const lv_area_t * mask);
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day);
static const char * get_day_name(lv_obj_t * calendar, uint8_t day);
static const char * get_month_name(lv_obj_t * calendar, int32_t month);
static uint8_t get_month_length(int32_t year, int32_t month);
static uint8_t is_leap_year(uint32_t year);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_cb_t ancestor_signal;
static lv_design_cb_t ancestor_design;
static const char * day_name[7] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"};
static const char * month_name[12] = {"January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a calendar object
* @param par pointer to an object, it will be the parent of the new calendar
* @param copy pointer to a calendar object, if not NULL then the new object will be copied from it
* @return pointer to the created calendar
*/
lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("calendar create started");
/*Create the ancestor of calendar*/
lv_obj_t * new_calendar = lv_obj_create(par, copy);
lv_mem_assert(new_calendar);
if(new_calendar == NULL) return NULL;
/*Allocate the calendar type specific extended data*/
lv_calendar_ext_t * ext = lv_obj_allocate_ext_attr(new_calendar, sizeof(lv_calendar_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_calendar);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_calendar);
/*Initialize the allocated 'ext' */
ext->today.year = 2018;
ext->today.month = 1;
ext->today.day = 1;
ext->showed_date.year = 2018;
ext->showed_date.month = 1;
ext->showed_date.day = 1;
ext->pressed_date.year = 0;
ext->pressed_date.month = 0;
ext->pressed_date.day = 0;
ext->highlighted_dates = NULL;
ext->highlighted_dates_num = 0;
ext->day_names = NULL;
ext->month_names = NULL;
ext->style_header = &lv_style_plain_color;
ext->style_header_pr = &lv_style_pretty_color;
ext->style_highlighted_days = &lv_style_plain_color;
ext->style_inactive_days = &lv_style_btn_ina;
ext->style_week_box = &lv_style_plain_color;
ext->style_today_box = &lv_style_pretty_color;
ext->style_day_names = &lv_style_pretty;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_cb(new_calendar, lv_calendar_signal);
lv_obj_set_design_cb(new_calendar, lv_calendar_design);
/*Init the new calendar calendar*/
if(copy == NULL) {
lv_obj_set_size(new_calendar, LV_DPI * 2, LV_DPI * 2);
lv_obj_set_style(new_calendar, &lv_style_pretty);
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, th->style.calendar.bg);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, th->style.calendar.header);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, th->style.calendar.header_pr);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, th->style.calendar.day_names);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, th->style.calendar.week_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, th->style.calendar.today_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, th->style.calendar.highlighted_days);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, th->style.calendar.inactive_days);
} else {
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, &lv_style_pretty);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, ext->style_header);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, ext->style_header_pr);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, ext->style_day_names);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, ext->style_week_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, ext->style_today_box);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, ext->style_highlighted_days);
lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, ext->style_inactive_days);
}
}
/*Copy an existing calendar*/
else {
lv_calendar_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->today.year = copy_ext->today.year;
ext->today.month = copy_ext->today.month;
ext->today.day = copy_ext->today.day;
ext->showed_date.year = copy_ext->showed_date.year;
ext->showed_date.month = copy_ext->showed_date.month;
ext->showed_date.day = copy_ext->showed_date.day;
ext->highlighted_dates = copy_ext->highlighted_dates;
ext->highlighted_dates_num = copy_ext->highlighted_dates_num;
ext->day_names = copy_ext->day_names;
ext->month_names = copy_ext->month_names;
ext->style_header = copy_ext->style_header;
ext->style_header_pr = copy_ext->style_header_pr;
ext->style_highlighted_days = copy_ext->style_highlighted_days;
ext->style_inactive_days = copy_ext->style_inactive_days;
ext->style_week_box = copy_ext->style_week_box;
ext->style_today_box = copy_ext->style_today_box;
ext->style_day_names = copy_ext->style_day_names;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_calendar);
}
LV_LOG_INFO("calendar created");
return new_calendar;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
/**
* Set the today's date
* @param calendar pointer to a calendar object
* @param today pointer to an `lv_calendar_date_t` variable containing the date of today. The value will be saved it can be local variable too.
*/
void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->today.year = today->year;
ext->today.month = today->month;
ext->today.day = today->day;
lv_obj_invalidate(calendar);
}
/**
* Set the currently showed
* @param calendar pointer to a calendar object
* @param showed pointer to an `lv_calendar_date_t` variable containing the date to show. The value will be saved it can be local variable too.
*/
void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->showed_date.year = showed->year;
ext->showed_date.month = showed->month;
ext->showed_date.day = showed->day;
lv_obj_invalidate(calendar);
}
/**
* Set the the highlighted dates
* @param calendar pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->highlighted_dates = highlighted;
ext->highlighted_dates_num = date_num;
lv_obj_invalidate(calendar);
}
/**
* Set the name of the days
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->day_names = day_names;
lv_obj_invalidate(calendar);
}
/**
* Set the name of the month
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->month_names = day_names;
lv_obj_invalidate(calendar);
}
/**
* Set a style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t * style)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
switch(type) {
case LV_CALENDAR_STYLE_BG:
lv_obj_set_style(calendar, style);
break;
case LV_CALENDAR_STYLE_DAY_NAMES:
ext->style_day_names = style;
break;
case LV_CALENDAR_STYLE_HEADER:
ext->style_header = style;
break;
case LV_CALENDAR_STYLE_HEADER_PR:
ext->style_header_pr = style;
break;
case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS:
ext->style_highlighted_days = style;
break;
case LV_CALENDAR_STYLE_INACTIVE_DAYS:
ext->style_inactive_days = style;
break;
case LV_CALENDAR_STYLE_TODAY_BOX:
ext->style_today_box = style;
break;
case LV_CALENDAR_STYLE_WEEK_BOX:
ext->style_week_box = style;
break;
}
lv_obj_invalidate(calendar);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return &ext->today;
}
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return &ext->showed_date;
}
/**
* Get the the pressed date.
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the pressed date.
*/
lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return &ext->pressed_date;
}
/**
* Get the the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->highlighted_dates;
}
/**
* Get the number of the highlighted dates
* @param calendar pointer to a calendar object
* @return number of highlighted days
*/
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->highlighted_dates_num;
}
/**
* Get the name of the days
* @param calendar pointer to a calendar object
* @return pointer to the array of day names
*/
const char ** lv_calendar_get_day_names(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->day_names;
}
/**
* Get the name of the month
* @param calendar pointer to a calendar object
* @return pointer to the array of month names
*/
const char ** lv_calendar_get_month_names(const lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return ext->month_names;
}
/**
* Get style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type)
{
lv_style_t * style = NULL;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
switch(type) {
case LV_CALENDAR_STYLE_BG:
style = lv_obj_get_style(calendar);
break;
case LV_CALENDAR_STYLE_HEADER:
style = ext->style_header;
break;
case LV_CALENDAR_STYLE_HEADER_PR:
style = ext->style_header_pr;
break;
case LV_CALENDAR_STYLE_DAY_NAMES:
style = ext->style_day_names;
break;
case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS:
style = ext->style_highlighted_days;
break;
case LV_CALENDAR_STYLE_INACTIVE_DAYS:
style = ext->style_inactive_days;
break;
case LV_CALENDAR_STYLE_WEEK_BOX:
style = ext->style_week_box;
break;
case LV_CALENDAR_STYLE_TODAY_BOX:
style = ext->style_today_box;
break;
default:
style = NULL;
break;
}
return style;
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the calendars
* @param calendar pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
lv_draw_rect(&calendar->coords, mask, lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG), opa_scale);
draw_header(calendar, mask);
draw_day_names(calendar, mask);
draw_days(calendar, mask);
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the calendar
* @param calendar pointer to a calendar object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(calendar, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_PRESSING) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_area_t header_area;
lv_area_copy(&header_area, &calendar->coords);
header_area.y2 = header_area.y1 + get_header_height(calendar);
lv_indev_t * indev = lv_indev_get_act();
lv_point_t p;
lv_indev_get_point(indev, &p);
if(lv_area_is_point_on(&header_area, &p)) {
if(p.x < header_area.x1 + lv_area_get_width(&header_area) / 2) {
if(ext->btn_pressing != -1) lv_obj_invalidate(calendar);
ext->btn_pressing = -1;
} else {
if(ext->btn_pressing != 1) lv_obj_invalidate(calendar);
ext->btn_pressing = 1;
}
ext->pressed_date.year = 0;
} else if(calculate_touched_day(calendar, &p)) {
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
ext->btn_pressing = 0;
} else {
if(ext->btn_pressing != 0) lv_obj_invalidate(calendar);
ext->btn_pressing = 0;
ext->pressed_date.year = 0;
}
} else if(sign == LV_SIGNAL_PRESS_LOST) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
ext->pressed_date.year = 0;
ext->btn_pressing = 0;
lv_obj_invalidate(calendar);
} else if(sign == LV_SIGNAL_RELEASED) {
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->btn_pressing < 0) {
if(ext->showed_date.month <= 1) {
ext->showed_date.month = 12;
ext->showed_date.year --;
} else {
ext->showed_date.month --;
}
} else if(ext->btn_pressing > 0) {
if(ext->showed_date.month >= 12) {
ext->showed_date.month = 1;
ext->showed_date.year ++;
} else {
ext->showed_date.month ++;
}
}
else if(ext->pressed_date.year != 0)
{
res = lv_obj_send_event(calendar, LV_EVENT_VALUE_CHANGED);
if(res != LV_RES_OK) return res;
}
ext->pressed_date.year = 0;
ext->btn_pressing = 0;
lv_obj_invalidate(calendar);
} else if(sign == LV_SIGNAL_CONTROLL) {
uint8_t c = *((uint8_t *) param);
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) {
if(ext->showed_date.month >= 12) {
ext->showed_date.month = 1;
ext->showed_date.year ++;
} else {
ext->showed_date.month ++;
}
lv_obj_invalidate(calendar);
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) {
if(ext->showed_date.month <= 1) {
ext->showed_date.month = 12;
ext->showed_date.year --;
} else {
ext->showed_date.month --;
}
lv_obj_invalidate(calendar);
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set date*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_calendar";
}
return res;
}
/**
* It will check if the days part of calendar is touched
* and if it is, it will calculate the day and put it in pressed_date of calendar object.
* @param calendar pointer to a calendar object
* @param pointer to a point
* @return true: days part of calendar is touched and its related date is put in pressed date
* false: the point is out of days part area.
*/
static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point)
{
lv_area_t days_area;
lv_area_copy(&days_area, &calendar->coords);
lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG);
days_area.x1 += style_bg->body.padding.left;
days_area.x2 -= style_bg->body.padding.right;
days_area.y1 = calendar->coords.y1 + get_header_height(calendar) + get_day_names_height(calendar) - style_bg->body.padding.top;
if(lv_area_is_point_on(&days_area, touched_point)) {
lv_coord_t w = (days_area.x2 - days_area.x1 + 1) / 7;
lv_coord_t h = (days_area.y2 - days_area.y1 + 1) / 6;
uint8_t x_pos = 0;
x_pos = (touched_point->x - days_area.x1) / w;
if(x_pos > 6) x_pos = 6;
uint8_t y_pos = 0;
y_pos = (touched_point->y - days_area.y1) / h;
if(y_pos > 5) y_pos = 5;
uint8_t i_pos = 0;
i_pos = (y_pos * 7) + x_pos;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(i_pos < get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) ) {
ext->pressed_date.year = ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0);
ext->pressed_date.month = ext->showed_date.month == 1 ? 12 : (ext->showed_date.month - 1);
ext->pressed_date.day = get_month_length(ext->pressed_date.year, ext->pressed_date.month) -
get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) + 1 + i_pos;
}
else if(i_pos < (get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) +
get_month_length(ext->showed_date.year, ext->showed_date.month))) {
ext->pressed_date.year = ext->showed_date.year;
ext->pressed_date.month = ext->showed_date.month;
ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1);
}
else if(i_pos < 42) {
ext->pressed_date.year = ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0);
ext->pressed_date.month = ext->showed_date.month == 12 ? 1 : (ext->showed_date.month + 1);
ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1)
- get_month_length(ext->showed_date.year, ext->showed_date.month);
}
return true;
}else {
return false;
}
}
/**
* Get the height of a calendar's header based on it's style
* @param calendar point to a calendar
* @return the header's height
*/
static lv_coord_t get_header_height(lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return lv_font_get_height(ext->style_header->text.font) + ext->style_header->body.padding.top + ext->style_header->body.padding.bottom ;
}
/**
* Get the height of a calendar's day_names based on it's style
* @param calendar point to a calendar
* @return the day_names's height
*/
static lv_coord_t get_day_names_height(lv_obj_t * calendar)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
return lv_font_get_height(ext->style_day_names->text.font) + ext->style_day_names->body.padding.top + ext->style_day_names->body.padding.bottom;
}
/**
* Draw the calendar header with month name and arrows
* @param calendar point to a calendar
* @param mask a mask for drawing
*/
static void draw_header(lv_obj_t * calendar, const lv_area_t * mask)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
lv_area_t header_area;
header_area.x1 = calendar->coords.x1;
header_area.x2 = calendar->coords.x2;
header_area.y1 = calendar->coords.y1;
header_area.y2 = calendar->coords.y1 + get_header_height(calendar);
lv_draw_rect(&header_area, mask, ext->style_header, opa_scale);
/*Add the year + month name*/
char txt_buf[64];
lv_math_num_to_str(ext->showed_date.year, txt_buf);
txt_buf[4] = ' ';
txt_buf[5] = '\0';
strcpy(&txt_buf[5], get_month_name(calendar, ext->showed_date.month));
header_area.y1 += ext->style_header->body.padding.top;
lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL);
/*Add the left arrow*/
lv_style_t * arrow_style = ext->btn_pressing < 0 ? ext->style_header_pr : ext->style_header;
header_area.x1 += ext->style_header->body.padding.left;
lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL);
/*Add the right arrow*/
arrow_style = ext->btn_pressing > 0 ? ext->style_header_pr : ext->style_header;
header_area.x1 = header_area.x2 - ext->style_header->body.padding.right -
lv_txt_get_width(LV_SYMBOL_RIGHT, strlen(LV_SYMBOL_RIGHT), arrow_style->text.font,
arrow_style->text.line_space, LV_TXT_FLAG_NONE);
lv_draw_label(&header_area, mask, arrow_style, opa_scale, LV_SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL);
}
/**
* Draw the day's name below the header
* @param calendar point to a calendar
* @param mask a mask for drawing
*/
static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
lv_coord_t l_pad = ext->style_day_names->body.padding.left;
lv_coord_t w = lv_obj_get_width(calendar) - ext->style_day_names->body.padding.left - ext->style_day_names->body.padding.right;
lv_coord_t box_w = w / 7;
lv_area_t label_area;
label_area.y1 = calendar->coords.y1 + get_header_height(calendar) + ext->style_day_names->body.padding.top;
label_area.y2 = label_area.y1 + lv_font_get_height(ext->style_day_names->text.font);
uint32_t i;
for(i = 0; i < 7; i++) {
label_area.x1 = calendar->coords.x1 + (w * i) / 7 + l_pad;
label_area.x2 = label_area.x1 + box_w;
lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, NULL);
}
}
/**
* Draw the date numbers in a matrix
* @param calendar point to a calendar
* @param mask a mask for drawing
*/
static void draw_days(lv_obj_t * calendar, const lv_area_t * mask)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG);
lv_area_t label_area;
lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar);
label_area.y1 = calendar->coords.y1 + get_header_height(calendar) +
ext->style_day_names->body.padding.top + lv_font_get_height(ext->style_day_names->text.font) +
ext->style_day_names->body.padding.bottom;
label_area.y2 = label_area.y1 + lv_font_get_height(style_bg->text.font);
lv_coord_t w = lv_obj_get_width(calendar) - style_bg->body.padding.left - style_bg->body.padding.right;
lv_coord_t h = calendar->coords.y2 - label_area.y1 - style_bg->body.padding.bottom;
lv_coord_t box_w = w / 7;
lv_coord_t vert_space = (h - (6 * lv_font_get_height(style_bg->text.font))) / 5;
uint32_t week;
uint8_t day_cnt;
uint8_t month_start_day = get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1);
day_draw_state_t draw_state; /*true: Not the prev. or next month is drawn*/
lv_style_t * act_style;
/*If starting with the first day of the week then the previous month is not visible*/
if(month_start_day == 0) {
day_cnt = 1;
draw_state = DAY_DRAW_ACT_MONTH;
act_style = style_bg;
} else {
draw_state = DAY_DRAW_PREV_MONTH;
day_cnt = get_month_length(ext->showed_date.year, ext->showed_date.month - 1); /*Length of the previous month*/
day_cnt -= month_start_day - 1; /*First visible number of the previous month*/
act_style = ext->style_inactive_days;
}
bool month_of_today_shown = false;
if(ext->showed_date.year == ext->today.year &&
ext->showed_date.month == ext->today.month) {
month_of_today_shown = true;
}
char buf[3];
bool in_week_box = false;
/*Draw 6 weeks*/
for(week = 0; week < 6; week++) {
/*Draw the "week box"*/
if(month_of_today_shown &&
((draw_state == DAY_DRAW_ACT_MONTH && ext->today.day >= day_cnt && ext->today.day < day_cnt + 7) ||
(draw_state == DAY_DRAW_PREV_MONTH && ext->today.day <= 7 - month_start_day && week == 0))) {
lv_area_t week_box_area;
lv_area_copy(&week_box_area, &label_area); /*'label_area' is already set for this row*/
week_box_area.x1 = calendar->coords.x1 + style_bg->body.padding.left - ext->style_week_box->body.padding.left;
week_box_area.x2 = calendar->coords.x2 - style_bg->body.padding.right + ext->style_week_box->body.padding.right;
week_box_area.y1 -= ext->style_week_box->body.padding.top;
week_box_area.y2 += ext->style_week_box->body.padding.bottom;
lv_draw_rect(&week_box_area, mask, ext->style_week_box, opa_scale);
in_week_box = true;
} else {
in_week_box = false;
}
/*Draw the 7 days of a week*/
uint32_t day;
for(day = 0; day < 7; day++) {
/*The previous month is over*/
if(draw_state == DAY_DRAW_PREV_MONTH && day == month_start_day) {
draw_state = DAY_DRAW_ACT_MONTH;
day_cnt = 1;
act_style = style_bg;
}
/*The current month is over*/
if(draw_state == DAY_DRAW_ACT_MONTH &&
day_cnt > get_month_length(ext->showed_date.year, ext->showed_date.month)) {
draw_state = DAY_DRAW_NEXT_MONTH;
day_cnt = 1;
act_style = ext->style_inactive_days;
}
label_area.x1 = calendar->coords.x1 + (w * day) / 7 + style_bg->body.padding.left + style_bg->body.padding.right;
label_area.x2 = label_area.x1 + box_w;
/*Draw the "today box"*/
if(draw_state == DAY_DRAW_ACT_MONTH && month_of_today_shown && ext->today.day == day_cnt) {
lv_area_t today_box_area;
lv_area_copy(&today_box_area, &label_area);
today_box_area.x1 = label_area.x1;
today_box_area.x2 = label_area.x2;
today_box_area.y1 = label_area.y1 - ext->style_today_box->body.padding.top;
today_box_area.y2 = label_area.y2 + ext->style_today_box->body.padding.bottom;
lv_draw_rect(&today_box_area, mask, ext->style_today_box, opa_scale);
}
/*Get the final style : highlighted/week box/today box/normal*/
lv_style_t * final_style;
if(draw_state == DAY_DRAW_PREV_MONTH &&
is_highlighted(calendar, ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0),
ext->showed_date.month == 1 ? 12 : ext->showed_date.month - 1,
day_cnt)) {
final_style = ext->style_highlighted_days;
} else if(draw_state == DAY_DRAW_ACT_MONTH &&
is_highlighted(calendar, ext->showed_date.year,
ext->showed_date.month,
day_cnt)) {
final_style = ext->style_highlighted_days;
} else if(draw_state == DAY_DRAW_NEXT_MONTH &&
is_highlighted(calendar, ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0),
ext->showed_date.month == 12 ? 1 : ext->showed_date.month + 1,
day_cnt)) {
final_style = ext->style_highlighted_days;
} else if(month_of_today_shown && day_cnt == ext->today.day && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_today_box;
else if(in_week_box && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_week_box;
else final_style = act_style;
/*Write the day's number*/
lv_math_num_to_str(day_cnt, buf);
lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL);
/*Go to the next day*/
day_cnt ++;
}
/*Got to the next weeks row*/
label_area.y1 += vert_space + lv_font_get_height(style_bg->text.font);
label_area.y2 += vert_space + lv_font_get_height(style_bg->text.font);
}
}
/**
* Check weather a date is highlighted or not
* @param calendar pointer to a calendar object
* @param year a year
* @param month a month [1..12]
* @param day a day [1..31]
* @return true: highlighted
*/
static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->highlighted_dates == NULL || ext->highlighted_dates_num == 0) return false;
uint32_t i;
for(i = 0; i < ext->highlighted_dates_num; i++) {
if(ext->highlighted_dates[i].year == year &&
ext->highlighted_dates[i].month == month &&
ext->highlighted_dates[i].day == day) {
return true;
}
}
return false;
}
/**
* Get the day name
* @param calendar pointer to a calendar object
* @param day a day in [0..6]
* @return
*/
static const char * get_day_name(lv_obj_t * calendar, uint8_t day)
{
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->day_names) return ext->day_names[day];
else return day_name[day];
}
/**
* Get the month name
* @param calendar pointer to a calendar object
* @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year
* @return
*/
static const char * get_month_name(lv_obj_t * calendar, int32_t month)
{
month --; /*Range of months id [1..12] but range of indexes is [0..11]*/
if(month < 0) month = 12 + month;
lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar);
if(ext->month_names) return ext->month_names[month];
else return month_name[month];
}
/**
* Get the number of days in a month
* @param year a year
* @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year
* @return [28..31]
*/
static uint8_t get_month_length(int32_t year, int32_t month)
{
month --; /*Range of months id [1..12] but range of indexes is [0..11]*/
if(month < 0) {
year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/
month = 12 + month; /*`month` is negative, the result will be < 12*/
}
if(month >= 12) {
year ++;
month -= 12;
}
/*month == 1 is february*/
return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
}
/**
* Tells whether a year is leap year or not
* @param year a year
* @return 0: not leap year; 1: leap year
*/
static uint8_t is_leap_year(uint32_t year)
{
return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
}
/**
* Get the day of the week
* @param year a year
* @param month a month
* @param day a day
* @return [0..6] which means [Sun..Sat]
*/
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
{
uint32_t a = month < 3 ? 1 : 0;
uint32_t b = year - a;
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) +
b + (b / 4) - (b / 100) + (b / 400)) % 7;
return day_of_week;
}
#endif

223
src/lv_objx/lv_calendar.h Normal file
View File

@@ -0,0 +1,223 @@
/**
* @file lv_calendar.h
*
*/
#ifndef LV_CALENDAR_H
#define LV_CALENDAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../../lv_conf.h"
#endif
#if LV_USE_CALENDAR != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint16_t year;
int8_t month;
int8_t day;
} lv_calendar_date_t;
/*Data of calendar*/
typedef struct {
/*None*/ /*Ext. of ancestor*/
/*New data for this type */
lv_calendar_date_t today; /*Date of today*/
lv_calendar_date_t showed_date; /*Currently visible month (day is ignored)*/
lv_calendar_date_t * highlighted_dates; /*Apply different style on these days (pointer to an array defined by the user)*/
uint8_t highlighted_dates_num; /*Number of elements in `highlighted_days`*/
int8_t btn_pressing; /*-1: prev month pressing, +1 next month pressing on the header*/
lv_calendar_date_t pressed_date;
const char ** day_names; /*Pointer to an array with the name of the days (NULL: use default names)*/
const char ** month_names; /*Pointer to an array with the name of the month (NULL. use default names)*/
/*Styles*/
lv_style_t * style_header;
lv_style_t * style_header_pr;
lv_style_t * style_day_names;
lv_style_t * style_highlighted_days;
lv_style_t * style_inactive_days;
lv_style_t * style_week_box;
lv_style_t * style_today_box;
} lv_calendar_ext_t;
/*Styles*/
enum {
LV_CALENDAR_STYLE_BG, /*Also the style of the "normal" date numbers*/
LV_CALENDAR_STYLE_HEADER,
LV_CALENDAR_STYLE_HEADER_PR,
LV_CALENDAR_STYLE_DAY_NAMES,
LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS,
LV_CALENDAR_STYLE_INACTIVE_DAYS,
LV_CALENDAR_STYLE_WEEK_BOX,
LV_CALENDAR_STYLE_TODAY_BOX,
};
typedef uint8_t lv_calendar_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar objects
* @param par pointer to an object, it will be the parent of the new calendar
* @param copy pointer to a calendar object, if not NULL then the new object will be copied from it
* @return pointer to the created calendar
*/
lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the today's date
* @param calendar pointer to a calendar object
* @param today pointer to an `lv_calendar_date_t` variable containing the date of today. The value will be saved it can be local variable too.
*/
void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today);
/**
* Set the currently showed
* @param calendar pointer to a calendar object
* @param showed pointer to an `lv_calendar_date_t` variable containing the date to show. The value will be saved it can be local variable too.
*/
void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed);
/**
* Set the the highlighted dates
* @param calendar pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num);
/**
* Set the name of the days
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names);
/**
* Set the name of the month
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names);
/**
* Set a style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar);
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar);
/**
* Get the the pressed date.
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the pressed date.
*/
lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar);
/**
* Get the the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar);
/**
* Get the number of the highlighted dates
* @param calendar pointer to a calendar object
* @return number of highlighted days
*/
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar);
/**
* Get the name of the days
* @param calendar pointer to a calendar object
* @return pointer to the array of day names
*/
const char ** lv_calendar_get_day_names(const lv_obj_t * calendar);
/**
* Get the name of the month
* @param calendar pointer to a calendar object
* @return pointer to the array of month names
*/
const char ** lv_calendar_get_month_names(const lv_obj_t * calendar);
/**
* Get style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_CALENDAR_H*/

Some files were not shown because too many files have changed in this diff Show More