1 /**
2 * @file lv_draw_vglite_blend.c
3 *
4 */
5
6 /**
7 * Copyright 2020-2024 NXP
8 *
9 * SPDX-License-Identifier: MIT
10 */
11
12 /*********************
13 * INCLUDES
14 *********************/
15
16 #include "lv_draw_vglite.h"
17
18 #if LV_USE_DRAW_VGLITE
19 #include "lv_vglite_buf.h"
20 #include "lv_vglite_matrix.h"
21 #include "lv_vglite_utils.h"
22 #include "lv_vglite_path.h"
23
24 #include "../../../misc/lv_log.h"
25
26 /*********************
27 * DEFINES
28 *********************/
29
30 #if LV_USE_VGLITE_BLIT_SPLIT
31 /**
32 * BLIT split threshold - BLITs with width or height higher than this value will
33 * be done in multiple steps. Value must be multiple of stride alignment in px.
34 * For most color formats the alignment is 16px (except the index formats).
35 */
36 #define VGLITE_BLIT_SPLIT_THR 352
37
38 /* Enable for logging debug traces. */
39 #define VGLITE_LOG_TRACE 0
40
41 #if VGLITE_LOG_TRACE
42 #define VGLITE_TRACE(fmt, ...) \
43 do { \
44 LV_LOG(fmt, ##__VA_ARGS__); \
45 } while (0)
46 #else
47 #define VGLITE_TRACE(fmt, ...) \
48 do { \
49 } while (0)
50 #endif
51 #endif /*LV_USE_VGLITE_BLIT_SPLIT*/
52
53 /**********************
54 * TYPEDEFS
55 **********************/
56
57 /**********************
58 * STATIC PROTOTYPES
59 **********************/
60
61 #if LV_USE_VGLITE_BLIT_SPLIT
62 /**
63 * Move buffer pointer as close as possible to area, but with respect to alignment requirements.
64 *
65 * @param[in] buf Buffer address pointer
66 * @param[in] area Area with relative coordinates to the buffer
67 * @param[in] stride Stride of buffer in bytes
68 * @param[in] cf Color format of buffer
69 */
70 static void _move_buf_close_to_area(void ** buf, lv_area_t * area, uint32_t stride, lv_color_format_t cf);
71
72 /**
73 * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects.
74 * By default, image is copied directly, with optional opacity.
75 *
76 * @param dest_buf Destination buffer
77 * @param[in] dest_area Destination area with relative coordinates to dest buffer
78 * @param[in] dest_stride Stride of destination buffer in bytes
79 * @param[in] dest_cf Color format of destination buffer
80 * @param[in] src_buf Source buffer
81 * @param[in] src_area Source area with relative coordinates to src buffer
82 * @param[in] src_stride Stride of source buffer in bytes
83 * @param[in] src_cf Color format of source buffer
84 * @param[in] dsc Image descriptor
85 *
86 */
87 static void _vglite_blit_split(void * dest_buf, lv_area_t * dest_area, uint32_t dest_stride, lv_color_format_t dest_cf,
88 const void * src_buf, lv_area_t * src_area, uint32_t src_stride, lv_color_format_t src_cf,
89 const lv_draw_image_dsc_t * dsc);
90 #endif /*LV_USE_VGLITE_BLIT_SPLIT*/
91
92 /**
93 * VGlite blit - fill a path with an image pattern
94 *
95 *
96 * @param[in] dest_area Destination area with relative coordinates to dest buffer
97 * @param[in] clip_area Clip area with relative coordinates to dest buff
98 * @param[in] coords Coordinates of the image (relative to dest buff)
99 * @param[in] dsc Image descriptor
100 *
101 */
102 static void _vglite_draw_pattern(const lv_area_t * clip_area, const lv_area_t * coords,
103 const lv_draw_image_dsc_t * dsc);
104
105 /**
106 * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with or without effects.
107 *
108 * @param[in] src_area Source area with relative coordinates to src buffer
109 * @param[in] dsc Image descriptor
110 *
111 */
112 static void _vglite_blit(const lv_area_t * src_area, const lv_draw_image_dsc_t * dsc);
113
114 static vg_lite_color_t _vglite_recolor(const lv_draw_image_dsc_t * dsc);
115
116 /**********************
117 * STATIC VARIABLES
118 **********************/
119
120 /**********************
121 * MACROS
122 **********************/
123
124 /**********************
125 * GLOBAL FUNCTIONS
126 **********************/
127
lv_draw_vglite_img(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * dsc,const lv_area_t * coords)128 void lv_draw_vglite_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc,
129 const lv_area_t * coords)
130 {
131 if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
132 return;
133
134 lv_layer_t * layer = draw_unit->target_layer;
135 const lv_image_dsc_t * img_dsc = dsc->src;
136
137 lv_area_t relative_coords;
138 lv_area_copy(&relative_coords, coords);
139 lv_area_move(&relative_coords, -layer->buf_area.x1, -layer->buf_area.y1);
140
141 lv_area_t clip_area;
142 lv_area_copy(&clip_area, draw_unit->clip_area);
143 lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
144
145 lv_area_t blend_area;
146 bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
147 if(has_transform)
148 lv_area_copy(&blend_area, &relative_coords);
149 else if(!lv_area_intersect(&blend_area, &relative_coords, &clip_area))
150 return; /*Fully clipped, nothing to do*/
151
152 const void * src_buf = img_dsc->data;
153
154 lv_area_t src_area;
155 src_area.x1 = blend_area.x1 - (coords->x1 - layer->buf_area.x1);
156 src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1);
157 src_area.x2 = img_dsc->header.w - 1;
158 src_area.y2 = img_dsc->header.h - 1;
159
160 lv_color_format_t src_cf = img_dsc->header.cf;
161 uint32_t src_stride = img_dsc->header.stride;
162
163 /* Set src_vgbuf structure. */
164 vglite_set_src_buf(src_buf, img_dsc->header.w, img_dsc->header.h, src_stride, src_cf);
165
166 #if LV_USE_VGLITE_BLIT_SPLIT
167 void * dest_buf = layer->draw_buf->data;
168 uint32_t dest_stride = layer->draw_buf->header.stride;
169 lv_color_format_t dest_cf = layer->draw_buf->header.cf;
170
171 if(!has_transform)
172 _vglite_blit_split(dest_buf, &blend_area, dest_stride, dest_cf,
173 src_buf, &src_area, src_stride, src_cf, dsc);
174 #else
175 vglite_set_transformation_matrix(&blend_area, dsc);
176 bool is_tiled = dsc->tile;
177 if(is_tiled)
178 _vglite_draw_pattern(&clip_area, &relative_coords, dsc);
179 else
180 _vglite_blit(&src_area, dsc);
181 #endif /*LV_USE_VGLITE_BLIT_SPLIT*/
182 }
183
184 /**********************
185 * STATIC FUNCTIONS
186 **********************/
_vglite_blit(const lv_area_t * src_area,const lv_draw_image_dsc_t * dsc)187 static void _vglite_blit(const lv_area_t * src_area, const lv_draw_image_dsc_t * dsc)
188 {
189 vg_lite_buffer_t * dst_vgbuf = vglite_get_dest_buf();
190 vg_lite_buffer_t * src_vgbuf = vglite_get_src_buf();
191
192 vg_lite_rectangle_t rect = {
193 .x = (vg_lite_int32_t)src_area->x1,
194 .y = (vg_lite_int32_t)src_area->y1,
195 .width = (vg_lite_int32_t)lv_area_get_width(src_area),
196 .height = (vg_lite_int32_t)lv_area_get_height(src_area)
197 };
198
199 src_vgbuf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
200 src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
201
202 vg_lite_color_t vgcol = _vglite_recolor(dsc);
203
204 vg_lite_matrix_t * vgmatrix = vglite_get_matrix();
205 vg_lite_blend_t vgblend = vglite_get_blend_mode(dsc->blend_mode);
206
207 VGLITE_CHECK_ERROR(vg_lite_blit_rect(dst_vgbuf, src_vgbuf, &rect, vgmatrix, vgblend, vgcol, VG_LITE_FILTER_POINT));
208
209 vglite_run();
210 }
211
212 #if LV_USE_VGLITE_BLIT_SPLIT
_move_buf_close_to_area(void ** buf,lv_area_t * area,uint32_t stride,lv_color_format_t cf)213 static void _move_buf_close_to_area(void ** buf, lv_area_t * area, uint32_t stride, lv_color_format_t cf)
214 {
215 uint8_t ** buf_u8 = (uint8_t **)buf;
216 uint8_t align_bytes = vglite_get_stride_alignment(cf);
217 uint8_t bits_per_pixel = lv_color_format_get_bpp(cf);
218
219 uint16_t align_pixels = align_bytes * 8 / bits_per_pixel;
220
221 if(area->x1 >= (int32_t)(area->x1 % align_pixels)) {
222 uint16_t shift_x = area->x1 - (area->x1 % align_pixels);
223
224 area->x1 -= shift_x;
225 area->x2 -= shift_x;
226 *buf_u8 += (shift_x * bits_per_pixel) / 8;
227 }
228
229 if(area->y1) {
230 uint16_t shift_y = area->y1;
231
232 area->y1 -= shift_y;
233 area->y2 -= shift_y;
234 *buf_u8 += shift_y * stride;
235 }
236 }
237
_vglite_blit_split(void * dest_buf,lv_area_t * dest_area,uint32_t dest_stride,lv_color_format_t dest_cf,const void * src_buf,lv_area_t * src_area,uint32_t src_stride,lv_color_format_t src_cf,const lv_draw_image_dsc_t * dsc)238 static void _vglite_blit_split(void * dest_buf, lv_area_t * dest_area, uint32_t dest_stride, lv_color_format_t dest_cf,
239 const void * src_buf, lv_area_t * src_area, uint32_t src_stride, lv_color_format_t src_cf,
240 const lv_draw_image_dsc_t * dsc)
241 {
242 VGLITE_TRACE("Blit "
243 "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
244 "Size: ([%dx%d] -> [%dx%d]) | "
245 "Addr: (0x%x -> 0x%x)",
246 src_area->x1, src_area->y1, src_area->x2, src_area->y2,
247 dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
248 lv_area_get_width(src_area), lv_area_get_height(src_area),
249 lv_area_get_width(dest_area), lv_area_get_height(dest_area),
250 (uintptr_t)src_buf, (uintptr_t)dest_buf);
251
252 /* Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible */
253 _move_buf_close_to_area((void **)&src_buf, src_area, src_stride, src_cf);
254 _move_buf_close_to_area(&dest_buf, dest_area, dest_stride, dest_cf);
255
256 /* Set clip area */
257 vglite_set_scissor(dest_area);
258
259 /* If we're in limit, do a single BLIT */
260 if((src_area->x2 < VGLITE_BLIT_SPLIT_THR) &&
261 (src_area->y2 < VGLITE_BLIT_SPLIT_THR)) {
262
263 /* Set new dest_vgbuf and src_vgbuf memory addresses */
264 vglite_set_dest_buf_ptr(dest_buf);
265 vglite_set_src_buf_ptr(src_buf);
266
267 vglite_set_transformation_matrix(dest_area, dsc);
268 _vglite_blit(src_area, dsc);
269
270 VGLITE_TRACE("Single "
271 "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
272 "Size: ([%dx%d] -> [%dx%d]) | "
273 "Addr: (0x%x -> 0x%x)",
274 src_area->x1, src_area->y1, src_area->x2, src_area->y2,
275 dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
276 lv_area_get_width(src_area), lv_area_get_height(src_area),
277 lv_area_get_width(dest_area), lv_area_get_height(dest_area),
278 (uintptr_t)src_buf, (uintptr_t)dest_buf);
279
280 return;
281 };
282
283 /* Split the BLIT into multiple tiles */
284 VGLITE_TRACE("Split "
285 "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
286 "Size: ([%dx%d] -> [%dx%d]) | "
287 "Addr: (0x%x -> 0x%x)",
288 src_area->x1, src_area->y1, src_area->x2, src_area->y2,
289 dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
290 lv_area_get_width(src_area), lv_area_get_height(src_area),
291 lv_area_get_width(dest_area), lv_area_get_height(dest_area),
292 (uintptr_t)src_buf, (uintptr_t)dest_buf);
293
294 int32_t width = LV_MIN(lv_area_get_width(src_area), lv_area_get_width(dest_area));
295 int32_t height = LV_MIN(lv_area_get_height(src_area), lv_area_get_height(dest_area));
296
297 /* Number of tiles needed */
298 uint8_t total_tiles_x = (src_area->x1 + width + VGLITE_BLIT_SPLIT_THR - 1) /
299 VGLITE_BLIT_SPLIT_THR;
300 uint8_t total_tiles_y = (src_area->y1 + height + VGLITE_BLIT_SPLIT_THR - 1) /
301 VGLITE_BLIT_SPLIT_THR;
302
303 uint16_t shift_src_x = src_area->x1;
304 uint16_t shift_dest_x = dest_area->x1;
305
306 VGLITE_TRACE("X shift: src: %d, dst: %d", shift_src_x, shift_dest_x);
307
308 uint8_t * tile_dest_buf;
309 lv_area_t tile_dest_area;
310 const uint8_t * tile_src_buf;
311 lv_area_t tile_src_area;
312
313 for(uint8_t y = 0; y < total_tiles_y; y++) {
314 /* y1 always start from 0 */
315 tile_src_area.y1 = 0;
316
317 /* Calculate y2 coordinates */
318 if(y < total_tiles_y - 1)
319 tile_src_area.y2 = VGLITE_BLIT_SPLIT_THR - 1;
320 else
321 tile_src_area.y2 = height - y * VGLITE_BLIT_SPLIT_THR - 1;
322
323 /* No vertical shift, dest y is always in sync with src y */
324 tile_dest_area.y1 = tile_src_area.y1;
325 tile_dest_area.y2 = tile_src_area.y2;
326
327 /* Advance start pointer for every tile, except the first column (y = 0) */
328 tile_src_buf = (uint8_t *)src_buf + y * VGLITE_BLIT_SPLIT_THR * src_stride;
329 tile_dest_buf = (uint8_t *)dest_buf + y * VGLITE_BLIT_SPLIT_THR * dest_stride;
330
331 for(uint8_t x = 0; x < total_tiles_x; x++) {
332 /* x1 always start from the same shift */
333 tile_src_area.x1 = shift_src_x;
334 tile_dest_area.x1 = shift_dest_x;
335 if(x > 0) {
336 /* Advance start pointer for every tile, except the first raw (x = 0) */
337 tile_src_buf += VGLITE_BLIT_SPLIT_THR * lv_color_format_get_bpp(src_cf) / 8;
338 tile_dest_buf += VGLITE_BLIT_SPLIT_THR * lv_color_format_get_bpp(dest_cf) / 8;
339 }
340
341 /* Calculate x2 coordinates */
342 if(x < total_tiles_x - 1)
343 tile_src_area.x2 = VGLITE_BLIT_SPLIT_THR - 1;
344 else
345 tile_src_area.x2 = width - x * VGLITE_BLIT_SPLIT_THR - 1;
346
347 tile_dest_area.x2 = tile_src_area.x2;
348
349 /* Shift x2 coordinates */
350 tile_src_area.x2 += shift_src_x;
351 tile_dest_area.x2 += shift_dest_x;
352
353 /* Set new dest_vgbuf and src_vgbuf memory addresses */
354 vglite_set_dest_buf_ptr(tile_dest_buf);
355 vglite_set_src_buf_ptr(tile_src_buf);
356
357 vglite_set_transformation_matrix(&tile_dest_area, dsc);
358 _vglite_blit(&tile_src_area, dsc);
359
360 VGLITE_TRACE("Tile [%d, %d] "
361 "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
362 "Size: ([%dx%d] -> [%dx%d]) | "
363 "Addr: (0x%x -> 0x%x)",
364 x, y,
365 tile_src_area.x1, tile_src_area.y1, tile_src_area.x2, tile_src_area.y2,
366 tile_dest_area.x1, tile_dest_area.y1, tile_dest_area.x2, tile_dest_area.y2,
367 lv_area_get_width(&tile_src_area), lv_area_get_height(&tile_src_area),
368 lv_area_get_width(&tile_dest_area), lv_area_get_height(&tile_dest_area),
369 (uintptr_t)tile_src_buf, (uintptr_t)tile_dest_buf);
370 }
371 }
372 }
373 #endif /*LV_USE_VGLITE_BLIT_SPLIT*/
374
_vglite_draw_pattern(const lv_area_t * clip_area,const lv_area_t * coords,const lv_draw_image_dsc_t * dsc)375 static void _vglite_draw_pattern(const lv_area_t * clip_area, const lv_area_t * coords,
376 const lv_draw_image_dsc_t * dsc)
377 {
378 /* Target buffer */
379 vg_lite_buffer_t * dst_vgbuf = vglite_get_dest_buf();
380
381 /* Path to draw */
382 int32_t path_data[RECT_PATH_DATA_MAX_SIZE];
383 uint32_t path_data_size;
384 vglite_create_rect_path_data(path_data, &path_data_size, dsc->clip_radius, coords);
385 vg_lite_quality_t path_quality = VG_LITE_MEDIUM;
386
387 vg_lite_path_t path;
388 VGLITE_CHECK_ERROR(vg_lite_init_path(&path, VG_LITE_S32, path_quality, path_data_size, path_data,
389 (vg_lite_float_t)clip_area->x1, (vg_lite_float_t)clip_area->y1,
390 ((vg_lite_float_t)clip_area->x2) + 1.0f, ((vg_lite_float_t)clip_area->y2) + 1.0f));
391
392 /* Pattern Image */
393 vg_lite_buffer_t * src_vgbuf = vglite_get_src_buf();
394 src_vgbuf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
395 src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
396
397 /* Pattern matrix */
398 vg_lite_matrix_t vgmatrix;
399 vg_lite_identity(&vgmatrix);
400 vg_lite_translate((vg_lite_float_t)dsc->image_area.x1, (vg_lite_float_t)dsc->image_area.y1, &vgmatrix);
401
402 /* Blend mode */
403 vg_lite_blend_t vgblend = vglite_get_blend_mode(dsc->blend_mode);
404
405 vg_lite_color_t vgcol = _vglite_recolor(dsc);
406
407 /* Filter */
408 bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
409 vg_lite_filter_t filter = has_transform ? VG_LITE_FILTER_BI_LINEAR : VG_LITE_FILTER_POINT;
410
411 /* Draw Pattern */
412 VGLITE_CHECK_ERROR(vg_lite_draw_pattern(dst_vgbuf, &path, VG_LITE_FILL_NON_ZERO, NULL,
413 src_vgbuf, &vgmatrix, vgblend, VG_LITE_PATTERN_REPEAT,
414 0, vgcol, filter));
415 }
416
_vglite_recolor(const lv_draw_image_dsc_t * dsc)417 static vg_lite_color_t _vglite_recolor(const lv_draw_image_dsc_t * dsc)
418 {
419 lv_color_t color;
420 lv_opa_t opa;
421
422 bool has_recolor = (dsc->recolor_opa > LV_OPA_MIN);
423 if(has_recolor) {
424 color = dsc->recolor;
425 opa = LV_OPA_MIX2(dsc->recolor_opa, dsc->opa);
426 }
427 else {
428 color.red = 0xFF;
429 color.green = 0xFF;
430 color.blue = 0xFF;
431 opa = dsc->opa;
432 }
433
434 lv_color32_t col32 = lv_color_to_32(color, opa);
435
436 return vglite_get_color(col32, false);
437 }
438
439 #endif /*LV_USE_DRAW_VGLITE*/
440