EFM32 Gecko Software Documentation  efm32g-doc-5.1.2
glib_line.c
Go to the documentation of this file.
1  /*************************************************************************/
16 /* Standard C header files */
17 #include <stdint.h>
18 #include <stdbool.h>
19 
20 /* EM types */
21 #include "em_types.h"
22 
23 /* GLIB Header files */
24 #include "glib.h"
25 
26 /* Local function prototypes */
27 static uint8_t GLIB_getClipCode(GLIB_Context_t *pContext, int32_t x, int32_t y);
28 static bool GLIB_clipLine(GLIB_Context_t *pContext, int32_t *pX1, int32_t *pY1,
29  int32_t *pX2, int32_t *pY2);
30 
31 /**************************************************************************/
49 EMSTATUS GLIB_drawLineH(GLIB_Context_t *pContext, int32_t x1, int32_t y1,
50  int32_t x2)
51 {
52  EMSTATUS status;
53  int32_t swap;
54  uint8_t red;
55  uint8_t green;
56  uint8_t blue;
57  uint32_t length;
58 
59  /* Check arguments */
60  if (pContext == NULL) return GLIB_ERROR_INVALID_ARGUMENT;
61 
62  /* Check if line is outside of clipping region */
63  if ((y1 < pContext->clippingRegion.yMin) || (y1 > pContext->clippingRegion.yMax)) {
65  }
66 
67  /* Swap the coordinates if x1 is larger than x2 */
68  if (x1 > x2) {
69  swap = x1;
70  x1 = x2;
71  x2 = swap;
72  }
73 
74  /* Check if entire line is outside clipping region */
75  if ((x1 > pContext->clippingRegion.xMax) || (x2 < pContext->clippingRegion.xMin)) {
77  }
78 
79  /* Clip the line if necessary */
80  if (x1 < pContext->clippingRegion.xMin) x1 = pContext->clippingRegion.xMin;
81  if (x2 > pContext->clippingRegion.xMax) x2 = pContext->clippingRegion.xMax;
82 
83  /* Translate color and draw line using display driver */
84  length = x2 - x1 + 1;
85  status = DMD_setClippingArea(x1, y1, length, 1);
86  if (status != DMD_OK) return status;
87 
88  GLIB_colorTranslate24bpp(pContext->foregroundColor, &red, &green, &blue);
89  status = DMD_writeColor(0, 0, red, green, blue, length);
90  if (status != DMD_OK) return status;
91 
92  /* Reset driver clipping area to GLIB clipping region */
93  return GLIB_applyClippingRegion(pContext);
94 }
95 
96 /**************************************************************************/
113 EMSTATUS GLIB_drawLineV(GLIB_Context_t *pContext, int32_t x1, int32_t y1,
114  int32_t y2)
115 {
116  EMSTATUS status;
117  int32_t swap;
118  int32_t length;
119  uint8_t red;
120  uint8_t green;
121  uint8_t blue;
122 
123  /* Check arguments */
124  if (pContext == NULL) return GLIB_ERROR_INVALID_ARGUMENT;
125 
126  /* Check if line is outside of clipping region */
127  if ((x1 < pContext->clippingRegion.xMin) || (x1 > pContext->clippingRegion.xMax)) {
129  }
130 
131  /* Swap the coordinates if y1 is larger than y2 */
132  if (y1 > y2) {
133  swap = y1;
134  y1 = y2;
135  y2 = swap;
136  }
137 
138  /* Check if entire line is outside clipping region */
139  if ((y1 > pContext->clippingRegion.yMax) || (y2 < pContext->clippingRegion.yMin)) {
141  }
142 
143  /* Clip the line if necessary */
144  if (y1 < pContext->clippingRegion.yMin) y1 = pContext->clippingRegion.yMin;
145  if (y2 > pContext->clippingRegion.yMax) y2 = pContext->clippingRegion.yMax;
146 
147  /* Translate color and draw line using display driver clipping (width = 1 => height <=> length) */
148  length = y2 - y1 + 1;
149  status = DMD_setClippingArea(x1, y1, 1, length);
150  if (status != DMD_OK) return status;
151 
152  GLIB_colorTranslate24bpp(pContext->foregroundColor, &red, &green, &blue);
153  status = DMD_writeColor(0, 0, red, green, blue, length);
154  if (status != DMD_OK) return status;
155 
156  /* Reset driver clipping area to GLIB clipping region */
157  return GLIB_applyClippingRegion(pContext);
158 }
159 
160 /**************************************************************************/
175 static uint8_t GLIB_getClipCode(GLIB_Context_t *pContext, int32_t x, int32_t y)
176 {
177  uint8_t code = 0;
178 
179  /* The point is to the left of the clipping region */
180  if (x < pContext->clippingRegion.xMin) code |= 1;
181 
182  /* The point is to the right of the clipping region */
183  if (x > pContext->clippingRegion.xMax) code |= 2;
184 
185  /* The point is below the clipping region */
186  if (y > pContext->clippingRegion.yMax) code |= 4;
187 
188  /* The point is above clipping region */
189  if (y < pContext->clippingRegion.yMin) code |= 8;
190 
191  return code;
192 }
193 
194 /**************************************************************************/
217 static bool GLIB_clipLine(GLIB_Context_t *pContext, int32_t *pX1,
218  int32_t *pY1, int32_t *pX2, int32_t *pY2)
219 {
220  uint8_t currentCode, code1, code2;
221  int32_t x=0, y=0;
222 
223  /* Compute the clipping code for the two points */
224  code1 = GLIB_getClipCode(pContext, *pX1, *pY1);
225  code2 = GLIB_getClipCode(pContext, *pX2, *pY2);
226 
227  while (true) {
228  /* Case 1: Check if the points is inside the clipping rectangle */
229  if ((code1 | code2) == 0) return true;
230 
231  /* Case 2: Check if the points can be trivially rejected */
232  if (code1 & code2) return false;
233 
234  /* Case 3: Move the points so they can be either trivially accepted or rejected */
235  /* Choose one of the points that are outside of the clipping region */
236  if (code1) currentCode = code1;
237  else currentCode = code2;
238 
239  /* Check if currentCode is to the left of the clipping region */
240  if (currentCode & 1) {
241  /* Move the point to the left edge of the clipping region */
242  y = *pY1 + ((*pY2 - *pY1) * (pContext->clippingRegion.xMin - *pX1)) / (*pX2 - *pX1);
243  x = pContext->clippingRegion.xMin;
244  }
245 
246  /* Check if currentCode is to the right of the clipping region */
247  else if (currentCode & 2) {
248  /* Move the point to the right edge of the clipping region */
249  x = pContext->clippingRegion.xMax;
250  y = *pY1 + ((*pY2 - *pY1) * (pContext->clippingRegion.xMax - *pX1)) / (*pX2 - *pX1);
251  }
252 
253  /* Check if currentCode is below the clipping region */
254  else if (currentCode & 4) {
255  /* Move the point to the bottom of the clipping region */
256  y = pContext->clippingRegion.yMax;
257  x = *pX1 + ((*pX2 - *pX1) * (pContext->clippingRegion.yMax - *pY1)) / (*pY2 - *pY1);
258  }
259 
260  /* Check if currentCode is above the clipping region */
261  else if (currentCode & 8) {
262  /* Move the point to the top of the clipping region */
263  y = pContext->clippingRegion.yMin;
264  x = *pX1 + ((*pX2 - *pX1) * (pContext->clippingRegion.yMin - *pY1)) / (*pY2 - *pY1);
265  }
266 
267  /* Determine which point is moved and set the new coordinates */
268  if (code1) {
269  *pX1 = x;
270  *pY1 = y;
271  /* Compute new clipCode */
272  code1 = GLIB_getClipCode(pContext, x, y);
273  } else {
274  *pX2 = x;
275  *pY2 = y;
276  /* Compute new clipCode */
277  code2 = GLIB_getClipCode(pContext, x, y);
278  }
279  }
280 }
281 
282 /**************************************************************************/
303 EMSTATUS GLIB_drawLine(GLIB_Context_t *pContext, int32_t x1, int32_t y1,
304  int32_t x2, int32_t y2)
305 {
306  EMSTATUS status;
307  int32_t error;
308  int32_t deltaX;
309  int32_t deltaY;
310  int32_t yMotion;
311  int32_t xMotion;
312  bool steepLine = false;
313  int32_t yStep = 1;
314 
315  /* Check arguments */
316  if (pContext == NULL) return GLIB_ERROR_INVALID_ARGUMENT;
317 
318  /* Use simple algorithm for vertical line */
319  if (x1 == x2) return GLIB_drawLineV(pContext, x1, y1, y2);
320 
321  /* Use simple algorithm for horizontal line */
322  if (y1 == y2) return GLIB_drawLineH(pContext, x1, y1, x2);
323 
324  /* Clip the line against the clipping region */
325  if (!GLIB_clipLine(pContext, &x1, &y1, &x2, &y2)) return GLIB_ERROR_NOTHING_TO_DRAW;
326 
327  /* Determine if steep or not steep
328  * (Steep means more motion in Y-direction than X-direction) */
329  yMotion = (y2 > y1) ? (y2 - y1) : (y1 - y2);
330  xMotion = (x2 > x1) ? (x2 - x1) : (x1 - x2);
331  if (yMotion > xMotion) {
332  /* If line is steep, swap x and y values */
333  steepLine = true;
334 
335  error = x1;
336  x1 = y1;
337  y1 = error;
338 
339  error = x2;
340  x2 = y2;
341  y2 = error;
342  }
343 
344  /* Place the leftmost point in x1, y1 */
345  if (x2 < x1) {
346  /* Swap x-values */
347  error = x1;
348  x1 = x2;
349  x2 = error;
350 
351  /* Swap y-values */
352  error = y1;
353  y1 = y2;
354  y2 = error;
355  }
356 
357  /* Compute the differences between the points */
358  deltaX = x2 - x1;
359  deltaY = (y2 > y1) ? (y2 - y1) : (y1 - y2);
360 
361  /* Set error to negative half deltaX ? */
362  error = -deltaX / 2;
363 
364  /* Determine which direction to step in */
365  if (y2 < y1) yStep = -1;
366 
367  /* Loop through all points along the x-axis */
368  for (; x1 <= x2; x1++) {
369  if (steepLine) {
370  /* If steep, swap x and y coordinates */
371  status = GLIB_drawPixel(pContext, y1, x1);
372  } else {
373  status = GLIB_drawPixel(pContext, x1, y1);
374  }
375 
376  if (status != GLIB_OK) return status;
377 
378  error += deltaY;
379 
380  if (error > 0) {
381  y1 += yStep;
382  error -= deltaX;
383  }
384  }
385 
386  return GLIB_OK;
387 }
int32_t yMax
Definition: glib.h:266
int32_t yMin
Definition: glib.h:262
EMSTATUS GLIB_drawLineV(GLIB_Context_t *pContext, int32_t x1, int32_t y1, int32_t y2)
Draws a vertical line from x1, y1 to x1, y2.
Definition: glib_line.c:113
#define GLIB_ERROR_NOTHING_TO_DRAW
Definition: glib.h:192
#define GLIB_ERROR_INVALID_ARGUMENT
Definition: glib.h:200
uint32_t foregroundColor
Definition: glib.h:282
Silicon Labs Graphics Library.
void GLIB_colorTranslate24bpp(uint32_t color, uint8_t *red, uint8_t *green, uint8_t *blue)
Extracts the color components from the 32-bit color passed and puts them in the passed in 8-bits ints...
Definition: glib.c:309
#define GLIB_OK
Definition: glib.h:190
EMSTATUS GLIB_drawLineH(GLIB_Context_t *pContext, int32_t x1, int32_t y1, int32_t x2)
Draws a horizontal line from x1, y1 to x2, y2.
Definition: glib_line.c:49
EMSTATUS GLIB_applyClippingRegion(const GLIB_Context_t *pContext)
Apply the clipping region from the GLIB_Context_t in the DMD driver.
Definition: glib.c:278
EMSTATUS GLIB_drawPixel(GLIB_Context_t *pContext, int32_t x, int32_t y)
Draws a pixel at x, y using foregroundColor defined in the GLIB_Context_t.
Definition: glib.c:350
int32_t xMax
Definition: glib.h:264
static bool GLIB_clipLine(GLIB_Context_t *pContext, int32_t *pX1, int32_t *pY1, int32_t *pX2, int32_t *pY2)
Clips the line if necessary, using the Cohen-Sutherland algorithm for clipping lines. See Wikipedia for algorithm.
Definition: glib_line.c:217
EMSTATUS GLIB_drawLine(GLIB_Context_t *pContext, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
Draws a line from x1,y1 to x2, y2.
Definition: glib_line.c:303
GLIB_Rectangle_t clippingRegion
Definition: glib.h:285
int32_t xMin
Definition: glib.h:260
static uint8_t GLIB_getClipCode(GLIB_Context_t *pContext, int32_t x, int32_t y)
Gets the clip code for the point that is used by Cohen-Sutherland algorithm.
Definition: glib_line.c:175
GLIB Drawing Context (Multiple instances of GLIB_Context_t can exist)
Definition: glib.h:273