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