In some cases, pixel inversion is managed by the display hardware and software inversion is not required. Signed-off-by: Robin-Charles Guihéneuf <robin-charles@hotmail.fr>
155 lines
4.0 KiB
C
155 lines
4.0 KiB
C
/*
|
|
* Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <lvgl.h>
|
|
#include <string.h>
|
|
#include "lvgl_display.h"
|
|
|
|
#define COLOR_PALETTE_HEADER_SIZE (8)
|
|
|
|
static uint8_t *mono_conv_buf;
|
|
static uint32_t mono_conv_buf_size;
|
|
|
|
static ALWAYS_INLINE void set_px_at_pos(uint8_t *dst_buf, uint32_t x, uint32_t y, uint32_t width,
|
|
const struct display_capabilities *caps)
|
|
{
|
|
uint8_t bit;
|
|
uint8_t *buf;
|
|
|
|
if (caps->screen_info & SCREEN_INFO_MONO_VTILED) {
|
|
buf = dst_buf + x + y / 8 * width;
|
|
|
|
if (caps->screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
|
|
bit = 7 - y % 8;
|
|
} else {
|
|
bit = y % 8;
|
|
}
|
|
} else {
|
|
buf = dst_buf + x / 8 + y * width / 8;
|
|
|
|
if (caps->screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
|
|
bit = 7 - x % 8;
|
|
} else {
|
|
bit = x % 8;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_LV_Z_COLOR_MONO_HW_INVERSION
|
|
*buf |= BIT(bit);
|
|
#else
|
|
if (caps->current_pixel_format == PIXEL_FORMAT_MONO10) {
|
|
*buf |= BIT(bit);
|
|
} else {
|
|
*buf &= ~BIT(bit);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void lvgl_transform_buffer(uint8_t **px_map, uint32_t width, uint32_t height,
|
|
const struct display_capabilities *caps)
|
|
{
|
|
#ifdef CONFIG_LV_Z_COLOR_MONO_HW_INVERSION
|
|
uint8_t clear_color = 0x00;
|
|
#else
|
|
uint8_t clear_color = caps->current_pixel_format == PIXEL_FORMAT_MONO10 ? 0x00 : 0xFF;
|
|
#endif
|
|
|
|
memset(mono_conv_buf, clear_color, mono_conv_buf_size);
|
|
|
|
/* Needed because LVGL reserves some bytes in the buffer for the color palette. */
|
|
*px_map += COLOR_PALETTE_HEADER_SIZE;
|
|
|
|
uint8_t *src_buf = *px_map;
|
|
uint32_t stride = (width + CONFIG_LV_DRAW_BUF_STRIDE_ALIGN - 1) &
|
|
~(CONFIG_LV_DRAW_BUF_STRIDE_ALIGN - 1);
|
|
|
|
for (uint32_t y = 0; y < height; y++) {
|
|
for (uint32_t x = 0; x < width; x++) {
|
|
uint32_t bit_idx = x + y * stride;
|
|
uint8_t src_bit = (src_buf[bit_idx / 8] >> (7 - (bit_idx % 8))) & 1;
|
|
|
|
if (src_bit) {
|
|
set_px_at_pos(mono_conv_buf, x, y, width, caps);
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(src_buf, mono_conv_buf, mono_conv_buf_size - COLOR_PALETTE_HEADER_SIZE);
|
|
}
|
|
|
|
void lvgl_flush_cb_mono(lv_display_t *display, const lv_area_t *area, uint8_t *px_map)
|
|
{
|
|
uint16_t w = area->x2 - area->x1 + 1;
|
|
uint16_t h = area->y2 - area->y1 + 1;
|
|
struct lvgl_disp_data *data = (struct lvgl_disp_data *)lv_display_get_user_data(display);
|
|
const struct device *display_dev = data->display_dev;
|
|
const bool is_epd = data->cap.screen_info & SCREEN_INFO_EPD;
|
|
const bool is_last = lv_display_flush_is_last(display);
|
|
|
|
lvgl_transform_buffer(&px_map, w, h, &data->cap);
|
|
|
|
if (is_epd && !data->blanking_on && !is_last) {
|
|
/*
|
|
* Turn on display blanking when using an EPD
|
|
* display. This prevents updates and the associated
|
|
* flicker if the screen is rendered in multiple
|
|
* steps.
|
|
*/
|
|
display_blanking_on(display_dev);
|
|
data->blanking_on = true;
|
|
}
|
|
|
|
struct display_buffer_descriptor desc = {
|
|
.buf_size = (w * h) / 8U,
|
|
.width = w,
|
|
.pitch = w,
|
|
.height = h,
|
|
.frame_incomplete = !is_last,
|
|
};
|
|
|
|
display_write(display_dev, area->x1, area->y1, &desc, (void *)px_map);
|
|
if (data->cap.screen_info & SCREEN_INFO_DOUBLE_BUFFER) {
|
|
display_write(display_dev, area->x1, area->y1, &desc, (void *)px_map);
|
|
}
|
|
|
|
if (is_epd && is_last && data->blanking_on) {
|
|
/*
|
|
* The entire screen has now been rendered. Update the
|
|
* display by disabling blanking.
|
|
*/
|
|
display_blanking_off(display_dev);
|
|
data->blanking_on = false;
|
|
}
|
|
|
|
lv_display_flush_ready(display);
|
|
}
|
|
|
|
void lvgl_rounder_cb_mono(lv_event_t *e)
|
|
{
|
|
lv_area_t *area = lv_event_get_param(e);
|
|
lv_display_t *display = lv_event_get_user_data(e);
|
|
struct lvgl_disp_data *data = (struct lvgl_disp_data *)lv_display_get_user_data(display);
|
|
|
|
if (data->cap.screen_info & SCREEN_INFO_X_ALIGNMENT_WIDTH) {
|
|
area->x1 = 0;
|
|
area->x2 = data->cap.x_resolution - 1;
|
|
} else {
|
|
area->x1 &= ~0x7;
|
|
area->x2 |= 0x7;
|
|
if (data->cap.screen_info & SCREEN_INFO_MONO_VTILED) {
|
|
area->y1 &= ~0x7;
|
|
area->y2 |= 0x7;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lvgl_set_mono_conversion_buffer(uint8_t *buffer, uint32_t buffer_size)
|
|
{
|
|
mono_conv_buf = buffer;
|
|
mono_conv_buf_size = buffer_size;
|
|
}
|