Files
MAX_CARLINK_A270S/MXC_A27-PCB4.5-270S/lib/awtk/awtk/src/vgcanvas/vgcanvas_nanovg_gl.inc
2025-01-21 16:49:37 +08:00

400 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "tkc/utils.h"
#include "vgcanvas_nanovg.inc"
static ret_t vgcanvas_nanovg_reinit(vgcanvas_t* vg, uint32_t w, uint32_t h, uint32_t stride,
bitmap_format_t format, void* data) {
(void)vg;
(void)w;
(void)h;
(void)format;
(void)data;
return RET_OK;
}
static inline rect_t vgcanvas_nanovg_get_dirty_rect(const rect_t* dirty_rect) {
rect_t r;
uint32_t w, h;
system_info_t* info = system_info();
return_value_if_fail(info != NULL, rect_init(0, 0, 0, 0));
w = info->lcd_w * info->device_pixel_ratio;
h = info->lcd_h * info->device_pixel_ratio;
if (dirty_rect == NULL) {
switch (info->lcd_orientation) {
case LCD_ORIENTATION_0:
r = rect_init(0, 0, w, h);
break;
case LCD_ORIENTATION_90:
r = rect_init(w - h, h - w, h, w);
break;
case LCD_ORIENTATION_180:
r = rect_init(0, 0, w, h);
break;
case LCD_ORIENTATION_270:
r = rect_init(0, 0, h, w);
break;
}
} else {
switch (info->lcd_orientation) {
case LCD_ORIENTATION_0:
r = rect_init(dirty_rect->x, h - dirty_rect->h - dirty_rect->y, dirty_rect->w, dirty_rect->h);
break;
case LCD_ORIENTATION_90:
r = rect_init(w - dirty_rect->h - dirty_rect->y, h - dirty_rect->w - dirty_rect->x, dirty_rect->h, dirty_rect->w);
break;
case LCD_ORIENTATION_180:
r = rect_init(w - dirty_rect->w - dirty_rect->x, dirty_rect->y, dirty_rect->w, dirty_rect->h);
break;
case LCD_ORIENTATION_270:
r = rect_init(dirty_rect->y, dirty_rect->x, dirty_rect->h, dirty_rect->w);
break;
}
}
return r;
}
static inline void vgcanvas_nanovg_set_offline_fb(vgcanvas_nanovg_t* canvas, uint32_t w,
uint32_t h, const dirty_rects_t* dirty_rects) {
GLint default_fbo = 0;
vgcanvas_nanovg_offline_fb_t* offline_fb = canvas->offline_fb;
if (offline_fb != NULL) {
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &default_fbo);
if (offline_fb->width != w || offline_fb->height != h) {
vgcanvas_destroy_offline_fb(canvas->offline_fb);
canvas->offline_fb = vgcanvas_create_offline_fb(w, h);
offline_fb = canvas->offline_fb;
}
if (offline_fb != NULL) {
offline_fb->last_fbo = (GLuint)default_fbo;
glBindFramebuffer(GL_FRAMEBUFFER, offline_fb->fbo);
glViewport(0, 0, w, h);
// 去除这两行注释就可以显示脏矩形的工作原理。
// glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
#if WITH_LCD_CLEAR_ALPHA
if (dirty_rects == NULL || dirty_rects->disable_multiple) {
const rect_t* dirty_rect = dirty_rects != NULL ? &(dirty_rects->max) : NULL;
rect_t r = vgcanvas_nanovg_get_dirty_rect(dirty_rect);
// 把脏矩形的区域刷新为黑色透明度为0的颜色
glScissor(r.x, r.y, r.w, r.h);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
} else {
int32_t i = 0;
for (i = 0; i < dirty_rects->nr; i++) {
const rect_t* iter = dirty_rects->rects + i;
rect_t r = vgcanvas_nanovg_get_dirty_rect(iter);
// 把脏矩形的区域刷新为黑色透明度为0的颜色
glScissor(r.x, r.y, r.w, r.h);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
}
#else
(void)dirty_rects;
#endif
}
}
}
static inline void vgcanvas_nanovg_offline_fb_flush(vgcanvas_nanovg_t* canvas) {
system_info_t* info = system_info();
vgcanvas_nanovg_offline_fb_t* offline_fb = canvas->offline_fb;
vgcanvas_nanovg_screen_shader_info_t* shader_info = canvas->shader_program;
if (offline_fb != NULL && shader_info != NULL) {
glBindFramebuffer(GL_FRAMEBUFFER, offline_fb->last_fbo);
glViewport(0, 0, info->lcd_w * info->device_pixel_ratio,
info->lcd_h * info->device_pixel_ratio);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glUseProgram(shader_info->program_object);
#if defined NANOVG_GL3
glBindVertexArray(shader_info->vao);
#endif
glEnableVertexAttribArray(shader_info->coord_loc);
glEnableVertexAttribArray(shader_info->position_loc);
glBindBuffer(GL_ARRAY_BUFFER, shader_info->vboIds[0]);
glVertexAttribPointer(shader_info->position_loc, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
glBindBuffer(GL_ARRAY_BUFFER, shader_info->vboIds[1]);
glVertexAttribPointer(shader_info->coord_loc, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);
// Bind the texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, offline_fb->textureId);
glUniform1i(shader_info->screentexture_loc, 0);
glDrawArrays(GL_TRIANGLES, 0, shader_info->draw_arrays);
glDisableVertexAttribArray(shader_info->coord_loc);
glDisableVertexAttribArray(shader_info->position_loc);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
#if defined NANOVG_GL3
glBindVertexArray(0);
#endif
}
}
static ret_t vgcanvas_nanovg_begin_frame(vgcanvas_t* vgcanvas, const dirty_rects_t* dirty_rects) {
float_t angle = 0.0f;
float_t anchor_x = 0.0f;
float_t anchor_y = 0.0f;
system_info_t* info = system_info();
vgcanvas_nanovg_t* canvas = (vgcanvas_nanovg_t*)vgcanvas;
const rect_t* dirty_rect = dirty_rects != NULL ? &(dirty_rects->max) : NULL;
native_window_gl_make_current(canvas->window);
if (dirty_rect != NULL) {
canvas->base.dirty_rect = rect_init(dirty_rect->x, dirty_rect->y, dirty_rect->w, dirty_rect->h);
} else {
if (info->lcd_orientation == LCD_ORIENTATION_90 ||
info->lcd_orientation == LCD_ORIENTATION_270) {
canvas->base.dirty_rect = rect_init(0, 0, info->lcd_h, info->lcd_w);
} else {
canvas->base.dirty_rect = rect_init(0, 0, info->lcd_w, info->lcd_h);
}
}
vgcanvas_nanovg_set_offline_fb(canvas, info->lcd_w * info->device_pixel_ratio,
info->lcd_h * info->device_pixel_ratio, dirty_rects);
nvgBeginFrame(canvas->vg, info->lcd_w, info->lcd_h, info->device_pixel_ratio);
switch (info->lcd_orientation) {
case LCD_ORIENTATION_0:
angle = 0.0f;
break;
case LCD_ORIENTATION_90:
angle = TK_D2R(90);
break;
case LCD_ORIENTATION_180:
angle = TK_D2R(180);
break;
case LCD_ORIENTATION_270:
angle = TK_D2R(270);
break;
}
anchor_x = info->lcd_w / 2.0f;
anchor_y = info->lcd_h / 2.0f;
nvgSave(canvas->vg);
if (info->lcd_orientation == LCD_ORIENTATION_90 || info->lcd_orientation == LCD_ORIENTATION_270) {
nvgTranslate(canvas->vg, anchor_x, anchor_y);
nvgRotate(canvas->vg, angle);
nvgTranslate(canvas->vg, -anchor_y, -anchor_x);
} else if (info->lcd_orientation == LCD_ORIENTATION_180) {
nvgTranslate(canvas->vg, anchor_x, anchor_y);
nvgRotate(canvas->vg, angle);
nvgTranslate(canvas->vg, -anchor_x, -anchor_y);
}
return RET_OK;
}
static ret_t vgcanvas_nanovg_end_frame(vgcanvas_t* vgcanvas) {
vgcanvas_nanovg_t* canvas = (vgcanvas_nanovg_t*)vgcanvas;
NVGcontext* vg = canvas->vg;
nvgRestore(vg);
nvgEndFrame(vg);
vgcanvas_nanovg_offline_fb_flush(canvas);
native_window_swap_buffer(canvas->window);
return RET_OK;
}
static ret_t vgcanvas_nanovg_create_fbo(vgcanvas_t* vgcanvas, uint32_t w, uint32_t h, bool_t custom_draw_model, framebuffer_object_t* fbo) {
NVGLUframebuffer* handle = NULL;
NVGcontext* vg = ((vgcanvas_nanovg_t*)vgcanvas)->vg;
handle = nvgluCreateFramebuffer(vg, (int)(w * vgcanvas->ratio),
(int)(h * vgcanvas->ratio), 0);
return_value_if_fail(handle != NULL, RET_FAIL);
fbo->w = w;
fbo->h = h;
fbo->init = FALSE;
fbo->handle = handle;
fbo->id = handle->image;
fbo->ratio = vgcanvas->ratio;
fbo->offline_fbo = handle->fbo;
fbo->custom_draw_model = custom_draw_model;
fbo->online_fbo = nvgluGetCurrFramebuffer();
return RET_OK;
}
static ret_t vgcanvas_nanovg_destroy_fbo(vgcanvas_t* vgcanvas, framebuffer_object_t* fbo) {
NVGLUframebuffer* handle = (NVGLUframebuffer*)fbo->handle;
handle->fbo = fbo->offline_fbo;
nvgluDeleteFramebuffer(handle);
(void)vgcanvas;
return RET_OK;
}
static ret_t vgcanvas_nanovg_bind_fbo(vgcanvas_t* vgcanvas, framebuffer_object_t* fbo) {
NVGcontext* vg = NULL;
vgcanvas_nanovg_t* canvas = (vgcanvas_nanovg_t*)vgcanvas;
NVGLUframebuffer* handle = (NVGLUframebuffer*)fbo->handle;
vg = canvas->vg;
fbo->online_fbo = nvgluGetCurrFramebuffer();
fbo->online_dirty_rect = rect_init(vgcanvas->dirty_rect.x, vgcanvas->dirty_rect.y, vgcanvas->dirty_rect.w, vgcanvas->dirty_rect.h);
handle->fbo = fbo->offline_fbo;
vgcanvas->dirty_rect = rect_init(0, 0, fbo->w, fbo->h);
nvgluBindFramebuffer(handle);
if (!fbo->custom_draw_model) {
glViewport(0, 0, fbo->w * fbo->ratio, fbo->h * fbo->ratio);
if (!fbo->init) {
glScissor(0, 0, fbo->w * fbo->ratio, fbo->h * fbo->ratio);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
fbo->init = TRUE;
}
nvgBeginFrameEx(vg, fbo->w, fbo->h, fbo->ratio, FALSE);
nvgSave(vg);
nvgReset(vg);
}
return RET_OK;
}
static ret_t vgcanvas_nanovg_unbind_fbo(vgcanvas_t* vgcanvas, framebuffer_object_t* fbo) {
NVGcontext* vg = NULL;
vgcanvas_nanovg_t* canvas = (vgcanvas_nanovg_t*)vgcanvas;
NVGLUframebuffer* handle = (NVGLUframebuffer*)fbo->handle;
vg = canvas->vg;
handle->fbo = fbo->online_fbo;
if (!fbo->custom_draw_model) {
nvgRestore(vg);
nvgEndFrame(vg);
}
nvgluBindFramebuffer(handle);
vgcanvas->dirty_rect = rect_init(fbo->online_dirty_rect.x, fbo->online_dirty_rect.y, fbo->online_dirty_rect.w, fbo->online_dirty_rect.h);
glViewport(0, 0, vgcanvas->w * fbo->ratio, vgcanvas->h * fbo->ratio);
glScissor(0, 0, vgcanvas->w * fbo->ratio, vgcanvas->h * fbo->ratio);
if (!fbo->custom_draw_model) {
nvgBeginFrameEx(vg, vgcanvas->w, vgcanvas->h, fbo->ratio, FALSE);
}
return RET_OK;
}
static ret_t vgcanvas_nanovg_fbo_to_bitmap(vgcanvas_t* vgcanvas, framebuffer_object_t* fbo,
bitmap_t* img, const rect_t* r) {
uint32_t i = 0;
uint32_t x = 0;
uint32_t y = 0;
uint32_t height = 0;
uint8_t* p = NULL;
uint8_t* data = NULL;
uint8_t* img_data = NULL;
int online_fbo = nvgluGetCurrFramebuffer();
NVGLUframebuffer* handle = (NVGLUframebuffer*)fbo->handle;
handle->fbo = fbo->offline_fbo;
nvgluBindFramebuffer(handle);
data = TKMEM_ZALLOCN(uint8_t, img->h * img->line_length);
img_data = (uint8_t*)bitmap_lock_buffer_for_write(img);
height = fbo->h * fbo->ratio;
if (r != NULL) {
x = r->x;
y = r->y;
}
/* 因为 opengles 的原点坐标为左下角,所以需要把 AWTK 的坐标AWTK 是右上角为原点的坐标系)转换为左下角为原点的坐标系*/
nvgluReadCurrentFramebufferData(x, height - img->h - y, img->w, img->h, fbo->w * fbo->ratio,
height, data);
p = data + ((img->h - 1) * img->line_length);
/*
* 图像数据垂直翻转
* opengles 出来的数据是预乘的数据,需要反算预乘。
*/
while (TRUE) {
uint8_t* src_data = p;
uint8_t* dst_data = img_data;
for (i = 0; i < img->w; i++) {
if (0x0 < src_data[3] && src_data[3] < 0xff) {
dst_data[0] = src_data[0] == src_data[3] ? 0xff : (src_data[0] << 8) / src_data[3];
dst_data[1] = src_data[1] == src_data[3] ? 0xff : (src_data[1] << 8) / src_data[3];
dst_data[2] = src_data[2] == src_data[3] ? 0xff : (src_data[2] << 8) / src_data[3];
} else {
dst_data[0] = src_data[0];
dst_data[1] = src_data[1];
dst_data[2] = src_data[2];
}
dst_data[3] = src_data[3];
src_data += 4;
dst_data += 4;
}
if (p == data) {
break;
}
p -= img->line_length;
img_data += img->line_length;
}
bitmap_unlock_buffer(img);
handle->fbo = online_fbo;
nvgluBindFramebuffer(handle);
TKMEM_FREE(data);
return RET_OK;
}
static ret_t vgcanvas_nanovg_destroy(vgcanvas_t* vgcanvas) {
vgcanvas_nanovg_t* canvas = (vgcanvas_nanovg_t*)vgcanvas;
NVGcontext* vg = canvas->vg;
vgcanvas_asset_manager_remove_vg(vgcanvas_asset_manager(), vgcanvas);
vgcanvas_nanovg_deinit(vgcanvas);
#if defined(WITH_NANOVG_GL3)
nvgDeleteGL3(vg);
#elif defined(WITH_NANOVG_GLES2)
nvgDeleteGLES2(vg);
#elif defined(WITH_NANOVG_GLES3)
nvgDeleteGLES3(vg);
#endif
TKMEM_FREE(canvas->offline_fb);
TKMEM_FREE(canvas->shader_program);
TKMEM_FREE(vgcanvas);
return RET_OK;
}