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