1 /**
2  * @file lv_draw_vglite_blend.c
3  *
4  */
5 
6 /**
7  * MIT License
8  *
9  * Copyright 2020-2023 NXP
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights to
14  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15  * the Software, and to permit persons to whom the Software is furnished to do so,
16  * subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice (including the next paragraph)
19  * shall be included in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
22  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
26  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  *
28  */
29 
30 /*********************
31  *      INCLUDES
32  *********************/
33 
34 #include "lv_draw_vglite_blend.h"
35 
36 #if LV_USE_GPU_NXP_VG_LITE
37 #include "lv_vglite_buf.h"
38 #include "lv_vglite_utils.h"
39 
40 /*********************
41  *      DEFINES
42  *********************/
43 
44 /** Stride in px required by VG-Lite HW*/
45 #define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U
46 
47 #if VG_LITE_BLIT_SPLIT_ENABLED
48     /**
49     * BLIT split threshold - BLITs with width or height higher than this value will be done
50     * in multiple steps. Value must be 16-aligned. Don't change.
51     */
52     #define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352
53 #endif
54 
55 /**********************
56  *      TYPEDEFS
57  **********************/
58 
59 /**********************
60  *  STATIC PROTOTYPES
61  **********************/
62 
63 /**
64  * Blit image, with optional opacity.
65  *
66  * @param[in] src_area Source area with relative coordinates of source buffer
67  * @param[in] opa Opacity
68  *
69  * @retval LV_RES_OK Transfer complete
70  * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
71  */
72 static lv_res_t lv_vglite_blit(const lv_area_t * src_area, lv_opa_t opa);
73 
74 /**
75  * Check source memory and stride alignment.
76  *
77  * @param[in] src_buf Source buffer
78  * @param[in] src_stride Stride of source buffer in pixels
79  *
80  * @retval LV_RES_OK Alignment OK
81  * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
82  */
83 static lv_res_t check_src_alignment(const lv_color_t * src_buf, lv_coord_t src_stride);
84 
85 /**
86  * Creates matrix that translates to origin of given destination area.
87  *
88  * @param[in] dest_area Area with relative coordinates of destination buffer
89  */
90 static inline void lv_vglite_set_translation_matrix(const lv_area_t * dest_area);
91 
92 /**
93  * Creates matrix that translates to origin of given destination area with transformation (scale or rotate).
94  *
95  * @param[in] dest_area Area with relative coordinates of destination buffer
96  * @param[in] dsc Image descriptor
97  */
98 static inline void lv_vglite_set_transformation_matrix(const lv_area_t * dest_area, const lv_draw_img_dsc_t * dsc);
99 
100 #if VG_LITE_BLIT_SPLIT_ENABLED
101 /**
102  * Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only.
103  *
104  * @param[in/out] area Area to be updated
105  * @param[in/out] buf Pointer to be updated
106  */
107 static void align_x(lv_area_t * area, lv_color_t ** buf);
108 
109 /**
110  * Move buffer pointer to the area start and update variables, Y-axis only.
111  *
112  * @param[in/out] area Area to be updated
113  * @param[in/out] buf Pointer to be updated
114  * @param[in] stride Buffer stride in pixels
115  */
116 static void align_y(lv_area_t * area, lv_color_t ** buf, lv_coord_t stride);
117 
118 /**
119  * Blit image split in tiles, with optional opacity.
120  *
121  * @param[in/out] dest_buf Destination buffer
122  * @param[in] dest_area Area with relative coordinates of destination buffer
123  * @param[in] dest_stride Stride of destination buffer in pixels
124  * @param[in] src_buf Source buffer
125  * @param[in] src_area Source area with relative coordinates of source buffer
126  * @param[in] src_stride Stride of source buffer in pixels
127  * @param[in] opa Opacity
128  *
129  * @retval LV_RES_OK Transfer complete
130  * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS)
131  */
132 static lv_res_t lv_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
133                                      const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
134                                      lv_opa_t opa);
135 #endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
136 
137 /**********************
138  *  STATIC VARIABLES
139  **********************/
140 
141 static vg_lite_matrix_t vgmatrix;
142 
143 /**********************
144  *      MACROS
145  **********************/
146 
147 /**********************
148  *   GLOBAL FUNCTIONS
149  **********************/
150 
lv_gpu_nxp_vglite_fill(const lv_area_t * dest_area,lv_color_t color,lv_opa_t opa)151 lv_res_t lv_gpu_nxp_vglite_fill(const lv_area_t * dest_area, lv_color_t color, lv_opa_t opa)
152 {
153     vg_lite_error_t err = VG_LITE_SUCCESS;
154     lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/
155     vg_lite_color_t vgcol; /* vglite takes ABGR */
156     vg_lite_buffer_t * vgbuf = lv_vglite_get_dest_buf();
157 
158     vg_lite_buffer_format_t color_format = LV_COLOR_DEPTH == 16 ? VG_LITE_BGRA8888 : VG_LITE_ABGR8888;
159     if(lv_vglite_premult_and_swizzle(&vgcol, col32, opa, color_format) != LV_RES_OK)
160         VG_LITE_RETURN_INV("Premultiplication and swizzle failed.");
161 
162     if(opa >= (lv_opa_t)LV_OPA_MAX) {   /*Opaque fill*/
163         vg_lite_rectangle_t rect = {
164             .x = dest_area->x1,
165             .y = dest_area->y1,
166             .width = lv_area_get_width(dest_area),
167             .height = lv_area_get_height(dest_area)
168         };
169 
170         err = vg_lite_clear(vgbuf, &rect, vgcol);
171         VG_LITE_ERR_RETURN_INV(err, "Clear failed.");
172 
173         if(lv_vglite_run() != LV_RES_OK)
174             VG_LITE_RETURN_INV("Run failed.");
175     }
176     else {   /*fill with transparency*/
177 
178         vg_lite_path_t path;
179         int32_t path_data[] = { /*VG rectangular path*/
180             VLC_OP_MOVE, dest_area->x1,  dest_area->y1,
181             VLC_OP_LINE, dest_area->x2 + 1,  dest_area->y1,
182             VLC_OP_LINE, dest_area->x2 + 1,  dest_area->y2 + 1,
183             VLC_OP_LINE, dest_area->x1,  dest_area->y2 + 1,
184             VLC_OP_LINE, dest_area->x1,  dest_area->y1,
185             VLC_OP_END
186         };
187 
188         err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_LOW, sizeof(path_data), path_data,
189                                 (vg_lite_float_t) dest_area->x1, (vg_lite_float_t) dest_area->y1,
190                                 ((vg_lite_float_t) dest_area->x2) + 1.0f, ((vg_lite_float_t) dest_area->y2) + 1.0f);
191         VG_LITE_ERR_RETURN_INV(err, "Init path failed.");
192 
193         vg_lite_matrix_t matrix;
194         vg_lite_identity(&matrix);
195 
196         /*Draw rectangle*/
197         err = vg_lite_draw(vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol);
198         VG_LITE_ERR_RETURN_INV(err, "Draw rectangle failed.");
199 
200         if(lv_vglite_run() != LV_RES_OK)
201             VG_LITE_RETURN_INV("Run failed.");
202 
203         err = vg_lite_clear_path(&path);
204         VG_LITE_ERR_RETURN_INV(err, "Clear path failed.");
205     }
206 
207     return LV_RES_OK;
208 }
209 
210 #if VG_LITE_BLIT_SPLIT_ENABLED
lv_gpu_nxp_vglite_blit_split(lv_color_t * dest_buf,lv_area_t * dest_area,lv_coord_t dest_stride,const lv_color_t * src_buf,lv_area_t * src_area,lv_coord_t src_stride,lv_opa_t opa)211 lv_res_t lv_gpu_nxp_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
212                                       const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
213                                       lv_opa_t opa)
214 {
215     /* Set src_vgbuf structure. */
216     lv_vglite_set_src_buf(src_buf, src_area, src_stride);
217 
218     lv_color_t * orig_dest_buf = dest_buf;
219 
220     lv_res_t rv = lv_vglite_blit_split(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, opa);
221 
222     /* Restore the original dest_vgbuf memory address. */
223     lv_vglite_set_dest_buf_ptr(orig_dest_buf);
224 
225     return rv;
226 }
227 #else
lv_gpu_nxp_vglite_blit(const lv_area_t * dest_area,const lv_color_t * src_buf,const lv_area_t * src_area,lv_coord_t src_stride,lv_opa_t opa)228 lv_res_t lv_gpu_nxp_vglite_blit(const lv_area_t * dest_area,
229                                 const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
230                                 lv_opa_t opa)
231 {
232     if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
233         VG_LITE_RETURN_INV("Check src alignment failed.");
234 
235     /* Set src_vgbuf structure. */
236     lv_vglite_set_src_buf(src_buf, src_area, src_stride);
237 
238     /* Set scissor. */
239     lv_vglite_set_scissor(dest_area);
240 
241     /* Set vgmatrix. */
242     lv_vglite_set_translation_matrix(dest_area);
243 
244     /* Start blit. */
245     lv_res_t rv = lv_vglite_blit(src_area, opa);
246 
247     /* Disable scissor. */
248     lv_vglite_disable_scissor();
249 
250     return rv;
251 }
252 
lv_gpu_nxp_vglite_blit_transform(const lv_area_t * dest_area,const lv_area_t * clip_area,const lv_color_t * src_buf,const lv_area_t * src_area,lv_coord_t src_stride,const lv_draw_img_dsc_t * dsc)253 lv_res_t lv_gpu_nxp_vglite_blit_transform(const lv_area_t * dest_area, const lv_area_t * clip_area,
254                                           const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
255                                           const lv_draw_img_dsc_t * dsc)
256 {
257     lv_res_t rv = LV_RES_INV;
258 
259     if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
260         VG_LITE_RETURN_INV("Check src alignment failed.");
261 
262     /* Set src_vgbuf structure. */
263     lv_vglite_set_src_buf(src_buf, src_area, src_stride);
264 
265     /* Set scissor */
266     lv_vglite_set_scissor(clip_area);
267 
268     /* Set vgmatrix. */
269     lv_vglite_set_transformation_matrix(dest_area, dsc);
270 
271     /* Start blit. */
272     rv = lv_vglite_blit(src_area, dsc->opa);
273 
274     /* Disable scissor. */
275     lv_vglite_disable_scissor();
276 
277     return rv;
278 }
279 #endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
280 
lv_gpu_nxp_vglite_buffer_copy(lv_color_t * dest_buf,const lv_area_t * dest_area,lv_coord_t dest_stride,const lv_color_t * src_buf,const lv_area_t * src_area,lv_coord_t src_stride)281 lv_res_t lv_gpu_nxp_vglite_buffer_copy(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
282                                        const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride)
283 {
284     vg_lite_error_t err = VG_LITE_SUCCESS;
285 
286     if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
287         VG_LITE_RETURN_INV("Check src alignment failed.");
288 
289     vg_lite_buffer_t src_vgbuf;
290     /* Set src_vgbuf structure. */
291     lv_vglite_set_buf(&src_vgbuf, src_buf, src_area, src_stride);
292 
293     vg_lite_buffer_t dest_vgbuf;
294     /* Set dest_vgbuf structure. */
295     lv_vglite_set_buf(&dest_vgbuf, dest_buf, dest_area, dest_stride);
296 
297     uint32_t rect[] = {
298         (uint32_t)src_area->x1, /* start x */
299         (uint32_t)src_area->y1, /* start y */
300         (uint32_t)lv_area_get_width(src_area), /* width */
301         (uint32_t)lv_area_get_height(src_area) /* height */
302     };
303 
304     /* Set scissor. */
305     lv_vglite_set_scissor(dest_area);
306 
307     /* Set vgmatrix. */
308     lv_vglite_set_translation_matrix(dest_area);
309 
310     err = vg_lite_blit_rect(&dest_vgbuf, &src_vgbuf, rect, &vgmatrix,
311                             VG_LITE_BLEND_NONE, 0xFFFFFFFFU, VG_LITE_FILTER_POINT);
312     if(err != VG_LITE_SUCCESS) {
313         LV_LOG_ERROR("Blit rectangle failed.");
314         /* Disable scissor. */
315         lv_vglite_disable_scissor();
316 
317         return LV_RES_INV;
318     }
319 
320     if(lv_vglite_run() != LV_RES_OK) {
321         LV_LOG_ERROR("Run failed.");
322         /* Disable scissor. */
323         lv_vglite_disable_scissor();
324 
325         return LV_RES_INV;
326     }
327 
328     /* Disable scissor. */
329     lv_vglite_disable_scissor();
330 
331     return LV_RES_OK;
332 }
333 
334 /**********************
335  *   STATIC FUNCTIONS
336  **********************/
337 
338 #if VG_LITE_BLIT_SPLIT_ENABLED
lv_vglite_blit_split(lv_color_t * dest_buf,lv_area_t * dest_area,lv_coord_t dest_stride,const lv_color_t * src_buf,lv_area_t * src_area,lv_coord_t src_stride,lv_opa_t opa)339 static lv_res_t lv_vglite_blit_split(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
340                                      const lv_color_t * src_buf, lv_area_t * src_area, lv_coord_t src_stride,
341                                      lv_opa_t opa)
342 {
343     lv_res_t rv = LV_RES_INV;
344 
345     VG_LITE_LOG_TRACE("Blit "
346                       "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
347                       "Size: ([%dx%d] -> [%dx%d]) | "
348                       "Addr: (0x%x -> 0x%x)",
349                       src_area->x1, src_area->y1, src_area->x2, src_area->y2,
350                       dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
351                       lv_area_get_width(src_area), lv_area_get_height(src_area),
352                       lv_area_get_width(dest_area), lv_area_get_height(dest_area),
353                       (uintptr_t)src_buf, (uintptr_t)dest_buf);
354 
355     /* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */
356     align_x(src_area, (lv_color_t **)&src_buf);
357     align_y(src_area, (lv_color_t **)&src_buf, src_stride);
358     align_x(dest_area, (lv_color_t **)&dest_buf);
359     align_y(dest_area, (lv_color_t **)&dest_buf, dest_stride);
360 
361     /* Stage 2: If we're in limit, do a single BLIT */
362     if((src_area->x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) &&
363        (src_area->y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) {
364         if(check_src_alignment(src_buf, src_stride) != LV_RES_OK)
365             VG_LITE_RETURN_INV("Check src alignment failed.");
366 
367         /* Set new dest_vgbuf and src_vgbuf memory addresses. */
368         lv_vglite_set_dest_buf_ptr(dest_buf);
369         lv_vglite_set_src_buf_ptr(src_buf);
370 
371         /* Set scissor */
372         lv_vglite_set_scissor(dest_area);
373 
374         /* Set vgmatrix. */
375         lv_vglite_set_translation_matrix(dest_area);
376 
377         /* Start blit. */
378         rv = lv_vglite_blit(src_area, opa);
379 
380         /* Disable scissor. */
381         lv_vglite_disable_scissor();
382 
383         VG_LITE_LOG_TRACE("Single "
384                           "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
385                           "Size: ([%dx%d] -> [%dx%d]) | "
386                           "Addr: (0x%x -> 0x%x) %s",
387                           src_area->x1, src_area->y1, src_area->x2, src_area->y2,
388                           dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
389                           lv_area_get_width(src_area), lv_area_get_height(src_area),
390                           lv_area_get_width(dest_area), lv_area_get_height(dest_area),
391                           (uintptr_t)src_buf, (uintptr_t)dest_buf,
392                           rv == LV_RES_OK ? "OK!" : "FAILED!");
393 
394         return rv;
395     };
396 
397     /* Stage 3: Split the BLIT into multiple tiles */
398     VG_LITE_LOG_TRACE("Split "
399                       "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
400                       "Size: ([%dx%d] -> [%dx%d]) | "
401                       "Addr: (0x%x -> 0x%x)",
402                       src_area->x1, src_area->y1, src_area->x2, src_area->y2,
403                       dest_area->x1, dest_area->y1, dest_area->x2, dest_area->y2,
404                       lv_area_get_width(src_area), lv_area_get_height(src_area),
405                       lv_area_get_width(dest_area), lv_area_get_height(dest_area),
406                       (uintptr_t)src_buf, (uintptr_t)dest_buf);
407 
408     lv_coord_t width = lv_area_get_width(src_area);
409     lv_coord_t height = lv_area_get_height(src_area);
410 
411     /* Number of tiles needed */
412     int total_tiles_x = (src_area->x1 + width + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
413                         LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
414     int total_tiles_y = (src_area->y1 + height + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) /
415                         LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
416 
417     /* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as
418      * the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be
419      * different */
420     int shift_src_x = (src_area->x1 > dest_area->x1) ? (src_area->x1 - dest_area->x1) : 0;
421     int shift_dest_x = (src_area->x1 < dest_area->x1) ? (dest_area->x1 - src_area->x1) : 0;
422 
423     VG_LITE_LOG_TRACE("X shift: src: %d, dst: %d", shift_src_x, shift_dest_x);
424 
425     lv_color_t * tile_dest_buf;
426     lv_area_t tile_dest_area;
427     const lv_color_t * tile_src_buf;
428     lv_area_t tile_src_area;
429 
430     for(int y = 0; y < total_tiles_y; y++) {
431 
432         tile_src_area.y1 = 0; /* no vertical alignment, always start from 0 */
433         tile_src_area.y2 = height - y * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
434         if(tile_src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
435             tile_src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */
436         }
437         tile_src_buf = src_buf + y * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * src_stride;
438 
439         tile_dest_area.y1 = tile_src_area.y1; /* y has no alignment, always in sync with src */
440         tile_dest_area.y2 = tile_src_area.y2;
441 
442         tile_dest_buf = dest_buf + y * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * dest_stride;
443 
444         for(int x = 0; x < total_tiles_x; x++) {
445 
446             if(x == 0) {
447                 /* 1st tile is special - there may be a gap between buffer start pointer
448                  * and area.x1 value, as the pointer has to be aligned.
449                  * tile_src_buf pointer - keep init value from Y-loop.
450                  * Also, 1st tile start is not shifted! shift is applied from 2nd tile */
451                 tile_src_area.x1 = src_area->x1;
452                 tile_dest_area.x1 = dest_area->x1;
453             }
454             else {
455                 /* subsequent tiles always starts from 0, but shifted*/
456                 tile_src_area.x1 = 0 + shift_src_x;
457                 tile_dest_area.x1 = 0 + shift_dest_x;
458                 /* and advance start pointer + 1 tile size */
459                 tile_src_buf += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
460                 tile_dest_buf += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR;
461             }
462 
463             /* Clip tile end coordinates */
464             tile_src_area.x2 = width + src_area->x1 - x * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
465             if(tile_src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
466                 tile_src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
467             }
468 
469             tile_dest_area.x2 = width + dest_area->x1 - x * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
470             if(tile_dest_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) {
471                 tile_dest_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1;
472             }
473 
474             if(x < (total_tiles_x - 1)) {
475                 /* And adjust end coords if shifted, but not for last tile! */
476                 tile_src_area.x2 += shift_src_x;
477                 tile_dest_area.x2 += shift_dest_x;
478             }
479 
480             if(check_src_alignment(tile_src_buf, src_stride) != LV_RES_OK)
481                 VG_LITE_RETURN_INV("Check src alignment failed.");
482 
483             /* Set new dest_vgbuf and src_vgbuf memory addresses. */
484             lv_vglite_set_dest_buf_ptr(tile_dest_buf);
485             lv_vglite_set_src_buf_ptr(tile_src_buf);
486 
487             /* Set scissor */
488             lv_vglite_set_scissor(&tile_dest_area);
489 
490             /* Set vgmatrix. */
491             lv_vglite_set_translation_matrix(&tile_dest_area);
492 
493             /* Start blit. */
494             rv = lv_vglite_blit(&tile_src_area, opa);
495 
496             /* Disable scissor. */
497             lv_vglite_disable_scissor();
498 
499             VG_LITE_LOG_TRACE("Tile [%d, %d] "
500                               "Area: ([%d,%d], [%d,%d]) -> ([%d,%d], [%d,%d]) | "
501                               "Size: ([%dx%d] -> [%dx%d]) | "
502                               "Addr: (0x%x -> 0x%x) %s",
503                               x, y,
504                               tile_src_area.x1, tile_src_area.y1, tile_src_area.x2, tile_src_area.y2,
505                               tile_dest_area.x1, tile_dest_area.y1, tile_dest_area.x2, tile_dest_area.y2,
506                               lv_area_get_width(&tile_src_area), lv_area_get_height(&tile_src_area),
507                               lv_area_get_width(&tile_dest_area), lv_area_get_height(&tile_dest_area),
508                               (uintptr_t)tile_src_buf, (uintptr_t)tile_dest_buf,
509                               rv == LV_RES_OK ? "OK!" : "FAILED!");
510 
511             if(rv != LV_RES_OK) {
512                 return rv;
513             }
514         }
515     }
516 
517     return rv;
518 }
519 #endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
520 
lv_vglite_blit(const lv_area_t * src_area,lv_opa_t opa)521 static lv_res_t lv_vglite_blit(const lv_area_t * src_area, lv_opa_t opa)
522 {
523     vg_lite_error_t err = VG_LITE_SUCCESS;
524     vg_lite_buffer_t * dst_vgbuf = lv_vglite_get_dest_buf();
525     vg_lite_buffer_t * src_vgbuf = lv_vglite_get_src_buf();
526 
527     uint32_t rect[] = {
528         (uint32_t)src_area->x1, /* start x */
529         (uint32_t)src_area->y1, /* start y */
530         (uint32_t)lv_area_get_width(src_area), /* width */
531         (uint32_t)lv_area_get_height(src_area) /* height */
532     };
533 
534     uint32_t color;
535     vg_lite_blend_t blend;
536     if(opa >= (lv_opa_t)LV_OPA_MAX) {
537         color = 0xFFFFFFFFU;
538         blend = VG_LITE_BLEND_SRC_OVER;
539         src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
540     }
541     else {
542         if(vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) {
543             color = (opa << 24) | 0x00FFFFFFU;
544         }
545         else {
546             color = (opa << 24) | (opa << 16) | (opa << 8) | opa;
547         }
548         blend = VG_LITE_BLEND_SRC_OVER;
549         src_vgbuf->image_mode = VG_LITE_MULTIPLY_IMAGE_MODE;
550         src_vgbuf->transparency_mode = VG_LITE_IMAGE_TRANSPARENT;
551     }
552 
553     err = vg_lite_blit_rect(dst_vgbuf, src_vgbuf, rect, &vgmatrix, blend, color, VG_LITE_FILTER_POINT);
554     VG_LITE_ERR_RETURN_INV(err, "Blit rectangle failed.");
555 
556     if(lv_vglite_run() != LV_RES_OK)
557         VG_LITE_RETURN_INV("Run failed.");
558 
559     return LV_RES_OK;
560 }
561 
check_src_alignment(const lv_color_t * src_buf,lv_coord_t src_stride)562 static lv_res_t check_src_alignment(const lv_color_t * src_buf, lv_coord_t src_stride)
563 {
564     /* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */
565 
566     /* Test for pointer alignment */
567     if((((uintptr_t)src_buf) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != (uintptr_t)0x0U)
568         VG_LITE_RETURN_INV("Src buffer ptr (0x%x) not aligned to 0x%x bytes.",
569                            (size_t)src_buf, LV_ATTRIBUTE_MEM_ALIGN_SIZE);
570 
571     /* Test for stride alignment */
572     if((src_stride % (lv_coord_t)LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0x0U)
573         VG_LITE_RETURN_INV("Src buffer stride (%d px) not aligned to %d px.",
574                            src_stride, LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX);
575     return LV_RES_OK;
576 }
577 
lv_vglite_set_translation_matrix(const lv_area_t * dest_area)578 static inline void lv_vglite_set_translation_matrix(const lv_area_t * dest_area)
579 {
580     vg_lite_identity(&vgmatrix);
581     vg_lite_translate((vg_lite_float_t)dest_area->x1, (vg_lite_float_t)dest_area->y1, &vgmatrix);
582 }
583 
lv_vglite_set_transformation_matrix(const lv_area_t * dest_area,const lv_draw_img_dsc_t * dsc)584 static inline void lv_vglite_set_transformation_matrix(const lv_area_t * dest_area, const lv_draw_img_dsc_t * dsc)
585 {
586     lv_vglite_set_translation_matrix(dest_area);
587 
588     bool has_scale = (dsc->zoom != LV_IMG_ZOOM_NONE);
589     bool has_rotation = (dsc->angle != 0);
590 
591     vg_lite_translate(dsc->pivot.x, dsc->pivot.y, &vgmatrix);
592     if(has_rotation)
593         vg_lite_rotate(dsc->angle / 10.0f, &vgmatrix);   /* angle is 1/10 degree */
594     if(has_scale) {
595         vg_lite_float_t scale = 1.0f * dsc->zoom / LV_IMG_ZOOM_NONE;
596         vg_lite_scale(scale, scale, &vgmatrix);
597     }
598     vg_lite_translate(0.0f - dsc->pivot.x, 0.0f - dsc->pivot.y, &vgmatrix);
599 }
600 
601 #if VG_LITE_BLIT_SPLIT_ENABLED
align_x(lv_area_t * area,lv_color_t ** buf)602 static void align_x(lv_area_t * area, lv_color_t ** buf)
603 {
604     int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE / sizeof(lv_color_t)));
605     VG_LITE_COND_STOP(alignedAreaStartPx < 0, "Negative X alignment.");
606 
607     area->x1 -= alignedAreaStartPx;
608     area->x2 -= alignedAreaStartPx;
609     *buf += alignedAreaStartPx;
610 }
611 
align_y(lv_area_t * area,lv_color_t ** buf,lv_coord_t stride)612 static void align_y(lv_area_t * area, lv_color_t ** buf, lv_coord_t stride)
613 {
614     int LineToAlignMem;
615     int alignedAreaStartPy;
616     /* find how many lines of pixels will respect memory alignment requirement */
617     if((stride % (lv_coord_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) == 0x0U) {
618         alignedAreaStartPy = area->y1;
619     }
620     else {
621         LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t));
622         VG_LITE_COND_STOP(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t)),
623                           "Complex case: need gcd function.");
624         alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem);
625         VG_LITE_COND_STOP(alignedAreaStartPy < 0, "Negative Y alignment.");
626     }
627 
628     area->y1 -= alignedAreaStartPy;
629     area->y2 -= alignedAreaStartPy;
630     *buf += (uint32_t)(alignedAreaStartPy * stride);
631 }
632 #endif /*VG_LITE_BLIT_SPLIT_ENABLED*/
633 
634 #endif /*LV_USE_GPU_NXP_VG_LITE*/
635