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