Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/libs/lvgl/lv_draw/lv_draw_vbasic.c
1476 views
1
/**
2
* @file lv_vdraw.c
3
*
4
*/
5
6
#include "lv_draw_vbasic.h"
7
8
#include <stdint.h>
9
#include <string.h>
10
11
#include "../lv_hal/lv_hal_disp.h"
12
#include "../lv_misc/lv_area.h"
13
#include "../lv_misc/lv_font.h"
14
#include "../lv_misc/lv_color.h"
15
#include "../lv_misc/lv_log.h"
16
17
#if LV_VDB_SIZE != 0
18
19
#include <stddef.h>
20
#include "../lv_core/lv_vdb.h"
21
#include "lv_draw.h"
22
23
/*********************
24
* INCLUDES
25
*********************/
26
27
/*********************
28
* DEFINES
29
*********************/
30
#define VFILL_HW_ACC_SIZE_LIMIT 50 /*Always fill < 50 px with 'sw_color_fill' because of the hw. init overhead*/
31
32
#ifndef LV_ATTRIBUTE_MEM_ALIGN
33
#define LV_ATTRIBUTE_MEM_ALIGN
34
#endif
35
36
/**********************
37
* TYPEDEFS
38
**********************/
39
40
/**********************
41
* STATIC PROTOTYPES
42
**********************/
43
static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
44
static void sw_color_fill(lv_area_t * mem_area, lv_color_t * mem, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa);
45
46
#if LV_COLOR_SCREEN_TRANSP
47
static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa);
48
#endif
49
50
/**********************
51
* STATIC VARIABLES
52
**********************/
53
54
/**********************
55
* MACROS
56
**********************/
57
58
/**********************
59
* GLOBAL FUNCTIONS
60
**********************/
61
62
/**
63
* Put a pixel in the Virtual Display Buffer
64
* @param x pixel x coordinate
65
* @param y pixel y coordinate
66
* @param mask_p fill only on this mask (truncated to VDB area)
67
* @param color pixel color
68
* @param opa opacity of the area (0..255)
69
*/
70
void lv_vpx(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
71
{
72
if(opa < LV_OPA_MIN) return;
73
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
74
75
lv_vdb_t * vdb_p = lv_vdb_get();
76
if(!vdb_p) {
77
LV_LOG_WARN("Invalid VDB pointer");
78
return;
79
}
80
81
/*Pixel out of the mask*/
82
if(x < mask_p->x1 || x > mask_p->x2 ||
83
y < mask_p->y1 || y > mask_p->y2) {
84
return;
85
}
86
87
uint32_t vdb_width = lv_area_get_width(&vdb_p->area);
88
89
/*Make the coordinates relative to VDB*/
90
x -= vdb_p->area.x1;
91
y -= vdb_p->area.y1;
92
93
lv_disp_t * disp = lv_disp_get_active();
94
if(disp->driver.vdb_wr) {
95
disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, x, y, color, opa);
96
} else {
97
lv_color_t * vdb_px_p = vdb_p->buf + y * vdb_width + x;
98
#if LV_COLOR_SCREEN_TRANSP == 0
99
if(opa == LV_OPA_COVER) {
100
*vdb_px_p = color;
101
} else {
102
*vdb_px_p = lv_color_mix(color, *vdb_px_p, opa);
103
}
104
#else
105
*vdb_px_p = color_mix_2_alpha(*vdb_px_p, (*vdb_px_p).alpha, color, opa);
106
#endif
107
}
108
}
109
110
111
/**
112
* Fill an area in the Virtual Display Buffer
113
* @param cords_p coordinates of the area to fill
114
* @param mask_p fill only o this mask (truncated to VDB area)
115
* @param color fill color
116
* @param opa opacity of the area (0..255)
117
*/
118
void lv_vfill(const lv_area_t * cords_p, const lv_area_t * mask_p,
119
lv_color_t color, lv_opa_t opa)
120
{
121
if(opa < LV_OPA_MIN) return;
122
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
123
124
lv_area_t res_a;
125
bool union_ok;
126
lv_vdb_t * vdb_p = lv_vdb_get();
127
if(!vdb_p) {
128
LV_LOG_WARN("Invalid VDB pointer");
129
return;
130
}
131
132
/*Get the union of cord and mask*/
133
/* The mask is already truncated to the vdb size
134
* in 'lv_refr_area_with_vdb' function */
135
union_ok = lv_area_intersect(&res_a, cords_p, mask_p);
136
137
/*If there are common part of the three area then draw to the vdb*/
138
if(union_ok == false) return;
139
140
lv_area_t vdb_rel_a; /*Stores relative coordinates on vdb*/
141
vdb_rel_a.x1 = res_a.x1 - vdb_p->area.x1;
142
vdb_rel_a.y1 = res_a.y1 - vdb_p->area.y1;
143
vdb_rel_a.x2 = res_a.x2 - vdb_p->area.x1;
144
vdb_rel_a.y2 = res_a.y2 - vdb_p->area.y1;
145
146
lv_color_t * vdb_buf_tmp = vdb_p->buf;
147
uint32_t vdb_width = lv_area_get_width(&vdb_p->area);
148
/*Move the vdb_tmp to the first row*/
149
vdb_buf_tmp += vdb_width * vdb_rel_a.y1;
150
151
152
#if USE_LV_GPU
153
static LV_ATTRIBUTE_MEM_ALIGN lv_color_t color_array_tmp[LV_HOR_RES]; /*Used by 'lv_disp_mem_blend'*/
154
static lv_coord_t last_width = -1;
155
156
lv_coord_t w = lv_area_get_width(&vdb_rel_a);
157
/*Don't use hw. acc. for every small fill (because of the init overhead)*/
158
if(w < VFILL_HW_ACC_SIZE_LIMIT) {
159
sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
160
}
161
/*Not opaque fill*/
162
else if(opa == LV_OPA_COVER) {
163
/*Use hw fill if present*/
164
if(lv_disp_is_mem_fill_supported()) {
165
lv_coord_t row;
166
for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
167
lv_disp_mem_fill(&vdb_buf_tmp[vdb_rel_a.x1], w, color);
168
vdb_buf_tmp += vdb_width;
169
}
170
}
171
/*Use hw blend if present and the area is not too small*/
172
else if(lv_area_get_height(&vdb_rel_a) > VFILL_HW_ACC_SIZE_LIMIT &&
173
lv_disp_is_mem_blend_supported()) {
174
/*Fill a one line sized buffer with a color and blend this later*/
175
if(color_array_tmp[0].full != color.full || last_width != w) {
176
uint16_t i;
177
for(i = 0; i < w; i++) {
178
color_array_tmp[i].full = color.full;
179
}
180
last_width = w;
181
}
182
183
/*Blend the filled line to every line VDB line-by-line*/
184
lv_coord_t row;
185
for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
186
lv_disp_mem_blend(&vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
187
vdb_buf_tmp += vdb_width;
188
}
189
190
}
191
/*Else use sw fill if no better option*/
192
else {
193
sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
194
}
195
196
}
197
/*Fill with opacity*/
198
else {
199
/*Use hw blend if present*/
200
if(lv_disp_is_mem_blend_supported()) {
201
if(color_array_tmp[0].full != color.full || last_width != w) {
202
uint16_t i;
203
for(i = 0; i < w; i++) {
204
color_array_tmp[i].full = color.full;
205
}
206
207
last_width = w;
208
}
209
lv_coord_t row;
210
for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
211
lv_disp_mem_blend(&vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
212
vdb_buf_tmp += vdb_width;
213
}
214
215
}
216
/*Use sw fill with opa if no better option*/
217
else {
218
sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
219
}
220
221
}
222
#else
223
sw_color_fill(&vdb_p->area, vdb_p->buf, &vdb_rel_a, color, opa);
224
#endif
225
}
226
227
/**
228
* Draw a letter in the Virtual Display Buffer
229
* @param pos_p left-top coordinate of the latter
230
* @param mask_p the letter will be drawn only on this area (truncated to VDB area)
231
* @param font_p pointer to font
232
* @param letter a letter to draw
233
* @param color color of letter
234
* @param opa opacity of letter (0..255)
235
*/
236
void lv_vletter(const lv_point_t * pos_p, const lv_area_t * mask_p,
237
const lv_font_t * font_p, uint32_t letter,
238
lv_color_t color, lv_opa_t opa)
239
{
240
const uint8_t bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
241
const uint8_t bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
242
const uint8_t bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
243
68, 85, 102, 119,
244
136, 153, 170, 187,
245
204, 221, 238, 255
246
};
247
if(opa < LV_OPA_MIN) return;
248
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
249
250
if(font_p == NULL) {
251
LV_LOG_WARN("Font: character's bitmap not found");
252
return;
253
}
254
255
lv_coord_t pos_x = pos_p->x;
256
lv_coord_t pos_y = pos_p->y;
257
uint8_t letter_w = lv_font_get_real_width(font_p, letter);
258
uint8_t letter_h = lv_font_get_height(font_p);
259
uint8_t bpp = lv_font_get_bpp(font_p, letter); /*Bit per pixel (1,2, 4 or 8)*/
260
const uint8_t * bpp_opa_table;
261
uint8_t mask_init;
262
uint8_t mask;
263
264
if(lv_font_is_monospace(font_p, letter)) {
265
pos_x += (lv_font_get_width(font_p, letter) - letter_w) / 2;
266
}
267
268
269
switch(bpp) {
270
case 1:
271
bpp_opa_table = bpp1_opa_table;
272
mask_init = 0x80;
273
break;
274
case 2:
275
bpp_opa_table = bpp2_opa_table;
276
mask_init = 0xC0;
277
break;
278
case 4:
279
bpp_opa_table = bpp4_opa_table;
280
mask_init = 0xF0;
281
break;
282
case 8:
283
bpp_opa_table = NULL;
284
mask_init = 0xFF;
285
break; /*No opa table, pixel value will be used directly*/
286
default:
287
return; /*Invalid bpp. Can't render the letter*/
288
}
289
290
const uint8_t * map_p = lv_font_get_bitmap(font_p, letter);
291
292
if(map_p == NULL) return;
293
294
/*If the letter is completely out of mask don't draw it */
295
if(pos_x + letter_w < mask_p->x1 || pos_x > mask_p->x2 ||
296
pos_y + letter_h < mask_p->y1 || pos_y > mask_p->y2) return;
297
298
lv_vdb_t * vdb_p = lv_vdb_get();
299
if(!vdb_p) {
300
LV_LOG_WARN("Invalid VDB pointer");
301
return;
302
}
303
304
lv_coord_t vdb_width = lv_area_get_width(&vdb_p->area);
305
lv_color_t * vdb_buf_tmp = vdb_p->buf;
306
lv_coord_t col, row;
307
uint8_t col_bit;
308
uint8_t col_byte_cnt;
309
uint8_t width_byte_scr = letter_w >> 3; /*Width in bytes (on the screen finally) (e.g. w = 11 -> 2 bytes wide)*/
310
if(letter_w & 0x7) width_byte_scr++;
311
uint8_t width_byte_bpp = (letter_w * bpp) >> 3; /*Letter width in byte. Real width in the font*/
312
if((letter_w * bpp) & 0x7) width_byte_bpp++;
313
314
/* Calculate the col/row start/end on the map*/
315
lv_coord_t col_start = pos_x >= mask_p->x1 ? 0 : mask_p->x1 - pos_x;
316
lv_coord_t col_end = pos_x + letter_w <= mask_p->x2 ? letter_w : mask_p->x2 - pos_x + 1;
317
lv_coord_t row_start = pos_y >= mask_p->y1 ? 0 : mask_p->y1 - pos_y;
318
lv_coord_t row_end = pos_y + letter_h <= mask_p->y2 ? letter_h : mask_p->y2 - pos_y + 1;
319
320
/*Set a pointer on VDB to the first pixel of the letter*/
321
vdb_buf_tmp += ((pos_y - vdb_p->area.y1) * vdb_width)
322
+ pos_x - vdb_p->area.x1;
323
324
/*If the letter is partially out of mask the move there on VDB*/
325
vdb_buf_tmp += (row_start * vdb_width) + col_start;
326
327
/*Move on the map too*/
328
map_p += (row_start * width_byte_bpp) + ((col_start * bpp) >> 3);
329
330
lv_disp_t * disp = lv_disp_get_active();
331
332
uint8_t letter_px;
333
lv_opa_t px_opa;
334
for(row = row_start; row < row_end; row ++) {
335
col_byte_cnt = 0;
336
col_bit = (col_start * bpp) % 8;
337
mask = mask_init >> col_bit;
338
for(col = col_start; col < col_end; col ++) {
339
letter_px = (*map_p & mask) >> (8 - col_bit - bpp);
340
if(letter_px != 0) {
341
if(opa == LV_OPA_COVER) {
342
px_opa = bpp == 8 ? letter_px : bpp_opa_table[letter_px];
343
} else {
344
px_opa = bpp == 8 ?
345
(uint16_t)((uint16_t)letter_px * opa) >> 8 :
346
(uint16_t)((uint16_t)bpp_opa_table[letter_px] * opa) >> 8;
347
}
348
349
if(disp->driver.vdb_wr) {
350
disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width,
351
(col + pos_x) - vdb_p->area.x1, (row + pos_y) - vdb_p->area.y1,
352
color, px_opa);
353
} else {
354
#if LV_COLOR_SCREEN_TRANSP == 0
355
*vdb_buf_tmp = lv_color_mix(color, *vdb_buf_tmp, px_opa);
356
#else
357
*vdb_buf_tmp = color_mix_2_alpha(*vdb_buf_tmp, (*vdb_buf_tmp).alpha, color, px_opa);
358
#endif
359
}
360
}
361
362
vdb_buf_tmp++;
363
364
if(col_bit < 8 - bpp) {
365
col_bit += bpp;
366
mask = mask >> bpp;
367
} else {
368
col_bit = 0;
369
col_byte_cnt ++;
370
mask = mask_init;
371
map_p ++;
372
}
373
}
374
375
map_p += (width_byte_bpp) - col_byte_cnt;
376
vdb_buf_tmp += vdb_width - (col_end - col_start); /*Next row in VDB*/
377
}
378
}
379
380
/**
381
* Draw a color map to the display (image)
382
* @param cords_p coordinates the color map
383
* @param mask_p the map will drawn only on this area (truncated to VDB area)
384
* @param map_p pointer to a lv_color_t array
385
* @param opa opacity of the map
386
* @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
387
* @param alpha_byte true: extra alpha byte is inserted for every pixel
388
* @param recolor mix the pixels with this color
389
* @param recolor_opa the intense of recoloring
390
*/
391
void lv_vmap(const lv_area_t * cords_p, const lv_area_t * mask_p,
392
const uint8_t * map_p, lv_opa_t opa, bool chroma_key, bool alpha_byte,
393
lv_color_t recolor, lv_opa_t recolor_opa)
394
{
395
396
if(opa < LV_OPA_MIN) return;
397
if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
398
399
lv_area_t masked_a;
400
bool union_ok;
401
lv_vdb_t * vdb_p = lv_vdb_get();
402
if(!vdb_p) {
403
LV_LOG_WARN("Invalid VDB pointer");
404
return;
405
}
406
407
/*Get the union of map size and mask*/
408
/* The mask is already truncated to the vdb size
409
* in 'lv_refr_area_with_vdb' function */
410
union_ok = lv_area_intersect(&masked_a, cords_p, mask_p);
411
412
/*If there are common part of the three area then draw to the vdb*/
413
if(union_ok == false) return;
414
415
/*The pixel size in byte is different if an alpha byte is added too*/
416
uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
417
418
/*If the map starts OUT of the masked area then calc. the first pixel*/
419
lv_coord_t map_width = lv_area_get_width(cords_p);
420
if(cords_p->y1 < masked_a.y1) {
421
map_p += (uint32_t) map_width * ((masked_a.y1 - cords_p->y1)) * px_size_byte;
422
}
423
if(cords_p->x1 < masked_a.x1) {
424
map_p += (masked_a.x1 - cords_p->x1) * px_size_byte;
425
}
426
427
/*Stores coordinates relative to the current VDB*/
428
masked_a.x1 = masked_a.x1 - vdb_p->area.x1;
429
masked_a.y1 = masked_a.y1 - vdb_p->area.y1;
430
masked_a.x2 = masked_a.x2 - vdb_p->area.x1;
431
masked_a.y2 = masked_a.y2 - vdb_p->area.y1;
432
433
lv_coord_t vdb_width = lv_area_get_width(&vdb_p->area);
434
lv_color_t * vdb_buf_tmp = vdb_p->buf;
435
vdb_buf_tmp += (uint32_t) vdb_width * masked_a.y1; /*Move to the first row*/
436
vdb_buf_tmp += (uint32_t) masked_a.x1; /*Move to the first col*/
437
438
lv_coord_t row;
439
lv_coord_t map_useful_w = lv_area_get_width(&masked_a);
440
441
lv_disp_t * disp = lv_disp_get_active();
442
443
/*The simplest case just copy the pixels into the VDB*/
444
if(chroma_key == false && alpha_byte == false && opa == LV_OPA_COVER && recolor_opa == LV_OPA_TRANSP) {
445
446
/*Use the custom VDB write function is exists*/
447
if(disp->driver.vdb_wr) {
448
lv_coord_t col;
449
for(row = masked_a.y1; row <= masked_a.y2; row++) {
450
for(col = 0; col < map_useful_w; col++) {
451
lv_color_t px_color = *((lv_color_t *)&map_p[(uint32_t)col * px_size_byte]);
452
disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, col + masked_a.x1, row, px_color, opa);
453
}
454
map_p += map_width * px_size_byte; /*Next row on the map*/
455
}
456
}
457
/*Normal native VDB*/
458
else {
459
for(row = masked_a.y1; row <= masked_a.y2; row++) {
460
#if USE_LV_GPU
461
if(lv_disp_is_mem_blend_supported() == false) {
462
sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
463
} else {
464
lv_disp_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
465
}
466
#else
467
sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
468
#endif
469
map_p += map_width * px_size_byte; /*Next row on the map*/
470
vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
471
}
472
}
473
}
474
475
/*In the other cases every pixel need to be checked one-by-one*/
476
else {
477
lv_color_t chroma_key_color = LV_COLOR_TRANSP;
478
lv_coord_t col;
479
lv_color_t last_img_px = LV_COLOR_BLACK;
480
lv_color_t recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
481
for(row = masked_a.y1; row <= masked_a.y2; row++) {
482
for(col = 0; col < map_useful_w; col++) {
483
lv_opa_t opa_result = opa;
484
uint8_t * px_color_p = (uint8_t *) &map_p[(uint32_t)col * px_size_byte];
485
lv_color_t px_color;
486
487
/*Calculate with the pixel level alpha*/
488
if(alpha_byte) {
489
#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
490
px_color.full = px_color_p[0];
491
#elif LV_COLOR_DEPTH == 16
492
/*Because of Alpha byte 16 bit color can start on odd address which can cause crash*/
493
px_color.full = px_color_p[0] + (px_color_p[1] << 8);
494
#elif LV_COLOR_DEPTH == 32
495
px_color = *((lv_color_t *)px_color_p);
496
#endif
497
lv_opa_t px_opa = *(px_color_p + LV_IMG_PX_SIZE_ALPHA_BYTE - 1);
498
if(px_opa == LV_OPA_TRANSP) continue;
499
else if(px_opa != LV_OPA_COVER) opa_result = (uint32_t)((uint32_t)px_opa * opa_result) >> 8;
500
} else {
501
px_color = *((lv_color_t *)px_color_p);
502
}
503
504
/*Handle chroma key*/
505
if(chroma_key && px_color.full == chroma_key_color.full) continue;
506
507
/*Re-color the pixel if required*/
508
if(recolor_opa != LV_OPA_TRANSP) {
509
if(last_img_px.full != px_color.full) { /*Minor acceleration: calculate only for new colors (save the last)*/
510
last_img_px = px_color;
511
recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
512
}
513
/*Handle custom VDB write is present*/
514
if(disp->driver.vdb_wr) {
515
disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, col + masked_a.x1, row, recolored_px, opa_result);
516
}
517
/*Normal native VDB write*/
518
else {
519
if(opa_result == LV_OPA_COVER) vdb_buf_tmp[col].full = recolored_px.full;
520
else vdb_buf_tmp[col] = lv_color_mix(recolored_px, vdb_buf_tmp[col], opa_result);
521
}
522
} else {
523
/*Handle custom VDB write is present*/
524
if(disp->driver.vdb_wr) {
525
disp->driver.vdb_wr((uint8_t *)vdb_p->buf, vdb_width, col + masked_a.x1, row, px_color, opa_result);
526
}
527
/*Normal native VDB write*/
528
else {
529
if(opa_result == LV_OPA_COVER) vdb_buf_tmp[col] = px_color;
530
else {
531
#if LV_COLOR_SCREEN_TRANSP == 0
532
vdb_buf_tmp[col] = lv_color_mix(px_color, vdb_buf_tmp[col], opa_result);
533
#else
534
vdb_buf_tmp[col] = color_mix_2_alpha(vdb_buf_tmp[col], vdb_buf_tmp[col].alpha, px_color, opa_result);
535
#endif
536
}
537
}
538
}
539
}
540
541
map_p += map_width * px_size_byte; /*Next row on the map*/
542
vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
543
}
544
}
545
}
546
547
/**********************
548
* STATIC FUNCTIONS
549
**********************/
550
551
/**
552
* Blend pixels to destination memory using opacity
553
* @param dest a memory address. Copy 'src' here.
554
* @param src pointer to pixel map. Copy it to 'dest'.
555
* @param length number of pixels in 'src'
556
* @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
557
*/
558
static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
559
{
560
if(opa == LV_OPA_COVER) {
561
memcpy(dest, src, length * sizeof(lv_color_t));
562
} else {
563
uint32_t col;
564
for(col = 0; col < length; col++) {
565
dest[col] = lv_color_mix(src[col], dest[col], opa);
566
}
567
}
568
}
569
570
/**
571
*
572
* @param mem_area coordinates of 'mem' memory area
573
* @param mem a memory address. Considered to a rectangular window according to 'mem_area'
574
* @param fill_area coordinates of an area to fill. Relative to 'mem_area'.
575
* @param color fill color
576
* @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
577
*/
578
static void sw_color_fill(lv_area_t * mem_area, lv_color_t * mem, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa)
579
{
580
/*Set all row in vdb to the given color*/
581
lv_coord_t row;
582
lv_coord_t col;
583
lv_coord_t mem_width = lv_area_get_width(mem_area);
584
585
lv_disp_t * disp = lv_disp_get_active();
586
if(disp->driver.vdb_wr) {
587
for(col = fill_area->x1; col <= fill_area->x2; col++) {
588
for(row = fill_area->y1; row <= fill_area->y2; row++) {
589
disp->driver.vdb_wr((uint8_t *)mem, mem_width, col, row, color, opa);
590
}
591
}
592
} else {
593
mem += fill_area->y1 * mem_width; /*Go to the first row*/
594
595
/*Run simpler function without opacity*/
596
if(opa == LV_OPA_COVER) {
597
598
/*Fill the first row with 'color'*/
599
for(col = fill_area->x1; col <= fill_area->x2; col++) {
600
mem[col] = color;
601
}
602
603
/*Copy the first row to all other rows*/
604
lv_color_t * mem_first = &mem[fill_area->x1];
605
lv_coord_t copy_size = (fill_area->x2 - fill_area->x1 + 1) * sizeof(lv_color_t);
606
mem += mem_width;
607
608
for(row = fill_area->y1 + 1; row <= fill_area->y2; row++) {
609
memcpy(&mem[fill_area->x1], mem_first, copy_size);
610
mem += mem_width;
611
}
612
}
613
/*Calculate with alpha too*/
614
else {
615
616
#if LV_COLOR_SCREEN_TRANSP == 0
617
lv_color_t bg_tmp = LV_COLOR_BLACK;
618
lv_color_t opa_tmp = lv_color_mix(color, bg_tmp, opa);
619
#endif
620
for(row = fill_area->y1; row <= fill_area->y2; row++) {
621
for(col = fill_area->x1; col <= fill_area->x2; col++) {
622
#if LV_COLOR_SCREEN_TRANSP == 0
623
/*If the bg color changed recalculate the result color*/
624
if(mem[col].full != bg_tmp.full) {
625
bg_tmp = mem[col];
626
opa_tmp = lv_color_mix(color, bg_tmp, opa);
627
}
628
629
mem[col] = opa_tmp;
630
631
#else
632
mem[col] = color_mix_2_alpha(mem[col], mem[col].alpha, color, opa);
633
#endif
634
}
635
mem += mem_width;
636
}
637
}
638
}
639
}
640
641
#if LV_COLOR_SCREEN_TRANSP
642
643
/**
644
* Mix two colors. Both color can have alpha value. It requires ARGB888 colors.
645
* @param bg_color background color
646
* @param bg_opa alpha of the background color
647
* @param fg_color foreground color
648
* @param fg_opa alpha of the foreground color
649
* @return the mixed color. the alpha channel (color.alpha) contains the result alpha
650
*/
651
static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa)
652
{
653
/* Pick the foreground if it's fully opaque or the Background is fully transparent*/
654
if(fg_opa == LV_OPA_COVER && bg_opa <= LV_OPA_MIN) {
655
fg_color.alpha = fg_opa;
656
return fg_color;
657
}
658
/*Transparent foreground: use the Background*/
659
else if(fg_opa <= LV_OPA_MIN) {
660
return bg_color;
661
}
662
/*Opaque background: use simple mix*/
663
else if(bg_opa >= LV_OPA_MAX) {
664
return lv_color_mix(fg_color, bg_color, fg_opa);
665
}
666
/*Both colors have alpha. Expensive calculation need to be applied*/
667
else {
668
/*Save the parameters and the result. If they will be asked again don't compute again*/
669
static lv_opa_t fg_opa_save = 0;
670
static lv_opa_t bg_opa_save = 0;
671
static lv_color_t c = {{0}};
672
673
if(fg_opa != fg_opa_save || bg_opa != bg_opa_save) {
674
fg_opa_save = fg_opa;
675
bg_opa_save = bg_opa;
676
/*Info: https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
677
lv_opa_t alpha_res = 255 - ((uint16_t)((uint16_t)(255 - fg_opa) * (255 - bg_opa)) >> 8);
678
if(alpha_res == 0) {
679
while(1);
680
}
681
lv_opa_t ratio = (uint16_t)((uint16_t) fg_opa * 255) / alpha_res;
682
c = lv_color_mix(fg_color, bg_color, ratio);
683
c.alpha = alpha_res;
684
}
685
return c;
686
687
}
688
}
689
#endif /*LV_COLOR_SCREEN_TRANSP*/
690
691
#endif
692
693