1 /**
2  * @file lv_draw_pxp_img.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_pxp.h"
17 
18 #if LV_USE_PXP
19 #if LV_USE_DRAW_PXP
20 #include "lv_pxp_cfg.h"
21 #include "lv_pxp_utils.h"
22 
23 #include <math.h>
24 
25 /*********************
26  *      DEFINES
27  *********************/
28 
29 /**********************
30  *      TYPEDEFS
31  **********************/
32 
33 /**********************
34  *  STATIC PROTOTYPES
35  **********************/
36 
37 /* Blit w/ recolor for images w/o opa and alpha channel */
38 static void _pxp_blit_recolor(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
39                               lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
40                               int32_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc);
41 
42 /* Blit w/ transformation for images w/o opa and alpha channel */
43 static void _pxp_blit_transform(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
44                                 lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
45                                 int32_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc);
46 
47 /* Blit simple w/ opa and alpha channel */
48 static void _pxp_blit(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
49                       lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
50                       int32_t src_stride, lv_color_format_t src_cf, lv_opa_t opa);
51 
52 /**********************
53  *  STATIC VARIABLES
54  **********************/
55 
56 /**********************
57  *      MACROS
58  **********************/
59 
60 /**********************
61  *   GLOBAL FUNCTIONS
62  **********************/
63 
lv_draw_pxp_img(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * dsc,const lv_area_t * coords)64 void lv_draw_pxp_img(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dsc,
65                      const lv_area_t * coords)
66 {
67     if(dsc->opa <= (lv_opa_t)LV_OPA_MIN)
68         return;
69 
70     lv_layer_t * layer = draw_unit->target_layer;
71     lv_draw_buf_t * draw_buf = layer->draw_buf;
72     const lv_image_dsc_t * img_dsc = dsc->src;
73 
74     lv_area_t rel_coords;
75     lv_area_copy(&rel_coords, coords);
76     lv_area_move(&rel_coords, -layer->buf_area.x1, -layer->buf_area.y1);
77 
78     lv_area_t rel_clip_area;
79     lv_area_copy(&rel_clip_area, draw_unit->clip_area);
80     lv_area_move(&rel_clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
81 
82     lv_area_t blend_area;
83     bool has_transform = (dsc->rotation != 0 || dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
84     if(has_transform)
85         lv_area_copy(&blend_area, &rel_coords);
86     else if(!lv_area_intersect(&blend_area, &rel_coords, &rel_clip_area))
87         return; /*Fully clipped, nothing to do*/
88 
89     const uint8_t * src_buf = img_dsc->data;
90 
91     lv_area_t src_area;
92     src_area.x1 = blend_area.x1 - (coords->x1 - layer->buf_area.x1);
93     src_area.y1 = blend_area.y1 - (coords->y1 - layer->buf_area.y1);
94     src_area.x2 = src_area.x1 + lv_area_get_width(coords) - 1;
95     src_area.y2 = src_area.y1 + lv_area_get_height(coords) - 1;
96     int32_t src_stride = img_dsc->header.stride;
97     lv_color_format_t src_cf = img_dsc->header.cf;
98 
99     uint8_t * dest_buf = draw_buf->data;
100     int32_t dest_stride = draw_buf->header.stride;
101     lv_color_format_t dest_cf = draw_buf->header.cf;
102     bool has_recolor = (dsc->recolor_opa > LV_OPA_MIN);
103 
104     if(has_recolor && !has_transform)
105         _pxp_blit_recolor(dest_buf, &blend_area, dest_stride, dest_cf,
106                           src_buf, &src_area, src_stride, src_cf, dsc);
107     else if(has_transform)
108         _pxp_blit_transform(dest_buf, &blend_area, dest_stride, dest_cf,
109                             src_buf, &src_area, src_stride, src_cf, dsc);
110     else
111         _pxp_blit(dest_buf, &blend_area, dest_stride, dest_cf,
112                   src_buf, &src_area, src_stride, src_cf, dsc->opa);
113 }
114 
115 /**********************
116  *   STATIC FUNCTIONS
117  **********************/
118 
_pxp_blit_recolor(uint8_t * dest_buf,const lv_area_t * dest_area,int32_t dest_stride,lv_color_format_t dest_cf,const uint8_t * src_buf,const lv_area_t * src_area,int32_t src_stride,lv_color_format_t src_cf,const lv_draw_image_dsc_t * dsc)119 static void _pxp_blit_recolor(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
120                               lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
121                               int32_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc)
122 {
123 
124     int32_t dest_w = lv_area_get_width(dest_area);
125     int32_t dest_h = lv_area_get_height(dest_area);
126     int32_t src_w = lv_area_get_width(src_area);
127     int32_t src_h = lv_area_get_height(src_area);
128 
129     bool src_has_alpha = (src_cf == LV_COLOR_FORMAT_ARGB8888);
130     uint8_t src_px_size = lv_color_format_get_size(src_cf);
131     uint8_t dest_px_size = lv_color_format_get_size(dest_cf);
132 
133     lv_pxp_reset();
134 
135     /*AS buffer - source image*/
136     pxp_as_buffer_config_t asBufferConfig = {
137         .pixelFormat = pxp_get_as_px_format(src_cf),
138         .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_px_size * src_area->x1),
139         .pitchBytes = src_stride
140     };
141     PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig);
142     PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
143 
144     /*Disable PS, use as color generator*/
145     PXP_SetProcessSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
146     PXP_SetProcessSurfaceBackGroundColor(PXP_ID, lv_color_to_u32(dsc->recolor));
147 
148     /*Output buffer*/
149     pxp_output_buffer_config_t outputBufferConfig = {
150         .pixelFormat = pxp_get_out_px_format(dest_cf),
151         .interlacedMode = kPXP_OutputProgressive,
152         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_px_size * dest_area->x1),
153         .buffer1Addr = (uint32_t)0U,
154         .pitchBytes = dest_stride,
155         .width = dest_w,
156         .height = dest_h
157     };
158     PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig);
159 
160     /**
161      * Configure Porter-Duff blending.
162      *
163      * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h:
164      * srcFactorMode is actually applied on PS alpha value
165      * dstFactorMode is actually applied on AS alpha value
166      */
167     pxp_porter_duff_config_t pdConfig = {
168         .enable = 1,
169         .dstColorMode = kPXP_PorterDuffColorWithAlpha,
170         .srcColorMode = kPXP_PorterDuffColorWithAlpha,
171         .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha,
172         .srcGlobalAlphaMode = src_has_alpha ? kPXP_PorterDuffLocalAlpha : kPXP_PorterDuffGlobalAlpha,
173         .dstFactorMode = kPXP_PorterDuffFactorStraight,
174         .srcFactorMode = kPXP_PorterDuffFactorInversed,
175         .dstGlobalAlpha = dsc->recolor_opa,
176         .srcGlobalAlpha = 0xff,
177         .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/
178         .srcAlphaMode = kPXP_PorterDuffAlphaStraight
179     };
180     PXP_SetPorterDuffConfig(PXP_ID, &pdConfig);
181 
182     lv_pxp_run();
183 }
184 
_pxp_blit_transform(uint8_t * dest_buf,const lv_area_t * dest_area,int32_t dest_stride,lv_color_format_t dest_cf,const uint8_t * src_buf,const lv_area_t * src_area,int32_t src_stride,lv_color_format_t src_cf,const lv_draw_image_dsc_t * dsc)185 static void _pxp_blit_transform(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
186                                 lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
187                                 int32_t src_stride, lv_color_format_t src_cf, const lv_draw_image_dsc_t * dsc)
188 {
189     int32_t src_w = lv_area_get_width(src_area);
190     int32_t src_h = lv_area_get_height(src_area);
191     int32_t dest_w = lv_area_get_width(dest_area);
192     int32_t dest_h = lv_area_get_height(dest_area);
193 
194     lv_point_t pivot = dsc->pivot;
195     /*The offsets are now relative to the transformation result with pivot ULC*/
196     int32_t piv_offset_x = 0;
197     int32_t piv_offset_y = 0;
198 
199     int32_t trim_x = 0;
200     int32_t trim_y = 0;
201 
202     bool has_rotation = (dsc->rotation != 0);
203     bool has_scale = (dsc->scale_x != LV_SCALE_NONE || dsc->scale_y != LV_SCALE_NONE);
204     uint8_t src_px_size = lv_color_format_get_size(src_cf);
205     uint8_t dest_px_size = lv_color_format_get_size(dest_cf);
206 
207     lv_pxp_reset();
208 
209     if(has_rotation) {
210         /*Convert rotation angle and calculate offsets caused by pivot*/
211         pxp_rotate_degree_t pxp_angle;
212         switch(dsc->rotation) {
213             case 0:
214                 pxp_angle = kPXP_Rotate0;
215                 piv_offset_x = 0;
216                 piv_offset_y = 0;
217                 break;
218             case 900:
219                 pxp_angle = kPXP_Rotate90;
220                 piv_offset_x = pivot.x + pivot.y - src_h;
221                 piv_offset_y = pivot.y - pivot.x;
222                 break;
223             case 1800:
224                 pxp_angle = kPXP_Rotate180;
225                 piv_offset_x = 2 * pivot.x - src_w;
226                 piv_offset_y = 2 * pivot.y - src_h;
227                 break;
228             case 2700:
229                 pxp_angle = kPXP_Rotate270;
230                 piv_offset_x = pivot.x - pivot.y;
231                 piv_offset_y = pivot.x + pivot.y - src_w;
232                 break;
233             default:
234                 pxp_angle = kPXP_Rotate0;
235                 piv_offset_x = 0;
236                 piv_offset_y = 0;
237         }
238         /*PS buffer rotation and decimation does not function at the same time*/
239         PXP_SetRotateConfig(PXP_ID, kPXP_RotateOutputBuffer, pxp_angle, kPXP_FlipDisable);
240     }
241 
242     if(has_scale) {
243         float fp_scale_x = (float)dsc->scale_x / LV_SCALE_NONE;
244         float fp_scale_y = (float)dsc->scale_y / LV_SCALE_NONE;
245         int32_t int_scale_x = (int32_t)fp_scale_x;
246         int32_t int_scale_y = (int32_t)fp_scale_y;
247 
248         /*Any scale_factor in (k, k + 1] will result in a trim equal to k*/
249         trim_x = (fp_scale_x == int_scale_x) ? int_scale_x - 1 : int_scale_x;
250         trim_y = (fp_scale_y == int_scale_y) ? int_scale_y - 1 : int_scale_y;
251 
252         dest_w = src_w * fp_scale_x + trim_x;
253         dest_h = src_h * fp_scale_y + trim_y;
254 
255         /*Final pivot offset = scale_factor * rotation_pivot_offset + scaling_pivot_offset*/
256         piv_offset_x = floorf(fp_scale_x * piv_offset_x) - floorf((fp_scale_x - 1) * pivot.x);
257         piv_offset_y = floorf(fp_scale_y * piv_offset_y) - floorf((fp_scale_y - 1) * pivot.y);
258     }
259 
260     /*PS buffer - source image*/
261     pxp_ps_buffer_config_t psBufferConfig = {
262         .pixelFormat = pxp_get_ps_px_format(src_cf),
263         .swapByte = false,
264         .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_px_size * src_area->x1),
265         .bufferAddrU = 0,
266         .bufferAddrV = 0,
267         .pitchBytes = src_stride
268     };
269     PXP_SetProcessSurfaceBufferConfig(PXP_ID, &psBufferConfig);
270     PXP_SetProcessSurfacePosition(PXP_ID, 0U, 0U, dest_w - trim_x - 1U, dest_h - trim_y - 1U);
271 
272     if(has_scale)
273         PXP_SetProcessSurfaceScaler(PXP_ID, src_w, src_h, dest_w, dest_h);
274 
275     /*AS disabled */
276     PXP_SetAlphaSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
277 
278     /*Output buffer*/
279     pxp_output_buffer_config_t outputBufferConfig = {
280         .pixelFormat = pxp_get_out_px_format(dest_cf),
281         .interlacedMode = kPXP_OutputProgressive,
282         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * (dest_area->y1 + piv_offset_y) + dest_px_size * (dest_area->x1 + piv_offset_x)),
283         .buffer1Addr = (uint32_t)0U,
284         .pitchBytes = dest_stride,
285         .width = dest_w - trim_x,
286         .height = dest_h - trim_y
287     };
288     PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig);
289 
290     lv_pxp_run();
291 }
292 
_pxp_blit(uint8_t * dest_buf,const lv_area_t * dest_area,int32_t dest_stride,lv_color_format_t dest_cf,const uint8_t * src_buf,const lv_area_t * src_area,int32_t src_stride,lv_color_format_t src_cf,lv_opa_t opa)293 static void _pxp_blit(uint8_t * dest_buf, const lv_area_t * dest_area, int32_t dest_stride,
294                       lv_color_format_t dest_cf, const uint8_t * src_buf, const lv_area_t * src_area,
295                       int32_t src_stride, lv_color_format_t src_cf, lv_opa_t opa)
296 {
297     int32_t dest_w = lv_area_get_width(dest_area);
298     int32_t dest_h = lv_area_get_height(dest_area);
299     int32_t src_w = lv_area_get_width(src_area);
300     int32_t src_h = lv_area_get_height(src_area);
301 
302     bool src_has_alpha = (src_cf == LV_COLOR_FORMAT_ARGB8888);
303     uint8_t src_px_size = lv_color_format_get_size(src_cf);
304     uint8_t dest_px_size = lv_color_format_get_size(dest_cf);
305 
306     lv_pxp_reset();
307 
308     pxp_as_blend_config_t asBlendConfig = {
309         .alpha = opa,
310         .invertAlpha = false,
311         .alphaMode = kPXP_AlphaRop,
312         .ropMode = kPXP_RopMergeAs
313     };
314 
315     if(opa >= (lv_opa_t)LV_OPA_MAX && !src_has_alpha) {
316         /*Simple blit, no effect - Disable PS buffer*/
317         PXP_SetProcessSurfacePosition(PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U);
318     }
319     else {
320         /*PS must be enabled to fetch background pixels.
321           PS and OUT buffers are the same, blend will be done in-place*/
322         pxp_ps_buffer_config_t psBufferConfig = {
323             .pixelFormat = pxp_get_ps_px_format(dest_cf),
324             .swapByte = false,
325             .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_px_size * dest_area->x1),
326             .bufferAddrU = 0U,
327             .bufferAddrV = 0U,
328             .pitchBytes = dest_stride
329         };
330 
331         if(opa >= (lv_opa_t)LV_OPA_MAX)
332             asBlendConfig.alphaMode = src_has_alpha ? kPXP_AlphaEmbedded : kPXP_AlphaOverride;
333         else
334             asBlendConfig.alphaMode = src_has_alpha ? kPXP_AlphaMultiply : kPXP_AlphaOverride;
335 
336         PXP_SetProcessSurfaceBufferConfig(PXP_ID, &psBufferConfig);
337         PXP_SetProcessSurfacePosition(PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U);
338     }
339 
340     /*AS buffer - source image*/
341     pxp_as_buffer_config_t asBufferConfig = {
342         .pixelFormat = pxp_get_as_px_format(src_cf),
343         .bufferAddr = (uint32_t)(src_buf + src_stride * src_area->y1 + src_px_size * src_area->x1),
344         .pitchBytes = src_stride
345     };
346     PXP_SetAlphaSurfaceBufferConfig(PXP_ID, &asBufferConfig);
347     PXP_SetAlphaSurfacePosition(PXP_ID, 0U, 0U, src_w - 1U, src_h - 1U);
348     PXP_SetAlphaSurfaceBlendConfig(PXP_ID, &asBlendConfig);
349     PXP_EnableAlphaSurfaceOverlayColorKey(PXP_ID, false);
350 
351     /*Output buffer.*/
352     pxp_output_buffer_config_t outputBufferConfig = {
353         .pixelFormat = pxp_get_out_px_format(dest_cf),
354         .interlacedMode = kPXP_OutputProgressive,
355         .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_px_size * dest_area->x1),
356         .buffer1Addr = (uint32_t)0U,
357         .pitchBytes = dest_stride,
358         .width = dest_w,
359         .height = dest_h
360     };
361     PXP_SetOutputBufferConfig(PXP_ID, &outputBufferConfig);
362 
363     lv_pxp_run();
364 }
365 
366 #endif /*LV_USE_DRAW_PXP*/
367 #endif /*LV_USE_PXP*/
368