1 /**
2  * @file lv_draw_pxp_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_pxp_blend.h"
35 
36 #if LV_USE_GPU_NXP_PXP
37 #include "lvgl_support.h"
38 
39 /*********************
40  *      DEFINES
41  *********************/
42 
43 #if LV_COLOR_16_SWAP
44     #error Color swap not implemented. Disable LV_COLOR_16_SWAP feature.
45 #endif
46 
47 #if LV_COLOR_DEPTH == 16
48     #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatRGB565
49     #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatRGB565
50     #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB565
51     #define PXP_TEMP_BUF_SIZE LCD_WIDTH * LCD_HEIGHT * 2U
52 #elif LV_COLOR_DEPTH == 32
53     #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatARGB8888
54     #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatARGB8888
55     #if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \
56         (!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3))
57         #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatARGB8888
58     #else
59         #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB888
60     #endif
61     #define PXP_TEMP_BUF_SIZE LCD_WIDTH * LCD_HEIGHT * 4U
62 #elif
63     #error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32.
64 #endif
65 
66 /**********************
67  *      TYPEDEFS
68  **********************/
69 
70 /**********************
71  *  STATIC PROTOTYPES
72  **********************/
73 
74 static LV_ATTRIBUTE_MEM_ALIGN uint8_t temp_buf[PXP_TEMP_BUF_SIZE];
75 
76 /**
77  * BLock Image Transfer - copy rectangular image from src buffer to dst buffer
78  * with combination of transformation (rotation, scale, recolor) and opacity, alpha channel
79  * or color keying. This requires two steps. First step is used for transformation into
80  * a temporary buffer and the second one will handle the color format or opacity.
81  *
82  * @param[in/out] dest_buf Destination buffer
83  * @param[in] dest_area Area with relative coordinates of destination buffer
84  * @param[in] dest_stride Stride of destination buffer in pixels
85  * @param[in] src_buf Source buffer
86  * @param[in] src_area Area with relative coordinates of source buffer
87  * @param[in] src_stride Stride of source buffer in pixels
88  * @param[in] dsc Image descriptor
89  * @param[in] cf Color format
90  */
91 static void lv_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
92                             const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
93                             const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
94 
95 /**
96  * BLock Image Transfer - copy rectangular image from src buffer to dst buffer
97  * with transformation and full opacity.
98  *
99  * @param[in/out] dest_buf Destination buffer
100  * @param[in] dest_area Area with relative coordinates of destination buffer
101  * @param[in] dest_stride Stride of destination buffer in pixels
102  * @param[in] src_buf Source buffer
103  * @param[in] src_area Area with relative coordinates of source buffer
104  * @param[in] src_stride Stride of source buffer in pixels
105  * @param[in] dsc Image descriptor
106  * @param[in] cf Color format
107  */
108 static void lv_pxp_blit_cover(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
109                               const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
110                               const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
111 
112 /**
113  * BLock Image Transfer - copy rectangular image from src buffer to dst buffer
114  * without transformation but handling color format or opacity.
115  *
116  * @param[in/out] dest_buf Destination buffer
117  * @param[in] dest_area Area with relative coordinates of destination buffer
118  * @param[in] dest_stride Stride of destination buffer in pixels
119  * @param[in] src_buf Source buffer
120  * @param[in] src_area Area with relative coordinates of source buffer
121  * @param[in] src_stride Stride of source buffer in pixels
122  * @param[in] dsc Image descriptor
123  * @param[in] cf Color format
124  */
125 static void lv_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
126                            const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
127                            const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf);
128 
129 /**********************
130  *  STATIC VARIABLES
131  **********************/
132 
133 /**********************
134  *      MACROS
135  **********************/
136 
137 /**********************
138  *   GLOBAL FUNCTIONS
139  **********************/
140 
lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf,const lv_area_t * dest_area,lv_coord_t dest_stride,lv_color_t color,lv_opa_t opa)141 void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
142                          lv_color_t color, lv_opa_t opa)
143 {
144     lv_coord_t dest_w = lv_area_get_width(dest_area);
145     lv_coord_t dest_h = lv_area_get_height(dest_area);
146 
147     lv_gpu_nxp_pxp_reset();
148 
149     /*OUT buffer configure*/
150     pxp_output_buffer_config_t outputConfig = {
151         .pixelFormat = PXP_OUT_PIXEL_FORMAT,
152         .interlacedMode = kPXP_OutputProgressive,
153         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
154         .buffer1Addr = (uint32_t)NULL,
155         .pitchBytes = dest_stride * sizeof(lv_color_t),
156         .width = dest_w,
157         .height = dest_h
158     };
159 
160     PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputConfig);
161 
162     if(opa >= (lv_opa_t)LV_OPA_MAX) {
163         /*Simple color fill without opacity - AS disabled*/
164         PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
165 
166     }
167     else {
168         /*Fill with opacity - AS used as source (same as OUT)*/
169         pxp_as_buffer_config_t asBufferConfig = {
170             .pixelFormat = PXP_AS_PIXEL_FORMAT,
171             .bufferAddr = (uint32_t)outputConfig.buffer0Addr,
172             .pitchBytes = outputConfig.pitchBytes
173         };
174 
175         PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
176         PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
177     }
178 
179     /*Disable PS, use as color generator*/
180     PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
181     PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color));
182 
183     /**
184      * Configure Porter-Duff blending - src settings are unused for fill without opacity (opa = 0xff).
185      *
186      * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
187      * srcFactorMode is actually applied on PS alpha value
188      * dstFactorMode is actually applied on AS alpha value
189      */
190     pxp_porter_duff_config_t pdConfig = {
191         .enable = 1,
192         .dstColorMode = kPXP_PorterDuffColorNoAlpha,
193         .srcColorMode = kPXP_PorterDuffColorNoAlpha,
194         .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
195         .srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
196         .dstFactorMode = kPXP_PorterDuffFactorStraight,
197         .srcFactorMode = (opa >= (lv_opa_t)LV_OPA_MAX) ? kPXP_PorterDuffFactorStraight : kPXP_PorterDuffFactorInversed,
198         .dstGlobalAlpha = opa,
199         .srcGlobalAlpha = opa,
200         .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
201         .srcAlphaMode = kPXP_PorterDuffAlphaStraight  /*don't care*/
202     };
203 
204     PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
205 
206     lv_gpu_nxp_pxp_run();
207 }
208 
lv_gpu_nxp_pxp_blit(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,lv_opa_t opa,lv_disp_rot_t angle)209 void lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
210                          const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
211                          lv_opa_t opa, lv_disp_rot_t angle)
212 {
213     lv_coord_t dest_w = lv_area_get_width(dest_area);
214     lv_coord_t dest_h = lv_area_get_height(dest_area);
215     lv_coord_t src_w = lv_area_get_width(src_area);
216     lv_coord_t src_h = lv_area_get_height(src_area);
217 
218     lv_gpu_nxp_pxp_reset();
219 
220     /* convert rotation angle */
221     pxp_rotate_degree_t pxp_rot;
222     switch(angle) {
223         case LV_DISP_ROT_NONE:
224             pxp_rot = kPXP_Rotate0;
225             break;
226         case LV_DISP_ROT_90:
227             pxp_rot = kPXP_Rotate90;
228             break;
229         case LV_DISP_ROT_180:
230             pxp_rot = kPXP_Rotate180;
231             break;
232         case LV_DISP_ROT_270:
233             pxp_rot = kPXP_Rotate270;
234             break;
235         default:
236             pxp_rot = kPXP_Rotate0;
237             break;
238     }
239     PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_rot, kPXP_FlipDisable);
240 
241     pxp_as_blend_config_t asBlendConfig = {
242         .alpha = opa,
243         .invertAlpha = false,
244         .alphaMode = kPXP_AlphaRop,
245         .ropMode = kPXP_RopMergeAs
246     };
247 
248     if(opa >= (lv_opa_t)LV_OPA_MAX) {
249         /*Simple blit, no effect - Disable PS buffer*/
250         PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
251     }
252     else {
253         pxp_ps_buffer_config_t psBufferConfig = {
254             .pixelFormat = PXP_PS_PIXEL_FORMAT,
255             .swapByte = false,
256             .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
257             .bufferAddrU = 0U,
258             .bufferAddrV = 0U,
259             .pitchBytes = dest_stride * sizeof(lv_color_t)
260         };
261 
262         asBlendConfig.alphaMode = kPXP_AlphaOverride;
263 
264         PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
265         PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
266     }
267 
268     /*AS buffer - source image*/
269     pxp_as_buffer_config_t asBufferConfig = {
270         .pixelFormat = PXP_AS_PIXEL_FORMAT,
271         .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_area->x1),
272         .pitchBytes = src_stride * sizeof(lv_color_t)
273     };
274     PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
275     PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
276     PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
277     PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, false);
278 
279     /*Output buffer.*/
280     pxp_output_buffer_config_t outputBufferConfig = {
281         .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
282         .interlacedMode = kPXP_OutputProgressive,
283         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
284         .buffer1Addr = (uint32_t)0U,
285         .pitchBytes = dest_stride * sizeof(lv_color_t),
286         .width = dest_w,
287         .height = dest_h
288     };
289     PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
290 
291     lv_gpu_nxp_pxp_run();
292 }
293 
lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf,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,const lv_draw_img_dsc_t * dsc,lv_img_cf_t cf)294 void lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
295                                    const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
296                                    const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
297 {
298     bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
299     bool has_rotation = (dsc->angle != 0);
300 
301     if(has_recolor || has_rotation) {
302         if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_has_alpha(cf) && !lv_img_cf_is_chroma_keyed(cf)) {
303             lv_pxp_blit_cover(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, dsc, cf);
304             return;
305         }
306         else {
307             /*Recolor and/or rotation with alpha or opacity is done in two steps.*/
308             lv_pxp_blit_opa(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, dsc, cf);
309             return;
310         }
311     }
312 
313     lv_pxp_blit_cf(dest_buf, dest_area, dest_stride, src_buf, src_area, src_stride, dsc, cf);
314 }
315 
lv_gpu_nxp_pxp_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)316 void lv_gpu_nxp_pxp_buffer_copy(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
317                                 const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride)
318 {
319     lv_coord_t src_width = lv_area_get_width(src_area);
320     lv_coord_t src_height = lv_area_get_height(src_area);
321 
322     lv_gpu_nxp_pxp_reset();
323 
324     const pxp_pic_copy_config_t picCopyConfig = {
325         .srcPicBaseAddr = (uint32_t)src_buf,
326         .srcPitchBytes = src_stride * sizeof(lv_color_t),
327         .srcOffsetX = src_area->x1,
328         .srcOffsetY = src_area->y1,
329         .destPicBaseAddr = (uint32_t)dest_buf,
330         .destPitchBytes = dest_stride * sizeof(lv_color_t),
331         .destOffsetX = dest_area->x1,
332         .destOffsetY = dest_area->y1,
333         .width = src_width,
334         .height = src_height,
335         .pixelFormat = PXP_AS_PIXEL_FORMAT
336     };
337 
338     PXP_StartPictureCopy(LV_GPU_NXP_PXP_ID, &picCopyConfig);
339 
340     lv_gpu_nxp_pxp_wait();
341 }
342 
343 /**********************
344  *   STATIC FUNCTIONS
345  **********************/
346 
lv_pxp_blit_opa(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,const lv_draw_img_dsc_t * dsc,lv_img_cf_t cf)347 static void lv_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
348                             const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
349                             const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
350 {
351     lv_area_t temp_area;
352     lv_area_copy(&temp_area, dest_area);
353     lv_coord_t temp_stride = dest_stride;
354     lv_coord_t temp_w = lv_area_get_width(&temp_area);
355     lv_coord_t temp_h = lv_area_get_height(&temp_area);
356 
357     /*Step 1: Transform with full opacity to temporary buffer*/
358     lv_pxp_blit_cover((lv_color_t *)temp_buf, &temp_area, temp_stride, src_buf, src_area, src_stride, dsc, cf);
359 
360     /*Switch width and height if angle requires it*/
361     if(dsc->angle == 900 || dsc->angle == 2700) {
362         temp_area.x2 = temp_area.x1 + temp_h - 1;
363         temp_area.y2 = temp_area.y1 + temp_w - 1;
364     }
365 
366     /*Step 2: Blit temporary result with required opacity to output*/
367     lv_pxp_blit_cf(dest_buf, &temp_area, dest_stride, (lv_color_t *)temp_buf, &temp_area, temp_stride, dsc, cf);
368 }
lv_pxp_blit_cover(lv_color_t * dest_buf,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,const lv_draw_img_dsc_t * dsc,lv_img_cf_t cf)369 static void lv_pxp_blit_cover(lv_color_t * dest_buf, lv_area_t * dest_area, lv_coord_t dest_stride,
370                               const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
371                               const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
372 {
373     lv_coord_t dest_w = lv_area_get_width(dest_area);
374     lv_coord_t dest_h = lv_area_get_height(dest_area);
375     lv_coord_t src_w = lv_area_get_width(src_area);
376     lv_coord_t src_h = lv_area_get_height(src_area);
377 
378     bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
379     bool has_rotation = (dsc->angle != 0);
380 
381     lv_point_t pivot = dsc->pivot;
382     lv_coord_t piv_offset_x;
383     lv_coord_t piv_offset_y;
384 
385     lv_gpu_nxp_pxp_reset();
386 
387     if(has_rotation) {
388         /*Convert rotation angle and calculate offsets caused by pivot*/
389         pxp_rotate_degree_t pxp_angle;
390         switch(dsc->angle) {
391             case 0:
392                 pxp_angle = kPXP_Rotate0;
393                 piv_offset_x = 0;
394                 piv_offset_y = 0;
395                 break;
396             case 900:
397                 piv_offset_x = pivot.x + pivot.y - dest_h;
398                 piv_offset_y = pivot.y - pivot.x;
399                 pxp_angle = kPXP_Rotate90;
400                 break;
401             case 1800:
402                 piv_offset_x = 2 * pivot.x - dest_w;
403                 piv_offset_y = 2 * pivot.y - dest_h;
404                 pxp_angle = kPXP_Rotate180;
405                 break;
406             case 2700:
407                 piv_offset_x = pivot.x - pivot.y;
408                 piv_offset_y = pivot.x + pivot.y - dest_w;
409                 pxp_angle = kPXP_Rotate270;
410                 break;
411             default:
412                 piv_offset_x = 0;
413                 piv_offset_y = 0;
414                 pxp_angle = kPXP_Rotate0;
415         }
416         PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_angle, kPXP_FlipDisable);
417         lv_area_move(dest_area, piv_offset_x, piv_offset_y);
418     }
419 
420     /*AS buffer - source image*/
421     pxp_as_buffer_config_t asBufferConfig = {
422         .pixelFormat = PXP_AS_PIXEL_FORMAT,
423         .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_area->x1),
424         .pitchBytes = src_stride * sizeof(lv_color_t)
425     };
426     PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
427     PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
428 
429     /*Disable PS buffer*/
430     PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
431     if(has_recolor)
432         /*Use as color generator*/
433         PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(dsc->recolor));
434 
435     /*Output buffer*/
436     pxp_output_buffer_config_t outputBufferConfig = {
437         .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
438         .interlacedMode = kPXP_OutputProgressive,
439         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
440         .buffer1Addr = (uint32_t)0U,
441         .pitchBytes = dest_stride * sizeof(lv_color_t),
442         .width = dest_w,
443         .height = dest_h
444     };
445     PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
446 
447     if(has_recolor || lv_img_cf_has_alpha(cf)) {
448         /**
449          * Configure Porter-Duff blending.
450          *
451          * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
452          * srcFactorMode is actually applied on PS alpha value
453          * dstFactorMode is actually applied on AS alpha value
454          */
455         pxp_porter_duff_config_t pdConfig = {
456             .enable = 1,
457             .dstColorMode = kPXP_PorterDuffColorWithAlpha,
458             .srcColorMode = kPXP_PorterDuffColorNoAlpha,
459             .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
460             .srcGlobalAlphaMode = lv_img_cf_has_alpha(cf) ? kPXP_PorterDuffLocalAlpha : kPXP_PorterDuffGlobalAlpha,
461             .dstFactorMode = kPXP_PorterDuffFactorStraight,
462             .srcFactorMode = kPXP_PorterDuffFactorInversed,
463             .dstGlobalAlpha = has_recolor ? dsc->recolor_opa : 0x00,
464             .srcGlobalAlpha = 0xff,
465             .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
466             .srcAlphaMode = kPXP_PorterDuffAlphaStraight
467         };
468         PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig);
469     }
470 
471     lv_gpu_nxp_pxp_run();
472 }
473 
lv_pxp_blit_cf(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,const lv_draw_img_dsc_t * dsc,lv_img_cf_t cf)474 static void lv_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
475                            const lv_color_t * src_buf, const lv_area_t * src_area, lv_coord_t src_stride,
476                            const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf)
477 {
478     lv_coord_t dest_w = lv_area_get_width(dest_area);
479     lv_coord_t dest_h = lv_area_get_height(dest_area);
480     lv_coord_t src_w = lv_area_get_width(src_area);
481     lv_coord_t src_h = lv_area_get_height(src_area);
482 
483     lv_gpu_nxp_pxp_reset();
484 
485     pxp_as_blend_config_t asBlendConfig = {
486         .alpha = dsc->opa,
487         .invertAlpha = false,
488         .alphaMode = kPXP_AlphaRop,
489         .ropMode = kPXP_RopMergeAs
490     };
491 
492     if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_is_chroma_keyed(cf) && !lv_img_cf_has_alpha(cf)) {
493         /*Simple blit, no effect - Disable PS buffer*/
494         PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
495     }
496     else {
497         /*PS must be enabled to fetch background pixels.
498           PS and OUT buffers are the same, blend will be done in-place*/
499         pxp_ps_buffer_config_t psBufferConfig = {
500             .pixelFormat = PXP_PS_PIXEL_FORMAT,
501             .swapByte = false,
502             .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
503             .bufferAddrU = 0U,
504             .bufferAddrV = 0U,
505             .pitchBytes = dest_stride * sizeof(lv_color_t)
506         };
507         if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) {
508             asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaEmbedded : kPXP_AlphaOverride;
509         }
510         else {
511             asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaMultiply : kPXP_AlphaOverride;
512         }
513         PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig);
514         PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
515     }
516 
517     /*AS buffer - source image*/
518     pxp_as_buffer_config_t asBufferConfig = {
519         .pixelFormat = PXP_AS_PIXEL_FORMAT,
520         .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_area->x1),
521         .pitchBytes = src_stride * sizeof(lv_color_t)
522     };
523     PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig);
524     PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
525     PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig);
526 
527     if(lv_img_cf_is_chroma_keyed(cf)) {
528         lv_color_t colorKeyLow = LV_COLOR_CHROMA_KEY;
529         lv_color_t colorKeyHigh = LV_COLOR_CHROMA_KEY;
530 
531         bool has_recolor = (dsc->recolor_opa != LV_OPA_TRANSP);
532 
533         if(has_recolor) {
534             /* New color key after recoloring */
535             lv_color_t colorKey =  lv_color_mix(dsc->recolor, LV_COLOR_CHROMA_KEY, dsc->recolor_opa);
536 
537             LV_COLOR_SET_R(colorKeyLow, colorKey.ch.red != 0 ? colorKey.ch.red - 1 : 0);
538             LV_COLOR_SET_G(colorKeyLow, colorKey.ch.green != 0 ? colorKey.ch.green - 1 : 0);
539             LV_COLOR_SET_B(colorKeyLow, colorKey.ch.blue != 0 ? colorKey.ch.blue - 1 : 0);
540 
541 #if LV_COLOR_DEPTH == 16
542             LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0x1f ? colorKey.ch.red + 1 : 0x1f);
543             LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0x3f ? colorKey.ch.green + 1 : 0x3f);
544             LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0x1f ? colorKey.ch.blue + 1 : 0x1f);
545 #else /*LV_COLOR_DEPTH == 32*/
546             LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0xff ? colorKey.ch.red + 1 : 0xff);
547             LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0xff ? colorKey.ch.green + 1 : 0xff);
548             LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0xff ? colorKey.ch.blue + 1 : 0xff);
549 #endif
550         }
551 
552         PXP_SetAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_color_to32(colorKeyLow),
553                                            lv_color_to32(colorKeyHigh));
554     }
555 
556     PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_img_cf_is_chroma_keyed(cf));
557 
558     /*Output buffer.*/
559     pxp_output_buffer_config_t outputBufferConfig = {
560         .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT,
561         .interlacedMode = kPXP_OutputProgressive,
562         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1),
563         .buffer1Addr = (uint32_t)0U,
564         .pitchBytes = dest_stride * sizeof(lv_color_t),
565         .width = dest_w,
566         .height = dest_h
567     };
568     PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig);
569 
570     lv_gpu_nxp_pxp_run();
571 }
572 
573 #endif /*LV_USE_GPU_NXP_PXP*/
574