display-dma2d  5.0.0
display-dma2d
ui_drawing_dma2d.c
1 /*
2  * C
3  *
4  * Copyright 2019-2024 MicroEJ Corp. All rights reserved.
5  * Use of this source code is governed by a BSD-style license that can be found with this software.
6  */
7 
8 /*
9  * @file
10  * @brief Implementation of a set of ui_drawing.h drawing functions (MicroUI library). These are
11  * implementations over the STM32 DMA2D (ChromART) and the destination buffer format is the format
12  * specified in the VEE port. When the drawing cannot be performed by the GPU, the software
13  * implementation is used insted.
14  *
15  * @author MicroEJ Developer Team
16  * @version 5.0.0
17  */
18 
19 // --------------------------------------------------------------------------------
20 // Includes
21 // --------------------------------------------------------------------------------
22 
23 #include "ui_drawing_dma2d.h"
24 #include "ui_drawing_soft.h"
25 #include "ui_image_drawing.h"
26 #include "ui_display_brs.h"
27 #include "bsp_util.h"
28 
29 // -----------------------------------------------------------------------------
30 // Configuration Sanity Check
31 // -----------------------------------------------------------------------------
32 
41 #if !defined DMA2D_CONFIGURATION_VERSION
42 #error "Undefined DMA2D_CONFIGURATION_VERSION, it must be defined in ui_drawing_nema_configuration.h"
43 #endif
44 
45 #if defined DMA2D_CONFIGURATION_VERSION && DMA2D_CONFIGURATION_VERSION != 1
46 #error "Version of the configuration file ui_drawing_dma2d_configuration.h is not compatible with this implementation."
47 #endif
48 
49 // --------------------------------------------------------------------------------
50 // Defines
51 // --------------------------------------------------------------------------------
52 
53 /*
54  * @brief Defines the DMA2D format according DRAWING_DMA2D_BPP.
55  */
56 #if DRAWING_DMA2D_BPP == 16
57 #define DRAWING_DMA2D_FORMAT DMA2D_RGB565
58 #elif DRAWING_DMA2D_BPP == 24
59 #define DRAWING_DMA2D_FORMAT DMA2D_RGB888
60 #elif DRAWING_DMA2D_BPP == 32
61 #define DRAWING_DMA2D_FORMAT DMA2D_ARGB8888
62 #else
63 #error "Define 'DRAWING_DMA2D_BPP' is required (16, 24 or 32)"
64 #endif
65 
66 /*
67  * @brief Ensures the configuration of the cache management.
68  */
69 #ifndef DRAWING_DMA2D_CACHE_MANAGEMENT
70 #error "Please define the DRAWING_DMA2D_CACHE_MANAGEMENT in ui_drawing_dma2d_configuration.h"
71 #endif
72 
73 // --------------------------------------------------------------------------------
74 // Types
75 // --------------------------------------------------------------------------------
76 /*
77  * @brief Function used to notify the graphical engine about the end of DMA2D work.
78  * Available values are LLUI_DISPLAY_notifyAsynchronousDrawingEnd() and LLUI_DISPLAY_flushDone().
79  */
80 typedef void (*t_drawing_notification)(bool under_isr);
81 
82 /*
83  * @brief Working data when blending an image with the DMA2D
84  */
85 typedef struct {
86  uint8_t* src_address; // address of the image to draw
87  uint8_t* dest_address; // address of the destination
88  uint32_t src_stride; // source image' stride
89  uint32_t dest_stride; // destination' stride
90  jchar dest_width; // destination's width
91  jchar dest_height; // destination's height
92  jint x_src; // source image's region X coordinate
93  jint y_src; // source image's region Y coordinate
94  jint width; // region's width
95  jint height; // region's height
96  jint x_dest; // destination X coordinate
97  jint y_dest; // destination X coordinate
98  jint alpha; // opacity to apply
99  uint32_t src_dma2d_format; // source image's format in DMA2D format
100  uint32_t src_bpp; // source image's bpp
102 
103 // --------------------------------------------------------------------------------
104 // Private fields
105 // --------------------------------------------------------------------------------
106 
107 /*
108  * @brief STM32 HAL DMA2D declaration.
109  */
110 static DMA2D_HandleTypeDef g_hdma2d;
111 
112 /*
113  * @brief Next notification to call after DMA2D work.
114  */
115 static t_drawing_notification g_callback_notification;
116 
117 /*
118  * @brief This boolean is set to true before launching a DMA2D action and back to
119  * false during DMA2D interrupt. It allows to know if the DMA2D is running or not.
120  */
121 static bool g_dma2d_running;
122 
123 /*
124  * @brief Binary semaphore used to lock a DMA2D user when DMA2D is already running.
125  */
126 static void* g_dma2d_semaphore;
127 
128 // --------------------------------------------------------------------------------
129 // Private functions
130 // --------------------------------------------------------------------------------
131 
132 /*
133  * @brief Ensures DMA2D previous work is done before returning.
134  */
135 static inline void _drawing_dma2d_wait(void) {
136  while(g_dma2d_running) {
137  LLUI_DISPLAY_IMPL_binarySemaphoreTake(g_dma2d_semaphore);
138  }
139 }
140 
141 /*
142  * @brief Invalidates the cache.
143  *
144  * After each DMA_2D transfer, the data cache must be invalidated because the
145  * graphics memory is in the memory which is defined "cache enabled" in the MPU
146  * configuration and reading back data will be done from the cache if the cache
147  * is not invalidated
148  *
149  * This feature is only required on STM32 CPUs that hold a cache.
150  */
151 static inline void _invalidateDCache(void) {
152 #if DRAWING_DMA2D_CACHE_MANAGEMENT == DRAWING_DMA2D_CACHE_MANAGEMENT_ENABLED
153  SCB_InvalidateDCache();
154 #endif
155 }
156 
157 /*
158  * @brief Cleans the cache.
159  *
160  * Before each DMA_2D transfer, the data cache must be cleaned because the
161  * graphics memory is in the memory which is defined "cache enabled" in the MPU
162  * configuration.
163  *
164  * This feature is only required on STM32 CPUs that hold a cache.
165  */
166 static inline void _cleanDCache(void) {
167 #if DRAWING_DMA2D_CACHE_MANAGEMENT == DRAWING_DMA2D_CACHE_MANAGEMENT_ENABLED
168  SCB_CleanDCache();
169 #endif
170 }
171 
172 /*
173  * @brief Notify the DMA2D has finished previous job.
174  */
175 static inline void _drawing_dma2d_done(void) {
176  g_dma2d_running = false;
177  _invalidateDCache();
178  LLUI_DISPLAY_IMPL_binarySemaphoreGive(g_dma2d_semaphore, true);
179 }
180 
181 /*
182  * @brief Adjusts the given address to target the given point.
183  */
184 static inline uint8_t* _drawing_dma2d_adjust_address(uint8_t* address, uint32_t x, uint32_t y, uint32_t stride, uint32_t bpp) {
185  // cppcheck-suppress [misra-c2012-18.4] address += offset
186  return address + ((((y * stride) + x) * bpp) / (uint32_t)8);
187 }
188 
189 /*
190  * @brief For A4 and A8 formats, alpha contains both the global alpha + wanted color.
191  */
192 static inline void _drawing_dma2d_configure_alpha_image_data(MICROUI_GraphicsContext* gc, jint* alphaAndColor) {
193  // for A4 and A8 formats, alphaAndColor is both the global alpha + wanted color
194  *(alphaAndColor) <<= 24;
195  *(alphaAndColor) |= (gc->foreground_color & 0xffffff);
196 }
197 
198 /*
199  * @brief Tells is the image to draw in the graphics context is compatible with the DMA2D.
200  *
201  * The image is compatible with the DMA2D when its format is supported by the DMA2D.
202  * Note for the A4 format: the odd left and right bands are drawn in sofware.
203  *
204  * @param[in] gc: the destination
205  * @param[in] image: the source
206  * @param[in] x_src: the source's region X coordinate
207  * @param[in] y_src: the source's region Y coordinate
208  * @param[in] width: the width of the region to draw
209  * @param[in] height: the width of the region to draw
210  * @param[in] x_dest: the destination X coordinate
211  * @param[in] y_dest: the destination Y coordinate
212  * @param[in] alpha: a point on the opacity to apply.
213  * @param[out] dma2d_blending_data: the data to configure the DMA2D registers
214  *
215  * @return true if the source is compatible with the DMA2D
216  */
217 static bool _drawing_dma2d_is_image_compatible_with_dma2d(MICROUI_GraphicsContext* gc, MICROUI_Image* image, jint x_src, jint y_src, jint width, jint height, jint x_dest, jint y_dest, jint alpha, DRAWING_DMA2D_blending_t* dma2d_blending_data){
218 
219  bool is_dma2d_compatible = true;
220  jint data_width = width;
221  jint data_x_src = x_src;
222  jint data_x_dest = x_dest;
223 
224  switch(image->format) {
225  case MICROUI_IMAGE_FORMAT_RGB565:
226  dma2d_blending_data->src_dma2d_format = CM_RGB565;
227  dma2d_blending_data->src_bpp = 16;
228  break;
229  case MICROUI_IMAGE_FORMAT_ARGB8888:
230  dma2d_blending_data->src_dma2d_format = CM_ARGB8888;
231  dma2d_blending_data->src_bpp = 32;
232  break;
233  case MICROUI_IMAGE_FORMAT_RGB888:
234  dma2d_blending_data->src_dma2d_format = CM_RGB888;
235  dma2d_blending_data->src_bpp = 24;
236  break;
237  case MICROUI_IMAGE_FORMAT_ARGB1555:
238  dma2d_blending_data->src_dma2d_format = CM_ARGB1555;
239  dma2d_blending_data->src_bpp = 16;
240  break;
241  case MICROUI_IMAGE_FORMAT_ARGB4444:
242  dma2d_blending_data->src_dma2d_format = CM_ARGB4444;
243  dma2d_blending_data->src_bpp = 16;
244  break;
245  case MICROUI_IMAGE_FORMAT_A4: {
246  // check DMA2D limitations: first and last vertical lines addresses must be aligned on 8 bits
247 
248  jint xAlign = data_x_src & 1;
249  jint wAlign = data_width & 1;
250 
251  if (xAlign == 0) {
252  if (wAlign != 0) {
253  // hard cannot draw last vertical line
254  --data_width;
255  UI_DRAWING_SOFT_drawImage(gc, image, data_x_src + data_width, y_src, 1, height, data_x_dest + data_width, y_dest, alpha);
256  }
257  // else: easy case: first and last vertical lines are aligned on 8 bits
258  }
259  else {
260  if (wAlign == 0) {
261  // worst case: hard cannot draw first and last vertical lines
262 
263  // first line
264  UI_DRAWING_SOFT_drawImage(gc, image, data_x_src, y_src, 1, height, data_x_dest, y_dest, alpha);
265 
266  // last line
267  --data_width;
268  UI_DRAWING_SOFT_drawImage(gc, image, data_x_src + data_width, y_src, 1, height, data_x_dest + data_width, y_dest, alpha);
269 
270  ++data_x_src;
271  ++data_x_dest;
272  --data_width;
273  }
274  else {
275  // cannot draw first vertical line
276  UI_DRAWING_SOFT_drawImage(gc, image, data_x_src, y_src, 1, height, data_x_dest, y_dest, alpha);
277  ++data_x_src;
278  ++data_x_dest;
279  --data_width;
280  }
281  }
282 
283  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
284  dma2d_blending_data->src_dma2d_format = CM_A4;
285  dma2d_blending_data->src_bpp = 4;
286  break;
287  }
288  case MICROUI_IMAGE_FORMAT_A8:
289  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
290  dma2d_blending_data->src_dma2d_format = CM_A8;
291  dma2d_blending_data->src_bpp = 8;
292  break;
293  default:
294  // unsupported image format
295  is_dma2d_compatible = false;
296  break;
297  }
298 
299  if (is_dma2d_compatible){
300  MICROUI_Image* dest = &gc->image;
301  dma2d_blending_data->src_address = LLUI_DISPLAY_getBufferAddress(image);
302  dma2d_blending_data->dest_address = LLUI_DISPLAY_getBufferAddress(dest);
303  dma2d_blending_data->src_stride = LLUI_DISPLAY_getStrideInPixels(image);
304  dma2d_blending_data->dest_stride = LLUI_DISPLAY_getStrideInPixels(dest);
305  dma2d_blending_data->dest_width = dest->width;
306  dma2d_blending_data->dest_height = dest->height;
307  dma2d_blending_data->x_src = data_x_src;
308  dma2d_blending_data->y_src = y_src;
309  dma2d_blending_data->width = data_width;
310  dma2d_blending_data->height = height;
311  dma2d_blending_data->x_dest = data_x_dest;
312  dma2d_blending_data->y_dest = y_dest;
313  dma2d_blending_data->alpha = alpha;
314 
315  _cleanDCache();
316  }
317 
318  return is_dma2d_compatible;
319 }
320 
321 /*
322  * @brief Configures the DMA2D to draw an image.
323  *
324  * This function ensures that the DMA2D is free before configuring the DMA2D.
325  *
326  * @param[in] dma2d_blending_data the blending configuration
327  */
328 static void _drawing_dma2d_blending_configure(DRAWING_DMA2D_blending_t* dma2d_blending_data) {
329 
330  _drawing_dma2d_wait();
331 
332  // de-init DMA2D
333  HAL_DMA2D_DeInit(&g_hdma2d);
334 
335  // configure background
336  g_hdma2d.LayerCfg[0].InputOffset = dma2d_blending_data->dest_stride - dma2d_blending_data->width;
337  g_hdma2d.LayerCfg[0].InputColorMode = DRAWING_DMA2D_FORMAT;
338  g_hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
339  g_hdma2d.LayerCfg[0].InputAlpha = 255;
340  HAL_DMA2D_ConfigLayer(&g_hdma2d, 0);
341 
342  // configure foreground
343  HAL_DMA2D_DisableCLUT(&g_hdma2d, 1);
344  g_hdma2d.LayerCfg[1].InputOffset = dma2d_blending_data->src_stride - dma2d_blending_data->width;
345  g_hdma2d.LayerCfg[1].InputColorMode = dma2d_blending_data->src_dma2d_format;
346  g_hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA;
347  g_hdma2d.LayerCfg[1].InputAlpha = dma2d_blending_data->alpha;
348  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
349 
350  // configure DMA2D
351  g_hdma2d.Init.Mode = DMA2D_M2M_BLEND;
352  g_hdma2d.Init.OutputOffset = dma2d_blending_data->dest_stride - dma2d_blending_data->width;
353  HAL_DMA2D_Init(&g_hdma2d) ;
354 
355  // configure environment (useless if false is returned)
356  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
357 }
358 
359 /*
360  * @brief Starts the DMA2D drawing previously configured thanks _drawing_dma2d_blending_configure().
361  *
362  * @param[in] dma2d_blending_data the blending configuration
363  */
364 static inline void _drawing_dma2d_blending_start(DRAWING_DMA2D_blending_t* dma2d_blending_data){
365  uint8_t* srcAddr = _drawing_dma2d_adjust_address(dma2d_blending_data->src_address, dma2d_blending_data->x_src, dma2d_blending_data->y_src, dma2d_blending_data->src_stride, dma2d_blending_data->src_bpp);
366  uint8_t* destAddr = _drawing_dma2d_adjust_address(dma2d_blending_data->dest_address, dma2d_blending_data->x_dest, dma2d_blending_data->y_dest, dma2d_blending_data->dest_stride, DRAWING_DMA2D_BPP);
367  g_dma2d_running = true;
368 
369  // cppcheck-suppress [misra-c2012-11.4] allow cast address in u32 (see HAL_DMA2D_BlendingStart_IT())
370  HAL_DMA2D_BlendingStart_IT(&g_hdma2d, (uint32_t)srcAddr, (uint32_t)destAddr, (uint32_t)destAddr, dma2d_blending_data->width, dma2d_blending_data->height);
371 }
372 
373 /*
374  * @brief Draws a region of an image at another position.
375  *
376  * This function draws block per block to prevent the overlapping in X.
377  *
378  * @param[in] dma2d_blending_data the blending configuration
379  */
380 static void _drawing_dma2d_overlap_horizontal(DRAWING_DMA2D_blending_t* dma2d_blending_data) {
381 
382  // retrieve band's width
383  jint width = dma2d_blending_data->width;
384  dma2d_blending_data->width = dma2d_blending_data->x_dest - dma2d_blending_data->x_src;
385  _drawing_dma2d_blending_configure(dma2d_blending_data);
386 
387  // go to x + band width
388  dma2d_blending_data->x_src += width;
389  dma2d_blending_data->x_dest = dma2d_blending_data->x_src + dma2d_blending_data->width;
390 
391  while (width > 0) {
392 
393  // adjust band's width
394  if (width < dma2d_blending_data->width){
395  dma2d_blending_data->width = width;
396  _drawing_dma2d_blending_configure(dma2d_blending_data);
397  }
398 
399  // adjust src & dest positions
400  dma2d_blending_data->x_src -= dma2d_blending_data->width;
401  dma2d_blending_data->x_dest -= dma2d_blending_data->width;
402 
403  _drawing_dma2d_wait();
404  _drawing_dma2d_blending_start(dma2d_blending_data);
405 
406  width -= dma2d_blending_data->width;
407  }
408 }
409 
410 /*
411  * @brief Draws a region of an image at another position.
412  *
413  * This function draws block per block to prevent the overlapping in Y.
414  *
415  * @param[in] dma2d_blending_data the blending configuration
416  */
417 static void _drawing_dma2d_overlap_vertical(DRAWING_DMA2D_blending_t* dma2d_blending_data) {
418 
419  // retrieve band's height
420  jint height = dma2d_blending_data->height;
421  dma2d_blending_data->height = dma2d_blending_data->y_dest - dma2d_blending_data->y_src;
422  _drawing_dma2d_blending_configure(dma2d_blending_data);
423 
424  // go to x + band height
425  dma2d_blending_data->y_src += height;
426  dma2d_blending_data->y_dest = dma2d_blending_data->y_src + dma2d_blending_data->height;
427 
428  while (height > 0) {
429 
430  // adjust band's height
431  if (height < dma2d_blending_data->height){
432  dma2d_blending_data->height = height;
433  // no need to configure again the DMA2D
434  }
435 
436  // adjust src & dest positions
437  dma2d_blending_data->y_src -= dma2d_blending_data->height;
438  dma2d_blending_data->y_dest -= dma2d_blending_data->height;
439 
440  _drawing_dma2d_wait();
441  _drawing_dma2d_blending_start(dma2d_blending_data);
442 
443  height -= dma2d_blending_data->height;
444  }
445 }
446 
447 static void _drawing_dma2d_restore_callback(bool from_isr) {
448  // notify memcpy's caller
449  UI_DRAWING_DMA2D_memcpy_callback(from_isr);
450  // unlock the MicroUI Graphics Engine
451  LLUI_DISPLAY_notifyAsynchronousDrawingEnd(from_isr);
452 }
453 
454 // --------------------------------------------------------------------------------
455 // Interrupt functions
456 // --------------------------------------------------------------------------------
457 
458 // See the header file for the function documentation
459 void UI_DRAWING_DMA2D_IRQHandler(void) {
460  // notify STM32 HAL
461  HAL_DMA2D_IRQHandler(&g_hdma2d);
462 
463  // notify DMA2D users
464  _drawing_dma2d_done();
465 
466  // notify graphical engine
467  g_callback_notification(true);
468 }
469 
470 // --------------------------------------------------------------------------------
471 // Public functions
472 // --------------------------------------------------------------------------------
473 
474 // See the header file for the function documentation
475 void UI_DRAWING_DMA2D_initialize(void* binary_semaphore_handle) {
476  // configure globals
477  g_dma2d_running = false;
478  g_dma2d_semaphore = binary_semaphore_handle;
479 
480  // configure DMA2D IRQ handler
481  HAL_NVIC_SetPriority(DMA2D_IRQn, 5, 3);
482  HAL_NVIC_EnableIRQ(DMA2D_IRQn);
483 
484  // configure DMA2D
485  g_hdma2d.Init.ColorMode = DRAWING_DMA2D_FORMAT;
486  g_hdma2d.Instance = DMA2D;
487 }
488 
489 // See the header file for the function documentation
490 void UI_DRAWING_DMA2D_configure_memcpy(uint8_t* srcAddr, uint8_t* destAddr, uint32_t xmin, uint32_t ymin, uint32_t xmax, uint32_t ymax, uint32_t stride, DRAWING_DMA2D_memcpy* memcpy_data) {
491  _drawing_dma2d_wait();
492 
493  uint32_t width = (xmax - xmin + (uint32_t)1);
494  uint32_t height = (ymax - ymin + (uint32_t)1);
495 
496  // de-init DMA2D
497  HAL_DMA2D_DeInit(&g_hdma2d);
498 
499  // configure foreground
500  g_hdma2d.LayerCfg[1].InputOffset = stride - width;
501  g_hdma2d.LayerCfg[1].InputColorMode = DRAWING_DMA2D_FORMAT;
502  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
503 
504  // configure DMA2D
505  g_hdma2d.Init.Mode = DMA2D_M2M;
506  g_hdma2d.Init.OutputOffset = stride - width;
507  HAL_DMA2D_Init(&g_hdma2d) ;
508 
509  // configure given structure (to give later to DRAWING_DMA2D_start_memcpy())
510  memcpy_data->src_address = _drawing_dma2d_adjust_address(srcAddr, xmin, ymin, stride, DRAWING_DMA2D_BPP);
511  memcpy_data->dest_address = _drawing_dma2d_adjust_address(destAddr, xmin, ymin, stride, DRAWING_DMA2D_BPP);
512  memcpy_data->width = width;
513  memcpy_data->height = height;
514 
515  // configure environment
516  g_callback_notification = &UI_DRAWING_DMA2D_memcpy_callback; // default callback
517  g_dma2d_running = true;
518 }
519 
520 // See the header file for the function documentation
521 void UI_DRAWING_DMA2D_start_memcpy(DRAWING_DMA2D_memcpy* memcpy_data) {
522  _cleanDCache();
523  HAL_DMA2D_Start_IT(
524  &g_hdma2d,
525  // cppcheck-suppress [misra-c2012-11.4] cast address as expectded by DMA2D driver
526  (uint32_t)(memcpy_data->src_address),
527  // cppcheck-suppress [misra-c2012-11.4] cast address as expectded by DMA2D driver
528  (uint32_t)(memcpy_data->dest_address),
529  memcpy_data->width,
530  memcpy_data->height
531  );
532 }
533 
534 // See the header file for the function documentation
535 BSP_DECLARE_WEAK_FCNT void UI_DRAWING_DMA2D_memcpy_callback(bool from_isr) {
536  // does nothing by default (see header file)
537  (void)from_isr;
538 }
539 
540 // --------------------------------------------------------------------------------
541 // ui_display_brs.h functions
542 // --------------------------------------------------------------------------------
543 
544 // See the header file for the function documentation
545 DRAWING_Status UI_DISPLAY_BRS_restore(MICROUI_GraphicsContext* gc, MICROUI_Image* old_back_buffer, ui_rect_t* rect) {
546  DRAWING_DMA2D_memcpy memcpy_data;
547  UI_DRAWING_DMA2D_configure_memcpy(LLUI_DISPLAY_getBufferAddress(old_back_buffer), LLUI_DISPLAY_getBufferAddress(&gc->image), rect->x1, rect->y1, rect->x2, rect->y2, LLUI_DISPLAY_getStrideInPixels(&gc->image), &memcpy_data);
548  g_callback_notification = &_drawing_dma2d_restore_callback;
549  UI_DRAWING_DMA2D_start_memcpy(&memcpy_data);
550  return DRAWING_RUNNING;
551 }
552 
553 // --------------------------------------------------------------------------------
554 // ui_drawing.h / ui_drawing_dma2d.h functions
555 // (the function names differ according to the available number of destination formats)
556 // --------------------------------------------------------------------------------
557 
558 // See the header file for the function documentation
559 DRAWING_Status UI_DRAWING_DMA2D_fillRectangle(MICROUI_GraphicsContext* gc, jint x1, jint y1, jint x2, jint y2) {
560 
561  _drawing_dma2d_wait();
562 
563  uint32_t rectangle_width = x2 - x1 + 1;
564  uint32_t rectangle_height = y2 - y1 + 1;
565  uint32_t stride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
566 
567  // de-init DMA2D
568  HAL_DMA2D_DeInit(&g_hdma2d);
569 
570  // configure DMA2D
571  g_hdma2d.Init.Mode = DMA2D_R2M;
572  g_hdma2d.Init.OutputOffset = stride - rectangle_width;
573  HAL_DMA2D_Init(&g_hdma2d);
574 
575  // configure environment
576  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
577  g_dma2d_running = true;
578 
579  // start DMA2D
580  _cleanDCache();
581  uint8_t* destination_address = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x1, y1, stride, DRAWING_DMA2D_BPP);
582  // cppcheck-suppress [misra-c2012-11.4] allow cast address in u32 (see HAL_DMA2D_Start_IT())
583  HAL_DMA2D_Start_IT(&g_hdma2d, gc->foreground_color, (uint32_t)destination_address, rectangle_width, rectangle_height);
584 
585  return DRAWING_RUNNING;
586 }
587 
588 // See the header file for the function documentation
589 DRAWING_Status UI_DRAWING_DMA2D_drawImage(MICROUI_GraphicsContext* gc, MICROUI_Image* image, jint x_src, jint y_src, jint width, jint height, jint x_dest, jint y_dest, jint alpha) {
590 
591  DRAWING_Status ret;
592  DRAWING_DMA2D_blending_t dma2d_blending_data;
593 
594  if (_drawing_dma2d_is_image_compatible_with_dma2d(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha, &dma2d_blending_data)){
595  _drawing_dma2d_blending_configure(&dma2d_blending_data);
596  _drawing_dma2d_blending_start(&dma2d_blending_data);
597  ret = DRAWING_RUNNING;
598  }
599  else {
600 #if !defined(LLUI_IMAGE_CUSTOM_FORMATS)
601  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
602  ret = DRAWING_DONE;
603 #else
604  ret = UI_IMAGE_DRAWING_draw(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
605 #endif
606  }
607 
608  return ret;
609 }
610 
611 // See the header file for the function documentation
612 DRAWING_Status UI_DRAWING_DMA2D_copyImage(MICROUI_GraphicsContext* gc, MICROUI_Image* img, jint regionX, jint regionY, jint width, jint height, jint x, jint y){
613  return (img == &gc->image) ?
614  // have to manage the overlap
615  UI_DRAWING_DMA2D_drawRegion(gc, regionX, regionY, width, height, x, y, 0xff)
616  // no overlap: draw image as usual
617  : UI_DRAWING_DMA2D_drawImage(gc, img, regionX, regionY, width, height, x, y, 0xff);
618 }
619 
620 // See the header file for the function documentation
621 DRAWING_Status UI_DRAWING_DMA2D_drawRegion(MICROUI_GraphicsContext* gc, jint x_src, jint y_src, jint width, jint height, jint x_dest, jint y_dest, jint alpha){
622 
623  DRAWING_Status ret;
624  DRAWING_DMA2D_blending_t dma2d_blending_data;
625 
626  if (_drawing_dma2d_is_image_compatible_with_dma2d(gc, &gc->image, x_src, y_src, width, height, x_dest, y_dest, alpha, &dma2d_blending_data)){
627  // use several times the DMA2D
628 
629  if ((dma2d_blending_data.y_dest == dma2d_blending_data.y_src) && (dma2d_blending_data.x_dest > dma2d_blending_data.x_src) && (dma2d_blending_data.x_dest < (dma2d_blending_data.x_src + dma2d_blending_data.width))){
630  // draw with overlap: cut the drawings in several widths
631  _drawing_dma2d_overlap_horizontal(&dma2d_blending_data);
632  }
633  else if ((dma2d_blending_data.y_dest > dma2d_blending_data.y_src) && (dma2d_blending_data.y_dest < (dma2d_blending_data.y_src + dma2d_blending_data.height))){
634  // draw with overlap: cut the drawings in several heights
635  _drawing_dma2d_overlap_vertical(&dma2d_blending_data);
636  }
637  else {
638  // draw source on itself applying an opacity and without overlap
639  _drawing_dma2d_blending_configure(&dma2d_blending_data);
640  _drawing_dma2d_blending_start(&dma2d_blending_data);
641  }
642  ret = DRAWING_RUNNING;
643  }
644  else {
645  // image not compatible with the DMA2D: let the Graphics Engine do the drawing
646 #if !defined(LLUI_IMAGE_CUSTOM_FORMATS)
647  UI_DRAWING_SOFT_drawImage(gc, &gc->image, x_src, y_src, width, height, x_dest, y_dest, alpha);
648  ret = DRAWING_DONE;
649 #else
650  ret = UI_IMAGE_DRAWING_draw(gc, &gc->image, x_src, y_src, width, height, x_dest, y_dest, alpha);
651 #endif
652  }
653  return ret;
654 }
655 
656 // --------------------------------------------------------------------------------
657 // EOF
658 // --------------------------------------------------------------------------------