microui  4.1.0
microui
LLUI_PAINTER_impl.c
1 /*
2  * Copyright 2020-2024 MicroEJ Corp. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be found with this software.
4  */
5 
6 /*
7  * @file
8  * @brief This file implements all MicroUI drawing native functions.
9  * @see LLUI_PAINTER_impl.h file comment
10  * @author MicroEJ Developer Team
11  * @version 4.1.0
12  * @since MicroEJ UI Pack 13.0.0
13  */
14 
15 // --------------------------------------------------------------------------------
16 // Includes
17 // --------------------------------------------------------------------------------
18 
19 // implements LLUI_PAINTER_impl functions
20 #include <LLUI_PAINTER_impl.h>
21 
22 // use graphical engine functions to synchronize drawings
23 #include <LLUI_DISPLAY.h>
24 
25 // check UI Pack version
26 #include <LLUI_DISPLAY_impl.h>
27 
28 // calls ui_drawing functions
29 #include "ui_drawing.h"
30 
31 // logs the drawings
32 #include "ui_log.h"
33 
34 // --------------------------------------------------------------------------------
35 // Macros and Defines
36 // --------------------------------------------------------------------------------
37 
38 #if (defined(LLUI_MAJOR_VERSION) && (LLUI_MAJOR_VERSION != 14)) || (defined(LLUI_MINOR_VERSION) && \
39  (LLUI_MINOR_VERSION < 0))
40 #error "This CCO is only compatible with UI Pack [14.0.0,15.0.0["
41 #endif
42 
43 // macros to log a drawing
44 #define LOG_DRAW_START(fn) LLTRACE_record_event_u32(LLUI_EVENT_group, LLUI_EVENT_offset + UI_LOG_DRAW, \
45  CONCAT_DEFINES(LOG_DRAW_, fn))
46 #define LOG_DRAW_END(s) LLTRACE_record_event_end_u32(LLUI_EVENT_group, LLUI_EVENT_offset + UI_LOG_DRAW, (s))
47 
48 /*
49  * LOG_DRAW_EVENT logs identifiers
50  */
51 #define LOG_DRAW_writePixel 1
52 #define LOG_DRAW_drawLine 2
53 #define LOG_DRAW_drawHorizontalLine 3
54 #define LOG_DRAW_drawVerticalLine 4
55 #define LOG_DRAW_drawRectangle 5
56 #define LOG_DRAW_fillRectangle 6
57 #define LOG_DRAW_drawRoundedRectangle 8
58 #define LOG_DRAW_fillRoundedRectangle 9
59 #define LOG_DRAW_drawCircleArc 10
60 #define LOG_DRAW_fillCircleArc 11
61 #define LOG_DRAW_drawEllipseArc 12
62 #define LOG_DRAW_fillEllipseArc 13
63 #define LOG_DRAW_drawEllipse 14
64 #define LOG_DRAW_fillEllipse 15
65 #define LOG_DRAW_drawCircle 16
66 #define LOG_DRAW_fillCircle 17
67 #define LOG_DRAW_drawARGB 18
68 #define LOG_DRAW_drawImage 19
69 
70 // --------------------------------------------------------------------------------
71 // Private functions
72 // --------------------------------------------------------------------------------
73 
74 /*
75  * Checks given bound to fit in bound limits: 0 and max (excluded). Updates size and
76  * origin in consequence
77  */
78 static inline void _check_bound(jint max, jint *bound, jint *size, jint *origin) {
79  if (*bound < 0) {
80  *size += *bound; // decrease size
81  *origin -= *bound; // increase origin
82  *bound = 0;
83  }
84 
85  if ((*bound + *size) > max) {
86  *size = max - *bound; // decrease size
87  }
88 }
89 
90 // --------------------------------------------------------------------------------
91 // LLUI_PAINTER_impl.h functions
92 // --------------------------------------------------------------------------------
93 
94 // See the header file for the function documentation
95 void LLUI_PAINTER_IMPL_writePixel(MICROUI_GraphicsContext *gc, jint x, jint y) {
96  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_writePixel)) {
97  DRAWING_Status status;
98  LOG_DRAW_START(writePixel);
99  if (LLUI_DISPLAY_isPixelInClip(gc, x, y)) {
100  LLUI_DISPLAY_configureClip(gc, false /* point is in clip */);
101  status = UI_DRAWING_writePixel(gc, x, y);
102  } else {
103  // requestDrawing() has been called and accepted: notify the end of empty drawing
104  status = DRAWING_DONE;
105  }
106  LLUI_DISPLAY_setDrawingStatus(status);
107  LOG_DRAW_END(status);
108  }
109 }
110 
111 // See the header file for the function documentation
112 void LLUI_PAINTER_IMPL_drawLine(MICROUI_GraphicsContext *gc, jint startX, jint startY, jint endX, jint endY) {
113  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawLine)) {
114  LOG_DRAW_START(drawLine);
115  // cannot reduce/clip line: may be endX < startX and / or endY < startY
116  DRAWING_Status status = UI_DRAWING_drawLine(gc, startX, startY, endX, endY);
117  LLUI_DISPLAY_setDrawingStatus(status);
118  LOG_DRAW_END(status);
119  }
120 }
121 
122 // See the header file for the function documentation
123 void LLUI_PAINTER_IMPL_drawHorizontalLine(MICROUI_GraphicsContext *gc, jint x, jint y, jint length) {
124  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawHorizontalLine)) {
125  DRAWING_Status status;
126  LOG_DRAW_START(drawHorizontalLine);
127 
128  jint x1 = x;
129  jint x2 = x + length - 1;
130 
131  // tests on size and clip are performed after suspend to prevent to perform it several times
132  if ((length > 0) && LLUI_DISPLAY_clipHorizontalLine(gc, &x1, &x2, y)) {
133  LLUI_DISPLAY_configureClip(gc, false /* line has been clipped */);
134  status = UI_DRAWING_drawHorizontalLine(gc, x1, x2, y);
135  } else {
136  // requestDrawing() has been called and accepted: notify the end of empty drawing
137  status = DRAWING_DONE;
138  }
139  LLUI_DISPLAY_setDrawingStatus(status);
140  LOG_DRAW_END(status);
141  }
142 }
143 
144 // See the header file for the function documentation
145 void LLUI_PAINTER_IMPL_drawVerticalLine(MICROUI_GraphicsContext *gc, jint x, jint y, jint length) {
146  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawVerticalLine)) {
147  DRAWING_Status status;
148  LOG_DRAW_START(drawVerticalLine);
149 
150  jint y1 = y;
151  jint y2 = y + length - 1;
152 
153  // tests on size and clip are performed after suspend to prevent to perform it several times
154  if ((length > 0) && LLUI_DISPLAY_clipVerticalLine(gc, &y1, &y2, x)) {
155  LLUI_DISPLAY_configureClip(gc, false /* line has been clipped */);
156  status = UI_DRAWING_drawVerticalLine(gc, x, y1, y2);
157  } else {
158  // requestDrawing() has been called and accepted: notify the end of empty drawing
159  status = DRAWING_DONE;
160  }
161  LLUI_DISPLAY_setDrawingStatus(status);
162  LOG_DRAW_END(status);
163  }
164 }
165 
166 // See the header file for the function documentation
167 void LLUI_PAINTER_IMPL_drawRectangle(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height) {
168  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawRectangle)) {
169  DRAWING_Status status;
170  LOG_DRAW_START(drawRectangle);
171 
172  // tests on size and clip are performed after suspend to prevent to perform it several times
173  if ((width > 0) && (height > 0)) {
174  jint x1 = x;
175  jint x2 = x + width - 1;
176  jint y1 = y;
177  jint y2 = y + height - 1;
178 
179  // cannot reduce rectangle; can only check if it is fully in clip
180  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRectangleInClip(gc, x1, y1, x2, y2));
181  status = UI_DRAWING_drawRectangle(gc, x1, y1, x2, y2);
182  } else {
183  // requestDrawing() has been called and accepted: notify the end of empty drawing
184  status = DRAWING_DONE;
185  }
186  LLUI_DISPLAY_setDrawingStatus(status);
187  LOG_DRAW_END(status);
188  }
189 }
190 
191 // See the header file for the function documentation
192 void LLUI_PAINTER_IMPL_fillRectangle(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height) {
193  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_fillRectangle)) {
194  DRAWING_Status status;
195  LOG_DRAW_START(fillRectangle);
196 
197  jint x1 = x;
198  jint x2 = x + width - 1;
199  jint y1 = y;
200  jint y2 = y + height - 1;
201 
202  // tests on size and clip are performed after suspend to prevent to perform it several times
203  if ((width > 0) && (height > 0) && LLUI_DISPLAY_clipRectangle(gc, &x1, &y1, &x2, &y2)) {
204  LLUI_DISPLAY_configureClip(gc, false /* rectangle has been clipped */);
205  status = UI_DRAWING_fillRectangle(gc, x1, y1, x2, y2);
206  } else {
207  // requestDrawing() has been called and accepted: notify the end of empty drawing
208  status = DRAWING_DONE;
209  }
210  LLUI_DISPLAY_setDrawingStatus(status);
211  LOG_DRAW_END(status);
212  }
213 }
214 
215 // See the header file for the function documentation
216 void LLUI_PAINTER_IMPL_drawRoundedRectangle(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height,
217  jint cornerEllipseWidth, jint cornerEllipseHeight) {
218  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawRoundedRectangle)) {
219  DRAWING_Status status;
220  LOG_DRAW_START(drawRoundedRectangle);
221 
222  // tests on size and clip are performed after suspend to prevent to perform it several times
223  if ((width > 0) && (height > 0)) {
224  // cannot reduce rectangle; can only check if it is fully in clip
225  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, width, height));
226  status = UI_DRAWING_drawRoundedRectangle(gc, x, y, width, height, cornerEllipseWidth, cornerEllipseHeight);
227  } else {
228  // requestDrawing() has been called and accepted: notify the end of empty drawing
229  status = DRAWING_DONE;
230  }
231  LLUI_DISPLAY_setDrawingStatus(status);
232  LOG_DRAW_END(status);
233  }
234 }
235 
236 // See the header file for the function documentation
237 void LLUI_PAINTER_IMPL_fillRoundedRectangle(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height,
238  jint cornerEllipseWidth, jint cornerEllipseHeight) {
239  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_fillRoundedRectangle)) {
240  DRAWING_Status status;
241  LOG_DRAW_START(fillRoundedRectangle);
242 
243  // tests on size and clip are performed after suspend to prevent to perform it several times
244  if ((width > 0) && (height > 0)) {
245  // cannot reduce rectangle; can only check if it is fully in clip
246  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, width, height));
247  status = UI_DRAWING_fillRoundedRectangle(gc, x, y, width, height, cornerEllipseWidth, cornerEllipseHeight);
248  } else {
249  // requestDrawing() has been called and accepted: notify the end of empty drawing
250  status = DRAWING_DONE;
251  }
252  LLUI_DISPLAY_setDrawingStatus(status);
253  LOG_DRAW_END(status);
254  }
255 }
256 
257 // See the header file for the function documentation
258 void LLUI_PAINTER_IMPL_drawCircleArc(MICROUI_GraphicsContext *gc, jint x, jint y, jint diameter, jfloat startAngle,
259  jfloat arcAngle) {
260  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawCircleArc)) {
261  DRAWING_Status status;
262  LOG_DRAW_START(drawCircleArc);
263 
264  // tests on size and clip are performed after suspend to prevent to perform it several times
265  if ((diameter > 0) && ((int32_t)arcAngle != 0)) {
266  // cannot reduce rectangle; can only check if it is fully in clip
267  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, diameter, diameter));
268  status = UI_DRAWING_drawCircleArc(gc, x, y, diameter, startAngle, arcAngle);
269  } else {
270  // requestDrawing() has been called and accepted: notify the end of empty drawing
271  status = DRAWING_DONE;
272  }
273  LLUI_DISPLAY_setDrawingStatus(status);
274  LOG_DRAW_END(status);
275  }
276 }
277 
278 // See the header file for the function documentation
279 void LLUI_PAINTER_IMPL_drawEllipseArc(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height,
280  jfloat startAngle, jfloat arcAngle) {
281  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawEllipseArc)) {
282  DRAWING_Status status;
283  LOG_DRAW_START(drawEllipseArc);
284 
285  // tests on size and clip are performed after suspend to prevent to perform it several times
286  if ((width > 0) && (height > 0) && ((int32_t)arcAngle != 0)) {
287  // cannot reduce rectangle; can only check if it is fully in clip
288  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, width, height));
289  status = UI_DRAWING_drawEllipseArc(gc, x, y, width, height, startAngle, arcAngle);
290  } else {
291  // requestDrawing() has been called and accepted: notify the end of empty drawing
292  status = DRAWING_DONE;
293  }
294  LLUI_DISPLAY_setDrawingStatus(status);
295  LOG_DRAW_END(status);
296  }
297 }
298 
299 // See the header file for the function documentation
300 void LLUI_PAINTER_IMPL_fillCircleArc(MICROUI_GraphicsContext *gc, jint x, jint y, jint diameter, jfloat startAngle,
301  jfloat arcAngle) {
302  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_fillCircleArc)) {
303  DRAWING_Status status;
304  LOG_DRAW_START(fillCircleArc);
305 
306  // tests on size and clip are performed after suspend to prevent to perform it several times
307  if ((diameter > 0) && ((int32_t)arcAngle != 0)) {
308  // cannot reduce rectangle; can only check if it is fully in clip
309  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, diameter, diameter));
310  status = UI_DRAWING_fillCircleArc(gc, x, y, diameter, startAngle, arcAngle);
311  } else {
312  // requestDrawing() has been called and accepted: notify the end of empty drawing
313  status = DRAWING_DONE;
314  }
315  LLUI_DISPLAY_setDrawingStatus(status);
316  LOG_DRAW_END(status);
317  }
318 }
319 
320 // See the header file for the function documentation
321 void LLUI_PAINTER_IMPL_fillEllipseArc(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height,
322  jfloat startAngle, jfloat arcAngle) {
323  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_fillEllipseArc)) {
324  DRAWING_Status status;
325  LOG_DRAW_START(fillEllipseArc);
326 
327  // tests on size and clip are performed after suspend to prevent to perform it several times
328  if ((width > 0) && (height > 0) && ((int32_t)arcAngle != 0)) {
329  // cannot reduce rectangle; can only check if it is fully in clip
330  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, width, height));
331  status = UI_DRAWING_fillEllipseArc(gc, x, y, width, height, startAngle, arcAngle);
332  } else {
333  // requestDrawing() has been called and accepted: notify the end of empty drawing
334  status = DRAWING_DONE;
335  }
336  LLUI_DISPLAY_setDrawingStatus(status);
337  LOG_DRAW_END(status);
338  }
339 }
340 
341 // See the header file for the function documentation
342 void LLUI_PAINTER_IMPL_drawEllipse(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height) {
343  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawEllipse)) {
344  DRAWING_Status status;
345  LOG_DRAW_START(drawEllipse);
346 
347  // tests on size and clip are performed after suspend to prevent to perform it several times
348  if ((width > 0) && (height > 0)) {
349  // cannot reduce rectangle; can only check if it is fully in clip
350  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, width, height));
351  status = UI_DRAWING_drawEllipse(gc, x, y, width, height);
352  } else {
353  // requestDrawing() has been called and accepted: notify the end of empty drawing
354  status = DRAWING_DONE;
355  }
356  LLUI_DISPLAY_setDrawingStatus(status);
357  LOG_DRAW_END(status);
358  }
359 }
360 
361 // See the header file for the function documentation
362 void LLUI_PAINTER_IMPL_fillEllipse(MICROUI_GraphicsContext *gc, jint x, jint y, jint width, jint height) {
363  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_fillEllipse)) {
364  DRAWING_Status status;
365  LOG_DRAW_START(fillEllipse);
366 
367  // tests on size and clip are performed after suspend to prevent to perform it several times
368  if ((width > 0) && (height > 0)) {
369  // cannot reduce rectangle; can only check if it is fully in clip
370  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, width, height));
371  status = UI_DRAWING_fillEllipse(gc, x, y, width, height);
372  } else {
373  // requestDrawing() has been called and accepted: notify the end of empty drawing
374  status = DRAWING_DONE;
375  }
376  LLUI_DISPLAY_setDrawingStatus(status);
377  LOG_DRAW_END(status);
378  }
379 }
380 
381 // See the header file for the function documentation
382 void LLUI_PAINTER_IMPL_drawCircle(MICROUI_GraphicsContext *gc, jint x, jint y, jint diameter) {
383  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawCircle)) {
384  DRAWING_Status status;
385  LOG_DRAW_START(drawCircle);
386 
387  // tests on size and clip are performed after suspend to prevent to perform it several times
388  if (diameter > 0) {
389  // cannot reduce rectangle; can only check if it is fully in clip
390  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, diameter, diameter));
391  status = UI_DRAWING_drawCircle(gc, x, y, diameter);
392  } else {
393  // requestDrawing() has been called and accepted: notify the end of empty drawing
394  status = DRAWING_DONE;
395  }
396  LLUI_DISPLAY_setDrawingStatus(status);
397  LOG_DRAW_END(status);
398  }
399 }
400 
401 // See the header file for the function documentation
402 void LLUI_PAINTER_IMPL_fillCircle(MICROUI_GraphicsContext *gc, jint x, jint y, jint diameter) {
403  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_fillCircle)) {
404  DRAWING_Status status;
405  LOG_DRAW_START(fillCircle);
406 
407  // tests on size and clip are performed after suspend to prevent to perform it several times
408  if (diameter > 0) {
409  // cannot reduce rectangle; can only check if it is fully in clip
410  LLUI_DISPLAY_configureClip(gc, !LLUI_DISPLAY_isRegionInClip(gc, x, y, diameter, diameter));
411  status = UI_DRAWING_fillCircle(gc, x, y, diameter);
412  } else {
413  // requestDrawing() has been called and accepted: notify the end of empty drawing
414  status = DRAWING_DONE;
415  }
416  LLUI_DISPLAY_setDrawingStatus(status);
417  LOG_DRAW_END(status);
418  }
419 }
420 
421 // See the header file for the function documentation
422 void LLUI_PAINTER_IMPL_drawImage(MICROUI_GraphicsContext *gc, MICROUI_Image *img, jint regionX, jint regionY,
423  jint width, jint height, jint x, jint y, jint alpha) {
424  if (LLUI_DISPLAY_requestDrawing(gc, (SNI_callback) & LLUI_PAINTER_IMPL_drawImage)) {
425  DRAWING_Status status = DRAWING_DONE;
426  LOG_DRAW_START(drawImage);
427 
428  // tests on parameters and clip are performed after suspend to prevent to perform it several times
429  if (!LLUI_DISPLAY_isClosed(img) && (alpha > 0)) {
430  // sanity check on opacity
431  jint l_alpha = (alpha > 255) ? 255 : alpha;
432 
433  // compute inside image bounds
434  _check_bound(img->width, &regionX, &width, &x);
435  _check_bound(img->height, &regionY, &height, &y);
436 
437  // compute inside graphics context bounds
438  _check_bound(gc->image.width, &x, &width, &regionX);
439  _check_bound(gc->image.height, &y, &height, &regionY);
440 
441  if ((width > 0) && (height > 0) && LLUI_DISPLAY_clipRegion(gc, &regionX, &regionY, &width, &height, &x,
442  &y)) {
443  LLUI_DISPLAY_configureClip(gc, false /* region has been clipped */);
444 
445  if (gc->image.format == img->format) {
446  // source & destination have got the same pixels memory representation
447 
448  MICROUI_Image *image = LLUI_DISPLAY_getSourceImage(img);
449 
450  if ((0xff /* fully opaque */ == l_alpha) && !LLUI_DISPLAY_isTransparent(img)) {
451  // copy source on destination without applying an opacity (beware about the overlapping)
452  status = UI_DRAWING_copyImage(gc, image, regionX, regionY, width, height, x, y);
453  } else if (LLUI_DISPLAY_getBufferAddress(img) == LLUI_DISPLAY_getBufferAddress(&gc->image)) {
454  // blend source on itself applying an opacity (beware about the overlapping)
455  status = UI_DRAWING_drawRegion(gc, regionX, regionY, width, height, x, y, l_alpha);
456  } else {
457  // blend source on destination applying an opacity
458  status = UI_DRAWING_drawImage(gc, image, regionX, regionY, width, height, x, y, l_alpha);
459  }
460  } else {
461  // draw source on destination applying an opacity
462  status = UI_DRAWING_drawImage(gc, img, regionX, regionY, width, height, x, y, l_alpha);
463  }
464  }
465  // else: nothing to do
466  }
467  // else: nothing to do
468 
469  // requestDrawing() has been called and accepted: notify the end of empty drawing
470  LLUI_DISPLAY_setDrawingStatus(status);
471  LOG_DRAW_END(status);
472  }
473 }
474 
475 // --------------------------------------------------------------------------------
476 // EOF
477 // --------------------------------------------------------------------------------