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