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