display-dma2d  1.0.6
display-dma2d
drawing_dma2d.c
Go to the documentation of this file.
1 /*
2  * C
3  *
4  * Copyright 2019-2020 MicroEJ Corp. All rights reserved.
5  * This library is provided in source code for use, modification and test, subject to license terms.
6  * Any modification of the source code will break MicroEJ Corp. warranties on the whole library.
7  */
8 
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20 
21 
22 /* Includes ------------------------------------------------------------------*/
23 
24 #include "drawing_dma2d.h"
25 #include "ui_drawing.h"
26 #include "ui_drawing_soft.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 /* Private fields ------------------------------------------------------------*/
52 
53 /*
54  * @brief STM32 HAL DMA2D declaration.
55  */
56 static DMA2D_HandleTypeDef g_hdma2d;
57 
58 /*
59  * @brief Next notification to call after DMA2D work.
60  */
61 static t_drawing_notification g_callback_notification;
62 
63 /*
64  * @brief This boolean is set to true before launching a DMA2D action and back to
65  * false during DMA2D interrupt. It allows to know if the DMA2D is running or not.
66  */
67 static bool g_dma2d_running;
68 
69 /*
70  * @brief Binary semaphore used to lock a DMA2D user when DMA2D is already running.
71  */
72 static void* g_dma2d_semaphore;
73 
74 /* Private functions ---------------------------------------------------------*/
75 
76 /*
77  * @brief Ensures DMA2D previous work is done before returning.
78  */
79 static inline void _drawing_dma2d_wait(void)
80 {
81  while(g_dma2d_running)
82  {
83  LLUI_DISPLAY_IMPL_binarySemaphoreTake(g_dma2d_semaphore);
84  }
85 }
86 
87 /*
88  * @brief Notify the DMA2D has finished previous job.
89  */
90 static inline void _drawing_dma2d_done(void)
91 {
92  g_dma2d_running = false;
93  LLUI_DISPLAY_IMPL_binarySemaphoreGive(g_dma2d_semaphore, true);
94 }
95 
96 /*
97  * @brief Adjusts the given address to target the given point.
98  */
99 static inline uint8_t* _drawing_dma2d_adjust_address(uint8_t* address, uint32_t x, uint32_t y, uint32_t stride, uint32_t bpp)
100 {
101  return address + ((y * stride + x) * bpp / 8);
102 }
103 
104 /*
105  * @brief For A4 and A8 formats, alpha contains both the global alpha + wanted color.
106  */
107 static inline void _drawing_dma2d_configure_alpha_image_data(MICROUI_GraphicsContext* gc, jint* alphaAndColor)
108 {
109  // for A4 and A8 formats, alphaAndColor is both the global alpha + wanted color
110  *(alphaAndColor) <<= 24;
111  *(alphaAndColor) |= (gc->foreground_color & 0xffffff);
112 }
113 
114 /* Interrupt functions -------------------------------------------------------*/
115 
116 void DRAWING_DMA2D_IRQHandler(void)
117 {
118  // notify STM32 HAL
119  HAL_DMA2D_IRQHandler(&g_hdma2d);
120 
121  // notify DMA2D users
122  _drawing_dma2d_done();
123 
124  // notify graphical engine
125  g_callback_notification(true);
126 }
127 
128 /* Public functions ----------------------------------------------------------*/
129 
130 void DRAWING_DMA2D_initialize(void* binary_semaphore_handle)
131 {
132  // configure globals
133  g_dma2d_running = false;
134  g_dma2d_semaphore = binary_semaphore_handle;
135 
136  // configure DMA2D IRQ handler
137  HAL_NVIC_SetPriority(DMA2D_IRQn, 5, 3);
138  HAL_NVIC_EnableIRQ(DMA2D_IRQn);
139 
140  // configure DMA2D
141  g_hdma2d.Init.ColorMode = DRAWING_DMA2D_FORMAT;
142  g_hdma2d.Instance = DMA2D;
143 }
144 
145 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)
146 {
147  _drawing_dma2d_wait();
148 
149  uint32_t width = (xmax - xmin + 1);
150  uint32_t height = (ymax - ymin + 1);
151 
152  // de-init DMA2D
153  HAL_DMA2D_DeInit(&g_hdma2d);
154 
155  // configure foreground
156  g_hdma2d.LayerCfg[1].InputOffset = stride - width;
157  g_hdma2d.LayerCfg[1].InputColorMode = DRAWING_DMA2D_FORMAT;
158  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
159 
160  // configure DMA2D
161  g_hdma2d.Init.Mode = DMA2D_M2M;
162  g_hdma2d.Init.OutputOffset = stride - width;
163  HAL_DMA2D_Init(&g_hdma2d) ;
164 
165  // configure given structure (to give later to DRAWING_DMA2D_start_memcpy())
166  memcpy_data->src_address = _drawing_dma2d_adjust_address(srcAddr, xmin, ymin, stride, DRAWING_DMA2D_BPP);
167  memcpy_data->dest_address = _drawing_dma2d_adjust_address(destAddr, xmin, ymin, stride, DRAWING_DMA2D_BPP);
168  memcpy_data->width = width;
169  memcpy_data->height = height;
170 
171  // configure environment
172  g_callback_notification = &LLUI_DISPLAY_flushDone;
173  g_dma2d_running = true;
174 }
175 
176 void DRAWING_DMA2D_start_memcpy(DRAWING_DMA2D_memcpy* memcpy_data)
177 {
178  HAL_DMA2D_Start_IT(
179  &g_hdma2d,
180  (uint32_t)memcpy_data->src_address,
181  (uint32_t)memcpy_data->dest_address,
182  memcpy_data->width,
183  memcpy_data->height
184  );
185 }
186 
187 /* ui_drawing.h functions --------------------------------------------*/
188 
189 DRAWING_Status UI_DRAWING_fillRectangle(MICROUI_GraphicsContext* gc, jint x1, jint y1, jint x2, jint y2)
190 {
191  if (LLUI_DISPLAY_isClipEnabled(gc) && !LLUI_DISPLAY_clipRectangle(gc, &x1, &y1, &x2, &y2))
192  {
193  // drawing is out of clip: nothing to do
194  return DRAWING_DONE;
195  }
196  // else: clip disabled (means drawing is fully in clip) or drawing fits the clip
197 
198  _drawing_dma2d_wait();
199 
200  LLUI_DISPLAY_setDrawingLimits(gc, x1, y1, x2, y2);
201 
202  uint32_t rectangle_width = x2 - x1 + 1;
203  uint32_t rectangle_height = y2 - y1 + 1;
204  uint32_t stride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
205 
206  // de-init DMA2D
207  HAL_DMA2D_DeInit(&g_hdma2d);
208 
209  // configure DMA2D
210  g_hdma2d.Init.Mode = DMA2D_R2M;
211  g_hdma2d.Init.OutputOffset = stride - rectangle_width;
212  HAL_DMA2D_Init(&g_hdma2d);
213 
214  // configure environment
215  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
216  g_dma2d_running = true;
217 
218  // start DMA2D
219  uint8_t* destination_address = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x1, y1, stride, DRAWING_DMA2D_BPP);
220  HAL_DMA2D_Start_IT(&g_hdma2d, gc->foreground_color, (uint32_t)destination_address, rectangle_width, rectangle_height);
221 
222  return DRAWING_RUNNING;
223 }
224 
225 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)
226 {
227  if (LLUI_DISPLAY_isClipEnabled(gc) && !!LLUI_DISPLAY_clipRegion(gc, &x_src, &y_src, &width, &height, &x_dest, &y_dest))
228  {
229  // clip disabled( means drawing is fully in clip) or drawing is out of clip
230  // nothing to do
231  return DRAWING_DONE;
232  }
233 
234  if ((uint32_t)&(gc->image) == (uint32_t)image) {
235  // image in image or display in display
236 
237  if ((y_dest > y_src && y_dest < (y_src + height)) || (y_dest == y_src && x_dest > x_src && x_dest < (x_src + width))) {
238  // overlap bottom and/or right: DMA2D is not able to perform the drawing
239  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
240  return DRAWING_DONE;
241  }
242  }
243 
244  _drawing_dma2d_wait();
245 
246  uint8_t format;
247  uint8_t bpp;
248 
249  switch(image->format)
250  {
251  case MICROUI_IMAGE_FORMAT_RGB565:
252  format = CM_RGB565;
253  bpp = 16;
254  break;
255  case MICROUI_IMAGE_FORMAT_ARGB8888:
256  format = CM_ARGB8888;
257  bpp = 32;
258  break;
259  case MICROUI_IMAGE_FORMAT_RGB888:
260  format = CM_RGB888;
261  bpp = 24;
262  break;
263  case MICROUI_IMAGE_FORMAT_ARGB1555:
264  format = CM_ARGB1555;
265  bpp = 16;
266  break;
267  case MICROUI_IMAGE_FORMAT_ARGB4444:
268  format = CM_ARGB4444;
269  bpp = 16;
270  break;
271  case MICROUI_IMAGE_FORMAT_A4:
272  {
273  // check DMA2D limitations: first and last vertical lines addresses must be aligned on 8 bits
274 
275  jint xAlign = x_src & 1;
276  jint wAlign = width & 1;
277 
278  if (xAlign == 0) {
279  if (wAlign != 0) {
280  // hard cannot draw last vertical line
281  --width;
282  UI_DRAWING_SOFT_drawImage(gc, image, x_src + width, y_src, 1, height, x_dest + width, y_dest, alpha);
283  }
284  // else: easy case: first and last vertical lines are aligned on 8 bits
285  }
286  else {
287  if (wAlign == 0) {
288  // worst case: hard cannot draw first and last vertical lines
289 
290  // first line
291  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, 1, height, x_dest, y_dest, alpha);
292 
293  // last line
294  --width;
295  UI_DRAWING_SOFT_drawImage(gc, image, x_src + width, y_src, 1, height, x_dest + width, y_dest, alpha);
296 
297  ++x_src;
298  ++x_dest;
299  --width;
300  }
301  else {
302  // cannot draw first vertical line
303  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, 1, height, x_dest, y_dest, alpha);
304  ++x_src;
305  ++x_dest;
306  --width;
307  }
308  }
309 
310  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
311  format = CM_A4;
312  bpp = 4;
313  break;
314  }
315  case MICROUI_IMAGE_FORMAT_A8:
316  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
317  format = CM_A8;
318  bpp = 8;
319  break;
320  default:
321  // unsupported image format
322  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
323  return DRAWING_DONE;
324  }
325 
326  uint32_t srcStride = LLUI_DISPLAY_getStrideInPixels(image);
327  uint32_t destStride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
328  uint8_t* srcAddr = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(image), x_src, y_src, srcStride, bpp);
329  uint8_t* destAddr = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x_dest, y_dest, destStride, DRAWING_DMA2D_BPP);
330 
331  // de-init DMA2D
332  HAL_DMA2D_DeInit(&g_hdma2d);
333 
334  // configure background
335  g_hdma2d.LayerCfg[0].InputOffset = destStride - width;
336  g_hdma2d.LayerCfg[0].InputColorMode = DRAWING_DMA2D_FORMAT;
337  g_hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
338  g_hdma2d.LayerCfg[0].InputAlpha = 255;
339  HAL_DMA2D_ConfigLayer(&g_hdma2d, 0);
340 
341  // configure foreground
342  HAL_DMA2D_DisableCLUT(&g_hdma2d, 1);
343  g_hdma2d.LayerCfg[1].InputOffset = srcStride - width;
344  g_hdma2d.LayerCfg[1].InputColorMode = format;
345  g_hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA;
346  g_hdma2d.LayerCfg[1].InputAlpha = alpha;
347  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
348 
349  // configure DMA2D
350  g_hdma2d.Init.Mode = DMA2D_M2M_BLEND;
351  g_hdma2d.Init.OutputOffset = destStride - width;
352  HAL_DMA2D_Init(&g_hdma2d) ;
353 
354  // configure environment
355  g_dma2d_running = true;
356  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
357  LLUI_DISPLAY_setDrawingLimits(gc, x_dest, y_dest, x_dest + width - 1, y_dest + height - 1);
358 
359  // start DMA2D
360  HAL_DMA2D_BlendingStart_IT(&g_hdma2d, (uint32_t)srcAddr, (uint32_t)destAddr, (uint32_t)destAddr, width, height);
361 
362  return DRAWING_RUNNING;
363 }
364 
365 /* EOF -----------------------------------------------------------------------*/
366 
367 #ifdef __cplusplus
368 }
369 #endif
370 
371 
Use STM32 DMA2D (ChromART) for MicroEJ ui_drawing.h implementation.