Skip to content

Commit bb222a7

Browse files
committed
RM690B0 driver for Waveshare ESP32-S3 AMOLED 2.41"
1 parent 799f13c commit bb222a7

34 files changed

Lines changed: 12730 additions & 0 deletions
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2025 Przemyslaw Patrick Socha
2+
//
3+
// SPDX-License-Identifier: MIT
4+
//
5+
// Minimal esp_jpeg interface used by the RM690B0 driver.
6+
// Provides a drop-in replacement for the ESP-IDF component by wrapping TJpgDec.
7+
8+
#pragma once
9+
10+
#include <stdbool.h>
11+
#include <stddef.h>
12+
#include <stdint.h>
13+
14+
#if defined(ESP_PLATFORM)
15+
#include "esp_err.h"
16+
#else
17+
typedef int32_t esp_err_t;
18+
#define ESP_OK 0
19+
#define ESP_FAIL -1
20+
#define ESP_ERR_INVALID_ARG 0x102
21+
#define ESP_ERR_NO_MEM 0x103
22+
#define ESP_ERR_NOT_SUPPORTED 0x105
23+
#define ESP_ERR_INVALID_SIZE 0x10B
24+
#endif
25+
26+
#ifdef __cplusplus
27+
extern "C" {
28+
#endif
29+
30+
typedef enum {
31+
JPEG_IMAGE_FORMAT_RGB565 = 0,
32+
JPEG_IMAGE_FORMAT_RGB888 = 1,
33+
JPEG_IMAGE_FORMAT_GRAYSCALE = 2,
34+
} esp_jpeg_image_format_t;
35+
36+
typedef enum {
37+
JPEG_IMAGE_SCALE_0 = 0,
38+
} esp_jpeg_image_scale_t;
39+
40+
typedef struct {
41+
bool swap_color_bytes : 1;
42+
bool use_scaler : 1;
43+
} esp_jpeg_flags_t;
44+
45+
typedef struct {
46+
const uint8_t *indata;
47+
size_t indata_size;
48+
uint8_t *outbuf;
49+
size_t outbuf_size;
50+
esp_jpeg_image_format_t out_format;
51+
esp_jpeg_image_scale_t out_scale;
52+
esp_jpeg_flags_t flags;
53+
intptr_t user_data;
54+
esp_err_t (*on_block)(intptr_t ctx,
55+
uint32_t top, uint32_t left,
56+
uint32_t bottom, uint32_t right,
57+
const uint16_t *pixels);
58+
} esp_jpeg_image_cfg_t;
59+
60+
typedef struct {
61+
uint32_t width;
62+
uint32_t height;
63+
uint8_t *outbuf;
64+
size_t outbuf_size;
65+
} esp_jpeg_image_output_t;
66+
67+
esp_err_t esp_jpeg_get_image_info(const esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *out);
68+
esp_err_t esp_jpeg_decode(const esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *out);
69+
70+
#ifdef __cplusplus
71+
}
72+
#endif

lib/esp_jpeg/src/esp_jpeg.c

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2025 Przemyslaw Patrick Socha
2+
//
3+
// SPDX-License-Identifier: MIT
4+
//
5+
// Minimal esp_jpeg implementation backed by TJpgDec.
6+
7+
#include "esp_jpeg/esp_jpeg.h"
8+
9+
#include <string.h>
10+
#include <stdlib.h>
11+
12+
#include "tjpgd.h"
13+
14+
#if defined(ESP_PLATFORM)
15+
#include "esp_heap_caps.h"
16+
#endif
17+
18+
#define ESP_JPEG_TJPGD_WORK_BUFFER_SIZE 4096
19+
#define ESP_JPEG_MAX_BLOCK_PIXELS 256
20+
21+
typedef struct {
22+
uint8_t *rgb565_buffer;
23+
uint32_t width;
24+
const uint8_t *jpg_data;
25+
size_t jpg_size;
26+
size_t jpg_offset;
27+
bool swap_bytes;
28+
intptr_t user_data;
29+
esp_err_t (*on_block)(intptr_t,
30+
uint32_t, uint32_t,
31+
uint32_t, uint32_t,
32+
const uint16_t *);
33+
esp_err_t last_error;
34+
} esp_jpeg_tjpgd_ctx_t;
35+
36+
static void *esp_jpeg_alloc(size_t size) {
37+
#if defined(ESP_PLATFORM)
38+
void *ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
39+
if (!ptr) {
40+
ptr = heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
41+
}
42+
return ptr;
43+
#else
44+
return malloc(size);
45+
#endif
46+
}
47+
48+
static void esp_jpeg_free(void *ptr) {
49+
#if defined(ESP_PLATFORM)
50+
heap_caps_free(ptr);
51+
#else
52+
free(ptr);
53+
#endif
54+
}
55+
56+
static size_t esp_jpeg_input(JDEC *jd, uint8_t *buff, size_t ndata) {
57+
esp_jpeg_tjpgd_ctx_t *ctx = (esp_jpeg_tjpgd_ctx_t *)jd->device;
58+
59+
if (!buff) {
60+
size_t bytes_available = ctx->jpg_size - ctx->jpg_offset;
61+
size_t bytes_to_skip = ndata;
62+
if (bytes_to_skip > bytes_available) {
63+
bytes_to_skip = bytes_available;
64+
}
65+
ctx->jpg_offset += bytes_to_skip;
66+
return bytes_to_skip;
67+
}
68+
69+
size_t bytes_to_read = ndata;
70+
size_t bytes_available = ctx->jpg_size - ctx->jpg_offset;
71+
if (bytes_to_read > bytes_available) {
72+
bytes_to_read = bytes_available;
73+
}
74+
75+
if (bytes_to_read > 0) {
76+
memcpy(buff, ctx->jpg_data + ctx->jpg_offset, bytes_to_read);
77+
ctx->jpg_offset += bytes_to_read;
78+
}
79+
80+
return bytes_to_read;
81+
}
82+
83+
static int esp_jpeg_output(JDEC *jd, void *bitmap, JRECT *rect) {
84+
esp_jpeg_tjpgd_ctx_t *ctx = (esp_jpeg_tjpgd_ctx_t *)jd->device;
85+
86+
if (ctx->rgb565_buffer != NULL) {
87+
uint8_t *src = (uint8_t *)bitmap;
88+
for (int y = rect->top; y <= rect->bottom; y++) {
89+
uint32_t row_base = (uint32_t)y * ctx->width;
90+
for (int x = rect->left; x <= rect->right; x++) {
91+
uint16_t rgb565 = ((uint16_t)src[0] << 8) | src[1];
92+
src += 2;
93+
uint32_t offset = (row_base + x) * 2;
94+
if (ctx->swap_bytes) {
95+
ctx->rgb565_buffer[offset] = (rgb565 >> 8) & 0xFF;
96+
ctx->rgb565_buffer[offset + 1] = rgb565 & 0xFF;
97+
} else {
98+
ctx->rgb565_buffer[offset] = rgb565 & 0xFF;
99+
ctx->rgb565_buffer[offset + 1] = (rgb565 >> 8) & 0xFF;
100+
}
101+
}
102+
}
103+
} else if (ctx->on_block != NULL) {
104+
uint32_t width = (uint32_t)rect->right - (uint32_t)rect->left + 1;
105+
uint32_t height = (uint32_t)rect->bottom - (uint32_t)rect->top + 1;
106+
uint32_t total = width * height;
107+
if (total == 0 || total > ESP_JPEG_MAX_BLOCK_PIXELS) {
108+
ctx->last_error = ESP_ERR_INVALID_SIZE;
109+
return 0;
110+
}
111+
112+
uint16_t block_pixels[ESP_JPEG_MAX_BLOCK_PIXELS];
113+
const uint8_t *block_src = (const uint8_t *)bitmap;
114+
for (uint32_t i = 0; i < total; i++) {
115+
uint16_t rgb565 = ((uint16_t)block_src[0] << 8) | block_src[1];
116+
block_src += 2;
117+
if (ctx->swap_bytes) {
118+
rgb565 = (uint16_t)((rgb565 << 8) | (rgb565 >> 8));
119+
}
120+
block_pixels[i] = rgb565;
121+
}
122+
123+
esp_err_t cb = ctx->on_block(ctx->user_data,
124+
(uint32_t)rect->top,
125+
(uint32_t)rect->left,
126+
(uint32_t)rect->bottom,
127+
(uint32_t)rect->right,
128+
block_pixels);
129+
if (cb != ESP_OK) {
130+
ctx->last_error = cb;
131+
return 0;
132+
}
133+
} else {
134+
return 0;
135+
}
136+
137+
return 1;
138+
}
139+
140+
static esp_err_t esp_jpeg_prepare_decoder(const esp_jpeg_image_cfg_t *cfg, JDEC *out_dec, esp_jpeg_tjpgd_ctx_t *ctx, void **work_buffer) {
141+
if (cfg == NULL || cfg->indata == NULL || cfg->indata_size == 0) {
142+
return ESP_ERR_INVALID_ARG;
143+
}
144+
145+
if (cfg->out_scale != JPEG_IMAGE_SCALE_0) {
146+
return ESP_ERR_NOT_SUPPORTED;
147+
}
148+
149+
void *work = esp_jpeg_alloc(ESP_JPEG_TJPGD_WORK_BUFFER_SIZE);
150+
if (!work) {
151+
return ESP_ERR_NO_MEM;
152+
}
153+
154+
ctx->rgb565_buffer = NULL;
155+
ctx->width = 0;
156+
ctx->jpg_data = cfg->indata;
157+
ctx->jpg_size = cfg->indata_size;
158+
ctx->jpg_offset = 0;
159+
ctx->swap_bytes = cfg->flags.swap_color_bytes;
160+
ctx->user_data = cfg->user_data;
161+
ctx->on_block = cfg->on_block;
162+
ctx->last_error = ESP_OK;
163+
164+
JRESULT res = jd_prepare(out_dec, esp_jpeg_input, work, ESP_JPEG_TJPGD_WORK_BUFFER_SIZE, ctx);
165+
if (res != JDR_OK) {
166+
esp_jpeg_free(work);
167+
return ESP_ERR_INVALID_ARG;
168+
}
169+
170+
if (out_dec->width == 0 || out_dec->height == 0) {
171+
esp_jpeg_free(work);
172+
return ESP_ERR_INVALID_ARG;
173+
}
174+
175+
*work_buffer = work;
176+
return ESP_OK;
177+
}
178+
179+
esp_err_t esp_jpeg_get_image_info(const esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *out) {
180+
if (out == NULL) {
181+
return ESP_ERR_INVALID_ARG;
182+
}
183+
184+
JDEC jdec;
185+
esp_jpeg_tjpgd_ctx_t ctx;
186+
void *work = NULL;
187+
188+
esp_err_t err = esp_jpeg_prepare_decoder(cfg, &jdec, &ctx, &work);
189+
if (err != ESP_OK) {
190+
return err;
191+
}
192+
193+
out->width = jdec.width;
194+
out->height = jdec.height;
195+
out->outbuf = NULL;
196+
out->outbuf_size = (size_t)jdec.width * jdec.height * 2;
197+
198+
esp_jpeg_free(work);
199+
return ESP_OK;
200+
}
201+
202+
esp_err_t esp_jpeg_decode(const esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *out) {
203+
if (cfg == NULL || (cfg->outbuf == NULL && cfg->on_block == NULL)) {
204+
return ESP_ERR_INVALID_ARG;
205+
}
206+
if (cfg->out_format != JPEG_IMAGE_FORMAT_RGB565) {
207+
return ESP_ERR_NOT_SUPPORTED;
208+
}
209+
210+
JDEC jdec;
211+
esp_jpeg_tjpgd_ctx_t ctx;
212+
void *work = NULL;
213+
214+
esp_err_t err = esp_jpeg_prepare_decoder(cfg, &jdec, &ctx, &work);
215+
if (err != ESP_OK) {
216+
return err;
217+
}
218+
219+
size_t required_size = (size_t)jdec.width * jdec.height * 2;
220+
if (cfg->outbuf != NULL && cfg->outbuf_size < required_size) {
221+
esp_jpeg_free(work);
222+
return ESP_ERR_NO_MEM;
223+
}
224+
225+
ctx.rgb565_buffer = cfg->outbuf;
226+
ctx.width = jdec.width;
227+
228+
JRESULT res = jd_decomp(&jdec, esp_jpeg_output, 0);
229+
esp_jpeg_free(work);
230+
231+
if (res != JDR_OK) {
232+
return (ctx.last_error != ESP_OK) ? ctx.last_error : ESP_FAIL;
233+
}
234+
235+
if (out) {
236+
out->width = jdec.width;
237+
out->height = jdec.height;
238+
out->outbuf = cfg->outbuf;
239+
out->outbuf_size = (cfg->outbuf != NULL) ? required_size : 0;
240+
}
241+
242+
return ESP_OK;
243+
}

0 commit comments

Comments
 (0)