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