perf: Enable 512KB LVGL image cache on PSRAM and move modified lvgl to local components

This commit is contained in:
Adolfo Reyna
2026-07-09 22:27:55 -04:00
parent df93252281
commit c4ec7ead55
3999 changed files with 813776 additions and 0 deletions
@@ -0,0 +1,22 @@
Copyright (c) 2013-2015, LKC Technologies, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. Redistributions in binary form
must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
@@ -0,0 +1,582 @@
// Copyright (c) 2013, LKC Technologies, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer. Redistributions in binary
// form must reproduce the above copyright notice, this list of conditions and
// the following disclaimer in the documentation and/or other materials
// provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
// HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../../../lvgl.h"
#if LV_USE_BARCODE
#include "code128.h"
#include <string.h>
#define CODE128_MALLOC lv_malloc
#define CODE128_REALLOC lv_realloc
#define CODE128_FREE lv_free
#define CODE128_MEMSET lv_memset
#define CODE128_STRLEN lv_strlen
#define CODE128_ASSERT LV_ASSERT
#define CODE128_QUIET_ZONE_LEN 10
#define CODE128_CHAR_LEN 11
#define CODE128_STOP_CODE_LEN 13
#define CODE128_START_CODE_A 103
#define CODE128_START_CODE_B 104
#define CODE128_START_CODE_C 105
#define CODE128_MODE_A 'a'
#define CODE128_MODE_B 'b'
#define CODE128_MODE_C 'c'
#define CODE128_MIN_ENCODE_LEN (CODE128_QUIET_ZONE_LEN * 2 + CODE128_CHAR_LEN * 2 + CODE128_STOP_CODE_LEN)
static const int code128_pattern[] = {
// value: pattern, bar/space widths
1740, // 0: 11011001100, 212222
1644, // 1: 11001101100, 222122
1638, // 2: 11001100110, 222221
1176, // 3: 10010011000, 121223
1164, // 4: 10010001100, 121322
1100, // 5: 10001001100, 131222
1224, // 6: 10011001000, 122213
1220, // 7: 10011000100, 122312
1124, // 8: 10001100100, 132212
1608, // 9: 11001001000, 221213
1604, // 10: 11001000100, 221312
1572, // 11: 11000100100, 231212
1436, // 12: 10110011100, 112232
1244, // 13: 10011011100, 122132
1230, // 14: 10011001110, 122231
1484, // 15: 10111001100, 113222
1260, // 16: 10011101100, 123122
1254, // 17: 10011100110, 123221
1650, // 18: 11001110010, 223211
1628, // 19: 11001011100, 221132
1614, // 20: 11001001110, 221231
1764, // 21: 11011100100, 213212
1652, // 22: 11001110100, 223112
1902, // 23: 11101101110, 312131
1868, // 24: 11101001100, 311222
1836, // 25: 11100101100, 321122
1830, // 26: 11100100110, 321221
1892, // 27: 11101100100, 312212
1844, // 28: 11100110100, 322112
1842, // 29: 11100110010, 322211
1752, // 30: 11011011000, 212123
1734, // 31: 11011000110, 212321
1590, // 32: 11000110110, 232121
1304, // 33: 10100011000, 111323
1112, // 34: 10001011000, 131123
1094, // 35: 10001000110, 131321
1416, // 36: 10110001000, 112313
1128, // 37: 10001101000, 132113
1122, // 38: 10001100010, 132311
1672, // 39: 11010001000, 211313
1576, // 40: 11000101000, 231113
1570, // 41: 11000100010, 231311
1464, // 42: 10110111000, 112133
1422, // 43: 10110001110
1134, // 44: 10001101110
1496, // 45: 10111011000, 113123
1478, // 46: 10111000110, 113321
1142, // 47: 10001110110, 133121
1910, // 48: 11101110110, 313121
1678, // 49: 11010001110, 211331
1582, // 50: 11000101110, 231131
1768, // 51: 11011101000, 213113
1762, // 52: 11011100010, 213311
1774, // 53: 11011101110, 213131
1880, // 54: 11101011000, 311123
1862, // 55: 11101000110, 311321
1814, // 56: 11100010110, 331121
1896, // 57: 11101101000, 312113
1890, // 58: 11101100010, 312311
1818, // 59: 11100011010, 332111
1914, // 60: 11101111010, 314111
1602, // 61: 11001000010, 221411
1930, // 62: 11110001010, 431111
1328, // 63: 10100110000, 111224
1292, // 64: 10100001100, 111422
1200, // 65: 10010110000, 121124
1158, // 66: 10010000110, 121421
1068, // 67: 10000101100, 141122
1062, // 68: 10000100110, 141221
1424, // 69: 10110010000, 112214
1412, // 70: 10110000100, 112412
1232, // 71: 10011010000, 122114
1218, // 72: 10011000010, 122411
1076, // 73: 10000110100, 142112
1074, // 74: 10000110010, 142211
1554, // 75: 11000010010, 241211
1616, // 76: 11001010000, 221114
1978, // 77: 11110111010, 413111
1556, // 78: 11000010100, 241112
1146, // 79: 10001111010, 134111
1340, // 80: 10100111100, 111242
1212, // 81: 10010111100, 121142
1182, // 82: 10010011110, 121241
1508, // 83: 10111100100, 114212
1268, // 84: 10011110100, 124112
1266, // 85: 10011110010, 124211
1956, // 86: 11110100100, 411212
1940, // 87: 11110010100, 421112
1938, // 88: 11110010010, 421211
1758, // 89: 11011011110, 212141
1782, // 90: 11011110110, 214121
1974, // 91: 11110110110, 412121
1400, // 92: 10101111000, 111143
1310, // 93: 10100011110, 111341
1118, // 94: 10001011110, 131141
1512, // 95: 10111101000, 114113
1506, // 96: 10111100010, 114311
1960, // 97: 11110101000, 411113
1954, // 98: 11110100010, 411311
1502, // 99: 10111011110, 113141
1518, // 100: 10111101110, 114131
1886, // 101: 11101011110, 311141
1966, // 102: 11110101110, 411131
1668, // 103: 11010000100, 211412
1680, // 104: 11010010000, 211214
1692 // 105: 11010011100, 211232
};
static const int code128_stop_pattern = 6379; // 1100011101011, 2331112
struct code128_step {
int prev_ix; // Index of previous step, if any
const char * next_input; // Remaining input
unsigned short len; // The length of the pattern so far (includes this step)
char mode; // State for the current encoding
signed char code; // What code should be written for this step
};
struct code128_state {
struct code128_step * steps;
int allocated_steps;
int current_ix;
int todo_ix;
int best_ix;
size_t maxlength;
};
size_t code128_estimate_len(const char * s)
{
return CODE128_QUIET_ZONE_LEN
+ CODE128_CHAR_LEN // start code
+ CODE128_CHAR_LEN * (CODE128_STRLEN(s) * 11 / 10) // contents + 10% padding
+ CODE128_CHAR_LEN // checksum
+ CODE128_STOP_CODE_LEN
+ CODE128_QUIET_ZONE_LEN;
}
static void code128_append_pattern(int pattern, int pattern_length, char * out)
{
// All patterns have their first bit set by design
CODE128_ASSERT(pattern & (1 << (pattern_length - 1)));
int i;
for(i = pattern_length - 1; i >= 0; i--) {
// cast avoids warning: implicit conversion from 'int' to 'char' changes value from 255 to -1 [-Wconstant-conversion]
*out++ = (unsigned char)((pattern & (1 << i)) ? 255 : 0);
}
}
static int code128_append_code(int code, char * out)
{
CODE128_ASSERT(code >= 0 && code < (int)(sizeof(code128_pattern) / sizeof(code128_pattern[0])));
code128_append_pattern(code128_pattern[code], CODE128_CHAR_LEN, out);
return CODE128_CHAR_LEN;
}
static int code128_append_stop_code(char * out)
{
code128_append_pattern(code128_stop_pattern, CODE128_STOP_CODE_LEN, out);
return CODE128_STOP_CODE_LEN;
}
static signed char code128_switch_code(char from_mode, char to_mode)
{
switch(from_mode) {
case CODE128_MODE_A:
switch(to_mode) {
case CODE128_MODE_B:
return 100;
case CODE128_MODE_C:
return 99;
}
break;
case CODE128_MODE_B:
switch(to_mode) {
case CODE128_MODE_A:
return 101;
case CODE128_MODE_C:
return 99;
}
break;
case CODE128_MODE_C:
switch(to_mode) {
case CODE128_MODE_B:
return 100;
case CODE128_MODE_A:
return 101;
}
break;
default:
break;
}
CODE128_ASSERT(0); // Invalid mode switch
return -1;
}
static signed char code128a_ascii_to_code(signed char value)
{
if(value >= ' ' && value <= '_')
return (signed char)(value - ' ');
else if(value >= 0 && value < ' ')
return (signed char)(value + 64);
else if(value == (signed char)CODE128_FNC1)
return 102;
else if(value == (signed char)CODE128_FNC2)
return 97;
else if(value == (signed char)CODE128_FNC3)
return 96;
else if(value == (signed char)CODE128_FNC4)
return 101;
else
return -1;
}
static signed char code128b_ascii_to_code(signed char value)
{
if(value >= ' ') // value <= 127 is implied
return (signed char)(value - ' ');
else if(value == (signed char)CODE128_FNC1)
return 102;
else if(value == (signed char)CODE128_FNC2)
return 97;
else if(value == (signed char)CODE128_FNC3)
return 96;
else if(value == (signed char)CODE128_FNC4)
return 100;
else
return -1;
}
static signed char code128c_ascii_to_code(const char * values)
{
if(values[0] == CODE128_FNC1)
return 102;
if(values[0] >= '0' && values[0] <= '9' &&
values[1] >= '0' && values[1] <= '9') {
char code = 10 * (values[0] - '0') + (values[1] - '0');
return code;
}
return -1;
}
static int code128_do_a_step(struct code128_step * base, int prev_ix, int ix)
{
struct code128_step * previous_step = &base[prev_ix];
struct code128_step * step = &base[ix];
char value = *previous_step->next_input;
// NOTE: Currently we can't encode NULL
if(value == 0)
return 0;
step->code = code128a_ascii_to_code(value);
if(step->code < 0)
return 0;
step->prev_ix = prev_ix;
step->next_input = previous_step->next_input + 1;
step->mode = CODE128_MODE_A;
step->len = previous_step->len + CODE128_CHAR_LEN;
if(step->mode != previous_step->mode)
step->len += CODE128_CHAR_LEN; // Need to switch modes
return 1;
}
static int code128_do_b_step(struct code128_step * base, int prev_ix, int ix)
{
struct code128_step * previous_step = &base[prev_ix];
struct code128_step * step = &base[ix];
char value = *previous_step->next_input;
// NOTE: Currently we can't encode NULL
if(value == 0)
return 0;
step->code = code128b_ascii_to_code(value);
if(step->code < 0)
return 0;
step->prev_ix = prev_ix;
step->next_input = previous_step->next_input + 1;
step->mode = CODE128_MODE_B;
step->len = previous_step->len + CODE128_CHAR_LEN;
if(step->mode != previous_step->mode)
step->len += CODE128_CHAR_LEN; // Need to switch modes
return 1;
}
static int code128_do_c_step(struct code128_step * base, int prev_ix, int ix)
{
struct code128_step * previous_step = &base[prev_ix];
struct code128_step * step = &base[ix];
char value = *previous_step->next_input;
// NOTE: Currently we can't encode NULL
if(value == 0)
return 0;
step->code = code128c_ascii_to_code(previous_step->next_input);
if(step->code < 0)
return 0;
step->prev_ix = prev_ix;
step->next_input = previous_step->next_input + 1;
// Mode C consumes 2 characters for codes 0-99
if(step->code < 100)
step->next_input++;
step->mode = CODE128_MODE_C;
step->len = previous_step->len + CODE128_CHAR_LEN;
if(step->mode != previous_step->mode)
step->len += CODE128_CHAR_LEN; // Need to switch modes
return 1;
}
static struct code128_step * code128_alloc_step(struct code128_state * state)
{
if(state->todo_ix >= state->allocated_steps) {
state->allocated_steps += 1024;
state->steps = (struct code128_step *) CODE128_REALLOC(state->steps,
state->allocated_steps * sizeof(struct code128_step));
}
struct code128_step * step = &state->steps[state->todo_ix];
CODE128_MEMSET(step, 0, sizeof(*step));
return step;
}
static void code128_do_step(struct code128_state * state)
{
struct code128_step * step = &state->steps[state->current_ix];
if(*step->next_input == 0) {
// Done, so see if we have a new shortest encoding.
if((step->len < state->maxlength) ||
(state->best_ix < 0 && step->len == state->maxlength)) {
state->best_ix = state->current_ix;
// Update maxlength to avoid considering anything longer
state->maxlength = step->len;
}
return;
}
// Don't try if we're already at or beyond the max acceptable
// length;
if(step->len >= state->maxlength)
return;
char mode = step->mode;
code128_alloc_step(state);
int mode_c_worked = 0;
// Always try mode C
if(code128_do_c_step(state->steps, state->current_ix, state->todo_ix)) {
state->todo_ix++;
code128_alloc_step(state);
mode_c_worked = 1;
}
if(mode == CODE128_MODE_A) {
// If A works, stick with A. There's no advantage to switching
// to B proactively if A still works.
if(code128_do_a_step(state->steps, state->current_ix, state->todo_ix) ||
code128_do_b_step(state->steps, state->current_ix, state->todo_ix))
state->todo_ix++;
}
else if(mode == CODE128_MODE_B) {
// The same logic applies here. There's no advantage to switching
// proactively to A if B still works.
if(code128_do_b_step(state->steps, state->current_ix, state->todo_ix) ||
code128_do_a_step(state->steps, state->current_ix, state->todo_ix))
state->todo_ix++;
}
else if(!mode_c_worked) {
// In mode C. If mode C worked and we're in mode C, trying anything
// else is pointless since the mode C encoding will be shorter and
// there won't be any mode switches.
// If we're leaving mode C, though, try both in case one ends up
// better than the other.
if(code128_do_a_step(state->steps, state->current_ix, state->todo_ix)) {
state->todo_ix++;
code128_alloc_step(state);
}
if(code128_do_b_step(state->steps, state->current_ix, state->todo_ix))
state->todo_ix++;
}
}
size_t code128_encode_raw(const char * s, char * out, size_t maxlength)
{
struct code128_state state;
const size_t overhead = CODE128_QUIET_ZONE_LEN
+ CODE128_CHAR_LEN // checksum
+ CODE128_STOP_CODE_LEN
+ CODE128_QUIET_ZONE_LEN;
if(maxlength < overhead + CODE128_CHAR_LEN + CODE128_CHAR_LEN) {
// Need space to encode the start character and one additional
// character.
return 0;
}
state.allocated_steps = 256;
state.steps = (struct code128_step *) CODE128_MALLOC(state.allocated_steps * sizeof(struct code128_step));
state.current_ix = 0;
state.todo_ix = 0;
state.maxlength = maxlength - overhead;
state.best_ix = -1;
// Initialize the first 3 steps for the 3 encoding routes (A, B, C)
state.steps[0].prev_ix = -1;
state.steps[0].next_input = s;
state.steps[0].len = CODE128_CHAR_LEN;
state.steps[0].mode = CODE128_MODE_C;
state.steps[0].code = CODE128_START_CODE_C;
state.steps[1].prev_ix = -1;
state.steps[1].next_input = s;
state.steps[1].len = CODE128_CHAR_LEN;
state.steps[1].mode = CODE128_MODE_A;
state.steps[1].code = CODE128_START_CODE_A;
state.steps[2].prev_ix = -1;
state.steps[2].next_input = s;
state.steps[2].len = CODE128_CHAR_LEN;
state.steps[2].mode = CODE128_MODE_B;
state.steps[2].code = CODE128_START_CODE_B;
state.todo_ix = 3;
// Keep going until no more work
do {
code128_do_step(&state);
state.current_ix++;
} while(state.current_ix != state.todo_ix);
// If no best_step, then fail.
if(state.best_ix < 0) {
CODE128_FREE(state.steps);
return 0;
}
// Determine the list of codes
size_t num_codes = state.maxlength / CODE128_CHAR_LEN;
char * codes = CODE128_MALLOC(num_codes);
CODE128_ASSERT(codes);
struct code128_step * step = &state.steps[state.best_ix];
size_t i;
for(i = num_codes - 1; i > 0; --i) {
struct code128_step * prev_step = &state.steps[step->prev_ix];
codes[i] = step->code;
if(step->mode != prev_step->mode) {
--i;
codes[i] = code128_switch_code(prev_step->mode, step->mode);
}
step = prev_step;
}
codes[0] = step->code;
// Encode everything up to the checksum
size_t actual_length = state.maxlength + overhead;
CODE128_MEMSET(out, 0, CODE128_QUIET_ZONE_LEN);
out += CODE128_QUIET_ZONE_LEN;
for(i = 0; i < num_codes; i++)
out += code128_append_code(codes[i], out);
// Compute the checksum
int sum = codes[0];
for(i = 1; i < num_codes; i++)
sum += (int)(codes[i] * i);
out += code128_append_code(sum % 103, out);
// Finalize the code.
out += code128_append_stop_code(out);
CODE128_MEMSET(out, 0, CODE128_QUIET_ZONE_LEN);
CODE128_FREE(codes);
CODE128_FREE(state.steps);
return actual_length;
}
/**
* @brief Encode the GS1 string
*
* This converts [FNC1] sequences to raw FNC1 characters and
* removes spaces before encoding the barcodes.
*
* @return the length of barcode data in bytes
*/
size_t code128_encode_gs1(const char * s, char * out, size_t maxlength)
{
size_t raw_size = CODE128_STRLEN(s) + 1;
char * raw = CODE128_MALLOC(raw_size);
CODE128_ASSERT(raw);
if(!raw) {
return 0;
}
char * p = raw;
for(; *s != '\0'; s++) {
if(strncmp(s, "[FNC1]", 6) == 0) {
*p++ = CODE128_FNC1;
s += 5;
}
else if(*s != ' ') {
*p++ = *s;
}
}
*p = '\0';
size_t length = code128_encode_raw(raw, out, maxlength);
CODE128_FREE(raw);
return length;
}
#endif /*LV_USE_BARCODE*/
@@ -0,0 +1,47 @@
// Copyright (c) 2013-15, LKC Technologies, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer. Redistributions in binary
// form must reproduce the above copyright notice, this list of conditions and
// the following disclaimer in the documentation and/or other materials
// provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
// HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CODE128_H
#define CODE128_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// Since the FNCn characters are not ASCII, define versions here to
// simplify encoding strings that include them.
#define CODE128_FNC1 '\xf1'
#define CODE128_FNC2 '\xf2'
#define CODE128_FNC3 '\xf3'
#define CODE128_FNC4 '\xf4'
size_t code128_estimate_len(const char * s);
size_t code128_encode_gs1(const char * s, char * out, size_t maxlength);
size_t code128_encode_raw(const char * s, char * out, size_t maxlength);
#ifdef __cplusplus
}
#endif
#endif // CODE128_H
@@ -0,0 +1,326 @@
/**
* @file lv_barcode.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../core/lv_obj_class_private.h"
#include "lv_barcode_private.h"
#include "../../lvgl.h"
#if LV_USE_BARCODE
#include "code128.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_barcode_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_barcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static bool lv_barcode_change_buf_size(lv_obj_t * obj, int32_t w, int32_t h);
static void lv_barcode_clear(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_barcode_class = {
.constructor_cb = lv_barcode_constructor,
.destructor_cb = lv_barcode_destructor,
.width_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_barcode_t),
.base_class = &lv_canvas_class,
.name = "lv_barcode",
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_barcode_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_barcode_set_dark_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->dark_color = color;
}
void lv_barcode_set_light_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->light_color = color;
}
void lv_barcode_set_scale(lv_obj_t * obj, uint16_t scale)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(scale == 0) {
scale = 1;
}
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->scale = scale;
}
void lv_barcode_set_direction(lv_obj_t * obj, lv_dir_t direction)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->direction = direction;
}
void lv_barcode_set_tiled(lv_obj_t * obj, bool tiled)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->tiled = tiled;
lv_image_set_inner_align(obj, tiled ? LV_IMAGE_ALIGN_TILE : LV_IMAGE_ALIGN_DEFAULT);
}
void lv_barcode_set_encoding(lv_obj_t * obj, lv_barcode_encoding_t encoding)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->encoding = encoding;
}
lv_result_t lv_barcode_update(lv_obj_t * obj, const char * data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(data);
if(data == NULL || lv_strlen(data) == 0) {
LV_LOG_WARN("data is empty");
lv_barcode_clear(obj);
return LV_RESULT_INVALID;
}
size_t len = code128_estimate_len(data);
LV_LOG_INFO("data: %s, len = %zu", data, len);
char * out_buf = lv_malloc(len);
LV_ASSERT_MALLOC(out_buf);
if(!out_buf) {
LV_LOG_ERROR("malloc failed for out_buf");
lv_barcode_clear(obj);
return LV_RESULT_INVALID;
}
lv_barcode_t * barcode = (lv_barcode_t *)obj;
int32_t barcode_w = 0;
switch(barcode->encoding) {
case LV_BARCODE_ENCODING_CODE128_GS1:
barcode_w = (int32_t) code128_encode_gs1(data, out_buf, len);
break;
case LV_BARCODE_ENCODING_CODE128_RAW:
barcode_w = (int32_t) code128_encode_raw(data, out_buf, len);
break;
}
LV_LOG_INFO("barcode width = %" LV_PRId32, barcode_w);
LV_ASSERT(barcode->scale > 0);
uint16_t scale = barcode->scale;
int32_t buf_w;
int32_t buf_h;
if(barcode->tiled) {
buf_w = (barcode->direction == LV_DIR_HOR) ? barcode_w * scale : 1;
buf_h = (barcode->direction == LV_DIR_VER) ? barcode_w * scale : 1;
}
else {
lv_obj_update_layout(obj);
buf_w = (barcode->direction == LV_DIR_HOR) ? barcode_w * scale : lv_obj_get_width(obj);
buf_h = (barcode->direction == LV_DIR_VER) ? barcode_w * scale : lv_obj_get_height(obj);
}
if(!lv_barcode_change_buf_size(obj, buf_w, buf_h)) {
lv_barcode_clear(obj);
lv_free(out_buf);
return LV_RESULT_INVALID;
}
/* Temporarily disable invalidation to improve the efficiency of lv_canvas_set_px */
lv_display_enable_invalidation(lv_obj_get_display(obj), false);
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
uint32_t stride = draw_buf->header.stride;
const lv_color_t color = lv_color_hex(1);
/* Clear the canvas */
lv_draw_buf_clear(draw_buf, NULL);
/* Set the palette */
lv_canvas_set_palette(obj, 0, lv_color_to_32(barcode->light_color, LV_OPA_COVER));
lv_canvas_set_palette(obj, 1, lv_color_to_32(barcode->dark_color, LV_OPA_COVER));
for(int32_t x = 0; x < barcode_w; x++) {
/*skip empty data*/
if(out_buf[x] == 0) {
continue;
}
for(uint16_t i = 0; i < scale; i++) {
int32_t offset = x * scale + i;
if(barcode->direction == LV_DIR_HOR) {
lv_canvas_set_px(obj, offset, 0, color, LV_OPA_COVER);
}
else { /*LV_DIR_VER*/
if(barcode->tiled) {
lv_canvas_set_px(obj, 0, offset, color, LV_OPA_COVER);
}
else {
uint8_t * dest = lv_draw_buf_goto_xy(draw_buf, 0, offset);
lv_memset(dest, 0xFF, stride);
}
}
}
}
/* Copy pixels by row */
if(!barcode->tiled && barcode->direction == LV_DIR_HOR && buf_h > 1) {
/* Skip the first row */
int32_t h = buf_h - 1;
const uint8_t * src = lv_draw_buf_goto_xy(draw_buf, 0, 0);
uint8_t * dest = lv_draw_buf_goto_xy(draw_buf, 0, 1);
while(h--) {
lv_memcpy(dest, src, stride);
dest += stride;
}
}
/* invalidate the canvas to refresh it */
lv_display_enable_invalidation(lv_obj_get_display(obj), true);
lv_obj_invalidate(obj);
lv_free(out_buf);
return LV_RESULT_OK;
}
lv_color_t lv_barcode_get_dark_color(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
return barcode->dark_color;
}
lv_color_t lv_barcode_get_light_color(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
return barcode->light_color;
}
uint16_t lv_barcode_get_scale(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
return barcode->scale;
}
lv_barcode_encoding_t lv_barcode_get_encoding(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_barcode_t * barcode = (const lv_barcode_t *)obj;
return barcode->encoding;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_barcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_barcode_t * barcode = (lv_barcode_t *)obj;
barcode->dark_color = lv_color_black();
barcode->light_color = lv_color_white();
barcode->scale = 1;
barcode->direction = LV_DIR_HOR;
barcode->encoding = LV_BARCODE_ENCODING_CODE128_GS1;
lv_image_set_inner_align(obj, LV_IMAGE_ALIGN_DEFAULT);
}
static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
if(draw_buf == NULL) return;
lv_image_cache_drop(draw_buf);
/*@fixme destroy buffer in cache free_cb.*/
lv_draw_buf_destroy(draw_buf);
}
static bool lv_barcode_change_buf_size(lv_obj_t * obj, int32_t w, int32_t h)
{
LV_ASSERT_NULL(obj);
if(w <= 0 || h <= 0) {
LV_LOG_WARN("invalid size: %" LV_PRId32 " x %" LV_PRId32, w, h);
return false;
}
lv_draw_buf_t * old_buf = lv_canvas_get_draw_buf(obj);
lv_draw_buf_t * new_buf = lv_draw_buf_create(w, h, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
if(new_buf == NULL) {
LV_LOG_ERROR("malloc failed for canvas buffer");
return false;
}
lv_canvas_set_draw_buf(obj, new_buf);
LV_LOG_INFO("set canvas buffer: %p, width = %" LV_PRId32, (void *)new_buf, w);
if(old_buf != NULL) lv_draw_buf_destroy(old_buf);
return true;
}
static void lv_barcode_clear(lv_obj_t * obj)
{
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
if(!draw_buf) {
return;
}
lv_draw_buf_clear(draw_buf, NULL);
lv_image_cache_drop(draw_buf);
lv_obj_invalidate(obj);
}
#endif /*LV_USE_BARCODE*/
@@ -0,0 +1,143 @@
/**
* @file lv_barcode.h
*
*/
#ifndef LV_BARCODE_H
#define LV_BARCODE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include "../../misc/lv_types.h"
#include "../../misc/lv_color.h"
#include "../../widgets/canvas/lv_canvas.h"
#if LV_USE_BARCODE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
/**
* Code 128 with GS1 encoding. Strips `[FCN1]` and spaces.
*/
LV_BARCODE_ENCODING_CODE128_GS1,
/**
* Code 128 with raw encoding.
*/
LV_BARCODE_ENCODING_CODE128_RAW,
} lv_barcode_encoding_t;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_barcode_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an empty barcode (an `lv_canvas`) object.
* @param parent point to an object where to create the barcode
* @return pointer to the created barcode object
*/
lv_obj_t * lv_barcode_create(lv_obj_t * parent);
/**
* Set the dark color of a barcode object
* @param obj pointer to barcode object
* @param color dark color of the barcode
*/
void lv_barcode_set_dark_color(lv_obj_t * obj, lv_color_t color);
/**
* Set the light color of a barcode object
* @param obj pointer to barcode object
* @param color light color of the barcode
*/
void lv_barcode_set_light_color(lv_obj_t * obj, lv_color_t color);
/**
* Set the scale of a barcode object
* @param obj pointer to barcode object
* @param scale scale factor
*/
void lv_barcode_set_scale(lv_obj_t * obj, uint16_t scale);
/**
* Set the direction of a barcode object
* @param obj pointer to barcode object
* @param direction draw direction (`LV_DIR_HOR` or `LB_DIR_VER`)
*/
void lv_barcode_set_direction(lv_obj_t * obj, lv_dir_t direction);
/**
* Set the tiled mode of a barcode object
* @param obj pointer to barcode object
* @param tiled true: tiled mode, false: normal mode (default)
*/
void lv_barcode_set_tiled(lv_obj_t * obj, bool tiled);
/**
* Set the encoding of a barcode object
* @param obj pointer to barcode object
* @param encoding encoding (default is `LV_BARCODE_CODE128_GS1`)
*/
void lv_barcode_set_encoding(lv_obj_t * obj, lv_barcode_encoding_t encoding);
/**
* Set the data of a barcode object
* @param obj pointer to barcode object
* @param data data to display
* @return LV_RESULT_OK: if no error; LV_RESULT_INVALID: on error
*/
lv_result_t lv_barcode_update(lv_obj_t * obj, const char * data);
/**
* Get the dark color of a barcode object
* @param obj pointer to barcode object
* @return dark color of the barcode
*/
lv_color_t lv_barcode_get_dark_color(lv_obj_t * obj);
/**
* Get the light color of a barcode object
* @param obj pointer to barcode object
* @return light color of the barcode
*/
lv_color_t lv_barcode_get_light_color(lv_obj_t * obj);
/**
* Get the scale of a barcode object
* @param obj pointer to barcode object
* @return scale factor
*/
uint16_t lv_barcode_get_scale(lv_obj_t * obj);
/**
* Get the encoding of a barcode object
* @param obj pointer to barcode object
* @return encoding
*/
lv_barcode_encoding_t lv_barcode_get_encoding(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BARCODE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BARCODE_H*/
@@ -0,0 +1,56 @@
/**
* @file lv_barcode_private.h
*
*/
#ifndef LV_BARCODE_PRIVATE_H
#define LV_BARCODE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../widgets/canvas/lv_canvas_private.h"
#include "lv_barcode.h"
#if LV_USE_BARCODE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of barcode*/
struct _lv_barcode_t {
lv_canvas_t canvas;
lv_color_t dark_color;
lv_color_t light_color;
uint16_t scale;
lv_dir_t direction;
bool tiled;
lv_barcode_encoding_t encoding;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_BARCODE */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_BARCODE_PRIVATE_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/**
* @file lv_bin_decoder.h
*
*/
#ifndef LV_BIN_DECODER_H
#define LV_BIN_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the binary image decoder module
*/
void lv_bin_decoder_init(void);
/**
* Get info about a lvgl binary image
* @param decoder the decoder where this function belongs
* @param dsc image descriptor containing the source and type of the image and other info.
* @param header store the image data here
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
*/
lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header);
lv_result_t lv_bin_decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
const lv_area_t * full_area, lv_area_t * decoded_area);
/**
* Open a lvgl binary image
* @param decoder the decoder where this function belongs
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
*/
lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
/**
* Close the pending decoding. Free resources etc.
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
*/
void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_BIN_DECODER_H*/
+253
View File
@@ -0,0 +1,253 @@
/**
* @file lv_bmp.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder_private.h"
#include "../../../lvgl.h"
#if LV_USE_BMP
#include <string.h>
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define DECODER_NAME "BMP"
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t f;
unsigned int px_offset;
int px_width;
int px_height;
unsigned int bpp;
int row_size_bytes;
} bmp_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
const lv_area_t * full_area, lv_area_t * decoded_area);
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_bmp_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_get_area_cb(dec, decoder_get_area);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->name = DECODER_NAME;
}
void lv_bmp_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a BMP image
* @param dsc image descriptor containing the source and type of the image and other info.
* @param header store the info here
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
*/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
{
LV_UNUSED(decoder);
const void * src = dsc->src;
lv_image_src_t src_type = dsc->src_type; /*Get the source type*/
/*If it's a BMP file...*/
if(src_type == LV_IMAGE_SRC_FILE) {
const char * fn = src;
if(lv_strcmp(lv_fs_get_ext(fn), "bmp") == 0) { /*Check the extension*/
/*Save the data in the header*/
uint8_t headers[54];
lv_fs_read(&dsc->file, headers, 54, NULL);
uint32_t w;
uint32_t h;
lv_memcpy(&w, headers + 18, 4);
lv_memcpy(&h, headers + 22, 4);
header->w = w;
header->h = h;
uint16_t bpp;
lv_memcpy(&bpp, headers + 28, 2);
switch(bpp) {
case 16:
header->cf = LV_COLOR_FORMAT_RGB565;
break;
case 24:
header->cf = LV_COLOR_FORMAT_RGB888;
break;
case 32:
header->cf = LV_COLOR_FORMAT_ARGB8888;
break;
default:
LV_LOG_WARN("Not supported bpp: %d", bpp);
return LV_RESULT_OK;
}
return LV_RESULT_OK;
}
}
/* BMP file as data not supported for simplicity.
* Convert them to LVGL compatible C arrays directly. */
else if(src_type == LV_IMAGE_SRC_VARIABLE) {
return LV_RESULT_INVALID;
}
return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a BMP image and return the decided image
* @param decoder pointer to the decoder
* @param dsc pointer to the decoder descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
*/
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
/*If it's a BMP file...*/
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
if(lv_strcmp(lv_fs_get_ext(fn), "bmp") != 0) {
return LV_RESULT_INVALID; /*Check the extension*/
}
bmp_dsc_t b;
lv_memset(&b, 0x00, sizeof(b));
lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return LV_RESULT_INVALID;
uint8_t header[54];
lv_fs_read(&b.f, header, 54, NULL);
if(0x42 != header[0] || 0x4d != header[1]) {
lv_fs_close(&b.f);
return LV_RESULT_INVALID;
}
lv_memcpy(&b.px_offset, header + 10, 4);
lv_memcpy(&b.px_width, header + 18, 4);
lv_memcpy(&b.px_height, header + 22, 4);
lv_memcpy(&b.bpp, header + 28, 2);
b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4;
dsc->user_data = lv_malloc(sizeof(bmp_dsc_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) return LV_RESULT_INVALID;
lv_memcpy(dsc->user_data, &b, sizeof(b));
return LV_RESULT_OK;
}
/* BMP file as data not supported for simplicity.
* Convert them to LVGL compatible C arrays directly. */
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
return LV_RESULT_INVALID;
}
return LV_RESULT_INVALID; /*If not returned earlier then it failed*/
}
static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
const lv_area_t * full_area, lv_area_t * decoded_area)
{
LV_UNUSED(decoder);
bmp_dsc_t * b = dsc->user_data;
lv_draw_buf_t * decoded = (void *)dsc->decoded;
if(decoded_area->y1 == LV_COORD_MIN) {
*decoded_area = *full_area;
decoded_area->y2 = decoded_area->y1;
int32_t w_px = lv_area_get_width(full_area);
lv_draw_buf_t * reshaped = lv_draw_buf_reshape(decoded, dsc->header.cf, w_px, 1, LV_STRIDE_AUTO);
if(reshaped == NULL) {
if(decoded != NULL) {
lv_draw_buf_destroy(decoded);
decoded = NULL;
dsc->decoded = NULL;
}
decoded = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, w_px, 1, dsc->header.cf, LV_STRIDE_AUTO);
if(decoded == NULL) return LV_RESULT_INVALID;
}
else {
decoded = reshaped;
}
dsc->decoded = decoded;
}
else {
decoded_area->y1++;
decoded_area->y2++;
}
if(decoded_area->y1 > full_area->y2) {
return LV_RESULT_INVALID;
}
else {
int32_t y = (b->px_height - 1) - (decoded_area->y1); /*BMP images are stored upside down*/
uint32_t p = b->px_offset + b->row_size_bytes * y;
p += (decoded_area->x1) * (b->bpp / 8);
lv_fs_seek(&b->f, p, LV_FS_SEEK_SET);
uint32_t line_width_byte = lv_area_get_width(full_area) * (b->bpp / 8);
lv_fs_read(&b->f, decoded->data, line_width_byte, NULL);
return LV_RESULT_OK;
}
}
/**
* Free the allocated resources
*/
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
bmp_dsc_t * b = dsc->user_data;
lv_fs_close(&b->f);
lv_free(dsc->user_data);
if(dsc->decoded) lv_draw_buf_destroy((void *)dsc->decoded);
}
#endif /*LV_USE_BMP*/
@@ -0,0 +1,43 @@
/**
* @file lv_bmp.h
*
*/
#ifndef LV_BMP_H
#define LV_BMP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_BMP
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_bmp_init(void);
void lv_bmp_deinit(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BMP*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BMP_H*/
@@ -0,0 +1,21 @@
Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper
Copyright (c) 2001-2025 Expat maintainers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
#!/bin/bash
#Add LVGL #if LV_USE_THORVG_INTERNAL guard
#Usage
# find -name "*.cpp" | xargs ./add_lvgl_if.sh
# find -name "t*.h" | xargs ./add_lvgl_if.sh
sed '0,/\*\/$/ {/\*\/$/ {n; s|^|\n#include "../../lv_conf_internal.h"\n#if LV_USE_XML\n|}}' $@ -i
sed -i -e '$a\
\
#endif /* LV_USE_XML */\
' $@ -i
@@ -0,0 +1,129 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1999-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2007 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#define ASCII_A 0x41
#define ASCII_B 0x42
#define ASCII_C 0x43
#define ASCII_D 0x44
#define ASCII_E 0x45
#define ASCII_F 0x46
#define ASCII_G 0x47
#define ASCII_H 0x48
#define ASCII_I 0x49
#define ASCII_J 0x4A
#define ASCII_K 0x4B
#define ASCII_L 0x4C
#define ASCII_M 0x4D
#define ASCII_N 0x4E
#define ASCII_O 0x4F
#define ASCII_P 0x50
#define ASCII_Q 0x51
#define ASCII_R 0x52
#define ASCII_S 0x53
#define ASCII_T 0x54
#define ASCII_U 0x55
#define ASCII_V 0x56
#define ASCII_W 0x57
#define ASCII_X 0x58
#define ASCII_Y 0x59
#define ASCII_Z 0x5A
#define ASCII_a 0x61
#define ASCII_b 0x62
#define ASCII_c 0x63
#define ASCII_d 0x64
#define ASCII_e 0x65
#define ASCII_f 0x66
#define ASCII_g 0x67
#define ASCII_h 0x68
#define ASCII_i 0x69
#define ASCII_j 0x6A
#define ASCII_k 0x6B
#define ASCII_l 0x6C
#define ASCII_m 0x6D
#define ASCII_n 0x6E
#define ASCII_o 0x6F
#define ASCII_p 0x70
#define ASCII_q 0x71
#define ASCII_r 0x72
#define ASCII_s 0x73
#define ASCII_t 0x74
#define ASCII_u 0x75
#define ASCII_v 0x76
#define ASCII_w 0x77
#define ASCII_x 0x78
#define ASCII_y 0x79
#define ASCII_z 0x7A
#define ASCII_0 0x30
#define ASCII_1 0x31
#define ASCII_2 0x32
#define ASCII_3 0x33
#define ASCII_4 0x34
#define ASCII_5 0x35
#define ASCII_6 0x36
#define ASCII_7 0x37
#define ASCII_8 0x38
#define ASCII_9 0x39
#define ASCII_TAB 0x09
#define ASCII_SPACE 0x20
#define ASCII_EXCL 0x21
#define ASCII_QUOT 0x22
#define ASCII_AMP 0x26
#define ASCII_APOS 0x27
#define ASCII_MINUS 0x2D
#define ASCII_PERIOD 0x2E
#define ASCII_COLON 0x3A
#define ASCII_SEMI 0x3B
#define ASCII_LT 0x3C
#define ASCII_EQUALS 0x3D
#define ASCII_GT 0x3E
#define ASCII_LSQB 0x5B
#define ASCII_RSQB 0x5D
#define ASCII_UNDERSCORE 0x5F
#define ASCII_LPAREN 0x28
#define ASCII_RPAREN 0x29
#define ASCII_FF 0x0C
#define ASCII_SLASH 0x2F
#define ASCII_HASH 0x23
#define ASCII_PIPE 0x7C
#define ASCII_COMMA 0x2C
#endif /* LV_USE_XML */
@@ -0,0 +1,72 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
#endif /* LV_USE_XML */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,151 @@
/* expat_config.h. Generated from expat_config.h.in by configure. */
#include "../../lv_conf_internal.h"
#if LV_USE_XML
/* expat_config.h.in. Generated from configure.ac by autoheader. */
#ifndef EXPAT_CONFIG_H
#define EXPAT_CONFIG_H 1
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* 1234 = LILENDIAN, 4321 = BIGENDIAN */
#define BYTEORDER 1234
#define XML_POOR_ENTROPY 1
/* Define to 1 if you have the `arc4random' function. */
/* #undef HAVE_ARC4RANDOM */
/* Define to 1 if you have the `arc4random_buf' function. */
/* #undef HAVE_ARC4RANDOM_BUF */
/* define if the compiler supports basic C++11 syntax */
/*#define HAVE_CXX11 1*/
/* Define to 1 if you have the <dlfcn.h> header file. */
/*#define HAVE_DLFCN_H 1*/
/* Define to 1 if you have the <fcntl.h> header file. */
/*#define HAVE_FCNTL_H 1*/
/* Define to 1 if you have the `getpagesize' function. */
/*#define HAVE_GETPAGESIZE 1*/
/* Define to 1 if you have the `getrandom' function. */
/*#define HAVE_GETRANDOM 1*/
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `bsd' library (-lbsd). */
/* #undef HAVE_LIBBSD */
/* Define to 1 if you have a working `mmap' system call. */
/*#define HAVE_MMAP 1*/
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
/*#define HAVE_STRINGS_H 1*/
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have `syscall' and `SYS_getrandom'. */
//#define HAVE_SYSCALL_GETRANDOM 0
/* Define to 1 if you have the <sys/param.h> header file. */
/*#define HAVE_SYS_PARAM_H 1*/
/* Define to 1 if you have the <sys/stat.h> header file. */
/*#define HAVE_SYS_STAT_H 1*/
/* Define to 1 if you have the <sys/types.h> header file. */
/*#define HAVE_SYS_TYPES_H 1*/
/* Define to 1 if you have the <unistd.h> header file. */
/*#define HAVE_UNISTD_H 1*/
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "expat"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "https://github.com/libexpat/libexpat/issues"
/* Define to the full name of this package. */
#define PACKAGE_NAME "expat"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "expat 2.6.3"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "expat"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.6.3"
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "2.6.3"
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* # undef WORDS_BIGENDIAN */
# endif
#endif
/* Define to allow retrieving the byte offsets for attribute names and values.
*/
/* #undef XML_ATTR_INFO */
/* Define to specify how much context to retain around the current parse
point, 0 to disable. */
#define XML_CONTEXT_BYTES 1024
/* Define to include code reading entropy from `/dev/urandom'. */
/*#define XML_DEV_URANDOM 1*/
/* Define to make parameter entity parsing functionality available. */
/*#define XML_DTD 1*/
/* Define as 1/0 to enable/disable support for general entities. */
#define XML_GE 0
/* Define to make XML Namespaces functionality available. */
/*#define XML_NS 1*/
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `long int' if <sys/types.h> does not define. */
/* #undef off_t */
#endif // ndef EXPAT_CONFIG_H
#endif /* LV_USE_XML */
@@ -0,0 +1,171 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2016 Cristian Rodríguez <crrodriguez@opensuse.org>
Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#ifndef Expat_External_INCLUDED
#define Expat_External_INCLUDED 1
/* External API definitions */
/* Expat tries very hard to make the API boundary very specifically
defined. There are two macros defined to control this boundary;
each of these can be defined before including this header to
achieve some different behavior, but doing so it not recommended or
tested frequently.
XMLCALL - The calling convention to use for all calls across the
"library boundary." This will default to cdecl, and
try really hard to tell the compiler that's what we
want.
XMLIMPORT - Whatever magic is needed to note that a function is
to be imported from a dynamically loaded library
(.dll, .so, or .sl, depending on your platform).
The XMLCALL macro was added in Expat 1.95.7. The only one which is
expected to be directly useful in client code is XMLCALL.
Note that on at least some Unix versions, the Expat library must be
compiled with the cdecl calling convention as the default since
system headers may assume the cdecl convention.
*/
#ifndef XMLCALL
# if defined(_MSC_VER)
# define XMLCALL __cdecl
# elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER)
# define XMLCALL __attribute__((cdecl))
# else
/* For any platform which uses this definition and supports more than
one calling convention, we need to extend this definition to
declare the convention used on that platform, if it's possible to
do so.
If this is the case for your platform, please file a bug report
with information on how to identify your platform via the C
pre-processor and how to specify the same calling convention as the
platform's malloc() implementation.
*/
# define XMLCALL
# endif
#endif /* not defined XMLCALL */
#if ! defined(XML_STATIC) && ! defined(XMLIMPORT)
# ifndef XML_BUILDING_EXPAT
/* using Expat from an application */
# if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) && ! defined(__CYGWIN__)
# define XMLIMPORT __declspec(dllimport)
# endif
# endif
#endif /* not defined XML_STATIC */
#ifndef XML_ENABLE_VISIBILITY
# define XML_ENABLE_VISIBILITY 0
#endif
#if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY
# define XMLIMPORT __attribute__((visibility("default")))
#endif
/* If we didn't define it above, define it away: */
#ifndef XMLIMPORT
# define XMLIMPORT
#endif
#if defined(__GNUC__) \
&& (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
# define XML_ATTR_MALLOC __attribute__((__malloc__))
#else
# define XML_ATTR_MALLOC
#endif
#if defined(__GNUC__) \
&& ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
#else
# define XML_ATTR_ALLOC_SIZE(x)
#endif
#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XML_UNICODE_WCHAR_T
# ifndef XML_UNICODE
# define XML_UNICODE
# endif
# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2)
# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc"
# endif
#endif
#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
# ifdef XML_UNICODE_WCHAR_T
typedef wchar_t XML_Char;
typedef wchar_t XML_LChar;
# else
typedef unsigned short XML_Char;
typedef char XML_LChar;
# endif /* XML_UNICODE_WCHAR_T */
#else /* Information is UTF-8 encoded. */
typedef char XML_Char;
typedef char XML_LChar;
#endif /* XML_UNICODE */
#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
typedef long long XML_Index;
typedef unsigned long long XML_Size;
#else
typedef long XML_Index;
typedef unsigned long XML_Size;
#endif /* XML_LARGE_SIZE */
#ifdef __cplusplus
}
#endif
#endif /* not Expat_External_INCLUDED */
#endif /* LV_USE_XML */
@@ -0,0 +1,73 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
#endif /* LV_USE_XML */
@@ -0,0 +1,182 @@
/* internal.h
Internal definitions used by Expat. This is not needed to compile
client code.
The following calling convention macros are defined for frequently
called functions:
FASTCALL - Used for those internal functions that have a simple
body and a low number of arguments and local variables.
PTRCALL - Used for functions called though function pointers.
PTRFASTCALL - Like PTRCALL, but for low number of arguments.
inline - Used for selected internal functions for which inlining
may improve performance on some platforms.
Note: Use of these macros is based on judgement, not hard rules,
and therefore subject to change.
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com>
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#if defined(__GNUC__) && defined(__i386__) && ! defined(__MINGW32__)
/* We'll use this version by default only where we know it helps.
regparm() generates warnings on Solaris boxes. See SF bug #692878.
Instability reported with egcs on a RedHat Linux 7.3.
Let's comment out:
#define FASTCALL __attribute__((stdcall, regparm(3)))
and let's try this:
*/
# define FASTCALL __attribute__((regparm(3)))
# define PTRFASTCALL __attribute__((regparm(3)))
#endif
/* Using __fastcall seems to have an unexpected negative effect under
MS VC++, especially for function pointers, so we won't use it for
now on that platform. It may be reconsidered for a future release
if it can be made more effective.
Likely reason: __fastcall on Windows is like stdcall, therefore
the compiler cannot perform stack optimizations for call clusters.
*/
/* Make sure all of these are defined if they aren't already. */
#ifndef FASTCALL
# define FASTCALL
#endif
#ifndef PTRCALL
# define PTRCALL
#endif
#ifndef PTRFASTCALL
# define PTRFASTCALL
#endif
#ifndef XML_MIN_SIZE
# if ! defined(__cplusplus) && ! defined(inline)
# ifdef __GNUC__
# define inline __inline
# endif /* __GNUC__ */
# endif
#endif /* XML_MIN_SIZE */
#ifdef __cplusplus
# define inline inline
#else
# ifndef inline
# define inline
# endif
#endif
#include <limits.h> // ULONG_MAX
#if defined(_WIN32) \
&& (! defined(__USE_MINGW_ANSI_STDIO) \
|| (1 - __USE_MINGW_ANSI_STDIO - 1 == 0))
# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
# if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u"
# else
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
# endif
#else
# define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
# if ! defined(ULONG_MAX)
# error Compiler did not define ULONG_MAX for us
# elif ULONG_MAX == 18446744073709551615u // 2^64-1
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
# else
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
# endif
#endif
#ifndef UNUSED_P
# define UNUSED_P(p) (void)p
#endif
/* NOTE BEGIN If you ever patch these defaults to greater values
for non-attack XML payload in your environment,
please file a bug report with libexpat. Thank you!
*/
#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \
100.0f
#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \
8388608 // 8 MiB, 2^23
/* NOTE END */
#include "expat.h" // so we can use type XML_Parser below
#ifdef __cplusplus
extern "C" {
#endif
void _INTERNAL_trim_to_complete_utf8_characters(const char *from,
const char **fromLimRef);
#if defined(XML_GE) && XML_GE == 1
unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
const char *unsignedCharToPrintable(unsigned char c);
#endif
extern
#if ! defined(XML_TESTING)
const
#endif
XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
#if defined(XML_TESTING)
extern unsigned int g_bytesScanned; // used for testing only
#endif
#ifdef __cplusplus
}
#endif
#endif /* LV_USE_XML */
@@ -0,0 +1,72 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
#endif /* LV_USE_XML */
@@ -0,0 +1,142 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
static const unsigned namingBitmap[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x04000000,
0x87FFFFFE, 0x07FFFFFE, 0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFE00F, 0xFC31FFFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, 0xFFFF0003, 0xFFFFFFFF,
0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, 0x00000000, 0x07FFFFFE,
0x000007FE, 0xFFFE0000, 0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, 0xFFF99FE0, 0x03C5FDFF,
0xB0000000, 0x00030003, 0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, 0xFFF99FE0, 0x23CDFDFF,
0xB0000000, 0x00000003, 0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, 0xFFFDDFE0, 0x03EFFDFF,
0x40000000, 0x00000003, 0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x000D7FFF,
0x0000003F, 0x00000000, 0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, 0x0007DAED, 0x50000000,
0x82315001, 0x002C62AB, 0x40000000, 0xF580C900, 0x00000007, 0x02010800,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0FFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0x03FFFFFF, 0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, 0x00000000, 0x00004C40,
0x00000000, 0x00000000, 0x00000007, 0x00000000, 0x00000000, 0x00000000,
0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, 0x001FFFFF, 0xFFFFFFFE,
0xFFFFFFFF, 0x07FFFFFF, 0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000000F,
0x00000000, 0x00000000, 0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, 0x00FFFFFF, 0x00000000,
0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, 0xFFFFD7C0, 0xFFFFFFFB,
0x547F7FFF, 0x000FFFFD, 0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000,
0x027FFFFF, 0xFFFFFFFE, 0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, 0xFFFFFFFF, 0x7CFFFFFF,
0xFFEF7FFF, 0x03FF3DFF, 0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, 0xFFF987E4, 0xD36DFDFF,
0x5E003987, 0x001FFFC0, 0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, 0xD63DC7EC, 0xC3BFC718,
0x00803DC7, 0x0000FF80, 0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, 0xFFFDDFEC, 0xC3FFFDFF,
0x00803DCF, 0x0000FFC3, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, 0xFEF02596, 0x3BFF6CAE,
0x03FF3F5F, 0x00000000, 0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, 0x661FFFFF, 0xFFFFFFFE,
0xFFFFFFFF, 0x77FFFFFF,
};
static const unsigned char nmstrtPages[] = {
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x00, 0x14, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
static const unsigned char namePages[] = {
0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, 0x00, 0x1F, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x26, 0x14, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
#endif /* LV_USE_XML */
@@ -0,0 +1,398 @@
/* ==========================================================================
* siphash.h - SipHash-2-4 in a single header file
* --------------------------------------------------------------------------
* Derived by William Ahern from the reference implementation[1] published[2]
* by Jean-Philippe Aumasson and Daniel J. Berstein.
* Minimal changes by Sebastian Pipping and Victor Stinner on top, see below.
* Licensed under the CC0 Public Domain Dedication license.
*
* 1. https://www.131002.net/siphash/siphash24.c
* 2. https://www.131002.net/siphash/
* --------------------------------------------------------------------------
* HISTORY:
*
* 2020-10-03 (Sebastian Pipping)
* - Drop support for Visual Studio 9.0/2008 and earlier
*
* 2019-08-03 (Sebastian Pipping)
* - Mark part of sip24_valid as to be excluded from clang-format
* - Re-format code using clang-format 9
*
* 2018-07-08 (Anton Maklakov)
* - Add "fall through" markers for GCC's -Wimplicit-fallthrough
*
* 2017-11-03 (Sebastian Pipping)
* - Hide sip_tobin and sip_binof unless SIPHASH_TOBIN macro is defined
*
* 2017-07-25 (Vadim Zeitlin)
* - Fix use of SIPHASH_MAIN macro
*
* 2017-07-05 (Sebastian Pipping)
* - Use _SIP_ULL macro to not require a C++11 compiler if compiled as C++
* - Add const qualifiers at two places
* - Ensure <=80 characters line length (assuming tab width 4)
*
* 2017-06-23 (Victor Stinner)
* - Address Win64 compile warnings
*
* 2017-06-18 (Sebastian Pipping)
* - Clarify license note in the header
* - Address C89 issues:
* - Stop using inline keyword (and let compiler decide)
* - Replace _Bool by int
* - Turn macro siphash24 into a function
* - Address invalid conversion (void pointer) by explicit cast
* - Address lack of stdint.h for Visual Studio 2003 to 2008
* - Always expose sip24_valid (for self-tests)
*
* 2012-11-04 - Born. (William Ahern)
* --------------------------------------------------------------------------
* USAGE:
*
* SipHash-2-4 takes as input two 64-bit words as the key, some number of
* message bytes, and outputs a 64-bit word as the message digest. This
* implementation employs two data structures: a struct sipkey for
* representing the key, and a struct siphash for representing the hash
* state.
*
* For converting a 16-byte unsigned char array to a key, use either the
* macro sip_keyof or the routine sip_tokey. The former instantiates a
* compound literal key, while the latter requires a key object as a
* parameter.
*
* unsigned char secret[16];
* arc4random_buf(secret, sizeof secret);
* struct sipkey *key = sip_keyof(secret);
*
* For hashing a message, use either the convenience macro siphash24 or the
* routines sip24_init, sip24_update, and sip24_final.
*
* struct siphash state;
* void *msg;
* size_t len;
* uint64_t hash;
*
* sip24_init(&state, key);
* sip24_update(&state, msg, len);
* hash = sip24_final(&state);
*
* or
*
* hash = siphash24(msg, len, key);
*
* To convert the 64-bit hash value to a canonical 8-byte little-endian
* binary representation, use either the macro sip_binof or the routine
* sip_tobin. The former instantiates and returns a compound literal array,
* while the latter requires an array object as a parameter.
* --------------------------------------------------------------------------
* NOTES:
*
* o Neither sip_keyof, sip_binof, nor siphash24 will work with compilers
* lacking compound literal support. Instead, you must use the lower-level
* interfaces which take as parameters the temporary state objects.
*
* o Uppercase macros may evaluate parameters more than once. Lowercase
* macros should not exhibit any such side effects.
* ==========================================================================
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#ifndef SIPHASH_H
#define SIPHASH_H
#include <stddef.h> /* size_t */
#include <stdint.h> /* uint64_t uint32_t uint8_t */
/*
* Workaround to not require a C++11 compiler for using ULL suffix
* if this code is included and compiled as C++; related GCC warning is:
* warning: use of C++11 long long integer constant [-Wlong-long]
*/
#define SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low))
#define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
#define SIP_U32TO8_LE(p, v) \
(p)[0] = (uint8_t)((v) >> 0); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24);
#define SIP_U64TO8_LE(p, v) \
SIP_U32TO8_LE((p) + 0, (uint32_t)((v) >> 0)); \
SIP_U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
#define SIP_U8TO64_LE(p) \
(((uint64_t)((p)[0]) << 0) | ((uint64_t)((p)[1]) << 8) \
| ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) \
| ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) \
| ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
#define SIPHASH_INITIALIZER {0, 0, 0, 0, {0}, 0, 0}
struct siphash {
uint64_t v0, v1, v2, v3;
unsigned char buf[8], *p;
uint64_t c;
}; /* struct siphash */
#define SIP_KEYLEN 16
struct sipkey {
uint64_t k[2];
}; /* struct sipkey */
#define sip_keyof(k) sip_tokey(&(struct sipkey){{0}}, (k))
static struct sipkey *
sip_tokey(struct sipkey *key, const void *src) {
key->k[0] = SIP_U8TO64_LE((const unsigned char *)src);
key->k[1] = SIP_U8TO64_LE((const unsigned char *)src + 8);
return key;
} /* sip_tokey() */
#ifdef SIPHASH_TOBIN
# define sip_binof(v) sip_tobin((unsigned char[8]){0}, (v))
static void *
sip_tobin(void *dst, uint64_t u64) {
SIP_U64TO8_LE((unsigned char *)dst, u64);
return dst;
} /* sip_tobin() */
#endif /* SIPHASH_TOBIN */
static void
sip_round(struct siphash *H, const int rounds) {
int i;
for (i = 0; i < rounds; i++) {
H->v0 += H->v1;
H->v1 = SIP_ROTL(H->v1, 13);
H->v1 ^= H->v0;
H->v0 = SIP_ROTL(H->v0, 32);
H->v2 += H->v3;
H->v3 = SIP_ROTL(H->v3, 16);
H->v3 ^= H->v2;
H->v0 += H->v3;
H->v3 = SIP_ROTL(H->v3, 21);
H->v3 ^= H->v0;
H->v2 += H->v1;
H->v1 = SIP_ROTL(H->v1, 17);
H->v1 ^= H->v2;
H->v2 = SIP_ROTL(H->v2, 32);
}
} /* sip_round() */
static struct siphash *
sip24_init(struct siphash *H, const struct sipkey *key) {
H->v0 = SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0];
H->v1 = SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1];
H->v2 = SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0];
H->v3 = SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1];
H->p = H->buf;
H->c = 0;
return H;
} /* sip24_init() */
#define sip_endof(a) (&(a)[sizeof(a) / sizeof *(a)])
static struct siphash *
sip24_update(struct siphash *H, const void *src, size_t len) {
const unsigned char *p = (const unsigned char *)src, *pe = p + len;
uint64_t m;
do {
while (p < pe && H->p < sip_endof(H->buf))
*H->p++ = *p++;
if (H->p < sip_endof(H->buf))
break;
m = SIP_U8TO64_LE(H->buf);
H->v3 ^= m;
sip_round(H, 2);
H->v0 ^= m;
H->p = H->buf;
H->c += 8;
} while (p < pe);
return H;
} /* sip24_update() */
static uint64_t
sip24_final(struct siphash *H) {
const char left = (char)(H->p - H->buf);
uint64_t b = (H->c + left) << 56;
switch (left) {
case 7:
b |= (uint64_t)H->buf[6] << 48;
/* fall through */
case 6:
b |= (uint64_t)H->buf[5] << 40;
/* fall through */
case 5:
b |= (uint64_t)H->buf[4] << 32;
/* fall through */
case 4:
b |= (uint64_t)H->buf[3] << 24;
/* fall through */
case 3:
b |= (uint64_t)H->buf[2] << 16;
/* fall through */
case 2:
b |= (uint64_t)H->buf[1] << 8;
/* fall through */
case 1:
b |= (uint64_t)H->buf[0] << 0;
/* fall through */
case 0:
break;
}
H->v3 ^= b;
sip_round(H, 2);
H->v0 ^= b;
H->v2 ^= 0xff;
sip_round(H, 4);
return H->v0 ^ H->v1 ^ H->v2 ^ H->v3;
} /* sip24_final() */
static uint64_t
siphash24(const void *src, size_t len, const struct sipkey *key) {
struct siphash state = SIPHASH_INITIALIZER;
return sip24_final(sip24_update(sip24_init(&state, key), src, len));
} /* siphash24() */
/*
* SipHash-2-4 output with
* k = 00 01 02 ...
* and
* in = (empty string)
* in = 00 (1 byte)
* in = 00 01 (2 bytes)
* in = 00 01 02 (3 bytes)
* ...
* in = 00 01 02 ... 3e (63 bytes)
*/
static int
sip24_valid(void) {
/* clang-format off */
static const unsigned char vectors[64][8] = {
{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
};
/* clang-format on */
unsigned char in[64];
struct sipkey k;
size_t i;
sip_tokey(&k, "\000\001\002\003\004\005\006\007\010\011"
"\012\013\014\015\016\017");
for (i = 0; i < sizeof in; ++i) {
in[i] = (unsigned char)i;
if (siphash24(in, i, &k) != SIP_U8TO64_LE(vectors[i]))
return 0;
}
return 1;
} /* sip24_valid() */
#ifdef SIPHASH_MAIN
# include <stdio.h>
int
main(void) {
const int ok = sip24_valid();
if (ok)
puts("OK");
else
puts("FAIL");
return ! ok;
} /* main() */
#endif /* SIPHASH_MAIN */
#endif /* SIPHASH_H */
#endif /* LV_USE_XML */
@@ -0,0 +1,72 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
#endif /* LV_USE_XML */
@@ -0,0 +1,54 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2023 Orgad Shaneh <orgad.shaneh@audiocodes.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#ifndef WINCONFIG_H
#define WINCONFIG_H
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <memory.h>
#include <string.h>
#endif /* ndef WINCONFIG_H */
#endif /* LV_USE_XML */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,148 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#ifndef XmlRole_INCLUDED
#define XmlRole_INCLUDED 1
#ifdef __VMS
/* 0 1 2 3 0 1 2 3
1234567890123456789012345678901 1234567890123456789012345678901 */
# define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt
#endif
#include "xmltok.h"
#ifdef __cplusplus
extern "C" {
#endif
enum {
XML_ROLE_ERROR = -1,
XML_ROLE_NONE = 0,
XML_ROLE_XML_DECL,
XML_ROLE_INSTANCE_START,
XML_ROLE_DOCTYPE_NONE,
XML_ROLE_DOCTYPE_NAME,
XML_ROLE_DOCTYPE_SYSTEM_ID,
XML_ROLE_DOCTYPE_PUBLIC_ID,
XML_ROLE_DOCTYPE_INTERNAL_SUBSET,
XML_ROLE_DOCTYPE_CLOSE,
XML_ROLE_GENERAL_ENTITY_NAME,
XML_ROLE_PARAM_ENTITY_NAME,
XML_ROLE_ENTITY_NONE,
XML_ROLE_ENTITY_VALUE,
XML_ROLE_ENTITY_SYSTEM_ID,
XML_ROLE_ENTITY_PUBLIC_ID,
XML_ROLE_ENTITY_COMPLETE,
XML_ROLE_ENTITY_NOTATION_NAME,
XML_ROLE_NOTATION_NONE,
XML_ROLE_NOTATION_NAME,
XML_ROLE_NOTATION_SYSTEM_ID,
XML_ROLE_NOTATION_NO_SYSTEM_ID,
XML_ROLE_NOTATION_PUBLIC_ID,
XML_ROLE_ATTRIBUTE_NAME,
XML_ROLE_ATTRIBUTE_TYPE_CDATA,
XML_ROLE_ATTRIBUTE_TYPE_ID,
XML_ROLE_ATTRIBUTE_TYPE_IDREF,
XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
XML_ROLE_ATTRIBUTE_ENUM_VALUE,
XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
XML_ROLE_ATTLIST_NONE,
XML_ROLE_ATTLIST_ELEMENT_NAME,
XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
XML_ROLE_FIXED_ATTRIBUTE_VALUE,
XML_ROLE_ELEMENT_NONE,
XML_ROLE_ELEMENT_NAME,
XML_ROLE_CONTENT_ANY,
XML_ROLE_CONTENT_EMPTY,
XML_ROLE_CONTENT_PCDATA,
XML_ROLE_GROUP_OPEN,
XML_ROLE_GROUP_CLOSE,
XML_ROLE_GROUP_CLOSE_REP,
XML_ROLE_GROUP_CLOSE_OPT,
XML_ROLE_GROUP_CLOSE_PLUS,
XML_ROLE_GROUP_CHOICE,
XML_ROLE_GROUP_SEQUENCE,
XML_ROLE_CONTENT_ELEMENT,
XML_ROLE_CONTENT_ELEMENT_REP,
XML_ROLE_CONTENT_ELEMENT_OPT,
XML_ROLE_CONTENT_ELEMENT_PLUS,
XML_ROLE_PI,
XML_ROLE_COMMENT,
#ifdef XML_DTD
XML_ROLE_TEXT_DECL,
XML_ROLE_IGNORE_SECT,
XML_ROLE_INNER_PARAM_ENTITY_REF,
#endif /* XML_DTD */
XML_ROLE_PARAM_ENTITY_REF
};
typedef struct prolog_state {
int(PTRCALL *handler)(struct prolog_state *state, int tok, const char *ptr,
const char *end, const ENCODING *enc);
unsigned level;
int role_none;
#ifdef XML_DTD
unsigned includeLevel;
int documentEntity;
int inEntityValue;
#endif /* XML_DTD */
} PROLOG_STATE;
void XmlPrologStateInit(PROLOG_STATE *state);
#ifdef XML_DTD
void XmlPrologStateInitExternalEntity(PROLOG_STATE *state);
#endif /* XML_DTD */
#define XmlTokenRole(state, tok, ptr, end, enc) \
(((state)->handler)(state, tok, ptr, end, enc))
#ifdef __cplusplus
}
#endif
#endif /* not XmlRole_INCLUDED */
#endif /* LV_USE_XML */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,327 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2002-2005 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#ifndef XmlTok_INCLUDED
#define XmlTok_INCLUDED 1
#ifdef __cplusplus
extern "C" {
#endif
/* The following token may be returned by XmlContentTok */
#define XML_TOK_TRAILING_RSQB \
-5 /* ] or ]] at the end of the scan; might be \
start of illegal ]]> sequence */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
#define XML_TOK_NONE -4 /* The string to be scanned is empty */
#define XML_TOK_TRAILING_CR \
-3 /* A CR at the end of the scan; \
might be part of CRLF sequence */
#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
#define XML_TOK_PARTIAL -1 /* only part of a token */
#define XML_TOK_INVALID 0
/* The following tokens are returned by XmlContentTok; some are also
returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
*/
#define XML_TOK_START_TAG_WITH_ATTS 1
#define XML_TOK_START_TAG_NO_ATTS 2
#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
#define XML_TOK_END_TAG 5
#define XML_TOK_DATA_CHARS 6
#define XML_TOK_DATA_NEWLINE 7
#define XML_TOK_CDATA_SECT_OPEN 8
#define XML_TOK_ENTITY_REF 9
#define XML_TOK_CHAR_REF 10 /* numeric character reference */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
#define XML_TOK_PI 11 /* processing instruction */
#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
#define XML_TOK_COMMENT 13
#define XML_TOK_BOM 14 /* Byte order mark */
/* The following tokens are returned only by XmlPrologTok */
#define XML_TOK_PROLOG_S 15
#define XML_TOK_DECL_OPEN 16 /* <!foo */
#define XML_TOK_DECL_CLOSE 17 /* > */
#define XML_TOK_NAME 18
#define XML_TOK_NMTOKEN 19
#define XML_TOK_POUND_NAME 20 /* #name */
#define XML_TOK_OR 21 /* | */
#define XML_TOK_PERCENT 22
#define XML_TOK_OPEN_PAREN 23
#define XML_TOK_CLOSE_PAREN 24
#define XML_TOK_OPEN_BRACKET 25
#define XML_TOK_CLOSE_BRACKET 26
#define XML_TOK_LITERAL 27
#define XML_TOK_PARAM_ENTITY_REF 28
#define XML_TOK_INSTANCE_START 29
/* The following occur only in element type declarations */
#define XML_TOK_NAME_QUESTION 30 /* name? */
#define XML_TOK_NAME_ASTERISK 31 /* name* */
#define XML_TOK_NAME_PLUS 32 /* name+ */
#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
#define XML_TOK_COMMA 38
/* The following token is returned only by XmlAttributeValueTok */
#define XML_TOK_ATTRIBUTE_VALUE_S 39
/* The following token is returned only by XmlCdataSectionTok */
#define XML_TOK_CDATA_SECT_CLOSE 40
/* With namespace processing this is returned by XmlPrologTok for a
name with a colon.
*/
#define XML_TOK_PREFIXED_NAME 41
#ifdef XML_DTD
# define XML_TOK_IGNORE_SECT 42
#endif /* XML_DTD */
#ifdef XML_DTD
# define XML_N_STATES 4
#else /* not XML_DTD */
# define XML_N_STATES 3
#endif /* not XML_DTD */
#define XML_PROLOG_STATE 0
#define XML_CONTENT_STATE 1
#define XML_CDATA_SECTION_STATE 2
#ifdef XML_DTD
# define XML_IGNORE_SECTION_STATE 3
#endif /* XML_DTD */
#define XML_N_LITERAL_TYPES 2
#define XML_ATTRIBUTE_VALUE_LITERAL 0
#define XML_ENTITY_VALUE_LITERAL 1
/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
#define XML_UTF8_ENCODE_MAX 4
/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
#define XML_UTF16_ENCODE_MAX 2
typedef struct position {
/* first line and first column are 0 not 1 */
XML_Size lineNumber;
XML_Size columnNumber;
} POSITION;
typedef struct {
const char *name;
const char *valuePtr;
const char *valueEnd;
char normalized;
} ATTRIBUTE;
struct encoding;
typedef struct encoding ENCODING;
typedef int(PTRCALL *SCANNER)(const ENCODING *, const char *, const char *,
const char **);
enum XML_Convert_Result {
XML_CONVERT_COMPLETED = 0,
XML_CONVERT_INPUT_INCOMPLETE = 1,
XML_CONVERT_OUTPUT_EXHAUSTED
= 2 /* and therefore potentially input remaining as well */
};
struct encoding {
SCANNER scanners[XML_N_STATES];
SCANNER literalScanners[XML_N_LITERAL_TYPES];
int(PTRCALL *nameMatchesAscii)(const ENCODING *, const char *, const char *,
const char *);
int(PTRFASTCALL *nameLength)(const ENCODING *, const char *);
const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *);
int(PTRCALL *getAtts)(const ENCODING *enc, const char *ptr, int attsMax,
ATTRIBUTE *atts);
int(PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr);
int(PTRCALL *predefinedEntityName)(const ENCODING *, const char *,
const char *);
void(PTRCALL *updatePosition)(const ENCODING *, const char *ptr,
const char *end, POSITION *);
int(PTRCALL *isPublicId)(const ENCODING *enc, const char *ptr,
const char *end, const char **badPtr);
enum XML_Convert_Result(PTRCALL *utf8Convert)(const ENCODING *enc,
const char **fromP,
const char *fromLim, char **toP,
const char *toLim);
enum XML_Convert_Result(PTRCALL *utf16Convert)(const ENCODING *enc,
const char **fromP,
const char *fromLim,
unsigned short **toP,
const unsigned short *toLim);
int minBytesPerChar;
char isUtf8;
char isUtf16;
};
/* Scan the string starting at ptr until the end of the next complete
token, but do not scan past eptr. Return an integer giving the
type of token.
Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
Return XML_TOK_PARTIAL when the string does not contain a complete
token; nextTokPtr will not be set.
Return XML_TOK_INVALID when the string does not start a valid
token; nextTokPtr will be set to point to the character which made
the token invalid.
Otherwise the string starts with a valid token; nextTokPtr will be
set to point to the character following the end of that token.
Each data character counts as a single token, but adjacent data
characters may be returned together. Similarly for characters in
the prolog outside literals, comments and processing instructions.
*/
#define XmlTok(enc, state, ptr, end, nextTokPtr) \
(((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
#define XmlContentTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
#ifdef XML_DTD
# define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
#endif /* XML_DTD */
/* This is used for performing a 2nd-level tokenization on the content
of a literal that has already been returned by XmlTok.
*/
#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
(((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
(((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
#define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr))
#define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr))
#define XmlGetAttributes(enc, ptr, attsMax, atts) \
(((enc)->getAtts)(enc, ptr, attsMax, atts))
#define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr))
#define XmlPredefinedEntityName(enc, ptr, end) \
(((enc)->predefinedEntityName)(enc, ptr, end))
#define XmlUpdatePosition(enc, ptr, end, pos) \
(((enc)->updatePosition)(enc, ptr, end, pos))
#define XmlIsPublicId(enc, ptr, end, badPtr) \
(((enc)->isPublicId)(enc, ptr, end, badPtr))
#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
(((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
(((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
typedef struct {
ENCODING initEnc;
const ENCODING **encPtr;
} INIT_ENCODING;
int XmlParseXmlDecl(int isGeneralTextEntity, const ENCODING *enc,
const char *ptr, const char *end, const char **badPtr,
const char **versionPtr, const char **versionEndPtr,
const char **encodingNamePtr,
const ENCODING **namedEncodingPtr, int *standalonePtr);
int XmlInitEncoding(INIT_ENCODING *p, const ENCODING **encPtr,
const char *name);
const ENCODING *XmlGetUtf8InternalEncoding(void);
const ENCODING *XmlGetUtf16InternalEncoding(void);
int FASTCALL XmlUtf8Encode(int charNumber, char *buf);
int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf);
int XmlSizeOfUnknownEncoding(void);
typedef int(XMLCALL *CONVERTER)(void *userData, const char *p);
ENCODING *XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert,
void *userData);
int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc,
const char *ptr, const char *end, const char **badPtr,
const char **versionPtr, const char **versionEndPtr,
const char **encodingNamePtr,
const ENCODING **namedEncodingPtr, int *standalonePtr);
int XmlInitEncodingNS(INIT_ENCODING *p, const ENCODING **encPtr,
const char *name);
const ENCODING *XmlGetUtf8InternalEncodingNS(void);
const ENCODING *XmlGetUtf16InternalEncodingNS(void);
ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert,
void *userData);
#ifdef __cplusplus
}
#endif
#endif /* not XmlTok_INCLUDED */
#endif /* LV_USE_XML */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,80 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2017-2019 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
enum {
BT_NONXML, /* e.g. noncharacter-FFFF */
BT_MALFORM, /* illegal, with regard to encoding */
BT_LT, /* less than = "<" */
BT_AMP, /* ampersand = "&" */
BT_RSQB, /* right square bracket = "[" */
BT_LEAD2, /* lead byte of a 2-byte UTF-8 character */
BT_LEAD3, /* lead byte of a 3-byte UTF-8 character */
BT_LEAD4, /* lead byte of a 4-byte UTF-8 character */
BT_TRAIL, /* trailing unit, e.g. second 16-bit unit of a 4-byte char. */
BT_CR, /* carriage return = "\r" */
BT_LF, /* line feed = "\n" */
BT_GT, /* greater than = ">" */
BT_QUOT, /* quotation character = "\"" */
BT_APOS, /* apostrophe = "'" */
BT_EQUALS, /* equal sign = "=" */
BT_QUEST, /* question mark = "?" */
BT_EXCL, /* exclamation mark = "!" */
BT_SOL, /* solidus, slash = "/" */
BT_SEMI, /* semicolon = ";" */
BT_NUM, /* number sign = "#" */
BT_LSQB, /* left square bracket = "[" */
BT_S, /* white space, e.g. "\t", " "[, "\r"] */
BT_NMSTRT, /* non-hex name start letter = "G".."Z" + "g".."z" + "_" */
BT_COLON, /* colon = ":" */
BT_HEX, /* hex letter = "A".."F" + "a".."f" */
BT_DIGIT, /* digit = "0".."9" */
BT_NAME, /* dot and middle dot = "." + chr(0xb7) */
BT_MINUS, /* minus = "-" */
BT_OTHER, /* known not to be a name or name start character */
BT_NONASCII, /* might be a name or name start character */
BT_PERCNT, /* percent sign = "%" */
BT_LPAR, /* left parenthesis = "(" */
BT_RPAR, /* right parenthesis = "(" */
BT_AST, /* asterisk = "*" */
BT_PLUS, /* plus sign = "+" */
BT_COMMA, /* comma = "," */
BT_VERBAR /* vertical bar = "|" */
};
#include <stddef.h>
#endif /* LV_USE_XML */
@@ -0,0 +1,128 @@
/* This file is included!
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../../lv_conf_internal.h"
#if LV_USE_XML
#ifdef XML_TOK_NS_C
const ENCODING *
NS(XmlGetUtf8InternalEncoding)(void) {
return &ns(internal_utf8_encoding).enc;
}
const ENCODING *
NS(XmlGetUtf16InternalEncoding)(void) {
# if BYTEORDER == 1234
return &ns(internal_little2_encoding).enc;
# elif BYTEORDER == 4321
return &ns(internal_big2_encoding).enc;
# else
const short n = 1;
return (*(const char *)&n ? &ns(internal_little2_encoding).enc
: &ns(internal_big2_encoding).enc);
# endif
}
static const ENCODING *const NS(encodings)[] = {
&ns(latin1_encoding).enc, &ns(ascii_encoding).enc,
&ns(utf8_encoding).enc, &ns(big2_encoding).enc,
&ns(big2_encoding).enc, &ns(little2_encoding).enc,
&ns(utf8_encoding).enc /* NO_ENC */
};
static int PTRCALL
NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
const char **nextTokPtr) {
return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE,
ptr, end, nextTokPtr);
}
static int PTRCALL
NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
const char **nextTokPtr) {
return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE,
ptr, end, nextTokPtr);
}
int
NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr,
const char *name) {
int i = getEncodingIndex(name);
if (i == UNKNOWN_ENC)
return 0;
SET_INIT_ENC_INDEX(p, i);
p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
p->initEnc.updatePosition = initUpdatePosition;
p->encPtr = encPtr;
*encPtr = &(p->initEnc);
return 1;
}
static const ENCODING *
NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) {
# define ENCODING_MAX 128
char buf[ENCODING_MAX] = "";
char *p = buf;
int i;
XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
if (ptr != end)
return 0;
*p = 0;
if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2)
return enc;
i = getEncodingIndex(buf);
if (i == UNKNOWN_ENC)
return 0;
return NS(encodings)[i];
}
int
NS(XmlParseXmlDecl)(int isGeneralTextEntity, const ENCODING *enc,
const char *ptr, const char *end, const char **badPtr,
const char **versionPtr, const char **versionEndPtr,
const char **encodingName, const ENCODING **encoding,
int *standalone) {
return doParseXmlDecl(NS(findEncoding), isGeneralTextEntity, enc, ptr, end,
badPtr, versionPtr, versionEndPtr, encodingName,
encoding, standalone);
}
#endif /* XML_TOK_NS_C */
#endif /* LV_USE_XML */
@@ -0,0 +1,991 @@
/**
* @file lv_ffmpeg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_ffmpeg_private.h"
#if LV_USE_FFMPEG != 0
#include "../../draw/lv_image_decoder_private.h"
#include "../../draw/lv_draw_buf_private.h"
#include "../../core/lv_obj_class_private.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libswscale/swscale.h>
/*********************
* DEFINES
*********************/
#define DECODER_NAME "FFMPEG"
#if LV_COLOR_DEPTH == 8
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB8
#elif LV_COLOR_DEPTH == 16
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565LE
#elif LV_COLOR_DEPTH == 32
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_BGR0
#else
#error Unsupported LV_COLOR_DEPTH
#endif
#define MY_CLASS (&lv_ffmpeg_player_class)
#define FRAME_DEF_REFR_PERIOD 33 /*[ms]*/
#define DECODER_BUFFER_SIZE (8 * 1024)
/**********************
* TYPEDEFS
**********************/
struct ffmpeg_context_s {
AVIOContext * io_ctx;
lv_fs_file_t lv_file;
AVFormatContext * fmt_ctx;
AVCodecContext * video_dec_ctx;
AVStream * video_stream;
uint8_t * video_src_data[4];
uint8_t * video_dst_data[4];
struct SwsContext * sws_ctx;
AVFrame * frame;
AVPacket * pkt;
int video_stream_idx;
int video_src_linesize[4];
int video_dst_linesize[4];
enum AVPixelFormat video_dst_pix_fmt;
bool has_alpha;
lv_draw_buf_t draw_buf;
lv_draw_buf_handlers_t draw_buf_handlers;
};
#pragma pack(1)
struct _lv_image_pixel_color_s {
lv_color_t c;
uint8_t alpha;
};
#pragma pack()
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
static int ffmpeg_lvfs_read(void * ptr, uint8_t * buf, int buf_size);
static int64_t ffmpeg_lvfs_seek(void * ptr, int64_t pos, int whence);
static AVIOContext * ffmpeg_open_io_context(lv_fs_file_t * file);
static struct ffmpeg_context_s * ffmpeg_open_file(const char * path, bool is_lv_fs_path);
static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx);
static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_get_image_header(lv_image_decoder_dsc_t * dsc, lv_image_header_t * header);
static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx);
static uint8_t * ffmpeg_get_image_data(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx);
static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt);
static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt);
static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_ffmpeg_player_class = {
.constructor_cb = lv_ffmpeg_player_constructor,
.destructor_cb = lv_ffmpeg_player_destructor,
.instance_size = sizeof(lv_ffmpeg_player_t),
.base_class = &lv_image_class,
.name = "lv_ffmpeg_player",
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_ffmpeg_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->name = DECODER_NAME;
#if LV_FFMPEG_DUMP_FORMAT == 0
av_log_set_level(AV_LOG_QUIET);
#endif
}
void lv_ffmpeg_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
int lv_ffmpeg_get_frame_num(const char * path)
{
int ret = -1;
struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path, LV_FFMPEG_PLAYER_USE_LV_FS);
if(ffmpeg_ctx) {
ret = ffmpeg_ctx->video_stream->nb_frames;
ffmpeg_close(ffmpeg_ctx);
}
return ret;
}
lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_result_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_result_t res = LV_RESULT_INVALID;
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(player->ffmpeg_ctx) {
ffmpeg_close(player->ffmpeg_ctx);
player->ffmpeg_ctx = NULL;
}
lv_timer_pause(player->timer);
player->ffmpeg_ctx = ffmpeg_open_file(path, LV_FFMPEG_PLAYER_USE_LV_FS);
if(!player->ffmpeg_ctx) {
goto failed;
}
if(ffmpeg_image_allocate(player->ffmpeg_ctx) < 0) {
LV_LOG_ERROR("ffmpeg image allocate failed");
ffmpeg_close(player->ffmpeg_ctx);
player->ffmpeg_ctx = NULL;
goto failed;
}
bool has_alpha = player->ffmpeg_ctx->has_alpha;
int width = player->ffmpeg_ctx->video_dec_ctx->width;
int height = player->ffmpeg_ctx->video_dec_ctx->height;
uint8_t * data = ffmpeg_get_image_data(player->ffmpeg_ctx);
lv_color_format_t cf = has_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE;
uint32_t stride = width * lv_color_format_get_size(cf);
uint32_t data_size = stride * height;
lv_memzero(data, data_size);
player->imgdsc.header.w = width;
player->imgdsc.header.h = height;
player->imgdsc.data_size = data_size;
player->imgdsc.header.cf = cf;
player->imgdsc.header.stride = stride;
player->imgdsc.data = data;
lv_image_set_src(&player->img.obj, &(player->imgdsc));
int period = ffmpeg_get_frame_refr_period(player->ffmpeg_ctx);
if(period > 0) {
LV_LOG_INFO("frame refresh period = %d ms, rate = %d fps",
period, 1000 / period);
lv_timer_set_period(player->timer, period);
}
else {
LV_LOG_WARN("unable to get frame refresh period");
}
res = LV_RESULT_OK;
failed:
return res;
}
void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(!player->ffmpeg_ctx) {
LV_LOG_ERROR("ffmpeg_ctx is NULL");
return;
}
lv_timer_t * timer = player->timer;
switch(cmd) {
case LV_FFMPEG_PLAYER_CMD_START:
av_seek_frame(player->ffmpeg_ctx->fmt_ctx,
0, 0, AVSEEK_FLAG_BACKWARD);
lv_timer_resume(timer);
LV_LOG_INFO("ffmpeg player start");
break;
case LV_FFMPEG_PLAYER_CMD_STOP:
av_seek_frame(player->ffmpeg_ctx->fmt_ctx,
0, 0, AVSEEK_FLAG_BACKWARD);
lv_timer_pause(timer);
LV_LOG_INFO("ffmpeg player stop");
break;
case LV_FFMPEG_PLAYER_CMD_PAUSE:
lv_timer_pause(timer);
LV_LOG_INFO("ffmpeg player pause");
break;
case LV_FFMPEG_PLAYER_CMD_RESUME:
lv_timer_resume(timer);
LV_LOG_INFO("ffmpeg player resume");
break;
default:
LV_LOG_ERROR("Error cmd: %d", cmd);
break;
}
}
void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
player->auto_restart = en;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
{
LV_UNUSED(decoder);
/* Get the source type */
lv_image_src_t src_type = dsc->src_type;
if(src_type == LV_IMAGE_SRC_FILE) {
if(ffmpeg_get_image_header(dsc, header) < 0) {
LV_LOG_ERROR("ffmpeg can't get image header");
return LV_RESULT_INVALID;
}
return LV_RESULT_OK;
}
/* If didn't succeeded earlier then it's an error */
return LV_RESULT_INVALID;
}
/**
* Decode an image using ffmpeg library
* @param decoder pointer to the decoder
* @param dsc pointer to the decoder descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
*/
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * path = dsc->src;
struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path, true);
if(ffmpeg_ctx == NULL) {
return LV_RESULT_INVALID;
}
if(ffmpeg_image_allocate(ffmpeg_ctx) < 0) {
LV_LOG_ERROR("ffmpeg image allocate failed");
ffmpeg_close(ffmpeg_ctx);
return LV_RESULT_INVALID;
}
if(ffmpeg_update_next_frame(ffmpeg_ctx) < 0) {
ffmpeg_close(ffmpeg_ctx);
LV_LOG_ERROR("ffmpeg update frame failed");
return LV_RESULT_INVALID;
}
ffmpeg_close_src_ctx(ffmpeg_ctx);
uint8_t * img_data = ffmpeg_get_image_data(ffmpeg_ctx);
dsc->user_data = ffmpeg_ctx;
lv_draw_buf_t * decoded = &ffmpeg_ctx->draw_buf;
lv_draw_buf_init(
decoded,
dsc->header.w,
dsc->header.h,
dsc->header.cf,
dsc->header.stride,
img_data,
dsc->header.stride * dsc->header.h);
lv_draw_buf_set_flag(decoded, LV_IMAGE_FLAGS_MODIFIABLE);
/* Empty handlers to avoid decoder asserts */
lv_draw_buf_handlers_init(&ffmpeg_ctx->draw_buf_handlers, NULL, NULL, NULL, NULL, NULL, NULL);
decoded->handlers = &ffmpeg_ctx->draw_buf_handlers;
if(dsc->args.premultiply && ffmpeg_ctx->has_alpha) {
lv_draw_buf_premultiply(decoded);
}
dsc->decoded = decoded;
/* The image is fully decoded. Return with its pointer */
return LV_RESULT_OK;
}
/* If not returned earlier then it failed */
return LV_RESULT_INVALID;
}
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
struct ffmpeg_context_s * ffmpeg_ctx = dsc->user_data;
ffmpeg_close(ffmpeg_ctx);
}
static uint8_t * ffmpeg_get_image_data(struct ffmpeg_context_s * ffmpeg_ctx)
{
uint8_t * img_data = ffmpeg_ctx->video_dst_data[0];
if(img_data == NULL) {
LV_LOG_ERROR("ffmpeg video dst data is NULL");
}
return img_data;
}
static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt);
if(desc == NULL) {
return false;
}
if(pix_fmt == AV_PIX_FMT_PAL8) {
return true;
}
return desc->flags & AV_PIX_FMT_FLAG_ALPHA;
}
static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt);
if(desc == NULL) {
return false;
}
return !(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components >= 2;
}
static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret = -1;
int width = ffmpeg_ctx->video_dec_ctx->width;
int height = ffmpeg_ctx->video_dec_ctx->height;
AVFrame * frame = ffmpeg_ctx->frame;
if(frame->width != width
|| frame->height != height
|| frame->format != ffmpeg_ctx->video_dec_ctx->pix_fmt) {
/* To handle this change, one could call av_image_alloc again and
* decode the following frames into another rawvideo file.
*/
LV_LOG_ERROR("Width, height and pixel format have to be "
"constant in a rawvideo file, but the width, height or "
"pixel format of the input video changed:\n"
"old: width = %d, height = %d, format = %s\n"
"new: width = %d, height = %d, format = %s\n",
width,
height,
av_get_pix_fmt_name(ffmpeg_ctx->video_dec_ctx->pix_fmt),
frame->width, frame->height,
av_get_pix_fmt_name(frame->format));
goto failed;
}
/* copy decoded frame to destination buffer:
* this is required since rawvideo expects non aligned data
*/
av_image_copy(ffmpeg_ctx->video_src_data, ffmpeg_ctx->video_src_linesize,
(const uint8_t **)(frame->data), frame->linesize,
ffmpeg_ctx->video_dec_ctx->pix_fmt, width, height);
if(ffmpeg_ctx->sws_ctx == NULL) {
int swsFlags = SWS_BILINEAR;
if(ffmpeg_pix_fmt_is_yuv(ffmpeg_ctx->video_dec_ctx->pix_fmt)) {
/* When the video width and height are not multiples of 8,
* and there is no size change in the conversion,
* a blurry screen will appear on the right side
* This problem was discovered in 2012 and
* continues to exist in version 4.1.3 in 2019
* This problem can be avoided by increasing SWS_ACCURATE_RND
*/
if((width & 0x7) || (height & 0x7)) {
LV_LOG_WARN("The width(%d) and height(%d) the image "
"is not a multiple of 8, "
"the decoding speed may be reduced",
width, height);
swsFlags |= SWS_ACCURATE_RND;
}
}
ffmpeg_ctx->sws_ctx = sws_getContext(
width, height, ffmpeg_ctx->video_dec_ctx->pix_fmt,
width, height, ffmpeg_ctx->video_dst_pix_fmt,
swsFlags,
NULL, NULL, NULL);
}
if(!ffmpeg_ctx->has_alpha) {
int lv_linesize = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE) * width;
int dst_linesize = ffmpeg_ctx->video_dst_linesize[0];
if(dst_linesize != lv_linesize) {
LV_LOG_WARN("ffmpeg linesize = %d, but lvgl image require %d",
dst_linesize,
lv_linesize);
ffmpeg_ctx->video_dst_linesize[0] = lv_linesize;
}
}
ret = sws_scale(
ffmpeg_ctx->sws_ctx,
(const uint8_t * const *)(ffmpeg_ctx->video_src_data),
ffmpeg_ctx->video_src_linesize,
0,
height,
ffmpeg_ctx->video_dst_data,
ffmpeg_ctx->video_dst_linesize);
failed:
return ret;
}
static int ffmpeg_decode_packet(AVCodecContext * dec, const AVPacket * pkt,
struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret = 0;
/* submit the packet to the decoder */
ret = avcodec_send_packet(dec, pkt);
if(ret < 0) {
LV_LOG_ERROR("Error submitting a packet for decoding (%s)",
av_err2str(ret));
return ret;
}
/* get all the available frames from the decoder */
while(ret >= 0) {
ret = avcodec_receive_frame(dec, ffmpeg_ctx->frame);
if(ret < 0) {
/* those two return values are special and mean there is
* no output frame available,
* but there were no errors during decoding
*/
if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
return 0;
}
LV_LOG_ERROR("Error during decoding (%s)", av_err2str(ret));
return ret;
}
/* write the frame data to output file */
if(dec->codec->type == AVMEDIA_TYPE_VIDEO) {
ret = ffmpeg_output_video_frame(ffmpeg_ctx);
}
av_frame_unref(ffmpeg_ctx->frame);
if(ret < 0) {
LV_LOG_WARN("ffmpeg_decode_packet ended %d", ret);
return ret;
}
}
return 0;
}
static int ffmpeg_open_codec_context(int * stream_idx,
AVCodecContext ** dec_ctx, AVFormatContext * fmt_ctx,
enum AVMediaType type)
{
int ret;
int stream_index;
AVStream * st;
const AVCodec * dec = NULL;
AVDictionary * opts = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if(ret < 0) {
LV_LOG_ERROR("Could not find %s stream in input file",
av_get_media_type_string(type));
return ret;
}
else {
stream_index = ret;
st = fmt_ctx->streams[stream_index];
/* find decoder for the stream */
dec = avcodec_find_decoder(st->codecpar->codec_id);
if(dec == NULL) {
LV_LOG_ERROR("Failed to find %s codec",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
/* Allocate a codec context for the decoder */
*dec_ctx = avcodec_alloc_context3(dec);
if(*dec_ctx == NULL) {
LV_LOG_ERROR("Failed to allocate the %s codec context",
av_get_media_type_string(type));
return AVERROR(ENOMEM);
}
/* Copy codec parameters from input stream to output codec context */
if((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
LV_LOG_ERROR(
"Failed to copy %s codec parameters to decoder context",
av_get_media_type_string(type));
return ret;
}
/* Init the decoders */
if((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
LV_LOG_ERROR("Failed to open %s codec",
av_get_media_type_string(type));
return ret;
}
*stream_idx = stream_index;
}
return 0;
}
static int ffmpeg_get_image_header(lv_image_decoder_dsc_t * dsc,
lv_image_header_t * header)
{
int ret = -1;
AVFormatContext * fmt_ctx = NULL;
AVCodecContext * video_dec_ctx = NULL;
AVIOContext * io_ctx = NULL;
int video_stream_idx;
io_ctx = ffmpeg_open_io_context(&dsc->file);
if(io_ctx == NULL) {
LV_LOG_ERROR("io_ctx malloc failed");
return ret;
}
fmt_ctx = avformat_alloc_context();
if(fmt_ctx == NULL) {
LV_LOG_ERROR("fmt_ctx malloc failed");
goto failed;
}
fmt_ctx->pb = io_ctx;
fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
/* open input file, and allocate format context */
if(avformat_open_input(&fmt_ctx, (const char *)dsc->src, NULL, NULL) < 0) {
LV_LOG_ERROR("Could not open source file %s", (const char *)dsc->src);
goto failed;
}
/* retrieve stream information */
if(avformat_find_stream_info(fmt_ctx, NULL) < 0) {
LV_LOG_ERROR("Could not find stream information");
goto failed;
}
if(ffmpeg_open_codec_context(&video_stream_idx, &video_dec_ctx,
fmt_ctx, AVMEDIA_TYPE_VIDEO)
>= 0) {
bool has_alpha = ffmpeg_pix_fmt_has_alpha(video_dec_ctx->pix_fmt);
/* allocate image where the decoded image will be put */
header->w = video_dec_ctx->width;
header->h = video_dec_ctx->height;
header->cf = has_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE;
header->stride = header->w * lv_color_format_get_size(header->cf);
ret = 0;
}
failed:
avcodec_free_context(&video_dec_ctx);
avformat_close_input(&fmt_ctx);
if(io_ctx != NULL) {
av_free(io_ctx->buffer);
av_free(io_ctx);
}
return ret;
}
static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx)
{
int avg_frame_rate_num = ffmpeg_ctx->video_stream->avg_frame_rate.num;
if(avg_frame_rate_num > 0) {
int period = 1000 * (int64_t)ffmpeg_ctx->video_stream->avg_frame_rate.den
/ avg_frame_rate_num;
return period;
}
return -1;
}
static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret = 0;
while(1) {
/* read frames from the file */
if(av_read_frame(ffmpeg_ctx->fmt_ctx, ffmpeg_ctx->pkt) >= 0) {
bool is_image = false;
/* check if the packet belongs to a stream we are interested in,
* otherwise skip it
*/
if(ffmpeg_ctx->pkt->stream_index == ffmpeg_ctx->video_stream_idx) {
ret = ffmpeg_decode_packet(ffmpeg_ctx->video_dec_ctx,
ffmpeg_ctx->pkt, ffmpeg_ctx);
is_image = true;
}
av_packet_unref(ffmpeg_ctx->pkt);
if(ret < 0) {
LV_LOG_WARN("video frame is empty %d", ret);
break;
}
/* Used to filter data that is not an image */
if(is_image) {
break;
}
}
else {
ret = -1;
break;
}
}
return ret;
}
static int ffmpeg_lvfs_read(void * ptr, uint8_t * buf, int buf_size)
{
lv_fs_file_t * file = ptr;
uint32_t bytesRead = 0;
lv_fs_res_t res = lv_fs_read(file, buf, buf_size, &bytesRead);
if(bytesRead == 0)
return AVERROR_EOF; /* Let FFmpeg know that we have reached eof */
if(res != LV_FS_RES_OK)
return AVERROR_EOF;
return bytesRead;
}
static int64_t ffmpeg_lvfs_seek(void * ptr, int64_t pos, int whence)
{
lv_fs_file_t * file = ptr;
if(whence == SEEK_SET && lv_fs_seek(file, pos, SEEK_SET) == LV_FS_RES_OK) {
return pos;
}
return -1;
}
static AVIOContext * ffmpeg_open_io_context(lv_fs_file_t * file)
{
uint8_t * iBuffer = av_malloc(DECODER_BUFFER_SIZE);
if(iBuffer == NULL) {
LV_LOG_ERROR("iBuffer malloc failed");
return NULL;
}
AVIOContext * pIOCtx = avio_alloc_context(iBuffer, DECODER_BUFFER_SIZE, /* internal Buffer and its size */
0, /* bWriteable (1=true,0=false) */
file, /* user data ; will be passed to our callback functions */
ffmpeg_lvfs_read, /* Read callback function */
0, /* Write callback function */
ffmpeg_lvfs_seek); /* Seek callback function */
if(pIOCtx == NULL) {
av_free(iBuffer);
return NULL;
}
return pIOCtx;
}
static struct ffmpeg_context_s * ffmpeg_open_file(const char * path, bool is_lv_fs_path)
{
if(path == NULL || lv_strlen(path) == 0) {
LV_LOG_ERROR("file path is empty");
return NULL;
}
struct ffmpeg_context_s * ffmpeg_ctx = lv_malloc_zeroed(sizeof(struct ffmpeg_context_s));
LV_ASSERT_MALLOC(ffmpeg_ctx);
if(ffmpeg_ctx == NULL) {
LV_LOG_ERROR("ffmpeg_ctx malloc failed");
goto failed;
}
if(is_lv_fs_path) {
const lv_fs_res_t fs_res = lv_fs_open(&(ffmpeg_ctx->lv_file), path, LV_FS_MODE_RD);
if(fs_res != LV_FS_RES_OK) {
LV_LOG_WARN("Could not open file: %s, res: %d", path, fs_res);
lv_free(ffmpeg_ctx);
return NULL;
}
ffmpeg_ctx->io_ctx = ffmpeg_open_io_context(&(ffmpeg_ctx->lv_file)); /* Save the buffer pointer to free it later */
if(ffmpeg_ctx->io_ctx == NULL) {
LV_LOG_ERROR("io_ctx malloc failed");
goto failed;
}
ffmpeg_ctx->fmt_ctx = avformat_alloc_context();
if(ffmpeg_ctx->fmt_ctx == NULL) {
LV_LOG_ERROR("fmt_ctx malloc failed");
goto failed;
}
ffmpeg_ctx->fmt_ctx->pb = ffmpeg_ctx->io_ctx;
ffmpeg_ctx->fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
}
/* open input file, and allocate format context */
if(avformat_open_input(&(ffmpeg_ctx->fmt_ctx), path, NULL, NULL) < 0) {
LV_LOG_ERROR("Could not open source file %s", path);
goto failed;
}
/* retrieve stream information */
if(avformat_find_stream_info(ffmpeg_ctx->fmt_ctx, NULL) < 0) {
LV_LOG_ERROR("Could not find stream information");
goto failed;
}
if(ffmpeg_open_codec_context(
&(ffmpeg_ctx->video_stream_idx),
&(ffmpeg_ctx->video_dec_ctx),
ffmpeg_ctx->fmt_ctx, AVMEDIA_TYPE_VIDEO)
>= 0) {
ffmpeg_ctx->video_stream = ffmpeg_ctx->fmt_ctx->streams[ffmpeg_ctx->video_stream_idx];
ffmpeg_ctx->has_alpha = ffmpeg_pix_fmt_has_alpha(ffmpeg_ctx->video_dec_ctx->pix_fmt);
ffmpeg_ctx->video_dst_pix_fmt = (ffmpeg_ctx->has_alpha ? AV_PIX_FMT_BGRA : AV_PIX_FMT_TRUE_COLOR);
}
#if LV_FFMPEG_DUMP_FORMAT
/* dump input information to stderr */
av_dump_format(ffmpeg_ctx->fmt_ctx, 0, path, 0);
#endif
if(ffmpeg_ctx->video_stream == NULL) {
LV_LOG_ERROR("Could not find video stream in the input, aborting");
goto failed;
}
return ffmpeg_ctx;
failed:
ffmpeg_close(ffmpeg_ctx);
return NULL;
}
static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret;
/* allocate image where the decoded image will be put */
ret = av_image_alloc(
ffmpeg_ctx->video_src_data,
ffmpeg_ctx->video_src_linesize,
ffmpeg_ctx->video_dec_ctx->width,
ffmpeg_ctx->video_dec_ctx->height,
ffmpeg_ctx->video_dec_ctx->pix_fmt,
4);
if(ret < 0) {
LV_LOG_ERROR("Could not allocate src raw video buffer");
return ret;
}
LV_LOG_INFO("alloc video_src_bufsize = %d", ret);
ret = av_image_alloc(
ffmpeg_ctx->video_dst_data,
ffmpeg_ctx->video_dst_linesize,
ffmpeg_ctx->video_dec_ctx->width,
ffmpeg_ctx->video_dec_ctx->height,
ffmpeg_ctx->video_dst_pix_fmt,
4);
if(ret < 0) {
LV_LOG_ERROR("Could not allocate dst raw video buffer");
return ret;
}
LV_LOG_INFO("allocate video_dst_bufsize = %d", ret);
ffmpeg_ctx->frame = av_frame_alloc();
if(ffmpeg_ctx->frame == NULL) {
LV_LOG_ERROR("Could not allocate frame");
return -1;
}
/* allocate packet, set data to NULL, let the demuxer fill it */
ffmpeg_ctx->pkt = av_packet_alloc();
if(ffmpeg_ctx->pkt == NULL) {
LV_LOG_ERROR("av_packet_alloc failed");
return -1;
}
ffmpeg_ctx->pkt->data = NULL;
ffmpeg_ctx->pkt->size = 0;
return 0;
}
static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx)
{
avcodec_free_context(&(ffmpeg_ctx->video_dec_ctx));
avformat_close_input(&(ffmpeg_ctx->fmt_ctx));
av_packet_free(&ffmpeg_ctx->pkt);
av_frame_free(&(ffmpeg_ctx->frame));
if(ffmpeg_ctx->video_src_data[0] != NULL) {
av_free(ffmpeg_ctx->video_src_data[0]);
ffmpeg_ctx->video_src_data[0] = NULL;
}
}
static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx)
{
if(ffmpeg_ctx->video_dst_data[0] != NULL) {
av_free(ffmpeg_ctx->video_dst_data[0]);
ffmpeg_ctx->video_dst_data[0] = NULL;
}
}
static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx)
{
if(ffmpeg_ctx == NULL) {
LV_LOG_WARN("ffmpeg_ctx is NULL");
return;
}
sws_freeContext(ffmpeg_ctx->sws_ctx);
ffmpeg_close_src_ctx(ffmpeg_ctx);
ffmpeg_close_dst_ctx(ffmpeg_ctx);
if(ffmpeg_ctx->io_ctx != NULL) {
av_free(ffmpeg_ctx->io_ctx->buffer);
av_free(ffmpeg_ctx->io_ctx);
lv_fs_close(&(ffmpeg_ctx->lv_file));
}
lv_free(ffmpeg_ctx);
LV_LOG_INFO("ffmpeg_ctx closed");
}
static void lv_ffmpeg_player_frame_update_cb(lv_timer_t * timer)
{
lv_obj_t * obj = (lv_obj_t *)lv_timer_get_user_data(timer);
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(!player->ffmpeg_ctx) {
return;
}
int has_next = ffmpeg_update_next_frame(player->ffmpeg_ctx);
if(has_next < 0) {
lv_ffmpeg_player_set_cmd(obj, player->auto_restart ? LV_FFMPEG_PLAYER_CMD_START : LV_FFMPEG_PLAYER_CMD_STOP);
if(!player->auto_restart) {
lv_obj_send_event((lv_obj_t *)player, LV_EVENT_READY, NULL);
}
return;
}
lv_image_cache_drop(lv_image_get_src(obj));
lv_obj_invalidate(obj);
}
static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p,
lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
player->auto_restart = false;
player->ffmpeg_ctx = NULL;
player->timer = lv_timer_create(lv_ffmpeg_player_frame_update_cb,
FRAME_DEF_REFR_PERIOD, obj);
lv_timer_pause(player->timer);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p,
lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(player->timer) {
lv_timer_delete(player->timer);
player->timer = NULL;
}
lv_image_cache_drop(lv_image_get_src(obj));
ffmpeg_close(player->ffmpeg_ctx);
player->ffmpeg_ctx = NULL;
LV_TRACE_OBJ_CREATE("finished");
}
#endif /*LV_USE_FFMPEG*/
@@ -0,0 +1,102 @@
/**
* @file lv_ffmpeg.h
*
*/
#ifndef LV_FFMPEG_H
#define LV_FFMPEG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_FFMPEG != 0
#include "../../misc/lv_types.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct ffmpeg_context_s;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_ffmpeg_player_class;
typedef enum {
LV_FFMPEG_PLAYER_CMD_START,
LV_FFMPEG_PLAYER_CMD_STOP,
LV_FFMPEG_PLAYER_CMD_PAUSE,
LV_FFMPEG_PLAYER_CMD_RESUME,
LV_FFMPEG_PLAYER_CMD_LAST
} lv_ffmpeg_player_cmd_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register FFMPEG image decoder
*/
void lv_ffmpeg_init(void);
/**
* De-initialize FFMPEG image decoder
*/
void lv_ffmpeg_deinit(void);
/**
* Get the number of frames contained in the file
* @param path image or video file name
* @return Number of frames, less than 0 means failed
*/
int lv_ffmpeg_get_frame_num(const char * path);
/**
* Create ffmpeg_player object
* @param parent pointer to an object, it will be the parent of the new player
* @return pointer to the created ffmpeg_player
*/
lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent);
/**
* Set the path of the file to be played.
* @param obj pointer to a ffmpeg_player object
* @param path video file path
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info.
*/
lv_result_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path);
/**
* Set command control video player
* @param obj pointer to a ffmpeg_player object
* @param cmd control commands
*/
void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd);
/**
* Set the video to automatically replay
* @param obj pointer to a ffmpeg_player object
* @param en true: enable the auto restart
*/
void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_FFMPEG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FFMPEG_H*/
@@ -0,0 +1,51 @@
/**
* @file lv_ffmpeg_private.h
*
*/
#ifndef LV_FFMPEG_PRIVATE_H
#define LV_FFMPEG_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_ffmpeg.h"
#if LV_USE_FFMPEG != 0
#include "../../widgets/image/lv_image_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_ffmpeg_player_t {
lv_image_t img;
lv_timer_t * timer;
lv_image_dsc_t imgdsc;
bool auto_restart;
struct ffmpeg_context_s * ffmpeg_ctx;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_FFMPEG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FFMPEG_PRIVATE_H*/
@@ -0,0 +1,170 @@
The FreeType Project LICENSE
----------------------------
2006-Jan-27
Copyright 1996-2002, 2006 by
David Turner, Robert Wilhelm, and Werner Lemberg
Introduction
============
The FreeType Project is distributed in several archive packages;
some of them may contain, in addition to the FreeType font engine,
various tools and contributions which rely on, or relate to, the
FreeType Project.
This license applies to all files found in such packages, and
which do not fall under their own explicit license. The license
affects thus the FreeType font engine, the test programs,
documentation and makefiles, at the very least.
This license was inspired by the BSD, Artistic, and IJG
(Independent JPEG Group) licenses, which all encourage inclusion
and use of free software in commercial and freeware products
alike. As a consequence, its main points are that:
o We don't promise that this software works. However, we will be
interested in any kind of bug reports. (`as is' distribution)
o You can use this software for whatever you want, in parts or
full form, without having to pay us. (`royalty-free' usage)
o You may not pretend that you wrote this software. If you use
it, or only parts of it, in a program, you must acknowledge
somewhere in your documentation that you have used the
FreeType code. (`credits')
We specifically permit and encourage the inclusion of this
software, with or without modifications, in commercial products.
We disclaim all warranties covering The FreeType Project and
assume no liability related to The FreeType Project.
Finally, many people asked us for a preferred form for a
credit/disclaimer to use in compliance with this license. We thus
encourage you to use the following text:
"""
Portions of this software are copyright © <year> The FreeType
Project (www.freetype.org). All rights reserved.
"""
Please replace <year> with the value from the FreeType version you
actually use.
Legal Terms
===========
0. Definitions
--------------
Throughout this license, the terms `package', `FreeType Project',
and `FreeType archive' refer to the set of files originally
distributed by the authors (David Turner, Robert Wilhelm, and
Werner Lemberg) as the `FreeType Project', be they named as alpha,
beta or final release.
`You' refers to the licensee, or person using the project, where
`using' is a generic term including compiling the project's source
code as well as linking it to form a `program' or `executable'.
This program is referred to as `a program using the FreeType
engine'.
This license applies to all files distributed in the original
FreeType Project, including all source code, binaries and
documentation, unless otherwise stated in the file in its
original, unmodified form as distributed in the original archive.
If you are unsure whether or not a particular file is covered by
this license, you must contact us to verify this.
The FreeType Project is copyright (C) 1996-2000 by David Turner,
Robert Wilhelm, and Werner Lemberg. All rights reserved except as
specified below.
1. No Warranty
--------------
THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
USE, OF THE FREETYPE PROJECT.
2. Redistribution
-----------------
This license grants a worldwide, royalty-free, perpetual and
irrevocable right and license to use, execute, perform, compile,
display, copy, create derivative works of, distribute and
sublicense the FreeType Project (in both source and object code
forms) and derivative works thereof for any purpose; and to
authorize others to exercise some or all of the rights granted
herein, subject to the following conditions:
o Redistribution of source code must retain this license file
(`FTL.TXT') unaltered; any additions, deletions or changes to
the original files must be clearly indicated in accompanying
documentation. The copyright notices of the unaltered,
original files must be preserved in all copies of source
files.
o Redistribution in binary form must provide a disclaimer that
states that the software is based in part of the work of the
FreeType Team, in the distribution documentation. We also
encourage you to put an URL to the FreeType web page in your
documentation, though this isn't mandatory.
These conditions apply to any software derived from or based on
the FreeType Project, not just the unmodified files. If you use
our work, you must acknowledge us. However, no fee need be paid
to us.
3. Advertising
--------------
Neither the FreeType authors and contributors nor you shall use
the name of the other for commercial, advertising, or promotional
purposes without specific prior written permission.
We suggest, but do not require, that you use one or more of the
following phrases to refer to this software in your documentation
or advertising materials: `FreeType Project', `FreeType Engine',
`FreeType library', or `FreeType Distribution'.
As you have not signed this license, you are not required to
accept it. However, as the FreeType Project is copyrighted
material, only this license, or another one contracted with the
authors, grants you the right to use, distribute, and modify it.
Therefore, by using, distributing, or modifying the FreeType
Project, you indicate that you understand and accept all the terms
of this license.
4. Contacts
-----------
There are two mailing lists related to FreeType:
o freetype@nongnu.org
Discusses general use and applications of FreeType, as well as
future and wanted additions to the library and distribution.
If you are looking for support, start in this list if you
haven't found anything to help you in the documentation.
o freetype-devel@nongnu.org
Discusses bugs, as well as engine internals, design issues,
specific licenses, porting, etc.
Our home page can be found at
https://www.freetype.org
--- end of FTL.TXT ---
Binary file not shown.
@@ -0,0 +1,33 @@
/*
* This file registers the FreeType modules compiled into the library.
*
* If you use GNU make, this file IS NOT USED! Instead, it is created in
* the objects directory (normally `<topdir>/objs/`) based on information
* from `<topdir>/modules.cfg`.
*
* Please read `docs/INSTALL.ANY` and `docs/CUSTOMIZE` how to compile
* FreeType without GNU make.
*
*/
/* FT_USE_MODULE( FT_Module_Class, autofit_module_class ) */
FT_USE_MODULE(FT_Driver_ClassRec, tt_driver_class)
/* FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, t1cid_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, pfr_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, t42_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, winfnt_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, pcf_driver_class ) */
/* FT_USE_MODULE( FT_Driver_ClassRec, bdf_driver_class ) */
/* FT_USE_MODULE( FT_Module_Class, psaux_module_class ) */
/* FT_USE_MODULE( FT_Module_Class, psnames_module_class ) */
/* FT_USE_MODULE( FT_Module_Class, pshinter_module_class ) */
FT_USE_MODULE(FT_Module_Class, sfnt_module_class)
FT_USE_MODULE(FT_Renderer_Class, ft_smooth_renderer_class)
/* FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class ) */
/* FT_USE_MODULE( FT_Renderer_Class, ft_sdf_renderer_class ) */
/* FT_USE_MODULE( FT_Renderer_Class, ft_bitmap_sdf_renderer_class ) */
/* FT_USE_MODULE( FT_Renderer_Class, ft_svg_renderer_class ) */
/* EOF */
@@ -0,0 +1,964 @@
/****************************************************************************
*
* ftoption.h
*
* User-selectable configuration macros (specification only).
*
* Copyright (C) 1996-2022 by
* David Turner, Robert Wilhelm, and Werner Lemberg.
*
* This file is part of the FreeType project, and may only be used,
* modified, and distributed under the terms of the FreeType project
* license, LICENSE.TXT. By continuing to use, modify, or distribute
* this file you indicate that you have read the license and
* understand and accept it fully.
*
*/
#ifndef FTOPTION_H_
#define FTOPTION_H_
#include <ft2build.h>
FT_BEGIN_HEADER
/**************************************************************************
*
* USER-SELECTABLE CONFIGURATION MACROS
*
* This file contains the default configuration macro definitions for a
* standard build of the FreeType library. There are three ways to use
* this file to build project-specific versions of the library:
*
* - You can modify this file by hand, but this is not recommended in
* cases where you would like to build several versions of the library
* from a single source directory.
*
* - You can put a copy of this file in your build directory, more
* precisely in `$BUILD/freetype/config/ftoption.h`, where `$BUILD` is
* the name of a directory that is included _before_ the FreeType include
* path during compilation.
*
* The default FreeType Makefiles use the build directory
* `builds/<system>` by default, but you can easily change that for your
* own projects.
*
* - Copy the file <ft2build.h> to `$BUILD/ft2build.h` and modify it
* slightly to pre-define the macro `FT_CONFIG_OPTIONS_H` used to locate
* this file during the build. For example,
*
* ```
* #define FT_CONFIG_OPTIONS_H <myftoptions.h>
* #include <freetype/config/ftheader.h>
* ```
*
* will use `$BUILD/myftoptions.h` instead of this file for macro
* definitions.
*
* Note also that you can similarly pre-define the macro
* `FT_CONFIG_MODULES_H` used to locate the file listing of the modules
* that are statically linked to the library at compile time. By
* default, this file is `<freetype/config/ftmodule.h>`.
*
* We highly recommend using the third method whenever possible.
*
*/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*#************************************************************************
*
* If you enable this configuration option, FreeType recognizes an
* environment variable called `FREETYPE_PROPERTIES`, which can be used to
* control the various font drivers and modules. The controllable
* properties are listed in the section @properties.
*
* You have to undefine this configuration option on platforms that lack
* the concept of environment variables (and thus don't have the `getenv`
* function), for example Windows CE.
*
* `FREETYPE_PROPERTIES` has the following syntax form (broken here into
* multiple lines for better readability).
*
* ```
* <optional whitespace>
* <module-name1> ':'
* <property-name1> '=' <property-value1>
* <whitespace>
* <module-name2> ':'
* <property-name2> '=' <property-value2>
* ...
* ```
*
* Example:
*
* ```
* FREETYPE_PROPERTIES=truetype:interpreter-version=35 \
* cff:no-stem-darkening=1
* ```
*
*/
#define FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
/**************************************************************************
*
* Uncomment the line below if you want to activate LCD rendering
* technology similar to ClearType in this build of the library. This
* technology triples the resolution in the direction color subpixels. To
* mitigate color fringes inherent to this technology, you also need to
* explicitly set up LCD filtering.
*
* When this macro is not defined, FreeType offers alternative LCD
* rendering technology that produces excellent output.
*/
/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/**************************************************************************
*
* Many compilers provide a non-ANSI 64-bit data type that can be used by
* FreeType to speed up some computations. However, this will create some
* problems when compiling the library in strict ANSI mode.
*
* For this reason, the use of 64-bit integers is normally disabled when
* the `__STDC__` macro is defined. You can however disable this by
* defining the macro `FT_CONFIG_OPTION_FORCE_INT64` here.
*
* For most compilers, this will only create compilation warnings when
* building the library.
*
* ObNote: The compiler-specific 64-bit integers are detected in the
* file `ftconfig.h` either statically or through the `configure`
* script on supported platforms.
*/
#undef FT_CONFIG_OPTION_FORCE_INT64
/**************************************************************************
*
* If this macro is defined, do not try to use an assembler version of
* performance-critical functions (e.g., @FT_MulFix). You should only do
* that to verify that the assembler function works properly, or to execute
* benchmark tests of the various implementations.
*/
/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */
/**************************************************************************
*
* If this macro is defined, try to use an inlined assembler version of the
* @FT_MulFix function, which is a 'hotspot' when loading and hinting
* glyphs, and which should be executed as fast as possible.
*
* Note that if your compiler or CPU is not supported, this will default to
* the standard and portable implementation found in `ftcalc.c`.
*/
#define FT_CONFIG_OPTION_INLINE_MULFIX
/**************************************************************************
*
* LZW-compressed file support.
*
* FreeType now handles font files that have been compressed with the
* `compress` program. This is mostly used to parse many of the PCF
* files that come with various X11 distributions. The implementation
* uses NetBSD's `zopen` to partially uncompress the file on the fly (see
* `src/lzw/ftgzip.c`).
*
* Define this macro if you want to enable this 'feature'.
*/
#define FT_CONFIG_OPTION_USE_LZW
/**************************************************************************
*
* Gzip-compressed file support.
*
* FreeType now handles font files that have been compressed with the
* `gzip` program. This is mostly used to parse many of the PCF files
* that come with XFree86. The implementation uses 'zlib' to partially
* uncompress the file on the fly (see `src/gzip/ftgzip.c`).
*
* Define this macro if you want to enable this 'feature'. See also the
* macro `FT_CONFIG_OPTION_SYSTEM_ZLIB` below.
*/
#define FT_CONFIG_OPTION_USE_ZLIB
/**************************************************************************
*
* ZLib library selection
*
* This macro is only used when `FT_CONFIG_OPTION_USE_ZLIB` is defined.
* It allows FreeType's 'ftgzip' component to link to the system's
* installation of the ZLib library. This is useful on systems like
* Unix or VMS where it generally is already available.
*
* If you let it undefined, the component will use its own copy of the
* zlib sources instead. These have been modified to be included
* directly within the component and **not** export external function
* names. This allows you to link any program with FreeType _and_ ZLib
* without linking conflicts.
*
* Do not `#undef` this macro here since the build system might define
* it for certain configurations only.
*
* If you use a build system like cmake or the `configure` script,
* options set by those programs have precedence, overwriting the value
* here with the configured one.
*
* If you use the GNU make build system directly (that is, without the
* `configure` script) and you define this macro, you also have to pass
* `SYSTEM_ZLIB=yes` as an argument to make.
*/
/* #define FT_CONFIG_OPTION_SYSTEM_ZLIB */
/**************************************************************************
*
* Bzip2-compressed file support.
*
* FreeType now handles font files that have been compressed with the
* `bzip2` program. This is mostly used to parse many of the PCF files
* that come with XFree86. The implementation uses `libbz2` to partially
* uncompress the file on the fly (see `src/bzip2/ftbzip2.c`). Contrary
* to gzip, bzip2 currently is not included and need to use the system
* available bzip2 implementation.
*
* Define this macro if you want to enable this 'feature'.
*
* If you use a build system like cmake or the `configure` script,
* options set by those programs have precedence, overwriting the value
* here with the configured one.
*/
/* #define FT_CONFIG_OPTION_USE_BZIP2 */
/**************************************************************************
*
* Define to disable the use of file stream functions and types, `FILE`,
* `fopen`, etc. Enables the use of smaller system libraries on embedded
* systems that have multiple system libraries, some with or without file
* stream support, in the cases where file stream support is not necessary
* such as memory loading of font files.
*/
/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */
/**************************************************************************
*
* PNG bitmap support.
*
* FreeType now handles loading color bitmap glyphs in the PNG format.
* This requires help from the external libpng library. Uncompressed
* color bitmaps do not need any external libraries and will be supported
* regardless of this configuration.
*
* Define this macro if you want to enable this 'feature'.
*
* If you use a build system like cmake or the `configure` script,
* options set by those programs have precedence, overwriting the value
* here with the configured one.
*/
#define FT_CONFIG_OPTION_USE_PNG
/**************************************************************************
*
* HarfBuzz support.
*
* FreeType uses the HarfBuzz library to improve auto-hinting of OpenType
* fonts. If available, many glyphs not directly addressable by a font's
* character map will be hinted also.
*
* Define this macro if you want to enable this 'feature'.
*
* If you use a build system like cmake or the `configure` script,
* options set by those programs have precedence, overwriting the value
* here with the configured one.
*/
/* #define FT_CONFIG_OPTION_USE_HARFBUZZ */
/**************************************************************************
*
* Brotli support.
*
* FreeType uses the Brotli library to provide support for decompressing
* WOFF2 streams.
*
* Define this macro if you want to enable this 'feature'.
*
* If you use a build system like cmake or the `configure` script,
* options set by those programs have precedence, overwriting the value
* here with the configured one.
*/
/* #define FT_CONFIG_OPTION_USE_BROTLI */
/**************************************************************************
*
* Glyph Postscript Names handling
*
* By default, FreeType 2 is compiled with the 'psnames' module. This
* module is in charge of converting a glyph name string into a Unicode
* value, or return a Macintosh standard glyph name for the use with the
* TrueType 'post' table.
*
* Undefine this macro if you do not want 'psnames' compiled in your
* build of FreeType. This has the following effects:
*
* - The TrueType driver will provide its own set of glyph names, if you
* build it to support postscript names in the TrueType 'post' table,
* but will not synthesize a missing Unicode charmap.
*
* - The Type~1 driver will not be able to synthesize a Unicode charmap
* out of the glyphs found in the fonts.
*
* You would normally undefine this configuration macro when building a
* version of FreeType that doesn't contain a Type~1 or CFF driver.
*/
#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES
/**************************************************************************
*
* Postscript Names to Unicode Values support
*
* By default, FreeType~2 is built with the 'psnames' module compiled in.
* Among other things, the module is used to convert a glyph name into a
* Unicode value. This is especially useful in order to synthesize on
* the fly a Unicode charmap from the CFF/Type~1 driver through a big
* table named the 'Adobe Glyph List' (AGL).
*
* Undefine this macro if you do not want the Adobe Glyph List compiled
* in your 'psnames' module. The Type~1 driver will not be able to
* synthesize a Unicode charmap out of the glyphs found in the fonts.
*/
#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST
/**************************************************************************
*
* Support for Mac fonts
*
* Define this macro if you want support for outline fonts in Mac format
* (mac dfont, mac resource, macbinary containing a mac resource) on
* non-Mac platforms.
*
* Note that the 'FOND' resource isn't checked.
*/
/* #define FT_CONFIG_OPTION_MAC_FONTS */
/**************************************************************************
*
* Guessing methods to access embedded resource forks
*
* Enable extra Mac fonts support on non-Mac platforms (e.g., GNU/Linux).
*
* Resource forks which include fonts data are stored sometimes in
* locations which users or developers don't expected. In some cases,
* resource forks start with some offset from the head of a file. In
* other cases, the actual resource fork is stored in file different from
* what the user specifies. If this option is activated, FreeType tries
* to guess whether such offsets or different file names must be used.
*
* Note that normal, direct access of resource forks is controlled via
* the `FT_CONFIG_OPTION_MAC_FONTS` option.
*/
#ifdef FT_CONFIG_OPTION_MAC_FONTS
#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
#endif
/**************************************************************************
*
* Allow the use of `FT_Incremental_Interface` to load typefaces that
* contain no glyph data, but supply it via a callback function. This is
* required by clients supporting document formats which supply font data
* incrementally as the document is parsed, such as the Ghostscript
* interpreter for the PostScript language.
*/
#define FT_CONFIG_OPTION_INCREMENTAL
/**************************************************************************
*
* The size in bytes of the render pool used by the scan-line converter to
* do all of its work.
*/
#define FT_RENDER_POOL_SIZE 16384L
/**************************************************************************
*
* FT_MAX_MODULES
*
* The maximum number of modules that can be registered in a single
* FreeType library object. 32~is the default.
*/
#define FT_MAX_MODULES 32
/**************************************************************************
*
* Debug level
*
* FreeType can be compiled in debug or trace mode. In debug mode,
* errors are reported through the 'ftdebug' component. In trace mode,
* additional messages are sent to the standard output during execution.
*
* Define `FT_DEBUG_LEVEL_ERROR` to build the library in debug mode.
* Define `FT_DEBUG_LEVEL_TRACE` to build it in trace mode.
*
* Don't define any of these macros to compile in 'release' mode!
*
* Do not `#undef` these macros here since the build system might define
* them for certain configurations only.
*/
/* #define FT_DEBUG_LEVEL_ERROR */
/* #define FT_DEBUG_LEVEL_TRACE */
/**************************************************************************
*
* Logging
*
* Compiling FreeType in debug or trace mode makes FreeType write error
* and trace log messages to `stderr`. Enabling this macro
* automatically forces the `FT_DEBUG_LEVEL_ERROR` and
* `FT_DEBUG_LEVEL_TRACE` macros and allows FreeType to write error and
* trace log messages to a file instead of `stderr`. For writing logs
* to a file, FreeType uses an the external `dlg` library (the source
* code is in `src/dlg`).
*
* This option needs a C99 compiler.
*/
/* #define FT_DEBUG_LOGGING */
/**************************************************************************
*
* Autofitter debugging
*
* If `FT_DEBUG_AUTOFIT` is defined, FreeType provides some means to
* control the autofitter behaviour for debugging purposes with global
* boolean variables (consequently, you should **never** enable this
* while compiling in 'release' mode):
*
* ```
* _af_debug_disable_horz_hints
* _af_debug_disable_vert_hints
* _af_debug_disable_blue_hints
* ```
*
* Additionally, the following functions provide dumps of various
* internal autofit structures to stdout (using `printf`):
*
* ```
* af_glyph_hints_dump_points
* af_glyph_hints_dump_segments
* af_glyph_hints_dump_edges
* af_glyph_hints_get_num_segments
* af_glyph_hints_get_segment_offset
* ```
*
* As an argument, they use another global variable:
*
* ```
* _af_debug_hints
* ```
*
* Please have a look at the `ftgrid` demo program to see how those
* variables and macros should be used.
*
* Do not `#undef` these macros here since the build system might define
* them for certain configurations only.
*/
/* #define FT_DEBUG_AUTOFIT */
/**************************************************************************
*
* Memory Debugging
*
* FreeType now comes with an integrated memory debugger that is capable
* of detecting simple errors like memory leaks or double deletes. To
* compile it within your build of the library, you should define
* `FT_DEBUG_MEMORY` here.
*
* Note that the memory debugger is only activated at runtime when when
* the _environment_ variable `FT2_DEBUG_MEMORY` is defined also!
*
* Do not `#undef` this macro here since the build system might define it
* for certain configurations only.
*/
/* #define FT_DEBUG_MEMORY */
/**************************************************************************
*
* Module errors
*
* If this macro is set (which is _not_ the default), the higher byte of
* an error code gives the module in which the error has occurred, while
* the lower byte is the real error code.
*
* Setting this macro makes sense for debugging purposes only, since it
* would break source compatibility of certain programs that use
* FreeType~2.
*
* More details can be found in the files `ftmoderr.h` and `fterrors.h`.
*/
#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS
/**************************************************************************
*
* OpenType SVG Glyph Support
*
* Setting this macro enables support for OpenType SVG glyphs. By
* default, FreeType can only fetch SVG documents. However, it can also
* render them if external rendering hook functions are plugged in at
* runtime.
*
* More details on the hooks can be found in file `otsvg.h`.
*/
#define FT_CONFIG_OPTION_SVG
/**************************************************************************
*
* Error Strings
*
* If this macro is set, `FT_Error_String` will return meaningful
* descriptions. This is not enabled by default to reduce the overall
* size of FreeType.
*
* More details can be found in the file `fterrors.h`.
*/
#define FT_CONFIG_OPTION_ERROR_STRINGS
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** S F N T D R I V E R C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_EMBEDDED_BITMAPS` if you want to support
* embedded bitmaps in all formats using the 'sfnt' module (namely
* TrueType~& OpenType).
*/
#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_COLOR_LAYERS` if you want to support colored
* outlines (from the 'COLR'/'CPAL' tables) in all formats using the 'sfnt'
* module (namely TrueType~& OpenType).
*/
#define TT_CONFIG_OPTION_COLOR_LAYERS
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_POSTSCRIPT_NAMES` if you want to be able to
* load and enumerate the glyph Postscript names in a TrueType or OpenType
* file.
*
* Note that when you do not compile the 'psnames' module by undefining the
* above `FT_CONFIG_OPTION_POSTSCRIPT_NAMES`, the 'sfnt' module will
* contain additional code used to read the PS Names table from a font.
*
* (By default, the module uses 'psnames' to extract glyph names.)
*/
#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_SFNT_NAMES` if your applications need to access
* the internal name table in a SFNT-based format like TrueType or
* OpenType. The name table contains various strings used to describe the
* font, like family name, copyright, version, etc. It does not contain
* any glyph name though.
*
* Accessing SFNT names is done through the functions declared in
* `ftsnames.h`.
*/
#define TT_CONFIG_OPTION_SFNT_NAMES
/**************************************************************************
*
* TrueType CMap support
*
* Here you can fine-tune which TrueType CMap table format shall be
* supported.
*/
#define TT_CONFIG_CMAP_FORMAT_0
#define TT_CONFIG_CMAP_FORMAT_2
#define TT_CONFIG_CMAP_FORMAT_4
#define TT_CONFIG_CMAP_FORMAT_6
#define TT_CONFIG_CMAP_FORMAT_8
#define TT_CONFIG_CMAP_FORMAT_10
#define TT_CONFIG_CMAP_FORMAT_12
#define TT_CONFIG_CMAP_FORMAT_13
#define TT_CONFIG_CMAP_FORMAT_14
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_BYTECODE_INTERPRETER` if you want to compile a
* bytecode interpreter in the TrueType driver.
*
* By undefining this, you will only compile the code necessary to load
* TrueType glyphs without hinting.
*
* Do not `#undef` this macro here, since the build system might define it
* for certain configurations only.
*/
#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_SUBPIXEL_HINTING` if you want to compile
* subpixel hinting support into the TrueType driver. This modifies the
* TrueType hinting mechanism when anything but `FT_RENDER_MODE_MONO` is
* requested.
*
* In particular, it modifies the bytecode interpreter to interpret (or
* not) instructions in a certain way so that all TrueType fonts look like
* they do in a Windows ClearType (DirectWrite) environment. See [1] for a
* technical overview on what this means. See `ttinterp.h` for more
* details on the LEAN option.
*
* There are three possible values.
*
* Value 1:
* This value is associated with the 'Infinality' moniker, contributed by
* an individual nicknamed Infinality with the goal of making TrueType
* fonts render better than on Windows. A high amount of configurability
* and flexibility, down to rules for single glyphs in fonts, but also
* very slow. Its experimental and slow nature and the original
* developer losing interest meant that this option was never enabled in
* default builds.
*
* The corresponding interpreter version is v38.
*
* Value 2:
* The new default mode for the TrueType driver. The Infinality code
* base was stripped to the bare minimum and all configurability removed
* in the name of speed and simplicity. The configurability was mainly
* aimed at legacy fonts like 'Arial', 'Times New Roman', or 'Courier'.
* Legacy fonts are fonts that modify vertical stems to achieve clean
* black-and-white bitmaps. The new mode focuses on applying a minimal
* set of rules to all fonts indiscriminately so that modern and web
* fonts render well while legacy fonts render okay.
*
* The corresponding interpreter version is v40.
*
* Value 3:
* Compile both, making both v38 and v40 available (the latter is the
* default).
*
* By undefining these, you get rendering behavior like on Windows without
* ClearType, i.e., Windows XP without ClearType enabled and Win9x
* (interpreter version v35). Or not, depending on how much hinting blood
* and testing tears the font designer put into a given font. If you
* define one or both subpixel hinting options, you can switch between
* between v35 and the ones you define (using `FT_Property_Set`).
*
* This option requires `TT_CONFIG_OPTION_BYTECODE_INTERPRETER` to be
* defined.
*
* [1]
* https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx
*/
/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 */
#define TT_CONFIG_OPTION_SUBPIXEL_HINTING 2
/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING ( 1 | 2 ) */
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED` to compile the
* TrueType glyph loader to use Apple's definition of how to handle
* component offsets in composite glyphs.
*
* Apple and MS disagree on the default behavior of component offsets in
* composites. Apple says that they should be scaled by the scaling
* factors in the transformation matrix (roughly, it's more complex) while
* MS says they should not. OpenType defines two bits in the composite
* flags array which can be used to disambiguate, but old fonts will not
* have them.
*
* https://www.microsoft.com/typography/otspec/glyf.htm
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html
*/
#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_GX_VAR_SUPPORT` if you want to include support
* for Apple's distortable font technology ('fvar', 'gvar', 'cvar', and
* 'avar' tables). Tagged 'Font Variations', this is now part of OpenType
* also. This has many similarities to Type~1 Multiple Masters support.
*/
#define TT_CONFIG_OPTION_GX_VAR_SUPPORT
/**************************************************************************
*
* Define `TT_CONFIG_OPTION_BDF` if you want to include support for an
* embedded 'BDF~' table within SFNT-based bitmap formats.
*/
#define TT_CONFIG_OPTION_BDF
/**************************************************************************
*
* Option `TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES` controls the maximum
* number of bytecode instructions executed for a single run of the
* bytecode interpreter, needed to prevent infinite loops. You don't want
* to change this except for very special situations (e.g., making a
* library fuzzer spend less time to handle broken fonts).
*
* It is not expected that this value is ever modified by a configuring
* script; instead, it gets surrounded with `#ifndef ... #endif` so that
* the value can be set as a preprocessor option on the compiler's command
* line.
*/
#ifndef TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES
#define TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES 1000000L
#endif
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/**************************************************************************
*
* `T1_MAX_DICT_DEPTH` is the maximum depth of nest dictionaries and arrays
* in the Type~1 stream (see `t1load.c`). A minimum of~4 is required.
*/
#define T1_MAX_DICT_DEPTH 5
/**************************************************************************
*
* `T1_MAX_SUBRS_CALLS` details the maximum number of nested sub-routine
* calls during glyph loading.
*/
#define T1_MAX_SUBRS_CALLS 16
/**************************************************************************
*
* `T1_MAX_CHARSTRING_OPERANDS` is the charstring stack's capacity. A
* minimum of~16 is required.
*
* The Chinese font 'MingTiEG-Medium' (covering the CNS 11643 character
* set) needs 256.
*/
#define T1_MAX_CHARSTRINGS_OPERANDS 256
/**************************************************************************
*
* Define this configuration macro if you want to prevent the compilation
* of the 't1afm' module, which is in charge of reading Type~1 AFM files
* into an existing face. Note that if set, the Type~1 driver will be
* unable to produce kerning distances.
*/
#undef T1_CONFIG_OPTION_NO_AFM
/**************************************************************************
*
* Define this configuration macro if you want to prevent the compilation
* of the Multiple Masters font support in the Type~1 driver.
*/
#undef T1_CONFIG_OPTION_NO_MM_SUPPORT
/**************************************************************************
*
* `T1_CONFIG_OPTION_OLD_ENGINE` controls whether the pre-Adobe Type~1
* engine gets compiled into FreeType. If defined, it is possible to
* switch between the two engines using the `hinting-engine` property of
* the 'type1' driver module.
*/
/* #define T1_CONFIG_OPTION_OLD_ENGINE */
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** C F F D R I V E R C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/**************************************************************************
*
* Using `CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4}` it is
* possible to set up the default values of the four control points that
* define the stem darkening behaviour of the (new) CFF engine. For more
* details please read the documentation of the `darkening-parameters`
* property (file `ftdriver.h`), which allows the control at run-time.
*
* Do **not** undefine these macros!
*/
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333
#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0
/**************************************************************************
*
* `CFF_CONFIG_OPTION_OLD_ENGINE` controls whether the pre-Adobe CFF engine
* gets compiled into FreeType. If defined, it is possible to switch
* between the two engines using the `hinting-engine` property of the 'cff'
* driver module.
*/
/* #define CFF_CONFIG_OPTION_OLD_ENGINE */
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** P C F D R I V E R C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/**************************************************************************
*
* There are many PCF fonts just called 'Fixed' which look completely
* different, and which have nothing to do with each other. When selecting
* 'Fixed' in KDE or Gnome one gets results that appear rather random, the
* style changes often if one changes the size and one cannot select some
* fonts at all. This option makes the 'pcf' module prepend the foundry
* name (plus a space) to the family name.
*
* We also check whether we have 'wide' characters; all put together, we
* get family names like 'Sony Fixed' or 'Misc Fixed Wide'.
*
* If this option is activated, it can be controlled with the
* `no-long-family-names` property of the 'pcf' driver module.
*/
/* #define PCF_CONFIG_OPTION_LONG_FAMILY_NAMES */
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/**************************************************************************
*
* Compile 'autofit' module with CJK (Chinese, Japanese, Korean) script
* support.
*/
#define AF_CONFIG_OPTION_CJK
/**************************************************************************
*
* Compile 'autofit' module with fallback Indic script support, covering
* some scripts that the 'latin' submodule of the 'autofit' module doesn't
* (yet) handle. Currently, this needs option `AF_CONFIG_OPTION_CJK`.
*/
#ifdef AF_CONFIG_OPTION_CJK
#define AF_CONFIG_OPTION_INDIC
#endif
/**************************************************************************
*
* Use TrueType-like size metrics for 'light' auto-hinting.
*
* It is strongly recommended to avoid this option, which exists only to
* help some legacy applications retain its appearance and behaviour with
* respect to auto-hinted TrueType fonts.
*
* The very reason this option exists at all are GNU/Linux distributions
* like Fedora that did not un-patch the following change (which was
* present in FreeType between versions 2.4.6 and 2.7.1, inclusive).
*
* ```
* 2011-07-16 Steven Chu <steven.f.chu@gmail.com>
*
* [truetype] Fix metrics on size request for scalable fonts.
* ```
*
* This problematic commit is now reverted (more or less).
*/
/* #define AF_CONFIG_OPTION_TT_SIZE_METRICS */
/* */
/*
* This macro is obsolete. Support has been removed in FreeType version
* 2.5.
*/
/* #define FT_CONFIG_OPTION_OLD_INTERNALS */
/*
* The next three macros are defined if native TrueType hinting is
* requested by the definitions above. Don't change this.
*/
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
#define TT_USE_BYTECODE_INTERPRETER
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 1
#define TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
#endif
#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 2
#define TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
#endif
#endif
#endif
/*
* The TT_SUPPORT_COLRV1 macro is defined to indicate to clients that this
* version of FreeType has support for 'COLR' v1 API. This definition is
* useful to FreeType clients that want to build in support for 'COLR' v1
* depending on a tip-of-tree checkout before it is officially released in
* FreeType, and while the feature cannot yet be tested against using
* version macros. Don't change this macro. This may be removed once the
* feature is in a FreeType release version and version macros can be used
* to test for availability.
*/
#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
#define TT_SUPPORT_COLRV1
#endif
/*
* Check CFF darkening parameters. The checks are the same as in function
* `cff_property_set` in file `cffdrivr.c`.
*/
#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \
\
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \
\
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \
\
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \
CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500
#error "Invalid CFF darkening parameters!"
#endif
FT_END_HEADER
#endif /* FTOPTION_H_ */
/* END */
@@ -0,0 +1,488 @@
/**
* @file lv_freetype.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_freetype_private.h"
#if LV_USE_FREETYPE
#include "../../misc/lv_fs_private.h"
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define ft_ctx LV_GLOBAL_DEFAULT()->ft_context
#define LV_FREETYPE_OUTLINE_REF_SIZE_DEF 128
/**< This value is from the FreeType's function `FT_GlyphSlot_Oblique` in `ftsynth.c` */
#define LV_FREETYPE_OBLIQUE_SLANT_DEF 0x0366A
#if LV_FREETYPE_CACHE_FT_GLYPH_CNT <= 0
#error "LV_FREETYPE_CACHE_FT_GLYPH_CNT must be greater than 0"
#endif
/**********************
* TYPEDEFS
**********************/
/* Use the pointer storing pathname as the unique request ID of the face */
typedef struct {
char * pathname;
int ref_cnt;
} face_id_node_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_freetype_cleanup(lv_freetype_context_t * ctx);
static FTC_FaceID lv_freetype_req_face_id(lv_freetype_context_t * ctx, const char * pathname);
static void lv_freetype_drop_face_id(lv_freetype_context_t * ctx, FTC_FaceID face_id);
static bool freetype_on_font_create(lv_freetype_font_dsc_t * dsc, uint32_t max_glyph_cnt);
static void freetype_on_font_set_cbs(lv_freetype_font_dsc_t * dsc);
static bool cache_node_cache_create_cb(lv_freetype_cache_node_t * node, void * user_data);
static void cache_node_cache_free_cb(lv_freetype_cache_node_t * node, void * user_data);
static lv_cache_compare_res_t cache_node_cache_compare_cb(const lv_freetype_cache_node_t * lhs,
const lv_freetype_cache_node_t * rhs);
static lv_font_t * freetype_font_create_cb(const lv_font_info_t * info, const void * src);
static void freetype_font_delete_cb(lv_font_t * font);
static void * freetype_font_dup_src_cb(const void * src);
static void freetype_font_free_src_cb(void * src);
/**********************
* STATIC VARIABLES
**********************/
const lv_font_class_t lv_freetype_font_class = {
.create_cb = freetype_font_create_cb,
.delete_cb = freetype_font_delete_cb,
.dup_src_cb = freetype_font_dup_src_cb,
.free_src_cb = freetype_font_free_src_cb,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_result_t lv_freetype_init(uint32_t max_glyph_cnt)
{
if(ft_ctx) {
LV_LOG_WARN("freetype already initialized");
return LV_RESULT_INVALID;
}
ft_ctx = lv_malloc_zeroed(sizeof(lv_freetype_context_t));
LV_ASSERT_MALLOC(ft_ctx);
if(!ft_ctx) {
LV_LOG_ERROR("malloc failed for lv_freetype_context_t");
return LV_RESULT_INVALID;
}
lv_freetype_context_t * ctx = lv_freetype_get_context();
ctx->max_glyph_cnt = max_glyph_cnt;
FT_Error error;
error = FT_Init_FreeType(&ctx->library);
if(error) {
FT_ERROR_MSG("FT_Init_FreeType", error);
return LV_RESULT_INVALID;
}
lv_ll_init(&ctx->face_id_ll, sizeof(face_id_node_t));
lv_cache_ops_t ops = {
.compare_cb = (lv_cache_compare_cb_t)cache_node_cache_compare_cb,
.create_cb = (lv_cache_create_cb_t)cache_node_cache_create_cb,
.free_cb = (lv_cache_free_cb_t)cache_node_cache_free_cb,
};
ctx->cache_node_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(lv_freetype_cache_node_t), INT32_MAX, ops);
lv_cache_set_name(ctx->cache_node_cache, "FREETYPE_CACHE_NODE");
return LV_RESULT_OK;
}
void lv_freetype_uninit(void)
{
lv_freetype_context_t * ctx = lv_freetype_get_context();
lv_freetype_cleanup(ctx);
lv_free(ft_ctx);
ft_ctx = NULL;
}
void lv_freetype_init_font_info(lv_font_info_t * font_info)
{
LV_ASSERT_NULL(font_info);
lv_memzero(font_info, sizeof(lv_font_info_t));
font_info->class_p = &lv_freetype_font_class;
font_info->render_mode = LV_FREETYPE_FONT_RENDER_MODE_BITMAP;
font_info->style = LV_FREETYPE_FONT_STYLE_NORMAL;
font_info->kerning = LV_FONT_KERNING_NONE;
}
lv_font_t * lv_freetype_font_create_with_info(const lv_font_info_t * font_info)
{
LV_ASSERT_NULL(font_info);
if(font_info->size == 0) {
LV_LOG_ERROR("font size can't be zero");
return NULL;
}
const char * pathname = font_info->name;
size_t pathname_len = pathname ? lv_strlen(pathname) : 0;
if(pathname_len == 0) {
LV_LOG_ERROR("font pathname can't be empty");
return NULL;
}
lv_freetype_context_t * ctx = lv_freetype_get_context();
lv_freetype_cache_node_t search_key = {
.pathname = lv_freetype_req_face_id(ctx, pathname),
.style = font_info->style,
.render_mode = font_info->render_mode,
};
bool cache_hitting = true;
lv_cache_entry_t * cache_node_entry = lv_cache_acquire(ctx->cache_node_cache, &search_key, NULL);
if(cache_node_entry == NULL) {
cache_hitting = false;
cache_node_entry = lv_cache_acquire_or_create(ctx->cache_node_cache, &search_key, NULL);
if(cache_node_entry == NULL) {
lv_freetype_drop_face_id(ctx, (FTC_FaceID)search_key.pathname);
LV_LOG_ERROR("cache node creating failed");
return NULL;
}
}
lv_freetype_font_dsc_t * dsc = lv_malloc_zeroed(sizeof(lv_freetype_font_dsc_t));
LV_ASSERT_MALLOC(dsc);
dsc->face_id = (FTC_FaceID)search_key.pathname;
dsc->render_mode = font_info->render_mode;
dsc->context = ctx;
dsc->size = font_info->size;
dsc->style = font_info->style;
dsc->kerning = font_info->kerning;
dsc->magic_num = LV_FREETYPE_FONT_DSC_MAGIC_NUM;
dsc->cache_node = lv_cache_entry_get_data(cache_node_entry);
dsc->cache_node_entry = cache_node_entry;
if(cache_hitting == false && freetype_on_font_create(dsc, ctx->max_glyph_cnt) == false) {
lv_cache_release(ctx->cache_node_cache, dsc->cache_node_entry, NULL);
lv_freetype_drop_face_id(ctx, dsc->face_id);
lv_free(dsc);
return NULL;
}
freetype_on_font_set_cbs(dsc);
FT_Face face = dsc->cache_node->face;
FT_Error error;
if(FT_IS_SCALABLE(face)) {
error = FT_Set_Pixel_Sizes(face, 0, font_info->size);
}
else {
LV_LOG_WARN("font is not scalable, selecting available size");
error = FT_Select_Size(face, 0);
}
if(error) {
FT_ERROR_MSG("FT_Set_Pixel_Sizes", error);
return NULL;
}
if(dsc->kerning != LV_FONT_KERNING_NONE && !dsc->cache_node->face_has_kerning) {
LV_LOG_WARN("font: '%s' doesn't have kerning info", pathname);
}
lv_font_t * font = &dsc->font;
font->dsc = dsc;
font->subpx = LV_FONT_SUBPX_NONE;
font->line_height = FT_F26DOT6_TO_INT(face->size->metrics.height);
font->base_line = -FT_F26DOT6_TO_INT(face->size->metrics.descender);
FT_Fixed scale = face->size->metrics.y_scale;
int8_t thickness = FT_F26DOT6_TO_INT(FT_MulFix(scale, face->underline_thickness));
font->underline_position = FT_F26DOT6_TO_INT(FT_MulFix(scale, face->underline_position));
font->underline_thickness = thickness < 1 ? 1 : thickness;
return font;
}
lv_font_t * lv_freetype_font_create(const char * pathname, lv_freetype_font_render_mode_t render_mode, uint32_t size,
lv_freetype_font_style_t style)
{
lv_font_info_t font_info;
lv_freetype_init_font_info(&font_info);
font_info.name = pathname;
font_info.size = size;
font_info.render_mode = render_mode;
font_info.style = style;
return lv_freetype_font_create_with_info(&font_info);
}
void lv_freetype_font_delete(lv_font_t * font)
{
LV_ASSERT_NULL(font);
lv_freetype_context_t * ctx = lv_freetype_get_context();
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)(font->dsc);
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
lv_cache_release(ctx->cache_node_cache, dsc->cache_node_entry, NULL);
if(lv_cache_entry_get_ref(dsc->cache_node_entry) == 0) {
lv_cache_drop(ctx->cache_node_cache, dsc->cache_node, NULL);
}
lv_freetype_drop_face_id(dsc->context, dsc->face_id);
/* invalidate magic number */
lv_memzero(dsc, sizeof(lv_freetype_font_dsc_t));
lv_free(dsc);
}
lv_freetype_context_t * lv_freetype_get_context(void)
{
return LV_GLOBAL_DEFAULT()->ft_context;
}
void lv_freetype_italic_transform(FT_Face face)
{
LV_ASSERT_NULL(face);
FT_Matrix matrix;
matrix.xx = FT_INT_TO_F16DOT16(1);
matrix.xy = LV_FREETYPE_OBLIQUE_SLANT_DEF;
matrix.yx = 0;
matrix.yy = FT_INT_TO_F16DOT16(1);
FT_Set_Transform(face, &matrix, NULL);
}
int32_t lv_freetype_italic_transform_on_pos(lv_point_t point)
{
return point.x + FT_F16DOT16_TO_INT(point.y * LV_FREETYPE_OBLIQUE_SLANT_DEF);
}
/**********************
* STATIC FUNCTIONS
**********************/
static bool freetype_on_font_create(lv_freetype_font_dsc_t * dsc, uint32_t max_glyph_cnt)
{
/*
* Glyph info uses a small amount of memory, and uses glyph info more frequently,
* so it plans to use twice the maximum number of caches here to
* get a better info acquisition performance.*/
lv_cache_t * glyph_cache = lv_freetype_create_glyph_cache(max_glyph_cnt * 2);
if(glyph_cache == NULL) {
LV_LOG_ERROR("glyph cache creating failed");
return false;
}
dsc->cache_node->glyph_cache = glyph_cache;
lv_cache_t * draw_data_cache = NULL;
if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_BITMAP) {
draw_data_cache = lv_freetype_create_draw_data_image(max_glyph_cnt);
}
else if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_OUTLINE) {
draw_data_cache = lv_freetype_create_draw_data_outline(max_glyph_cnt);
}
else {
LV_LOG_ERROR("unknown render mode");
return false;
}
if(draw_data_cache == NULL) {
LV_LOG_ERROR("draw data cache creating failed");
return false;
}
dsc->cache_node->draw_data_cache = draw_data_cache;
return true;
}
static void freetype_on_font_set_cbs(lv_freetype_font_dsc_t * dsc)
{
lv_freetype_set_cbs_glyph(dsc);
if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_BITMAP) {
lv_freetype_set_cbs_image_font(dsc);
}
else if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_OUTLINE) {
lv_freetype_set_cbs_outline_font(dsc);
}
}
static void lv_freetype_cleanup(lv_freetype_context_t * ctx)
{
LV_ASSERT_NULL(ctx);
if(ctx->cache_node_cache) {
lv_cache_destroy(ctx->cache_node_cache, NULL);
ctx->cache_node_cache = NULL;
}
if(ctx->library) {
FT_Done_FreeType(ctx->library);
ctx->library = NULL;
}
}
static FTC_FaceID lv_freetype_req_face_id(lv_freetype_context_t * ctx, const char * pathname)
{
size_t len = lv_strlen(pathname);
LV_ASSERT(len > 0);
lv_ll_t * ll_p = &ctx->face_id_ll;
face_id_node_t * node;
/* search cache */
LV_LL_READ(ll_p, node) {
if(strcmp(node->pathname, pathname) == 0) {
node->ref_cnt++;
LV_LOG_INFO("reuse face_id: %s, ref_cnt = %d", node->pathname, node->ref_cnt);
return node->pathname;
}
}
/* insert new cache */
node = lv_ll_ins_tail(ll_p);
LV_ASSERT_MALLOC(node);
#if LV_USE_FS_MEMFS
if(pathname[0] == LV_FS_MEMFS_LETTER) {
#if !LV_FREETYPE_USE_LVGL_PORT
LV_LOG_WARN("LV_FREETYPE_USE_LVGL_PORT is not enabled");
#endif
node->pathname = lv_malloc(sizeof(lv_fs_path_ex_t));
LV_ASSERT_MALLOC(node->pathname);
lv_memcpy(node->pathname, pathname, sizeof(lv_fs_path_ex_t));
}
else
#endif
{
node->pathname = lv_strdup(pathname);
LV_ASSERT_NULL(node->pathname);
}
LV_LOG_INFO("add face_id: %s", node->pathname);
node->ref_cnt = 1;
return node->pathname;
}
static void lv_freetype_drop_face_id(lv_freetype_context_t * ctx, FTC_FaceID face_id)
{
lv_ll_t * ll_p = &ctx->face_id_ll;
face_id_node_t * node;
LV_LL_READ(ll_p, node) {
if(face_id == node->pathname) {
LV_LOG_INFO("found face_id: %s, ref_cnt = %d", node->pathname, node->ref_cnt);
node->ref_cnt--;
if(node->ref_cnt == 0) {
LV_LOG_INFO("drop face_id: %s", node->pathname);
lv_ll_remove(ll_p, node);
lv_free(node->pathname);
lv_free(node);
}
return;
}
}
LV_ASSERT_MSG(false, "face_id not found");
}
/*-----------------
* Cache Node Cache Callbacks
*----------------*/
static bool cache_node_cache_create_cb(lv_freetype_cache_node_t * node, void * user_data)
{
LV_UNUSED(user_data);
lv_freetype_context_t * ctx = lv_freetype_get_context();
/* Cache miss, load face */
FT_Face face;
FT_Error error = FT_New_Face(ctx->library, node->pathname, 0, &face);
if(error) {
FT_ERROR_MSG("FT_New_Face", error);
return false;
}
node->ref_size = LV_FREETYPE_OUTLINE_REF_SIZE_DEF;
if(node->style & LV_FREETYPE_FONT_STYLE_ITALIC) {
lv_freetype_italic_transform(face);
}
node->face = face;
node->face_has_kerning = FT_HAS_KERNING(face);
lv_mutex_init(&node->face_lock);
return true;
}
static void cache_node_cache_free_cb(lv_freetype_cache_node_t * node, void * user_data)
{
FT_Done_Face(node->face);
lv_mutex_delete(&node->face_lock);
if(node->glyph_cache) {
lv_cache_destroy(node->glyph_cache, user_data);
node->glyph_cache = NULL;
}
if(node->draw_data_cache) {
lv_cache_destroy(node->draw_data_cache, user_data);
node->draw_data_cache = NULL;
}
}
static lv_cache_compare_res_t cache_node_cache_compare_cb(const lv_freetype_cache_node_t * lhs,
const lv_freetype_cache_node_t * rhs)
{
if(lhs->render_mode != rhs->render_mode) {
return lhs->render_mode > rhs->render_mode ? 1 : -1;
}
if(lhs->style != rhs->style) {
return lhs->style > rhs->style ? 1 : -1;
}
int32_t cmp_res = lv_strcmp(lhs->pathname, rhs->pathname);
if(cmp_res != 0) {
return cmp_res > 0 ? 1 : -1;
}
return 0;
}
static lv_font_t * freetype_font_create_cb(const lv_font_info_t * info, const void * src)
{
lv_font_info_t font_info = *info;
font_info.name = src;
return lv_freetype_font_create_with_info(&font_info);
}
static void freetype_font_delete_cb(lv_font_t * font)
{
lv_freetype_font_delete(font);
}
static void * freetype_font_dup_src_cb(const void * src)
{
return lv_strdup(src);
}
static void freetype_font_free_src_cb(void * src)
{
lv_free(src);
}
#endif /*LV_USE_FREETYPE*/
@@ -0,0 +1,149 @@
/**
* @file lv_freetype.h
*
*/
#ifndef LV_FREETYPE_H
#define LV_FREETYPE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_FREETYPE
#include "../../misc/lv_types.h"
#include "../../misc/lv_event.h"
#include "../../misc/lv_color.h"
#include LV_STDBOOL_INCLUDE
/*********************
* DEFINES
*********************/
#define LV_FREETYPE_F26DOT6_TO_INT(x) ((x) >> 6)
#define LV_FREETYPE_F26DOT6_TO_FLOAT(x) ((float)(x) / 64)
#define FT_FONT_STYLE_NORMAL LV_FREETYPE_FONT_STYLE_NORMAL
#define FT_FONT_STYLE_ITALIC LV_FREETYPE_FONT_STYLE_ITALIC
#define FT_FONT_STYLE_BOLD LV_FREETYPE_FONT_STYLE_BOLD
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_FREETYPE_FONT_STYLE_NORMAL = 0,
LV_FREETYPE_FONT_STYLE_ITALIC = 1 << 0,
LV_FREETYPE_FONT_STYLE_BOLD = 1 << 1,
} lv_freetype_font_style_t;
typedef lv_freetype_font_style_t LV_FT_FONT_STYLE;
typedef enum {
LV_FREETYPE_FONT_RENDER_MODE_BITMAP = 0,
LV_FREETYPE_FONT_RENDER_MODE_OUTLINE = 1,
} lv_freetype_font_render_mode_t;
typedef void * lv_freetype_outline_t;
typedef enum {
LV_FREETYPE_OUTLINE_END,
LV_FREETYPE_OUTLINE_MOVE_TO,
LV_FREETYPE_OUTLINE_LINE_TO,
LV_FREETYPE_OUTLINE_CUBIC_TO,
LV_FREETYPE_OUTLINE_CONIC_TO,
LV_FREETYPE_OUTLINE_BORDER_START, /* When line width > 0 the border glyph is drawn after the regular glyph */
} lv_freetype_outline_type_t;
/* Only path string is required */
typedef const char lv_freetype_font_src_t;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_font_class_t lv_freetype_font_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the freetype library.
* @return LV_RESULT_OK on success, otherwise LV_RESULT_INVALID.
*/
lv_result_t lv_freetype_init(uint32_t max_glyph_cnt);
/**
* Uninitialize the freetype library
*/
void lv_freetype_uninit(void);
/**
* Initialize a font info structure.
* @param font_info font info structure to be initialized.
*/
void lv_freetype_init_font_info(lv_font_info_t * font_info);
/**
* Create a freetype font with a font info structure.
* @param font_info font info structure.
* @return Created font, or NULL on failure.
*/
lv_font_t * lv_freetype_font_create_with_info(const lv_font_info_t * font_info);
/**
* Create a freetype font.
* @param pathname font file path.
* @param render_mode font render mode(see @lv_freetype_font_render_mode_t for details).
* @param size font size.
* @param style font style(see lv_freetype_font_style_t for details).
* @return Created font, or NULL on failure.
*/
lv_font_t * lv_freetype_font_create(const char * pathname, lv_freetype_font_render_mode_t render_mode, uint32_t size,
lv_freetype_font_style_t style);
/**
* Delete a freetype font.
* @param font freetype font to be deleted.
*/
void lv_freetype_font_delete(lv_font_t * font);
/**
* Register a callback function to generate outlines for FreeType fonts.
*
* @param cb The callback function to be registered.
* @param user_data User data to be passed to the callback function.
* @return The ID of the registered callback function, or a negative value on failure.
*/
void lv_freetype_outline_add_event(lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);
/**
* Get the scale of a FreeType font.
*
* @param font The FreeType font to get the scale of.
* @return The scale of the FreeType font.
*/
uint32_t lv_freetype_outline_get_scale(const lv_font_t * font);
/**
* Check if the font is an outline font.
*
* @param font The FreeType font.
* @return Is outline font on success, otherwise false.
*/
bool lv_freetype_is_outline_font(const lv_font_t * font);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FREETYPE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_FREETYPE_H */
@@ -0,0 +1,242 @@
/**
* @file lv_freetype_glyph.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lvgl.h"
#include "lv_freetype_private.h"
#if LV_USE_FREETYPE
/*********************
* DEFINES
*********************/
#define CACHE_NAME "FREETYPE_GLYPH"
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_freetype_glyph_cache_data_t {
uint32_t unicode;
uint32_t size;
lv_font_glyph_dsc_t glyph_dsc;
} lv_freetype_glyph_cache_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
static bool freetype_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc, uint32_t unicode_letter,
uint32_t unicode_letter_next);
static bool freetype_glyph_create_cb(lv_freetype_glyph_cache_data_t * data, void * user_data);
static void freetype_glyph_free_cb(lv_freetype_glyph_cache_data_t * data, void * user_data);
static lv_cache_compare_res_t freetype_glyph_compare_cb(const lv_freetype_glyph_cache_data_t * lhs,
const lv_freetype_glyph_cache_data_t * rhs);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_cache_t * lv_freetype_create_glyph_cache(uint32_t cache_size)
{
lv_cache_ops_t ops = {
.create_cb = (lv_cache_create_cb_t)freetype_glyph_create_cb,
.free_cb = (lv_cache_free_cb_t)freetype_glyph_free_cb,
.compare_cb = (lv_cache_compare_cb_t)freetype_glyph_compare_cb,
};
lv_cache_t * glyph_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(lv_freetype_glyph_cache_data_t),
cache_size, ops);
lv_cache_set_name(glyph_cache, CACHE_NAME);
return glyph_cache;
}
void lv_freetype_set_cbs_glyph(lv_freetype_font_dsc_t * dsc)
{
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
dsc->font.get_glyph_dsc = freetype_get_glyph_dsc_cb;
}
/**********************
* STATIC FUNCTIONS
**********************/
static bool freetype_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc, uint32_t unicode_letter,
uint32_t unicode_letter_next)
{
LV_ASSERT_NULL(font);
LV_ASSERT_NULL(g_dsc);
LV_PROFILER_FONT_BEGIN;
if(unicode_letter < 0x20) {
g_dsc->adv_w = 0;
g_dsc->box_h = 0;
g_dsc->box_w = 0;
g_dsc->ofs_x = 0;
g_dsc->ofs_y = 0;
g_dsc->format = LV_FONT_GLYPH_FORMAT_NONE;
LV_PROFILER_FONT_END;
return true;
}
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)font->dsc;
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
lv_freetype_glyph_cache_data_t search_key = {
.unicode = unicode_letter,
.size = dsc->size,
};
lv_cache_t * glyph_cache = dsc->cache_node->glyph_cache;
lv_cache_entry_t * entry = lv_cache_acquire_or_create(glyph_cache, &search_key, dsc);
if(entry == NULL) {
LV_LOG_ERROR("glyph lookup failed for unicode = 0x%" LV_PRIx32, unicode_letter);
LV_PROFILER_FONT_END;
return false;
}
lv_freetype_glyph_cache_data_t * data = lv_cache_entry_get_data(entry);
*g_dsc = data->glyph_dsc;
if((dsc->style & LV_FREETYPE_FONT_STYLE_ITALIC) && (unicode_letter_next == '\0')) {
g_dsc->adv_w = g_dsc->box_w + g_dsc->ofs_x;
}
if(dsc->kerning == LV_FONT_KERNING_NORMAL && dsc->cache_node->face_has_kerning && unicode_letter_next != '\0') {
lv_mutex_lock(&dsc->cache_node->face_lock);
FT_Face face = dsc->cache_node->face;
FT_UInt glyph_index_next = FT_Get_Char_Index(face, unicode_letter_next);
FT_Vector kerning;
FT_Error error = FT_Get_Kerning(face, g_dsc->gid.index, glyph_index_next, FT_KERNING_DEFAULT, &kerning);
if(!error) {
g_dsc->adv_w += LV_FREETYPE_F26DOT6_TO_INT(kerning.x);
}
else {
FT_ERROR_MSG("FT_Get_Kerning", error);
}
lv_mutex_unlock(&dsc->cache_node->face_lock);
}
g_dsc->entry = NULL;
lv_cache_release(glyph_cache, entry, NULL);
LV_PROFILER_FONT_END;
return true;
}
/*-----------------
* Cache Callbacks
*----------------*/
static bool freetype_glyph_create_cb(lv_freetype_glyph_cache_data_t * data, void * user_data)
{
LV_PROFILER_FONT_BEGIN;
FT_Error error;
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)user_data;
lv_font_glyph_dsc_t * dsc_out = &data->glyph_dsc;
lv_mutex_lock(&dsc->cache_node->face_lock);
FT_Face face = dsc->cache_node->face;
FT_UInt glyph_index = FT_Get_Char_Index(face, data->unicode);
if(FT_IS_SCALABLE(face)) {
error = FT_Set_Pixel_Sizes(face, 0, dsc->size);
}
else {
error = FT_Select_Size(face, 0);
}
if(error) {
FT_ERROR_MSG("FT_Set_Pixel_Sizes", error);
lv_mutex_unlock(&dsc->cache_node->face_lock);
return false;
}
if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_OUTLINE) {
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_COMPUTE_METRICS | FT_LOAD_NO_BITMAP | FT_LOAD_NO_AUTOHINT);
}
else if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_BITMAP) {
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_COMPUTE_METRICS | FT_LOAD_NO_AUTOHINT);
}
if(error) {
FT_ERROR_MSG("FT_Load_Glyph", error);
lv_mutex_unlock(&dsc->cache_node->face_lock);
LV_PROFILER_FONT_END;
return false;
}
FT_GlyphSlot glyph = face->glyph;
if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_OUTLINE) {
dsc_out->adv_w = FT_F26DOT6_TO_INT(glyph->metrics.horiAdvance);
dsc_out->box_h = FT_F26DOT6_TO_INT(glyph->metrics.height); /*Height of the bitmap in [px]*/
dsc_out->box_w = FT_F26DOT6_TO_INT(glyph->metrics.width); /*Width of the bitmap in [px]*/
dsc_out->ofs_x = FT_F26DOT6_TO_INT(glyph->metrics.horiBearingX); /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = FT_F26DOT6_TO_INT(glyph->metrics.horiBearingY -
glyph->metrics.height); /*Y offset of the bitmap measured from the as line*/
dsc_out->format = LV_FONT_GLYPH_FORMAT_VECTOR;
/*Transform the glyph to italic if required */
if(dsc->style & LV_FREETYPE_FONT_STYLE_ITALIC) {
dsc_out->box_w = lv_freetype_italic_transform_on_pos((lv_point_t) {
dsc_out->box_w, dsc_out->box_h
});
}
}
else if(dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_BITMAP) {
FT_Bitmap * glyph_bitmap = &face->glyph->bitmap;
dsc_out->adv_w = FT_F26DOT6_TO_INT(glyph->advance.x); /*Width of the glyph in [pf]*/
dsc_out->box_h = glyph_bitmap->rows; /*Height of the bitmap in [px]*/
dsc_out->box_w = glyph_bitmap->width; /*Width of the bitmap in [px]*/
dsc_out->ofs_x = glyph->bitmap_left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = glyph->bitmap_top -
dsc_out->box_h; /*Y offset of the bitmap measured from the as line*/
if(glyph->format == FT_GLYPH_FORMAT_BITMAP)
dsc_out->format = LV_FONT_GLYPH_FORMAT_IMAGE;
else
dsc_out->format = LV_FONT_GLYPH_FORMAT_A8;
}
dsc_out->is_placeholder = glyph_index == 0;
dsc_out->gid.index = (uint32_t)glyph_index;
lv_mutex_unlock(&dsc->cache_node->face_lock);
LV_PROFILER_FONT_END;
return true;
}
static void freetype_glyph_free_cb(lv_freetype_glyph_cache_data_t * data, void * user_data)
{
LV_UNUSED(data);
LV_UNUSED(user_data);
}
static lv_cache_compare_res_t freetype_glyph_compare_cb(const lv_freetype_glyph_cache_data_t * lhs,
const lv_freetype_glyph_cache_data_t * rhs)
{
if(lhs->unicode != rhs->unicode) {
return lhs->unicode > rhs->unicode ? 1 : -1;
}
if(lhs->size != rhs->size) {
return lhs->size > rhs->size ? 1 : -1;
}
return 0;
}
#endif /*LV_USE_FREETYPE*/
@@ -0,0 +1,226 @@
/**
* @file lv_freetype_image.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lvgl.h"
#include "lv_freetype_private.h"
#if LV_USE_FREETYPE
#include "../../core/lv_global.h"
#define font_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->font_draw_buf_handlers)
/*********************
* DEFINES
*********************/
#define CACHE_NAME "FREETYPE_IMAGE"
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_freetype_image_cache_data_t {
FT_UInt glyph_index;
uint32_t size;
lv_draw_buf_t * draw_buf;
} lv_freetype_image_cache_data_t;
/**********************
* STATIC PROTOTYPES
**********************/
static const void * freetype_get_glyph_bitmap_cb(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf);
static bool freetype_image_create_cb(lv_freetype_image_cache_data_t * data, void * user_data);
static void freetype_image_free_cb(lv_freetype_image_cache_data_t * node, void * user_data);
static lv_cache_compare_res_t freetype_image_compare_cb(const lv_freetype_image_cache_data_t * lhs,
const lv_freetype_image_cache_data_t * rhs);
static void freetype_image_release_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_cache_t * lv_freetype_create_draw_data_image(uint32_t cache_size)
{
lv_cache_ops_t ops = {
.compare_cb = (lv_cache_compare_cb_t)freetype_image_compare_cb,
.create_cb = (lv_cache_create_cb_t)freetype_image_create_cb,
.free_cb = (lv_cache_free_cb_t)freetype_image_free_cb,
};
lv_cache_t * draw_data_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(lv_freetype_image_cache_data_t),
cache_size, ops);
lv_cache_set_name(draw_data_cache, CACHE_NAME);
return draw_data_cache;
}
void lv_freetype_set_cbs_image_font(lv_freetype_font_dsc_t * dsc)
{
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
dsc->font.get_glyph_bitmap = freetype_get_glyph_bitmap_cb;
dsc->font.release_glyph = freetype_image_release_cb;
}
/**********************
* STATIC FUNCTIONS
**********************/
static const void * freetype_get_glyph_bitmap_cb(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf)
{
LV_UNUSED(draw_buf);
LV_PROFILER_FONT_BEGIN;
const lv_font_t * font = g_dsc->resolved_font;
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)font->dsc;
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
FT_UInt glyph_index = (FT_UInt)g_dsc->gid.index;
lv_cache_t * cache = dsc->cache_node->draw_data_cache;
lv_freetype_image_cache_data_t search_key = {
.glyph_index = glyph_index,
.size = dsc->size,
};
lv_cache_entry_t * entry = lv_cache_acquire_or_create(cache, &search_key, dsc);
if(entry == NULL) {
LV_LOG_ERROR("glyph bitmap lookup failed for glyph_index = 0x%" LV_PRIx32, (uint32_t)glyph_index);
LV_PROFILER_FONT_END;
return NULL;
}
g_dsc->entry = entry;
lv_freetype_image_cache_data_t * cache_node = lv_cache_entry_get_data(entry);
LV_PROFILER_FONT_END;
return cache_node->draw_buf;
}
static void freetype_image_release_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc)
{
LV_ASSERT_NULL(font);
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)font->dsc;
lv_cache_release(dsc->cache_node->draw_data_cache, g_dsc->entry, NULL);
g_dsc->entry = NULL;
}
/*-----------------
* Cache Callbacks
*----------------*/
static bool freetype_image_create_cb(lv_freetype_image_cache_data_t * data, void * user_data)
{
LV_PROFILER_FONT_BEGIN;
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)user_data;
FT_Error error;
lv_mutex_lock(&dsc->cache_node->face_lock);
FT_Face face = dsc->cache_node->face;
if(FT_IS_SCALABLE(face)) {
error = FT_Set_Pixel_Sizes(face, 0, dsc->size);
}
else {
error = FT_Select_Size(face, 0);
}
if(error) {
FT_ERROR_MSG("FT_Set_Pixel_Sizes", error);
lv_mutex_unlock(&dsc->cache_node->face_lock);
return false;
}
error = FT_Load_Glyph(face, data->glyph_index,
FT_LOAD_COLOR | FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_AUTOHINT);
if(error) {
FT_ERROR_MSG("FT_Load_Glyph", error);
lv_mutex_unlock(&dsc->cache_node->face_lock);
LV_PROFILER_FONT_END;
return false;
}
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
if(error) {
FT_ERROR_MSG("FT_Render_Glyph", error);
lv_mutex_unlock(&dsc->cache_node->face_lock);
LV_PROFILER_FONT_END;
return false;
}
FT_Glyph glyph;
error = FT_Get_Glyph(face->glyph, &glyph);
if(error) {
FT_ERROR_MSG("FT_Get_Glyph", error);
lv_mutex_unlock(&dsc->cache_node->face_lock);
LV_PROFILER_FONT_END;
return false;
}
FT_BitmapGlyph glyph_bitmap = (FT_BitmapGlyph)glyph;
uint16_t box_h = glyph_bitmap->bitmap.rows; /*Height of the bitmap in [px]*/
uint16_t box_w = glyph_bitmap->bitmap.width; /*Width of the bitmap in [px]*/
lv_color_format_t col_format;
if(glyph_bitmap->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
col_format = LV_COLOR_FORMAT_ARGB8888;
}
else {
col_format = LV_COLOR_FORMAT_A8;
}
uint32_t pitch = glyph_bitmap->bitmap.pitch;
uint32_t stride = lv_draw_buf_width_to_stride(box_w, col_format);
data->draw_buf = lv_draw_buf_create_ex(font_draw_buf_handlers, box_w, box_h, col_format, stride);
if(!data->draw_buf) {
LV_LOG_WARN("Could not create draw buffer");
FT_Done_Glyph(glyph);
LV_PROFILER_FONT_END;
return false;
}
lv_draw_buf_clear(data->draw_buf, NULL);
for(int y = 0; y < box_h; ++y) {
lv_memcpy((uint8_t *)(data->draw_buf->data) + y * stride, glyph_bitmap->bitmap.buffer + y * pitch,
pitch);
}
lv_draw_buf_flush_cache(data->draw_buf, NULL);
FT_Done_Glyph(glyph);
lv_mutex_unlock(&dsc->cache_node->face_lock);
LV_PROFILER_FONT_END;
return true;
}
static void freetype_image_free_cb(lv_freetype_image_cache_data_t * data, void * user_data)
{
LV_UNUSED(user_data);
lv_draw_buf_destroy(data->draw_buf);
}
static lv_cache_compare_res_t freetype_image_compare_cb(const lv_freetype_image_cache_data_t * lhs,
const lv_freetype_image_cache_data_t * rhs)
{
if(lhs->glyph_index != rhs->glyph_index) {
return lhs->glyph_index > rhs->glyph_index ? 1 : -1;
}
if(lhs->size != rhs->size) {
return lhs->size > rhs->size ? 1 : -1;
}
return 0;
}
#endif /*LV_USE_FREETYPE*/
@@ -0,0 +1,481 @@
/**
* @file lv_freetype_outline.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_event_private.h"
#include "../../lvgl.h"
#include "lv_freetype_private.h"
#if LV_USE_FREETYPE
/*********************
* DEFINES
*********************/
#define CACHE_NAME "FREETYPE_OUTLINE"
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_freetype_outline_node_t {
FT_UInt glyph_index;
lv_freetype_outline_t outline;
} lv_freetype_outline_node_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_freetype_outline_t outline_create(lv_freetype_context_t * ctx, FT_Face face, FT_UInt glyph_index,
uint32_t size, uint32_t strength, uint32_t border_width);
static lv_result_t outline_delete(lv_freetype_context_t * ctx, lv_freetype_outline_t outline);
static const void * freetype_get_glyph_bitmap_cb(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf);
static void freetype_release_glyph_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc);
static lv_cache_entry_t * lv_freetype_outline_lookup(lv_freetype_font_dsc_t * dsc, FT_UInt glyph_index);
/*glyph dsc cache lru callbacks*/
static bool freetype_glyph_outline_create_cb(lv_freetype_outline_node_t * node, lv_freetype_font_dsc_t * dsc);
static void freetype_glyph_outline_free_cb(lv_freetype_outline_node_t * node, lv_freetype_font_dsc_t * dsc);
static lv_cache_compare_res_t freetype_glyph_outline_cmp_cb(const lv_freetype_outline_node_t * node_a,
const lv_freetype_outline_node_t * node_b);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_cache_t * lv_freetype_create_draw_data_outline(uint32_t cache_size)
{
lv_cache_ops_t glyph_outline_cache_ops = {
.create_cb = (lv_cache_create_cb_t)freetype_glyph_outline_create_cb,
.free_cb = (lv_cache_free_cb_t)freetype_glyph_outline_free_cb,
.compare_cb = (lv_cache_compare_cb_t)freetype_glyph_outline_cmp_cb,
};
lv_cache_t * draw_data_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(lv_freetype_outline_node_t),
cache_size,
glyph_outline_cache_ops);
lv_cache_set_name(draw_data_cache, CACHE_NAME);
return draw_data_cache;
}
void lv_freetype_set_cbs_outline_font(lv_freetype_font_dsc_t * dsc)
{
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
dsc->font.get_glyph_bitmap = freetype_get_glyph_bitmap_cb;
dsc->font.release_glyph = freetype_release_glyph_cb;
}
void lv_freetype_outline_add_event(lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data)
{
LV_UNUSED(user_data);
lv_freetype_context_t * ctx = lv_freetype_get_context();
LV_UNUSED(filter);
ctx->event_cb = event_cb;
}
uint32_t lv_freetype_outline_get_scale(const lv_font_t * font)
{
LV_ASSERT_NULL(font);
const lv_freetype_font_dsc_t * dsc = font->dsc;
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
return FT_INT_TO_F26DOT6(dsc->size) / dsc->cache_node->ref_size;
}
bool lv_freetype_is_outline_font(const lv_font_t * font)
{
LV_ASSERT_NULL(font);
const lv_freetype_font_dsc_t * dsc = font->dsc;
if(!LV_FREETYPE_FONT_DSC_HAS_MAGIC_NUM(dsc)) {
return false;
}
return dsc->render_mode == LV_FREETYPE_FONT_RENDER_MODE_OUTLINE;
}
/**********************
* STATIC FUNCTIONS
**********************/
/*-------------------
* OUTLINE CACHE
*------------------*/
static bool freetype_glyph_outline_create_cb(lv_freetype_outline_node_t * node, lv_freetype_font_dsc_t * dsc)
{
LV_PROFILER_FONT_BEGIN;
lv_freetype_outline_t outline;
lv_mutex_lock(&dsc->cache_node->face_lock);
outline = outline_create(dsc->context,
dsc->cache_node->face,
node->glyph_index,
dsc->cache_node->ref_size,
dsc->style & LV_FREETYPE_FONT_STYLE_BOLD ? 1 : 0,
dsc->outline_stroke_width);
lv_mutex_unlock(&dsc->cache_node->face_lock);
if(!outline) {
LV_PROFILER_FONT_END;
return false;
}
LV_LOG_INFO("glyph_index = 0x%" LV_PRIx32, (uint32_t)node->glyph_index);
node->outline = outline;
LV_PROFILER_FONT_END;
return true;
}
static void freetype_glyph_outline_free_cb(lv_freetype_outline_node_t * node, lv_freetype_font_dsc_t * dsc)
{
LV_UNUSED(dsc);
lv_freetype_outline_t outline = node->outline;
lv_freetype_context_t * ctx = lv_freetype_get_context();
outline_delete(ctx, outline);
}
static lv_cache_compare_res_t freetype_glyph_outline_cmp_cb(const lv_freetype_outline_node_t * node_a,
const lv_freetype_outline_node_t * node_b)
{
if(node_a->glyph_index == node_b->glyph_index) {
return 0;
}
return node_a->glyph_index > node_b->glyph_index ? 1 : -1;
}
static const void * freetype_get_glyph_bitmap_cb(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf)
{
LV_UNUSED(draw_buf);
const lv_font_t * font = g_dsc->resolved_font;
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)font->dsc;
LV_ASSERT_FREETYPE_FONT_DSC(dsc);
dsc->outline_stroke_width = g_dsc->outline_stroke_width;
lv_cache_entry_t * entry = lv_freetype_outline_lookup(dsc, (FT_UInt)g_dsc->gid.index);
if(entry == NULL) {
return NULL;
}
lv_freetype_outline_node_t * node = lv_cache_entry_get_data(entry);
g_dsc->entry = entry;
return node ? node->outline : NULL;
}
static void freetype_release_glyph_cb(const lv_font_t * font, lv_font_glyph_dsc_t * g_dsc)
{
LV_ASSERT_NULL(font);
lv_freetype_font_dsc_t * dsc = (lv_freetype_font_dsc_t *)font->dsc;
if(g_dsc->entry == NULL) {
return;
}
lv_cache_release(dsc->cache_node->draw_data_cache, g_dsc->entry, NULL);
g_dsc->entry = NULL;
}
static lv_cache_entry_t * lv_freetype_outline_lookup(lv_freetype_font_dsc_t * dsc, FT_UInt glyph_index)
{
LV_PROFILER_FONT_BEGIN;
lv_freetype_cache_node_t * cache_node = dsc->cache_node;
lv_freetype_outline_node_t tmp_node;
tmp_node.glyph_index = glyph_index;
lv_cache_entry_t * entry = lv_cache_acquire_or_create(cache_node->draw_data_cache, &tmp_node, dsc);
if(!entry) {
LV_LOG_ERROR("glyph outline lookup failed for glyph_index = 0x%" LV_PRIx32, (uint32_t)glyph_index);
LV_PROFILER_FONT_END;
return NULL;
}
LV_PROFILER_FONT_END;
return entry;
}
static void ft_vector_to_lv_vector(lv_freetype_outline_vector_t * dest, const FT_Vector * src)
{
dest->x = src ? src->x : 0;
dest->y = src ? src->y : 0;
}
static lv_result_t outline_send_event(lv_freetype_context_t * ctx, lv_event_code_t code,
lv_freetype_outline_event_param_t * param)
{
if(!ctx->event_cb) {
LV_LOG_ERROR("event_cb is not set");
return LV_RESULT_INVALID;
}
lv_event_t e;
lv_memzero(&e, sizeof(e));
e.code = code;
e.param = param;
e.user_data = NULL;
ctx->event_cb(&e);
return LV_RESULT_OK;
}
static lv_result_t outline_push_point(
lv_freetype_outline_t outline,
lv_freetype_outline_type_t type,
const FT_Vector * control1,
const FT_Vector * control2,
const FT_Vector * to)
{
LV_PROFILER_FONT_BEGIN;
lv_freetype_context_t * ctx = lv_freetype_get_context();
lv_freetype_outline_event_param_t param;
lv_memzero(&param, sizeof(param));
param.outline = outline;
param.type = type;
ft_vector_to_lv_vector(&param.control1, control1);
ft_vector_to_lv_vector(&param.control2, control2);
ft_vector_to_lv_vector(&param.to, to);
LV_PROFILER_FONT_END;
return outline_send_event(ctx, LV_EVENT_INSERT, &param);
}
static int outline_move_to_cb(
const FT_Vector * to,
void * user)
{
lv_freetype_outline_t outline = user;
outline_push_point(outline, LV_FREETYPE_OUTLINE_MOVE_TO, NULL, NULL, to);
return FT_Err_Ok;
}
static int outline_line_to_cb(
const FT_Vector * to,
void * user)
{
lv_freetype_outline_t outline = user;
outline_push_point(outline, LV_FREETYPE_OUTLINE_LINE_TO, NULL, NULL, to);
return FT_Err_Ok;
}
static int outline_conic_to_cb(
const FT_Vector * control,
const FT_Vector * to,
void * user)
{
lv_freetype_outline_t outline = user;
outline_push_point(outline, LV_FREETYPE_OUTLINE_CONIC_TO, control, NULL, to);
return FT_Err_Ok;
}
static int outline_cubic_to_cb(
const FT_Vector * control1,
const FT_Vector * control2,
const FT_Vector * to,
void * user)
{
lv_freetype_outline_t outline = user;
outline_push_point(outline, LV_FREETYPE_OUTLINE_CUBIC_TO, control1, control2, to);
return FT_Err_Ok;
}
static lv_freetype_outline_t outline_create(
lv_freetype_context_t * ctx,
FT_Face face,
FT_UInt glyph_index,
uint32_t size,
uint32_t strength,
uint32_t border_width)
{
LV_PROFILER_FONT_BEGIN;
LV_ASSERT_NULL(ctx);
FT_Error error;
FT_Glyph glyph;
FT_Stroker stroker;
error = FT_Set_Pixel_Sizes(face, 0, size);
if(error) {
FT_ERROR_MSG("FT_Set_Char_Size", error);
LV_PROFILER_FONT_END;
return NULL;
}
/**
* Disable AUTOHINT(https://freetype.org/autohinting/hinter.html) to avoid display clipping
* caused by inconsistent glyph measurement and outline.
*/
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_AUTOHINT);
if(error) {
FT_ERROR_MSG("FT_Load_Glyph", error);
LV_PROFILER_FONT_END;
return NULL;
}
if(strength > 0) {
error = FT_Outline_Embolden(&face->glyph->outline, FT_INT_TO_F26DOT6(strength));
if(error) {
FT_ERROR_MSG("FT_Outline_Embolden", error);
}
}
FT_Outline_Funcs outline_funcs = {
.move_to = outline_move_to_cb,
.line_to = outline_line_to_cb,
.conic_to = outline_conic_to_cb,
.cubic_to = outline_cubic_to_cb,
.shift = 0,
.delta = 0
};
lv_result_t res;
lv_freetype_outline_event_param_t param;
lv_memzero(&param, sizeof(param));
lv_freetype_outline_t outline;
res = outline_send_event(ctx, LV_EVENT_CREATE, &param);
outline = param.outline;
if(res != LV_RESULT_OK || !outline) {
LV_LOG_ERROR("Outline object create failed");
LV_PROFILER_FONT_END;
return NULL;
}
/* 1 iteration if there is no border */
/* 2 iterations if there is a a border and the glyph itsef */
for(int i = 0; i < (border_width > 0 ? 2 : 1); i++) {
FT_Outline glyph_outline;
if(i == 1) {
/* decompose the border glyph */
FT_Stroker_New(ctx->library, &stroker);
FT_Stroker_Set(stroker, border_width * 64,
FT_STROKER_LINECAP_ROUND,
FT_STROKER_LINEJOIN_ROUND,
0);
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_StrokeBorder(&glyph, stroker, 0, true);
FT_OutlineGlyph g = (FT_OutlineGlyph) glyph;
FT_Stroker_Done(stroker);
glyph_outline = g->outline;
}
else {
/* decompose glyph */
glyph_outline = face->glyph->outline;
}
/*Calculate Total Segments Before decompose */
int32_t tag_size = glyph_outline.n_points;
int32_t segments = 0;
int32_t vectors = 0;
for(int j = 0; j < tag_size; j++) {
#if 0
if(j == 0 && (glyph_outline.tags[j] & 0x1) == 0) {
/* TODO handle the case where the first point is 'off curve' */
https://stackoverflow.com/questions/3465809/how-to-interpret-a-freetype-glyph-outline-when-the-first-point-on-the-contour-is
}
#endif
if((glyph_outline.tags[j] & 0x1) == 0x1) {
segments++;
vectors++;
}
else {
int jj = j + 1 < tag_size ? j + 1 : 0;
if(glyph_outline.tags[jj] & 0x1) {
vectors++;
}
else {
segments++;
vectors += 2;
}
}
}
/*Also for every contour we may have a line for close*/
segments += glyph_outline.n_contours;
vectors += glyph_outline.n_contours;
param.sizes.data_size = vectors * 2;
param.sizes.segments_size = segments;
/* Run outline decompose again to fill outline data */
error = FT_Outline_Decompose(&glyph_outline, &outline_funcs, outline);
if(error) {
FT_ERROR_MSG("FT_Outline_Decompose", error);
outline_delete(ctx, outline);
LV_PROFILER_FONT_END;
return NULL;
}
if(i == 0 && border_width > 0) {
/* Close the border glyph before decomposing the inside glyph */
res = outline_push_point(outline, LV_FREETYPE_OUTLINE_BORDER_START, NULL, NULL, NULL);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Outline object close failed");
outline_delete(ctx, outline);
LV_PROFILER_FONT_END;
return NULL;
}
}
else if(i == 0 || (i == 1 && border_width > 0)) {
/* Close the border glyph or the regular glyph */
res = outline_push_point(outline, LV_FREETYPE_OUTLINE_END, NULL, NULL, NULL);
if(res != LV_RESULT_OK) {
LV_LOG_ERROR("Outline object close failed");
outline_delete(ctx, outline);
LV_PROFILER_FONT_END;
return NULL;
}
}
}
LV_PROFILER_FONT_END;
return outline;
}
static lv_result_t outline_delete(lv_freetype_context_t * ctx, lv_freetype_outline_t outline)
{
lv_freetype_outline_event_param_t param;
lv_memzero(&param, sizeof(param));
param.outline = outline;
return outline_send_event(ctx, LV_EVENT_DELETE, &param);
}
#endif /*LV_USE_FREETYPE*/
@@ -0,0 +1,161 @@
/**
* @file lv_freetype_private.h
*
*/
#ifndef LV_FREETYPE_PRIVATE_H
#define LV_FREETYPE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_freetype.h"
#if LV_USE_FREETYPE
#include "../../misc/cache/lv_cache.h"
#include "../../misc/lv_ll.h"
#include "../../font/lv_font.h"
#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_CACHE_H
#include FT_SIZES_H
#include FT_IMAGE_H
#include FT_OUTLINE_H
#include FT_STROKER_H
/*********************
* DEFINES
*********************/
#ifdef FT_CONFIG_OPTION_ERROR_STRINGS
#define FT_ERROR_MSG(msg, error_code) \
LV_LOG_ERROR(msg " error(0x%x): %s", (int)error_code, FT_Error_String(error_code))
#else
#define FT_ERROR_MSG(msg, error_code) \
LV_LOG_ERROR(msg " error(0x%x)", (int)error_code)
#endif
#define LV_FREETYPE_FONT_DSC_MAGIC_NUM 0x5F5F4654 /* '__FT' */
#define LV_FREETYPE_FONT_DSC_HAS_MAGIC_NUM(dsc) ((dsc)->magic_num == LV_FREETYPE_FONT_DSC_MAGIC_NUM)
#define LV_ASSERT_FREETYPE_FONT_DSC(dsc) \
do { \
LV_ASSERT_NULL(dsc); \
LV_ASSERT_FORMAT_MSG(LV_FREETYPE_FONT_DSC_HAS_MAGIC_NUM(dsc), \
"Invalid font descriptor: 0x%" LV_PRIx32, (dsc)->magic_num); \
} while (0)
#define FT_INT_TO_F26DOT6(x) ((x) << 6)
#define FT_F26DOT6_TO_INT(x) ((x) >> 6)
#define FT_INT_TO_F16DOT16(x) ((x) << 16)
#define FT_F16DOT16_TO_INT(x) ((x) >> 16)
/**********************
* TYPEDEFS
**********************/
struct _lv_freetype_outline_vector_t {
int32_t x;
int32_t y;
};
typedef struct {
int32_t segments_size;
int32_t data_size;
} lv_freetype_outline_sizes_t;
struct _lv_freetype_outline_event_param_t {
lv_freetype_outline_t outline;
lv_freetype_outline_type_t type;
lv_freetype_outline_vector_t to;
lv_freetype_outline_vector_t control1;
lv_freetype_outline_vector_t control2;
lv_freetype_outline_sizes_t sizes;
};
typedef struct _lv_freetype_cache_node_t lv_freetype_cache_node_t;
struct _lv_freetype_cache_node_t {
const char * pathname;
lv_freetype_font_style_t style;
lv_freetype_font_render_mode_t render_mode;
uint32_t ref_size; /**< Reference size for calculating outline glyph's real size.*/
FT_Face face;
lv_mutex_t face_lock;
bool face_has_kerning;
/*glyph cache*/
lv_cache_t * glyph_cache;
/*draw data cache*/
lv_cache_t * draw_data_cache;
};
typedef struct _lv_freetype_context_t {
FT_Library library;
lv_ll_t face_id_ll;
lv_event_cb_t event_cb;
uint32_t max_glyph_cnt;
lv_cache_t * cache_node_cache;
} lv_freetype_context_t;
typedef struct _lv_freetype_font_dsc_t {
uint32_t magic_num;
lv_font_t font;
uint32_t size;
lv_freetype_font_style_t style;
lv_freetype_font_render_mode_t render_mode;
lv_freetype_context_t * context;
lv_freetype_cache_node_t * cache_node;
lv_cache_entry_t * cache_node_entry;
FTC_FaceID face_id;
uint32_t outline_stroke_width;
lv_font_kerning_t kerning;
} lv_freetype_font_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Get the FreeType context.
*
* @return A pointer to the FreeType context used by LittlevGL.
*/
lv_freetype_context_t * lv_freetype_get_context(void);
void lv_freetype_italic_transform(FT_Face face);
int32_t lv_freetype_italic_transform_on_pos(lv_point_t point);
lv_cache_t * lv_freetype_create_glyph_cache(uint32_t cache_size);
void lv_freetype_set_cbs_glyph(lv_freetype_font_dsc_t * dsc);
lv_cache_t * lv_freetype_create_draw_data_image(uint32_t cache_size);
void lv_freetype_set_cbs_image_font(lv_freetype_font_dsc_t * dsc);
lv_cache_t * lv_freetype_create_draw_data_outline(uint32_t cache_size);
void lv_freetype_set_cbs_outline_font(lv_freetype_font_dsc_t * dsc);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FREETYPE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FREETYPE_PRIVATE_H*/
@@ -0,0 +1,291 @@
/**
* @file lv_ftsystem.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FREETYPE && LV_FREETYPE_USE_LVGL_PORT
#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/ftstream.h>
#include <freetype/ftsystem.h>
#include <freetype/fterrors.h>
#include <freetype/fttypes.h>
/*********************
* DEFINES
*********************/
/* The macro FT_COMPONENT is used in trace mode. It is an implicit
* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
* messages during execution.
*/
#undef FT_COMPONENT
#define FT_COMPONENT io
/* We use the macro STREAM_FILE for convenience to extract the */
/* system-specific stream handle from a given FreeType stream object */
#define STREAM_FILE( stream ) ( (lv_fs_file_t*)stream->descriptor.pointer )
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
FT_CALLBACK_DEF(unsigned long)
ft_lv_fs_stream_io(FT_Stream stream,
unsigned long offset,
unsigned char * buffer,
unsigned long count);
FT_CALLBACK_DEF(void)
ft_lv_fs_stream_close(FT_Stream stream);
FT_CALLBACK_DEF(void *)
ft_alloc(FT_Memory memory,
long size);
FT_CALLBACK_DEF(void *)
ft_realloc(FT_Memory memory,
long cur_size,
long new_size,
void * block);
FT_CALLBACK_DEF(void)
ft_free(FT_Memory memory,
void * block);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#ifdef FT_DEBUG_MEMORY
extern FT_Int
ft_mem_debug_init(FT_Memory memory);
extern void
ft_mem_debug_done(FT_Memory memory);
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
/* documentation is in ftstream.h */
FT_BASE_DEF(FT_Error)
FT_Stream_Open(FT_Stream stream,
const char * filepathname)
{
lv_fs_file_t file;
if(!stream)
return FT_THROW(Invalid_Stream_Handle);
stream->descriptor.pointer = NULL;
stream->pathname.pointer = (char *)filepathname;
stream->base = NULL;
stream->pos = 0;
stream->read = NULL;
stream->close = NULL;
lv_fs_res_t res = lv_fs_open(&file, filepathname, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
FT_ERROR(("FT_Stream_Open:"
" could not open `%s'\n", filepathname));
return FT_THROW(Cannot_Open_Resource);
}
lv_fs_seek(&file, 0, LV_FS_SEEK_END);
uint32_t pos;
res = lv_fs_tell(&file, &pos);
if(res != LV_FS_RES_OK) {
FT_ERROR(("FT_Stream_Open:"));
FT_ERROR((" opened `%s' but zero-sized\n", filepathname));
lv_fs_close(&file);
return FT_THROW(Cannot_Open_Stream);
}
stream->size = pos;
lv_fs_seek(&file, 0, LV_FS_SEEK_SET);
lv_fs_file_t * file_p = lv_malloc(sizeof(lv_fs_file_t));
LV_ASSERT_MALLOC(file_p);
if(!file_p) {
FT_ERROR(("FT_Stream_Open: malloc failed for file_p"));
lv_fs_close(&file);
return FT_THROW(Cannot_Open_Stream);
}
*file_p = file;
stream->descriptor.pointer = file_p;
stream->read = ft_lv_fs_stream_io;
stream->close = ft_lv_fs_stream_close;
FT_TRACE1(("FT_Stream_Open:"));
FT_TRACE1((" opened `%s' (%ld bytes) successfully\n",
filepathname, stream->size));
return FT_Err_Ok;
}
#endif /* !FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */
/* documentation is in ftobjs.h */
FT_BASE_DEF(FT_Memory)
FT_New_Memory(void)
{
FT_Memory memory;
memory = (FT_Memory)lv_malloc(sizeof(*memory));
if(memory) {
memory->user = NULL;
memory->alloc = ft_alloc;
memory->realloc = ft_realloc;
memory->free = ft_free;
#ifdef FT_DEBUG_MEMORY
ft_mem_debug_init(memory);
#endif
}
return memory;
}
/* documentation is in ftobjs.h */
FT_BASE_DEF(void)
FT_Done_Memory(FT_Memory memory)
{
#ifdef FT_DEBUG_MEMORY
ft_mem_debug_done(memory);
#endif
lv_free(memory);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* The memory allocation function.
* @param memory A pointer to the memory object.
* @param size The requested size in bytes.
* @return The address of newly allocated block.
*/
FT_CALLBACK_DEF(void *)
ft_alloc(FT_Memory memory,
long size)
{
FT_UNUSED(memory);
return lv_malloc((size_t)size);
}
/**
* The memory reallocation function.
* @param memory A pointer to the memory object.
* @param cur_size The current size of the allocated memory block.
* @param new_size The newly requested size in bytes.
* @param block The current address of the block in memory.
* @return The address of the reallocated memory block.
*/
FT_CALLBACK_DEF(void *)
ft_realloc(FT_Memory memory,
long cur_size,
long new_size,
void * block)
{
FT_UNUSED(memory);
FT_UNUSED(cur_size);
return lv_realloc(block, (size_t)new_size);
}
/**
* The memory release function.
* @param memory A pointer to the memory object.
* @param block The address of block in memory to be freed.
*/
FT_CALLBACK_DEF(void)
ft_free(FT_Memory memory,
void * block)
{
FT_UNUSED(memory);
lv_free(block);
}
#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
/**
* The function to close a stream.
* @param stream A pointer to the stream object.
*/
FT_CALLBACK_DEF(void)
ft_lv_fs_stream_close(FT_Stream stream)
{
lv_fs_file_t * file_p = STREAM_FILE(stream);
lv_fs_close(file_p);
lv_free(file_p);
stream->descriptor.pointer = NULL;
stream->size = 0;
stream->base = NULL;
}
/**
* The function to open a stream.
* @param stream A pointer to the stream object.
* @param offset The position in the data stream to start reading.
* @param buffer The address of buffer to store the read data.
* @param count The number of bytes to read from the stream.
* @return The number of bytes actually read. If `count' is zero (this is,
* the function is used for seeking), a non-zero return value
* indicates an error.
*/
FT_CALLBACK_DEF(unsigned long)
ft_lv_fs_stream_io(FT_Stream stream,
unsigned long offset,
unsigned char * buffer,
unsigned long count)
{
lv_fs_file_t * file_p;
if(!count && offset > stream->size)
return 1;
file_p = STREAM_FILE(stream);
if(stream->pos != offset)
lv_fs_seek(file_p, (long)offset, LV_FS_SEEK_SET);
if(count == 0)
return 0;
uint32_t br;
lv_fs_res_t res = lv_fs_read(file_p, buffer, count, &br);
return res == LV_FS_RES_OK ? br : 0;
}
#endif /* !FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */
#endif/*LV_FREETYPE_USE_LV_FTSYSTEM*/
@@ -0,0 +1,194 @@
#include "../../../lvgl.h"
#if LV_USE_FS_ARDUINO_ESP_LITTLEFS
#include "../../core/lv_global.h"
#include "LittleFS.h"
#if !LV_FS_IS_VALID_LETTER(LV_FS_ARDUINO_ESP_LITTLEFS_LETTER)
#error "Invalid drive letter"
#endif
typedef struct ArduinoEspLittleFile {
File file;
} ArduinoEspLittleFile;
/**********************
* STATIC PROTOTYPES
**********************/
static void fs_init(void);
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
/**
* Register a driver for the LittleFS File System interface
*/
extern "C" void lv_fs_arduino_esp_littlefs_init(void)
{
fs_init();
lv_fs_drv_t * fs_drv = &(LV_GLOBAL_DEFAULT()->arduino_esp_littlefs_fs_drv);
lv_fs_drv_init(fs_drv);
fs_drv->letter = LV_FS_ARDUINO_ESP_LITTLEFS_LETTER;
fs_drv->open_cb = fs_open;
fs_drv->close_cb = fs_close;
fs_drv->read_cb = fs_read;
fs_drv->write_cb = fs_write;
fs_drv->seek_cb = fs_seek;
fs_drv->tell_cb = fs_tell;
fs_drv->dir_close_cb = NULL;
fs_drv->dir_open_cb = NULL;
fs_drv->dir_read_cb = NULL;
lv_fs_drv_register(fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your Storage device and File system.*/
static void fs_init(void)
{
LittleFS.begin();
}
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 a file descriptor or NULL on error
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
const char * flags;
if(mode == LV_FS_MODE_WR)
flags = FILE_WRITE;
else if(mode == LV_FS_MODE_RD)
flags = FILE_READ;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
flags = FILE_WRITE;
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_ARDUINO_ESP_LITTLEFS_PATH "%s", path);
File file = LittleFS.open(buf, flags);
if(!file) {
return NULL;
}
ArduinoEspLittleFile * lf = new ArduinoEspLittleFile{file};
return (void *)lf;
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
ArduinoEspLittleFile * lf = (ArduinoEspLittleFile *)file_p;
lf->file.close();
delete lf;
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
ArduinoEspLittleFile * lf = (ArduinoEspLittleFile *)file_p;
*br = lf->file.read((uint8_t *)buf, btr);
return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw the number of real written bytes (Bytes Written)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
ArduinoEspLittleFile * lf = (ArduinoEspLittleFile *)file_p;
*bw = lf->file.write((uint8_t *)buf, btw);
return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @param whence tells from where to interpret the `pos`. See @lv_fs_whence_t
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
SeekMode mode;
if(whence == LV_FS_SEEK_SET)
mode = SeekSet;
else if(whence == LV_FS_SEEK_CUR)
mode = SeekCur;
else if(whence == LV_FS_SEEK_END)
mode = SeekEnd;
ArduinoEspLittleFile * lf = (ArduinoEspLittleFile *)file_p;
int rc = lf->file.seek(pos, mode);
return rc < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_p variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
ArduinoEspLittleFile * lf = (ArduinoEspLittleFile *)file_p;
*pos_p = lf->file.position();
return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
#else /*LV_USE_FS_ARDUINO_ESP_LITTLEFS == 0*/
#if defined(LV_FS_ARDUINO_ESP_LITTLEFS_LETTER) && LV_FS_ARDUINO_ESP_LITTLEFS_LETTER != '\0'
#warning "LV_USE_FS_ARDUINO_ESP_LITTLEFS is not enabled but LV_FS_ARDUINO_ESP_LITTLEFS_LETTER is set"
#endif
#endif /*LV_USE_FS_ARDUINO_ESP_LITTLEFS*/
@@ -0,0 +1,186 @@
#include "../../../lvgl.h"
#if LV_USE_FS_ARDUINO_SD
#include "../../core/lv_global.h"
#include <SPI.h>
#include "SD.h"
#if !LV_FS_IS_VALID_LETTER(LV_FS_ARDUINO_SD_LETTER)
#error "Invalid drive letter"
#endif
typedef struct SdFile {
File file;
} SdFile;
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
/**
* Register a driver for the SD File System interface
*/
extern "C" void lv_fs_arduino_sd_init(void)
{
lv_fs_drv_t * fs_drv = &(LV_GLOBAL_DEFAULT()->arduino_sd_fs_drv);
lv_fs_drv_init(fs_drv);
fs_drv->letter = LV_FS_ARDUINO_SD_LETTER;
fs_drv->open_cb = fs_open;
fs_drv->close_cb = fs_close;
fs_drv->read_cb = fs_read;
fs_drv->write_cb = fs_write;
fs_drv->seek_cb = fs_seek;
fs_drv->tell_cb = fs_tell;
fs_drv->dir_close_cb = NULL;
fs_drv->dir_open_cb = NULL;
fs_drv->dir_read_cb = NULL;
lv_fs_drv_register(fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 a file descriptor or NULL on error
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
const char * flags;
if(mode == LV_FS_MODE_WR)
flags = FILE_WRITE;
else if(mode == LV_FS_MODE_RD)
flags = FILE_READ;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
flags = FILE_WRITE;
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_ARDUINO_SD_PATH "%s", path);
File file = SD.open(buf, flags);
if(!file) {
return NULL;
}
SdFile * lf = new SdFile{file};
return (void *)lf;
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
SdFile * lf = (SdFile *)file_p;
lf->file.close();
delete lf;
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
SdFile * lf = (SdFile *)file_p;
*br = lf->file.read((uint8_t *)buf, btr);
return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw the number of real written bytes (Bytes Written)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
SdFile * lf = (SdFile *)file_p;
*bw = lf->file.write((uint8_t *)buf, btw);
return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @param whence tells from where to interpret the `pos`. See @lv_fs_whence_t
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
SeekMode mode;
if(whence == LV_FS_SEEK_SET)
mode = SeekSet;
else if(whence == LV_FS_SEEK_CUR)
mode = SeekCur;
else if(whence == LV_FS_SEEK_END)
mode = SeekEnd;
SdFile * lf = (SdFile *)file_p;
int rc = lf->file.seek(pos, mode);
return rc < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_p variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
SdFile * lf = (SdFile *)file_p;
*pos_p = lf->file.position();
return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
#else /*LV_USE_FS_ARDUINO_SD == 0*/
#if defined(LV_FS_ARDUINO_SD_LETTER) && LV_FS_ARDUINO_SD_LETTER != '\0'
#warning "LV_USE_FS_ARDUINO_SD is not enabled but LV_FS_ARDUINO_SD_LETTER is set"
#endif
#endif /*LV_USE_FS_ARDUINO_SD*/
@@ -0,0 +1,10 @@
// this file should not exist
#ifdef __GNUC__
#define IS_NOT_USED __attribute__ ((unused))
#else
#define IS_NOT_USED
#endif
IS_NOT_USED static void nothing(void)
{
// do nothing
}
@@ -0,0 +1,303 @@
/**
* @file lv_fs_fatfs.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_FATFS
#include "ff.h"
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#ifdef ESP_PLATFORM
#define DIR FF_DIR /* ESP IDF typedefs `DIR` as `FF_DIR` in its version of ff.h. Use `FF_DIR` in LVGL too */
#endif
#if !LV_FS_IS_VALID_LETTER(LV_FS_FATFS_LETTER)
#error "Invalid drive letter"
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void fs_init(void);
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_fs_fatfs_init(void)
{
/*----------------------------------------------------
* Initialize your storage device and File System
* -------------------------------------------------*/
fs_init();
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->fatfs_fs_drv);
lv_fs_drv_init(fs_drv_p);
/*Set up fields...*/
fs_drv_p->letter = LV_FS_FATFS_LETTER;
fs_drv_p->cache_size = LV_FS_FATFS_CACHE_SIZE;
fs_drv_p->open_cb = fs_open;
fs_drv_p->close_cb = fs_close;
fs_drv_p->read_cb = fs_read;
fs_drv_p->write_cb = fs_write;
fs_drv_p->seek_cb = fs_seek;
fs_drv_p->tell_cb = fs_tell;
fs_drv_p->dir_close_cb = fs_dir_close;
fs_drv_p->dir_open_cb = fs_dir_open;
fs_drv_p->dir_read_cb = fs_dir_read;
lv_fs_drv_register(fs_drv_p);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your Storage device and File system.*/
static void fs_init(void)
{
/*Initialize the SD card and FatFS itself.
*Better to do it in your code to keep this library untouched for easy updating*/
}
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
uint8_t flags = 0;
if(mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS;
else if(mode == LV_FS_MODE_RD) flags = FA_READ;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
FIL * f = lv_malloc(sizeof(FIL));
if(f == NULL) return NULL;
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_FATFS_PATH "%s", path);
FRESULT res = f_open(f, buf, flags);
if(res == FR_OK) {
return f;
}
else {
lv_free(f);
return NULL;
}
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
f_close(file_p);
lv_free(file_p);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
FRESULT res = f_read(file_p, buf, btr, (UINT *)br);
if(res == FR_OK) return LV_FS_RES_OK;
else return LV_FS_RES_UNKNOWN;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw 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
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
FRESULT res = f_write(file_p, buf, btw, (UINT *)bw);
if(res == FR_OK) return LV_FS_RES_OK;
else return LV_FS_RES_UNKNOWN;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @param whence only LV_SEEK_SET is supported
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
switch(whence) {
case LV_FS_SEEK_SET:
f_lseek(file_p, pos);
break;
case LV_FS_SEEK_CUR:
f_lseek(file_p, f_tell((FIL *)file_p) + pos);
break;
case LV_FS_SEEK_END:
f_lseek(file_p, f_size((FIL *)file_p) + pos);
break;
default:
break;
}
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
*pos_p = f_tell((FIL *)file_p);
return LV_FS_RES_OK;
}
/**
* Initialize a 'DIR' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
DIR * d = lv_malloc(sizeof(DIR));
if(d == NULL) return NULL;
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_FATFS_PATH "%s", path);
FRESULT res = f_opendir(d, buf);
if(res != FR_OK) {
lv_free(d);
d = NULL;
}
return d;
}
/**
* Read the next filename from a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' variable
* @param fn pointer to a buffer to store the filename
* @param fn_len length of the buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
{
LV_UNUSED(drv);
if(fn_len == 0) return LV_FS_RES_INV_PARAM;
FRESULT res;
FILINFO fno;
fn[0] = '\0';
do {
res = f_readdir(dir_p, &fno);
if(res != FR_OK) return LV_FS_RES_UNKNOWN;
if(fno.fname[0] == 0) break; /* End of the directory */
if(fno.fattrib & AM_DIR) {
lv_snprintf(fn, fn_len, "/%s", fno.fname);
}
else lv_strlcpy(fn, fno.fname, fn_len);
} while(lv_strcmp(fn, "/.") == 0 || lv_strcmp(fn, "/..") == 0);
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
f_closedir(dir_p);
lv_free(dir_p);
return LV_FS_RES_OK;
}
#else /*LV_USE_FS_FATFS == 0*/
#if defined(LV_FS_FATFS_LETTER) && LV_FS_FATFS_LETTER != '\0'
#warning "LV_USE_FS_FATFS is not enabled but LV_FS_FATFS_LETTER is set"
#endif
#endif /*LV_USE_FS_FATFS*/
@@ -0,0 +1,283 @@
#include "../../../lvgl.h"
#if LV_USE_FS_LITTLEFS
#include "lfs.h"
#include "../../core/lv_global.h"
#if !LV_FS_IS_VALID_LETTER(LV_FS_LITTLEFS_LETTER)
#error "Invalid drive letter"
#endif
typedef struct LittleFile {
lfs_file_t file;
} LittleFile;
typedef struct LittleDirectory {
lfs_dir_t dir;
} LittleDirectory;
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
void lv_littlefs_set_handler(lfs_t * lfs)
{
lv_fs_drv_t * drv = lv_fs_get_drv(LV_FS_LITTLEFS_LETTER);
drv->user_data = lfs;
}
/**
* Register a driver for the LittleFS File System interface
*/
void lv_fs_littlefs_init(void)
{
lv_fs_drv_t * fs_drv = &(LV_GLOBAL_DEFAULT()->littlefs_fs_drv);
lv_fs_drv_init(fs_drv);
fs_drv->letter = LV_FS_LITTLEFS_LETTER;
fs_drv->open_cb = fs_open;
fs_drv->close_cb = fs_close;
fs_drv->read_cb = fs_read;
fs_drv->write_cb = fs_write;
fs_drv->seek_cb = fs_seek;
fs_drv->tell_cb = fs_tell;
fs_drv->dir_open_cb = fs_dir_open;
fs_drv->dir_close_cb = fs_dir_close;
fs_drv->dir_read_cb = fs_dir_read;
lv_fs_drv_register(fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 a file descriptor or NULL on error
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
int flags = 0;
if(mode == LV_FS_MODE_WR)
flags = LFS_O_WRONLY;
else if(mode == LV_FS_MODE_RD)
flags = LFS_O_RDONLY;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD))
flags = LFS_O_RDWR;
LittleFile * lf = lv_malloc(sizeof(LittleFile));
LV_ASSERT_MALLOC(lf);
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_LITTLEFS_PATH "%s", path);
lfs_t * lfs = drv->user_data;
int err = lfs_file_open(lfs, &lf->file, buf, flags);
if(err) {
return NULL;
}
return (void *)lf;
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LittleFile * lf = file_p;
lfs_t * lfs = drv->user_data;
lfs_file_close(lfs, &lf->file);
lv_free(lf);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LittleFile * lf = file_p;
lfs_t * lfs = drv->user_data;
*br = lfs_file_read(lfs, &lf->file, (uint8_t *)buf, btr);
return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw the number of real written bytes (Bytes Written)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LittleFile * lf = file_p;
lfs_t * lfs = drv->user_data;
*bw = lfs_file_write(lfs, &lf->file, (uint8_t *)buf, btw);
return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_t variable. (opened with fs_open)
* @param pos the new position of read write pointer
* @param whence tells from where to interpret the `pos`. See @lv_fs_whence_t
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
int mode = 0;
if(whence == LV_FS_SEEK_SET)
mode = LFS_SEEK_SET;
else if(whence == LV_FS_SEEK_CUR)
mode = LFS_SEEK_CUR;
else if(whence == LV_FS_SEEK_END)
mode = LFS_SEEK_END;
LittleFile * lf = file_p;
lfs_t * lfs = drv->user_data;
int rc = lfs_file_seek(lfs, &lf->file, pos, mode);
return rc < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a file_p variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LittleFile * lf = file_p;
lfs_t * lfs = drv->user_data;
*pos_p = lfs_file_tell(lfs, &lf->file);
return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Open a directory
* @param drv pointer to a driver where this function belongs
* @param path path to the directory beginning with the driver letter (e.g. S:/folder)
* @return a directory descriptor or NULL on error
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LittleDirectory * ld = lv_malloc(sizeof(LittleDirectory));
LV_ASSERT_MALLOC(ld);
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_LITTLEFS_PATH "%s", path);
lfs_t * lfs = drv->user_data;
int err = lfs_dir_open(lfs, &ld->dir, buf);
if(err != LFS_ERR_OK) {
lv_free(ld);
return NULL;
}
return (void *)ld;
}
/**
* Close an opened directory
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to a dir_p variable. (opened with fs_dir_open)
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LittleDirectory * ld = dir_p;
lfs_t * lfs = drv->user_data;
int rc = lfs_dir_close(lfs, &ld->dir);
if(rc < 0) return LV_FS_RES_UNKNOWN;
lv_free(ld);
return LV_FS_RES_OK;
}
/**
* Read data from an opened directory
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to a file_t variable.
* @param fn pointer to a buffer to store the filename
* @param fn_len length of the buffer to store the filename
* @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
{
if(fn_len == 0) return LV_FS_RES_INV_PARAM;
LittleDirectory * lf = dir_p;
lfs_t * lfs = drv->user_data;
fn[0] = '\0';
do {
struct lfs_info info;
int res = lfs_dir_read(lfs, &lf->dir, &info);
if(res < 0) return LV_FS_RES_UNKNOWN;
if(res == 0) { /* End of the directory */
fn[0] = '\0';
break;
}
if(info.type != LFS_TYPE_DIR) {
lv_strlcpy(fn, info.name, fn_len);
}
else {
lv_snprintf(fn, fn_len, "/%s", info.name);
}
} while(lv_strcmp(fn, "/.") == 0 || lv_strcmp(fn, "/..") == 0);
return LV_FS_RES_OK;
}
#else /*LV_USE_FS_LITTLEFS == 0*/
#if defined(LV_FS_LITTLEFS_LETTER) && LV_FS_LITTLEFS_LETTER != '\0'
#warning "LV_USE_FS_LITTLEFS is not enabled but LV_FS_LITTLEFS_LETTER is set"
#endif
#endif /*LV_USE_FS_LITTLEFS*/
@@ -0,0 +1,219 @@
/**
* @file lv_fs_memfs.c
*
* File System Interface driver for memory-mapped files
*
* This driver allows using a memory area as a file that can be read by normal file operations. It can
* be used, e.g., to store font files in slow flash memory, and load them into RAM on demand.
*
* You can enable it in lv_conf.h:
*
* #define LV_USE_FS_MEMFS 1
*
* The actual implementation uses the built-in cache mechanism of the file system interface.
*
* Since this is not an actual file system, file write and directories are not supported.
*
* The default drive letter is 'M', but this can be changed in lv_conf.h:
*
* #define LV_FS_MEMFS_LETTER 'M'
*
* To use it seamlessly with the file system interface a new extended path object has been introduced:
*
* lv_fs_path_ex_t mempath;
*
* This structure can be initialized with the helper function:
*
* lv_fs_make_path_ex(&mempath, (const uint8_t *) & my_mem_buffer, sizeof(my_mem_buffer));
*
* Then the "file" can be opened with:
*
* lv_fs_file_t file;
* lv_fs_res_t res = lv_fs_open(&file, (const char *) & mempath, LV_FS_MODE_RD);
*
* The path object can be used at any place where a file path is required, e.g.:
*
* lv_font_t* my_font = lv_binfont_create((const char *) & mempath);
*
*/
/*********************
* INCLUDES
*********************/
#include "../../misc/lv_fs_private.h"
#include "../../../lvgl.h"
#if LV_USE_FS_MEMFS
/*********************
* DEFINES
*********************/
#if !LV_FS_IS_VALID_LETTER(LV_FS_MEMFS_LETTER)
#error "Invalid drive letter"
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
/**********************
* STATIC VARIABLES
**********************/
static lv_fs_drv_t fs_drv; /*A driver descriptor*/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_memfs_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_FS_MEMFS_LETTER;
fs_drv.cache_size = LV_FS_CACHE_FROM_BUFFER;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = NULL;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_close_cb = NULL;
fs_drv.dir_open_cb = NULL;
fs_drv.dir_read_cb = NULL;
lv_fs_drv_register(&fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @param path pointer to an extended path object containing the memory buffer address and size
* @param mode read: FS_MODE_RD (currently only reading from the buffer is supported)
* @return pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
LV_UNUSED(mode);
return (void *)path;
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
LV_UNUSED(file_p);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
LV_UNUSED(file_p);
LV_UNUSED(buf);
LV_UNUSED(btr);
*br = 0;
return LV_FS_RES_OK;
}
/**
* Set the read pointer.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open )
* @param pos the new position of read pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
/* NOTE: this function is only called to determine the end of the buffer when LV_FS_SEEK_END was given to lv_fs_seek() */
LV_UNUSED(drv);
lv_fs_file_t * fp = (lv_fs_file_t *)file_p;
switch(whence) {
case LV_FS_SEEK_SET: {
fp->cache->file_position = pos;
break;
}
case LV_FS_SEEK_CUR: {
fp->cache->file_position += pos;
break;
}
case LV_FS_SEEK_END: {
fp->cache->file_position = fp->cache->end - pos;
break;
}
}
if(fp->cache->file_position < fp->cache->start)
fp->cache->file_position = fp->cache->start;
else if(fp->cache->file_position > fp->cache->end)
fp->cache->file_position = fp->cache->end;
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
*pos_p = ((lv_fs_file_t *)file_p)->cache->file_position;
return LV_FS_RES_OK;
}
#else /*LV_USE_FS_MEMFS == 0*/
#if defined(LV_FS_MEMFS_LETTER) && LV_FS_MEMFS_LETTER != '\0'
#warning "LV_USE_FS_MEMFS is not enabled but LV_FS_MEMFS_LETTER is set"
#endif
#endif /*LV_USE_FS_MEMFS*/
@@ -0,0 +1,385 @@
/**
* @file lv_fs_posix.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_POSIX
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#if !LV_FS_IS_VALID_LETTER(LV_FS_POSIX_LETTER)
#error "Invalid drive letter"
#endif
/** The reason for 'fd + 1' is because open() may return a legal fd with a value of 0,
* preventing it from being judged as NULL when converted to a pointer type.
*/
#define FILEP2FD(file_p) ((lv_uintptr_t)file_p - 1)
#define FD2FILEP(fd) ((void *)(lv_uintptr_t)(fd + 1))
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
static lv_fs_res_t fs_errno_to_res(int errno_val);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_posix_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->posix_fs_drv);
lv_fs_drv_init(fs_drv_p);
/*Set up fields...*/
fs_drv_p->letter = LV_FS_POSIX_LETTER;
fs_drv_p->cache_size = LV_FS_POSIX_CACHE_SIZE;
fs_drv_p->open_cb = fs_open;
fs_drv_p->close_cb = fs_close;
fs_drv_p->read_cb = fs_read;
fs_drv_p->write_cb = fs_write;
fs_drv_p->seek_cb = fs_seek;
fs_drv_p->tell_cb = fs_tell;
fs_drv_p->dir_close_cb = fs_dir_close;
fs_drv_p->dir_open_cb = fs_dir_open;
fs_drv_p->dir_read_cb = fs_dir_read;
lv_fs_drv_register(fs_drv_p);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 a file handle or -1 in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
int flags = 0;
if(mode == LV_FS_MODE_WR) flags = O_WRONLY | O_CREAT;
else if(mode == LV_FS_MODE_RD) flags = O_RDONLY;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = O_RDWR | O_CREAT;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path);
int fd = open(buf, flags, 0666);
if(fd < 0) {
LV_LOG_WARN("Could not open file: %s, flags: 0x%x, errno: %d", buf, flags, errno);
return NULL;
}
return FD2FILEP(fd);
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
int fd = FILEP2FD(file_p);
int ret = close(fd);
if(ret < 0) {
LV_LOG_WARN("Could not close file: %d, errno: %d", fd, errno);
return fs_errno_to_res(errno);
}
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
int fd = FILEP2FD(file_p);
ssize_t ret = read(fd, buf, btr);
if(ret < 0) {
LV_LOG_WARN("Could not read file: %d, errno: %d", fd, errno);
return fs_errno_to_res(errno);
}
*br = (uint32_t)ret;
return LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw 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
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
int fd = FILEP2FD(file_p);
ssize_t ret = write(fd, buf, btw);
if(ret < 0) {
LV_LOG_WARN("Could not write file: %d, errno: %d", fd, errno);
return fs_errno_to_res(errno);
}
*bw = (uint32_t)ret;
return LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
int w;
switch(whence) {
case LV_FS_SEEK_SET:
w = SEEK_SET;
break;
case LV_FS_SEEK_CUR:
w = SEEK_CUR;
break;
case LV_FS_SEEK_END:
w = SEEK_END;
break;
default:
return LV_FS_RES_INV_PARAM;
}
int fd = FILEP2FD(file_p);
off_t offset = lseek(fd, pos, w);
if(offset < 0) {
LV_LOG_WARN("Could not seek file: %d, errno: %d", fd, errno);
return fs_errno_to_res(errno);
}
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
int fd = FILEP2FD(file_p);
off_t offset = lseek(fd, 0, SEEK_CUR);
if(offset < 0) {
LV_LOG_WARN("Could not get position of file: %d, errno: %d", fd, errno);
return fs_errno_to_res(errno);
}
*pos_p = (uint32_t)offset;
return LV_FS_RES_OK;
}
/**
* Initialize a 'fs_read_dir_t' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' or 'HANDLE' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path);
void * dir = opendir(buf);
if(!dir) {
LV_LOG_WARN("Could not open directory: %s, errno: %d", buf, errno);
return NULL;
}
return dir;
}
/**
* Read the next filename from a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @param fn pointer to a buffer to store the filename
* @param fn_len length of the buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
{
LV_UNUSED(drv);
if(fn_len == 0) return LV_FS_RES_INV_PARAM;
struct dirent * entry;
do {
entry = readdir(dir_p);
if(entry) {
if(entry->d_type == DT_DIR) lv_snprintf(fn, fn_len, "/%s", entry->d_name);
else lv_strlcpy(fn, entry->d_name, fn_len);
}
else {
lv_strlcpy(fn, "", fn_len);
}
} while(lv_strcmp(fn, "/.") == 0 || lv_strcmp(fn, "/..") == 0);
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
int ret = closedir(dir_p);
if(ret < 0) {
LV_LOG_WARN("Could not close directory: errno: %d", errno);
return fs_errno_to_res(errno);
}
return LV_FS_RES_OK;
}
/**
* Convert an errno value to a lv_fs_res_t value
* @param errno_val an errno value
* @return a corresponding lv_fs_res_t value
*/
static lv_fs_res_t fs_errno_to_res(int errno_val)
{
switch(errno_val) {
case 0:
return LV_FS_RES_OK;
case EIO: /* I/O error */
return LV_FS_RES_HW_ERR;
case EFAULT: /* Bad address */
return LV_FS_RES_FS_ERR;
case ENOENT: /* No such file or directory */
return LV_FS_RES_NOT_EX;
case ENOSPC: /* No space left on device */
return LV_FS_RES_FULL;
case EALREADY: /* Operation already in progress */
return LV_FS_RES_LOCKED;
case EACCES: /* Permission denied */
return LV_FS_RES_DENIED;
case EBUSY: /* Device or resource busy */
return LV_FS_RES_BUSY;
case ETIMEDOUT: /* Connection timed out */
return LV_FS_RES_TOUT;
case ENOSYS: /* Invalid system call number */
return LV_FS_RES_NOT_IMP;
case ENOMEM: /* Out of memory */
return LV_FS_RES_OUT_OF_MEM;
case EINVAL: /* "Invalid argument" */
return LV_FS_RES_INV_PARAM;
default:
break;
}
return LV_FS_RES_UNKNOWN;
}
#else /*LV_USE_FS_POSIX == 0*/
#if defined(LV_FS_POSIX_LETTER) && LV_FS_POSIX_LETTER != '\0'
#warning "LV_USE_FS_POSIX is not enabled but LV_FS_POSIX_LETTER is set"
#endif
#endif /*LV_USE_FS_POSIX*/
@@ -0,0 +1,349 @@
/**
* @file lv_fs_stdio.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_STDIO
#include <stdio.h>
#ifndef WIN32
#include <dirent.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#if !LV_FS_IS_VALID_LETTER(LV_FS_STDIO_LETTER)
#error "Invalid drive letter"
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct {
#ifdef _WIN32
HANDLE dir_p;
char next_fn[LV_FS_MAX_PATH_LEN];
#else
DIR * dir_p;
#endif
} dir_handle_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_stdio_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->stdio_fs_drv);
lv_fs_drv_init(fs_drv_p);
/*Set up fields...*/
fs_drv_p->letter = LV_FS_STDIO_LETTER;
fs_drv_p->cache_size = LV_FS_STDIO_CACHE_SIZE;
fs_drv_p->open_cb = fs_open;
fs_drv_p->close_cb = fs_close;
fs_drv_p->read_cb = fs_read;
fs_drv_p->write_cb = fs_write;
fs_drv_p->seek_cb = fs_seek;
fs_drv_p->tell_cb = fs_tell;
fs_drv_p->dir_close_cb = fs_dir_close;
fs_drv_p->dir_open_cb = fs_dir_open;
fs_drv_p->dir_read_cb = fs_dir_read;
lv_fs_drv_register(fs_drv_p);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
const char * flags = "";
if(mode == LV_FS_MODE_WR) flags = "wb";
else if(mode == LV_FS_MODE_RD) flags = "rb";
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+";
/*Make the path relative to the current directory (the projects root folder)*/
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
return fopen(buf, flags);
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
fclose(file_p);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
*br = fread(buf, 1, btr, file_p);
return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw 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
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
*bw = fwrite(buf, 1, btw, file_p);
return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
int w;
switch(whence) {
case LV_FS_SEEK_SET:
w = SEEK_SET;
break;
case LV_FS_SEEK_CUR:
w = SEEK_CUR;
break;
case LV_FS_SEEK_END:
w = SEEK_END;
break;
default:
return LV_FS_RES_INV_PARAM;
}
fseek(file_p, pos, w);
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
*pos_p = ftell(file_p);
return LV_FS_RES_OK;
}
/**
* Initialize a 'DIR' or 'HANDLE' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' or 'HANDLE' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
dir_handle_t * handle = (dir_handle_t *)lv_malloc(sizeof(dir_handle_t));
#ifndef WIN32
/*Make the path relative to the current directory (the projects root folder)*/
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
handle->dir_p = opendir(buf);
if(handle->dir_p == NULL) {
lv_free(handle);
return NULL;
}
return handle;
#else
handle->dir_p = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s\\*", path);
lv_strcpy(handle->next_fn, "");
handle->dir_p = FindFirstFileA(buf, &fdata);
do {
if(lv_strcmp(fdata.cFileName, ".") == 0 || lv_strcmp(fdata.cFileName, "..") == 0) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(handle->dir_p, &fdata));
if(handle->dir_p == INVALID_HANDLE_VALUE) {
lv_free(handle);
return INVALID_HANDLE_VALUE;
}
return handle;
#endif
}
/**
* Read the next filename form a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @param fn pointer to a buffer to store the filename
* @param fn_len length of the buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
{
LV_UNUSED(drv);
if(fn_len == 0) return LV_FS_RES_INV_PARAM;
dir_handle_t * handle = (dir_handle_t *)dir_p;
#ifndef WIN32
struct dirent * entry;
do {
entry = readdir(handle->dir_p);
if(entry) {
/*Note, DT_DIR is not defined in C99*/
if(entry->d_type == DT_DIR) lv_snprintf(fn, fn_len, "/%s", entry->d_name);
else lv_strlcpy(fn, entry->d_name, fn_len);
}
else {
lv_strlcpy(fn, "", fn_len);
}
} while(lv_strcmp(fn, "/.") == 0 || lv_strcmp(fn, "/..") == 0);
#else
lv_strlcpy(fn, handle->next_fn, fn_len);
lv_strcpy(handle->next_fn, "");
WIN32_FIND_DATAA fdata;
if(FindNextFileA(handle->dir_p, &fdata) == false) return LV_FS_RES_OK;
do {
if(lv_strcmp(fdata.cFileName, ".") == 0 || lv_strcmp(fdata.cFileName, "..") == 0) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(handle->dir_p, &fdata));
#endif
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
dir_handle_t * handle = (dir_handle_t *)dir_p;
#ifndef WIN32
closedir(handle->dir_p);
#else
FindClose(handle->dir_p);
#endif
lv_free(handle);
return LV_FS_RES_OK;
}
#else /*LV_USE_FS_STDIO == 0*/
#if defined(LV_FS_STDIO_LETTER) && LV_FS_STDIO_LETTER != '\0'
#warning "LV_USE_FS_STDIO is not enabled but LV_FS_STDIO_LETTER is set"
#endif
#endif /*LV_USE_FS_POSIX*/
@@ -0,0 +1,607 @@
/**
* @file lv_fs_uefi.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_UEFI && LV_USE_UEFI
#include "../../drivers/uefi/lv_uefi_private.h"
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#if LV_FS_UEFI_LETTER == '\0'
#error "LV_FS_UEFI_LETTER must be set to a valid value"
#else
#if (LV_FS_UEFI_LETTER < 'A') || (LV_FS_UEFI_LETTER > 'Z')
#if LV_FS_DEFAULT_DRIVE_LETTER != '\0' /* When using default driver-identifier letter, strict format (X:) is mandatory. */
#error "LV_FS_UEFI_LETTER must be an upper case ASCII letter"
#else /*Lean rules for backward compatibility*/
#warning LV_FS_UEFI_LETTER should be an upper case ASCII letter. \
Using a slash symbol as driver - identifier letter should be replaced with LV_FS_DEFAULT_DRIVE_LETTER mechanism.
#endif
#endif
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_uefi_fs_file_context_t {
EFI_FILE_PROTOCOL * interface;
} lv_uefi_fs_file_context_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_fs_drv_uefi_init(lv_fs_drv_t * drv, char fs_drive_letter, EFI_HANDLE fs_handle);
static void lv_fs_drv_uefi_deinit(lv_fs_drv_t * drv);
static void lv_fs_uefi_lvgl_path_to_uefi_path(CHAR16 * path);
static void lv_fs_uefi_uefi_path_to_lvgl_path(CHAR16 * path);
static bool lv_fs_uefi_is_dot_path(CONST CHAR16 * path);
static bool lv_fs_uefi_is_dir(EFI_FILE_PROTOCOL * dir);
static bool lv_fs_uefi_is_file(EFI_FILE_PROTOCOL * file);
static EFI_FILE_INFO * lv_fs_uefi_get_info(EFI_FILE_PROTOCOL * file);
static bool lv_fs_uefi_ready_cb(lv_fs_drv_t * drv);
static void * lv_fs_uefi_open_cb(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t lv_fs_uefi_close_cb(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t lv_fs_uefi_read_cb(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t lv_fs_uefi_write_cb(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t lv_fs_uefi_seek_cb(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t lv_fs_uefi_tell_cb(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * lv_fs_uefi_dir_open_cb(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t lv_fs_uefi_dir_read_cb(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len);
static lv_fs_res_t lv_fs_uefi_dir_close_cb(lv_fs_drv_t * drv, void * rddir_p);
/**********************
* STATIC VARIABLES
**********************/
static EFI_GUID _uefi_guid_simple_file_system = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
static EFI_GUID _uefi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID;
static EFI_GUID _uefi_guid_file_info = EFI_FILE_INFO_ID;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_uefi_init(void)
{
EFI_LOADED_IMAGE_PROTOCOL * interface_loaded_image = NULL;
EFI_HANDLE fs_handle = NULL;
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
interface_loaded_image = lv_uefi_protocol_open(gLvEfiImageHandle, &_uefi_guid_loaded_image);
LV_ASSERT_NULL(interface_loaded_image);
fs_handle = interface_loaded_image->DeviceHandle;
if(fs_handle == NULL) return;
lv_uefi_protocol_close(gLvEfiImageHandle, &_uefi_guid_loaded_image);
/*Add a simple driver to open images*/
lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->uefi_fs_drv);
lv_fs_drv_uefi_init(fs_drv_p, LV_FS_UEFI_LETTER, fs_handle);
lv_fs_drv_register(fs_drv_p);
}
/**********************
* STATIC FUNCTIONS
**********************/
static bool lv_fs_uefi_ready_cb(lv_fs_drv_t * drv)
{
EFI_HANDLE fs_handle = (EFI_HANDLE)drv->user_data;
return fs_handle != NULL;
}
static void * lv_fs_uefi_open_cb(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
EFI_STATUS status;
EFI_FILE_PROTOCOL * fs_root = NULL;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * fs_interface = NULL;
CHAR16 path_ucs2[LV_FS_MAX_PATH_LENGTH + 1];
lv_uefi_fs_file_context_t * file_ctx = NULL;
UINT64 uefi_mode = 0;
EFI_HANDLE fs_handle = (EFI_HANDLE)drv->user_data;
fs_interface = lv_uefi_protocol_open(fs_handle, &_uefi_guid_simple_file_system);
if(fs_interface == NULL) {
LV_LOG_WARN("[lv_uefi] Unable to open file system protocol.");
goto error;
}
status = fs_interface->OpenVolume(fs_interface, &fs_root);
if(status != EFI_SUCCESS) {
LV_LOG_WARN("[lv_uefi] Unable to open file system root.");
goto error;
}
if(lv_uefi_ascii_to_ucs2(path, path_ucs2, LV_FS_MAX_PATH_LENGTH + 1) == 0) {
LV_LOG_WARN("[lv_uefi] Unable to convert the ASCII path into an UCS-2 path.");
goto error;
}
lv_fs_uefi_lvgl_path_to_uefi_path(path_ucs2);
file_ctx = lv_calloc(1, sizeof(lv_uefi_fs_file_context_t));
LV_ASSERT_MALLOC(file_ctx);
if(mode == LV_FS_MODE_WR) {
uefi_mode = EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE;
}
else {
uefi_mode = EFI_FILE_MODE_READ;
}
status = fs_root->Open(
fs_root,
&file_ctx->interface,
path_ucs2,
uefi_mode,
0);
if(status != EFI_SUCCESS) {
LV_LOG_WARN("[lv_uefi] Unable to open file '%s'.", path);
goto error;
}
if(!lv_fs_uefi_is_file(file_ctx->interface)) {
goto error;
}
goto finish;
error:
if(file_ctx != NULL) {
if(file_ctx->interface != NULL) file_ctx->interface->Close(file_ctx->interface);
lv_free(file_ctx);
file_ctx = NULL;
}
finish:
if(fs_interface != NULL) lv_uefi_protocol_close(fs_handle, &_uefi_guid_simple_file_system);
if(fs_root != NULL) fs_root->Close(fs_root);
return file_ctx;
}
static lv_fs_res_t lv_fs_uefi_close_cb(lv_fs_drv_t * drv, void * file_p)
{
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
status = file_ctx->interface->Close(file_ctx->interface);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
lv_free(file_ctx);
return LV_FS_RES_OK;
}
static lv_fs_res_t lv_fs_uefi_read_cb(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p;
UINTN buf_size = btr;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
status = file_ctx->interface->Read(
file_ctx->interface,
&buf_size,
buf);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
*br = (uint32_t) buf_size;
return LV_FS_RES_OK;
}
static lv_fs_res_t lv_fs_uefi_write_cb(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p;
UINTN buf_size = btw;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
status = file_ctx->interface->Write(
file_ctx->interface,
&buf_size,
(VOID *)buf);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
file_ctx->interface->Flush(file_ctx->interface);
*bw = (uint32_t) buf_size;
return LV_FS_RES_OK;
}
static lv_fs_res_t lv_fs_uefi_seek_cb(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p;
UINT64 new_pos;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
if(whence == LV_FS_SEEK_END) {
status = file_ctx->interface->SetPosition(
file_ctx->interface,
UINT64_MAX);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
status = file_ctx->interface->GetPosition(
file_ctx->interface,
&new_pos);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
if(new_pos < pos) {
new_pos = 0;
}
else {
new_pos -= pos;
}
}
else if(whence == LV_FS_SEEK_SET) {
new_pos = pos;
}
else if(whence == LV_FS_SEEK_CUR) {
status = file_ctx->interface->GetPosition(
file_ctx->interface,
&new_pos);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
new_pos += pos;
}
else {
return LV_FS_RES_INV_PARAM;
}
status = file_ctx->interface->SetPosition(
file_ctx->interface,
new_pos);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
return LV_FS_RES_OK;
}
static lv_fs_res_t lv_fs_uefi_tell_cb(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)file_p;
UINT64 pos;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
status = file_ctx->interface->GetPosition(
file_ctx->interface,
&pos);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
if(pos > UINT32_MAX) return LV_FS_RES_UNKNOWN;
*pos_p = (uint32_t) pos;
return LV_FS_RES_OK;
}
static void * lv_fs_uefi_dir_open_cb(lv_fs_drv_t * drv, const char * path)
{
EFI_STATUS status;
EFI_FILE_PROTOCOL * fs_root = NULL;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * fs_interface = NULL;
CHAR16 path_ucs2[LV_FS_MAX_PATH_LENGTH + 1];
lv_uefi_fs_file_context_t * file_ctx = NULL;
UINT64 mode = 0;
UINT64 attributes = 0;
EFI_HANDLE fs_handle = (EFI_HANDLE)drv->user_data;
fs_interface = lv_uefi_protocol_open(fs_handle, &_uefi_guid_simple_file_system);
if(fs_interface == NULL) {
LV_LOG_WARN("[lv_uefi] Unable to open file system protocol.");
goto error;
}
status = fs_interface->OpenVolume(fs_interface, &fs_root);
if(status != EFI_SUCCESS) {
LV_LOG_WARN("[lv_uefi] Unable to open file system root.");
goto error;
}
if(path != NULL && path[0] != '\0') {
if(lv_uefi_ascii_to_ucs2(path, path_ucs2, LV_FS_MAX_PATH_LENGTH + 1) == 0) {
LV_LOG_WARN("[lv_uefi] Unable to convert the ASCII path into an UCS-2 path.");
goto error;
}
}
else {
path_ucs2[0] = '\\';
path_ucs2[1] = '\0';
}
lv_fs_uefi_lvgl_path_to_uefi_path(path_ucs2);
file_ctx = lv_calloc(1, sizeof(lv_uefi_fs_file_context_t));
LV_ASSERT_MALLOC(file_ctx);
mode = EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE;
attributes = EFI_FILE_DIRECTORY;
status = fs_root->Open(
fs_root,
&file_ctx->interface,
path_ucs2,
mode,
attributes);
if(status != EFI_SUCCESS) {
LV_LOG_WARN("[lv_uefi] Unable to open directory '%s'.", path);
goto error;
}
if(!lv_fs_uefi_is_dir(file_ctx->interface)) {
goto error;
}
goto finish;
error:
if(file_ctx != NULL) {
if(file_ctx->interface != NULL) {
file_ctx->interface->Close(file_ctx->interface);
}
lv_free(file_ctx);
file_ctx = NULL;
}
finish:
if(fs_interface != NULL) lv_uefi_protocol_close(fs_handle, &_uefi_guid_simple_file_system);
if(fs_root != NULL) fs_root->Close(fs_root);
return file_ctx;
}
static lv_fs_res_t lv_fs_uefi_dir_read_cb(lv_fs_drv_t * drv, void * rddir_p, char * fn, uint32_t fn_len)
{
lv_fs_res_t return_code;
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)rddir_p;
EFI_FILE_INFO * info = NULL;
UINTN size;
CONST CHAR16 * fn_ucs2;
if(fn == NULL || fn_len == 0) return LV_FS_RES_INV_PARAM;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
// skip . and ..
do {
if(info != NULL) lv_free(info);
info = NULL;
size = 0;
status = file_ctx->interface->Read(
file_ctx->interface,
&size,
info);
if(status == EFI_SUCCESS && size == 0) {
return_code = LV_FS_RES_OK;
*fn = '\0';
goto finish;
}
else if(status != EFI_BUFFER_TOO_SMALL) {
return_code = LV_FS_RES_NOT_EX;
goto error;
}
info = lv_calloc(1, size);
LV_ASSERT_MALLOC(info);
status = file_ctx->interface->Read(
file_ctx->interface,
&size,
info);
if(status != EFI_SUCCESS) {
return_code = LV_FS_RES_HW_ERR;
goto error;
}
} while(lv_fs_uefi_is_dot_path(info->FileName));
lv_fs_uefi_uefi_path_to_lvgl_path(info->FileName);
// skip leading \ and /
for(fn_ucs2 = info->FileName; *fn_ucs2 != L'\0'; fn_ucs2++) {
if(*fn_ucs2 != L'\\' && *fn_ucs2 != L'/') {
break;
}
}
if((info->Attribute & EFI_FILE_DIRECTORY) != 0) {
if(fn_len == 0) {
return_code = LV_FS_RES_UNKNOWN;
goto error;
}
fn[0] = '/';
fn++;
fn_len--;
}
if(lv_uefi_ucs2_to_ascii(fn_ucs2, fn, fn_len) == 0) {
LV_LOG_WARN("[lv_uefi] Unable to convert the UCS-2 path into an ascii path.");
return_code = LV_FS_RES_UNKNOWN;
goto error;
}
return_code = LV_FS_RES_OK;
goto finish;
error:
finish:
if(info) lv_free(info);
return return_code;
}
static lv_fs_res_t lv_fs_uefi_dir_close_cb(lv_fs_drv_t * drv, void * rddir_p)
{
EFI_STATUS status;
lv_uefi_fs_file_context_t * file_ctx = (lv_uefi_fs_file_context_t *)rddir_p;
if(file_ctx == NULL || file_ctx->interface == NULL) return LV_FS_RES_INV_PARAM;
status = file_ctx->interface->Close(file_ctx->interface);
if(status != EFI_SUCCESS) return LV_FS_RES_HW_ERR;
lv_free(file_ctx);
return LV_FS_RES_OK;
}
static void lv_fs_drv_uefi_init(lv_fs_drv_t * drv, char fs_drive_letter, EFI_HANDLE fs_handle)
{
LV_ASSERT_NULL(drv);
LV_ASSERT_NULL(fs_handle);
lv_fs_drv_init(drv);
drv->letter = fs_drive_letter;
drv->cache_size = 0;
drv->ready_cb = lv_fs_uefi_ready_cb;
drv->open_cb = lv_fs_uefi_open_cb;
drv->close_cb = lv_fs_uefi_close_cb;
drv->read_cb = lv_fs_uefi_read_cb;
drv->write_cb = lv_fs_uefi_write_cb;
drv->seek_cb = lv_fs_uefi_seek_cb;
drv->tell_cb = lv_fs_uefi_tell_cb;
drv->dir_open_cb = lv_fs_uefi_dir_open_cb;
drv->dir_read_cb = lv_fs_uefi_dir_read_cb;
drv->dir_close_cb = lv_fs_uefi_dir_close_cb;
drv->user_data = (void *) fs_handle;
}
static void lv_fs_drv_uefi_deinit(lv_fs_drv_t * drv)
{
LV_ASSERT_NULL(drv);
drv->user_data = NULL;
}
static void lv_fs_uefi_lvgl_path_to_uefi_path(CHAR16 * path)
{
if(path == NULL) return;
for(; *path != '\0'; path++) {
if(*path == L'/') *path = L'\\';
}
}
static void lv_fs_uefi_uefi_path_to_lvgl_path(CHAR16 * path)
{
if(path == NULL) return;
for(; *path != '\0'; path++) {
if(*path == L'\\') *path = L'/';
}
}
static bool lv_fs_uefi_is_dot_path(CONST CHAR16 * path)
{
if(path == NULL) return FALSE;
if(path[0] == L'.' && path[1] == L'\0') return TRUE;
if(path[0] == L'.' && path[1] == L'.' && path[2] == L'\0') return TRUE;
return FALSE;
}
static bool lv_fs_uefi_is_dir(EFI_FILE_PROTOCOL * dir)
{
UINT64 attributes;
if(dir == NULL) return FALSE;
EFI_FILE_INFO * info = lv_fs_uefi_get_info(dir);
if(info == NULL) return FALSE;
attributes = info->Attribute;
lv_free(info);
return (attributes & EFI_FILE_DIRECTORY) != 0;
}
static bool lv_fs_uefi_is_file(EFI_FILE_PROTOCOL * file)
{
UINT64 attributes;
if(file == NULL) return FALSE;
EFI_FILE_INFO * info = lv_fs_uefi_get_info(file);
if(info == NULL) return FALSE;
attributes = info->Attribute;
lv_free(info);
return (attributes & EFI_FILE_DIRECTORY) == 0;
}
static EFI_FILE_INFO * lv_fs_uefi_get_info(EFI_FILE_PROTOCOL * file)
{
EFI_STATUS status;
EFI_FILE_INFO * info = NULL;
UINTN size = 0;
status = file->GetInfo(file, &_uefi_guid_file_info, &size, info);
if(status != EFI_BUFFER_TOO_SMALL) return NULL;
info = lv_calloc(1, size);
LV_ASSERT_MALLOC(info);
status = file->GetInfo(file, &_uefi_guid_file_info, &size, info);
if(status != EFI_SUCCESS) {
lv_free(info);
return NULL;
}
return info;
}
#else /* LV_FS_UEFI_LETTER == 0*/
#if defined(LV_FS_UEFI_LETTER) && LV_FS_UEFI_LETTER != '\0'
#warning "LV_FS_UEFI is not enabled but LV_FS_UEFI_LETTER is set"
#endif
#endif
@@ -0,0 +1,472 @@
/**
* @file lv_fs_win32.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_WIN32
#include <windows.h>
#include <stdio.h>
#include <limits.h>
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#if !LV_FS_IS_VALID_LETTER(LV_FS_WIN32_LETTER)
#error "Invalid drive letter"
#endif
/**********************
* TYPEDEFS
**********************/
typedef struct {
HANDLE dir_p;
char next_fn[LV_FS_MAX_PATH_LEN];
lv_fs_res_t next_error;
} dir_handle_t;
/**********************
* STATIC PROTOTYPES
**********************/
static bool is_dots_name(const char * name);
static lv_fs_res_t fs_error_from_win32(DWORD error);
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_win32_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LVGL
*--------------------------------------------------*/
/*Add a simple driver to open images*/
lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->win32_fs_drv);
lv_fs_drv_init(fs_drv_p);
/*Set up fields...*/
fs_drv_p->letter = LV_FS_WIN32_LETTER;
fs_drv_p->cache_size = LV_FS_WIN32_CACHE_SIZE;
fs_drv_p->open_cb = fs_open;
fs_drv_p->close_cb = fs_close;
fs_drv_p->read_cb = fs_read;
fs_drv_p->write_cb = fs_write;
fs_drv_p->seek_cb = fs_seek;
fs_drv_p->tell_cb = fs_tell;
fs_drv_p->dir_close_cb = fs_dir_close;
fs_drv_p->dir_open_cb = fs_dir_open;
fs_drv_p->dir_read_cb = fs_dir_read;
lv_fs_drv_register(fs_drv_p);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Check the dots name
* @param name file or dir name
* @return true if the name is dots name
*/
static bool is_dots_name(const char * name)
{
return name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]));
}
/**
* Convert Win32 error code to error from lv_fs_res_t enum
* @param error Win32 error code
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_error_from_win32(DWORD error)
{
lv_fs_res_t res;
switch(error) {
case ERROR_SUCCESS:
res = LV_FS_RES_OK;
break;
case ERROR_BAD_UNIT:
case ERROR_NOT_READY:
case ERROR_CRC:
case ERROR_SEEK:
case ERROR_NOT_DOS_DISK:
case ERROR_WRITE_FAULT:
case ERROR_READ_FAULT:
case ERROR_GEN_FAILURE:
case ERROR_WRONG_DISK:
res = LV_FS_RES_HW_ERR;
break;
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_TARGET_HANDLE:
res = LV_FS_RES_FS_ERR;
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_DRIVE:
case ERROR_NO_MORE_FILES:
case ERROR_SECTOR_NOT_FOUND:
case ERROR_BAD_NETPATH:
case ERROR_BAD_NET_NAME:
case ERROR_BAD_PATHNAME:
case ERROR_FILENAME_EXCED_RANGE:
res = LV_FS_RES_NOT_EX;
break;
case ERROR_DISK_FULL:
res = LV_FS_RES_FULL;
break;
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
case ERROR_DRIVE_LOCKED:
res = LV_FS_RES_LOCKED;
break;
case ERROR_ACCESS_DENIED:
case ERROR_CURRENT_DIRECTORY:
case ERROR_WRITE_PROTECT:
case ERROR_NETWORK_ACCESS_DENIED:
case ERROR_CANNOT_MAKE:
case ERROR_FAIL_I24:
case ERROR_SEEK_ON_DEVICE:
case ERROR_NOT_LOCKED:
case ERROR_LOCK_FAILED:
res = LV_FS_RES_DENIED;
break;
case ERROR_BUSY:
res = LV_FS_RES_BUSY;
break;
case ERROR_TIMEOUT:
res = LV_FS_RES_TOUT;
break;
case ERROR_NOT_SAME_DEVICE:
case ERROR_DIRECT_ACCESS_HANDLE:
res = LV_FS_RES_NOT_IMP;
break;
case ERROR_TOO_MANY_OPEN_FILES:
case ERROR_ARENA_TRASHED:
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_INVALID_BLOCK:
case ERROR_OUT_OF_PAPER:
case ERROR_SHARING_BUFFER_EXCEEDED:
case ERROR_NOT_ENOUGH_QUOTA:
res = LV_FS_RES_OUT_OF_MEM;
break;
case ERROR_INVALID_FUNCTION:
case ERROR_INVALID_ACCESS:
case ERROR_INVALID_DATA:
case ERROR_BAD_COMMAND:
case ERROR_BAD_LENGTH:
case ERROR_INVALID_PARAMETER:
case ERROR_NEGATIVE_SEEK:
res = LV_FS_RES_INV_PARAM;
break;
default:
res = LV_FS_RES_UNKNOWN;
break;
}
return res;
}
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @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 pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
DWORD desired_access = 0;
if(mode & LV_FS_MODE_RD) {
desired_access |= GENERIC_READ;
}
if(mode & LV_FS_MODE_WR) {
desired_access |= GENERIC_WRITE;
}
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH];
lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s", path);
return (void *)CreateFileA(
buf,
desired_access,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
return CloseHandle((HANDLE)file_p)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
return ReadFile((HANDLE)file_p, buf, btr, (LPDWORD)br, NULL)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param buf pointer to a buffer with the bytes to write
* @param btw Bytes To Write
* @param bw 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
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
return WriteFile((HANDLE)file_p, buf, btw, (LPDWORD)bw, NULL)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
DWORD move_method = (DWORD) -1;
if(whence == LV_FS_SEEK_SET) {
move_method = FILE_BEGIN;
}
else if(whence == LV_FS_SEEK_CUR) {
move_method = FILE_CURRENT;
}
else if(whence == LV_FS_SEEK_END) {
move_method = FILE_END;
}
LARGE_INTEGER distance_to_move;
distance_to_move.QuadPart = pos;
return SetFilePointerEx((HANDLE)file_p, distance_to_move, NULL, move_method)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param pos_p pointer to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
if(!pos_p) {
return LV_FS_RES_INV_PARAM;
}
LARGE_INTEGER file_pointer;
file_pointer.QuadPart = 0;
LARGE_INTEGER distance_to_move;
distance_to_move.QuadPart = 0;
if(SetFilePointerEx(
(HANDLE)file_p,
distance_to_move,
&file_pointer,
FILE_CURRENT)) {
if(file_pointer.QuadPart > LONG_MAX) {
return LV_FS_RES_INV_PARAM;
}
else {
*pos_p = file_pointer.LowPart;
return LV_FS_RES_OK;
}
}
else {
return fs_error_from_win32(GetLastError());
}
}
/**
* Initialize a 'DIR' or 'HANDLE' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' or 'HANDLE' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
dir_handle_t * handle = (dir_handle_t *)lv_malloc(sizeof(dir_handle_t));
handle->dir_p = INVALID_HANDLE_VALUE;
handle->next_error = LV_FS_RES_OK;
WIN32_FIND_DATAA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[LV_FS_MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s\\*", path);
lv_strcpy(handle->next_fn, "");
handle->dir_p = FindFirstFileA(buf, &fdata);
if(handle->dir_p != INVALID_HANDLE_VALUE) {
do {
if(is_dots_name(fdata.cFileName)) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(handle->dir_p, &fdata));
}
if(handle->dir_p == INVALID_HANDLE_VALUE) {
lv_free(handle);
handle->next_error = fs_error_from_win32(GetLastError());
return INVALID_HANDLE_VALUE;
}
else {
handle->next_error = LV_FS_RES_OK;
return handle;
}
}
/**
* Read the next filename from a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @param fn pointer to a buffer to store the filename
* @param fn_len length of the buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
{
LV_UNUSED(drv);
if(fn_len == 0) return LV_FS_RES_INV_PARAM;
dir_handle_t * handle = (dir_handle_t *)dir_p;
lv_strlcpy(fn, handle->next_fn, fn_len);
lv_fs_res_t current_error = handle->next_error;
lv_strcpy(handle->next_fn, "");
WIN32_FIND_DATAA fdata;
while(FindNextFileA(handle->dir_p, &fdata)) {
if(is_dots_name(fdata.cFileName)) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
}
if(handle->next_fn[0] == '\0') {
handle->next_error = fs_error_from_win32(GetLastError());
}
return current_error;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
dir_handle_t * handle = (dir_handle_t *)dir_p;
lv_fs_res_t res = FindClose(handle->dir_p)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
lv_free(handle);
return res;
}
#else /*LV_USE_FS_WIN32 == 0*/
#if defined(LV_FS_WIN32_LETTER) && LV_FS_WIN32_LETTER != '\0'
#warning "LV_USE_FS_WIN32 is not enabled but LV_FS_WIN32_LETTER is set"
#endif
#endif
@@ -0,0 +1,78 @@
/**
* @file lv_fsdrv.h
*
*/
#ifndef LV_FSDRV_H
#define LV_FSDRV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
/*********************
* DEFINES
*********************/
#define LV_FS_MAX_PATH_LEN 256
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_USE_FS_FATFS
void lv_fs_fatfs_init(void);
#endif
#if LV_USE_FS_STDIO
void lv_fs_stdio_init(void);
#endif
#if LV_USE_FS_POSIX
void lv_fs_posix_init(void);
#endif
#if LV_USE_FS_WIN32
void lv_fs_win32_init(void);
#endif
#if LV_USE_FS_MEMFS
void lv_fs_memfs_init(void);
#endif
#if LV_USE_FS_LITTLEFS
struct lfs;
void lv_littlefs_set_handler(struct lfs *);
void lv_fs_littlefs_init(void);
#endif
#if LV_USE_FS_ARDUINO_ESP_LITTLEFS
void lv_fs_arduino_esp_littlefs_init(void);
#endif
#if LV_USE_FS_ARDUINO_SD
void lv_fs_arduino_sd_init(void);
#endif
#if LV_USE_FS_UEFI
void lv_fs_uefi_init(void);
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_FSDRV_H*/
@@ -0,0 +1,2 @@
All of the source code and documentation for gifdec is released into the
public domain and provided without warranty of any kind.
+823
View File
@@ -0,0 +1,823 @@
#include "gifdec.h"
#include "../../misc/lv_log.h"
#include "../../stdlib/lv_mem.h"
#include "../../misc/lv_color.h"
#if LV_USE_GIF
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
typedef struct Entry {
uint16_t length;
uint16_t prefix;
uint8_t suffix;
} Entry;
typedef struct Table {
int bulk;
int nentries;
Entry * entries;
} Table;
#if LV_GIF_CACHE_DECODE_DATA
#define LZW_MAXBITS 12
#define LZW_TABLE_SIZE (1 << LZW_MAXBITS)
#define LZW_CACHE_SIZE (LZW_TABLE_SIZE * 4)
#endif
static gd_GIF * gif_open(gd_GIF * gif);
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file);
static void f_gif_read(gd_GIF * gif, void * buf, size_t len);
static int f_gif_seek(gd_GIF * gif, size_t pos, int k);
static void f_gif_close(gd_GIF * gif);
#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM
#include "gifdec_mve.h"
#endif
static uint16_t
read_num(gd_GIF * gif)
{
uint8_t bytes[2];
f_gif_read(gif, bytes, 2);
return bytes[0] + (((uint16_t) bytes[1]) << 8);
}
gd_GIF *
gd_open_gif_file(const char * fname)
{
gd_GIF gif_base;
memset(&gif_base, 0, sizeof(gif_base));
bool res = f_gif_open(&gif_base, fname, true);
if(!res) return NULL;
return gif_open(&gif_base);
}
gd_GIF *
gd_open_gif_data(const void * data)
{
gd_GIF gif_base;
memset(&gif_base, 0, sizeof(gif_base));
bool res = f_gif_open(&gif_base, data, false);
if(!res) return NULL;
return gif_open(&gif_base);
}
static gd_GIF * gif_open(gd_GIF * gif_base)
{
uint8_t sigver[3];
uint16_t width, height, depth;
uint8_t fdsz, bgidx, aspect;
uint8_t * bgcolor;
int gct_sz;
gd_GIF * gif = NULL;
/* Header */
f_gif_read(gif_base, sigver, 3);
if(memcmp(sigver, "GIF", 3) != 0) {
LV_LOG_WARN("invalid signature");
goto fail;
}
/* Version */
f_gif_read(gif_base, sigver, 3);
if(memcmp(sigver, "89a", 3) != 0) {
LV_LOG_WARN("invalid version");
goto fail;
}
/* Width x Height */
width = read_num(gif_base);
height = read_num(gif_base);
/* FDSZ */
f_gif_read(gif_base, &fdsz, 1);
/* Presence of GCT */
if(!(fdsz & 0x80)) {
LV_LOG_WARN("no global color table");
goto fail;
}
/* Color Space's Depth */
depth = ((fdsz >> 4) & 7) + 1;
/* Ignore Sort Flag. */
/* GCT Size */
gct_sz = 1 << ((fdsz & 0x07) + 1);
/* Background Color Index */
f_gif_read(gif_base, &bgidx, 1);
/* Aspect Ratio */
f_gif_read(gif_base, &aspect, 1);
/* Create gd_GIF Structure. */
if(0 == width || 0 == height){
LV_LOG_WARN("Zero size image");
goto fail;
}
#if LV_GIF_CACHE_DECODE_DATA
if(0 == (INT_MAX - sizeof(gd_GIF) - LZW_CACHE_SIZE) / width / height / 5){
LV_LOG_WARN("Image dimensions are too large");
goto fail;
}
gif = lv_malloc(sizeof(gd_GIF) + 5 * width * height + LZW_CACHE_SIZE);
#else
if(0 == (INT_MAX - sizeof(gd_GIF)) / width / height / 5){
LV_LOG_WARN("Image dimensions are too large");
goto fail;
}
gif = lv_malloc(sizeof(gd_GIF) + 5 * width * height);
#endif
if(!gif) goto fail;
memcpy(gif, gif_base, sizeof(gd_GIF));
gif->width = width;
gif->height = height;
gif->depth = depth;
/* Read GCT */
gif->gct.size = gct_sz;
f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size);
gif->palette = &gif->gct;
gif->bgindex = bgidx;
gif->canvas = (uint8_t *) &gif[1];
gif->frame = &gif->canvas[4 * width * height];
if(gif->bgindex) {
memset(gif->frame, gif->bgindex, gif->width * gif->height);
}
bgcolor = &gif->palette->colors[gif->bgindex * 3];
#if LV_GIF_CACHE_DECODE_DATA
gif->lzw_cache = gif->frame + width * height;
#endif
#ifdef GIFDEC_FILL_BG
GIFDEC_FILL_BG(gif->canvas, gif->width * gif->height, 1, gif->width * gif->height, bgcolor, 0xff);
#else
for(int i = 0; i < gif->width * gif->height; i++) {
gif->canvas[i * 4 + 0] = *(bgcolor + 2);
gif->canvas[i * 4 + 1] = *(bgcolor + 1);
gif->canvas[i * 4 + 2] = *(bgcolor + 0);
gif->canvas[i * 4 + 3] = 0xff;
}
#endif
gif->anim_start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->loop_count = -1;
goto ok;
fail:
f_gif_close(gif_base);
ok:
return gif;
}
static void
discard_sub_blocks(gd_GIF * gif)
{
uint8_t size;
do {
f_gif_read(gif, &size, 1);
f_gif_seek(gif, size, LV_FS_SEEK_CUR);
} while(size);
}
static void
read_plain_text_ext(gd_GIF * gif)
{
if(gif->plain_text) {
uint16_t tx, ty, tw, th;
uint8_t cw, ch, fg, bg;
size_t sub_block;
f_gif_seek(gif, 1, LV_FS_SEEK_CUR); /* block size = 12 */
tx = read_num(gif);
ty = read_num(gif);
tw = read_num(gif);
th = read_num(gif);
f_gif_read(gif, &cw, 1);
f_gif_read(gif, &ch, 1);
f_gif_read(gif, &fg, 1);
f_gif_read(gif, &bg, 1);
sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
}
else {
/* Discard plain text metadata. */
f_gif_seek(gif, 13, LV_FS_SEEK_CUR);
}
/* Discard plain text sub-blocks. */
discard_sub_blocks(gif);
}
static void
read_graphic_control_ext(gd_GIF * gif)
{
uint8_t rdit;
/* Discard block size (always 0x04). */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
f_gif_read(gif, &rdit, 1);
gif->gce.disposal = (rdit >> 2) & 3;
gif->gce.input = rdit & 2;
gif->gce.transparency = rdit & 1;
gif->gce.delay = read_num(gif);
f_gif_read(gif, &gif->gce.tindex, 1);
/* Skip block terminator. */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
}
static void
read_comment_ext(gd_GIF * gif)
{
if(gif->comment) {
size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->comment(gif);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
}
/* Discard comment sub-blocks. */
discard_sub_blocks(gif);
}
static void
read_application_ext(gd_GIF * gif)
{
char app_id[8];
char app_auth_code[3];
uint16_t loop_count;
/* Discard block size (always 0x0B). */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
/* Application Identifier. */
f_gif_read(gif, app_id, 8);
/* Application Authentication Code. */
f_gif_read(gif, app_auth_code, 3);
if(!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
/* Discard block size (0x03) and constant byte (0x01). */
f_gif_seek(gif, 2, LV_FS_SEEK_CUR);
loop_count = read_num(gif);
if(gif->loop_count < 0) {
if(loop_count == 0) {
gif->loop_count = 0;
}
else {
gif->loop_count = loop_count + 1;
}
}
/* Skip block terminator. */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
}
else if(gif->application) {
size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->application(gif, app_id, app_auth_code);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
discard_sub_blocks(gif);
}
else {
discard_sub_blocks(gif);
}
}
static void
read_ext(gd_GIF * gif)
{
uint8_t label;
f_gif_read(gif, &label, 1);
switch(label) {
case 0x01:
read_plain_text_ext(gif);
break;
case 0xF9:
read_graphic_control_ext(gif);
break;
case 0xFE:
read_comment_ext(gif);
break;
case 0xFF:
read_application_ext(gif);
break;
default:
LV_LOG_WARN("unknown extension: %02X\n", label);
}
}
static uint16_t
get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
{
int bits_read;
int rpad;
int frag_size;
uint16_t key;
key = 0;
for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
rpad = (*shift + bits_read) % 8;
if (rpad == 0) {
/* Update byte. */
if (*sub_len == 0) {
f_gif_read(gif, sub_len, 1); /* Must be nonzero! */
if (*sub_len == 0) return 0x1000;
}
f_gif_read(gif, byte, 1);
(*sub_len)--;
}
frag_size = MIN(key_size - bits_read, 8 - rpad);
key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
}
/* Clear extra bits to the left. */
key &= (1 << key_size) - 1;
*shift = (*shift + key_size) % 8;
return key;
}
#if LV_GIF_CACHE_DECODE_DATA
/* Decompress image pixels.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
static int
read_image_data(gd_GIF *gif, int interlace)
{
uint8_t sub_len, shift, byte;
int ret = 0;
int key_size;
int y, pass, linesize;
uint8_t *ptr = NULL;
uint8_t *ptr_row_start = NULL;
uint8_t *ptr_base = NULL;
size_t start, end;
uint16_t key, clear_code, stop_code, curr_code;
int frm_off, frm_size,curr_size,top_slot,new_codes,slot;
/* The first value of the value sequence corresponding to key */
int first_value;
int last_key;
uint8_t *sp = NULL;
uint8_t *p_stack = NULL;
uint8_t *p_suffix = NULL;
uint16_t *p_prefix = NULL;
/* get initial key size and clear code, stop code */
f_gif_read(gif, &byte, 1);
key_size = (int) byte;
clear_code = 1 << key_size;
stop_code = clear_code + 1;
key = 0;
start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
discard_sub_blocks(gif);
end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
f_gif_seek(gif, start, LV_FS_SEEK_SET);
linesize = gif->width;
ptr_base = &gif->frame[gif->fy * linesize + gif->fx];
ptr_row_start = ptr_base;
ptr = ptr_row_start;
sub_len = shift = 0;
/* decoder */
pass = 0;
y = 0;
p_stack = gif->lzw_cache;
p_suffix = gif->lzw_cache + LZW_TABLE_SIZE;
p_prefix = (uint16_t*)(gif->lzw_cache + LZW_TABLE_SIZE * 2);
frm_off = 0;
frm_size = gif->fw * gif->fh;
curr_size = key_size + 1;
top_slot = 1 << curr_size;
new_codes = clear_code + 2;
slot = new_codes;
first_value = -1;
last_key = -1;
sp = p_stack;
while (frm_off < frm_size) {
/* copy data to frame buffer */
while (sp > p_stack) {
if(frm_off >= frm_size){
LV_LOG_WARN("LZW table token overflows the frame buffer");
return -1;
}
*ptr++ = *(--sp);
frm_off += 1;
/* read one line */
if ((ptr - ptr_row_start) == gif->fw) {
if (interlace) {
switch(pass) {
case 0:
case 1:
y += 8;
ptr_row_start += linesize * 8;
break;
case 2:
y += 4;
ptr_row_start += linesize * 4;
break;
case 3:
y += 2;
ptr_row_start += linesize * 2;
break;
default:
break;
}
while (y >= gif->fh) {
y = 4 >> pass;
ptr_row_start = ptr_base + linesize * y;
pass++;
}
} else {
ptr_row_start += linesize;
}
ptr = ptr_row_start;
}
}
key = get_key(gif, curr_size, &sub_len, &shift, &byte);
if (key == stop_code || key >= LZW_TABLE_SIZE)
break;
if (key == clear_code) {
curr_size = key_size + 1;
slot = new_codes;
top_slot = 1 << curr_size;
first_value = last_key = -1;
sp = p_stack;
continue;
}
curr_code = key;
/*
* If the current code is a code that will be added to the decoding
* dictionary, it is composed of the data list corresponding to the
* previous key and its first data.
* */
if (curr_code == slot && first_value >= 0) {
*sp++ = first_value;
curr_code = last_key;
}else if(curr_code >= slot)
break;
while (curr_code >= new_codes) {
*sp++ = p_suffix[curr_code];
curr_code = p_prefix[curr_code];
}
*sp++ = curr_code;
/* Add code to decoding dictionary */
if (slot < top_slot && last_key >= 0) {
p_suffix[slot] = curr_code;
p_prefix[slot++] = last_key;
}
first_value = curr_code;
last_key = key;
if (slot >= top_slot) {
if (curr_size < LZW_MAXBITS) {
top_slot <<= 1;
curr_size += 1;
}
}
}
if (key == stop_code) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
f_gif_seek(gif, end, LV_FS_SEEK_SET);
return ret;
}
#else
static Table *
new_table(int key_size)
{
int key;
int init_bulk = MAX(1 << (key_size + 1), 0x100);
Table * table = lv_malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
if(table) {
table->bulk = init_bulk;
table->nentries = (1 << key_size) + 2;
table->entries = (Entry *) &table[1];
for(key = 0; key < (1 << key_size); key++)
table->entries[key] = (Entry) {
1, 0xFFF, key
};
}
return table;
}
/* Add table entry. Return value:
* 0 on success
* +1 if key size must be incremented after this addition
* -1 if could not realloc table */
static int
add_entry(Table ** tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
{
Table * table = *tablep;
if(table->nentries == table->bulk) {
table->bulk *= 2;
table = lv_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
if(!table) return -1;
table->entries = (Entry *) &table[1];
*tablep = table;
}
table->entries[table->nentries] = (Entry) {
length, prefix, suffix
};
table->nentries++;
if((table->nentries & (table->nentries - 1)) == 0)
return 1;
return 0;
}
/* Compute output index of y-th input line, in frame of height h. */
static int
interlaced_line_index(int h, int y)
{
int p; /* number of lines in current pass */
p = (h - 1) / 8 + 1;
if(y < p) /* pass 1 */
return y * 8;
y -= p;
p = (h - 5) / 8 + 1;
if(y < p) /* pass 2 */
return y * 8 + 4;
y -= p;
p = (h - 3) / 4 + 1;
if(y < p) /* pass 3 */
return y * 4 + 2;
y -= p;
/* pass 4 */
return y * 2 + 1;
}
/* Decompress image pixels.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
static int
read_image_data(gd_GIF * gif, int interlace)
{
uint8_t sub_len, shift, byte;
int init_key_size, key_size, table_is_full = 0;
int frm_off, frm_size, str_len = 0, i, p, x, y;
uint16_t key, clear, stop;
int ret;
Table * table;
Entry entry = {0};
size_t start, end;
f_gif_read(gif, &byte, 1);
key_size = (int) byte;
start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
discard_sub_blocks(gif);
end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
f_gif_seek(gif, start, LV_FS_SEEK_SET);
clear = 1 << key_size;
stop = clear + 1;
table = new_table(key_size);
key_size++;
init_key_size = key_size;
sub_len = shift = 0;
key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
frm_off = 0;
ret = 0;
frm_size = gif->fw * gif->fh;
while(frm_off < frm_size) {
if(key == clear) {
key_size = init_key_size;
table->nentries = (1 << (key_size - 1)) + 2;
table_is_full = 0;
}
else if(!table_is_full) {
ret = add_entry(&table, str_len + 1, key, entry.suffix);
if(ret == -1) {
lv_free(table);
return -1;
}
if(table->nentries == 0x1000) {
ret = 0;
table_is_full = 1;
}
}
key = get_key(gif, key_size, &sub_len, &shift, &byte);
if(key == clear) continue;
if(key == stop || key == 0x1000) break;
if(ret == 1) key_size++;
entry = table->entries[key];
str_len = entry.length;
if(frm_off + str_len > frm_size){
LV_LOG_WARN("LZW table token overflows the frame buffer");
lv_free(table);
return -1;
}
for(i = 0; i < str_len; i++) {
p = frm_off + entry.length - 1;
x = p % gif->fw;
y = p / gif->fw;
if(interlace)
y = interlaced_line_index((int) gif->fh, y);
gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
if(entry.prefix == 0xFFF)
break;
else
entry = table->entries[entry.prefix];
}
frm_off += str_len;
if(key < table->nentries - 1 && !table_is_full)
table->entries[table->nentries - 1].suffix = entry.suffix;
}
lv_free(table);
if(key == stop) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
f_gif_seek(gif, end, LV_FS_SEEK_SET);
return 0;
}
#endif
/* Read image.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table) or parse error. */
static int
read_image(gd_GIF * gif)
{
uint8_t fisrz;
int interlace;
/* Image Descriptor. */
gif->fx = read_num(gif);
gif->fy = read_num(gif);
gif->fw = read_num(gif);
gif->fh = read_num(gif);
if(gif->fx + (uint32_t)gif->fw > gif->width || gif->fy + (uint32_t)gif->fh > gif->height){
LV_LOG_WARN("Frame coordinates out of image bounds");
return -1;
}
f_gif_read(gif, &fisrz, 1);
interlace = fisrz & 0x40;
/* Ignore Sort Flag. */
/* Local Color Table? */
if(fisrz & 0x80) {
/* Read LCT */
gif->lct.size = 1 << ((fisrz & 0x07) + 1);
f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size);
gif->palette = &gif->lct;
}
else
gif->palette = &gif->gct;
/* Image Data. */
return read_image_data(gif, interlace);
}
static void
render_frame_rect(gd_GIF * gif, uint8_t * buffer)
{
int i = gif->fy * gif->width + gif->fx;
#ifdef GIFDEC_RENDER_FRAME
GIFDEC_RENDER_FRAME(&buffer[i * 4], gif->fw, gif->fh, gif->width,
&gif->frame[i], gif->palette->colors,
gif->gce.transparency ? gif->gce.tindex : 0x100);
#else
int j, k;
uint8_t index, * color;
for(j = 0; j < gif->fh; j++) {
for(k = 0; k < gif->fw; k++) {
index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
color = &gif->palette->colors[index * 3];
if(!gif->gce.transparency || index != gif->gce.tindex) {
buffer[(i + k) * 4 + 0] = *(color + 2);
buffer[(i + k) * 4 + 1] = *(color + 1);
buffer[(i + k) * 4 + 2] = *(color + 0);
buffer[(i + k) * 4 + 3] = 0xFF;
}
}
i += gif->width;
}
#endif
}
static void
dispose(gd_GIF * gif)
{
int i;
uint8_t * bgcolor;
switch(gif->gce.disposal) {
case 2: /* Restore to background color. */
bgcolor = &gif->palette->colors[gif->bgindex * 3];
uint8_t opa = 0xff;
if(gif->gce.transparency) opa = 0x00;
i = gif->fy * gif->width + gif->fx;
#ifdef GIFDEC_FILL_BG
GIFDEC_FILL_BG(&(gif->canvas[i * 4]), gif->fw, gif->fh, gif->width, bgcolor, opa);
#else
int j, k;
for(j = 0; j < gif->fh; j++) {
for(k = 0; k < gif->fw; k++) {
gif->canvas[(i + k) * 4 + 0] = *(bgcolor + 2);
gif->canvas[(i + k) * 4 + 1] = *(bgcolor + 1);
gif->canvas[(i + k) * 4 + 2] = *(bgcolor + 0);
gif->canvas[(i + k) * 4 + 3] = opa;
}
i += gif->width;
}
#endif
break;
case 3: /* Restore to previous, i.e., don't update canvas.*/
break;
default:
/* Add frame non-transparent pixels to canvas. */
render_frame_rect(gif, gif->canvas);
}
}
/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
int
gd_get_frame(gd_GIF * gif)
{
char sep;
dispose(gif);
f_gif_read(gif, &sep, 1);
while(sep != ',') {
if(sep == ';') {
f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
if(gif->loop_count == 1 || gif->loop_count < 0) {
return 0;
}
else if(gif->loop_count > 1) {
gif->loop_count--;
}
}
else if(sep == '!')
read_ext(gif);
else return -1;
f_gif_read(gif, &sep, 1);
}
if(read_image(gif) == -1)
return -1;
return 1;
}
void
gd_render_frame(gd_GIF * gif, uint8_t * buffer)
{
render_frame_rect(gif, buffer);
}
void
gd_rewind(gd_GIF * gif)
{
gif->loop_count = -1;
f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
}
void
gd_close_gif(gd_GIF * gif)
{
f_gif_close(gif);
lv_free(gif);
}
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file)
{
gif->f_rw_p = 0;
gif->data = NULL;
gif->is_file = is_file;
if(is_file) {
lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return false;
else return true;
}
else {
gif->data = path;
return true;
}
}
static void f_gif_read(gd_GIF * gif, void * buf, size_t len)
{
if(gif->is_file) {
lv_fs_read(&gif->fd, buf, len, NULL);
}
else {
memcpy(buf, &gif->data[gif->f_rw_p], len);
gif->f_rw_p += len;
}
}
static int f_gif_seek(gd_GIF * gif, size_t pos, int k)
{
if(gif->is_file) {
lv_fs_seek(&gif->fd, pos, k);
uint32_t x;
lv_fs_tell(&gif->fd, &x);
return x;
}
else {
if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos;
else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos;
return gif->f_rw_p;
}
}
static void f_gif_close(gd_GIF * gif)
{
if(gif->is_file) {
lv_fs_close(&gif->fd);
}
}
#endif /*LV_USE_GIF*/
@@ -0,0 +1,71 @@
#ifndef GIFDEC_H
#define GIFDEC_H
#ifdef __cplusplus
extern "C" {
#endif
#include "../../misc/lv_fs.h"
#if LV_USE_GIF
#include <stdint.h>
typedef struct _gd_Palette {
int size;
uint8_t colors[0x100 * 3];
} gd_Palette;
typedef struct _gd_GCE {
uint16_t delay;
uint8_t tindex;
uint8_t disposal;
int input;
int transparency;
} gd_GCE;
typedef struct _gd_GIF {
lv_fs_file_t fd;
const char * data;
uint8_t is_file;
uint32_t f_rw_p;
int32_t anim_start;
uint16_t width, height;
uint16_t depth;
int32_t loop_count;
gd_GCE gce;
gd_Palette * palette;
gd_Palette lct, gct;
void (*plain_text)(
struct _gd_GIF * gif, uint16_t tx, uint16_t ty,
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
uint8_t fg, uint8_t bg
);
void (*comment)(struct _gd_GIF * gif);
void (*application)(struct _gd_GIF * gif, char id[8], char auth[3]);
uint16_t fx, fy, fw, fh;
uint8_t bgindex;
uint8_t * canvas, * frame;
#if LV_GIF_CACHE_DECODE_DATA
uint8_t *lzw_cache;
#endif
} gd_GIF;
gd_GIF * gd_open_gif_file(const char * fname);
gd_GIF * gd_open_gif_data(const void * data);
void gd_render_frame(gd_GIF * gif, uint8_t * buffer);
int gd_get_frame(gd_GIF * gif);
void gd_rewind(gd_GIF * gif);
void gd_close_gif(gd_GIF * gif);
#endif /*LV_USE_GIF*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* GIFDEC_H */
@@ -0,0 +1,140 @@
/**
* @file gifdec_mve.h
*
*/
#ifndef GIFDEC_MVE_H
#define GIFDEC_MVE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#define GIFDEC_FILL_BG(dst, w, h, stride, color, opa) \
_gifdec_fill_bg_mve(dst, w, h, stride, color, opa)
#define GIFDEC_RENDER_FRAME(dst, w, h, stride, frame, pattern, tindex) \
_gifdec_render_frame_mve(dst, w, h, stride, frame, pattern, tindex)
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
static inline void _gifdec_fill_bg_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * color,
uint8_t opa)
{
lv_color32_t c = lv_color32_make(*(color + 0), *(color + 1), *(color + 2), opa);
uint32_t color_32 = *(uint32_t *)&c;
__asm volatile(
".p2align 2 \n"
"vdup.32 q0, %[src] \n"
"3: \n"
"mov r0, %[dst] \n"
"wlstp.32 lr, %[w], 1f \n"
"2: \n"
"vstrw.32 q0, [r0], #16 \n"
"letp lr, 2b \n"
"1: \n"
"add %[dst], %[iTargetStride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[h] "+r"(h)
: [src] "r"(color_32),
[w] "r"(w),
[iTargetStride] "r"(stride * sizeof(uint32_t))
: "r0", "q0", "memory", "r14", "cc");
}
static inline void _gifdec_render_frame_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * frame,
uint8_t * pattern, uint16_t tindex)
{
if(w == 0 || h == 0) {
return;
}
__asm volatile(
"vmov.u16 q3, #255 \n"
"vshl.u16 q3, q3, #8 \n" /* left shift 8 for a*/
"mov r0, #2 \n"
"vidup.u16 q6, r0, #4 \n" /* [2, 6, 10, 14, 18, 22, 26, 30] */
"mov r0, #0 \n"
"vidup.u16 q7, r0, #4 \n" /* [0, 4, 8, 12, 16, 20, 24, 28] */
"3: \n"
"mov r1, %[dst] \n"
"mov r2, %[frame] \n"
"wlstp.16 lr, %[w], 1f \n"
"2: \n"
"mov r0, #3 \n"
"vldrb.u16 q4, [r2], #8 \n"
"vmul.u16 q5, q4, r0 \n"
"mov r0, #1 \n"
"vldrb.u16 q2, [%[pattern], q5] \n" /* load 8 pixel r*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q1, [%[pattern], q5] \n" /* load 8 pixel g*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q0, [%[pattern], q5] \n" /* load 8 pixel b*/
"vshl.u16 q1, q1, #8 \n" /* left shift 8 for g*/
"vorr.u16 q0, q0, q1 \n" /* make 8 pixel gb*/
"vorr.u16 q1, q2, q3 \n" /* make 8 pixel ar*/
"vcmp.i16 ne, q4, %[tindex] \n"
"vpstt \n"
"vstrht.16 q0, [r1, q7] \n"
"vstrht.16 q1, [r1, q6] \n"
"add r1, r1, #32 \n"
"letp lr, 2b \n"
"1: \n"
"mov r0, %[stride], LSL #2 \n"
"add %[dst], r0 \n"
"add %[frame], %[stride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[frame] "+r"(frame),
[h] "+r"(h)
: [pattern] "r"(pattern),
[w] "r"(w),
[stride] "r"(stride),
[tindex] "r"(tindex)
: "r0", "r1", "r2", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory", "r14", "cc");
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*GIFDEC_MVE_H*/
+221
View File
@@ -0,0 +1,221 @@
/**
* @file lv_gifenc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gif_private.h"
#if LV_USE_GIF
#include "../../misc/lv_timer_private.h"
#include "../../misc/cache/lv_cache.h"
#include "../../core/lv_obj_class_private.h"
#include "gifdec.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_gif_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void next_frame_task_cb(lv_timer_t * t);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_gif_class = {
.constructor_cb = lv_gif_constructor,
.destructor_cb = lv_gif_destructor,
.instance_size = sizeof(lv_gif_t),
.base_class = &lv_image_class,
.name = "lv_gif",
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_gif_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_gif_set_src(lv_obj_t * obj, const void * src)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
gd_GIF * gif = gifobj->gif;
/*Close previous gif if any*/
if(gif != NULL) {
lv_image_cache_drop(lv_image_get_src(obj));
gd_close_gif(gif);
gifobj->gif = NULL;
gifobj->imgdsc.data = NULL;
}
if(lv_image_src_get_type(src) == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = src;
gif = gd_open_gif_data(img_dsc->data);
}
else if(lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) {
gif = gd_open_gif_file(src);
}
if(gif == NULL) {
LV_LOG_WARN("Couldn't load the source");
return;
}
gifobj->gif = gif;
gifobj->imgdsc.data = gif->canvas;
gifobj->imgdsc.header.magic = LV_IMAGE_HEADER_MAGIC;
gifobj->imgdsc.header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
gifobj->imgdsc.header.cf = LV_COLOR_FORMAT_ARGB8888;
gifobj->imgdsc.header.h = gif->height;
gifobj->imgdsc.header.w = gif->width;
gifobj->imgdsc.header.stride = gif->width * 4;
gifobj->imgdsc.data_size = gif->width * gif->height * 4;
gifobj->last_call = lv_tick_get();
lv_image_set_src(obj, &gifobj->imgdsc);
lv_timer_resume(gifobj->timer);
lv_timer_reset(gifobj->timer);
next_frame_task_cb(gifobj->timer);
}
void lv_gif_restart(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
LV_LOG_WARN("Gif resource not loaded correctly");
return;
}
gd_rewind(gifobj->gif);
lv_timer_resume(gifobj->timer);
lv_timer_reset(gifobj->timer);
}
void lv_gif_pause(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
lv_timer_pause(gifobj->timer);
}
void lv_gif_resume(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
LV_LOG_WARN("Gif resource not loaded correctly");
return;
}
lv_timer_resume(gifobj->timer);
}
bool lv_gif_is_loaded(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
return (gifobj->gif != NULL);
}
int32_t lv_gif_get_loop_count(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
return -1;
}
return gifobj->gif->loop_count;
}
void lv_gif_set_loop_count(lv_obj_t * obj, int32_t count)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
LV_LOG_WARN("Gif resource not loaded correctly");
return;
}
gifobj->gif->loop_count = count;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_gif_t * gifobj = (lv_gif_t *) obj;
gifobj->gif = NULL;
gifobj->timer = lv_timer_create(next_frame_task_cb, 10, obj);
lv_timer_pause(gifobj->timer);
}
static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_gif_t * gifobj = (lv_gif_t *) obj;
lv_image_cache_drop(lv_image_get_src(obj));
if(gifobj->gif)
gd_close_gif(gifobj->gif);
lv_timer_delete(gifobj->timer);
}
static void next_frame_task_cb(lv_timer_t * t)
{
lv_obj_t * obj = t->user_data;
lv_gif_t * gifobj = (lv_gif_t *) obj;
uint32_t elaps = lv_tick_elaps(gifobj->last_call);
if(elaps < gifobj->gif->gce.delay * 10) return;
gifobj->last_call = lv_tick_get();
int has_next = gd_get_frame(gifobj->gif);
if(has_next == 0) {
/*It was the last repeat*/
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_READY, NULL);
lv_timer_pause(t);
if(res != LV_RESULT_OK) return;
}
gd_render_frame(gifobj->gif, (uint8_t *)gifobj->imgdsc.data);
lv_image_cache_drop(lv_image_get_src(obj));
lv_obj_invalidate(obj);
}
#endif /*LV_USE_GIF*/
+104
View File
@@ -0,0 +1,104 @@
/**
* @file lv_gif.h
*
*/
#ifndef LV_GIF_H
#define LV_GIF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include "../../misc/lv_types.h"
#include "../../draw/lv_draw_buf.h"
#include "../../widgets/image/lv_image.h"
#include "../../core/lv_obj_class.h"
#include LV_STDBOOL_INCLUDE
#include LV_STDINT_INCLUDE
#if LV_USE_GIF
#include "gifdec.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_gif_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a gif object
* @param parent pointer to an object, it will be the parent of the new gif.
* @return pointer to the gif obj
*/
lv_obj_t * lv_gif_create(lv_obj_t * parent);
/**
* Set the gif data to display on the object
* @param obj pointer to a gif object
* @param src 1) pointer to an ::lv_image_dsc_t descriptor (which contains gif raw data) or
* 2) path to a gif file (e.g. "S:/dir/anim.gif")
*/
void lv_gif_set_src(lv_obj_t * obj, const void * src);
/**
* Restart a gif animation.
* @param obj pointer to a gif obj
*/
void lv_gif_restart(lv_obj_t * obj);
/**
* Pause a gif animation.
* @param obj pointer to a gif obj
*/
void lv_gif_pause(lv_obj_t * obj);
/**
* Resume a gif animation.
* @param obj pointer to a gif obj
*/
void lv_gif_resume(lv_obj_t * obj);
/**
* Checks if the GIF was loaded correctly.
* @param obj pointer to a gif obj
*/
bool lv_gif_is_loaded(lv_obj_t * obj);
/**
* Get the loop count for the GIF.
* @param obj pointer to a gif obj
*/
int32_t lv_gif_get_loop_count(lv_obj_t * obj);
/**
* Set the loop count for the GIF.
* @param obj pointer to a gif obj
* @param count the loop count to set
*/
void lv_gif_set_loop_count(lv_obj_t * obj, int32_t count);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GIF*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_GIF_H*/
@@ -0,0 +1,57 @@
/**
* @file lv_gif_private.h
*
*/
#ifndef LV_GIF_PRIVATE_H
#define LV_GIF_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../widgets/image/lv_image_private.h"
#include "lv_gif.h"
#if LV_USE_GIF
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_gif_t {
lv_image_t img;
gd_GIF * gif;
lv_timer_t * timer;
lv_image_dsc_t imgdsc;
uint32_t last_call;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_GIF */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GIF_PRIVATE_H*/
@@ -0,0 +1,598 @@
/**
* @file lv_libjpeg_turbo.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder_private.h"
#include "../../../lvgl.h"
#if LV_USE_LIBJPEG_TURBO
#include "lv_libjpeg_turbo.h"
#include <stdio.h>
#include <jpeglib.h>
#include <jpegint.h>
#include <setjmp.h>
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define DECODER_NAME "JPEG_TURBO"
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
#define JPEG_PIXEL_SIZE 3 /* RGB888 */
#define JPEG_SIGNATURE 0xFFD8FF
#define IS_JPEG_SIGNATURE(x) (((x) & 0x00FFFFFF) == JPEG_SIGNATURE)
/**********************
* TYPEDEFS
**********************/
typedef struct error_mgr_s {
struct jpeg_error_mgr pub;
jmp_buf jb;
} error_mgr_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header);
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static lv_draw_buf_t * decode_jpeg_file(const char * filename);
static uint8_t * read_file(const char * filename, uint32_t * size);
static bool get_jpeg_head_info(const char * filename, uint32_t * width, uint32_t * height, uint32_t * orientation);
static bool get_jpeg_size(uint8_t * data, uint32_t data_size, uint32_t * width, uint32_t * height);
static bool get_jpeg_direction(uint8_t * data, uint32_t data_size, uint32_t * orientation);
static void rotate_buffer(lv_draw_buf_t * decoded, uint8_t * buffer, uint32_t line_index, uint32_t angle);
static void error_exit(j_common_ptr cinfo);
/**********************
* STATIC VARIABLES
**********************/
const int JPEG_EXIF = 0x45786966; /* Exif data structure tag */
const int JPEG_BIG_ENDIAN_TAG = 0x4d4d;
const int JPEG_LITTLE_ENDIAN_TAG = 0x4949;
/**********************
* MACROS
**********************/
#define TRANS_32_VALUE(big_endian, data) big_endian ? \
((*(data) << 24) | (*((data) + 1) << 16) | (*((data) + 2) << 8) | *((data) + 3)) : \
(*(data) | (*((data) + 1) << 8) | (*((data) + 2) << 16) | (*((data) + 3) << 24))
#define TRANS_16_VALUE(big_endian, data) big_endian ? \
((*(data) << 8) | *((data) + 1)) : (*(data) | (*((data) + 1) << 8))
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register the JPEG decoder functions in LVGL
*/
void lv_libjpeg_turbo_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->name = DECODER_NAME;
}
void lv_libjpeg_turbo_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a JPEG image
* @param dsc image descriptor containing the source and type of the image and other info.
* @param header store the info here
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
*/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_image_src_t src_type = dsc->src_type; /*Get the source type*/
/*If it's a JPEG file...*/
if(src_type == LV_IMAGE_SRC_FILE) {
const char * src = dsc->src;
uint32_t jpg_signature = 0;
uint32_t rn;
lv_fs_read(&dsc->file, &jpg_signature, sizeof(jpg_signature), &rn);
if(rn != sizeof(jpg_signature)) {
LV_LOG_WARN("file: %s signature len = %" LV_PRIu32 " error", src, rn);
return LV_RESULT_INVALID;
}
const char * ext = lv_fs_get_ext(src);
bool is_jpeg_ext = (lv_strcmp(ext, "jpg") == 0)
|| (lv_strcmp(ext, "jpeg") == 0);
if(!IS_JPEG_SIGNATURE(jpg_signature)) {
if(is_jpeg_ext) {
LV_LOG_WARN("file: %s signature = 0X%" LV_PRIX32 " error", src, jpg_signature);
}
return LV_RESULT_INVALID;
}
uint32_t width;
uint32_t height;
uint32_t orientation = 0;
if(!get_jpeg_head_info(src, &width, &height, &orientation)) {
return LV_RESULT_INVALID;
}
/*Save the data in the header*/
header->cf = LV_COLOR_FORMAT_RGB888;
header->w = (orientation % 180) ? height : width;
header->h = (orientation % 180) ? width : height;
return LV_RESULT_OK;
}
return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a JPEG image and return the decided image
* @param decoder pointer to the decoder
* @param dsc pointer to the decoder descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
*/
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
/*If it's a JPEG file...*/
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
lv_draw_buf_t * decoded = decode_jpeg_file(fn);
if(decoded == NULL) {
LV_LOG_WARN("decode jpeg file failed");
return LV_RESULT_INVALID;
}
dsc->decoded = decoded;
if(dsc->args.no_cache) return LV_RESULT_OK;
/*If the image cache is disabled, just return the decoded image*/
if(!lv_image_cache_is_enabled()) return LV_RESULT_OK;
/*Add the decoded image to the cache*/
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
if(entry == NULL) {
lv_draw_buf_destroy(decoded);
return LV_RESULT_INVALID;
}
dsc->cache_entry = entry;
return LV_RESULT_OK; /*If not returned earlier then it failed*/
}
return LV_RESULT_INVALID; /*If not returned earlier then it failed*/
}
/**
* Free the allocated resources
*/
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
if(dsc->args.no_cache ||
!lv_image_cache_is_enabled()) lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
}
static uint8_t * read_file(const char * filename, uint32_t * size)
{
uint8_t * data = NULL;
lv_fs_file_t f;
uint32_t data_size;
uint32_t rn;
lv_fs_res_t res;
*size = 0;
res = lv_fs_open(&f, filename, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("can't open %s", filename);
return NULL;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_END);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_tell(&f, &data_size);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
goto failed;
}
/*Read file to buffer*/
data = lv_malloc(data_size);
if(data == NULL) {
LV_LOG_WARN("malloc failed for data");
goto failed;
}
res = lv_fs_read(&f, data, data_size, &rn);
if(res == LV_FS_RES_OK && rn == data_size) {
*size = rn;
}
else {
LV_LOG_WARN("read file failed");
lv_free(data);
data = NULL;
}
failed:
lv_fs_close(&f);
return data;
}
static lv_draw_buf_t * decode_jpeg_file(const char * filename)
{
/* This struct contains the JPEG decompression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
struct jpeg_decompress_struct cinfo;
/* We use our private extension JPEG error handler.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
error_mgr_t jerr;
/* More stuff */
JSAMPARRAY buffer; /* Output row buffer */
int row_stride; /* physical row width in output buffer */
uint32_t image_angle = 0; /* image rotate angle */
lv_draw_buf_t * decoded = NULL;
/* In this example we want to open the input file before doing anything else,
* so that the setjmp() error recovery below can assume the file is open.
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
* requires it in order to read binary files.
*/
uint32_t data_size;
uint8_t * data = read_file(filename, &data_size);
if(data == NULL) {
LV_LOG_WARN("can't load file %s", filename);
return NULL;
}
/* allocate and initialize JPEG decompression object */
/* We set up the normal JPEG error routines, then override error_exit. */
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = error_exit;
/* Establish the setjmp return context for my_error_exit to use. */
if(setjmp(jerr.jb)) {
LV_LOG_WARN("decoding error");
if(decoded) {
lv_draw_buf_destroy(decoded);
}
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_decompress(&cinfo);
lv_free(data);
return NULL;
}
/* Get rotate angle from Exif data */
if(!get_jpeg_direction(data, data_size, &image_angle)) {
LV_LOG_WARN("read jpeg orientation failed.");
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
/* specify data source (eg, a file or buffer) */
jpeg_mem_src(&cinfo, data, data_size);
/* read file parameters with jpeg_read_header() */
jpeg_read_header(&cinfo, TRUE);
/* We can ignore the return value from jpeg_read_header since
* (a) suspension is not possible with the stdio data source, and
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
* See libjpeg.doc for more info.
*/
/* set parameters for decompression */
cinfo.out_color_space = JCS_EXT_BGR;
/* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
*/
/* Start decompressor */
jpeg_start_decompress(&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* We may need to do some setup of our own at this point before reading
* the data. After jpeg_start_decompress() we have the correct scaled
* output image dimensions available, as well as the output colormap
* if we asked for color quantization.
* In this example, we need to make an output work buffer of the right size.
*/
/* JSAMPLEs per row in output buffer */
row_stride = cinfo.output_width * cinfo.output_components;
/* Make a one-row-high sample array that will go away when done with image */
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
uint32_t buf_width = (image_angle % 180) ? cinfo.output_height : cinfo.output_width;
uint32_t buf_height = (image_angle % 180) ? cinfo.output_width : cinfo.output_height;
decoded = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, buf_width, buf_height, LV_COLOR_FORMAT_RGB888,
LV_STRIDE_AUTO);
if(decoded != NULL) {
uint32_t line_index = 0;
/* while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while(cinfo.output_scanline < cinfo.output_height) {
/* jpeg_read_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could ask for
* more than one scanline at a time if that's more convenient.
*/
jpeg_read_scanlines(&cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */
rotate_buffer(decoded, buffer[0], line_index, image_angle);
line_index++;
}
}
/* Finish decompression */
jpeg_finish_decompress(&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* Release JPEG decompression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_decompress(&cinfo);
/* After finish_decompress, we can close the input file.
* Here we postpone it until after no more JPEG errors are possible,
* so as to simplify the setjmp error logic above. (Actually, I don't
* think that jpeg_destroy can do an error exit, but why assume anything...)
*/
lv_free(data);
/* At this point you may want to check to see whether any corrupt-data
* warnings occurred (test whether jerr.pub.num_warnings is nonzero).
*/
/* And we're done! */
return decoded;
}
static bool get_jpeg_head_info(const char * filename, uint32_t * width, uint32_t * height, uint32_t * orientation)
{
uint8_t * data = NULL;
uint32_t data_size;
data = read_file(filename, &data_size);
if(data == NULL) {
return false;
}
if(!get_jpeg_size(data, data_size, width, height)) {
LV_LOG_WARN("read jpeg size failed.");
}
if(!get_jpeg_direction(data, data_size, orientation)) {
LV_LOG_WARN("read jpeg orientation failed.");
}
lv_free(data);
return JPEG_HEADER_OK;
}
static bool get_jpeg_size(uint8_t * data, uint32_t data_size, uint32_t * width, uint32_t * height)
{
struct jpeg_decompress_struct cinfo;
error_mgr_t jerr;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = error_exit;
if(setjmp(jerr.jb)) {
LV_LOG_WARN("read jpeg head failed");
jpeg_destroy_decompress(&cinfo);
return false;
}
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, data, data_size);
int ret = jpeg_read_header(&cinfo, TRUE);
if(ret == JPEG_HEADER_OK) {
*width = cinfo.image_width;
*height = cinfo.image_height;
}
else {
LV_LOG_WARN("read jpeg head failed: %d", ret);
}
jpeg_destroy_decompress(&cinfo);
return JPEG_HEADER_OK;
}
static bool get_jpeg_direction(uint8_t * data, uint32_t data_size, uint32_t * orientation)
{
struct jpeg_decompress_struct cinfo;
error_mgr_t jerr;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = error_exit;
if(setjmp(jerr.jb)) {
LV_LOG_WARN("read jpeg orientation failed");
jpeg_destroy_decompress(&cinfo);
return false;
}
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, data, data_size);
jpeg_save_markers(&cinfo, JPEG_APP0 + 1, 0xFFFF);
cinfo.marker->read_markers(&cinfo);
jpeg_saved_marker_ptr marker = cinfo.marker_list;
while(marker != NULL) {
if(marker->marker == JPEG_APP0 + 1) {
JOCTET FAR * app1_data = marker->data;
if(TRANS_32_VALUE(true, app1_data) == JPEG_EXIF) {
uint16_t endian_tag = TRANS_16_VALUE(true, app1_data + 4 + 2);
if(!(endian_tag == JPEG_LITTLE_ENDIAN_TAG || endian_tag == JPEG_BIG_ENDIAN_TAG)) {
jpeg_destroy_decompress(&cinfo);
return false;
}
bool is_big_endian = endian_tag == JPEG_BIG_ENDIAN_TAG;
/* first ifd offset addr : 4bytes(Exif) + 2bytes(0x00) + 2bytes(align) + 2bytes(tag mark) */
unsigned int offset = TRANS_32_VALUE(is_big_endian, app1_data + 8 + 2);
/* ifd base : 4bytes(Exif) + 2bytes(0x00) */
unsigned char * ifd = 0;
do {
/* ifd start: 4bytes(Exif) + 2bytes(0x00) + offset value(2bytes(align) + 2bytes(tag mark) + 4bytes(offset size)) */
unsigned int entry_offset = 4 + 2 + offset + 2;
if(entry_offset >= marker->data_length) {
jpeg_destroy_decompress(&cinfo);
return false;
}
ifd = app1_data + entry_offset;
unsigned short num_entries = TRANS_16_VALUE(is_big_endian, ifd - 2);
if(entry_offset + num_entries * 12 >= marker->data_length) {
jpeg_destroy_decompress(&cinfo);
return false;
}
for(int i = 0; i < num_entries; i++) {
unsigned short tag = TRANS_16_VALUE(is_big_endian, ifd);
if(tag == 0x0112) {
/* ifd entry: 12bytes = 2bytes(tag number) + 2bytes(kind of data) + 4bytes(number of components) + 4bytes(data)
* orientation kind(0x03) of data is unsigned short */
int dirc = TRANS_16_VALUE(is_big_endian, ifd + 2 + 2 + 4);
switch(dirc) {
case 1:
*orientation = 0;
break;
case 3:
*orientation = 180;
break;
case 6:
*orientation = 90;
break;
case 8:
*orientation = 270;
break;
default:
*orientation = 0;
}
}
ifd += 12;
}
offset = TRANS_32_VALUE(is_big_endian, ifd);
} while(offset != 0);
}
break;
}
marker = marker->next;
}
jpeg_destroy_decompress(&cinfo);
return JPEG_HEADER_OK;
}
static void rotate_buffer(lv_draw_buf_t * decoded, uint8_t * buffer, uint32_t line_index, uint32_t angle)
{
if(angle == 90) {
for(uint32_t x = 0; x < decoded->header.h; x++) {
uint32_t dst_index = x * decoded->header.stride + (decoded->header.w - line_index - 1) * JPEG_PIXEL_SIZE;
lv_memcpy(decoded->data + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
}
}
else if(angle == 180) {
for(uint32_t x = 0; x < decoded->header.w; x++) {
uint32_t dst_index = (decoded->header.h - line_index - 1) * decoded->header.stride + x * JPEG_PIXEL_SIZE;
lv_memcpy(decoded->data + dst_index, buffer + (decoded->header.w - x - 1) * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
}
}
else if(angle == 270) {
for(uint32_t x = 0; x < decoded->header.h; x++) {
uint32_t dst_index = (decoded->header.h - x - 1) * decoded->header.stride + line_index * JPEG_PIXEL_SIZE;
lv_memcpy(decoded->data + dst_index, buffer + x * JPEG_PIXEL_SIZE, JPEG_PIXEL_SIZE);
}
}
else {
lv_memcpy(decoded->data + line_index * decoded->header.stride, buffer, decoded->header.stride);
}
}
static void error_exit(j_common_ptr cinfo)
{
error_mgr_t * myerr = (error_mgr_t *)cinfo->err;
(*cinfo->err->output_message)(cinfo);
longjmp(myerr->jb, 1);
}
#endif /*LV_USE_LIBJPEG_TURBO*/
@@ -0,0 +1,48 @@
/**
* @file lv_libjpeg_turbo.h
*
*/
#ifndef LV_LIBJPEG_TURBO_H
#define LV_LIBJPEG_TURBO_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_LIBJPEG_TURBO
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register the JPEG-Turbo decoder functions in LVGL
*/
void lv_libjpeg_turbo_init(void);
void lv_libjpeg_turbo_deinit(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LIBJPEG_TURBO*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LIBJPEG_TURBO_H*/
@@ -0,0 +1,341 @@
/**
* @file lv_libpng.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder_private.h"
#include "../../../lvgl.h"
#if LV_USE_LIBPNG
#include "lv_libpng.h"
#include <png.h>
#include <string.h>
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define DECODER_NAME "PNG"
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static lv_draw_buf_t * decode_png(lv_image_decoder_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register the PNG decoder functions in LVGL
*/
void lv_libpng_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->name = DECODER_NAME;
}
void lv_libpng_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a PNG image
* @param dsc can be file name or pointer to a C array
* @param header store the info here
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
*/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_image_src_t src_type = dsc->src_type; /*Get the source type*/
if(src_type == LV_IMAGE_SRC_FILE || src_type == LV_IMAGE_SRC_VARIABLE) {
uint32_t * size;
static const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
uint8_t buf[24];
/*If it's a PNG file...*/
if(src_type == LV_IMAGE_SRC_FILE) {
/* Read the width and height from the file. They have a constant location:
* [16..19]: width
* [20..23]: height
*/
uint32_t rn;
lv_fs_read(&dsc->file, buf, sizeof(buf), &rn);
if(rn != sizeof(buf)) return LV_RESULT_INVALID;
if(lv_memcmp(buf, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID;
size = (uint32_t *)&buf[16];
}
/*If it's a PNG file in a C array...*/
else {
const lv_image_dsc_t * img_dsc = dsc->src;
const uint32_t data_size = img_dsc->data_size;
size = ((uint32_t *)img_dsc->data) + 4;
if(data_size < sizeof(magic)) return LV_RESULT_INVALID;
if(lv_memcmp(img_dsc->data, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID;
}
/*Save the data in the header*/
header->cf = LV_COLOR_FORMAT_ARGB8888;
/*The width and height are stored in Big endian format so convert them to little endian*/
header->w = (int32_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
header->h = (int32_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
return LV_RESULT_OK;
}
return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a PNG image and return the decided image
* @param decoder pointer to the decoder
* @param dsc pointer to the decoder descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
*/
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
LV_PROFILER_DECODER_BEGIN_TAG("lv_libpng_decoder_open");
lv_draw_buf_t * decoded;
decoded = decode_png(dsc);
if(decoded == NULL) {
LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
return LV_RESULT_INVALID;
}
lv_draw_buf_t * adjusted = lv_image_decoder_post_process(dsc, decoded);
if(adjusted == NULL) {
lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
return LV_RESULT_INVALID;
}
/*The adjusted draw buffer is newly allocated.*/
if(adjusted != decoded) {
lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
decoded = adjusted;
}
dsc->decoded = decoded;
if(dsc->args.no_cache) {
LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
return LV_RESULT_OK;
}
/*If the image cache is disabled, just return the decoded image*/
if(!lv_image_cache_is_enabled()) {
LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
return LV_RESULT_OK;
}
/*Add the decoded image to the cache*/
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
if(entry == NULL) {
lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
return LV_RESULT_INVALID;
}
dsc->cache_entry = entry;
LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
return LV_RESULT_OK; /*The image is fully decoded. Return with its pointer*/
}
/**
* Free the allocated resources
*/
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
if(dsc->args.no_cache ||
!lv_image_cache_is_enabled()) lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, (lv_draw_buf_t *)dsc->decoded);
}
static uint8_t * alloc_file(const char * filename, uint32_t * size)
{
uint8_t * data = NULL;
lv_fs_file_t f;
uint32_t data_size;
uint32_t rn;
lv_fs_res_t res;
*size = 0;
res = lv_fs_open(&f, filename, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("can't open %s", filename);
return NULL;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_END);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_tell(&f, &data_size);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
goto failed;
}
/*Read file to buffer*/
data = lv_malloc(data_size);
if(data == NULL) {
LV_LOG_WARN("malloc failed for data");
goto failed;
}
res = lv_fs_read(&f, data, data_size, &rn);
if(res == LV_FS_RES_OK && rn == data_size) {
*size = rn;
}
else {
LV_LOG_WARN("read file failed");
lv_free(data);
data = NULL;
}
failed:
lv_fs_close(&f);
return data;
}
static lv_draw_buf_t * decode_png(lv_image_decoder_dsc_t * dsc)
{
int ret;
uint8_t * png_data;
uint32_t png_data_size;
/*Prepare png_image*/
png_image image;
lv_memzero(&image, sizeof(image));
image.version = PNG_IMAGE_VERSION;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
png_data = alloc_file(dsc->src, &png_data_size);
if(png_data == NULL) {
LV_LOG_WARN("can't load file: %s", (const char *)dsc->src);
return NULL;
}
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = dsc->src;
png_data = (uint8_t *)img_dsc->data;
png_data_size = img_dsc->data_size;
}
else
return NULL;
/*Ready to read file*/
ret = png_image_begin_read_from_memory(&image, png_data, png_data_size);
if(!ret) {
LV_LOG_ERROR("png read failed: %d", ret);
if(dsc->src_type == LV_IMAGE_SRC_FILE)
lv_free(png_data);
return NULL;
}
lv_color_format_t cf;
if(dsc->args.use_indexed && (image.format & PNG_FORMAT_FLAG_COLORMAP)) {
cf = LV_COLOR_FORMAT_I8;
image.format = PNG_FORMAT_BGRA_COLORMAP;
}
else {
cf = LV_COLOR_FORMAT_ARGB8888;
image.format = PNG_FORMAT_BGRA;
}
/*Alloc image buffer*/
lv_draw_buf_t * decoded;
decoded = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, image.width, image.height, cf, LV_STRIDE_AUTO);
if(decoded == NULL) {
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
LV_LOG_ERROR("alloc PNG_IMAGE_SIZE(%" LV_PRIu32 ") failed: %s", (uint32_t)PNG_IMAGE_SIZE(image),
(const char *)dsc->src);
lv_free(png_data);
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE)
LV_LOG_ERROR("alloc PNG_IMAGE_SIZE(%" LV_PRIu32 ")", (uint32_t)PNG_IMAGE_SIZE(image));
png_image_free(&image);
return NULL;
}
void * palette = decoded->data;
void * map = decoded->data + LV_COLOR_INDEXED_PALETTE_SIZE(cf) * sizeof(lv_color32_t);
/*Start decoding*/
ret = png_image_finish_read(&image, NULL, map, decoded->header.stride, palette);
png_image_free(&image);
if(dsc->src_type == LV_IMAGE_SRC_FILE)
lv_free(png_data);
if(!ret) {
LV_LOG_ERROR("png decode failed: %s", image.message);
lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
return NULL;
}
return decoded;
}
#endif /*LV_USE_LIBPNG*/
@@ -0,0 +1,48 @@
/**
* @file lv_libpng.h
*
*/
#ifndef LV_LIBPNG_H
#define LV_LIBPNG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_LIBPNG
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register the PNG decoder functions in LVGL
*/
void lv_libpng_init(void);
void lv_libpng_deinit(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LIBPNG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LIBPNG_H*/
@@ -0,0 +1,21 @@
Copyright (c) 2005-2018 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,273 @@
/**
* @file lv_lodepng.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder_private.h"
#include "../../../lvgl.h"
#include "../../core/lv_global.h"
#if LV_USE_LODEPNG
#include "lv_lodepng.h"
#include "lodepng.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
#define DECODER_NAME "LODEPNG"
#define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
static void convert_color_depth(uint8_t * img_p, uint32_t px_cnt);
static lv_draw_buf_t * decode_png_data(const void * png_data, size_t png_data_size);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register the PNG decoder functions in LVGL
*/
void lv_lodepng_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->name = DECODER_NAME;
}
void lv_lodepng_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a PNG image
* @param decoder pointer to the decoder where this function belongs
* @param dsc image descriptor containing the source and type of the image and other info.
* @param header image information is set in header parameter
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
*/
static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
{
LV_UNUSED(decoder); /*Unused*/
lv_image_src_t src_type = dsc->src_type; /*Get the source type*/
if(src_type == LV_IMAGE_SRC_FILE || src_type == LV_IMAGE_SRC_VARIABLE) {
uint32_t * size;
static const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
uint8_t buf[24];
/*If it's a PNG file...*/
if(src_type == LV_IMAGE_SRC_FILE) {
/* Read the width and height from the file. They have a constant location:
* [16..19]: width
* [20..23]: height
*/
uint32_t rn;
lv_fs_read(&dsc->file, buf, sizeof(buf), &rn);
if(rn != sizeof(buf)) return LV_RESULT_INVALID;
if(lv_memcmp(buf, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID;
size = (uint32_t *)&buf[16];
}
/*If it's a PNG file in a C array...*/
else {
const lv_image_dsc_t * img_dsc = dsc->src;
const uint32_t data_size = img_dsc->data_size;
size = ((uint32_t *)img_dsc->data) + 4;
if(data_size < sizeof(magic)) return LV_RESULT_INVALID;
if(lv_memcmp(img_dsc->data, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID;
}
/*Save the data in the header*/
header->cf = LV_COLOR_FORMAT_ARGB8888;
/*The width and height are stored in Big endian format so convert them to little endian*/
header->w = (int32_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
header->h = (int32_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
return LV_RESULT_OK;
}
return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a PNG image and decode it into dsc.decoded
* @param decoder pointer to the decoder where this function belongs
* @param dsc decoded image descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
*/
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
LV_PROFILER_DECODER_BEGIN_TAG("lv_lodepng_decoder_open");
const uint8_t * png_data = NULL;
size_t png_data_size = 0;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
/*Load the file*/
unsigned error = lodepng_load_file((void *)&png_data, &png_data_size, fn);
if(error) {
if(png_data != NULL) {
lv_free((void *)png_data);
}
LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_INVALID;
}
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = dsc->src;
png_data = img_dsc->data;
png_data_size = img_dsc->data_size;
}
else {
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_INVALID;
}
lv_draw_buf_t * decoded = decode_png_data(png_data, png_data_size);
if(dsc->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)png_data);
if(!decoded) {
LV_LOG_WARN("Error decoding PNG");
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_INVALID;
}
lv_draw_buf_t * adjusted = lv_image_decoder_post_process(dsc, decoded);
if(adjusted == NULL) {
lv_draw_buf_destroy(decoded);
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_INVALID;
}
/*The adjusted draw buffer is newly allocated.*/
if(adjusted != decoded) {
lv_draw_buf_destroy(decoded);
decoded = adjusted;
}
dsc->decoded = decoded;
if(dsc->args.no_cache) {
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_OK;
}
/*If the image cache is disabled, just return the decoded image*/
if(!lv_image_cache_is_enabled()) {
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_OK;
}
/*Add the decoded image to the cache*/
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
if(entry == NULL) {
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_INVALID;
}
dsc->cache_entry = entry;
LV_PROFILER_DECODER_END_TAG("lv_lodepng_decoder_open");
return LV_RESULT_OK; /*If not returned earlier then it failed*/
}
/**
* Close PNG image and free data
* @param decoder pointer to the decoder where this function belongs
* @param dsc decoded image descriptor
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
*/
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
if(dsc->args.no_cache ||
!lv_image_cache_is_enabled()) lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
}
static lv_draw_buf_t * decode_png_data(const void * png_data, size_t png_data_size)
{
unsigned png_width; /*Not used, just required by the decoder*/
unsigned png_height; /*Not used, just required by the decoder*/
lv_draw_buf_t * decoded = NULL;
/*Decode the image in ARGB8888 */
unsigned error = lodepng_decode32((unsigned char **)&decoded, &png_width, &png_height, png_data, png_data_size);
if(error) {
if(decoded != NULL) lv_draw_buf_destroy(decoded);
return NULL;
}
/*Convert the image to the system's color depth*/
convert_color_depth(decoded->data, png_width * png_height);
return decoded;
}
/**
* If the display is not in 32 bit format (ARGB888) then convert the image to the current color depth
* @param img the ARGB888 image
* @param px_cnt number of pixels in `img`
*/
static void convert_color_depth(uint8_t * img_p, uint32_t px_cnt)
{
lv_color32_t * img_argb = (lv_color32_t *)img_p;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
uint8_t blue = img_argb[i].blue;
img_argb[i].blue = img_argb[i].red;
img_argb[i].red = blue;
}
}
#endif /*LV_USE_LODEPNG*/
@@ -0,0 +1,48 @@
/**
* @file lv_lodepng.h
*
*/
#ifndef LV_LODEPNG_H
#define LV_LODEPNG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_LODEPNG
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register the PNG decoder functions in LVGL
*/
void lv_lodepng_init(void);
void lv_lodepng_deinit(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LODEPNG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LODEPNG_H*/
@@ -0,0 +1,24 @@
LZ4 Library
Copyright (c) 2011-2020, Yann Collet
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
File diff suppressed because it is too large Load Diff
+899
View File
@@ -0,0 +1,899 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-2023, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 homepage : http://www.lz4.org
- LZ4 source repository : https://github.com/lz4/lz4
*/
#include "../../lv_conf_internal.h"
#if LV_USE_LZ4_INTERNAL
#if defined (__cplusplus)
extern "C" {
#endif
/**
* LVGL's porting
*/
#include "../../lvgl.h"
#define LZ4_FREESTANDING 1
#define LZ4_memset lv_memset
#define LZ4_memcpy lv_memcpy
#define LZ4_memmove lv_memmove
#ifndef LZ4_H_2983827168210
#define LZ4_H_2983827168210
/* --- Dependency --- */
#include <stddef.h> /* size_t */
/**
Introduction
LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
The LZ4 compression library provides in-memory compression and decompression functions.
It gives full buffer control to user.
Compression can be done in:
- a single step (described as Simple Functions)
- a single step, reusing a context (described in Advanced Functions)
- unbounded multiple steps (described as Streaming compression)
lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
Decompressing such a compressed block requires additional metadata.
Exact metadata depends on exact decompression function.
For the typical case of LZ4_decompress_safe(),
metadata includes block's compressed size, and maximum bound of decompressed size.
Each application is free to encode and pass such metadata in whichever way it wants.
lz4.h only handle blocks, it can not generate Frames.
Blocks are different from Frames (doc/lz4_Frame_format.md).
Frames bundle both blocks and metadata in a specified manner.
Embedding metadata is required for compressed data to be self-contained and portable.
Frame format is delivered through a companion API, declared in lz4frame.h.
The `lz4` CLI can only manage frames.
*/
/*^***************************************************************
* Export parameters
*****************************************************************/
/*
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
* LZ4LIB_VISIBILITY :
* Control library symbols visibility.
*/
#ifndef LZ4LIB_VISIBILITY
# if defined(__GNUC__) && (__GNUC__ >= 4)
# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
# else
# define LZ4LIB_VISIBILITY
# endif
#endif
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define LZ4LIB_API LZ4LIB_VISIBILITY
#endif
/*! LZ4_FREESTANDING :
* When this macro is set to 1, it enables "freestanding mode" that is
* suitable for typical freestanding environment which doesn't support
* standard C library.
*
* - LZ4_FREESTANDING is a compile-time switch.
* - It requires the following macros to be defined:
* LZ4_memcpy, LZ4_memmove, LZ4_memset.
* - It only enables LZ4/HC functions which don't use heap.
* All LZ4F_* functions are not supported.
* - See tests/freestanding.c to check its basic setup.
*/
#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
# define LZ4_HEAPMODE 0
# define LZ4HC_HEAPMODE 0
# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
# if !defined(LZ4_memcpy)
# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
# endif
# if !defined(LZ4_memset)
# error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
# endif
# if !defined(LZ4_memmove)
# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
# endif
#elif ! defined(LZ4_FREESTANDING)
# define LZ4_FREESTANDING 0
#endif
/*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 10 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */
/*-************************************
* Tuning memory usage
**************************************/
/*!
* LZ4_MEMORY_USAGE :
* Can be selected at compile time, by setting LZ4_MEMORY_USAGE.
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB)
* Increasing memory usage improves compression ratio, generally at the cost of speed.
* Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into most L1 caches.
*/
#ifndef LZ4_MEMORY_USAGE
# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
#endif
/* These are absolute limits, they should not be changed by users */
#define LZ4_MEMORY_USAGE_MIN 10
#define LZ4_MEMORY_USAGE_DEFAULT 14
#define LZ4_MEMORY_USAGE_MAX 20
#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
# error "LZ4_MEMORY_USAGE is too small !"
#endif
#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
# error "LZ4_MEMORY_USAGE is too large !"
#endif
/*-************************************
* Simple Functions
**************************************/
/*! LZ4_compress_default() :
* Compresses 'srcSize' bytes from buffer 'src'
* into already allocated 'dst' buffer of size 'dstCapacity'.
* Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
* It also runs faster, so it's a recommended setting.
* If the function cannot compress 'src' into a more limited 'dst' budget,
* compression stops *immediately*, and the function result is zero.
* In which case, 'dst' content is undefined (invalid).
* srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
* dstCapacity : size of buffer 'dst' (which must be already allocated)
* @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
* or 0 if compression fails
* Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
*/
LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_safe() :
* @compressedSize : is the exact complete size of the compressed block.
* @dstCapacity : is the size of destination buffer (which must be already allocated),
* presumed an upper bound of decompressed size.
* @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
* Note 1 : This function is protected against malicious data packets :
* it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
* even if the compressed block is maliciously modified to order the decoder to do these actions.
* In such case, the decoder stops immediately, and considers the compressed block malformed.
* Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
* The implementation is free to send / store / derive this information in whichever way is most beneficial.
* If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
*/
LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
/*-************************************
* Advanced Functions
**************************************/
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
/*! LZ4_compressBound() :
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
This function is primarily useful for memory allocation purposes (destination buffer size).
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
return : maximum output size in a "worst case" scenario
or 0, if input size is incorrect (too large or negative)
*/
LZ4LIB_API int LZ4_compressBound(int inputSize);
/*! LZ4_compress_fast() :
Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
An acceleration value of "1" is the same as regular LZ4_compress_default()
Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
*/
LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_fast_extState() :
* Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
* Use LZ4_sizeofState() to know how much memory must be allocated,
* and allocate it on 8-bytes boundaries (using `malloc()` typically).
* Then, provide this buffer as `void* state` to compression function.
*/
LZ4LIB_API int LZ4_sizeofState(void);
LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_destSize() :
* Reverse the logic : compresses as much data as possible from 'src' buffer
* into already allocated buffer 'dst', of size >= 'dstCapacity'.
* This function either compresses the entire 'src' content into 'dst' if it's large enough,
* or fill 'dst' buffer completely with as much data as possible from 'src'.
* note: acceleration parameter is fixed to "default".
*
* *srcSizePtr : in+out parameter. Initially contains size of input.
* Will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
* New value is necessarily <= input value.
* @return : Nb bytes written into 'dst' (necessarily <= dstCapacity)
* or 0 if compression fails.
*
* Note : from v1.8.2 to v1.9.1, this function had a bug (fixed in v1.9.2+):
* the produced compressed content could, in specific circumstances,
* require to be decompressed into a destination buffer larger
* by at least 1 byte than the content to decompress.
* If an application uses `LZ4_compress_destSize()`,
* it's highly recommended to update liblz4 to v1.9.2 or better.
* If this can't be done or ensured,
* the receiving decompression function should provide
* a dstCapacity which is > decompressedSize, by at least 1 byte.
* See https://github.com/lz4/lz4/issues/859 for details
*/
LZ4LIB_API int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize);
/*! LZ4_decompress_safe_partial() :
* Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
* into destination buffer 'dst' of size 'dstCapacity'.
* Up to 'targetOutputSize' bytes will be decoded.
* The function stops decoding on reaching this objective.
* This can be useful to boost performance
* whenever only the beginning of a block is required.
*
* @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
* If source stream is detected malformed, function returns a negative result.
*
* Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
*
* Note 2 : targetOutputSize must be <= dstCapacity
*
* Note 3 : this function effectively stops decoding on reaching targetOutputSize,
* so dstCapacity is kind of redundant.
* This is because in older versions of this function,
* decoding operation would still write complete sequences.
* Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
* it could write more bytes, though only up to dstCapacity.
* Some "margin" used to be required for this operation to work properly.
* Thankfully, this is no longer necessary.
* The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
*
* Note 4 : If srcSize is the exact size of the block,
* then targetOutputSize can be any value,
* including larger than the block's decompressed size.
* The function will, at most, generate block's decompressed size.
*
* Note 5 : If srcSize is _larger_ than block's compressed size,
* then targetOutputSize **MUST** be <= block's decompressed size.
* Otherwise, *silent corruption will occur*.
*/
LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
/*-*********************************************
* Streaming Compression Functions
***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
/*!
Note about RC_INVOKED
- RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
- Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
and reports warning "RC4011: identifier truncated".
- To eliminate the warning, we surround long preprocessor symbol with
"#if !defined(RC_INVOKED) ... #endif" block that means
"skip this block when rc.exe is trying to read it".
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_resetStream_fast() : v1.9.0+
* Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
* (e.g., LZ4_compress_fast_continue()).
*
* An LZ4_stream_t must be initialized once before usage.
* This is automatically done when created by LZ4_createStream().
* However, should the LZ4_stream_t be simply declared on stack (for example),
* it's necessary to initialize it first, using LZ4_initStream().
*
* After init, start any new stream with LZ4_resetStream_fast().
* A same LZ4_stream_t can be re-used multiple times consecutively
* and compress multiple streams,
* provided that it starts each new stream with LZ4_resetStream_fast().
*
* LZ4_resetStream_fast() is much faster than LZ4_initStream(),
* but is not compatible with memory regions containing garbage data.
*
* Note: it's only useful to call LZ4_resetStream_fast()
* in the context of streaming compression.
* The *extState* functions perform their own resets.
* Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
*/
LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
/*! LZ4_loadDict() :
* Use this function to reference a static dictionary into LZ4_stream_t.
* The dictionary must remain available during compression.
* LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
* The same dictionary will have to be loaded on decompression side for successful decoding.
* Dictionary are useful for better compression of small data (KB range).
* While LZ4 itself accepts any input as dictionary, dictionary efficiency is also a topic.
* When in doubt, employ the Zstandard's Dictionary Builder.
* Loading a size of 0 is allowed, and is the same as reset.
* @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded)
*/
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_loadDictSlow() : v1.10.0+
* Same as LZ4_loadDict(),
* but uses a bit more cpu to reference the dictionary content more thoroughly.
* This is expected to slightly improve compression ratio.
* The extra-cpu cost is likely worth it if the dictionary is re-used across multiple sessions.
* @return : loaded dictionary size, in bytes (note: only the last 64 KB are loaded)
*/
LZ4LIB_API int LZ4_loadDictSlow(LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_attach_dictionary() : stable since v1.10.0
*
* This allows efficient re-use of a static dictionary multiple times.
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
* working LZ4_stream_t, this function introduces a no-copy setup mechanism,
* in which the working stream references @dictionaryStream in-place.
*
* Several assumptions are made about the state of @dictionaryStream.
* Currently, only states which have been prepared by LZ4_loadDict() or
* LZ4_loadDictSlow() should be expected to work.
*
* Alternatively, the provided @dictionaryStream may be NULL,
* in which case any existing dictionary stream is unset.
*
* If a dictionary is provided, it replaces any pre-existing stream history.
* The dictionary contents are the only history that can be referenced and
* logically immediately precede the data compressed in the first subsequent
* compression call.
*
* The dictionary will only remain attached to the working stream through the
* first compression call, at the end of which it is cleared.
* @dictionaryStream stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the compression session.
*
* Note: there is no equivalent LZ4_attach_*() method on the decompression side
* because there is no initialization cost, hence no need to share the cost across multiple sessions.
* To decompress LZ4 blocks using dictionary, attached or not,
* just employ the regular LZ4_setStreamDecode() for streaming,
* or the stateless LZ4_decompress_safe_usingDict() for one-shot decompression.
*/
LZ4LIB_API void
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
const LZ4_stream_t* dictionaryStream);
/*! LZ4_compress_fast_continue() :
* Compress 'src' content using data from previously compressed blocks, for better compression ratio.
* 'dst' buffer must be already allocated.
* If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
*
* @return : size of compressed block
* or 0 if there is an error (typically, cannot fit into 'dst').
*
* Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
* Each block has precise boundaries.
* Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
* It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
*
* Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
*
* Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
* Make sure that buffers are separated, by at least one byte.
* This construction ensures that each block only depends on previous block.
*
* Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
*
* Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
*/
LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_saveDict() :
* If last 64KB data cannot be guaranteed to remain available at its current memory location,
* save it into a safer place (char* safeBuffer).
* This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
* but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
* @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
*/
LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
/*-**********************************************
* Streaming Decompression Functions
* Bufferless synchronous API
************************************************/
typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
* creation / destruction of streaming decompression tracking context.
* A tracking context can be re-used multiple times.
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_setStreamDecode() :
* An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
* Use this function to start decompression of a new stream of blocks.
* A dictionary can optionally be set. Use NULL or size 0 for a reset order.
* Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
* @return : 1 if OK, 0 if error
*/
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
/*! LZ4_decoderRingBufferSize() : v1.8.2+
* Note : in a ring buffer scenario (optional),
* blocks are presumed decompressed next to each other
* up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
* at which stage it resumes from beginning of ring buffer.
* When setting such a ring buffer for streaming decompression,
* provides the minimum size of this ring buffer
* to be compatible with any source respecting maxBlockSize condition.
* @return : minimum ring buffer size,
* or 0 if there is an error (invalid maxBlockSize).
*/
LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
/*! LZ4_decompress_safe_continue() :
* This decoding function allows decompression of consecutive blocks in "streaming" mode.
* The difference with the usual independent blocks is that
* new blocks are allowed to find references into former blocks.
* A block is an unsplittable entity, and must be presented entirely to the decompression function.
* LZ4_decompress_safe_continue() only accepts one block at a time.
* It's modeled after `LZ4_decompress_safe()` and behaves similarly.
*
* @LZ4_streamDecode : decompression state, tracking the position in memory of past data
* @compressedSize : exact complete size of one compressed block.
* @dstCapacity : size of destination buffer (which must be already allocated),
* must be an upper bound of decompressed size.
* @return : number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
*
* The last 64KB of previously decoded data *must* remain available and unmodified
* at the memory position where they were previously decoded.
* If less than 64KB of data has been decoded, all the data must be present.
*
* Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
* - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
* maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
* In which case, encoding and decoding buffers do not need to be synchronized.
* Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
* - Synchronized mode :
* Decompression buffer size is _exactly_ the same as compression buffer size,
* and follows exactly same update rule (block boundaries at same positions),
* and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
* _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
* - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
* In which case, encoding and decoding buffers do not need to be synchronized,
* and encoding ring buffer can have any size, including small ones ( < 64 KB).
*
* Whenever these conditions are not possible,
* save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
* then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
*/
LZ4LIB_API int
LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
const char* src, char* dst,
int srcSize, int dstCapacity);
/*! LZ4_decompress_safe_usingDict() :
* Works the same as
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_safe_continue()
* However, it's stateless: it doesn't need any LZ4_streamDecode_t state.
* Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int
LZ4_decompress_safe_usingDict(const char* src, char* dst,
int srcSize, int dstCapacity,
const char* dictStart, int dictSize);
/*! LZ4_decompress_safe_partial_usingDict() :
* Behaves the same as LZ4_decompress_safe_partial()
* with the added ability to specify a memory segment for past data.
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int
LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
int compressedSize,
int targetOutputSize, int maxOutputSize,
const char* dictStart, int dictSize);
#endif /* LZ4_H_2983827168210 */
/*^*************************************
* !!!!!! STATIC LINKING ONLY !!!!!!
***************************************/
/*-****************************************************************************
* Experimental section
*
* Symbols declared in this section must be considered unstable. Their
* signatures or semantics may change, or they may be removed altogether in the
* future. They are therefore only safe to depend on when the caller is
* statically linked against the library.
*
* To protect against unsafe usage, not only are the declarations guarded,
* the definitions are hidden by default
* when building LZ4 as a shared/dynamic library.
*
* In order to access these declarations,
* define LZ4_STATIC_LINKING_ONLY in your application
* before including LZ4's headers.
*
* In order to make their implementations accessible dynamically, you must
* define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
******************************************************************************/
#ifdef LZ4_STATIC_LINKING_ONLY
#ifndef LZ4_STATIC_3504398509
#define LZ4_STATIC_3504398509
#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
# define LZ4LIB_STATIC_API LZ4LIB_API
#else
# define LZ4LIB_STATIC_API
#endif
/*! LZ4_compress_fast_extState_fastReset() :
* A variant of LZ4_compress_fast_extState().
*
* Using this variant avoids an expensive initialization step.
* It is only safe to call if the state buffer is known to be correctly initialized already
* (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
* From a high level, the difference is that
* this function initializes the provided state with a call to something like LZ4_resetStream_fast()
* while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
*/
LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_destSize_extState() : introduced in v1.10.0
* Same as LZ4_compress_destSize(), but using an externally allocated state.
* Also: exposes @acceleration
*/
int LZ4_compress_destSize_extState(void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize, int acceleration);
/*! In-place compression and decompression
*
* It's possible to have input and output sharing the same buffer,
* for highly constrained memory environments.
* In both cases, it requires input to lay at the end of the buffer,
* and decompression to start at beginning of the buffer.
* Buffer size must feature some margin, hence be larger than final size.
*
* |<------------------------buffer--------------------------------->|
* |<-----------compressed data--------->|
* |<-----------decompressed size------------------>|
* |<----margin---->|
*
* This technique is more useful for decompression,
* since decompressed size is typically larger,
* and margin is short.
*
* In-place decompression will work inside any buffer
* which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
* This presumes that decompressedSize > compressedSize.
* Otherwise, it means compression actually expanded data,
* and it would be more efficient to store such data with a flag indicating it's not compressed.
* This can happen when data is not compressible (already compressed, or encrypted).
*
* For in-place compression, margin is larger, as it must be able to cope with both
* history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
* and data expansion, which can happen when input is not compressible.
* As a consequence, buffer size requirements are much higher,
* and memory savings offered by in-place compression are more limited.
*
* There are ways to limit this cost for compression :
* - Reduce history size, by modifying LZ4_DISTANCE_MAX.
* Note that it is a compile-time constant, so all compressions will apply this limit.
* Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
* so it's a reasonable trick when inputs are known to be small.
* - Require the compressor to deliver a "maximum compressed size".
* This is the `dstCapacity` parameter in `LZ4_compress*()`.
* When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
* in which case, the return code will be 0 (zero).
* The caller must be ready for these cases to happen,
* and typically design a backup scheme to send data uncompressed.
* The combination of both techniques can significantly reduce
* the amount of margin required for in-place compression.
*
* In-place compression can work in any buffer
* which size is >= (maxCompressedSize)
* with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
* LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
* so it's possible to reduce memory requirements by playing with them.
*/
#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
#endif
#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
#endif /* LZ4_STATIC_3504398509 */
#endif /* LZ4_STATIC_LINKING_ONLY */
#ifndef LZ4_H_98237428734687
#define LZ4_H_98237428734687
/*-************************************************************
* Private Definitions
**************************************************************
* Do not use these definitions directly.
* They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
* Accessing members will expose user code to API and/or ABI break in future versions of the library.
**************************************************************/
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# include <stdint.h>
typedef int8_t LZ4_i8;
typedef uint8_t LZ4_byte;
typedef uint16_t LZ4_u16;
typedef uint32_t LZ4_u32;
#else
typedef signed char LZ4_i8;
typedef unsigned char LZ4_byte;
typedef unsigned short LZ4_u16;
typedef unsigned int LZ4_u32;
#endif
/*! LZ4_stream_t :
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_stream_t object.
**/
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
struct LZ4_stream_t_internal {
LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
const LZ4_byte* dictionary;
const LZ4_stream_t_internal* dictCtx;
LZ4_u32 currentOffset;
LZ4_u32 tableType;
LZ4_u32 dictSize;
/* Implicit padding to ensure structure is aligned */
};
#define LZ4_STREAM_MINSIZE ((1UL << (LZ4_MEMORY_USAGE)) + 32) /* static size, for inter-version compatibility */
union LZ4_stream_u {
char minStateSize[LZ4_STREAM_MINSIZE];
LZ4_stream_t_internal internal_donotuse;
}; /* previously typedef'd to LZ4_stream_t */
/*! LZ4_initStream() : v1.9.0+
* An LZ4_stream_t structure must be initialized at least once.
* This is automatically done when invoking LZ4_createStream(),
* but it's not when the structure is simply declared on stack (for example).
*
* Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
* It can also initialize any arbitrary buffer of sufficient size,
* and will @return a pointer of proper type upon initialization.
*
* Note : initialization fails if size and alignment conditions are not respected.
* In which case, the function will @return NULL.
* Note2: An LZ4_stream_t structure guarantees correct alignment and size.
* Note3: Before v1.9.0, use LZ4_resetStream() instead
**/
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* stateBuffer, size_t size);
/*! LZ4_streamDecode_t :
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
**/
typedef struct {
const LZ4_byte* externalDict;
const LZ4_byte* prefixEnd;
size_t extDictSize;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#define LZ4_STREAMDECODE_MINSIZE 32
union LZ4_streamDecode_u {
char minStateSize[LZ4_STREAMDECODE_MINSIZE];
LZ4_streamDecode_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_streamDecode_t */
/*-************************************
* Obsolete Functions
**************************************/
/*! Deprecation warnings
*
* Deprecated functions make the compiler generate a warning when invoked.
* This is meant to invite users to update their source code.
* Should deprecation warnings be a problem, it is generally possible to disable them,
* typically with -Wno-deprecated-declarations for gcc
* or _CRT_SECURE_NO_WARNINGS in Visual.
*
* Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
* before including the header file.
*/
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
# elif defined(_MSC_VER)
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
# else
# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
# define LZ4_DEPRECATED(message) /* disabled */
# endif
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
/*! Obsolete compression functions (since v1.7.3) */
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/*! Obsolete decompression functions (since v1.8.0) */
LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
/* Obsolete streaming functions (since v1.7.0)
* degraded functionality; do not use!
*
* In order to perform streaming compression, these functions depended on data
* that is no longer tracked in the state. They have been preserved as well as
* possible: using them will still produce a correct output. However, they don't
* actually retain any history between compression calls. The compression ratio
* achieved will therefore be no better than compressing each chunk
* independently.
*/
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
/*! Obsolete streaming decoding functions (since v1.7.0) */
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
* These functions used to be faster than LZ4_decompress_safe(),
* but this is no longer the case. They are now slower.
* This is because LZ4_decompress_fast() doesn't know the input size,
* and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
* On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
* As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
*
* The last remaining LZ4_decompress_fast() specificity is that
* it can decompress a block without knowing its compressed size.
* Such functionality can be achieved in a more secure manner
* by employing LZ4_decompress_safe_partial().
*
* Parameters:
* originalSize : is the uncompressed size to regenerate.
* `dst` must be already allocated, its size must be >= 'originalSize' bytes.
* @return : number of bytes read from source buffer (== compressed size).
* The function expects to finish at block's end exactly.
* If the source stream is detected malformed, the function stops decoding and returns a negative result.
* note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
* However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
* Also, since match offsets are not validated, match reads from 'src' may underflow too.
* These issues never happen if input (compressed) data is correct.
* But they may happen if input data is invalid (error or intentional tampering).
* As a consequence, use these functions in trusted environments with trusted data **only**.
*/
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial() instead")
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider migrating towards LZ4_decompress_safe_continue() instead. "
"Note that the contract will change (requires block's compressed size, instead of decompressed size)")
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_partial_usingDict() instead")
LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
/*! LZ4_resetStream() :
* An LZ4_stream_t structure must be initialized at least once.
* This is done with LZ4_initStream(), or LZ4_resetStream().
* Consider switching to LZ4_initStream(),
* invoking LZ4_resetStream() will trigger deprecation warnings in the future.
*/
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
#endif /* LZ4_H_98237428734687 */
#if defined (__cplusplus)
}
#endif
#endif /* LV_USE_LZ4_INTERNAL */
@@ -0,0 +1,8 @@
Copyright © 2025 Project Nayuki. (MIT License)
https://www.nayuki.io/page/qr-code-generator-library
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
@@ -0,0 +1,242 @@
/**
* @file lv_qrcode.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../core/lv_obj_class_private.h"
#include "lv_qrcode_private.h"
#if LV_USE_QRCODE
#include "qrcodegen.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_qrcode_class)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_qrcode_class = {
.constructor_cb = lv_qrcode_constructor,
.destructor_cb = lv_qrcode_destructor,
.instance_size = sizeof(lv_qrcode_t),
.base_class = &lv_canvas_class,
.name = "lv_qrcode",
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_qrcode_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_qrcode_set_size(lv_obj_t * obj, int32_t size)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_draw_buf_t * old_buf = lv_canvas_get_draw_buf(obj);
lv_draw_buf_t * new_buf = lv_draw_buf_create(size, size, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
if(new_buf == NULL) {
LV_LOG_ERROR("malloc failed for canvas buffer");
return;
}
lv_canvas_set_draw_buf(obj, new_buf);
LV_LOG_INFO("set canvas buffer: %p, size = %d", (void *)new_buf, (int)size);
/*Clear canvas buffer*/
lv_draw_buf_clear(new_buf, NULL);
if(old_buf != NULL) lv_draw_buf_destroy(old_buf);
}
void lv_qrcode_set_dark_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_qrcode_t * qrcode = (lv_qrcode_t *)obj;
qrcode->dark_color = color;
}
void lv_qrcode_set_light_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_qrcode_t * qrcode = (lv_qrcode_t *)obj;
qrcode->light_color = color;
}
lv_result_t lv_qrcode_update(lv_obj_t * obj, const void * data, uint32_t data_len)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_qrcode_t * qrcode = (lv_qrcode_t *)obj;
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
if(draw_buf == NULL) {
LV_LOG_ERROR("canvas draw buffer is NULL");
return LV_RESULT_INVALID;
}
lv_draw_buf_clear(draw_buf, NULL);
lv_canvas_set_palette(obj, 0, lv_color_to_32(qrcode->light_color, LV_OPA_COVER));
lv_canvas_set_palette(obj, 1, lv_color_to_32(qrcode->dark_color, LV_OPA_COVER));
lv_image_cache_drop(draw_buf);
lv_obj_invalidate(obj);
if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RESULT_INVALID;
int32_t qr_version = qrcodegen_getMinFitVersion(qrcodegen_Ecc_MEDIUM, data_len);
if(qr_version <= 0) return LV_RESULT_INVALID;
int32_t qr_size = qrcodegen_version2size(qr_version);
if(qr_size <= 0) return LV_RESULT_INVALID;
int32_t scale = draw_buf->header.w / qr_size;
if(scale <= 0) return LV_RESULT_INVALID;
/* Pick the largest QR code that still maintains scale. */
for(int32_t i = qr_version + 1; i < qrcodegen_VERSION_MAX; i++) {
if(qrcodegen_version2size(i) * scale > draw_buf->header.w)
break;
qr_version = i;
}
qr_size = qrcodegen_version2size(qr_version);
uint8_t * qr0 = lv_malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
LV_ASSERT_MALLOC(qr0);
uint8_t * data_tmp = lv_malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
LV_ASSERT_MALLOC(data_tmp);
lv_memcpy(data_tmp, data, data_len);
bool ok = qrcodegen_encodeBinary(data_tmp, data_len,
qr0, qrcodegen_Ecc_MEDIUM,
qr_version, qr_version,
qrcodegen_Mask_AUTO, true);
if(!ok) {
lv_free(qr0);
lv_free(data_tmp);
return LV_RESULT_INVALID;
}
/* Temporarily disable invalidation to improve the efficiency of lv_canvas_set_px */
lv_display_enable_invalidation(lv_obj_get_display(obj), false);
int32_t obj_w = draw_buf->header.w;
qr_size = qrcodegen_getSize(qr0);
scale = obj_w / qr_size;
int scaled = qr_size * scale;
int margin = (obj_w - scaled) / 2;
uint8_t * buf_u8 = (uint8_t *)draw_buf->data + 8; /*+8 skip the palette*/
lv_color_t c = lv_color_hex(1);
/* Copy the qr code canvas:
* A simple `lv_canvas_set_px` would work but it's slow for so many pixels.
* So buffer 1 byte (8 px) from the qr code and set it in the canvas image */
uint32_t row_byte_cnt = draw_buf->header.stride;
int y;
for(y = margin; y < scaled + margin; y += scale) {
uint8_t b = 0;
uint8_t p = 0;
bool aligned = false;
int x;
for(x = margin; x < scaled + margin; x++) {
bool a = qrcodegen_getModule(qr0, (x - margin) / scale, (y - margin) / scale);
if(aligned == false && (x & 0x7) == 0) aligned = true;
if(aligned == false) {
if(a) {
lv_canvas_set_px(obj, x, y, c, LV_OPA_COVER);
}
}
else {
if(!a) b |= (1 << (7 - p));
p++;
if(p == 8) {
uint32_t px = row_byte_cnt * y + (x >> 3);
buf_u8[px] = ~b;
b = 0;
p = 0;
}
}
}
/*Process the last byte of the row*/
if(p) {
/*Make the rest of the bits white*/
b |= (1 << (8 - p)) - 1;
uint32_t px = row_byte_cnt * y + (x >> 3);
buf_u8[px] = ~b;
}
/*The Qr is probably scaled so simply to the repeated rows*/
int s;
const uint8_t * row_ori = buf_u8 + row_byte_cnt * y;
for(s = 1; s < scale; s++) {
lv_memcpy((uint8_t *)buf_u8 + row_byte_cnt * (y + s), row_ori, row_byte_cnt);
}
}
/* invalidate the canvas to refresh it */
lv_display_enable_invalidation(lv_obj_get_display(obj), true);
lv_free(qr0);
lv_free(data_tmp);
return LV_RESULT_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
/*Set default size*/
lv_qrcode_set_size(obj, LV_DPI_DEF);
/*Set default color*/
lv_qrcode_set_dark_color(obj, lv_color_black());
lv_qrcode_set_light_color(obj, lv_color_white());
}
static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
if(draw_buf == NULL) return;
lv_image_cache_drop(draw_buf);
/*@fixme destroy buffer in cache free_cb.*/
lv_draw_buf_destroy(draw_buf);
}
#endif /*LV_USE_QRCODE*/
@@ -0,0 +1,85 @@
/**
* @file lv_qrcode.h
*
*/
#ifndef LV_QRCODE_H
#define LV_QRCODE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#include "../../misc/lv_color.h"
#include "../../misc/lv_types.h"
#include "../../widgets/canvas/lv_canvas.h"
#include LV_STDBOOL_INCLUDE
#include LV_STDINT_INCLUDE
#if LV_USE_QRCODE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_qrcode_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an empty QR code (an `lv_canvas`) object.
* @param parent point to an object where to create the QR code
* @return pointer to the created QR code object
*/
lv_obj_t * lv_qrcode_create(lv_obj_t * parent);
/**
* Set QR code size.
* @param obj pointer to a QR code object
* @param size width and height of the QR code
*/
void lv_qrcode_set_size(lv_obj_t * obj, int32_t size);
/**
* Set QR code dark color.
* @param obj pointer to a QR code object
* @param color dark color of the QR code
*/
void lv_qrcode_set_dark_color(lv_obj_t * obj, lv_color_t color);
/**
* Set QR code light color.
* @param obj pointer to a QR code object
* @param color light color of the QR code
*/
void lv_qrcode_set_light_color(lv_obj_t * obj, lv_color_t color);
/**
* Set the data of a QR code object
* @param obj pointer to a QR code object
* @param data data to display
* @param data_len length of data in bytes
* @return LV_RESULT_OK: if no error; LV_RESULT_INVALID: on error
*/
lv_result_t lv_qrcode_update(lv_obj_t * obj, const void * data, uint32_t data_len);
/**********************
* MACROS
**********************/
#endif /*LV_USE_QRCODE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_QRCODE_H*/
@@ -0,0 +1,52 @@
/**
* @file lv_qrcode_private.h
*
*/
#ifndef LV_QRCODE_PRIVATE_H
#define LV_QRCODE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../widgets/canvas/lv_canvas_private.h"
#include "lv_qrcode.h"
#if LV_USE_QRCODE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of qrcode*/
struct _lv_qrcode_t {
lv_canvas_t canvas;
lv_color_t dark_color;
lv_color_t light_color;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_QRCODE */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_QRCODE_PRIVATE_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,324 @@
/*
* QR Code generator library (C)
*
* Copyright (c) Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
#pragma once
#include "../../../lvgl.h"
#ifdef LV_USE_QRCODE
#include LV_STDBOOL_INCLUDE
#include LV_STDDEF_INCLUDE
#include LV_STDINT_INCLUDE
#ifdef __cplusplus
extern "C" {
#endif
/*
* This library creates QR Code symbols, which is a type of two-dimension barcode.
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
* A QR Code structure is an immutable square grid of black and white cells.
* The library provides functions to create a QR Code from text or binary data.
* The library covers the QR Code Model 2 specification, supporting all versions (sizes)
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
*
* Ways to create a QR Code object:
* - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary().
* - Low level: Custom-make the list of segments and call
* qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
* (Note that all ways require supplying the desired error correction level and various byte buffers.)
*/
/*---- Enum and struct types----*/
/*
* The error correction level in a QR Code symbol.
*/
enum qrcodegen_Ecc {
// Must be declared in ascending order of error protection
// so that an internal qrcodegen function works properly
qrcodegen_Ecc_LOW = 0, // The QR Code can tolerate about 7% erroneous codewords
qrcodegen_Ecc_MEDIUM, // The QR Code can tolerate about 15% erroneous codewords
qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
qrcodegen_Ecc_HIGH, // The QR Code can tolerate about 30% erroneous codewords
};
/*
* The mask pattern used in a QR Code symbol.
*/
enum qrcodegen_Mask {
// A special value to tell the QR Code encoder to
// automatically select an appropriate mask pattern
qrcodegen_Mask_AUTO = -1,
// The eight actual mask patterns
qrcodegen_Mask_0 = 0,
qrcodegen_Mask_1,
qrcodegen_Mask_2,
qrcodegen_Mask_3,
qrcodegen_Mask_4,
qrcodegen_Mask_5,
qrcodegen_Mask_6,
qrcodegen_Mask_7,
};
/*
* Describes how a segment's data bits are interpreted.
*/
enum qrcodegen_Mode {
qrcodegen_Mode_NUMERIC = 0x1,
qrcodegen_Mode_ALPHANUMERIC = 0x2,
qrcodegen_Mode_BYTE = 0x4,
qrcodegen_Mode_KANJI = 0x8,
qrcodegen_Mode_ECI = 0x7,
};
/*
* A segment of character/binary/control data in a QR Code symbol.
* The mid-level way to create a segment is to take the payload data
* and call a factory function such as qrcodegen_makeNumeric().
* The low-level way to create a segment is to custom-make the bit buffer
* and initialize a qrcodegen_Segment struct with appropriate values.
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
* Moreover, the maximum allowed bit length is 32767 because
* the largest QR Code (version 40) has 31329 modules.
*/
struct qrcodegen_Segment {
// The mode indicator of this segment.
enum qrcodegen_Mode mode;
// The length of this segment's unencoded data. Measured in characters for
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
// Always zero or positive. Not the same as the data's bit length.
int numChars;
// The data bits of this segment, packed in bitwise big endian.
// Can be null if the bit length is zero.
uint8_t * data;
// The number of valid data bits used in the buffer. Requires
// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
// The character count (numChars) must agree with the mode and the bit buffer length.
int bitLength;
};
/*---- Macro constants and functions ----*/
#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard
#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard
// Calculates the number of bytes needed to store any QR Code up to and including the given version number,
// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16).
// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX.
#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1)
// The worst-case number of bytes needed to store one QR Code, up to and including
// version 40. This value equals 3918, which is just under 4 kilobytes.
// Use this more convenient value to avoid calculating tighter memory bounds for buffers.
#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)
/*---- Functions (high level) to generate QR Codes ----*/
/*
* Encodes the given text string to a QR Code, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
* - The input text must be encoded in UTF-8 and contain no NULs.
* - The variables ecl and mask must correspond to enum constant values.
* - Requires 1 <= minVersion <= maxVersion <= 40.
* - The arrays tempBuffer and qrcode must each have a length
* of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
* - After the function returns, tempBuffer contains no useful data.
* - If successful, the resulting QR Code may use numeric,
* alphanumeric, or byte mode to encode the text.
* - In the most optimistic case, a QR Code at version 40 with low ECC
* can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
* up to 4296 characters, or any digit string up to 7089 characters.
* These numbers represent the hard upper limit of the QR Code standard.
* - Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeText(const char * text, uint8_t tempBuffer[], uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/*
* Encodes the given binary data to a QR Code, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
* - The input array range dataAndTemp[0 : dataLen] should normally be
* valid UTF-8 text, but is not required by the QR Code standard.
* - The variables ecl and mask must correspond to enum constant values.
* - Requires 1 <= minVersion <= maxVersion <= 40.
* - The arrays dataAndTemp and qrcode must each have a length
* of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
* - After the function returns, the contents of dataAndTemp may have changed,
* and does not represent useful data anymore.
* - If successful, the resulting QR Code will use byte mode to encode the data.
* - In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte
* sequence up to length 2953. This is the hard upper limit of the QR Code standard.
* - Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/*---- Functions (low level) to generate QR Codes ----*/
/*
* Renders a QR Code representing the given segments at the given error correction level.
* The smallest possible QR Code version is automatically chosen for the output. Returns true if
* QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level
* of the result may be higher than the ecl argument if it can be done without increasing the version.
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
* To save memory, the segments' data buffers can alias/overlap tempBuffer, and will
* result in them being clobbered, but the QR Code output will still be correct.
* But the qrcode array must not overlap tempBuffer or any segment's data buffer.
*/
bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]);
/*
* Renders a QR Code representing the given segments with the given encoding parameters.
* Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions.
* The smallest possible QR Code version within the given range is automatically
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
* may be higher than the ecl argument if it can be done without increasing the
* version. The mask number is either between 0 to 7 (inclusive) to force that
* mask, or -1 to automatically choose an appropriate mask (which may be slow).
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
* To save memory, the segments' data buffers can alias/overlap tempBuffer, and will
* result in them being clobbered, but the QR Code output will still be correct.
* But the qrcode array must not overlap tempBuffer or any segment's data buffer.
*/
bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl,
int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]);
/*
* Tests whether the given string can be encoded as a segment in alphanumeric mode.
* A string is encodable iff each character is in the following set: 0 to 9, A to Z
* (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
bool qrcodegen_isAlphanumeric(const char * text);
/*
* Tests whether the given string can be encoded as a segment in numeric mode.
* A string is encodable iff each character is in the range 0 to 9.
*/
bool qrcodegen_isNumeric(const char * text);
/*
* Returns the number of bytes (uint8_t) needed for the data buffer of a segment
* containing the given number of characters using the given mode. Notes:
* - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or
* the number of needed bits exceeds INT16_MAX (i.e. 32767).
* - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096.
* - It is okay for the user to allocate more bytes for the buffer than needed.
* - For byte mode, numChars measures the number of bytes, not Unicode code points.
* - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned.
* An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
*/
size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
/*
* Returns a segment representing the given binary data encoded in
* byte mode. All input byte arrays are acceptable. Any text string
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
*/
struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
/*
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
*/
struct qrcodegen_Segment qrcodegen_makeNumeric(const char * digits, uint8_t buf[]);
/*
* Returns a segment representing the given text string encoded in alphanumeric mode.
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char * text, uint8_t buf[]);
/*
* Returns a segment representing an Extended Channel Interpretation
* (ECI) designator with the given assignment value.
*/
struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
/*---- Functions to extract raw data from QR Codes ----*/
/*
* Returns the side length of the given QR Code, assuming that encoding succeeded.
* The result is in the range [21, 177]. Note that the length of the array buffer
* is related to the side length - every 'uint8_t qrcode[]' must have length at least
* qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
*/
int qrcodegen_getSize(const uint8_t qrcode[]);
/*
* Returns the color of the module (pixel) at the given coordinates, which is false
* for white or true for black. The top left corner has the coordinates (x=0, y=0).
* If the given coordinates are out of bounds, then false (white) is returned.
*/
bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
/*
* Returns the qrcode size of the specified version. Returns -1 on failure
*/
int qrcodegen_version2size(int version);
/*
* Returns the min version of the data that can be stored. Returns -1 on failure
*/
int qrcodegen_getMinFitVersion(enum qrcodegen_Ecc ecl, size_t dataLen);
#ifdef __cplusplus
}
#endif
#endif
+112
View File
@@ -0,0 +1,112 @@
/**
* @file lv_rle.c
*/
/*********************
* INCLUDES
*********************/
#include "../../stdlib/lv_string.h"
#include "lv_rle.h"
#if LV_USE_RLE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
uint32_t lv_rle_decompress(const uint8_t * input,
uint32_t input_buff_len, uint8_t * output,
uint32_t output_buff_len, uint8_t blk_size)
{
uint32_t ctrl_byte;
uint32_t rd_len = 0;
uint32_t wr_len = 0;
while(rd_len < input_buff_len) {
ctrl_byte = input[0];
rd_len++;
input++;
if(rd_len > input_buff_len)
return 0;
if(ctrl_byte & 0x80) {
/* copy directly from input to output */
uint32_t bytes = blk_size * (ctrl_byte & 0x7f);
rd_len += bytes;
if(rd_len > input_buff_len)
return 0;
wr_len += bytes;
if(wr_len > output_buff_len) {
if(wr_len > output_buff_len + blk_size)
return 0; /* Error */
lv_memcpy(output, input, output_buff_len - (wr_len - bytes));
return output_buff_len;
}
lv_memcpy(output, input, bytes);
output += bytes;
input += bytes;
}
else {
rd_len += blk_size;
if(rd_len > input_buff_len)
return 0;
wr_len += blk_size * ctrl_byte;
if(wr_len > output_buff_len) {
if(wr_len > output_buff_len + blk_size)
return 0; /* Error happened */
/* Skip the last pixel, which could overflow output buffer.*/
for(uint32_t i = 0; i < ctrl_byte - 1; i++) {
lv_memcpy(output, input, blk_size);
output += blk_size;
}
return output_buff_len;
}
if(blk_size == 1) {
/* optimize the most common case. */
lv_memset(output, input[0], ctrl_byte);
output += ctrl_byte;
}
else {
for(uint32_t i = 0; i < ctrl_byte; i++) {
lv_memcpy(output, input, blk_size);
output += blk_size;
}
}
input += blk_size;
}
}
return wr_len;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_RLE*/
@@ -0,0 +1,46 @@
/**
* @file lv_rle.h
*
*/
#ifndef LV_RLE_H
#define LV_RLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_RLE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
uint32_t lv_rle_decompress(const uint8_t * input,
uint32_t input_buff_len, uint8_t * output,
uint32_t output_buff_len, uint8_t blk_size);
/**********************
* MACROS
**********************/
#endif /*LV_USE_RLE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_RLE_H*/
@@ -0,0 +1,244 @@
/**
* @file lv_rlottie.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../lvgl.h"
#if LV_USE_RLOTTIE
#include "lv_rlottie_private.h"
#include "../../core/lv_obj_class_private.h"
#include <rlottie_capi.h>
#include <string.h>
/*********************
* DEFINES
*********************/
#define MY_CLASS (&lv_rlottie_class)
#define LV_ARGB32 32
/**********************
* TYPEDEFS
**********************/
#define LV_ARGB32 32
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void next_frame_task_cb(lv_timer_t * t);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_rlottie_class = {
.constructor_cb = lv_rlottie_constructor,
.destructor_cb = lv_rlottie_destructor,
.instance_size = sizeof(lv_rlottie_t),
.base_class = &lv_image_class,
.name = "lv_rlottie",
};
typedef struct {
int32_t width;
int32_t height;
const char * rlottie_desc;
const char * path;
} lv_rlottie_create_info_t;
/*Only used in lv_obj_class_create_obj, no affect multiple instances*/
static lv_rlottie_create_info_t create_info;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent, int32_t width, int32_t height, const char * path)
{
create_info.width = width;
create_info.height = height;
create_info.path = path;
create_info.rlottie_desc = NULL;
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, int32_t width, int32_t height, const char * rlottie_desc)
{
create_info.width = width;
create_info.height = height;
create_info.rlottie_desc = rlottie_desc;
create_info.path = NULL;
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_rlottie_set_play_mode(lv_obj_t * obj, const lv_rlottie_ctrl_t ctrl)
{
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
rlottie->play_ctrl = ctrl;
if(rlottie->task && (rlottie->dest_frame != rlottie->current_frame ||
(rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PLAY)) {
lv_timer_resume(rlottie->task);
}
}
void lv_rlottie_set_current_frame(lv_obj_t * obj, const size_t goto_frame)
{
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
rlottie->current_frame = goto_frame < rlottie->total_frames ? goto_frame : rlottie->total_frames - 1;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if(create_info.rlottie_desc) {
rlottie->animation = lottie_animation_from_data(create_info.rlottie_desc, create_info.rlottie_desc, "");
}
else if(create_info.path) {
rlottie->animation = lottie_animation_from_file(create_info.path);
}
if(rlottie->animation == NULL) {
LV_LOG_WARN("The animation can't be opened");
return;
}
rlottie->total_frames = lottie_animation_get_totalframe(rlottie->animation);
rlottie->framerate = (size_t)lottie_animation_get_framerate(rlottie->animation);
rlottie->current_frame = 0;
rlottie->scanline_width = create_info.width * LV_ARGB32 / 8;
size_t allocated_buf_size = (create_info.width * create_info.height * LV_ARGB32 / 8);
rlottie->allocated_buf = lv_malloc(allocated_buf_size);
if(rlottie->allocated_buf != NULL) {
rlottie->allocated_buffer_size = allocated_buf_size;
memset(rlottie->allocated_buf, 0, allocated_buf_size);
}
rlottie->imgdsc.header.cf = LV_COLOR_FORMAT_ARGB8888;
rlottie->imgdsc.header.h = create_info.height;
rlottie->imgdsc.header.w = create_info.width;
rlottie->imgdsc.data = (void *)rlottie->allocated_buf;
rlottie->imgdsc.data_size = allocated_buf_size;
lv_image_set_src(obj, &rlottie->imgdsc);
rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD | LV_RLOTTIE_CTRL_PLAY | LV_RLOTTIE_CTRL_LOOP;
rlottie->dest_frame = rlottie->total_frames; /* invalid destination frame so it's possible to pause on frame 0 */
rlottie->task = lv_timer_create(next_frame_task_cb, 1000 / rlottie->framerate, obj);
lv_obj_update_layout(obj);
}
static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if(rlottie->animation) {
lottie_animation_destroy(rlottie->animation);
rlottie->animation = 0;
rlottie->current_frame = 0;
rlottie->framerate = 0;
rlottie->scanline_width = 0;
rlottie->total_frames = 0;
}
if(rlottie->task) {
lv_timer_delete(rlottie->task);
rlottie->task = NULL;
rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD;
rlottie->dest_frame = 0;
}
lv_image_cache_drop(&rlottie->imgdsc);
if(rlottie->allocated_buf) {
lv_free(rlottie->allocated_buf);
rlottie->allocated_buf = NULL;
rlottie->allocated_buffer_size = 0;
}
}
static void next_frame_task_cb(lv_timer_t * t)
{
lv_obj_t * obj = lv_timer_get_user_data(t);
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PAUSE) {
if(rlottie->current_frame == rlottie->dest_frame) {
/* Pause the timer too when it has run once to avoid CPU consumption */
lv_timer_pause(t);
return;
}
rlottie->dest_frame = rlottie->current_frame;
}
else {
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_BACKWARD) == LV_RLOTTIE_CTRL_BACKWARD) {
if(rlottie->current_frame > 0)
--rlottie->current_frame;
else { /* Looping ? */
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
rlottie->current_frame = rlottie->total_frames - 1;
else {
lv_obj_send_event(obj, LV_EVENT_READY, NULL);
lv_timer_pause(t);
return;
}
}
}
else {
if(rlottie->current_frame < rlottie->total_frames)
++rlottie->current_frame;
else { /* Looping ? */
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
rlottie->current_frame = 0;
else {
lv_obj_send_event(obj, LV_EVENT_READY, NULL);
lv_timer_pause(t);
return;
}
}
}
}
lottie_animation_render(
rlottie->animation,
rlottie->current_frame,
rlottie->allocated_buf,
rlottie->imgdsc.header.w,
rlottie->imgdsc.header.h,
rlottie->scanline_width
);
lv_obj_invalidate(obj);
}
#endif /*LV_USE_RLOTTIE*/
@@ -0,0 +1,58 @@
/**
* @file lv_rlottie.h
*
*/
#ifndef LV_RLOTTIE_H
#define LV_RLOTTIE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_RLOTTIE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_RLOTTIE_CTRL_FORWARD = 0,
LV_RLOTTIE_CTRL_BACKWARD = 1,
LV_RLOTTIE_CTRL_PAUSE = 2,
LV_RLOTTIE_CTRL_PLAY = 0, /* Yes, play = 0 is the default mode */
LV_RLOTTIE_CTRL_LOOP = 8,
} lv_rlottie_ctrl_t;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_rlottie_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent, int32_t width, int32_t height, const char * path);
lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, int32_t width, int32_t height,
const char * rlottie_desc);
void lv_rlottie_set_play_mode(lv_obj_t * rlottie, const lv_rlottie_ctrl_t ctrl);
void lv_rlottie_set_current_frame(lv_obj_t * rlottie, const size_t goto_frame);
/**********************
* MACROS
**********************/
#endif /*LV_USE_RLOTTIE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_RLOTTIE_H*/
@@ -0,0 +1,61 @@
/**
* @file lv_rlottie_private.h
*
*/
#ifndef LV_RLOTTIE_PRIVATE_H
#define LV_RLOTTIE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_rlottie.h"
#if LV_USE_RLOTTIE
#include "../../widgets/image/lv_image_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** definition in lottieanimation_capi.c */
struct Lottie_Animation_S;
struct _lv_rlottie_t {
lv_image_t img_ext;
struct Lottie_Animation_S * animation;
lv_timer_t * task;
lv_image_dsc_t imgdsc;
size_t total_frames;
size_t current_frame;
size_t framerate;
uint32_t * allocated_buf;
size_t allocated_buffer_size;
size_t scanline_width;
lv_rlottie_ctrl_t play_ctrl;
size_t dest_frame;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_RLOTTIE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_RLOTTIE_PRIVATE_H*/
+124
View File
@@ -0,0 +1,124 @@
/**
* @file lv_svg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_svg.h"
#if LV_USE_SVG
#include "../../misc/lv_assert.h"
#include "../../misc/lv_log.h"
#include "../../stdlib/lv_mem.h"
#include "lv_svg_token.h"
#include "lv_svg_parser.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_svg_node_constructor(const lv_tree_class_t * class_p, lv_tree_node_t * node)
{
LV_UNUSED(class_p);
lv_svg_node_t * t = (lv_svg_node_t *)node;
t->xml_id = NULL;
t->type = LV_SVG_TAG_INVALID;
lv_array_init(&t->attrs, 4, sizeof(lv_svg_attr_t));
t->render_obj = NULL;
}
static void lv_svg_node_destructor(const lv_tree_class_t * class_p, lv_tree_node_t * node)
{
LV_UNUSED(class_p);
lv_svg_node_t * t = (lv_svg_node_t *)node;
if(t->xml_id) {
lv_free(t->xml_id);
}
for(uint32_t i = 0; i < lv_array_size(&t->attrs); i++) {
lv_svg_attr_t * attr = lv_array_at(&t->attrs, i);
if(attr->val_type == LV_SVG_ATTR_VALUE_PTR) {
lv_free(attr->value.val);
}
}
lv_array_deinit(&t->attrs);
}
static bool svg_token_process_cb(_lv_svg_token_t * token, void * data)
{
_lv_svg_parser_t * parser = (_lv_svg_parser_t *)data;
return _lv_svg_parser_token(parser, token);
}
/**********************
* STATIC VARIABLES
**********************/
const lv_tree_class_t lv_svg_node_class = {
.base_class = &lv_tree_node_class,
.instance_size = sizeof(lv_svg_node_t),
.constructor_cb = lv_svg_node_constructor,
.destructor_cb = lv_svg_node_destructor,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_svg_node_t * lv_svg_load_data(const char * svg_data, uint32_t data_len)
{
LV_ASSERT_NULL(svg_data);
LV_ASSERT(data_len > 0);
_lv_svg_parser_t parser;
_lv_svg_parser_init(&parser);
if(_lv_svg_tokenizer(svg_data, data_len, svg_token_process_cb, &parser)) {
if(_lv_svg_parser_is_finish(&parser)) {
lv_svg_node_t * doc = parser.doc_root;
parser.doc_root = NULL;
_lv_svg_parser_deinit(&parser);
#if LV_USE_SVG_DEBUG
_lv_svg_dump_tree(doc, 0);
#endif
return doc;
}
else {
_lv_svg_parser_deinit(&parser);
LV_LOG_ERROR("svg document parser raise errors!");
return NULL;
}
}
else {
_lv_svg_parser_deinit(&parser);
LV_LOG_ERROR("svg document tokenizer raise errors!");
return NULL;
}
}
lv_svg_node_t * lv_svg_node_create(lv_svg_node_t * parent)
{
lv_tree_node_t * node = lv_tree_node_create(&lv_svg_node_class, (lv_tree_node_t *)parent);
return (lv_svg_node_t *)node;
}
void lv_svg_node_delete(lv_svg_node_t * node)
{
lv_tree_node_delete((lv_tree_node_t *)node);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_SVG*/
+334
View File
@@ -0,0 +1,334 @@
/**
* @file lv_svg.h
*
*/
#ifndef LV_SVG_H
#define LV_SVG_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "../../misc/lv_array.h"
#include "../../misc/lv_tree.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_SVG_TAG_INVALID = -1,
LV_SVG_TAG_CONTENT,
LV_SVG_TAG_SVG,
LV_SVG_TAG_USE,
LV_SVG_TAG_G,
LV_SVG_TAG_PATH,
LV_SVG_TAG_RECT,
LV_SVG_TAG_CIRCLE,
LV_SVG_TAG_ELLIPSE,
LV_SVG_TAG_LINE,
LV_SVG_TAG_POLYLINE,
LV_SVG_TAG_POLYGON,
LV_SVG_TAG_SOLID_COLOR,
LV_SVG_TAG_LINEAR_GRADIENT,
LV_SVG_TAG_RADIAL_GRADIENT,
LV_SVG_TAG_STOP,
LV_SVG_TAG_DEFS,
LV_SVG_TAG_IMAGE,
#if LV_USE_SVG_ANIMATION
LV_SVG_TAG_MPATH,
LV_SVG_TAG_SET,
LV_SVG_TAG_ANIMATE,
LV_SVG_TAG_ANIMATE_COLOR,
LV_SVG_TAG_ANIMATE_TRANSFORM,
LV_SVG_TAG_ANIMATE_MOTION,
#endif
LV_SVG_TAG_TEXT,
LV_SVG_TAG_TSPAN,
LV_SVG_TAG_TEXT_AREA,
};
typedef int8_t lv_svg_tag_t;
enum {
LV_SVG_ATTR_INVALID = 0,
LV_SVG_ATTR_ID,
LV_SVG_ATTR_XML_ID,
LV_SVG_ATTR_VERSION,
LV_SVG_ATTR_BASE_PROFILE,
LV_SVG_ATTR_VIEWBOX,
LV_SVG_ATTR_PRESERVE_ASPECT_RATIO,
LV_SVG_ATTR_VIEWPORT_FILL,
LV_SVG_ATTR_VIEWPORT_FILL_OPACITY,
LV_SVG_ATTR_DISPLAY,
LV_SVG_ATTR_VISIBILITY,
LV_SVG_ATTR_X,
LV_SVG_ATTR_Y,
LV_SVG_ATTR_WIDTH,
LV_SVG_ATTR_HEIGHT,
LV_SVG_ATTR_RX,
LV_SVG_ATTR_RY,
LV_SVG_ATTR_CX,
LV_SVG_ATTR_CY,
LV_SVG_ATTR_R,
LV_SVG_ATTR_X1,
LV_SVG_ATTR_Y1,
LV_SVG_ATTR_X2,
LV_SVG_ATTR_Y2,
LV_SVG_ATTR_POINTS,
LV_SVG_ATTR_D,
LV_SVG_ATTR_PATH_LENGTH,
LV_SVG_ATTR_XLINK_HREF,
LV_SVG_ATTR_FILL,
LV_SVG_ATTR_FILL_RULE,
LV_SVG_ATTR_FILL_OPACITY,
LV_SVG_ATTR_STROKE,
LV_SVG_ATTR_STROKE_WIDTH,
LV_SVG_ATTR_STROKE_LINECAP,
LV_SVG_ATTR_STROKE_LINEJOIN,
LV_SVG_ATTR_STROKE_MITER_LIMIT,
LV_SVG_ATTR_STROKE_DASH_ARRAY,
LV_SVG_ATTR_STROKE_DASH_OFFSET,
LV_SVG_ATTR_STROKE_OPACITY,
LV_SVG_ATTR_OPACITY,
LV_SVG_ATTR_SOLID_COLOR,
LV_SVG_ATTR_SOLID_OPACITY,
LV_SVG_ATTR_GRADIENT_UNITS,
LV_SVG_ATTR_GRADIENT_STOP_OFFSET,
LV_SVG_ATTR_GRADIENT_STOP_COLOR,
LV_SVG_ATTR_GRADIENT_STOP_OPACITY,
LV_SVG_ATTR_FONT_FAMILY,
LV_SVG_ATTR_FONT_STYLE,
LV_SVG_ATTR_FONT_VARIANT,
LV_SVG_ATTR_FONT_WEIGHT,
LV_SVG_ATTR_FONT_SIZE,
LV_SVG_ATTR_TRANSFORM,
LV_SVG_ATTR_TEXT_ANCHOR,
#if LV_USE_SVG_ANIMATION
LV_SVG_ATTR_ATTRIBUTE_NAME,
LV_SVG_ATTR_ATTRIBUTE_TYPE,
LV_SVG_ATTR_BEGIN,
LV_SVG_ATTR_END,
LV_SVG_ATTR_DUR,
LV_SVG_ATTR_MIN,
LV_SVG_ATTR_MAX,
LV_SVG_ATTR_RESTART,
LV_SVG_ATTR_REPEAT_COUNT,
LV_SVG_ATTR_REPEAT_DUR,
LV_SVG_ATTR_CALC_MODE,
LV_SVG_ATTR_VALUES,
LV_SVG_ATTR_KEY_TIMES,
LV_SVG_ATTR_KEY_SPLINES,
LV_SVG_ATTR_KEY_POINTS,
LV_SVG_ATTR_FROM,
LV_SVG_ATTR_TO,
LV_SVG_ATTR_BY,
LV_SVG_ATTR_ADDITIVE,
LV_SVG_ATTR_ACCUMULATE,
LV_SVG_ATTR_PATH,
LV_SVG_ATTR_ROTATE,
LV_SVG_ATTR_TRANSFORM_TYPE,
#endif
};
typedef uint8_t lv_svg_attr_type_t;
enum {
LV_SVG_TRANSFORM_TYPE_MATRIX = 1,
LV_SVG_TRANSFORM_TYPE_TRANSLATE,
LV_SVG_TRANSFORM_TYPE_ROTATE,
LV_SVG_TRANSFORM_TYPE_SCALE,
LV_SVG_TRANSFORM_TYPE_SKEW_X,
LV_SVG_TRANSFORM_TYPE_SKEW_Y,
};
typedef uint8_t lv_svg_transform_type_t;
#if LV_USE_SVG_ANIMATION
enum {
LV_SVG_ANIM_REMOVE = 0,
LV_SVG_ANIM_FREEZE,
};
enum {
LV_SVG_ANIM_RESTART_ALWAYS = 0,
LV_SVG_ANIM_RESTART_WHEN_NOT_ACTIVE,
LV_SVG_ANIM_RESTART_NEVER,
};
enum {
LV_SVG_ANIM_CALC_MODE_LINEAR = 0,
LV_SVG_ANIM_CALC_MODE_PACED,
LV_SVG_ANIM_CALC_MODE_SPLINE,
LV_SVG_ANIM_CALC_MODE_DISCRETE,
};
enum {
LV_SVG_ANIM_ADDITIVE_REPLACE = 0,
LV_SVG_ANIM_ADDITIVE_SUM,
};
enum {
LV_SVG_ANIM_ACCUMULATE_NONE = 0,
LV_SVG_ANIM_ACCUMULATE_SUM,
};
#endif
enum {
LV_SVG_ASPECT_RATIO_NONE = 0,
LV_SVG_ASPECT_RATIO_XMIN_YMIN = (1 << 1),
LV_SVG_ASPECT_RATIO_XMID_YMIN = (2 << 1),
LV_SVG_ASPECT_RATIO_XMAX_YMIN = (3 << 1),
LV_SVG_ASPECT_RATIO_XMIN_YMID = (4 << 1),
LV_SVG_ASPECT_RATIO_XMID_YMID = (5 << 1),
LV_SVG_ASPECT_RATIO_XMAX_YMID = (6 << 1),
LV_SVG_ASPECT_RATIO_XMIN_YMAX = (7 << 1),
LV_SVG_ASPECT_RATIO_XMID_YMAX = (8 << 1),
LV_SVG_ASPECT_RATIO_XMAX_YMAX = (9 << 1),
};
enum {
LV_SVG_ASPECT_RATIO_OPT_MEET = 0,
LV_SVG_ASPECT_RATIO_OPT_SLICE,
};
typedef uint32_t lv_svg_aspect_ratio_t;
typedef struct {
float x;
float y;
} lv_svg_point_t;
typedef struct {
float m[3][3];
} lv_svg_matrix_t;
typedef uint32_t lv_svg_color_t;
enum {
LV_SVG_FILL_NONZERO = 0,
LV_SVG_FILL_EVENODD,
};
typedef uint8_t lv_svg_fill_rule_t;
enum {
LV_SVG_LINE_CAP_BUTT = 0,
LV_SVG_LINE_CAP_SQUARE,
LV_SVG_LINE_CAP_ROUND,
};
typedef uint8_t lv_svg_line_cap_t;
enum {
LV_SVG_LINE_JOIN_MITER = 0,
LV_SVG_LINE_JOIN_BEVEL,
LV_SVG_LINE_JOIN_ROUND,
};
typedef uint8_t lv_svg_line_join_t;
enum {
LV_SVG_GRADIENT_UNITS_OBJECT = 0,
LV_SVG_GRADIENT_UNITS_USER_SPACE,
};
typedef uint8_t lv_svg_gradient_units_t;
typedef union {
int32_t ival;
uint32_t uval;
float fval;
char * sval;
void * val;
} lv_svg_attr_value_t;
/*
* to simplify list buffer management, allocate enough memory for all data and length.
* | size | data[0] | data[1] | data[2] | ... |
*/
typedef struct {
uint32_t length;
uint8_t data[1];
} lv_svg_attr_values_list_t;
/* https://www.w3.org/TR/SVGTiny12/svgudomidl.html */
enum {
LV_SVG_PATH_CMD_MOVE_TO = 77,
LV_SVG_PATH_CMD_LINE_TO = 76,
LV_SVG_PATH_CMD_CURVE_TO = 67,
LV_SVG_PATH_CMD_QUAD_TO = 81,
LV_SVG_PATH_CMD_CLOSE = 90,
};
/*
* to simplify list buffer management, allocate enough memory for all path data and cmd.
* | cmd | data[0] | data[1] | data[2] | ... |
*/
typedef struct {
uint32_t cmd;
uint8_t data[1];
} lv_svg_attr_path_value_t;
enum {
LV_SVG_ATTR_VALUE_DATA = 0,
LV_SVG_ATTR_VALUE_PTR,
};
typedef uint8_t lv_svg_attr_value_type_t;
enum {
LV_SVG_ATTR_VALUE_NONE = 0,
LV_SVG_ATTR_VALUE_INITIAL,
LV_SVG_ATTR_VALUE_INHERIT,
};
typedef uint8_t lv_svg_attr_value_class_t;
typedef struct {
lv_svg_attr_type_t id;
lv_svg_attr_value_type_t val_type;
lv_svg_attr_value_class_t class_type;
lv_svg_attr_value_t value;
} lv_svg_attr_t;
struct _lv_svg_render_obj;
typedef struct {
lv_tree_node_t base;
char * xml_id; /* xml_id or content */
lv_svg_tag_t type;
lv_array_t attrs;
struct _lv_svg_render_obj * render_obj;
} lv_svg_node_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Loading SVG data and creating the DOM tree
* @param svg_data pointer to the SVG data
* @param data_len the SVG data length
*/
lv_svg_node_t * lv_svg_load_data(const char * svg_data, uint32_t data_len);
/**
* @brief Create an SVG DOM node
* @param parent pointer to the parent node
* @return true: an new SVG DOM node, false: NULL
*/
lv_svg_node_t * lv_svg_node_create(lv_svg_node_t * parent);
/**
* @brief Delete an SVG DOM subtree
* @param node pointer to an SVG DOM subtree
*/
void lv_svg_node_delete(lv_svg_node_t * node);
/**********************
* MACROS
**********************/
#define LV_SVG_NODE_CHILD(n, i) \
((lv_svg_node_t *)(LV_TREE_NODE((n))->children[i]))
#define LV_SVG_NODE(n) ((lv_svg_node_t*)(n))
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_H*/
@@ -0,0 +1,379 @@
/**
* @file lv_svg_decoder.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../draw/lv_image_decoder_private.h"
#include "../../../lvgl.h"
#if LV_USE_SVG
#include "lv_svg_decoder.h"
#include "lv_svg.h"
#include "../../draw/lv_draw_buf_private.h"
#include "../../display/lv_display_private.h"
/*********************
* DEFINES
*********************/
#define DECODER_NAME "SVG"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t svg_decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src,
lv_image_header_t * header);
static lv_result_t svg_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void svg_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static uint8_t * alloc_file(const char * filename, uint32_t * size);
static void svg_draw_buf_free(void * svg_buf);
static void svg_draw(lv_layer_t * layer, const lv_image_decoder_dsc_t * dsc, const lv_area_t * coords,
const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * clip_area);
/**********************
* STATIC VARIABLES
**********************/
static struct _lv_draw_buf_handlers_t _svg_draw_buf_handler = {
.buf_free_cb = svg_draw_buf_free,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register the SVG decoder functions in LVGL
*/
void lv_svg_decoder_init(void)
{
lv_image_decoder_t * dec = lv_image_decoder_create();
lv_image_decoder_set_info_cb(dec, svg_decoder_info);
lv_image_decoder_set_open_cb(dec, svg_decoder_open);
lv_image_decoder_set_close_cb(dec, svg_decoder_close);
dec->name = DECODER_NAME;
}
void lv_svg_decoder_deinit(void)
{
lv_image_decoder_t * dec = NULL;
while((dec = lv_image_decoder_get_next(dec)) != NULL) {
if(dec->info_cb == svg_decoder_info) {
lv_image_decoder_delete(dec);
break;
}
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static bool valid_svg_data(const uint8_t * data, uint32_t data_size)
{
return (data_size >= 4 && lv_memcmp(data, "<svg", 4) == 0)
|| (data_size >= 5 && lv_memcmp(data, "<?xml", 5) == 0);
}
static lv_result_t svg_decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src,
lv_image_header_t * header)
{
lv_image_src_t src_type = src->src_type;
int width = 0;
int height = 0;
if(src_type == LV_IMAGE_SRC_FILE || src_type == LV_IMAGE_SRC_VARIABLE) {
const void * src_data = src->src;
uint8_t * buf = NULL;
if(src_type == LV_IMAGE_SRC_FILE) {
/*Support only "*.svg" files*/
if(lv_strcmp(lv_fs_get_ext(src_data), "svg")) {
return LV_RESULT_INVALID;
}
uint32_t rn;
lv_fs_res_t res;
uint32_t file_size = 0;
res = lv_fs_seek(&src->file, 0, LV_FS_SEEK_END);
if(res == LV_FS_RES_OK) {
lv_fs_tell(&src->file, &file_size);
lv_fs_seek(&src->file, 0, LV_FS_SEEK_SET); /* Reset position to start */
}
#ifdef LV_USE_SVG_DEBUG
LV_LOG_INFO("LVGL file_size = %d.", file_size);
#endif
if(file_size > 512)
file_size = 512;
buf = (uint8_t *)lv_zalloc(file_size);
LV_ASSERT_NULL(buf);
/* read some bytes for searching svg header */
res = lv_fs_read(&src->file, buf, file_size, &rn);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("can't open %s", (char *)src_data);
lv_free(buf);
return LV_RESULT_INVALID;
}
if(!valid_svg_data(buf, rn)) {
lv_free(buf);
return LV_RESULT_INVALID;
}
width = LV_DPI_DEF;
height = LV_DPI_DEF;
uint8_t * svg_start = NULL;
uint8_t * svg_end = NULL;
uint8_t * ptr = buf;
uint8_t * ptr_end = buf + file_size - 1;
while(ptr < ptr_end) {
if(*ptr == '<') {
if(lv_strncmp((char *)(ptr + 1), "svg", 3) == 0) {
svg_start = ptr;
}
}
if(svg_start && (*ptr == '>')) {
svg_end = ptr;
break;
}
ptr++;
}
if(svg_start && svg_end) {
lv_svg_node_t * svg_doc = lv_svg_load_data((char *)svg_start, svg_end - svg_start);
lv_svg_render_obj_t * svg_header = lv_svg_render_create(svg_doc);
if(svg_header->tag == LV_SVG_TAG_SVG) {
lv_area_t bounds;
svg_header->clz->get_bounds(svg_header, &bounds);
width = lv_area_get_width(&bounds) - 1;
height = lv_area_get_height(&bounds) - 1;
}
lv_svg_render_delete(svg_header);
lv_svg_node_delete(svg_doc);
}
else
LV_LOG_WARN("can't find svg viewport tag end");
lv_free(buf);
}
else {
const lv_image_dsc_t * img_dsc = src_data;
uint32_t data_size = img_dsc->data_size;
width = img_dsc->header.w;
height = img_dsc->header.h;
if(!valid_svg_data(img_dsc->data, data_size)) {
return LV_RESULT_INVALID;
}
}
header->cf = LV_COLOR_FORMAT_ARGB8888;
header->w = width;
header->h = height;
header->flags |= LV_IMAGE_FLAGS_CUSTOM_DRAW;
decoder->custom_draw_cb = svg_draw;
return LV_RESULT_OK;
}
return LV_RESULT_INVALID;
}
static lv_result_t svg_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
LV_PROFILER_DECODER_BEGIN_TAG("lv_svg_decoder_open");
uint8_t * svg_data = NULL;
uint32_t svg_data_size = 0;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
if(lv_strcmp(lv_fs_get_ext(fn), "svg") == 0) { /*Check the extension*/
svg_data = alloc_file(fn, &svg_data_size);
if(svg_data == NULL) {
LV_LOG_WARN("can't load file: %s", (const char *)dsc->src);
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
}
else {
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = dsc->src;
svg_data = (uint8_t *)img_dsc->data;
svg_data_size = (uint32_t)img_dsc->data_size;
}
else {
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
lv_svg_node_t * svg_doc = lv_svg_load_data((char *)svg_data, svg_data_size);
lv_svg_render_obj_t * draw_list = lv_svg_render_create(svg_doc);
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
lv_free(svg_data);
}
lv_svg_node_delete(svg_doc);
/* create a fake draw_buf object */
lv_draw_buf_t * draw_buf = lv_zalloc(sizeof(lv_draw_buf_t));
draw_buf->header.w = 1;
draw_buf->header.h = 1;
draw_buf->header.cf = LV_COLOR_FORMAT_ARGB8888;
draw_buf->header.flags = LV_IMAGE_FLAGS_ALLOCATED | LV_IMAGE_FLAGS_CUSTOM_DRAW;
draw_buf->header.stride = 4;
draw_buf->header.magic = LV_IMAGE_HEADER_MAGIC;
draw_buf->data = NULL;
draw_buf->unaligned_data = (void *)draw_list;
draw_buf->data_size = lv_svg_render_get_size(draw_list);
draw_buf->handlers = &_svg_draw_buf_handler;
dsc->decoded = draw_buf;
if(!dsc->args.no_cache && lv_image_cache_is_enabled()) {
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = dsc->decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, draw_buf, NULL);
if(entry == NULL) {
lv_draw_buf_destroy(draw_buf);
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_INVALID;
}
dsc->cache_entry = entry;
}
LV_PROFILER_DECODER_END_TAG("lv_svg_decoder_open");
return LV_RESULT_OK;
}
static void svg_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
if(dsc->args.no_cache ||
!lv_image_cache_is_enabled()) lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
}
static uint8_t * alloc_file(const char * filename, uint32_t * size)
{
uint8_t * data = NULL;
lv_fs_file_t f;
uint32_t data_size;
uint32_t rn;
lv_fs_res_t res;
*size = 0;
res = lv_fs_open(&f, filename, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("can't open %s", filename);
return NULL;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_END);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_tell(&f, &data_size);
if(res != LV_FS_RES_OK) {
goto failed;
}
res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
goto failed;
}
/*Read file to buffer*/
data = lv_malloc(data_size);
if(data == NULL) {
LV_LOG_WARN("malloc failed for data size %u", data_size);
goto failed;
}
res = lv_fs_read(&f, data, data_size, &rn);
if(res == LV_FS_RES_OK && rn == data_size) {
*size = rn;
}
else {
LV_LOG_WARN("read file failed");
lv_free(data);
data = NULL;
}
failed:
lv_fs_close(&f);
return data;
}
static void svg_draw_buf_free(void * svg_buf)
{
lv_svg_render_obj_t * draw_list = (lv_svg_render_obj_t *)svg_buf;
lv_svg_render_delete(draw_list);
}
static void svg_draw(lv_layer_t * layer, const lv_image_decoder_dsc_t * dsc, const lv_area_t * coords,
const lv_draw_image_dsc_t * image_dsc, const lv_area_t * clip_area)
{
const lv_draw_buf_t * draw_buf = dsc->decoded;
const lv_svg_render_obj_t * list = draw_buf->unaligned_data;
LV_PROFILER_DRAW_BEGIN;
lv_vector_dsc_t * ctx = lv_vector_dsc_create(layer);
lv_matrix_t matrix;
lv_matrix_identity(&matrix);
lv_matrix_translate(&matrix, coords->x1, coords->y1);
ctx->current_dsc.scissor_area = *clip_area;
if(image_dsc) {
int32_t off_x = (lv_area_get_width(coords) - image_dsc->header.w - 1) / 2;
int32_t off_y = (lv_area_get_height(coords) - image_dsc->header.h - 1) / 2;
if(image_dsc->pivot.x != 0 || image_dsc->pivot.y != 0) {
lv_matrix_translate(&matrix, off_x, off_y);
}
lv_matrix_translate(&matrix, image_dsc->pivot.x, image_dsc->pivot.y);
lv_matrix_rotate(&matrix, image_dsc->rotation / 10.0f);
lv_matrix_scale(&matrix, image_dsc->scale_x / 256.0f, image_dsc->scale_y / 256.0f);
lv_matrix_translate(&matrix, -image_dsc->pivot.x, -image_dsc->pivot.y);
}
lv_vector_dsc_set_transform(ctx, &matrix);
lv_draw_svg_render(ctx, list);
lv_draw_vector(ctx);
lv_vector_dsc_delete(ctx);
LV_PROFILER_DRAW_END;
}
#endif /*LV_USE_SVG*/
@@ -0,0 +1,51 @@
/**
* @file lv_svg_decoder.h
*
*/
#ifndef LV_SVG_DECODER_H
#define LV_SVG_DECODER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "../../draw/lv_image_decoder.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register the SVG decoder functions in LVGL
*/
void lv_svg_decoder_init(void);
void lv_svg_decoder_deinit(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SVG_DECODER_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
/**
* @file lv_svg_parser.h
*
*/
#ifndef LV_SVG_PARSER_H
#define LV_SVG_PARSER_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "lv_svg.h"
#include "lv_svg_token.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_SVG_PARSER_PROCESS = 0,
LV_SVG_PARSER_IGNORE,
} _lv_svg_parser_state_t;
typedef struct {
uint16_t state;
char * ignore_name;
uint32_t ignore_len;
int32_t dpi;
lv_svg_node_t * doc_root;
lv_svg_node_t * cur_node;
} _lv_svg_parser_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Initialize the SVG parser
* @param parser pointer to a parser object
*/
void _lv_svg_parser_init(_lv_svg_parser_t * parser);
/**
* @brief Deinitialize the SVG parser
* @param parser pointer to a parser object
*/
void _lv_svg_parser_deinit(_lv_svg_parser_t * parser);
/**
* @brief Parse an SVG document
* @param parser pointer to a parser object
* @param token pointer to a token object
* @return true: the parsing is finished, false: the parsing is not finished yet.
*/
bool _lv_svg_parser_token(_lv_svg_parser_t * parser, const _lv_svg_token_t * token);
/**
* @brief Check if the parsing is finished
* @param parser pointer to a parser object
* @return true: the parsing is finished, false: the parsing is not finished yet.
*/
bool _lv_svg_parser_is_finish(_lv_svg_parser_t * parser);
/**
* @brief Dump the SVG tree
* @param root pointer to the root of the SVG tree
* @param depth the depth of the current node in the tree
*/
void _lv_svg_dump_tree(lv_svg_node_t * root, int depth);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_PARSER_H*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,125 @@
/**
* @file lv_svg_render.h
*
*/
#ifndef LV_SVG_RENDER_H
#define LV_SVG_RENDER_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#if !LV_USE_VECTOR_GRAPHIC
#error "LV_USE_SVG requires LV_USE_VECTOR_GRAPHIC = 1"
#endif
#include "lv_svg.h"
#include "../../misc/lv_types.h"
#include "../../draw/lv_draw_vector_private.h"
/*********************
* DEFINES
*********************/
#define LV_SVG_RENDER_OBJ(n) ((lv_svg_render_obj_t*)(n))
/**********************
* TYPEDEFS
**********************/
struct _lv_svg_render_class;
typedef struct _lv_svg_render_obj {
struct _lv_svg_render_obj * next;
lv_svg_tag_t tag;
uint32_t flags;
char * id;
lv_vector_draw_dsc_t dsc;
lv_matrix_t matrix;
/* for url(XXX) reference */
struct _lv_svg_render_obj * head;
char * fill_ref;
char * stroke_ref;
struct _lv_svg_render_class * clz;
} lv_svg_render_obj_t;
typedef struct _lv_svg_render_class {
void (*set_paint_ref)(struct _lv_svg_render_obj * obj, lv_vector_draw_dsc_t * dsc,
const struct _lv_svg_render_obj * target_obj, bool fill);
void (*init)(struct _lv_svg_render_obj * obj, const lv_svg_node_t * node);
void (*render)(const struct _lv_svg_render_obj * obj, lv_vector_dsc_t * dsc, const lv_matrix_t * matrix);
void (*set_attr)(struct _lv_svg_render_obj * obj, lv_vector_draw_dsc_t * dsc, const lv_svg_attr_t * attr);
void (*get_bounds)(const struct _lv_svg_render_obj * obj, lv_area_t * area);
void (*get_size)(const struct _lv_svg_render_obj * obj, uint32_t * size);
void (*destroy)(struct _lv_svg_render_obj * obj);
} lv_svg_render_class;
typedef struct _lv_svg_render_hal {
void (*load_image)(const char * image_url, lv_draw_image_dsc_t * img_dsc);
const char * (*get_font_path)(const char * font_family);
} lv_svg_render_hal_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Initialize the SVG render
* @param hal pointer to a structure with rendering functions
*/
void lv_svg_render_init(const lv_svg_render_hal_t * hal);
/**
* @brief Create a new SVG render from an SVG document
* @param svg_doc pointer to the SVG document
* @return pointer to the new SVG render object
*/
lv_svg_render_obj_t * lv_svg_render_create(const lv_svg_node_t * svg_doc);
/**
* @brief Delete an SVG render object
* @param render pointer to the SVG render object to delete
*/
void lv_svg_render_delete(lv_svg_render_obj_t * render);
/**
* @brief Get size of render objects
* @param render pointer to the SVG render object
* @return the bytes of SVG render objects
*/
uint32_t lv_svg_render_get_size(const lv_svg_render_obj_t * render);
/**
* @brief Get viewport's width and height of the render object
* @param render pointer to the SVG render object
* @param width pointer to save the width of the viewport of the SVG render object
* @param height pointer to save the height of the viewport of the SVG render object
* @return lv_result_t, LV_RESULT_OK if success, LV_RESULT_INVALID if fail
*/
lv_result_t lv_svg_render_get_viewport_size(const lv_svg_render_obj_t * render, float * width, float * height);
/**
* @brief Render an SVG object to a vector graphics
* @param dsc pointer to the vector graphics descriptor
* @param render pointer to the SVG render object to render
*/
void lv_draw_svg_render(lv_vector_dsc_t * dsc, const lv_svg_render_obj_t * render);
/**
* @brief Draw an SVG document to a layer
* @param layer pointer to the target layer
* @param svg_doc pointer to the SVG document to draw
*/
void lv_draw_svg(lv_layer_t * layer, const lv_svg_node_t * svg_doc);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_RENDER_H*/
@@ -0,0 +1,483 @@
/**
* @file lv_svg_token.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_svg_token.h"
#if LV_USE_SVG
#include "../../../lvgl.h"
#include <ctype.h>
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*
* tag mask quote mask tag search comment doc type xml inst
* | 0 0 0 | 0 0 | 0 | 0 | 0 | 0 | 0 |
*/
enum {
SVG_TAG_MASK = (1 << 3) - 1,
SVG_QUOTE_MASK = (1 << 5) - (1 << 3),
SVG_TAG = 1 << 5,
SVG_SEARCH = 1 << 6,
SVG_COMMENT = 1 << 7,
SVG_DOCTYPE = 1 << 8,
SVG_XMLINST = 1 << 9,
};
typedef uint32_t _lv_svg_parser_bits_t;
enum {
SVG_NO_QUOTE = 0,
SVG_SINGLE_QUOTE = 1,
SVG_DOUBLE_QUOTE = 2,
};
typedef uint32_t _lv_svg_parser_quote_t;
enum {
SVG_NO_TAG = 0,
SVG_TAG_NAME = 1,
SVG_ATTR_START = 2,
SVG_ATTR_NAME = 3,
SVG_SEARCH_EQUAL = 4,
SVG_SEARCH_VALUE = 5,
SVG_QUOTE_VALUE = 6,
SVG_VALUE = 7,
};
typedef uint32_t _lv_svg_parser_tag_state_t;
typedef struct {
uint32_t flags;
const char * cur;
const char * end;
} _lv_svg_parser_state_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void _set_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags |= bit;
}
static void _clear_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags &= ~bit;
}
static bool _is_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
return state->flags & bit;
}
static void _set_tag_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags = (state->flags & ~SVG_TAG_MASK) | bit;
}
static void _set_quote_state(_lv_svg_parser_state_t * state, uint32_t bit)
{
state->flags = (state->flags & ~SVG_QUOTE_MASK) | (bit << 3);
}
static bool _special_handle(_lv_svg_parser_state_t * state)
{
return state->flags & (SVG_TAG | SVG_SEARCH | SVG_TAG_MASK | SVG_COMMENT | SVG_DOCTYPE | SVG_XMLINST);
}
static void _lv_svg_token_init(_lv_svg_token_t * token)
{
token->start = NULL;
token->end = NULL;
token->type = LV_SVG_TOKEN_CONTENT;
token->flat = false;
token->cur_attr = NULL;
lv_array_init(&token->attrs, LV_ARRAY_DEFAULT_CAPACITY, sizeof(_lv_svg_token_attr_t));
}
static void _lv_svg_token_reset(_lv_svg_token_t * token)
{
token->start = NULL;
token->end = NULL;
token->type = LV_SVG_TOKEN_CONTENT;
token->flat = false;
token->cur_attr = NULL;
lv_array_clear(&token->attrs);
}
static bool _lv_svg_token_process(_lv_svg_token_t * token, svg_token_process cb, void * data)
{
if(!token->start || SVG_TOKEN_LEN(token) == 0)
return true;
bool ret = cb(token, data);
_lv_svg_token_reset(token);
return ret;
}
static _lv_svg_token_attr_t * _new_svg_attr(_lv_svg_token_t * token)
{
if((lv_array_size(&token->attrs) + 1) > lv_array_capacity(&token->attrs)) {
lv_array_resize(&token->attrs, token->attrs.capacity << 1);
}
token->attrs.size++;
_lv_svg_token_attr_t * attr = lv_array_at(&token->attrs, token->attrs.size - 1);
lv_memset(attr, 0, sizeof(_lv_svg_token_attr_t));
return attr;
}
static void _svg_parser_xml_inst(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
{
LV_UNUSED(token);
while(state->cur <= state->end) {
char ch = *(state->cur);
if(ch == '>' && (*(state->cur - 1)) == '?') {
_clear_state(state, SVG_XMLINST);
state->cur++;
break;
}
state->cur++;
}
}
static void _svg_parser_comment(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
{
LV_UNUSED(token);
while(state->cur <= state->end) {
char ch = *(state->cur);
if(ch == '>' && (*(state->cur - 1)) == '-' && (*(state->cur - 2)) == '-') {
_clear_state(state, SVG_COMMENT);
state->cur++;
break;
}
state->cur++;
}
}
static void _svg_parser_doctype(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
{
LV_UNUSED(token);
//TODO: processing DTD type
while(state->cur <= state->end) {
char ch = *(state->cur);
if(ch == '>') {
_clear_state(state, SVG_DOCTYPE);
state->cur++;
break;
}
state->cur++;
}
}
static bool _svg_parser_tag(_lv_svg_parser_state_t * state, _lv_svg_token_t * token, svg_token_process cb, void * data)
{
while(state->cur <= state->end) {
switch(state->flags & SVG_TAG_MASK) {
case SVG_NO_TAG: {
if(!_lv_svg_token_process(token, cb, data)) {
return false;
}
state->cur++;
}
return true;
case SVG_TAG_NAME: {
char ch = *(state->cur);
if(ch == '/') {
token->type = LV_SVG_TOKEN_END;
state->cur++;
if(!token->start) {
token->start = state->cur;
}
continue;
}
else if(ch == '>' || isspace(ch)) {
token->end = state->cur;
_set_tag_state(state, SVG_ATTR_START);
continue;
}
else {
if(!token->start) {
token->type = LV_SVG_TOKEN_BEGIN;
token->start = state->cur;
}
state->cur++;
continue;
}
}
break;
case SVG_ATTR_START: {
char ch = *(state->cur);
if(!isspace(ch) && ch != '\'' && ch != '\"') {
if(ch == '/') {
token->flat = true;
state->cur++;
continue;
}
if(ch == '>') {
_set_tag_state(state, SVG_NO_TAG);
}
else {
token->cur_attr = NULL;
_set_tag_state(state, SVG_ATTR_NAME);
}
continue;
}
}
break;
case SVG_ATTR_NAME: {
if(!token->cur_attr) {
token->cur_attr = _new_svg_attr(token);
}
char ch = *(state->cur);
if(isspace(ch) || ch == '=' || ch == '/' || ch == '>') {
token->cur_attr->name_end = state->cur;
_set_tag_state(state, SVG_SEARCH_EQUAL);
continue;
}
else {
if(!token->cur_attr->name_start) {
token->cur_attr->name_start = state->cur;
}
state->cur++;
continue;
}
}
break;
case SVG_SEARCH_EQUAL: {
char ch = *(state->cur);
if(!isspace(ch) && ch != '/' && ch != '\'' && ch != '\"') {
if(ch == '=') {
_set_tag_state(state, SVG_SEARCH_VALUE);
}
else {
// attr name has empty value
token->cur_attr = NULL;
_set_tag_state(state, SVG_ATTR_START);
continue;
}
}
}
break;
case SVG_SEARCH_VALUE: {
char ch = *(state->cur);
if(!isspace(ch)) {
if(ch == '\'' || ch == '\"') {
if(ch == '\'') {
_set_quote_state(state, SVG_SINGLE_QUOTE);
}
else {
_set_quote_state(state, SVG_DOUBLE_QUOTE);
}
_set_tag_state(state, SVG_QUOTE_VALUE);
}
else {
_set_tag_state(state, SVG_VALUE);
continue;
}
}
}
break;
case SVG_QUOTE_VALUE: {
char ch = *(state->cur);
if((ch == '\'' && ((state->flags & SVG_QUOTE_MASK) >> 3) == SVG_SINGLE_QUOTE)
|| (ch == '\"' && ((state->flags & SVG_QUOTE_MASK) >> 3) == SVG_DOUBLE_QUOTE)) {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
token->cur_attr->value_end = state->cur;
_set_quote_state(state, SVG_NO_QUOTE);
_set_tag_state(state, SVG_ATTR_START);
continue;
}
else {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
state->cur++;
continue;
}
}
break;
case SVG_VALUE: {
char ch = *(state->cur);
if(isspace(ch) || ch == '>' || ch == '/') {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
token->cur_attr->value_end = state->cur;
_set_quote_state(state, SVG_NO_QUOTE);
_set_tag_state(state, SVG_ATTR_START);
continue;
}
else {
if(!token->cur_attr->value_start) {
token->cur_attr->value_start = state->cur;
}
state->cur++;
continue;
}
}
break;
}
state->cur++;
}
return true;
}
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool _lv_svg_tokenizer(const char * svg_data, uint32_t data_len, svg_token_process cb, void * data)
{
LV_ASSERT_NULL(svg_data);
LV_ASSERT(data_len > 0);
LV_ASSERT_NULL(cb);
LV_ASSERT_NULL(data);
_lv_svg_token_t token;
_lv_svg_token_init(&token);
_lv_svg_parser_state_t state = {
.flags = 0,
.cur = svg_data,
.end = svg_data + data_len,
};
while(state.cur < state.end) {
char ch = *(state.cur);
if(ch == '\r' || ch == '\n') { // skip LR character
state.cur++;
continue;
}
else if(_special_handle(&state)) {
if(_is_state(&state, SVG_TAG)) {
_clear_state(&state, SVG_TAG);
switch(ch) {
case '/': // end tag
_set_tag_state(&state, SVG_TAG_NAME);
break;
case '!': {
// <!-- comment or <!DOCTYPE>
_set_state(&state, SVG_SEARCH); // get more character
state.cur++;
}
break;
case '?': {
// xml instruction
_set_state(&state, SVG_XMLINST);
state.cur++;
}
break;
default: {
if(isalpha(ch)) {
_set_tag_state(&state, SVG_TAG_NAME);
}
else {
LV_LOG_ERROR("svg document parser error!");
lv_array_deinit(&token.attrs);
return false;
}
}
}
// process token
if(!_lv_svg_token_process(&token, cb, data)) {
LV_LOG_ERROR("svg document parser error!");
lv_array_deinit(&token.attrs);
return false;
}
}
else if(_is_state(&state, SVG_SEARCH)) {
if(ch == '-' || isalpha(ch)) {
if(!token.start) {
token.start = state.cur;
}
token.end = state.cur;
}
else {
// processing as a normal tag name.
_clear_state(&state, SVG_SEARCH);
_set_tag_state(&state, SVG_TAG_NAME);
continue;
}
if(((token.end - token.start) == 1) && (token.start[0] == '-') && (token.start[1] == '-')) {
// is <!-- comment start
_clear_state(&state, SVG_SEARCH);
token.start = token.end = NULL;
_set_state(&state, SVG_COMMENT);
}
else if(((token.end - token.start) == 6) && (strncmp(token.start, "DOCTYPE", 7) == 0)) {
_clear_state(&state, SVG_SEARCH);
token.start = token.end = NULL;
_set_state(&state, SVG_DOCTYPE);
}
state.cur++;
}
else if(_is_state(&state, SVG_COMMENT)) {
_svg_parser_comment(&state, &token);
}
else if(_is_state(&state, SVG_DOCTYPE)) {
_svg_parser_doctype(&state, &token);
}
else if(_is_state(&state, SVG_TAG_MASK)) {
if(!_svg_parser_tag(&state, &token, cb, data)) {
LV_LOG_ERROR("svg document parser error!");
lv_array_deinit(&token.attrs);
return false;
}
}
else if(_is_state(&state, SVG_XMLINST)) {
_svg_parser_xml_inst(&state, &token);
}
}
else {
switch(ch) {
case '<': {
_set_state(&state, SVG_TAG); // start a new tag
state.cur++;
}
break;
default: {
if(!token.start) {
token.start = state.cur;
}
if(state.cur == state.end) {
goto finish;
}
token.end = ++state.cur;
}
}
}
}
finish:
lv_array_deinit(&token.attrs);
return true;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_SVG*/
@@ -0,0 +1,70 @@
/**
* @file lv_svg_token.h
*
*/
#ifndef LV_SVG_TOKEN_H
#define LV_SVG_TOKEN_H
/*********************
* INCLUDES
*********************/
#include "../../lv_conf_internal.h"
#if LV_USE_SVG
#include "../../misc/lv_array.h"
/*********************
* DEFINES
*********************/
#define SVG_TOKEN_LEN(t) ((t)->end - (t)->start)
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_SVG_TOKEN_BEGIN = 0,
LV_SVG_TOKEN_END,
LV_SVG_TOKEN_CONTENT,
} _lv_svg_token_type_t;
typedef struct {
const char * name_start;
const char * name_end;
const char * value_start;
const char * value_end;
} _lv_svg_token_attr_t;
typedef struct {
const char * start;
const char * end;
_lv_svg_token_type_t type;
bool flat;
_lv_svg_token_attr_t * cur_attr;
lv_array_t attrs;
} _lv_svg_token_t;
typedef bool (*svg_token_process)(_lv_svg_token_t * token, void * user_data);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Parse SVG data and call a callback for each token
* @param svg_data pointer to SVG data
* @param len length of the SVG data
* @param cb callback function to be called for each token
* @param user_data custom data to be passed to the callback function
* @return true: SVG data successfully parsed, false: error occurred
*/
bool _lv_svg_tokenizer(const char * svg_data, uint32_t len, svg_token_process cb, void * user_data);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SVG*/
#endif /*LV_SVG_TOKEN_H*/
@@ -0,0 +1,7 @@
Copyright (c) 2020 - 2025 notice for the ThorVG Project (see CONTRIBUTORS)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
#!/bin/bash
#Add LVGL #if LV_USE_THORVG_INTERNAL guard
#Usage
# find -name "*.cpp" | xargs ./add_lvgl_if.sh
# find -name "t*.h" | xargs ./add_lvgl_if.sh
sed '0,/\*\/$/ {/\*\/$/ {n; s|^|\n#include "../../lv_conf_internal.h"\n#if LV_USE_THORVG_INTERNAL\n|}}' $@ -i
sed -i -e '$a\
\
#endif /* LV_USE_THORVG_INTERNAL */\
' $@ -i
@@ -0,0 +1,700 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ALLOCATORS_H_
#define RAPIDJSON_ALLOCATORS_H_
#include "rapidjson.h"
#include "internal/meta.h"
#include <memory>
#include <limits>
#if RAPIDJSON_HAS_CXX11
#include <type_traits>
#endif
#include "../../../misc/lv_assert.h"
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Allocator
/*! \class rapidjson::Allocator
\brief Concept for allocating, resizing and freeing memory block.
Note that Malloc() and Realloc() are non-static but Free() is static.
So if an allocator need to support Free(), it needs to put its pointer in
the header of memory block.
\code
concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block.
// \param size of the memory block in bytes.
// \returns pointer to the memory block.
void* Malloc(size_t size);
// Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block.
// \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr);
};
\endcode
*/
/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
\ingroup RAPIDJSON_CONFIG
\brief User-defined kDefaultChunkCapacity definition.
User can define this as any \c size that is a power of 2.
*/
#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
#endif
///////////////////////////////////////////////////////////////////////////////
// CrtAllocator
//! C-runtime library allocator.
/*! This class is just wrapper for standard C library memory routines.
\note implements Allocator concept
*/
class CrtAllocator {
public:
static const bool kNeedFree = true;
void* Malloc(size_t size) {
if (size) { // behavior of malloc(0) is implementation defined.
void * p = RAPIDJSON_MALLOC(size);
LV_ASSERT_MALLOC(p);
return p;
} else {
return NULL; // standardize to returning NULL.
}
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
(void)originalSize;
if (newSize == 0) {
RAPIDJSON_FREE(originalPtr);
return NULL;
}
void * p = RAPIDJSON_REALLOC(originalPtr, newSize);
LV_ASSERT_MALLOC(p);
return p;
}
static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); }
bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
return true;
}
bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
return false;
}
};
///////////////////////////////////////////////////////////////////////////////
// MemoryPoolAllocator
//! Default memory allocator used by the parser and DOM.
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
It does not free memory blocks. And Realloc() only allocate new memory.
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
User may also supply a buffer as the first chunk.
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
The user-buffer is not deallocated by this allocator.
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
\note implements Allocator concept
*/
template <typename BaseAllocator = CrtAllocator>
class MemoryPoolAllocator {
//! Chunk header for perpending to each chunk.
/*! Chunks are stored as a singly linked list.
*/
struct ChunkHeader {
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
size_t size; //!< Current size of allocated memory in bytes.
ChunkHeader *next; //!< Next chunk in the linked list.
};
struct SharedData {
ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
BaseAllocator* ownBaseAllocator; //!< base allocator created by this object.
size_t refcount;
bool ownBuffer;
};
static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData));
static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader));
static inline ChunkHeader *GetChunkHead(SharedData *shared)
{
return reinterpret_cast<ChunkHeader*>(reinterpret_cast<uint8_t*>(shared) + SIZEOF_SHARED_DATA);
}
static inline uint8_t *GetChunkBuffer(SharedData *shared)
{
return reinterpret_cast<uint8_t*>(shared->chunkHead) + SIZEOF_CHUNK_HEADER;
}
static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
public:
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy
//! Constructor with chunkSize.
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
explicit
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunk_capacity_(chunkSize),
baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()),
shared_(static_cast<SharedData*>(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0))
{
RAPIDJSON_ASSERT(baseAllocator_ != 0);
RAPIDJSON_ASSERT(shared_ != 0);
if (baseAllocator) {
shared_->ownBaseAllocator = 0;
}
else {
shared_->ownBaseAllocator = baseAllocator_;
}
shared_->chunkHead = GetChunkHead(shared_);
shared_->chunkHead->capacity = 0;
shared_->chunkHead->size = 0;
shared_->chunkHead->next = 0;
shared_->ownBuffer = true;
shared_->refcount = 1;
}
//! Constructor with user-supplied buffer.
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
The user buffer will not be deallocated when this allocator is destructed.
\param buffer User supplied buffer.
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunk_capacity_(chunkSize),
baseAllocator_(baseAllocator),
shared_(static_cast<SharedData*>(AlignBuffer(buffer, size)))
{
RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER);
shared_->chunkHead = GetChunkHead(shared_);
shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER;
shared_->chunkHead->size = 0;
shared_->chunkHead->next = 0;
shared_->ownBaseAllocator = 0;
shared_->ownBuffer = false;
shared_->refcount = 1;
}
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT :
chunk_capacity_(rhs.chunk_capacity_),
baseAllocator_(rhs.baseAllocator_),
shared_(rhs.shared_)
{
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
++shared_->refcount;
}
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT
{
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
++rhs.shared_->refcount;
this->~MemoryPoolAllocator();
baseAllocator_ = rhs.baseAllocator_;
chunk_capacity_ = rhs.chunk_capacity_;
shared_ = rhs.shared_;
return *this;
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT :
chunk_capacity_(rhs.chunk_capacity_),
baseAllocator_(rhs.baseAllocator_),
shared_(rhs.shared_)
{
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
rhs.shared_ = 0;
}
MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT
{
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
this->~MemoryPoolAllocator();
baseAllocator_ = rhs.baseAllocator_;
chunk_capacity_ = rhs.chunk_capacity_;
shared_ = rhs.shared_;
rhs.shared_ = 0;
return *this;
}
#endif
//! Destructor.
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
*/
~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT {
if (!shared_) {
// do nothing if moved
return;
}
if (shared_->refcount > 1) {
--shared_->refcount;
return;
}
Clear();
BaseAllocator *a = shared_->ownBaseAllocator;
if (shared_->ownBuffer) {
baseAllocator_->Free(shared_);
}
RAPIDJSON_DELETE(a);
}
//! Deallocates all memory chunks, excluding the first/user one.
void Clear() RAPIDJSON_NOEXCEPT {
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
for (;;) {
ChunkHeader* c = shared_->chunkHead;
if (!c->next) {
break;
}
shared_->chunkHead = c->next;
baseAllocator_->Free(c);
}
shared_->chunkHead->size = 0;
}
//! Computes the total capacity of allocated memory chunks.
/*! \return total capacity in bytes.
*/
size_t Capacity() const RAPIDJSON_NOEXCEPT {
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
size_t capacity = 0;
for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
capacity += c->capacity;
return capacity;
}
//! Computes the memory blocks allocated.
/*! \return total used bytes.
*/
size_t Size() const RAPIDJSON_NOEXCEPT {
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
size_t size = 0;
for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
size += c->size;
return size;
}
//! Whether the allocator is shared.
/*! \return true or false.
*/
bool Shared() const RAPIDJSON_NOEXCEPT {
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
return shared_->refcount > 1;
}
//! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) {
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
if (!size)
return NULL;
size = RAPIDJSON_ALIGN(size);
if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity))
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
return NULL;
void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size;
shared_->chunkHead->size += size;
return buffer;
}
//! Resizes a memory block (concept Allocator)
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
if (originalPtr == 0)
return Malloc(newSize);
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
if (newSize == 0)
return NULL;
originalSize = RAPIDJSON_ALIGN(originalSize);
newSize = RAPIDJSON_ALIGN(newSize);
// Do not shrink if new size is smaller than original
if (originalSize >= newSize)
return originalPtr;
// Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize);
if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) {
shared_->chunkHead->size += increment;
return originalPtr;
}
}
// Realloc process: allocate and copy memory, do not free original buffer.
if (void* newBuffer = Malloc(newSize)) {
if (originalSize)
std::memcpy(newBuffer, originalPtr, originalSize);
return newBuffer;
}
else
return NULL;
}
//! Frees a memory block (concept Allocator)
static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing
//! Compare (equality) with another MemoryPoolAllocator
bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
return shared_ == rhs.shared_;
}
//! Compare (inequality) with another MemoryPoolAllocator
bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
return !operator==(rhs);
}
private:
//! Creates a new chunk.
/*! \param capacity Capacity of the chunk in bytes.
\return true if success.
*/
bool AddChunk(size_t capacity) {
if (!baseAllocator_)
shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
if (ChunkHeader* chunk = static_cast<ChunkHeader*>(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) {
chunk->capacity = capacity;
chunk->size = 0;
chunk->next = shared_->chunkHead;
shared_->chunkHead = chunk;
return true;
}
else
return false;
}
static inline void* AlignBuffer(void* buf, size_t &size)
{
RAPIDJSON_NOEXCEPT_ASSERT(buf != 0);
const uintptr_t mask = sizeof(void*) - 1;
const uintptr_t ubuf = reinterpret_cast<uintptr_t>(buf);
if (RAPIDJSON_UNLIKELY(ubuf & mask)) {
const uintptr_t abuf = (ubuf + mask) & ~mask;
RAPIDJSON_ASSERT(size >= abuf - ubuf);
buf = reinterpret_cast<void*>(abuf);
size -= abuf - ubuf;
}
return buf;
}
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
SharedData *shared_; //!< The shared data of the allocator
};
namespace internal {
template<typename, typename = void>
struct IsRefCounted :
public FalseType
{ };
template<typename T>
struct IsRefCounted<T, typename internal::EnableIfCond<T::kRefCounted>::Type> :
public TrueType
{ };
}
template<typename T, typename A>
inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n)
{
RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits<size_t>::max)() / sizeof(T) && new_n <= (std::numeric_limits<size_t>::max)() / sizeof(T));
return static_cast<T*>(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T)));
}
template<typename T, typename A>
inline T *Malloc(A& a, size_t n = 1)
{
return Realloc<T, A>(a, NULL, 0, n);
}
template<typename T, typename A>
inline void Free(A& a, T *p, size_t n = 1)
{
static_cast<void>(Realloc<T, A>(a, p, n, 0));
}
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited
#endif
template <typename T, typename BaseAllocator = CrtAllocator>
class StdAllocator :
public std::allocator<T>
{
typedef std::allocator<T> allocator_type;
#if RAPIDJSON_HAS_CXX11
typedef std::allocator_traits<allocator_type> traits_type;
#else
typedef allocator_type traits_type;
#endif
public:
typedef BaseAllocator BaseAllocatorType;
StdAllocator() RAPIDJSON_NOEXCEPT :
allocator_type(),
baseAllocator_()
{ }
StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
allocator_type(rhs),
baseAllocator_(rhs.baseAllocator_)
{ }
template<typename U>
StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
allocator_type(rhs),
baseAllocator_(rhs.baseAllocator_)
{ }
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT :
allocator_type(std::move(rhs)),
baseAllocator_(std::move(rhs.baseAllocator_))
{ }
#endif
#if RAPIDJSON_HAS_CXX11
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
#endif
/* implicit */
StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT :
allocator_type(),
baseAllocator_(allocator)
{ }
~StdAllocator() RAPIDJSON_NOEXCEPT
{ }
template<typename U>
struct rebind {
typedef StdAllocator<U, BaseAllocator> other;
};
typedef typename traits_type::size_type size_type;
typedef typename traits_type::difference_type difference_type;
typedef typename traits_type::value_type value_type;
typedef typename traits_type::pointer pointer;
typedef typename traits_type::const_pointer const_pointer;
#if RAPIDJSON_HAS_CXX11
typedef typename std::add_lvalue_reference<value_type>::type &reference;
typedef typename std::add_lvalue_reference<typename std::add_const<value_type>::type>::type &const_reference;
pointer address(reference r) const RAPIDJSON_NOEXCEPT
{
return std::addressof(r);
}
const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
{
return std::addressof(r);
}
size_type max_size() const RAPIDJSON_NOEXCEPT
{
return traits_type::max_size(*this);
}
template <typename ...Args>
void construct(pointer p, Args&&... args)
{
traits_type::construct(*this, p, std::forward<Args>(args)...);
}
void destroy(pointer p)
{
traits_type::destroy(*this, p);
}
#else // !RAPIDJSON_HAS_CXX11
typedef typename allocator_type::reference reference;
typedef typename allocator_type::const_reference const_reference;
pointer address(reference r) const RAPIDJSON_NOEXCEPT
{
return allocator_type::address(r);
}
const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
{
return allocator_type::address(r);
}
size_type max_size() const RAPIDJSON_NOEXCEPT
{
return allocator_type::max_size();
}
void construct(pointer p, const_reference r)
{
allocator_type::construct(p, r);
}
void destroy(pointer p)
{
allocator_type::destroy(p);
}
#endif // !RAPIDJSON_HAS_CXX11
template <typename U>
U* allocate(size_type n = 1, const void* = 0)
{
return RAPIDJSON_NAMESPACE::Malloc<U>(baseAllocator_, n);
}
template <typename U>
void deallocate(U* p, size_type n = 1)
{
RAPIDJSON_NAMESPACE::Free<U>(baseAllocator_, p, n);
}
pointer allocate(size_type n = 1, const void* = 0)
{
return allocate<value_type>(n);
}
void deallocate(pointer p, size_type n = 1)
{
deallocate<value_type>(p, n);
}
#if RAPIDJSON_HAS_CXX11
using is_always_equal = std::is_empty<BaseAllocator>;
#endif
template<typename U>
bool operator==(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
{
return baseAllocator_ == rhs.baseAllocator_;
}
template<typename U>
bool operator!=(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
{
return !operator==(rhs);
}
//! rapidjson Allocator concept
static const bool kNeedFree = BaseAllocator::kNeedFree;
static const bool kRefCounted = internal::IsRefCounted<BaseAllocator>::Value;
void* Malloc(size_t size)
{
return baseAllocator_.Malloc(size);
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
{
return baseAllocator_.Realloc(originalPtr, originalSize, newSize);
}
static void Free(void *ptr) RAPIDJSON_NOEXCEPT
{
BaseAllocator::Free(ptr);
}
private:
template <typename, typename>
friend class StdAllocator; // access to StdAllocator<!T>.*
BaseAllocator baseAllocator_;
};
#if !RAPIDJSON_HAS_CXX17 // std::allocator<void> deprecated in C++17
template <typename BaseAllocator>
class StdAllocator<void, BaseAllocator> :
public std::allocator<void>
{
typedef std::allocator<void> allocator_type;
public:
typedef BaseAllocator BaseAllocatorType;
StdAllocator() RAPIDJSON_NOEXCEPT :
allocator_type(),
baseAllocator_()
{ }
StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
allocator_type(rhs),
baseAllocator_(rhs.baseAllocator_)
{ }
template<typename U>
StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
allocator_type(rhs),
baseAllocator_(rhs.baseAllocator_)
{ }
/* implicit */
StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT :
allocator_type(),
baseAllocator_(baseAllocator)
{ }
~StdAllocator() RAPIDJSON_NOEXCEPT
{ }
template<typename U>
struct rebind {
typedef StdAllocator<U, BaseAllocator> other;
};
typedef typename allocator_type::value_type value_type;
private:
template <typename, typename>
friend class StdAllocator; // access to StdAllocator<!T>.*
BaseAllocator baseAllocator_;
};
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ENCODINGS_H_

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