1 /*
2
3 SDL2_gfxPrimitives.c: graphics primitives for SDL2 renderers
4
5 Copyright (C) 2012-2014 Andreas Schiffler
6
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
10
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
14
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
19
20 2. Altered source versions must be plainly marked as such, and must not be
21 misrepresented as being the original software.
22
23 3. This notice may not be removed or altered from any source
24 distribution.
25
26 Andreas Schiffler -- aschiffler at ferzkopp dot net
27
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <math.h>
33 #include <string.h>
34
35 #include "SDL2_gfxPrimitives.h"
36 #include "SDL2_rotozoom.h"
37 #include "SDL2_gfxPrimitives_font.h"
38
39 /* ---- Structures */
40
41 /*!
42 \brief The structure passed to the internal Bresenham iterator.
43 */
44 typedef struct {
45 Sint16 x, y;
46 int dx, dy, s1, s2, swapdir, error;
47 Uint32 count;
48 } SDL2_gfxBresenhamIterator;
49
50 /*!
51 \brief The structure passed to the internal Murphy iterator.
52 */
53 typedef struct {
54 SDL_Renderer *renderer;
55 int u, v; /* delta x , delta y */
56 int ku, kt, kv, kd; /* loop constants */
57 int oct2;
58 int quad4;
59 Sint16 last1x, last1y, last2x, last2y, first1x, first1y, first2x, first2y, tempx, tempy;
60 } SDL2_gfxMurphyIterator;
61
62 /* ---- Pixel */
63
64 /*!
65 \brief Draw pixel in currently set color.
66
67 \param renderer The renderer to draw on.
68 \param x X (horizontal) coordinate of the pixel.
69 \param y Y (vertical) coordinate of the pixel.
70
71 \returns Returns 0 on success, -1 on failure.
72 */
pixel(SDL_Renderer * renderer,Sint16 x,Sint16 y)73 int pixel(SDL_Renderer *renderer, Sint16 x, Sint16 y)
74 {
75 return SDL_RenderDrawPoint(renderer, x, y);
76 }
77
78 /*!
79 \brief Draw pixel with blending enabled if a<255.
80
81 \param renderer The renderer to draw on.
82 \param x X (horizontal) coordinate of the pixel.
83 \param y Y (vertical) coordinate of the pixel.
84 \param color The color value of the pixel to draw (0xRRGGBBAA).
85
86 \returns Returns 0 on success, -1 on failure.
87 */
pixelColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Uint32 color)88 int pixelColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint32 color)
89 {
90 Uint8 *c = (Uint8 *)&color;
91 return pixelRGBA(renderer, x, y, c[0], c[1], c[2], c[3]);
92 }
93
94 /*!
95 \brief Draw pixel with blending enabled if a<255.
96
97 \param renderer The renderer to draw on.
98 \param x X (horizontal) coordinate of the pixel.
99 \param y Y (vertical) coordinate of the pixel.
100 \param r The red color value of the pixel to draw.
101 \param g The green color value of the pixel to draw.
102 \param b The blue color value of the pixel to draw.
103 \param a The alpha value of the pixel to draw.
104
105 \returns Returns 0 on success, -1 on failure.
106 */
pixelRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Uint8 r,Uint8 g,Uint8 b,Uint8 a)107 int pixelRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
108 {
109 int result = 0;
110 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
111 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
112 result |= SDL_RenderDrawPoint(renderer, x, y);
113 return result;
114 }
115
116 /*!
117 \brief Draw pixel with blending enabled and using alpha weight on color.
118
119 \param renderer The renderer to draw on.
120 \param x The horizontal coordinate of the pixel.
121 \param y The vertical position of the pixel.
122 \param r The red color value of the pixel to draw.
123 \param g The green color value of the pixel to draw.
124 \param b The blue color value of the pixel to draw.
125 \param a The alpha value of the pixel to draw.
126 \param weight The weight multiplied into the alpha value of the pixel.
127
128 \returns Returns 0 on success, -1 on failure.
129 */
pixelRGBAWeight(SDL_Renderer * renderer,Sint16 x,Sint16 y,Uint8 r,Uint8 g,Uint8 b,Uint8 a,Uint32 weight)130 int pixelRGBAWeight(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint32 weight)
131 {
132 /*
133 * Modify Alpha by weight
134 */
135 Uint32 ax = a;
136 ax = ((ax * weight) >> 8);
137 if (ax > 255) {
138 a = 255;
139 } else {
140 a = (Uint8)(ax & 0x000000ff);
141 }
142
143 return pixelRGBA(renderer, x, y, r, g, b, a);
144 }
145
146 /* ---- Hline */
147
148 /*!
149 \brief Draw horizontal line in currently set color
150
151 \param renderer The renderer to draw on.
152 \param x1 X coordinate of the first point (i.e. left) of the line.
153 \param x2 X coordinate of the second point (i.e. right) of the line.
154 \param y Y coordinate of the points of the line.
155
156 \returns Returns 0 on success, -1 on failure.
157 */
hline(SDL_Renderer * renderer,Sint16 x1,Sint16 x2,Sint16 y)158 int hline(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y)
159 {
160 return SDL_RenderDrawLine(renderer, x1, y, x2, y);;
161 }
162
163
164 /*!
165 \brief Draw horizontal line with blending.
166
167 \param renderer The renderer to draw on.
168 \param x1 X coordinate of the first point (i.e. left) of the line.
169 \param x2 X coordinate of the second point (i.e. right) of the line.
170 \param y Y coordinate of the points of the line.
171 \param color The color value of the line to draw (0xRRGGBBAA).
172
173 \returns Returns 0 on success, -1 on failure.
174 */
hlineColor(SDL_Renderer * renderer,Sint16 x1,Sint16 x2,Sint16 y,Uint32 color)175 int hlineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color)
176 {
177 Uint8 *c = (Uint8 *)&color;
178 return hlineRGBA(renderer, x1, x2, y, c[0], c[1], c[2], c[3]);
179 }
180
181 /*!
182 \brief Draw horizontal line with blending.
183
184 \param renderer The renderer to draw on.
185 \param x1 X coordinate of the first point (i.e. left) of the line.
186 \param x2 X coordinate of the second point (i.e. right) of the line.
187 \param y Y coordinate of the points of the line.
188 \param r The red value of the line to draw.
189 \param g The green value of the line to draw.
190 \param b The blue value of the line to draw.
191 \param a The alpha value of the line to draw.
192
193 \returns Returns 0 on success, -1 on failure.
194 */
hlineRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 x2,Sint16 y,Uint8 r,Uint8 g,Uint8 b,Uint8 a)195 int hlineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
196 {
197 int result = 0;
198 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
199 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
200 result |= SDL_RenderDrawLine(renderer, x1, y, x2, y);
201 return result;
202 }
203
204 /* ---- Vline */
205
206 /*!
207 \brief Draw vertical line in currently set color
208
209 \param renderer The renderer to draw on.
210 \param x X coordinate of points of the line.
211 \param y1 Y coordinate of the first point (i.e. top) of the line.
212 \param y2 Y coordinate of the second point (i.e. bottom) of the line.
213
214 \returns Returns 0 on success, -1 on failure.
215 */
vline(SDL_Renderer * renderer,Sint16 x,Sint16 y1,Sint16 y2)216 int vline(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2)
217 {
218 return SDL_RenderDrawLine(renderer, x, y1, x, y2);;
219 }
220
221 /*!
222 \brief Draw vertical line with blending.
223
224 \param renderer The renderer to draw on.
225 \param x X coordinate of the points of the line.
226 \param y1 Y coordinate of the first point (i.e. top) of the line.
227 \param y2 Y coordinate of the second point (i.e. bottom) of the line.
228 \param color The color value of the line to draw (0xRRGGBBAA).
229
230 \returns Returns 0 on success, -1 on failure.
231 */
vlineColor(SDL_Renderer * renderer,Sint16 x,Sint16 y1,Sint16 y2,Uint32 color)232 int vlineColor(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color)
233 {
234 Uint8 *c = (Uint8 *)&color;
235 return vlineRGBA(renderer, x, y1, y2, c[0], c[1], c[2], c[3]);
236 }
237
238 /*!
239 \brief Draw vertical line with blending.
240
241 \param renderer The renderer to draw on.
242 \param x X coordinate of the points of the line.
243 \param y1 Y coordinate of the first point (i.e. top) of the line.
244 \param y2 Y coordinate of the second point (i.e. bottom) of the line.
245 \param r The red value of the line to draw.
246 \param g The green value of the line to draw.
247 \param b The blue value of the line to draw.
248 \param a The alpha value of the line to draw.
249
250 \returns Returns 0 on success, -1 on failure.
251 */
vlineRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y1,Sint16 y2,Uint8 r,Uint8 g,Uint8 b,Uint8 a)252 int vlineRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
253 {
254 int result = 0;
255 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
256 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
257 result |= SDL_RenderDrawLine(renderer, x, y1, x, y2);
258 return result;
259 }
260
261 /* ---- Rectangle */
262
263 /*!
264 \brief Draw rectangle with blending.
265
266 \param renderer The renderer to draw on.
267 \param x1 X coordinate of the first point (i.e. top right) of the rectangle.
268 \param y1 Y coordinate of the first point (i.e. top right) of the rectangle.
269 \param x2 X coordinate of the second point (i.e. bottom left) of the rectangle.
270 \param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle.
271 \param color The color value of the rectangle to draw (0xRRGGBBAA).
272
273 \returns Returns 0 on success, -1 on failure.
274 */
rectangleColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color)275 int rectangleColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color)
276 {
277 Uint8 *c = (Uint8 *)&color;
278 return rectangleRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]);
279 }
280
281 /*!
282 \brief Draw rectangle with blending.
283
284 \param renderer The renderer to draw on.
285 \param x1 X coordinate of the first point (i.e. top right) of the rectangle.
286 \param y1 Y coordinate of the first point (i.e. top right) of the rectangle.
287 \param x2 X coordinate of the second point (i.e. bottom left) of the rectangle.
288 \param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle.
289 \param r The red value of the rectangle to draw.
290 \param g The green value of the rectangle to draw.
291 \param b The blue value of the rectangle to draw.
292 \param a The alpha value of the rectangle to draw.
293
294 \returns Returns 0 on success, -1 on failure.
295 */
rectangleRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 r,Uint8 g,Uint8 b,Uint8 a)296 int rectangleRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
297 {
298 int result;
299 Sint16 tmp;
300 SDL_Rect rect;
301
302 /*
303 * Test for special cases of straight lines or single point
304 */
305 if (x1 == x2) {
306 if (y1 == y2) {
307 return (pixelRGBA(renderer, x1, y1, r, g, b, a));
308 } else {
309 return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a));
310 }
311 } else {
312 if (y1 == y2) {
313 return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a));
314 }
315 }
316
317 /*
318 * Swap x1, x2 if required
319 */
320 if (x1 > x2) {
321 tmp = x1;
322 x1 = x2;
323 x2 = tmp;
324 }
325
326 /*
327 * Swap y1, y2 if required
328 */
329 if (y1 > y2) {
330 tmp = y1;
331 y1 = y2;
332 y2 = tmp;
333 }
334
335 /*
336 * Create destination rect
337 */
338 rect.x = x1;
339 rect.y = y1;
340 rect.w = x2 - x1;
341 rect.h = y2 - y1;
342
343 /*
344 * Draw
345 */
346 result = 0;
347 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
348 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
349 result |= SDL_RenderDrawRect(renderer, &rect);
350 return result;
351 }
352
353 /* ---- Rounded Rectangle */
354
355 /*!
356 \brief Draw rounded-corner rectangle with blending.
357
358 \param renderer The renderer to draw on.
359 \param x1 X coordinate of the first point (i.e. top right) of the rectangle.
360 \param y1 Y coordinate of the first point (i.e. top right) of the rectangle.
361 \param x2 X coordinate of the second point (i.e. bottom left) of the rectangle.
362 \param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle.
363 \param rad The radius of the corner arc.
364 \param color The color value of the rectangle to draw (0xRRGGBBAA).
365
366 \returns Returns 0 on success, -1 on failure.
367 */
roundedRectangleColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 rad,Uint32 color)368 int roundedRectangleColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint32 color)
369 {
370 Uint8 *c = (Uint8 *)&color;
371 return roundedRectangleRGBA(renderer, x1, y1, x2, y2, rad, c[0], c[1], c[2], c[3]);
372 }
373
374 /*!
375 \brief Draw rounded-corner rectangle with blending.
376
377 \param renderer The renderer to draw on.
378 \param x1 X coordinate of the first point (i.e. top right) of the rectangle.
379 \param y1 Y coordinate of the first point (i.e. top right) of the rectangle.
380 \param x2 X coordinate of the second point (i.e. bottom left) of the rectangle.
381 \param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle.
382 \param rad The radius of the corner arc.
383 \param r The red value of the rectangle to draw.
384 \param g The green value of the rectangle to draw.
385 \param b The blue value of the rectangle to draw.
386 \param a The alpha value of the rectangle to draw.
387
388 \returns Returns 0 on success, -1 on failure.
389 */
roundedRectangleRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 rad,Uint8 r,Uint8 g,Uint8 b,Uint8 a)390 int roundedRectangleRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
391 {
392 int result = 0;
393 Sint16 tmp;
394 Sint16 w, h;
395 Sint16 xx1, xx2;
396 Sint16 yy1, yy2;
397
398 /*
399 * Check renderer
400 */
401 if (renderer == NULL)
402 {
403 return -1;
404 }
405
406 /*
407 * Check radius vor valid range
408 */
409 if (rad < 0) {
410 return -1;
411 }
412
413 /*
414 * Special case - no rounding
415 */
416 if (rad <= 1) {
417 return rectangleRGBA(renderer, x1, y1, x2, y2, r, g, b, a);
418 }
419
420 /*
421 * Test for special cases of straight lines or single point
422 */
423 if (x1 == x2) {
424 if (y1 == y2) {
425 return (pixelRGBA(renderer, x1, y1, r, g, b, a));
426 } else {
427 return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a));
428 }
429 } else {
430 if (y1 == y2) {
431 return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a));
432 }
433 }
434
435 /*
436 * Swap x1, x2 if required
437 */
438 if (x1 > x2) {
439 tmp = x1;
440 x1 = x2;
441 x2 = tmp;
442 }
443
444 /*
445 * Swap y1, y2 if required
446 */
447 if (y1 > y2) {
448 tmp = y1;
449 y1 = y2;
450 y2 = tmp;
451 }
452
453 /*
454 * Calculate width&height
455 */
456 w = x2 - x1;
457 h = y2 - y1;
458
459 /*
460 * Maybe adjust radius
461 */
462 if ((rad * 2) > w)
463 {
464 rad = w / 2;
465 }
466 if ((rad * 2) > h)
467 {
468 rad = h / 2;
469 }
470
471 /*
472 * Draw corners
473 */
474 xx1 = x1 + rad;
475 xx2 = x2 - rad;
476 yy1 = y1 + rad;
477 yy2 = y2 - rad;
478 result |= arcRGBA(renderer, xx1, yy1, rad, 180, 270, r, g, b, a);
479 result |= arcRGBA(renderer, xx2, yy1, rad, 270, 360, r, g, b, a);
480 result |= arcRGBA(renderer, xx1, yy2, rad, 90, 180, r, g, b, a);
481 result |= arcRGBA(renderer, xx2, yy2, rad, 0, 90, r, g, b, a);
482
483 /*
484 * Draw lines
485 */
486 if (xx1 <= xx2) {
487 result |= hlineRGBA(renderer, xx1, xx2, y1, r, g, b, a);
488 result |= hlineRGBA(renderer, xx1, xx2, y2, r, g, b, a);
489 }
490 if (yy1 <= yy2) {
491 result |= vlineRGBA(renderer, x1, yy1, yy2, r, g, b, a);
492 result |= vlineRGBA(renderer, x2, yy1, yy2, r, g, b, a);
493 }
494
495 return result;
496 }
497
498 /* ---- Rounded Box */
499
500 /*!
501 \brief Draw rounded-corner box (filled rectangle) with blending.
502
503 \param renderer The renderer to draw on.
504 \param x1 X coordinate of the first point (i.e. top right) of the box.
505 \param y1 Y coordinate of the first point (i.e. top right) of the box.
506 \param x2 X coordinate of the second point (i.e. bottom left) of the box.
507 \param y2 Y coordinate of the second point (i.e. bottom left) of the box.
508 \param rad The radius of the corner arcs of the box.
509 \param color The color value of the box to draw (0xRRGGBBAA).
510
511 \returns Returns 0 on success, -1 on failure.
512 */
roundedBoxColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 rad,Uint32 color)513 int roundedBoxColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint32 color)
514 {
515 Uint8 *c = (Uint8 *)&color;
516 return roundedBoxRGBA(renderer, x1, y1, x2, y2, rad, c[0], c[1], c[2], c[3]);
517 }
518
519 /*!
520 \brief Draw rounded-corner box (filled rectangle) with blending.
521
522 \param renderer The renderer to draw on.
523 \param x1 X coordinate of the first point (i.e. top right) of the box.
524 \param y1 Y coordinate of the first point (i.e. top right) of the box.
525 \param x2 X coordinate of the second point (i.e. bottom left) of the box.
526 \param y2 Y coordinate of the second point (i.e. bottom left) of the box.
527 \param rad The radius of the corner arcs of the box.
528 \param r The red value of the box to draw.
529 \param g The green value of the box to draw.
530 \param b The blue value of the box to draw.
531 \param a The alpha value of the box to draw.
532
533 \returns Returns 0 on success, -1 on failure.
534 */
roundedBoxRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 rad,Uint8 r,Uint8 g,Uint8 b,Uint8 a)535 int roundedBoxRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2,
536 Sint16 y2, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
537 {
538 int result;
539 Sint16 w, h, r2, tmp;
540 Sint16 cx = 0;
541 Sint16 cy = rad;
542 Sint16 ocx = (Sint16) 0xffff;
543 Sint16 ocy = (Sint16) 0xffff;
544 Sint16 df = 1 - rad;
545 Sint16 d_e = 3;
546 Sint16 d_se = -2 * rad + 5;
547 Sint16 xpcx, xmcx, xpcy, xmcy;
548 Sint16 ypcy, ymcy, ypcx, ymcx;
549 Sint16 x, y, dx, dy;
550
551 /*
552 * Check destination renderer
553 */
554 if (renderer == NULL)
555 {
556 return -1;
557 }
558
559 /*
560 * Check radius vor valid range
561 */
562 if (rad < 0) {
563 return -1;
564 }
565
566 /*
567 * Special case - no rounding
568 */
569 if (rad <= 1) {
570 return boxRGBA(renderer, x1, y1, x2, y2, r, g, b, a);
571 }
572
573 /*
574 * Test for special cases of straight lines or single point
575 */
576 if (x1 == x2) {
577 if (y1 == y2) {
578 return (pixelRGBA(renderer, x1, y1, r, g, b, a));
579 } else {
580 return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a));
581 }
582 } else {
583 if (y1 == y2) {
584 return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a));
585 }
586 }
587
588 /*
589 * Swap x1, x2 if required
590 */
591 if (x1 > x2) {
592 tmp = x1;
593 x1 = x2;
594 x2 = tmp;
595 }
596
597 /*
598 * Swap y1, y2 if required
599 */
600 if (y1 > y2) {
601 tmp = y1;
602 y1 = y2;
603 y2 = tmp;
604 }
605
606 /*
607 * Calculate width&height
608 */
609 w = x2 - x1 + 1;
610 h = y2 - y1 + 1;
611
612 /*
613 * Maybe adjust radius
614 */
615 r2 = rad + rad;
616 if (r2 > w)
617 {
618 rad = w / 2;
619 r2 = rad + rad;
620 }
621 if (r2 > h)
622 {
623 rad = h / 2;
624 }
625
626 /* Setup filled circle drawing for corners */
627 x = x1 + rad;
628 y = y1 + rad;
629 dx = x2 - x1 - rad - rad;
630 dy = y2 - y1 - rad - rad;
631
632 /*
633 * Set color
634 */
635 result = 0;
636 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
637 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
638
639 /*
640 * Draw corners
641 */
642 do {
643 xpcx = x + cx;
644 xmcx = x - cx;
645 xpcy = x + cy;
646 xmcy = x - cy;
647 if (ocy != cy) {
648 if (cy > 0) {
649 ypcy = y + cy;
650 ymcy = y - cy;
651 result |= hline(renderer, xmcx, xpcx + dx, ypcy + dy);
652 result |= hline(renderer, xmcx, xpcx + dx, ymcy);
653 } else {
654 result |= hline(renderer, xmcx, xpcx + dx, y);
655 }
656 ocy = cy;
657 }
658 if (ocx != cx) {
659 if (cx != cy) {
660 if (cx > 0) {
661 ypcx = y + cx;
662 ymcx = y - cx;
663 result |= hline(renderer, xmcy, xpcy + dx, ymcx);
664 result |= hline(renderer, xmcy, xpcy + dx, ypcx + dy);
665 } else {
666 result |= hline(renderer, xmcy, xpcy + dx, y);
667 }
668 }
669 ocx = cx;
670 }
671
672 /*
673 * Update
674 */
675 if (df < 0) {
676 df += d_e;
677 d_e += 2;
678 d_se += 2;
679 } else {
680 df += d_se;
681 d_e += 2;
682 d_se += 4;
683 cy--;
684 }
685 cx++;
686 } while (cx <= cy);
687
688 /* Inside */
689 if (dx > 0 && dy > 0) {
690 result |= boxRGBA(renderer, x1, y1 + rad + 1, x2, y2 - rad, r, g, b, a);
691 }
692
693 return (result);
694 }
695
696 /* ---- Box */
697
698 /*!
699 \brief Draw box (filled rectangle) with blending.
700
701 \param renderer The renderer to draw on.
702 \param x1 X coordinate of the first point (i.e. top right) of the box.
703 \param y1 Y coordinate of the first point (i.e. top right) of the box.
704 \param x2 X coordinate of the second point (i.e. bottom left) of the box.
705 \param y2 Y coordinate of the second point (i.e. bottom left) of the box.
706 \param color The color value of the box to draw (0xRRGGBBAA).
707
708 \returns Returns 0 on success, -1 on failure.
709 */
boxColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color)710 int boxColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color)
711 {
712 Uint8 *c = (Uint8 *)&color;
713 return boxRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]);
714 }
715
716 /*!
717 \brief Draw box (filled rectangle) with blending.
718
719 \param renderer The renderer to draw on.
720 \param x1 X coordinate of the first point (i.e. top right) of the box.
721 \param y1 Y coordinate of the first point (i.e. top right) of the box.
722 \param x2 X coordinate of the second point (i.e. bottom left) of the box.
723 \param y2 Y coordinate of the second point (i.e. bottom left) of the box.
724 \param r The red value of the box to draw.
725 \param g The green value of the box to draw.
726 \param b The blue value of the box to draw.
727 \param a The alpha value of the box to draw.
728
729 \returns Returns 0 on success, -1 on failure.
730 */
boxRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 r,Uint8 g,Uint8 b,Uint8 a)731 int boxRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
732 {
733 int result;
734 Sint16 tmp;
735 SDL_Rect rect;
736
737 /*
738 * Test for special cases of straight lines or single point
739 */
740 if (x1 == x2) {
741 if (y1 == y2) {
742 return (pixelRGBA(renderer, x1, y1, r, g, b, a));
743 } else {
744 return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a));
745 }
746 } else {
747 if (y1 == y2) {
748 return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a));
749 }
750 }
751
752 /*
753 * Swap x1, x2 if required
754 */
755 if (x1 > x2) {
756 tmp = x1;
757 x1 = x2;
758 x2 = tmp;
759 }
760
761 /*
762 * Swap y1, y2 if required
763 */
764 if (y1 > y2) {
765 tmp = y1;
766 y1 = y2;
767 y2 = tmp;
768 }
769
770 /*
771 * Create destination rect
772 */
773 rect.x = x1;
774 rect.y = y1;
775 rect.w = x2 - x1 + 1;
776 rect.h = y2 - y1 + 1;
777
778 /*
779 * Draw
780 */
781 result = 0;
782 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
783 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
784 result |= SDL_RenderFillRect(renderer, &rect);
785 return result;
786 }
787
788 /* ----- Line */
789
790 /*!
791 \brief Draw line with alpha blending using the currently set color.
792
793 \param renderer The renderer to draw on.
794 \param x1 X coordinate of the first point of the line.
795 \param y1 Y coordinate of the first point of the line.
796 \param x2 X coordinate of the second point of the line.
797 \param y2 Y coordinate of the second point of the line.
798
799 \returns Returns 0 on success, -1 on failure.
800 */
line(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2)801 int line(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2)
802 {
803 /*
804 * Draw
805 */
806 return SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
807 }
808
809 /*!
810 \brief Draw line with alpha blending.
811
812 \param renderer The renderer to draw on.
813 \param x1 X coordinate of the first point of the line.
814 \param y1 Y coordinate of the first point of the line.
815 \param x2 X coordinate of the second point of the line.
816 \param y2 Y coordinate of the seond point of the line.
817 \param color The color value of the line to draw (0xRRGGBBAA).
818
819 \returns Returns 0 on success, -1 on failure.
820 */
lineColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color)821 int lineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color)
822 {
823 Uint8 *c = (Uint8 *)&color;
824 return lineRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]);
825 }
826
827 /*!
828 \brief Draw line with alpha blending.
829
830 \param renderer The renderer to draw on.
831 \param x1 X coordinate of the first point of the line.
832 \param y1 Y coordinate of the first point of the line.
833 \param x2 X coordinate of the second point of the line.
834 \param y2 Y coordinate of the second point of the line.
835 \param r The red value of the line to draw.
836 \param g The green value of the line to draw.
837 \param b The blue value of the line to draw.
838 \param a The alpha value of the line to draw.
839
840 \returns Returns 0 on success, -1 on failure.
841 */
lineRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 r,Uint8 g,Uint8 b,Uint8 a)842 int lineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
843 {
844 /*
845 * Draw
846 */
847 int result = 0;
848 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
849 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
850 result |= SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
851 return result;
852 }
853
854 /* ---- AA Line */
855
856 #define AAlevels 256
857 #define AAbits 8
858
859 /*!
860 \brief Internal function to draw anti-aliased line with alpha blending and endpoint control.
861
862 This implementation of the Wu antialiasing code is based on Mike Abrash's
863 DDJ article which was reprinted as Chapter 42 of his Graphics Programming
864 Black Book, but has been optimized to work with SDL and utilizes 32-bit
865 fixed-point arithmetic by A. Schiffler. The endpoint control allows the
866 supression to draw the last pixel useful for rendering continous aa-lines
867 with alpha<255.
868
869 \param renderer The renderer to draw on.
870 \param x1 X coordinate of the first point of the aa-line.
871 \param y1 Y coordinate of the first point of the aa-line.
872 \param x2 X coordinate of the second point of the aa-line.
873 \param y2 Y coordinate of the second point of the aa-line.
874 \param r The red value of the aa-line to draw.
875 \param g The green value of the aa-line to draw.
876 \param b The blue value of the aa-line to draw.
877 \param a The alpha value of the aa-line to draw.
878 \param draw_endpoint Flag indicating if the endpoint should be drawn; draw if non-zero.
879
880 \returns Returns 0 on success, -1 on failure.
881 */
_aalineRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 r,Uint8 g,Uint8 b,Uint8 a,int draw_endpoint)882 int _aalineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a, int draw_endpoint)
883 {
884 Sint32 xx0, yy0, xx1, yy1;
885 int result;
886 Uint32 intshift, erracc, erradj;
887 Uint32 erracctmp, wgt, wgtcompmask;
888 int dx, dy, tmp, xdir, y0p1, x0pxdir;
889
890 /*
891 * Keep on working with 32bit numbers
892 */
893 xx0 = x1;
894 yy0 = y1;
895 xx1 = x2;
896 yy1 = y2;
897
898 /*
899 * Reorder points to make dy positive
900 */
901 if (yy0 > yy1) {
902 tmp = yy0;
903 yy0 = yy1;
904 yy1 = tmp;
905 tmp = xx0;
906 xx0 = xx1;
907 xx1 = tmp;
908 }
909
910 /*
911 * Calculate distance
912 */
913 dx = xx1 - xx0;
914 dy = yy1 - yy0;
915
916 /*
917 * Adjust for negative dx and set xdir
918 */
919 if (dx >= 0) {
920 xdir = 1;
921 } else {
922 xdir = -1;
923 dx = (-dx);
924 }
925
926 /*
927 * Check for special cases
928 */
929 if (dx == 0) {
930 /*
931 * Vertical line
932 */
933 if (draw_endpoint)
934 {
935 return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a));
936 } else {
937 if (dy > 0) {
938 return (vlineRGBA(renderer, x1, yy0, yy0+dy, r, g, b, a));
939 } else {
940 return (pixelRGBA(renderer, x1, y1, r, g, b, a));
941 }
942 }
943 } else if (dy == 0) {
944 /*
945 * Horizontal line
946 */
947 if (draw_endpoint)
948 {
949 return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a));
950 } else {
951 if (dx > 0) {
952 return (hlineRGBA(renderer, xx0, xx0+(xdir*dx), y1, r, g, b, a));
953 } else {
954 return (pixelRGBA(renderer, x1, y1, r, g, b, a));
955 }
956 }
957 } else if ((dx == dy) && (draw_endpoint)) {
958 /*
959 * Diagonal line (with endpoint)
960 */
961 return (lineRGBA(renderer, x1, y1, x2, y2, r, g, b, a));
962 }
963
964
965 /*
966 * Line is not horizontal, vertical or diagonal (with endpoint)
967 */
968 result = 0;
969
970 /*
971 * Zero accumulator
972 */
973 erracc = 0;
974
975 /*
976 * # of bits by which to shift erracc to get intensity level
977 */
978 intshift = 32 - AAbits;
979
980 /*
981 * Mask used to flip all bits in an intensity weighting
982 */
983 wgtcompmask = AAlevels - 1;
984
985 /*
986 * Draw the initial pixel in the foreground color
987 */
988 result |= pixelRGBA(renderer, x1, y1, r, g, b, a);
989
990 /*
991 * x-major or y-major?
992 */
993 if (dy > dx) {
994
995 /*
996 * y-major. Calculate 16-bit fixed point fractional part of a pixel that
997 * X advances every time Y advances 1 pixel, truncating the result so that
998 * we won't overrun the endpoint along the X axis
999 */
1000 /*
1001 * Not-so-portable version: erradj = ((Uint64)dx << 32) / (Uint64)dy;
1002 */
1003 erradj = ((dx << 16) / dy) << 16;
1004
1005 /*
1006 * draw all pixels other than the first and last
1007 */
1008 x0pxdir = xx0 + xdir;
1009 while (--dy) {
1010 erracctmp = erracc;
1011 erracc += erradj;
1012 if (erracc <= erracctmp) {
1013 /*
1014 * rollover in error accumulator, x coord advances
1015 */
1016 xx0 = x0pxdir;
1017 x0pxdir += xdir;
1018 }
1019 yy0++; /* y-major so always advance Y */
1020
1021 /*
1022 * the AAbits most significant bits of erracc give us the intensity
1023 * weighting for this pixel, and the complement of the weighting for
1024 * the paired pixel.
1025 */
1026 wgt = (erracc >> intshift) & 255;
1027 result |= pixelRGBAWeight (renderer, xx0, yy0, r, g, b, a, 255 - wgt);
1028 result |= pixelRGBAWeight (renderer, x0pxdir, yy0, r, g, b, a, wgt);
1029 }
1030
1031 } else {
1032
1033 /*
1034 * x-major line. Calculate 16-bit fixed-point fractional part of a pixel
1035 * that Y advances each time X advances 1 pixel, truncating the result so
1036 * that we won't overrun the endpoint along the X axis.
1037 */
1038 /*
1039 * Not-so-portable version: erradj = ((Uint64)dy << 32) / (Uint64)dx;
1040 */
1041 erradj = ((dy << 16) / dx) << 16;
1042
1043 /*
1044 * draw all pixels other than the first and last
1045 */
1046 y0p1 = yy0 + 1;
1047 while (--dx) {
1048
1049 erracctmp = erracc;
1050 erracc += erradj;
1051 if (erracc <= erracctmp) {
1052 /*
1053 * Accumulator turned over, advance y
1054 */
1055 yy0 = y0p1;
1056 y0p1++;
1057 }
1058 xx0 += xdir; /* x-major so always advance X */
1059 /*
1060 * the AAbits most significant bits of erracc give us the intensity
1061 * weighting for this pixel, and the complement of the weighting for
1062 * the paired pixel.
1063 */
1064 wgt = (erracc >> intshift) & 255;
1065 result |= pixelRGBAWeight (renderer, xx0, yy0, r, g, b, a, 255 - wgt);
1066 result |= pixelRGBAWeight (renderer, xx0, y0p1, r, g, b, a, wgt);
1067 }
1068 }
1069
1070 /*
1071 * Do we have to draw the endpoint
1072 */
1073 if (draw_endpoint) {
1074 /*
1075 * Draw final pixel, always exactly intersected by the line and doesn't
1076 * need to be weighted.
1077 */
1078 result |= pixelRGBA (renderer, x2, y2, r, g, b, a);
1079 }
1080
1081 return (result);
1082 }
1083
1084 /*!
1085 \brief Draw anti-aliased line with alpha blending.
1086
1087 \param renderer The renderer to draw on.
1088 \param x1 X coordinate of the first point of the aa-line.
1089 \param y1 Y coordinate of the first point of the aa-line.
1090 \param x2 X coordinate of the second point of the aa-line.
1091 \param y2 Y coordinate of the second point of the aa-line.
1092 \param color The color value of the aa-line to draw (0xRRGGBBAA).
1093
1094 \returns Returns 0 on success, -1 on failure.
1095 */
aalineColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint32 color)1096 int aalineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color)
1097 {
1098 Uint8 *c = (Uint8 *)&color;
1099 return _aalineRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3], 1);
1100 }
1101
1102 /*!
1103 \brief Draw anti-aliased line with alpha blending.
1104
1105 \param renderer The renderer to draw on.
1106 \param x1 X coordinate of the first point of the aa-line.
1107 \param y1 Y coordinate of the first point of the aa-line.
1108 \param x2 X coordinate of the second point of the aa-line.
1109 \param y2 Y coordinate of the second point of the aa-line.
1110 \param r The red value of the aa-line to draw.
1111 \param g The green value of the aa-line to draw.
1112 \param b The blue value of the aa-line to draw.
1113 \param a The alpha value of the aa-line to draw.
1114
1115 \returns Returns 0 on success, -1 on failure.
1116 */
aalineRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1117 int aalineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1118 {
1119 return _aalineRGBA(renderer, x1, y1, x2, y2, r, g, b, a, 1);
1120 }
1121
1122 /* ----- Circle */
1123
1124 /*!
1125 \brief Draw circle with blending.
1126
1127 \param renderer The renderer to draw on.
1128 \param x X coordinate of the center of the circle.
1129 \param y Y coordinate of the center of the circle.
1130 \param rad Radius in pixels of the circle.
1131 \param color The color value of the circle to draw (0xRRGGBBAA).
1132
1133 \returns Returns 0 on success, -1 on failure.
1134 */
circleColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Uint32 color)1135 int circleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color)
1136 {
1137 Uint8 *c = (Uint8 *)&color;
1138 return ellipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]);
1139 }
1140
1141 /*!
1142 \brief Draw circle with blending.
1143
1144 \param renderer The renderer to draw on.
1145 \param x X coordinate of the center of the circle.
1146 \param y Y coordinate of the center of the circle.
1147 \param rad Radius in pixels of the circle.
1148 \param r The red value of the circle to draw.
1149 \param g The green value of the circle to draw.
1150 \param b The blue value of the circle to draw.
1151 \param a The alpha value of the circle to draw.
1152
1153 \returns Returns 0 on success, -1 on failure.
1154 */
circleRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1155 int circleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1156 {
1157 return ellipseRGBA(renderer, x, y, rad, rad, r, g, b, a);
1158 }
1159
1160 /* ----- Arc */
1161
1162 /*!
1163 \brief Arc with blending.
1164
1165 \param renderer The renderer to draw on.
1166 \param x X coordinate of the center of the arc.
1167 \param y Y coordinate of the center of the arc.
1168 \param rad Radius in pixels of the arc.
1169 \param start Starting radius in degrees of the arc. 0 degrees is down, increasing counterclockwise.
1170 \param end Ending radius in degrees of the arc. 0 degrees is down, increasing counterclockwise.
1171 \param color The color value of the arc to draw (0xRRGGBBAA).
1172
1173 \returns Returns 0 on success, -1 on failure.
1174 */
arcColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint32 color)1175 int arcColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color)
1176 {
1177 Uint8 *c = (Uint8 *)&color;
1178 return arcRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3]);
1179 }
1180
1181 /*!
1182 \brief Arc with blending.
1183
1184 \param renderer The renderer to draw on.
1185 \param x X coordinate of the center of the arc.
1186 \param y Y coordinate of the center of the arc.
1187 \param rad Radius in pixels of the arc.
1188 \param start Starting radius in degrees of the arc. 0 degrees is down, increasing counterclockwise.
1189 \param end Ending radius in degrees of the arc. 0 degrees is down, increasing counterclockwise.
1190 \param r The red value of the arc to draw.
1191 \param g The green value of the arc to draw.
1192 \param b The blue value of the arc to draw.
1193 \param a The alpha value of the arc to draw.
1194
1195 \returns Returns 0 on success, -1 on failure.
1196 */
1197 /* TODO: rewrite algorithm; arc endpoints are not always drawn */
arcRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1198 int arcRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1199 {
1200 int result;
1201 Sint16 cx = 0;
1202 Sint16 cy = rad;
1203 Sint16 df = 1 - rad;
1204 Sint16 d_e = 3;
1205 Sint16 d_se = -2 * rad + 5;
1206 Sint16 xpcx, xmcx, xpcy, xmcy;
1207 Sint16 ypcy, ymcy, ypcx, ymcx;
1208 Uint8 drawoct;
1209 int startoct, endoct, oct, stopval_start = 0, stopval_end = 0;
1210 double dstart, dend, temp = 0.;
1211
1212 /*
1213 * Sanity check radius
1214 */
1215 if (rad < 0) {
1216 return (-1);
1217 }
1218
1219 /*
1220 * Special case for rad=0 - draw a point
1221 */
1222 if (rad == 0) {
1223 return (pixelRGBA(renderer, x, y, r, g, b, a));
1224 }
1225
1226 /*
1227 Octant labeling
1228
1229 \ 5 | 6 /
1230 \ | /
1231 4 \ | / 7
1232 \|/
1233 ------+------ +x
1234 /|\
1235 3 / | \ 0
1236 / | \
1237 / 2 | 1 \
1238 +y
1239
1240 Initially reset bitmask to 0x00000000
1241 the set whether or not to keep drawing a given octant.
1242 For example: 0x00111100 means we're drawing in octants 2-5
1243 */
1244 drawoct = 0;
1245
1246 /*
1247 * Fixup angles
1248 */
1249 start %= 360;
1250 end %= 360;
1251 /* 0 <= start & end < 360; note that sometimes start > end - if so, arc goes back through 0. */
1252 while (start < 0) start += 360;
1253 while (end < 0) end += 360;
1254 start %= 360;
1255 end %= 360;
1256
1257 /* now, we find which octants we're drawing in. */
1258 startoct = start / 45;
1259 endoct = end / 45;
1260 oct = startoct - 1;
1261
1262 /* stopval_start, stopval_end; what values of cx to stop at. */
1263 do {
1264 oct = (oct + 1) % 8;
1265
1266 if (oct == startoct) {
1267 /* need to compute stopval_start for this octant. Look at picture above if this is unclear */
1268 dstart = (double)start;
1269 switch (oct)
1270 {
1271 case 0:
1272 case 3:
1273 temp = sin(dstart * M_PI / 180.);
1274 break;
1275 case 1:
1276 case 6:
1277 temp = cos(dstart * M_PI / 180.);
1278 break;
1279 case 2:
1280 case 5:
1281 temp = -cos(dstart * M_PI / 180.);
1282 break;
1283 case 4:
1284 case 7:
1285 temp = -sin(dstart * M_PI / 180.);
1286 break;
1287 }
1288 temp *= rad;
1289 stopval_start = (int)temp;
1290
1291 /*
1292 This isn't arbitrary, but requires graph paper to explain well.
1293 The basic idea is that we're always changing drawoct after we draw, so we
1294 stop immediately after we render the last sensible pixel at x = ((int)temp).
1295 and whether to draw in this octant initially
1296 */
1297 if (oct % 2) drawoct |= (1 << oct); /* this is basically like saying drawoct[oct] = true, if drawoct were a bool array */
1298 else drawoct &= 255 - (1 << oct); /* this is basically like saying drawoct[oct] = false */
1299 }
1300 if (oct == endoct) {
1301 /* need to compute stopval_end for this octant */
1302 dend = (double)end;
1303 switch (oct)
1304 {
1305 case 0:
1306 case 3:
1307 temp = sin(dend * M_PI / 180);
1308 break;
1309 case 1:
1310 case 6:
1311 temp = cos(dend * M_PI / 180);
1312 break;
1313 case 2:
1314 case 5:
1315 temp = -cos(dend * M_PI / 180);
1316 break;
1317 case 4:
1318 case 7:
1319 temp = -sin(dend * M_PI / 180);
1320 break;
1321 }
1322 temp *= rad;
1323 stopval_end = (int)temp;
1324
1325 /* and whether to draw in this octant initially */
1326 if (startoct == endoct) {
1327 /* note: we start drawing, stop, then start again in this case */
1328 /* otherwise: we only draw in this octant, so initialize it to false, it will get set back to true */
1329 if (start > end) {
1330 /* unfortunately, if we're in the same octant and need to draw over the whole circle, */
1331 /* we need to set the rest to true, because the while loop will end at the bottom. */
1332 drawoct = 255;
1333 } else {
1334 drawoct &= 255 - (1 << oct);
1335 }
1336 }
1337 else if (oct % 2) drawoct &= 255 - (1 << oct);
1338 else drawoct |= (1 << oct);
1339 } else if (oct != startoct) { /* already verified that it's != endoct */
1340 drawoct |= (1 << oct); /* draw this entire segment */
1341 }
1342 } while (oct != endoct);
1343
1344 /* so now we have what octants to draw and when to draw them. all that's left is the actual raster code. */
1345
1346 /*
1347 * Set color
1348 */
1349 result = 0;
1350 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
1351 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
1352
1353 /*
1354 * Draw arc
1355 */
1356 do {
1357 ypcy = y + cy;
1358 ymcy = y - cy;
1359 if (cx > 0) {
1360 xpcx = x + cx;
1361 xmcx = x - cx;
1362
1363 /* always check if we're drawing a certain octant before adding a pixel to that octant. */
1364 if (drawoct & 4) result |= pixel(renderer, xmcx, ypcy);
1365 if (drawoct & 2) result |= pixel(renderer, xpcx, ypcy);
1366 if (drawoct & 32) result |= pixel(renderer, xmcx, ymcy);
1367 if (drawoct & 64) result |= pixel(renderer, xpcx, ymcy);
1368 } else {
1369 if (drawoct & 96) result |= pixel(renderer, x, ymcy);
1370 if (drawoct & 6) result |= pixel(renderer, x, ypcy);
1371 }
1372
1373 xpcy = x + cy;
1374 xmcy = x - cy;
1375 if (cx > 0 && cx != cy) {
1376 ypcx = y + cx;
1377 ymcx = y - cx;
1378 if (drawoct & 8) result |= pixel(renderer, xmcy, ypcx);
1379 if (drawoct & 1) result |= pixel(renderer, xpcy, ypcx);
1380 if (drawoct & 16) result |= pixel(renderer, xmcy, ymcx);
1381 if (drawoct & 128) result |= pixel(renderer, xpcy, ymcx);
1382 } else if (cx == 0) {
1383 if (drawoct & 24) result |= pixel(renderer, xmcy, y);
1384 if (drawoct & 129) result |= pixel(renderer, xpcy, y);
1385 }
1386
1387 /*
1388 * Update whether we're drawing an octant
1389 */
1390 if (stopval_start == cx) {
1391 /* works like an on-off switch. */
1392 /* This is just in case start & end are in the same octant. */
1393 if (drawoct & (1 << startoct)) drawoct &= 255 - (1 << startoct);
1394 else drawoct |= (1 << startoct);
1395 }
1396 if (stopval_end == cx) {
1397 if (drawoct & (1 << endoct)) drawoct &= 255 - (1 << endoct);
1398 else drawoct |= (1 << endoct);
1399 }
1400
1401 /*
1402 * Update pixels
1403 */
1404 if (df < 0) {
1405 df += d_e;
1406 d_e += 2;
1407 d_se += 2;
1408 } else {
1409 df += d_se;
1410 d_e += 2;
1411 d_se += 4;
1412 cy--;
1413 }
1414 cx++;
1415 } while (cx <= cy);
1416
1417 return (result);
1418 }
1419
1420 /* ----- AA Circle */
1421
1422 /*!
1423 \brief Draw anti-aliased circle with blending.
1424
1425 \param renderer The renderer to draw on.
1426 \param x X coordinate of the center of the aa-circle.
1427 \param y Y coordinate of the center of the aa-circle.
1428 \param rad Radius in pixels of the aa-circle.
1429 \param color The color value of the aa-circle to draw (0xRRGGBBAA).
1430
1431 \returns Returns 0 on success, -1 on failure.
1432 */
aacircleColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Uint32 color)1433 int aacircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color)
1434 {
1435 Uint8 *c = (Uint8 *)&color;
1436 return aaellipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]);
1437 }
1438
1439 /*!
1440 \brief Draw anti-aliased circle with blending.
1441
1442 \param renderer The renderer to draw on.
1443 \param x X coordinate of the center of the aa-circle.
1444 \param y Y coordinate of the center of the aa-circle.
1445 \param rad Radius in pixels of the aa-circle.
1446 \param r The red value of the aa-circle to draw.
1447 \param g The green value of the aa-circle to draw.
1448 \param b The blue value of the aa-circle to draw.
1449 \param a The alpha value of the aa-circle to draw.
1450
1451 \returns Returns 0 on success, -1 on failure.
1452 */
aacircleRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1453 int aacircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1454 {
1455 /*
1456 * Draw
1457 */
1458 return aaellipseRGBA(renderer, x, y, rad, rad, r, g, b, a);
1459 }
1460
1461 /* ----- Ellipse */
1462
1463 /*!
1464 \brief Internal function to draw pixels or lines in 4 quadrants.
1465
1466 \param renderer The renderer to draw on.
1467 \param x X coordinate of the center of the quadrant.
1468 \param y Y coordinate of the center of the quadrant.
1469 \param dx X offset in pixels of the corners of the quadrant.
1470 \param dy Y offset in pixels of the corners of the quadrant.
1471 \param f Flag indicating if the quadrant should be filled (1) or not (0).
1472
1473 \returns Returns 0 on success, -1 on failure.
1474 */
_drawQuadrants(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 dx,Sint16 dy,Sint32 f)1475 int _drawQuadrants(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 dx, Sint16 dy, Sint32 f)
1476 {
1477 int result = 0;
1478 Sint16 xpdx, xmdx;
1479 Sint16 ypdy, ymdy;
1480
1481 if (dx == 0) {
1482 if (dy == 0) {
1483 result |= pixel(renderer, x, y);
1484 } else {
1485 ypdy = y + dy;
1486 ymdy = y - dy;
1487 if (f) {
1488 result |= vline(renderer, x, ymdy, ypdy);
1489 } else {
1490 result |= pixel(renderer, x, ypdy);
1491 result |= pixel(renderer, x, ymdy);
1492 }
1493 }
1494 } else {
1495 xpdx = x + dx;
1496 xmdx = x - dx;
1497 ypdy = y + dy;
1498 ymdy = y - dy;
1499 if (f) {
1500 result |= vline(renderer, xpdx, ymdy, ypdy);
1501 result |= vline(renderer, xmdx, ymdy, ypdy);
1502 } else {
1503 result |= pixel(renderer, xpdx, ypdy);
1504 result |= pixel(renderer, xmdx, ypdy);
1505 result |= pixel(renderer, xpdx, ymdy);
1506 result |= pixel(renderer, xmdx, ymdy);
1507 }
1508 }
1509
1510 return result;
1511 }
1512
1513 /*!
1514 \brief Internal function to draw ellipse or filled ellipse with blending.
1515
1516 \param renderer The renderer to draw on.
1517 \param x X coordinate of the center of the ellipse.
1518 \param y Y coordinate of the center of the ellipse.
1519 \param rx Horizontal radius in pixels of the ellipse.
1520 \param ry Vertical radius in pixels of the ellipse.
1521 \param r The red value of the ellipse to draw.
1522 \param g The green value of the ellipse to draw.
1523 \param b The blue value of the ellipse to draw.
1524 \param a The alpha value of the ellipse to draw.
1525 \param f Flag indicating if the ellipse should be filled (1) or not (0).
1526
1527 \returns Returns 0 on success, -1 on failure.
1528 */
1529 #define DEFAULT_ELLIPSE_OVERSCAN 4
_ellipseRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint8 r,Uint8 g,Uint8 b,Uint8 a,Sint32 f)1530 int _ellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Sint32 f)
1531 {
1532 int result;
1533 Sint32 rxi, ryi;
1534 Sint32 rx2, ry2, rx22, ry22;
1535 Sint32 error;
1536 Sint32 curX, curY, curXp1, curYm1;
1537 Sint32 scrX, scrY, oldX, oldY;
1538 Sint32 deltaX, deltaY;
1539 Sint32 ellipseOverscan;
1540
1541 /*
1542 * Sanity check radii
1543 */
1544 if ((rx < 0) || (ry < 0)) {
1545 return (-1);
1546 }
1547
1548 /*
1549 * Set color
1550 */
1551 result = 0;
1552 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
1553 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
1554
1555 /*
1556 * Special cases for rx=0 and/or ry=0: draw a hline/vline/pixel
1557 */
1558 if (rx == 0) {
1559 if (ry == 0) {
1560 return (pixel(renderer, x, y));
1561 } else {
1562 return (vline(renderer, x, y - ry, y + ry));
1563 }
1564 } else {
1565 if (ry == 0) {
1566 return (hline(renderer, x - rx, x + rx, y));
1567 }
1568 }
1569
1570 /*
1571 * Adjust overscan
1572 */
1573 rxi = rx;
1574 ryi = ry;
1575 if (rxi >= 512 || ryi >= 512)
1576 {
1577 ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 4;
1578 }
1579 else if (rxi >= 256 || ryi >= 256)
1580 {
1581 ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 2;
1582 }
1583 else
1584 {
1585 ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 1;
1586 }
1587
1588 /*
1589 * Top/bottom center points.
1590 */
1591 oldX = scrX = 0;
1592 oldY = scrY = ryi;
1593 result |= _drawQuadrants(renderer, x, y, 0, ry, f);
1594
1595 /* Midpoint ellipse algorithm with overdraw */
1596 rxi *= ellipseOverscan;
1597 ryi *= ellipseOverscan;
1598 rx2 = rxi * rxi;
1599 rx22 = rx2 + rx2;
1600 ry2 = ryi * ryi;
1601 ry22 = ry2 + ry2;
1602 curX = 0;
1603 curY = ryi;
1604 deltaX = 0;
1605 deltaY = rx22 * curY;
1606
1607 /* Points in segment 1 */
1608 error = ry2 - rx2 * ryi + rx2 / 4;
1609 while (deltaX <= deltaY)
1610 {
1611 curX++;
1612 deltaX += ry22;
1613
1614 error += deltaX + ry2;
1615 if (error >= 0)
1616 {
1617 curY--;
1618 deltaY -= rx22;
1619 error -= deltaY;
1620 }
1621
1622 scrX = curX / ellipseOverscan;
1623 scrY = curY / ellipseOverscan;
1624 if ((scrX != oldX && scrY == oldY) || (scrX != oldX && scrY != oldY)) {
1625 result |= _drawQuadrants(renderer, x, y, scrX, scrY, f);
1626 oldX = scrX;
1627 oldY = scrY;
1628 }
1629 }
1630
1631 /* Points in segment 2 */
1632 if (curY > 0)
1633 {
1634 curXp1 = curX + 1;
1635 curYm1 = curY - 1;
1636 error = ry2 * curX * curXp1 + ((ry2 + 3) / 4) + rx2 * curYm1 * curYm1 - rx2 * ry2;
1637 while (curY > 0)
1638 {
1639 curY--;
1640 deltaY -= rx22;
1641
1642 error += rx2;
1643 error -= deltaY;
1644
1645 if (error <= 0)
1646 {
1647 curX++;
1648 deltaX += ry22;
1649 error += deltaX;
1650 }
1651
1652 scrX = curX / ellipseOverscan;
1653 scrY = curY / ellipseOverscan;
1654 if ((scrX != oldX && scrY == oldY) || (scrX != oldX && scrY != oldY)) {
1655 oldY--;
1656 for (;oldY >= scrY; oldY--) {
1657 result |= _drawQuadrants(renderer, x, y, scrX, oldY, f);
1658 /* prevent overdraw */
1659 if (f) {
1660 oldY = scrY - 1;
1661 }
1662 }
1663 oldX = scrX;
1664 oldY = scrY;
1665 }
1666 }
1667
1668 /* Remaining points in vertical */
1669 if (!f) {
1670 oldY--;
1671 for (;oldY >= 0; oldY--) {
1672 result |= _drawQuadrants(renderer, x, y, scrX, oldY, f);
1673 }
1674 }
1675 }
1676
1677 return (result);
1678 }
1679
1680 /*!
1681 \brief Draw ellipse with blending.
1682
1683 \param renderer The renderer to draw on.
1684 \param x X coordinate of the center of the ellipse.
1685 \param y Y coordinate of the center of the ellipse.
1686 \param rx Horizontal radius in pixels of the ellipse.
1687 \param ry Vertical radius in pixels of the ellipse.
1688 \param color The color value of the ellipse to draw (0xRRGGBBAA).
1689
1690 \returns Returns 0 on success, -1 on failure.
1691 */
ellipseColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint32 color)1692 int ellipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color)
1693 {
1694 Uint8 *c = (Uint8 *)&color;
1695 return _ellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3], 0);
1696 }
1697
1698 /*!
1699 \brief Draw ellipse with blending.
1700
1701 \param renderer The renderer to draw on.
1702 \param x X coordinate of the center of the ellipse.
1703 \param y Y coordinate of the center of the ellipse.
1704 \param rx Horizontal radius in pixels of the ellipse.
1705 \param ry Vertical radius in pixels of the ellipse.
1706 \param r The red value of the ellipse to draw.
1707 \param g The green value of the ellipse to draw.
1708 \param b The blue value of the ellipse to draw.
1709 \param a The alpha value of the ellipse to draw.
1710
1711 \returns Returns 0 on success, -1 on failure.
1712 */
ellipseRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1713 int ellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1714 {
1715 return _ellipseRGBA(renderer, x, y, rx, ry, r, g, b, a, 0);
1716 }
1717
1718 /* ----- Filled Circle */
1719
1720 /*!
1721 \brief Draw filled circle with blending.
1722
1723 \param renderer The renderer to draw on.
1724 \param x X coordinate of the center of the filled circle.
1725 \param y Y coordinate of the center of the filled circle.
1726 \param rad Radius in pixels of the filled circle.
1727 \param color The color value of the filled circle to draw (0xRRGGBBAA).
1728
1729 \returns Returns 0 on success, -1 on failure.
1730 */
filledCircleColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Uint32 color)1731 int filledCircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color)
1732 {
1733 Uint8 *c = (Uint8 *)&color;
1734 return filledEllipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]);
1735 }
1736
1737 /*!
1738 \brief Draw filled circle with blending.
1739
1740 \param renderer The renderer to draw on.
1741 \param x X coordinate of the center of the filled circle.
1742 \param y Y coordinate of the center of the filled circle.
1743 \param rad Radius in pixels of the filled circle.
1744 \param r The red value of the filled circle to draw.
1745 \param g The green value of the filled circle to draw.
1746 \param b The blue value of the filled circle to draw.
1747 \param a The alpha value of the filled circle to draw.
1748
1749 \returns Returns 0 on success, -1 on failure.
1750 */
filledCircleRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1751 int filledCircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1752 {
1753 return _ellipseRGBA(renderer, x, y, rad, rad, r, g ,b, a, 1);
1754 }
1755
1756
1757 /* ----- AA Ellipse */
1758
1759 /* Windows targets do not have lrint, so provide a local inline version */
1760 #if defined(_MSC_VER)
1761 /* Detect 64bit and use intrinsic version */
1762 #ifdef _M_X64
1763 #include <emmintrin.h>
1764 static __inline long
lrint(float f)1765 lrint(float f)
1766 {
1767 return _mm_cvtss_si32(_mm_load_ss(&f));
1768 }
1769 #elif defined(_M_IX86)
1770 __inline long int
lrint(double flt)1771 lrint (double flt)
1772 {
1773 int intgr;
1774 _asm
1775 {
1776 fld flt
1777 fistp intgr
1778 };
1779 return intgr;
1780 }
1781 #elif defined(_M_ARM)
1782 #include <armintr.h>
1783 #pragma warning(push)
1784 #pragma warning(disable: 4716)
1785 __declspec(naked) long int
lrint(double flt)1786 lrint (double flt)
1787 {
1788 __emit(0xEC410B10); // fmdrr d0, r0, r1
1789 __emit(0xEEBD0B40); // ftosid s0, d0
1790 __emit(0xEE100A10); // fmrs r0, s0
1791 __emit(0xE12FFF1E); // bx lr
1792 }
1793 #pragma warning(pop)
1794 #else
1795 #error lrint needed for MSVC on non X86/AMD64/ARM targets.
1796 #endif
1797 #endif
1798
1799 /*!
1800 \brief Draw anti-aliased ellipse with blending.
1801
1802 \param renderer The renderer to draw on.
1803 \param x X coordinate of the center of the aa-ellipse.
1804 \param y Y coordinate of the center of the aa-ellipse.
1805 \param rx Horizontal radius in pixels of the aa-ellipse.
1806 \param ry Vertical radius in pixels of the aa-ellipse.
1807 \param color The color value of the aa-ellipse to draw (0xRRGGBBAA).
1808
1809 \returns Returns 0 on success, -1 on failure.
1810 */
aaellipseColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint32 color)1811 int aaellipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color)
1812 {
1813 Uint8 *c = (Uint8 *)&color;
1814 return aaellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3]);
1815 }
1816
1817 /*!
1818 \brief Draw anti-aliased ellipse with blending.
1819
1820 \param renderer The renderer to draw on.
1821 \param x X coordinate of the center of the aa-ellipse.
1822 \param y Y coordinate of the center of the aa-ellipse.
1823 \param rx Horizontal radius in pixels of the aa-ellipse.
1824 \param ry Vertical radius in pixels of the aa-ellipse.
1825 \param r The red value of the aa-ellipse to draw.
1826 \param g The green value of the aa-ellipse to draw.
1827 \param b The blue value of the aa-ellipse to draw.
1828 \param a The alpha value of the aa-ellipse to draw.
1829
1830 \returns Returns 0 on success, -1 on failure.
1831 */
aaellipseRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint8 r,Uint8 g,Uint8 b,Uint8 a)1832 int aaellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
1833 {
1834 int result;
1835 int i;
1836 int a2, b2, ds, dt, dxt, t, s, d;
1837 Sint16 xp, yp, xs, ys, dyt, od, xx, yy, xc2, yc2;
1838 float cp;
1839 double sab;
1840 Uint8 weight, iweight;
1841
1842 /*
1843 * Sanity check radii
1844 */
1845 if ((rx < 0) || (ry < 0)) {
1846 return (-1);
1847 }
1848
1849 /*
1850 * Special cases for rx=0 and/or ry=0: draw a hline/vline/pixel
1851 */
1852 if (rx == 0) {
1853 if (ry == 0) {
1854 return (pixelRGBA(renderer, x, y, r, g, b, a));
1855 } else {
1856 return (vlineRGBA(renderer, x, y - ry, y + ry, r, g, b, a));
1857 }
1858 } else {
1859 if (ry == 0) {
1860 return (hlineRGBA(renderer, x - rx, x + rx, y, r, g, b, a));
1861 }
1862 }
1863
1864 /* Variable setup */
1865 a2 = rx * rx;
1866 b2 = ry * ry;
1867
1868 ds = 2 * a2;
1869 dt = 2 * b2;
1870
1871 xc2 = 2 * x;
1872 yc2 = 2 * y;
1873
1874 sab = sqrt((double)(a2 + b2));
1875 od = (Sint16)lrint(sab*0.01) + 1; /* introduce some overdraw */
1876 dxt = (Sint16)lrint((double)a2 / sab) + od;
1877
1878 t = 0;
1879 s = -2 * a2 * ry;
1880 d = 0;
1881
1882 xp = x;
1883 yp = y - ry;
1884
1885 /* Draw */
1886 result = 0;
1887 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
1888
1889 /* "End points" */
1890 result |= pixelRGBA(renderer, xp, yp, r, g, b, a);
1891 result |= pixelRGBA(renderer, xc2 - xp, yp, r, g, b, a);
1892 result |= pixelRGBA(renderer, xp, yc2 - yp, r, g, b, a);
1893 result |= pixelRGBA(renderer, xc2 - xp, yc2 - yp, r, g, b, a);
1894
1895 for (i = 1; i <= dxt; i++) {
1896 xp--;
1897 d += t - b2;
1898
1899 if (d >= 0)
1900 ys = yp - 1;
1901 else if ((d - s - a2) > 0) {
1902 if ((2 * d - s - a2) >= 0)
1903 ys = yp + 1;
1904 else {
1905 ys = yp;
1906 yp++;
1907 d -= s + a2;
1908 s += ds;
1909 }
1910 } else {
1911 yp++;
1912 ys = yp + 1;
1913 d -= s + a2;
1914 s += ds;
1915 }
1916
1917 t -= dt;
1918
1919 /* Calculate alpha */
1920 if (s != 0) {
1921 cp = (float) abs(d) / (float) abs(s);
1922 if (cp > 1.0) {
1923 cp = 1.0;
1924 }
1925 } else {
1926 cp = 1.0;
1927 }
1928
1929 /* Calculate weights */
1930 weight = (Uint8) (cp * 255);
1931 iweight = 255 - weight;
1932
1933 /* Upper half */
1934 xx = xc2 - xp;
1935 result |= pixelRGBAWeight(renderer, xp, yp, r, g, b, a, iweight);
1936 result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, iweight);
1937
1938 result |= pixelRGBAWeight(renderer, xp, ys, r, g, b, a, weight);
1939 result |= pixelRGBAWeight(renderer, xx, ys, r, g, b, a, weight);
1940
1941 /* Lower half */
1942 yy = yc2 - yp;
1943 result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, iweight);
1944 result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, iweight);
1945
1946 yy = yc2 - ys;
1947 result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, weight);
1948 result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, weight);
1949 }
1950
1951 /* Replaces original approximation code dyt = abs(yp - yc); */
1952 dyt = (Sint16)lrint((double)b2 / sab ) + od;
1953
1954 for (i = 1; i <= dyt; i++) {
1955 yp++;
1956 d -= s + a2;
1957
1958 if (d <= 0)
1959 xs = xp + 1;
1960 else if ((d + t - b2) < 0) {
1961 if ((2 * d + t - b2) <= 0)
1962 xs = xp - 1;
1963 else {
1964 xs = xp;
1965 xp--;
1966 d += t - b2;
1967 t -= dt;
1968 }
1969 } else {
1970 xp--;
1971 xs = xp - 1;
1972 d += t - b2;
1973 t -= dt;
1974 }
1975
1976 s += ds;
1977
1978 /* Calculate alpha */
1979 if (t != 0) {
1980 cp = (float) abs(d) / (float) abs(t);
1981 if (cp > 1.0) {
1982 cp = 1.0;
1983 }
1984 } else {
1985 cp = 1.0;
1986 }
1987
1988 /* Calculate weight */
1989 weight = (Uint8) (cp * 255);
1990 iweight = 255 - weight;
1991
1992 /* Left half */
1993 xx = xc2 - xp;
1994 yy = yc2 - yp;
1995 result |= pixelRGBAWeight(renderer, xp, yp, r, g, b, a, iweight);
1996 result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, iweight);
1997
1998 result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, iweight);
1999 result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, iweight);
2000
2001 /* Right half */
2002 xx = xc2 - xs;
2003 result |= pixelRGBAWeight(renderer, xs, yp, r, g, b, a, weight);
2004 result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, weight);
2005
2006 result |= pixelRGBAWeight(renderer, xs, yy, r, g, b, a, weight);
2007 result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, weight);
2008 }
2009
2010 return (result);
2011 }
2012
2013 /* ---- Filled Ellipse */
2014
2015 /*!
2016 \brief Draw filled ellipse with blending.
2017
2018 \param renderer The renderer to draw on.
2019 \param x X coordinate of the center of the filled ellipse.
2020 \param y Y coordinate of the center of the filled ellipse.
2021 \param rx Horizontal radius in pixels of the filled ellipse.
2022 \param ry Vertical radius in pixels of the filled ellipse.
2023 \param color The color value of the filled ellipse to draw (0xRRGGBBAA).
2024
2025 \returns Returns 0 on success, -1 on failure.
2026 */
filledEllipseColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint32 color)2027 int filledEllipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color)
2028 {
2029 Uint8 *c = (Uint8 *)&color;
2030 return _ellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3], 1);
2031 }
2032
2033 /*!
2034 \brief Draw filled ellipse with blending.
2035
2036 \param renderer The renderer to draw on.
2037 \param x X coordinate of the center of the filled ellipse.
2038 \param y Y coordinate of the center of the filled ellipse.
2039 \param rx Horizontal radius in pixels of the filled ellipse.
2040 \param ry Vertical radius in pixels of the filled ellipse.
2041 \param r The red value of the filled ellipse to draw.
2042 \param g The green value of the filled ellipse to draw.
2043 \param b The blue value of the filled ellipse to draw.
2044 \param a The alpha value of the filled ellipse to draw.
2045
2046 \returns Returns 0 on success, -1 on failure.
2047 */
filledEllipseRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rx,Sint16 ry,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2048 int filledEllipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2049 {
2050 return _ellipseRGBA(renderer, x, y, rx, ry, r, g, b, a, 1);
2051 }
2052
2053 /* ----- Pie */
2054
2055 /*!
2056 \brief Internal float (low-speed) pie-calc implementation by drawing polygons.
2057
2058 Note: Determines vertex array and uses polygon or filledPolygon drawing routines to render.
2059
2060 \param renderer The renderer to draw on.
2061 \param x X coordinate of the center of the pie.
2062 \param y Y coordinate of the center of the pie.
2063 \param rad Radius in pixels of the pie.
2064 \param start Starting radius in degrees of the pie.
2065 \param end Ending radius in degrees of the pie.
2066 \param r The red value of the pie to draw.
2067 \param g The green value of the pie to draw.
2068 \param b The blue value of the pie to draw.
2069 \param a The alpha value of the pie to draw.
2070 \param filled Flag indicating if the pie should be filled (=1) or not (=0).
2071
2072 \returns Returns 0 on success, -1 on failure.
2073 */
2074 /* TODO: rewrite algorithm; pie is not always accurate */
_pieRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint8 r,Uint8 g,Uint8 b,Uint8 a,Uint8 filled)2075 int _pieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 filled)
2076 {
2077 int result;
2078 double angle, start_angle, end_angle;
2079 double deltaAngle;
2080 double dr;
2081 int numpoints, i;
2082 Sint16 *vx, *vy;
2083
2084 /*
2085 * Sanity check radii
2086 */
2087 if (rad < 0) {
2088 return (-1);
2089 }
2090
2091 /*
2092 * Fixup angles
2093 */
2094 start = start % 360;
2095 end = end % 360;
2096
2097 /*
2098 * Special case for rad=0 - draw a point
2099 */
2100 if (rad == 0) {
2101 return (pixelRGBA(renderer, x, y, r, g, b, a));
2102 }
2103
2104 /*
2105 * Variable setup
2106 */
2107 dr = (double) rad;
2108 deltaAngle = 3.0 / dr;
2109 start_angle = (double) start *(2.0 * M_PI / 360.0);
2110 end_angle = (double) end *(2.0 * M_PI / 360.0);
2111 if (start > end) {
2112 end_angle += (2.0 * M_PI);
2113 }
2114
2115 /* We will always have at least 2 points */
2116 numpoints = 2;
2117
2118 /* Count points (rather than calculating it) */
2119 angle = start_angle;
2120 while (angle < end_angle) {
2121 angle += deltaAngle;
2122 numpoints++;
2123 }
2124
2125 /* Allocate combined vertex array */
2126 vx = vy = (Sint16 *) malloc(2 * sizeof(Uint16) * numpoints);
2127 if (vx == NULL) {
2128 return (-1);
2129 }
2130
2131 /* Update point to start of vy */
2132 vy += numpoints;
2133
2134 /* Center */
2135 vx[0] = x;
2136 vy[0] = y;
2137
2138 /* First vertex */
2139 angle = start_angle;
2140 vx[1] = x + (int) (dr * cos(angle));
2141 vy[1] = y + (int) (dr * sin(angle));
2142
2143 if (numpoints<3)
2144 {
2145 result = lineRGBA(renderer, vx[0], vy[0], vx[1], vy[1], r, g, b, a);
2146 }
2147 else
2148 {
2149 /* Calculate other vertices */
2150 i = 2;
2151 angle = start_angle;
2152 while (angle < end_angle) {
2153 angle += deltaAngle;
2154 if (angle>end_angle)
2155 {
2156 angle = end_angle;
2157 }
2158 vx[i] = x + (int) (dr * cos(angle));
2159 vy[i] = y + (int) (dr * sin(angle));
2160 i++;
2161 }
2162
2163 /* Draw */
2164 if (filled) {
2165 result = filledPolygonRGBA(renderer, vx, vy, numpoints, r, g, b, a);
2166 } else {
2167 result = polygonRGBA(renderer, vx, vy, numpoints, r, g, b, a);
2168 }
2169 }
2170
2171 /* Free combined vertex array */
2172 free(vx);
2173
2174 return (result);
2175 }
2176
2177 /*!
2178 \brief Draw pie (outline) with alpha blending.
2179
2180 \param renderer The renderer to draw on.
2181 \param x X coordinate of the center of the pie.
2182 \param y Y coordinate of the center of the pie.
2183 \param rad Radius in pixels of the pie.
2184 \param start Starting radius in degrees of the pie.
2185 \param end Ending radius in degrees of the pie.
2186 \param color The color value of the pie to draw (0xRRGGBBAA).
2187
2188 \returns Returns 0 on success, -1 on failure.
2189 */
pieColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint32 color)2190 int pieColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad,
2191 Sint16 start, Sint16 end, Uint32 color)
2192 {
2193 Uint8 *c = (Uint8 *)&color;
2194 return _pieRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], 0);
2195 }
2196
2197 /*!
2198 \brief Draw pie (outline) with alpha blending.
2199
2200 \param renderer The renderer to draw on.
2201 \param x X coordinate of the center of the pie.
2202 \param y Y coordinate of the center of the pie.
2203 \param rad Radius in pixels of the pie.
2204 \param start Starting radius in degrees of the pie.
2205 \param end Ending radius in degrees of the pie.
2206 \param r The red value of the pie to draw.
2207 \param g The green value of the pie to draw.
2208 \param b The blue value of the pie to draw.
2209 \param a The alpha value of the pie to draw.
2210
2211 \returns Returns 0 on success, -1 on failure.
2212 */
pieRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2213 int pieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad,
2214 Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2215 {
2216 return _pieRGBA(renderer, x, y, rad, start, end, r, g, b, a, 0);
2217 }
2218
2219 /*!
2220 \brief Draw filled pie with alpha blending.
2221
2222 \param renderer The renderer to draw on.
2223 \param x X coordinate of the center of the filled pie.
2224 \param y Y coordinate of the center of the filled pie.
2225 \param rad Radius in pixels of the filled pie.
2226 \param start Starting radius in degrees of the filled pie.
2227 \param end Ending radius in degrees of the filled pie.
2228 \param color The color value of the filled pie to draw (0xRRGGBBAA).
2229
2230 \returns Returns 0 on success, -1 on failure.
2231 */
filledPieColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint32 color)2232 int filledPieColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color)
2233 {
2234 Uint8 *c = (Uint8 *)&color;
2235 return _pieRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], 1);
2236 }
2237
2238 /*!
2239 \brief Draw filled pie with alpha blending.
2240
2241 \param renderer The renderer to draw on.
2242 \param x X coordinate of the center of the filled pie.
2243 \param y Y coordinate of the center of the filled pie.
2244 \param rad Radius in pixels of the filled pie.
2245 \param start Starting radius in degrees of the filled pie.
2246 \param end Ending radius in degrees of the filled pie.
2247 \param r The red value of the filled pie to draw.
2248 \param g The green value of the filled pie to draw.
2249 \param b The blue value of the filled pie to draw.
2250 \param a The alpha value of the filled pie to draw.
2251
2252 \returns Returns 0 on success, -1 on failure.
2253 */
filledPieRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,Sint16 rad,Sint16 start,Sint16 end,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2254 int filledPieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad,
2255 Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2256 {
2257 return _pieRGBA(renderer, x, y, rad, start, end, r, g, b, a, 1);
2258 }
2259
2260 /* ------ Trigon */
2261
2262 /*!
2263 \brief Draw trigon (triangle outline) with alpha blending.
2264
2265 Note: Creates vertex array and uses polygon routine to render.
2266
2267 \param renderer The renderer to draw on.
2268 \param x1 X coordinate of the first point of the trigon.
2269 \param y1 Y coordinate of the first point of the trigon.
2270 \param x2 X coordinate of the second point of the trigon.
2271 \param y2 Y coordinate of the second point of the trigon.
2272 \param x3 X coordinate of the third point of the trigon.
2273 \param y3 Y coordinate of the third point of the trigon.
2274 \param color The color value of the trigon to draw (0xRRGGBBAA).
2275
2276 \returns Returns 0 on success, -1 on failure.
2277 */
trigonColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 x3,Sint16 y3,Uint32 color)2278 int trigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color)
2279 {
2280 Sint16 vx[3];
2281 Sint16 vy[3];
2282
2283 vx[0]=x1;
2284 vx[1]=x2;
2285 vx[2]=x3;
2286 vy[0]=y1;
2287 vy[1]=y2;
2288 vy[2]=y3;
2289
2290 return(polygonColor(renderer,vx,vy,3,color));
2291 }
2292
2293 /*!
2294 \brief Draw trigon (triangle outline) with alpha blending.
2295
2296 \param renderer The renderer to draw on.
2297 \param x1 X coordinate of the first point of the trigon.
2298 \param y1 Y coordinate of the first point of the trigon.
2299 \param x2 X coordinate of the second point of the trigon.
2300 \param y2 Y coordinate of the second point of the trigon.
2301 \param x3 X coordinate of the third point of the trigon.
2302 \param y3 Y coordinate of the third point of the trigon.
2303 \param r The red value of the trigon to draw.
2304 \param g The green value of the trigon to draw.
2305 \param b The blue value of the trigon to draw.
2306 \param a The alpha value of the trigon to draw.
2307
2308 \returns Returns 0 on success, -1 on failure.
2309 */
trigonRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 x3,Sint16 y3,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2310 int trigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3,
2311 Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2312 {
2313 Sint16 vx[3];
2314 Sint16 vy[3];
2315
2316 vx[0]=x1;
2317 vx[1]=x2;
2318 vx[2]=x3;
2319 vy[0]=y1;
2320 vy[1]=y2;
2321 vy[2]=y3;
2322
2323 return(polygonRGBA(renderer,vx,vy,3,r,g,b,a));
2324 }
2325
2326 /* ------ AA-Trigon */
2327
2328 /*!
2329 \brief Draw anti-aliased trigon (triangle outline) with alpha blending.
2330
2331 Note: Creates vertex array and uses aapolygon routine to render.
2332
2333 \param renderer The renderer to draw on.
2334 \param x1 X coordinate of the first point of the aa-trigon.
2335 \param y1 Y coordinate of the first point of the aa-trigon.
2336 \param x2 X coordinate of the second point of the aa-trigon.
2337 \param y2 Y coordinate of the second point of the aa-trigon.
2338 \param x3 X coordinate of the third point of the aa-trigon.
2339 \param y3 Y coordinate of the third point of the aa-trigon.
2340 \param color The color value of the aa-trigon to draw (0xRRGGBBAA).
2341
2342 \returns Returns 0 on success, -1 on failure.
2343 */
aatrigonColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 x3,Sint16 y3,Uint32 color)2344 int aatrigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color)
2345 {
2346 Sint16 vx[3];
2347 Sint16 vy[3];
2348
2349 vx[0]=x1;
2350 vx[1]=x2;
2351 vx[2]=x3;
2352 vy[0]=y1;
2353 vy[1]=y2;
2354 vy[2]=y3;
2355
2356 return(aapolygonColor(renderer,vx,vy,3,color));
2357 }
2358
2359 /*!
2360 \brief Draw anti-aliased trigon (triangle outline) with alpha blending.
2361
2362 \param renderer The renderer to draw on.
2363 \param x1 X coordinate of the first point of the aa-trigon.
2364 \param y1 Y coordinate of the first point of the aa-trigon.
2365 \param x2 X coordinate of the second point of the aa-trigon.
2366 \param y2 Y coordinate of the second point of the aa-trigon.
2367 \param x3 X coordinate of the third point of the aa-trigon.
2368 \param y3 Y coordinate of the third point of the aa-trigon.
2369 \param r The red value of the aa-trigon to draw.
2370 \param g The green value of the aa-trigon to draw.
2371 \param b The blue value of the aa-trigon to draw.
2372 \param a The alpha value of the aa-trigon to draw.
2373
2374 \returns Returns 0 on success, -1 on failure.
2375 */
aatrigonRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 x3,Sint16 y3,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2376 int aatrigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3,
2377 Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2378 {
2379 Sint16 vx[3];
2380 Sint16 vy[3];
2381
2382 vx[0]=x1;
2383 vx[1]=x2;
2384 vx[2]=x3;
2385 vy[0]=y1;
2386 vy[1]=y2;
2387 vy[2]=y3;
2388
2389 return(aapolygonRGBA(renderer,vx,vy,3,r,g,b,a));
2390 }
2391
2392 /* ------ Filled Trigon */
2393
2394 /*!
2395 \brief Draw filled trigon (triangle) with alpha blending.
2396
2397 Note: Creates vertex array and uses aapolygon routine to render.
2398
2399 \param renderer The renderer to draw on.
2400 \param x1 X coordinate of the first point of the filled trigon.
2401 \param y1 Y coordinate of the first point of the filled trigon.
2402 \param x2 X coordinate of the second point of the filled trigon.
2403 \param y2 Y coordinate of the second point of the filled trigon.
2404 \param x3 X coordinate of the third point of the filled trigon.
2405 \param y3 Y coordinate of the third point of the filled trigon.
2406 \param color The color value of the filled trigon to draw (0xRRGGBBAA).
2407
2408 \returns Returns 0 on success, -1 on failure.
2409 */
filledTrigonColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 x3,Sint16 y3,Uint32 color)2410 int filledTrigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color)
2411 {
2412 Sint16 vx[3];
2413 Sint16 vy[3];
2414
2415 vx[0]=x1;
2416 vx[1]=x2;
2417 vx[2]=x3;
2418 vy[0]=y1;
2419 vy[1]=y2;
2420 vy[2]=y3;
2421
2422 return(filledPolygonColor(renderer,vx,vy,3,color));
2423 }
2424
2425 /*!
2426 \brief Draw filled trigon (triangle) with alpha blending.
2427
2428 Note: Creates vertex array and uses aapolygon routine to render.
2429
2430 \param renderer The renderer to draw on.
2431 \param x1 X coordinate of the first point of the filled trigon.
2432 \param y1 Y coordinate of the first point of the filled trigon.
2433 \param x2 X coordinate of the second point of the filled trigon.
2434 \param y2 Y coordinate of the second point of the filled trigon.
2435 \param x3 X coordinate of the third point of the filled trigon.
2436 \param y3 Y coordinate of the third point of the filled trigon.
2437 \param r The red value of the filled trigon to draw.
2438 \param g The green value of the filled trigon to draw.
2439 \param b The blue value of the filled trigon to draw.
2440 \param a The alpha value of the filled trigon to draw.
2441
2442 \returns Returns 0 on success, -1 on failure.
2443 */
filledTrigonRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Sint16 x3,Sint16 y3,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2444 int filledTrigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3,
2445 Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2446 {
2447 Sint16 vx[3];
2448 Sint16 vy[3];
2449
2450 vx[0]=x1;
2451 vx[1]=x2;
2452 vx[2]=x3;
2453 vy[0]=y1;
2454 vy[1]=y2;
2455 vy[2]=y3;
2456
2457 return(filledPolygonRGBA(renderer,vx,vy,3,r,g,b,a));
2458 }
2459
2460 /* ---- Polygon */
2461
2462 /*!
2463 \brief Draw polygon with alpha blending.
2464
2465 \param renderer The renderer to draw on.
2466 \param vx Vertex array containing X coordinates of the points of the polygon.
2467 \param vy Vertex array containing Y coordinates of the points of the polygon.
2468 \param n Number of points in the vertex array. Minimum number is 3.
2469 \param color The color value of the polygon to draw (0xRRGGBBAA).
2470
2471 \returns Returns 0 on success, -1 on failure.
2472 */
polygonColor(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint32 color)2473 int polygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color)
2474 {
2475 Uint8 *c = (Uint8 *)&color;
2476 return polygonRGBA(renderer, vx, vy, n, c[0], c[1], c[2], c[3]);
2477 }
2478
2479 /*!
2480 \brief Draw polygon with the currently set color and blend mode.
2481
2482 \param renderer The renderer to draw on.
2483 \param vx Vertex array containing X coordinates of the points of the polygon.
2484 \param vy Vertex array containing Y coordinates of the points of the polygon.
2485 \param n Number of points in the vertex array. Minimum number is 3.
2486
2487 \returns Returns 0 on success, -1 on failure.
2488 */
polygon(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n)2489 int polygon(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n)
2490 {
2491 /*
2492 * Draw
2493 */
2494 int result = 0;
2495 int i, nn;
2496 SDL_Point* points;
2497
2498 /*
2499 * Vertex array NULL check
2500 */
2501 if (vx == NULL) {
2502 return (-1);
2503 }
2504 if (vy == NULL) {
2505 return (-1);
2506 }
2507
2508 /*
2509 * Sanity check
2510 */
2511 if (n < 3) {
2512 return (-1);
2513 }
2514
2515 /*
2516 * Create array of points
2517 */
2518 nn = n + 1;
2519 points = (SDL_Point*)malloc(sizeof(SDL_Point) * nn);
2520 if (points == NULL)
2521 {
2522 return -1;
2523 }
2524 for (i=0; i<n; i++)
2525 {
2526 points[i].x = vx[i];
2527 points[i].y = vy[i];
2528 }
2529 points[n].x = vx[0];
2530 points[n].y = vy[0];
2531
2532 /*
2533 * Draw
2534 */
2535 result |= SDL_RenderDrawLines(renderer, points, nn);
2536 free(points);
2537
2538 return (result);
2539 }
2540
2541 /*!
2542 \brief Draw polygon with alpha blending.
2543
2544 \param renderer The renderer to draw on.
2545 \param vx Vertex array containing X coordinates of the points of the polygon.
2546 \param vy Vertex array containing Y coordinates of the points of the polygon.
2547 \param n Number of points in the vertex array. Minimum number is 3.
2548 \param r The red value of the polygon to draw.
2549 \param g The green value of the polygon to draw.
2550 \param b The blue value of the polygon to draw.
2551 \param a The alpha value of the polygon to draw.
2552
2553 \returns Returns 0 on success, -1 on failure.
2554 */
polygonRGBA(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2555 int polygonRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2556 {
2557 /*
2558 * Draw
2559 */
2560 int result;
2561 const Sint16 *x1, *y1, *x2, *y2;
2562
2563 /*
2564 * Vertex array NULL check
2565 */
2566 if (vx == NULL) {
2567 return (-1);
2568 }
2569 if (vy == NULL) {
2570 return (-1);
2571 }
2572
2573 /*
2574 * Sanity check
2575 */
2576 if (n < 3) {
2577 return (-1);
2578 }
2579
2580 /*
2581 * Pointer setup
2582 */
2583 x1 = x2 = vx;
2584 y1 = y2 = vy;
2585 x2++;
2586 y2++;
2587
2588 /*
2589 * Set color
2590 */
2591 result = 0;
2592 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
2593 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
2594
2595 /*
2596 * Draw
2597 */
2598 result |= polygon(renderer, vx, vy, n);
2599
2600 return (result);
2601 }
2602
2603 /* ---- AA-Polygon */
2604
2605 /*!
2606 \brief Draw anti-aliased polygon with alpha blending.
2607
2608 \param renderer The renderer to draw on.
2609 \param vx Vertex array containing X coordinates of the points of the aa-polygon.
2610 \param vy Vertex array containing Y coordinates of the points of the aa-polygon.
2611 \param n Number of points in the vertex array. Minimum number is 3.
2612 \param color The color value of the aa-polygon to draw (0xRRGGBBAA).
2613
2614 \returns Returns 0 on success, -1 on failure.
2615 */
aapolygonColor(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint32 color)2616 int aapolygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color)
2617 {
2618 Uint8 *c = (Uint8 *)&color;
2619 return aapolygonRGBA(renderer, vx, vy, n, c[0], c[1], c[2], c[3]);
2620 }
2621
2622 /*!
2623 \brief Draw anti-aliased polygon with alpha blending.
2624
2625 \param renderer The renderer to draw on.
2626 \param vx Vertex array containing X coordinates of the points of the aa-polygon.
2627 \param vy Vertex array containing Y coordinates of the points of the aa-polygon.
2628 \param n Number of points in the vertex array. Minimum number is 3.
2629 \param r The red value of the aa-polygon to draw.
2630 \param g The green value of the aa-polygon to draw.
2631 \param b The blue value of the aa-polygon to draw.
2632 \param a The alpha value of the aa-polygon to draw.
2633
2634 \returns Returns 0 on success, -1 on failure.
2635 */
aapolygonRGBA(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2636 int aapolygonRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2637 {
2638 int result;
2639 int i;
2640 const Sint16 *x1, *y1, *x2, *y2;
2641
2642 /*
2643 * Vertex array NULL check
2644 */
2645 if (vx == NULL) {
2646 return (-1);
2647 }
2648 if (vy == NULL) {
2649 return (-1);
2650 }
2651
2652 /*
2653 * Sanity check
2654 */
2655 if (n < 3) {
2656 return (-1);
2657 }
2658
2659 /*
2660 * Pointer setup
2661 */
2662 x1 = x2 = vx;
2663 y1 = y2 = vy;
2664 x2++;
2665 y2++;
2666
2667 /*
2668 * Draw
2669 */
2670 result = 0;
2671 for (i = 1; i < n; i++) {
2672 result |= _aalineRGBA(renderer, *x1, *y1, *x2, *y2, r, g, b, a, 0);
2673 x1 = x2;
2674 y1 = y2;
2675 x2++;
2676 y2++;
2677 }
2678
2679 result |= _aalineRGBA(renderer, *x1, *y1, *vx, *vy, r, g, b, a, 0);
2680
2681 return (result);
2682 }
2683
2684 /* ---- Filled Polygon */
2685
2686 /*!
2687 \brief Internal helper qsort callback functions used in filled polygon drawing.
2688
2689 \param a The surface to draw on.
2690 \param b Vertex array containing X coordinates of the points of the polygon.
2691
2692 \returns Returns 0 if a==b, a negative number if a<b or a positive number if a>b.
2693 */
_gfxPrimitivesCompareInt(const void * a,const void * b)2694 int _gfxPrimitivesCompareInt(const void *a, const void *b)
2695 {
2696 return (*(const int *) a) - (*(const int *) b);
2697 }
2698
2699 /*!
2700 \brief Global vertex array to use if optional parameters are not given in filledPolygonMT calls.
2701
2702 Note: Used for non-multithreaded (default) operation of filledPolygonMT.
2703 */
2704 static int *gfxPrimitivesPolyIntsGlobal = NULL;
2705
2706 /*!
2707 \brief Flag indicating if global vertex array was already allocated.
2708
2709 Note: Used for non-multithreaded (default) operation of filledPolygonMT.
2710 */
2711 static int gfxPrimitivesPolyAllocatedGlobal = 0;
2712
2713 /*!
2714 \brief Draw filled polygon with alpha blending (multi-threaded capable).
2715
2716 Note: The last two parameters are optional; but are required for multithreaded operation.
2717
2718 \param renderer The renderer to draw on.
2719 \param vx Vertex array containing X coordinates of the points of the filled polygon.
2720 \param vy Vertex array containing Y coordinates of the points of the filled polygon.
2721 \param n Number of points in the vertex array. Minimum number is 3.
2722 \param r The red value of the filled polygon to draw.
2723 \param g The green value of the filled polygon to draw.
2724 \param b The blue value of the filled polygon to draw.
2725 \param a The alpha value of the filled polygon to draw.
2726 \param polyInts Preallocated, temporary vertex array used for sorting vertices. Required for multithreaded operation; set to NULL otherwise.
2727 \param polyAllocated Flag indicating if temporary vertex array was allocated. Required for multithreaded operation; set to NULL otherwise.
2728
2729 \returns Returns 0 on success, -1 on failure.
2730 */
filledPolygonRGBAMT(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint8 r,Uint8 g,Uint8 b,Uint8 a,int ** polyInts,int * polyAllocated)2731 int filledPolygonRGBAMT(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a, int **polyInts, int *polyAllocated)
2732 {
2733 int result;
2734 int i;
2735 int y, xa, xb;
2736 int miny, maxy;
2737 int x1, y1;
2738 int x2, y2;
2739 int ind1, ind2;
2740 int ints;
2741 int *gfxPrimitivesPolyInts = NULL;
2742 int *gfxPrimitivesPolyIntsNew = NULL;
2743 int gfxPrimitivesPolyAllocated = 0;
2744
2745 /*
2746 * Vertex array NULL check
2747 */
2748 if (vx == NULL) {
2749 return (-1);
2750 }
2751 if (vy == NULL) {
2752 return (-1);
2753 }
2754
2755 /*
2756 * Sanity check number of edges
2757 */
2758 if (n < 3) {
2759 return -1;
2760 }
2761
2762 /*
2763 * Map polygon cache
2764 */
2765 if ((polyInts==NULL) || (polyAllocated==NULL)) {
2766 /* Use global cache */
2767 gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal;
2768 gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal;
2769 } else {
2770 /* Use local cache */
2771 gfxPrimitivesPolyInts = *polyInts;
2772 gfxPrimitivesPolyAllocated = *polyAllocated;
2773 }
2774
2775 /*
2776 * Allocate temp array, only grow array
2777 */
2778 if (!gfxPrimitivesPolyAllocated) {
2779 gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n);
2780 gfxPrimitivesPolyAllocated = n;
2781 } else {
2782 if (gfxPrimitivesPolyAllocated < n) {
2783 gfxPrimitivesPolyIntsNew = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n);
2784 if (!gfxPrimitivesPolyIntsNew) {
2785 if (!gfxPrimitivesPolyInts) {
2786 free(gfxPrimitivesPolyInts);
2787 gfxPrimitivesPolyInts = NULL;
2788 }
2789 gfxPrimitivesPolyAllocated = 0;
2790 } else {
2791 gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsNew;
2792 gfxPrimitivesPolyAllocated = n;
2793 }
2794 }
2795 }
2796
2797 /*
2798 * Check temp array
2799 */
2800 if (gfxPrimitivesPolyInts==NULL) {
2801 gfxPrimitivesPolyAllocated = 0;
2802 }
2803
2804 /*
2805 * Update cache variables
2806 */
2807 if ((polyInts==NULL) || (polyAllocated==NULL)) {
2808 gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts;
2809 gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated;
2810 } else {
2811 *polyInts = gfxPrimitivesPolyInts;
2812 *polyAllocated = gfxPrimitivesPolyAllocated;
2813 }
2814
2815 /*
2816 * Check temp array again
2817 */
2818 if (gfxPrimitivesPolyInts==NULL) {
2819 return(-1);
2820 }
2821
2822 /*
2823 * Determine Y maxima
2824 */
2825 miny = vy[0];
2826 maxy = vy[0];
2827 for (i = 1; (i < n); i++) {
2828 if (vy[i] < miny) {
2829 miny = vy[i];
2830 } else if (vy[i] > maxy) {
2831 maxy = vy[i];
2832 }
2833 }
2834
2835 /*
2836 * Draw, scanning y
2837 */
2838 result = 0;
2839 for (y = miny; (y <= maxy); y++) {
2840 ints = 0;
2841 for (i = 0; (i < n); i++) {
2842 if (!i) {
2843 ind1 = n - 1;
2844 ind2 = 0;
2845 } else {
2846 ind1 = i - 1;
2847 ind2 = i;
2848 }
2849 y1 = vy[ind1];
2850 y2 = vy[ind2];
2851 if (y1 < y2) {
2852 x1 = vx[ind1];
2853 x2 = vx[ind2];
2854 } else if (y1 > y2) {
2855 y2 = vy[ind1];
2856 y1 = vy[ind2];
2857 x2 = vx[ind1];
2858 x1 = vx[ind2];
2859 } else {
2860 continue;
2861 }
2862 if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) {
2863 gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1);
2864 }
2865 }
2866
2867 qsort(gfxPrimitivesPolyInts, ints, sizeof(int), _gfxPrimitivesCompareInt);
2868
2869 /*
2870 * Set color
2871 */
2872 result = 0;
2873 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
2874 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
2875
2876 for (i = 0; (i < ints); i += 2) {
2877 xa = gfxPrimitivesPolyInts[i] + 1;
2878 xa = (xa >> 16) + ((xa & 32768) >> 15);
2879 xb = gfxPrimitivesPolyInts[i+1] - 1;
2880 xb = (xb >> 16) + ((xb & 32768) >> 15);
2881 result |= hline(renderer, xa, xb, y);
2882 }
2883 }
2884
2885 return (result);
2886 }
2887
2888 /*!
2889 \brief Draw filled polygon with alpha blending.
2890
2891 \param renderer The renderer to draw on.
2892 \param vx Vertex array containing X coordinates of the points of the filled polygon.
2893 \param vy Vertex array containing Y coordinates of the points of the filled polygon.
2894 \param n Number of points in the vertex array. Minimum number is 3.
2895 \param color The color value of the filled polygon to draw (0xRRGGBBAA).
2896
2897 \returns Returns 0 on success, -1 on failure.
2898 */
filledPolygonColor(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint32 color)2899 int filledPolygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color)
2900 {
2901 Uint8 *c = (Uint8 *)&color;
2902 return filledPolygonRGBAMT(renderer, vx, vy, n, c[0], c[1], c[2], c[3], NULL, NULL);
2903 }
2904
2905 /*!
2906 \brief Draw filled polygon with alpha blending.
2907
2908 \param renderer The renderer to draw on.
2909 \param vx Vertex array containing X coordinates of the points of the filled polygon.
2910 \param vy Vertex array containing Y coordinates of the points of the filled polygon.
2911 \param n Number of points in the vertex array. Minimum number is 3.
2912 \param r The red value of the filled polygon to draw.
2913 \param g The green value of the filled polygon to draw.
2914 \param b The blue value of the filed polygon to draw.
2915 \param a The alpha value of the filled polygon to draw.
2916
2917 \returns Returns 0 on success, -1 on failure.
2918 */
filledPolygonRGBA(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,Uint8 r,Uint8 g,Uint8 b,Uint8 a)2919 int filledPolygonRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2920 {
2921 return filledPolygonRGBAMT(renderer, vx, vy, n, r, g, b, a, NULL, NULL);
2922 }
2923
2924 /* ---- Textured Polygon */
2925
2926 /*!
2927 \brief Internal function to draw a textured horizontal line.
2928
2929 \param renderer The renderer to draw on.
2930 \param x1 X coordinate of the first point (i.e. left) of the line.
2931 \param x2 X coordinate of the second point (i.e. right) of the line.
2932 \param y Y coordinate of the points of the line.
2933 \param texture The texture to retrieve color information from.
2934 \param texture_w The width of the texture.
2935 \param texture_h The height of the texture.
2936 \param texture_dx The X offset for the texture lookup.
2937 \param texture_dy The Y offset for the textured lookup.
2938
2939 \returns Returns 0 on success, -1 on failure.
2940 */
_HLineTextured(SDL_Renderer * renderer,Sint16 x1,Sint16 x2,Sint16 y,SDL_Texture * texture,int texture_w,int texture_h,int texture_dx,int texture_dy)2941 int _HLineTextured(SDL_Renderer *renderer, Sint16 x1, Sint16 x2, Sint16 y, SDL_Texture *texture, int texture_w, int texture_h, int texture_dx, int texture_dy)
2942 {
2943 Sint16 w;
2944 Sint16 xtmp;
2945 int result = 0;
2946 int texture_x_walker;
2947 int texture_y_start;
2948 SDL_Rect source_rect,dst_rect;
2949 int pixels_written,write_width;
2950
2951 /*
2952 * Swap x1, x2 if required to ensure x1<=x2
2953 */
2954 if (x1 > x2) {
2955 xtmp = x1;
2956 x1 = x2;
2957 x2 = xtmp;
2958 }
2959
2960 /*
2961 * Calculate width to draw
2962 */
2963 w = x2 - x1 + 1;
2964
2965 /*
2966 * Determine where in the texture we start drawing
2967 */
2968 texture_x_walker = (x1 - texture_dx) % texture_w;
2969 if (texture_x_walker < 0){
2970 texture_x_walker = texture_w + texture_x_walker ;
2971 }
2972
2973 texture_y_start = (y + texture_dy) % texture_h;
2974 if (texture_y_start < 0){
2975 texture_y_start = texture_h + texture_y_start;
2976 }
2977
2978 /* setup the source rectangle; we are only drawing one horizontal line */
2979 source_rect.y = texture_y_start;
2980 source_rect.x = texture_x_walker;
2981 source_rect.h = 1;
2982
2983 /* we will draw to the current y */
2984 dst_rect.y = y;
2985 dst_rect.h = 1;
2986
2987 /* if there are enough pixels left in the current row of the texture */
2988 /* draw it all at once */
2989 if (w <= texture_w -texture_x_walker){
2990 source_rect.w = w;
2991 source_rect.x = texture_x_walker;
2992 dst_rect.x= x1;
2993 dst_rect.w = source_rect.w;
2994 result = (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0);
2995 } else {
2996 /* we need to draw multiple times */
2997 /* draw the first segment */
2998 pixels_written = texture_w - texture_x_walker;
2999 source_rect.w = pixels_written;
3000 source_rect.x = texture_x_walker;
3001 dst_rect.x= x1;
3002 dst_rect.w = source_rect.w;
3003 result |= (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0);
3004 write_width = texture_w;
3005
3006 /* now draw the rest */
3007 /* set the source x to 0 */
3008 source_rect.x = 0;
3009 while (pixels_written < w){
3010 if (write_width >= w - pixels_written) {
3011 write_width = w - pixels_written;
3012 }
3013 source_rect.w = write_width;
3014 dst_rect.x = x1 + pixels_written;
3015 dst_rect.w = source_rect.w;
3016 result |= (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0);
3017 pixels_written += write_width;
3018 }
3019 }
3020
3021 return result;
3022 }
3023
3024 /*!
3025 \brief Draws a polygon filled with the given texture (Multi-Threading Capable).
3026
3027 \param renderer The renderer to draw on.
3028 \param vx array of x vector components
3029 \param vy array of x vector components
3030 \param n the amount of vectors in the vx and vy array
3031 \param texture the sdl surface to use to fill the polygon
3032 \param texture_dx the offset of the texture relative to the screeen. If you move the polygon 10 pixels
3033 to the left and want the texture to apear the same you need to increase the texture_dx value
3034 \param texture_dy see texture_dx
3035 \param polyInts Preallocated temp array storage for vertex sorting (used for multi-threaded operation)
3036 \param polyAllocated Flag indicating oif the temp array was allocated (used for multi-threaded operation)
3037
3038 \returns Returns 0 on success, -1 on failure.
3039 */
texturedPolygonMT(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,SDL_Surface * texture,int texture_dx,int texture_dy,int ** polyInts,int * polyAllocated)3040 int texturedPolygonMT(SDL_Renderer *renderer, const Sint16 * vx, const Sint16 * vy, int n,
3041 SDL_Surface * texture, int texture_dx, int texture_dy, int **polyInts, int *polyAllocated)
3042 {
3043 int result;
3044 int i;
3045 int y, xa, xb;
3046 int minx,maxx,miny, maxy;
3047 int x1, y1;
3048 int x2, y2;
3049 int ind1, ind2;
3050 int ints;
3051 int *gfxPrimitivesPolyInts = NULL;
3052 int *gfxPrimitivesPolyIntsTemp = NULL;
3053 int gfxPrimitivesPolyAllocated = 0;
3054 SDL_Texture *textureAsTexture = NULL;
3055
3056 /*
3057 * Sanity check number of edges
3058 */
3059 if (n < 3) {
3060 return -1;
3061 }
3062
3063 /*
3064 * Map polygon cache
3065 */
3066 if ((polyInts==NULL) || (polyAllocated==NULL)) {
3067 /* Use global cache */
3068 gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal;
3069 gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal;
3070 } else {
3071 /* Use local cache */
3072 gfxPrimitivesPolyInts = *polyInts;
3073 gfxPrimitivesPolyAllocated = *polyAllocated;
3074 }
3075
3076 /*
3077 * Allocate temp array, only grow array
3078 */
3079 if (!gfxPrimitivesPolyAllocated) {
3080 gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n);
3081 gfxPrimitivesPolyAllocated = n;
3082 } else {
3083 if (gfxPrimitivesPolyAllocated < n) {
3084 gfxPrimitivesPolyIntsTemp = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n);
3085 if (gfxPrimitivesPolyIntsTemp == NULL) {
3086 /* Realloc failed - keeps original memory block, but fails this operation */
3087 return(-1);
3088 }
3089 gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsTemp;
3090 gfxPrimitivesPolyAllocated = n;
3091 }
3092 }
3093
3094 /*
3095 * Check temp array
3096 */
3097 if (gfxPrimitivesPolyInts==NULL) {
3098 gfxPrimitivesPolyAllocated = 0;
3099 }
3100
3101 /*
3102 * Update cache variables
3103 */
3104 if ((polyInts==NULL) || (polyAllocated==NULL)) {
3105 gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts;
3106 gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated;
3107 } else {
3108 *polyInts = gfxPrimitivesPolyInts;
3109 *polyAllocated = gfxPrimitivesPolyAllocated;
3110 }
3111
3112 /*
3113 * Check temp array again
3114 */
3115 if (gfxPrimitivesPolyInts==NULL) {
3116 return(-1);
3117 }
3118
3119 /*
3120 * Determine X,Y minima,maxima
3121 */
3122 miny = vy[0];
3123 maxy = vy[0];
3124 minx = vx[0];
3125 maxx = vx[0];
3126 for (i = 1; (i < n); i++) {
3127 if (vy[i] < miny) {
3128 miny = vy[i];
3129 } else if (vy[i] > maxy) {
3130 maxy = vy[i];
3131 }
3132 if (vx[i] < minx) {
3133 minx = vx[i];
3134 } else if (vx[i] > maxx) {
3135 maxx = vx[i];
3136 }
3137 }
3138
3139 /* Create texture for drawing */
3140 textureAsTexture = SDL_CreateTextureFromSurface(renderer, texture);
3141 if (textureAsTexture == NULL)
3142 {
3143 return -1;
3144 }
3145 SDL_SetTextureBlendMode(textureAsTexture, SDL_BLENDMODE_BLEND);
3146
3147 /*
3148 * Draw, scanning y
3149 */
3150 result = 0;
3151 for (y = miny; (y <= maxy); y++) {
3152 ints = 0;
3153 for (i = 0; (i < n); i++) {
3154 if (!i) {
3155 ind1 = n - 1;
3156 ind2 = 0;
3157 } else {
3158 ind1 = i - 1;
3159 ind2 = i;
3160 }
3161 y1 = vy[ind1];
3162 y2 = vy[ind2];
3163 if (y1 < y2) {
3164 x1 = vx[ind1];
3165 x2 = vx[ind2];
3166 } else if (y1 > y2) {
3167 y2 = vy[ind1];
3168 y1 = vy[ind2];
3169 x2 = vx[ind1];
3170 x1 = vx[ind2];
3171 } else {
3172 continue;
3173 }
3174 if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) {
3175 gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1);
3176 }
3177 }
3178
3179 qsort(gfxPrimitivesPolyInts, ints, sizeof(int), _gfxPrimitivesCompareInt);
3180
3181 for (i = 0; (i < ints); i += 2) {
3182 xa = gfxPrimitivesPolyInts[i] + 1;
3183 xa = (xa >> 16) + ((xa & 32768) >> 15);
3184 xb = gfxPrimitivesPolyInts[i+1] - 1;
3185 xb = (xb >> 16) + ((xb & 32768) >> 15);
3186 result |= _HLineTextured(renderer, xa, xb, y, textureAsTexture, texture->w, texture->h, texture_dx, texture_dy);
3187 }
3188 }
3189
3190 SDL_RenderPresent(renderer);
3191 SDL_DestroyTexture(textureAsTexture);
3192
3193 return (result);
3194 }
3195
3196 /*!
3197 \brief Draws a polygon filled with the given texture.
3198
3199 This standard version is calling multithreaded versions with NULL cache parameters.
3200
3201 \param renderer The renderer to draw on.
3202 \param vx array of x vector components
3203 \param vy array of x vector components
3204 \param n the amount of vectors in the vx and vy array
3205 \param texture the sdl surface to use to fill the polygon
3206 \param texture_dx the offset of the texture relative to the screeen. if you move the polygon 10 pixels
3207 to the left and want the texture to apear the same you need to increase the texture_dx value
3208 \param texture_dy see texture_dx
3209
3210 \returns Returns 0 on success, -1 on failure.
3211 */
texturedPolygon(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,SDL_Surface * texture,int texture_dx,int texture_dy)3212 int texturedPolygon(SDL_Renderer *renderer, const Sint16 * vx, const Sint16 * vy, int n, SDL_Surface *texture, int texture_dx, int texture_dy)
3213 {
3214 /*
3215 * Draw
3216 */
3217 return (texturedPolygonMT(renderer, vx, vy, n, texture, texture_dx, texture_dy, NULL, NULL));
3218 }
3219
3220 /* ---- Character */
3221
3222 /*!
3223 \brief Global cache for NxM pixel font textures created at runtime.
3224 */
3225 static SDL_Texture *gfxPrimitivesFont[256];
3226
3227 /*!
3228 \brief Pointer to the current font data. Default is a 8x8 pixel internal font.
3229 */
3230 static const unsigned char *currentFontdata = gfxPrimitivesFontdata;
3231
3232 /*!
3233 \brief Width of the current font. Default is 8.
3234 */
3235 static Uint32 charWidth = 8;
3236
3237 /*!
3238 \brief Height of the current font. Default is 8.
3239 */
3240 static Uint32 charHeight = 8;
3241
3242 /*!
3243 \brief Width for rendering. Autocalculated.
3244 */
3245 static Uint32 charWidthLocal = 8;
3246
3247 /*!
3248 \brief Height for rendering. Autocalculated.
3249 */
3250 static Uint32 charHeightLocal = 8;
3251
3252 /*!
3253 \brief Pitch of the current font in bytes. Default is 1.
3254 */
3255 static Uint32 charPitch = 1;
3256
3257 /*!
3258 \brief Characters 90deg clockwise rotations. Default is 0. Max is 3.
3259 */
3260 static Uint32 charRotation = 0;
3261
3262 /*!
3263 \brief Character data size in bytes of the current font. Default is 8.
3264 */
3265 static Uint32 charSize = 8;
3266
3267 /*!
3268 \brief Sets or resets the current global font data.
3269
3270 The font data array is organized in follows:
3271 [fontdata] = [character 0][character 1]...[character 255] where
3272 [character n] = [byte 1 row 1][byte 2 row 1]...[byte {pitch} row 1][byte 1 row 2] ...[byte {pitch} row height] where
3273 [byte n] = [bit 0]...[bit 7] where
3274 [bit n] = [0 for transparent pixel|1 for colored pixel]
3275
3276 \param fontdata Pointer to array of font data. Set to NULL, to reset global font to the default 8x8 font.
3277 \param cw Width of character in bytes. Ignored if fontdata==NULL.
3278 \param ch Height of character in bytes. Ignored if fontdata==NULL.
3279 */
gfxPrimitivesSetFont(const void * fontdata,Uint32 cw,Uint32 ch)3280 void gfxPrimitivesSetFont(const void *fontdata, Uint32 cw, Uint32 ch)
3281 {
3282 int i;
3283
3284 if ((fontdata) && (cw) && (ch)) {
3285 currentFontdata = (unsigned char *)fontdata;
3286 charWidth = cw;
3287 charHeight = ch;
3288 } else {
3289 currentFontdata = gfxPrimitivesFontdata;
3290 charWidth = 8;
3291 charHeight = 8;
3292 }
3293
3294 charPitch = (charWidth+7)/8;
3295 charSize = charPitch * charHeight;
3296
3297 /* Maybe flip width/height for rendering */
3298 if ((charRotation==1) || (charRotation==3))
3299 {
3300 charWidthLocal = charHeight;
3301 charHeightLocal = charWidth;
3302 }
3303 else
3304 {
3305 charWidthLocal = charWidth;
3306 charHeightLocal = charHeight;
3307 }
3308
3309 /* Clear character cache */
3310 for (i = 0; i < 256; i++) {
3311 if (gfxPrimitivesFont[i]) {
3312 SDL_DestroyTexture(gfxPrimitivesFont[i]);
3313 gfxPrimitivesFont[i] = NULL;
3314 }
3315 }
3316 }
3317
3318 /*!
3319 \brief Sets current global font character rotation steps.
3320
3321 Default is 0 (no rotation). 1 = 90deg clockwise. 2 = 180deg clockwise. 3 = 270deg clockwise.
3322 Changing the rotation, will reset the character cache.
3323
3324 \param rotation Number of 90deg clockwise steps to rotate
3325 */
gfxPrimitivesSetFontRotation(Uint32 rotation)3326 void gfxPrimitivesSetFontRotation(Uint32 rotation)
3327 {
3328 int i;
3329
3330 rotation = rotation & 3;
3331 if (charRotation != rotation)
3332 {
3333 /* Store rotation */
3334 charRotation = rotation;
3335
3336 /* Maybe flip width/height for rendering */
3337 if ((charRotation==1) || (charRotation==3))
3338 {
3339 charWidthLocal = charHeight;
3340 charHeightLocal = charWidth;
3341 }
3342 else
3343 {
3344 charWidthLocal = charWidth;
3345 charHeightLocal = charHeight;
3346 }
3347
3348 /* Clear character cache */
3349 for (i = 0; i < 256; i++) {
3350 if (gfxPrimitivesFont[i]) {
3351 SDL_DestroyTexture(gfxPrimitivesFont[i]);
3352 gfxPrimitivesFont[i] = NULL;
3353 }
3354 }
3355 }
3356 }
3357
3358 /*!
3359 \brief Draw a character of the currently set font.
3360
3361 \param renderer The Renderer to draw on.
3362 \param x X (horizontal) coordinate of the upper left corner of the character.
3363 \param y Y (vertical) coordinate of the upper left corner of the character.
3364 \param c The character to draw.
3365 \param r The red value of the character to draw.
3366 \param g The green value of the character to draw.
3367 \param b The blue value of the character to draw.
3368 \param a The alpha value of the character to draw.
3369
3370 \returns Returns 0 on success, -1 on failure.
3371 */
characterRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,char c,Uint8 r,Uint8 g,Uint8 b,Uint8 a)3372 int characterRGBA(SDL_Renderer *renderer, Sint16 x, Sint16 y, char c, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
3373 {
3374 SDL_Rect srect;
3375 SDL_Rect drect;
3376 int result;
3377 Uint32 ix, iy;
3378 const unsigned char *charpos;
3379 Uint8 *curpos;
3380 Uint8 patt, mask;
3381 Uint8 *linepos;
3382 Uint32 pitch;
3383 SDL_Surface *character;
3384 SDL_Surface *rotatedCharacter;
3385 Uint32 ci;
3386
3387 /*
3388 * Setup source rectangle
3389 */
3390 srect.x = 0;
3391 srect.y = 0;
3392 srect.w = charWidthLocal;
3393 srect.h = charHeightLocal;
3394
3395 /*
3396 * Setup destination rectangle
3397 */
3398 drect.x = x;
3399 drect.y = y;
3400 drect.w = charWidthLocal;
3401 drect.h = charHeightLocal;
3402
3403 /* Character index in cache */
3404 ci = (unsigned char) c;
3405
3406 /*
3407 * Create new charWidth x charHeight bitmap surface if not already present.
3408 * Might get rotated later.
3409 */
3410 if (gfxPrimitivesFont[ci] == NULL) {
3411 /*
3412 * Redraw character into surface
3413 */
3414 character = SDL_CreateRGBSurface(SDL_SWSURFACE,
3415 charWidth, charHeight, 32,
3416 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
3417 if (character == NULL) {
3418 return (-1);
3419 }
3420
3421 charpos = currentFontdata + ci * charSize;
3422 linepos = (Uint8 *)character->pixels;
3423 pitch = character->pitch;
3424
3425 /*
3426 * Drawing loop
3427 */
3428 patt = 0;
3429 for (iy = 0; iy < charHeight; iy++) {
3430 mask = 0x00;
3431 curpos = linepos;
3432 for (ix = 0; ix < charWidth; ix++) {
3433 if (!(mask >>= 1)) {
3434 patt = *charpos++;
3435 mask = 0x80;
3436 }
3437 if (patt & mask) {
3438 *(Uint32 *)curpos = 0xffffffff;
3439 } else {
3440 *(Uint32 *)curpos = 0;
3441 }
3442 curpos += 4;
3443 }
3444 linepos += pitch;
3445 }
3446
3447 /* Maybe rotate and replace cached image */
3448 if (charRotation>0)
3449 {
3450 rotatedCharacter = rotateSurface90Degrees(character, charRotation);
3451 SDL_FreeSurface(character);
3452 character = rotatedCharacter;
3453 }
3454
3455 /* Convert temp surface into texture */
3456 gfxPrimitivesFont[ci] = SDL_CreateTextureFromSurface(renderer, character);
3457 SDL_FreeSurface(character);
3458
3459 /*
3460 * Check pointer
3461 */
3462 if (gfxPrimitivesFont[ci] == NULL) {
3463 return (-1);
3464 }
3465 }
3466
3467 /*
3468 * Set color
3469 */
3470 result = 0;
3471 result |= SDL_SetTextureColorMod(gfxPrimitivesFont[ci], r, g, b);
3472 result |= SDL_SetTextureAlphaMod(gfxPrimitivesFont[ci], a);
3473
3474 /*
3475 * Draw texture onto destination
3476 */
3477 result |= SDL_RenderCopy(renderer, gfxPrimitivesFont[ci], &srect, &drect);
3478
3479 return (result);
3480 }
3481
3482
3483 /*!
3484 \brief Draw a character of the currently set font.
3485
3486 \param renderer The renderer to draw on.
3487 \param x X (horizontal) coordinate of the upper left corner of the character.
3488 \param y Y (vertical) coordinate of the upper left corner of the character.
3489 \param c The character to draw.
3490 \param color The color value of the character to draw (0xRRGGBBAA).
3491
3492 \returns Returns 0 on success, -1 on failure.
3493 */
characterColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,char c,Uint32 color)3494 int characterColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, char c, Uint32 color)
3495 {
3496 Uint8 *co = (Uint8 *)&color;
3497 return characterRGBA(renderer, x, y, c, co[0], co[1], co[2], co[3]);
3498 }
3499
3500
3501 /*!
3502 \brief Draw a string in the currently set font.
3503
3504 The spacing between consequtive characters in the string is the fixed number of pixels
3505 of the character width of the current global font.
3506
3507 \param renderer The renderer to draw on.
3508 \param x X (horizontal) coordinate of the upper left corner of the string.
3509 \param y Y (vertical) coordinate of the upper left corner of the string.
3510 \param s The string to draw.
3511 \param color The color value of the string to draw (0xRRGGBBAA).
3512
3513 \returns Returns 0 on success, -1 on failure.
3514 */
stringColor(SDL_Renderer * renderer,Sint16 x,Sint16 y,const char * s,Uint32 color)3515 int stringColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint32 color)
3516 {
3517 Uint8 *c = (Uint8 *)&color;
3518 return stringRGBA(renderer, x, y, s, c[0], c[1], c[2], c[3]);
3519 }
3520
3521 /*!
3522 \brief Draw a string in the currently set font.
3523
3524 \param renderer The renderer to draw on.
3525 \param x X (horizontal) coordinate of the upper left corner of the string.
3526 \param y Y (vertical) coordinate of the upper left corner of the string.
3527 \param s The string to draw.
3528 \param r The red value of the string to draw.
3529 \param g The green value of the string to draw.
3530 \param b The blue value of the string to draw.
3531 \param a The alpha value of the string to draw.
3532
3533 \returns Returns 0 on success, -1 on failure.
3534 */
stringRGBA(SDL_Renderer * renderer,Sint16 x,Sint16 y,const char * s,Uint8 r,Uint8 g,Uint8 b,Uint8 a)3535 int stringRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
3536 {
3537 int result = 0;
3538 Sint16 curx = x;
3539 Sint16 cury = y;
3540 const char *curchar = s;
3541
3542 while (*curchar && !result) {
3543 result |= characterRGBA(renderer, curx, cury, *curchar, r, g, b, a);
3544 switch (charRotation)
3545 {
3546 case 0:
3547 curx += charWidthLocal;
3548 break;
3549 case 2:
3550 curx -= charWidthLocal;
3551 break;
3552 case 1:
3553 cury += charHeightLocal;
3554 break;
3555 case 3:
3556 cury -= charHeightLocal;
3557 break;
3558 }
3559 curchar++;
3560 }
3561
3562 return (result);
3563 }
3564
3565 /* ---- Bezier curve */
3566
3567 /*!
3568 \brief Internal function to calculate bezier interpolator of data array with ndata values at position 't'.
3569
3570 \param data Array of values.
3571 \param ndata Size of array.
3572 \param t Position for which to calculate interpolated value. t should be between [0, ndata].
3573
3574 \returns Interpolated value at position t, value[0] when t<0, value[n-1] when t>n.
3575 */
_evaluateBezier(double * data,int ndata,double t)3576 double _evaluateBezier (double *data, int ndata, double t)
3577 {
3578 double mu, result;
3579 int n,k,kn,nn,nkn;
3580 double blend,muk,munk;
3581
3582 /* Sanity check bounds */
3583 if (t<0.0) {
3584 return(data[0]);
3585 }
3586 if (t>=(double)ndata) {
3587 return(data[ndata-1]);
3588 }
3589
3590 /* Adjust t to the range 0.0 to 1.0 */
3591 mu=t/(double)ndata;
3592
3593 /* Calculate interpolate */
3594 n=ndata-1;
3595 result=0.0;
3596 muk = 1;
3597 munk = pow(1-mu,(double)n);
3598 for (k=0;k<=n;k++) {
3599 nn = n;
3600 kn = k;
3601 nkn = n - k;
3602 blend = muk * munk;
3603 muk *= mu;
3604 munk /= (1-mu);
3605 while (nn >= 1) {
3606 blend *= nn;
3607 nn--;
3608 if (kn > 1) {
3609 blend /= (double)kn;
3610 kn--;
3611 }
3612 if (nkn > 1) {
3613 blend /= (double)nkn;
3614 nkn--;
3615 }
3616 }
3617 result += data[k] * blend;
3618 }
3619
3620 return (result);
3621 }
3622
3623 /*!
3624 \brief Draw a bezier curve with alpha blending.
3625
3626 \param renderer The renderer to draw on.
3627 \param vx Vertex array containing X coordinates of the points of the bezier curve.
3628 \param vy Vertex array containing Y coordinates of the points of the bezier curve.
3629 \param n Number of points in the vertex array. Minimum number is 3.
3630 \param s Number of steps for the interpolation. Minimum number is 2.
3631 \param color The color value of the bezier curve to draw (0xRRGGBBAA).
3632
3633 \returns Returns 0 on success, -1 on failure.
3634 */
bezierColor(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,int s,Uint32 color)3635 int bezierColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, int s, Uint32 color)
3636 {
3637 Uint8 *c = (Uint8 *)&color;
3638 return bezierRGBA(renderer, vx, vy, n, s, c[0], c[1], c[2], c[3]);
3639 }
3640
3641 /*!
3642 \brief Draw a bezier curve with alpha blending.
3643
3644 \param renderer The renderer to draw on.
3645 \param vx Vertex array containing X coordinates of the points of the bezier curve.
3646 \param vy Vertex array containing Y coordinates of the points of the bezier curve.
3647 \param n Number of points in the vertex array. Minimum number is 3.
3648 \param s Number of steps for the interpolation. Minimum number is 2.
3649 \param r The red value of the bezier curve to draw.
3650 \param g The green value of the bezier curve to draw.
3651 \param b The blue value of the bezier curve to draw.
3652 \param a The alpha value of the bezier curve to draw.
3653
3654 \returns Returns 0 on success, -1 on failure.
3655 */
bezierRGBA(SDL_Renderer * renderer,const Sint16 * vx,const Sint16 * vy,int n,int s,Uint8 r,Uint8 g,Uint8 b,Uint8 a)3656 int bezierRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, int s, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
3657 {
3658 int result;
3659 int i;
3660 double *x, *y, t, stepsize;
3661 Sint16 x1, y1, x2, y2;
3662
3663 /*
3664 * Sanity check
3665 */
3666 if (n < 3) {
3667 return (-1);
3668 }
3669 if (s < 2) {
3670 return (-1);
3671 }
3672
3673 /*
3674 * Variable setup
3675 */
3676 stepsize=(double)1.0/(double)s;
3677
3678 /* Transfer vertices into float arrays */
3679 if ((x=(double *)malloc(sizeof(double)*(n+1)))==NULL) {
3680 return(-1);
3681 }
3682 if ((y=(double *)malloc(sizeof(double)*(n+1)))==NULL) {
3683 free(x);
3684 return(-1);
3685 }
3686 for (i=0; i<n; i++) {
3687 x[i]=(double)vx[i];
3688 y[i]=(double)vy[i];
3689 }
3690 x[n]=(double)vx[0];
3691 y[n]=(double)vy[0];
3692
3693 /*
3694 * Set color
3695 */
3696 result = 0;
3697 result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND);
3698 result |= SDL_SetRenderDrawColor(renderer, r, g, b, a);
3699
3700 /*
3701 * Draw
3702 */
3703 t=0.0;
3704 x1=(Sint16)lrint(_evaluateBezier(x,n+1,t));
3705 y1=(Sint16)lrint(_evaluateBezier(y,n+1,t));
3706 for (i = 0; i <= (n*s); i++) {
3707 t += stepsize;
3708 x2=(Sint16)_evaluateBezier(x,n,t);
3709 y2=(Sint16)_evaluateBezier(y,n,t);
3710 result |= line(renderer, x1, y1, x2, y2);
3711 x1 = x2;
3712 y1 = y2;
3713 }
3714
3715 /* Clean up temporary array */
3716 free(x);
3717 free(y);
3718
3719 return (result);
3720 }
3721
3722
3723 /*!
3724 \brief Draw a thick line with alpha blending.
3725
3726 \param renderer The renderer to draw on.
3727 \param x1 X coordinate of the first point of the line.
3728 \param y1 Y coordinate of the first point of the line.
3729 \param x2 X coordinate of the second point of the line.
3730 \param y2 Y coordinate of the second point of the line.
3731 \param width Width of the line in pixels. Must be >0.
3732 \param color The color value of the line to draw (0xRRGGBBAA).
3733
3734 \returns Returns 0 on success, -1 on failure.
3735 */
thickLineColor(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 width,Uint32 color)3736 int thickLineColor(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint32 color)
3737 {
3738 Uint8 *c = (Uint8 *)&color;
3739 return thickLineRGBA(renderer, x1, y1, x2, y2, width, c[0], c[1], c[2], c[3]);
3740 }
3741
3742 /*!
3743 \brief Draw a thick line with alpha blending.
3744
3745 \param renderer The renderer to draw on.
3746 \param x1 X coordinate of the first point of the line.
3747 \param y1 Y coordinate of the first point of the line.
3748 \param x2 X coordinate of the second point of the line.
3749 \param y2 Y coordinate of the second point of the line.
3750 \param width Width of the line in pixels. Must be >0.
3751 \param r The red value of the character to draw.
3752 \param g The green value of the character to draw.
3753 \param b The blue value of the character to draw.
3754 \param a The alpha value of the character to draw.
3755
3756 \returns Returns 0 on success, -1 on failure.
3757 */
thickLineRGBA(SDL_Renderer * renderer,Sint16 x1,Sint16 y1,Sint16 x2,Sint16 y2,Uint8 width,Uint8 r,Uint8 g,Uint8 b,Uint8 a)3758 int thickLineRGBA(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
3759 {
3760 int wh;
3761 double dx, dy, dx1, dy1, dx2, dy2;
3762 double l, wl2, nx, ny, ang, adj;
3763 Sint16 px[4], py[4];
3764
3765 if (renderer == NULL) {
3766 return -1;
3767 }
3768
3769 if (width < 1) {
3770 return -1;
3771 }
3772
3773 /* Special case: thick "point" */
3774 if ((x1 == x2) && (y1 == y2)) {
3775 wh = width / 2;
3776 return boxRGBA(renderer, x1 - wh, y1 - wh, x2 + width, y2 + width, r, g, b, a);
3777 }
3778
3779 /* Special case: width == 1 */
3780 if (width == 1) {
3781 return lineRGBA(renderer, x1, y1, x2, y2, r, g, b, a);
3782 }
3783
3784 /* Calculate offsets for sides */
3785 dx = (double)(x2 - x1);
3786 dy = (double)(y2 - y1);
3787 l = SDL_sqrt(dx*dx + dy*dy);
3788 ang = SDL_atan2(dx, dy);
3789 adj = 0.1 + 0.9 * SDL_fabs(SDL_cos(2.0 * ang));
3790 wl2 = ((double)width - adj)/(2.0 * l);
3791 nx = dx * wl2;
3792 ny = dy * wl2;
3793
3794 /* Build polygon */
3795 dx1 = (double)x1;
3796 dy1 = (double)y1;
3797 dx2 = (double)x2;
3798 dy2 = (double)y2;
3799 px[0] = (Sint16)(dx1 + ny);
3800 px[1] = (Sint16)(dx1 - ny);
3801 px[2] = (Sint16)(dx2 - ny);
3802 px[3] = (Sint16)(dx2 + ny);
3803 py[0] = (Sint16)(dy1 - nx);
3804 py[1] = (Sint16)(dy1 + nx);
3805 py[2] = (Sint16)(dy2 + nx);
3806 py[3] = (Sint16)(dy2 - nx);
3807
3808 /* Draw polygon */
3809 return filledPolygonRGBA(renderer, px, py, 4, r, g, b, a);
3810 }
3811