From f5d03b9be6a7fc677174199a5bcf05063cc26b0e Mon Sep 17 00:00:00 2001 From: Frank DeMarco Date: Thu, 6 Aug 2020 18:54:50 -0400 Subject: [PATCH] - extended version of sdl gfx primitives - drag left and right edge box - complete segment member functions - extension functions: get segments, get relative step, populate pixel array --- demo/Demo.cpp | 2 +- lib/sdl2-gfx/SDL2_gfxPrimitives.c | 9046 +++++++++++++++++------------ lib/sdl2-gfx/SDL2_gfxPrimitives.h | 35 +- src/Box.cpp | 87 +- src/Box.hpp | 16 +- src/Configuration.cpp | 3 +- src/Game.cpp | 12 +- src/Game.hpp | 3 +- src/Node.cpp | 20 +- src/Node.hpp | 3 + src/Segment.cpp | 32 +- src/Segment.hpp | 9 +- src/Sprite.cpp | 59 +- src/Sprite.hpp | 10 + src/extension.cpp | 99 +- src/extension.hpp | 12 +- 16 files changed, 5585 insertions(+), 3863 deletions(-) diff --git a/demo/Demo.cpp b/demo/Demo.cpp index 4d57eee..92f2aa9 100644 --- a/demo/Demo.cpp +++ b/demo/Demo.cpp @@ -11,7 +11,7 @@ dict with metadata, move added sprite locations by offset when location is changed, gradients, level select code input, logging, variable screen resolution, debug display, loading wheel animation, shadowed sprite, separate - update and draw, sprite movement cage + update and draw, sprite movement cage, multiple windows, multiple renderers :) SWEATY HANDS :) OILY SNACKS :) AND BAD HYGIENE :) diff --git a/lib/sdl2-gfx/SDL2_gfxPrimitives.c b/lib/sdl2-gfx/SDL2_gfxPrimitives.c index a7a5d0b..f3b46fa 100755 --- a/lib/sdl2-gfx/SDL2_gfxPrimitives.c +++ b/lib/sdl2-gfx/SDL2_gfxPrimitives.c @@ -1,3810 +1,5236 @@ -/* - -SDL2_gfxPrimitives.c: graphics primitives for SDL2 renderers - -Copyright (C) 2012-2014 Andreas Schiffler - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not -claim that you wrote the original software. If you use this software -in a product, an acknowledgment in the product documentation would be -appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not be -misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. - -Andreas Schiffler -- aschiffler at ferzkopp dot net - -*/ - -#include -#include -#include -#include - -#include "SDL2_gfxPrimitives.h" -#include "SDL2_rotozoom.h" -#include "SDL2_gfxPrimitives_font.h" - -/* ---- Structures */ - -/*! -\brief The structure passed to the internal Bresenham iterator. -*/ -typedef struct { - Sint16 x, y; - int dx, dy, s1, s2, swapdir, error; - Uint32 count; -} SDL2_gfxBresenhamIterator; - -/*! -\brief The structure passed to the internal Murphy iterator. -*/ -typedef struct { - SDL_Renderer *renderer; - int u, v; /* delta x , delta y */ - int ku, kt, kv, kd; /* loop constants */ - int oct2; - int quad4; - Sint16 last1x, last1y, last2x, last2y, first1x, first1y, first2x, first2y, tempx, tempy; -} SDL2_gfxMurphyIterator; - -/* ---- Pixel */ - -/*! -\brief Draw pixel in currently set color. - -\param renderer The renderer to draw on. -\param x X (horizontal) coordinate of the pixel. -\param y Y (vertical) coordinate of the pixel. - -\returns Returns 0 on success, -1 on failure. -*/ -int pixel(SDL_Renderer *renderer, Sint16 x, Sint16 y) -{ - return SDL_RenderDrawPoint(renderer, x, y); -} - -/*! -\brief Draw pixel with blending enabled if a<255. - -\param renderer The renderer to draw on. -\param x X (horizontal) coordinate of the pixel. -\param y Y (vertical) coordinate of the pixel. -\param color The color value of the pixel to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int pixelColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return pixelRGBA(renderer, x, y, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw pixel with blending enabled if a<255. - -\param renderer The renderer to draw on. -\param x X (horizontal) coordinate of the pixel. -\param y Y (vertical) coordinate of the pixel. -\param r The red color value of the pixel to draw. -\param g The green color value of the pixel to draw. -\param b The blue color value of the pixel to draw. -\param a The alpha value of the pixel to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int pixelRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - result |= SDL_RenderDrawPoint(renderer, x, y); - return result; -} - -/*! -\brief Draw pixel with blending enabled and using alpha weight on color. - -\param renderer The renderer to draw on. -\param x The horizontal coordinate of the pixel. -\param y The vertical position of the pixel. -\param r The red color value of the pixel to draw. -\param g The green color value of the pixel to draw. -\param b The blue color value of the pixel to draw. -\param a The alpha value of the pixel to draw. -\param weight The weight multiplied into the alpha value of the pixel. - -\returns Returns 0 on success, -1 on failure. -*/ -int pixelRGBAWeight(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint32 weight) -{ - /* - * Modify Alpha by weight - */ - Uint32 ax = a; - ax = ((ax * weight) >> 8); - if (ax > 255) { - a = 255; - } else { - a = (Uint8)(ax & 0x000000ff); - } - - return pixelRGBA(renderer, x, y, r, g, b, a); -} - -/* ---- Hline */ - -/*! -\brief Draw horizontal line in currently set color - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. left) of the line. -\param x2 X coordinate of the second point (i.e. right) of the line. -\param y Y coordinate of the points of the line. - -\returns Returns 0 on success, -1 on failure. -*/ -int hline(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y) -{ - return SDL_RenderDrawLine(renderer, x1, y, x2, y);; -} - - -/*! -\brief Draw horizontal line with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. left) of the line. -\param x2 X coordinate of the second point (i.e. right) of the line. -\param y Y coordinate of the points of the line. -\param color The color value of the line to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int hlineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return hlineRGBA(renderer, x1, x2, y, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw horizontal line with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. left) of the line. -\param x2 X coordinate of the second point (i.e. right) of the line. -\param y Y coordinate of the points of the line. -\param r The red value of the line to draw. -\param g The green value of the line to draw. -\param b The blue value of the line to draw. -\param a The alpha value of the line to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int hlineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - result |= SDL_RenderDrawLine(renderer, x1, y, x2, y); - return result; -} - -/* ---- Vline */ - -/*! -\brief Draw vertical line in currently set color - -\param renderer The renderer to draw on. -\param x X coordinate of points of the line. -\param y1 Y coordinate of the first point (i.e. top) of the line. -\param y2 Y coordinate of the second point (i.e. bottom) of the line. - -\returns Returns 0 on success, -1 on failure. -*/ -int vline(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2) -{ - return SDL_RenderDrawLine(renderer, x, y1, x, y2);; -} - -/*! -\brief Draw vertical line with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the points of the line. -\param y1 Y coordinate of the first point (i.e. top) of the line. -\param y2 Y coordinate of the second point (i.e. bottom) of the line. -\param color The color value of the line to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int vlineColor(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return vlineRGBA(renderer, x, y1, y2, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw vertical line with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the points of the line. -\param y1 Y coordinate of the first point (i.e. top) of the line. -\param y2 Y coordinate of the second point (i.e. bottom) of the line. -\param r The red value of the line to draw. -\param g The green value of the line to draw. -\param b The blue value of the line to draw. -\param a The alpha value of the line to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int vlineRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - result |= SDL_RenderDrawLine(renderer, x, y1, x, y2); - return result; -} - -/* ---- Rectangle */ - -/*! -\brief Draw rectangle with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the rectangle. -\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. -\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. -\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. -\param color The color value of the rectangle to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int rectangleColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return rectangleRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw rectangle with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the rectangle. -\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. -\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. -\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. -\param r The red value of the rectangle to draw. -\param g The green value of the rectangle to draw. -\param b The blue value of the rectangle to draw. -\param a The alpha value of the rectangle to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int rectangleRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result; - Sint16 tmp; - SDL_Rect rect; - - /* - * Test for special cases of straight lines or single point - */ - if (x1 == x2) { - if (y1 == y2) { - return (pixelRGBA(renderer, x1, y1, r, g, b, a)); - } else { - return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); - } - } else { - if (y1 == y2) { - return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); - } - } - - /* - * Swap x1, x2 if required - */ - if (x1 > x2) { - tmp = x1; - x1 = x2; - x2 = tmp; - } - - /* - * Swap y1, y2 if required - */ - if (y1 > y2) { - tmp = y1; - y1 = y2; - y2 = tmp; - } - - /* - * Create destination rect - */ - rect.x = x1; - rect.y = y1; - rect.w = x2 - x1; - rect.h = y2 - y1; - - /* - * Draw - */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - result |= SDL_RenderDrawRect(renderer, &rect); - return result; -} - -/* ---- Rounded Rectangle */ - -/*! -\brief Draw rounded-corner rectangle with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the rectangle. -\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. -\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. -\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. -\param rad The radius of the corner arc. -\param color The color value of the rectangle to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int roundedRectangleColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return roundedRectangleRGBA(renderer, x1, y1, x2, y2, rad, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw rounded-corner rectangle with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the rectangle. -\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. -\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. -\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. -\param rad The radius of the corner arc. -\param r The red value of the rectangle to draw. -\param g The green value of the rectangle to draw. -\param b The blue value of the rectangle to draw. -\param a The alpha value of the rectangle to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int roundedRectangleRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result = 0; - Sint16 tmp; - Sint16 w, h; - Sint16 xx1, xx2; - Sint16 yy1, yy2; - - /* - * Check renderer - */ - if (renderer == NULL) - { - return -1; - } - - /* - * Check radius vor valid range - */ - if (rad < 0) { - return -1; - } - - /* - * Special case - no rounding - */ - if (rad <= 1) { - return rectangleRGBA(renderer, x1, y1, x2, y2, r, g, b, a); - } - - /* - * Test for special cases of straight lines or single point - */ - if (x1 == x2) { - if (y1 == y2) { - return (pixelRGBA(renderer, x1, y1, r, g, b, a)); - } else { - return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); - } - } else { - if (y1 == y2) { - return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); - } - } - - /* - * Swap x1, x2 if required - */ - if (x1 > x2) { - tmp = x1; - x1 = x2; - x2 = tmp; - } - - /* - * Swap y1, y2 if required - */ - if (y1 > y2) { - tmp = y1; - y1 = y2; - y2 = tmp; - } - - /* - * Calculate width&height - */ - w = x2 - x1; - h = y2 - y1; - - /* - * Maybe adjust radius - */ - if ((rad * 2) > w) - { - rad = w / 2; - } - if ((rad * 2) > h) - { - rad = h / 2; - } - - /* - * Draw corners - */ - xx1 = x1 + rad; - xx2 = x2 - rad; - yy1 = y1 + rad; - yy2 = y2 - rad; - result |= arcRGBA(renderer, xx1, yy1, rad, 180, 270, r, g, b, a); - result |= arcRGBA(renderer, xx2, yy1, rad, 270, 360, r, g, b, a); - result |= arcRGBA(renderer, xx1, yy2, rad, 90, 180, r, g, b, a); - result |= arcRGBA(renderer, xx2, yy2, rad, 0, 90, r, g, b, a); - - /* - * Draw lines - */ - if (xx1 <= xx2) { - result |= hlineRGBA(renderer, xx1, xx2, y1, r, g, b, a); - result |= hlineRGBA(renderer, xx1, xx2, y2, r, g, b, a); - } - if (yy1 <= yy2) { - result |= vlineRGBA(renderer, x1, yy1, yy2, r, g, b, a); - result |= vlineRGBA(renderer, x2, yy1, yy2, r, g, b, a); - } - - return result; -} - -/* ---- Rounded Box */ - -/*! -\brief Draw rounded-corner box (filled rectangle) with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the box. -\param y1 Y coordinate of the first point (i.e. top right) of the box. -\param x2 X coordinate of the second point (i.e. bottom left) of the box. -\param y2 Y coordinate of the second point (i.e. bottom left) of the box. -\param rad The radius of the corner arcs of the box. -\param color The color value of the box to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int roundedBoxColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return roundedBoxRGBA(renderer, x1, y1, x2, y2, rad, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw rounded-corner box (filled rectangle) with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the box. -\param y1 Y coordinate of the first point (i.e. top right) of the box. -\param x2 X coordinate of the second point (i.e. bottom left) of the box. -\param y2 Y coordinate of the second point (i.e. bottom left) of the box. -\param rad The radius of the corner arcs of the box. -\param r The red value of the box to draw. -\param g The green value of the box to draw. -\param b The blue value of the box to draw. -\param a The alpha value of the box to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int roundedBoxRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, - Sint16 y2, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result; - Sint16 w, h, r2, tmp; - Sint16 cx = 0; - Sint16 cy = rad; - Sint16 ocx = (Sint16) 0xffff; - Sint16 ocy = (Sint16) 0xffff; - Sint16 df = 1 - rad; - Sint16 d_e = 3; - Sint16 d_se = -2 * rad + 5; - Sint16 xpcx, xmcx, xpcy, xmcy; - Sint16 ypcy, ymcy, ypcx, ymcx; - Sint16 x, y, dx, dy; - - /* - * Check destination renderer - */ - if (renderer == NULL) - { - return -1; - } - - /* - * Check radius vor valid range - */ - if (rad < 0) { - return -1; - } - - /* - * Special case - no rounding - */ - if (rad <= 1) { - return boxRGBA(renderer, x1, y1, x2, y2, r, g, b, a); - } - - /* - * Test for special cases of straight lines or single point - */ - if (x1 == x2) { - if (y1 == y2) { - return (pixelRGBA(renderer, x1, y1, r, g, b, a)); - } else { - return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); - } - } else { - if (y1 == y2) { - return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); - } - } - - /* - * Swap x1, x2 if required - */ - if (x1 > x2) { - tmp = x1; - x1 = x2; - x2 = tmp; - } - - /* - * Swap y1, y2 if required - */ - if (y1 > y2) { - tmp = y1; - y1 = y2; - y2 = tmp; - } - - /* - * Calculate width&height - */ - w = x2 - x1 + 1; - h = y2 - y1 + 1; - - /* - * Maybe adjust radius - */ - r2 = rad + rad; - if (r2 > w) - { - rad = w / 2; - r2 = rad + rad; - } - if (r2 > h) - { - rad = h / 2; - } - - /* Setup filled circle drawing for corners */ - x = x1 + rad; - y = y1 + rad; - dx = x2 - x1 - rad - rad; - dy = y2 - y1 - rad - rad; - - /* - * Set color - */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - - /* - * Draw corners - */ - do { - xpcx = x + cx; - xmcx = x - cx; - xpcy = x + cy; - xmcy = x - cy; - if (ocy != cy) { - if (cy > 0) { - ypcy = y + cy; - ymcy = y - cy; - result |= hline(renderer, xmcx, xpcx + dx, ypcy + dy); - result |= hline(renderer, xmcx, xpcx + dx, ymcy); - } else { - result |= hline(renderer, xmcx, xpcx + dx, y); - } - ocy = cy; - } - if (ocx != cx) { - if (cx != cy) { - if (cx > 0) { - ypcx = y + cx; - ymcx = y - cx; - result |= hline(renderer, xmcy, xpcy + dx, ymcx); - result |= hline(renderer, xmcy, xpcy + dx, ypcx + dy); - } else { - result |= hline(renderer, xmcy, xpcy + dx, y); - } - } - ocx = cx; - } - - /* - * Update - */ - if (df < 0) { - df += d_e; - d_e += 2; - d_se += 2; - } else { - df += d_se; - d_e += 2; - d_se += 4; - cy--; - } - cx++; - } while (cx <= cy); - - /* Inside */ - if (dx > 0 && dy > 0) { - result |= boxRGBA(renderer, x1, y1 + rad + 1, x2, y2 - rad, r, g, b, a); - } - - return (result); -} - -/* ---- Box */ - -/*! -\brief Draw box (filled rectangle) with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the box. -\param y1 Y coordinate of the first point (i.e. top right) of the box. -\param x2 X coordinate of the second point (i.e. bottom left) of the box. -\param y2 Y coordinate of the second point (i.e. bottom left) of the box. -\param color The color value of the box to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int boxColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return boxRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw box (filled rectangle) with blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. top right) of the box. -\param y1 Y coordinate of the first point (i.e. top right) of the box. -\param x2 X coordinate of the second point (i.e. bottom left) of the box. -\param y2 Y coordinate of the second point (i.e. bottom left) of the box. -\param r The red value of the box to draw. -\param g The green value of the box to draw. -\param b The blue value of the box to draw. -\param a The alpha value of the box to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int boxRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result; - Sint16 tmp; - SDL_Rect rect; - - /* - * Test for special cases of straight lines or single point - */ - if (x1 == x2) { - if (y1 == y2) { - return (pixelRGBA(renderer, x1, y1, r, g, b, a)); - } else { - return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); - } - } else { - if (y1 == y2) { - return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); - } - } - - /* - * Swap x1, x2 if required - */ - if (x1 > x2) { - tmp = x1; - x1 = x2; - x2 = tmp; - } - - /* - * Swap y1, y2 if required - */ - if (y1 > y2) { - tmp = y1; - y1 = y2; - y2 = tmp; - } - - /* - * Create destination rect - */ - rect.x = x1; - rect.y = y1; - rect.w = x2 - x1 + 1; - rect.h = y2 - y1 + 1; - - /* - * Draw - */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - result |= SDL_RenderFillRect(renderer, &rect); - return result; -} - -/* ----- Line */ - -/*! -\brief Draw line with alpha blending using the currently set color. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the line. -\param y1 Y coordinate of the first point of the line. -\param x2 X coordinate of the second point of the line. -\param y2 Y coordinate of the second point of the line. - -\returns Returns 0 on success, -1 on failure. -*/ -int line(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2) -{ - /* - * Draw - */ - return SDL_RenderDrawLine(renderer, x1, y1, x2, y2); -} - -/*! -\brief Draw line with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the line. -\param y1 Y coordinate of the first point of the line. -\param x2 X coordinate of the second point of the line. -\param y2 Y coordinate of the seond point of the line. -\param color The color value of the line to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int lineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return lineRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw line with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the line. -\param y1 Y coordinate of the first point of the line. -\param x2 X coordinate of the second point of the line. -\param y2 Y coordinate of the second point of the line. -\param r The red value of the line to draw. -\param g The green value of the line to draw. -\param b The blue value of the line to draw. -\param a The alpha value of the line to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int lineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - /* - * Draw - */ - int result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - result |= SDL_RenderDrawLine(renderer, x1, y1, x2, y2); - return result; -} - -/* ---- AA Line */ - -#define AAlevels 256 -#define AAbits 8 - -/*! -\brief Internal function to draw anti-aliased line with alpha blending and endpoint control. - -This implementation of the Wu antialiasing code is based on Mike Abrash's -DDJ article which was reprinted as Chapter 42 of his Graphics Programming -Black Book, but has been optimized to work with SDL and utilizes 32-bit -fixed-point arithmetic by A. Schiffler. The endpoint control allows the -supression to draw the last pixel useful for rendering continous aa-lines -with alpha<255. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the aa-line. -\param y1 Y coordinate of the first point of the aa-line. -\param x2 X coordinate of the second point of the aa-line. -\param y2 Y coordinate of the second point of the aa-line. -\param r The red value of the aa-line to draw. -\param g The green value of the aa-line to draw. -\param b The blue value of the aa-line to draw. -\param a The alpha value of the aa-line to draw. -\param draw_endpoint Flag indicating if the endpoint should be drawn; draw if non-zero. - -\returns Returns 0 on success, -1 on failure. -*/ -int _aalineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a, int draw_endpoint) -{ - Sint32 xx0, yy0, xx1, yy1; - int result; - Uint32 intshift, erracc, erradj; - Uint32 erracctmp, wgt, wgtcompmask; - int dx, dy, tmp, xdir, y0p1, x0pxdir; - - /* - * Keep on working with 32bit numbers - */ - xx0 = x1; - yy0 = y1; - xx1 = x2; - yy1 = y2; - - /* - * Reorder points to make dy positive - */ - if (yy0 > yy1) { - tmp = yy0; - yy0 = yy1; - yy1 = tmp; - tmp = xx0; - xx0 = xx1; - xx1 = tmp; - } - - /* - * Calculate distance - */ - dx = xx1 - xx0; - dy = yy1 - yy0; - - /* - * Adjust for negative dx and set xdir - */ - if (dx >= 0) { - xdir = 1; - } else { - xdir = -1; - dx = (-dx); - } - - /* - * Check for special cases - */ - if (dx == 0) { - /* - * Vertical line - */ - if (draw_endpoint) - { - return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); - } else { - if (dy > 0) { - return (vlineRGBA(renderer, x1, yy0, yy0+dy, r, g, b, a)); - } else { - return (pixelRGBA(renderer, x1, y1, r, g, b, a)); - } - } - } else if (dy == 0) { - /* - * Horizontal line - */ - if (draw_endpoint) - { - return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); - } else { - if (dx > 0) { - return (hlineRGBA(renderer, xx0, xx0+(xdir*dx), y1, r, g, b, a)); - } else { - return (pixelRGBA(renderer, x1, y1, r, g, b, a)); - } - } - } else if ((dx == dy) && (draw_endpoint)) { - /* - * Diagonal line (with endpoint) - */ - return (lineRGBA(renderer, x1, y1, x2, y2, r, g, b, a)); - } - - - /* - * Line is not horizontal, vertical or diagonal (with endpoint) - */ - result = 0; - - /* - * Zero accumulator - */ - erracc = 0; - - /* - * # of bits by which to shift erracc to get intensity level - */ - intshift = 32 - AAbits; - - /* - * Mask used to flip all bits in an intensity weighting - */ - wgtcompmask = AAlevels - 1; - - /* - * Draw the initial pixel in the foreground color - */ - result |= pixelRGBA(renderer, x1, y1, r, g, b, a); - - /* - * x-major or y-major? - */ - if (dy > dx) { - - /* - * y-major. Calculate 16-bit fixed point fractional part of a pixel that - * X advances every time Y advances 1 pixel, truncating the result so that - * we won't overrun the endpoint along the X axis - */ - /* - * Not-so-portable version: erradj = ((Uint64)dx << 32) / (Uint64)dy; - */ - erradj = ((dx << 16) / dy) << 16; - - /* - * draw all pixels other than the first and last - */ - x0pxdir = xx0 + xdir; - while (--dy) { - erracctmp = erracc; - erracc += erradj; - if (erracc <= erracctmp) { - /* - * rollover in error accumulator, x coord advances - */ - xx0 = x0pxdir; - x0pxdir += xdir; - } - yy0++; /* y-major so always advance Y */ - - /* - * the AAbits most significant bits of erracc give us the intensity - * weighting for this pixel, and the complement of the weighting for - * the paired pixel. - */ - wgt = (erracc >> intshift) & 255; - result |= pixelRGBAWeight (renderer, xx0, yy0, r, g, b, a, 255 - wgt); - result |= pixelRGBAWeight (renderer, x0pxdir, yy0, r, g, b, a, wgt); - } - - } else { - - /* - * x-major line. Calculate 16-bit fixed-point fractional part of a pixel - * that Y advances each time X advances 1 pixel, truncating the result so - * that we won't overrun the endpoint along the X axis. - */ - /* - * Not-so-portable version: erradj = ((Uint64)dy << 32) / (Uint64)dx; - */ - erradj = ((dy << 16) / dx) << 16; - - /* - * draw all pixels other than the first and last - */ - y0p1 = yy0 + 1; - while (--dx) { - - erracctmp = erracc; - erracc += erradj; - if (erracc <= erracctmp) { - /* - * Accumulator turned over, advance y - */ - yy0 = y0p1; - y0p1++; - } - xx0 += xdir; /* x-major so always advance X */ - /* - * the AAbits most significant bits of erracc give us the intensity - * weighting for this pixel, and the complement of the weighting for - * the paired pixel. - */ - wgt = (erracc >> intshift) & 255; - result |= pixelRGBAWeight (renderer, xx0, yy0, r, g, b, a, 255 - wgt); - result |= pixelRGBAWeight (renderer, xx0, y0p1, r, g, b, a, wgt); - } - } - - /* - * Do we have to draw the endpoint - */ - if (draw_endpoint) { - /* - * Draw final pixel, always exactly intersected by the line and doesn't - * need to be weighted. - */ - result |= pixelRGBA (renderer, x2, y2, r, g, b, a); - } - - return (result); -} - -/*! -\brief Draw anti-aliased line with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the aa-line. -\param y1 Y coordinate of the first point of the aa-line. -\param x2 X coordinate of the second point of the aa-line. -\param y2 Y coordinate of the second point of the aa-line. -\param color The color value of the aa-line to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int aalineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return _aalineRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3], 1); -} - -/*! -\brief Draw anti-aliased line with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the aa-line. -\param y1 Y coordinate of the first point of the aa-line. -\param x2 X coordinate of the second point of the aa-line. -\param y2 Y coordinate of the second point of the aa-line. -\param r The red value of the aa-line to draw. -\param g The green value of the aa-line to draw. -\param b The blue value of the aa-line to draw. -\param a The alpha value of the aa-line to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int aalineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return _aalineRGBA(renderer, x1, y1, x2, y2, r, g, b, a, 1); -} - -/* ----- Circle */ - -/*! -\brief Draw circle with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the circle. -\param y Y coordinate of the center of the circle. -\param rad Radius in pixels of the circle. -\param color The color value of the circle to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int circleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return ellipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw circle with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the circle. -\param y Y coordinate of the center of the circle. -\param rad Radius in pixels of the circle. -\param r The red value of the circle to draw. -\param g The green value of the circle to draw. -\param b The blue value of the circle to draw. -\param a The alpha value of the circle to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int circleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return ellipseRGBA(renderer, x, y, rad, rad, r, g, b, a); -} - -/* ----- Arc */ - -/*! -\brief Arc with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the arc. -\param y Y coordinate of the center of the arc. -\param rad Radius in pixels of the arc. -\param start Starting radius in degrees of the arc. 0 degrees is down, increasing counterclockwise. -\param end Ending radius in degrees of the arc. 0 degrees is down, increasing counterclockwise. -\param color The color value of the arc to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int arcColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return arcRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Arc with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the arc. -\param y Y coordinate of the center of the arc. -\param rad Radius in pixels of the arc. -\param start Starting radius in degrees of the arc. 0 degrees is down, increasing counterclockwise. -\param end Ending radius in degrees of the arc. 0 degrees is down, increasing counterclockwise. -\param r The red value of the arc to draw. -\param g The green value of the arc to draw. -\param b The blue value of the arc to draw. -\param a The alpha value of the arc to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -/* TODO: rewrite algorithm; arc endpoints are not always drawn */ -int arcRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result; - Sint16 cx = 0; - Sint16 cy = rad; - Sint16 df = 1 - rad; - Sint16 d_e = 3; - Sint16 d_se = -2 * rad + 5; - Sint16 xpcx, xmcx, xpcy, xmcy; - Sint16 ypcy, ymcy, ypcx, ymcx; - Uint8 drawoct; - int startoct, endoct, oct, stopval_start = 0, stopval_end = 0; - double dstart, dend, temp = 0.; - - /* - * Sanity check radius - */ - if (rad < 0) { - return (-1); - } - - /* - * Special case for rad=0 - draw a point - */ - if (rad == 0) { - return (pixelRGBA(renderer, x, y, r, g, b, a)); - } - - /* - Octant labeling - - \ 5 | 6 / - \ | / - 4 \ | / 7 - \|/ - ------+------ +x - /|\ - 3 / | \ 0 - / | \ - / 2 | 1 \ - +y - - Initially reset bitmask to 0x00000000 - the set whether or not to keep drawing a given octant. - For example: 0x00111100 means we're drawing in octants 2-5 - */ - drawoct = 0; - - /* - * Fixup angles - */ - start %= 360; - end %= 360; - /* 0 <= start & end < 360; note that sometimes start > end - if so, arc goes back through 0. */ - while (start < 0) start += 360; - while (end < 0) end += 360; - start %= 360; - end %= 360; - - /* now, we find which octants we're drawing in. */ - startoct = start / 45; - endoct = end / 45; - oct = startoct - 1; - - /* stopval_start, stopval_end; what values of cx to stop at. */ - do { - oct = (oct + 1) % 8; - - if (oct == startoct) { - /* need to compute stopval_start for this octant. Look at picture above if this is unclear */ - dstart = (double)start; - switch (oct) - { - case 0: - case 3: - temp = sin(dstart * M_PI / 180.); - break; - case 1: - case 6: - temp = cos(dstart * M_PI / 180.); - break; - case 2: - case 5: - temp = -cos(dstart * M_PI / 180.); - break; - case 4: - case 7: - temp = -sin(dstart * M_PI / 180.); - break; - } - temp *= rad; - stopval_start = (int)temp; - - /* - This isn't arbitrary, but requires graph paper to explain well. - The basic idea is that we're always changing drawoct after we draw, so we - stop immediately after we render the last sensible pixel at x = ((int)temp). - and whether to draw in this octant initially - */ - if (oct % 2) drawoct |= (1 << oct); /* this is basically like saying drawoct[oct] = true, if drawoct were a bool array */ - else drawoct &= 255 - (1 << oct); /* this is basically like saying drawoct[oct] = false */ - } - if (oct == endoct) { - /* need to compute stopval_end for this octant */ - dend = (double)end; - switch (oct) - { - case 0: - case 3: - temp = sin(dend * M_PI / 180); - break; - case 1: - case 6: - temp = cos(dend * M_PI / 180); - break; - case 2: - case 5: - temp = -cos(dend * M_PI / 180); - break; - case 4: - case 7: - temp = -sin(dend * M_PI / 180); - break; - } - temp *= rad; - stopval_end = (int)temp; - - /* and whether to draw in this octant initially */ - if (startoct == endoct) { - /* note: we start drawing, stop, then start again in this case */ - /* otherwise: we only draw in this octant, so initialize it to false, it will get set back to true */ - if (start > end) { - /* unfortunately, if we're in the same octant and need to draw over the whole circle, */ - /* we need to set the rest to true, because the while loop will end at the bottom. */ - drawoct = 255; - } else { - drawoct &= 255 - (1 << oct); - } - } - else if (oct % 2) drawoct &= 255 - (1 << oct); - else drawoct |= (1 << oct); - } else if (oct != startoct) { /* already verified that it's != endoct */ - drawoct |= (1 << oct); /* draw this entire segment */ - } - } while (oct != endoct); - - /* so now we have what octants to draw and when to draw them. all that's left is the actual raster code. */ - - /* - * Set color - */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - - /* - * Draw arc - */ - do { - ypcy = y + cy; - ymcy = y - cy; - if (cx > 0) { - xpcx = x + cx; - xmcx = x - cx; - - /* always check if we're drawing a certain octant before adding a pixel to that octant. */ - if (drawoct & 4) result |= pixel(renderer, xmcx, ypcy); - if (drawoct & 2) result |= pixel(renderer, xpcx, ypcy); - if (drawoct & 32) result |= pixel(renderer, xmcx, ymcy); - if (drawoct & 64) result |= pixel(renderer, xpcx, ymcy); - } else { - if (drawoct & 96) result |= pixel(renderer, x, ymcy); - if (drawoct & 6) result |= pixel(renderer, x, ypcy); - } - - xpcy = x + cy; - xmcy = x - cy; - if (cx > 0 && cx != cy) { - ypcx = y + cx; - ymcx = y - cx; - if (drawoct & 8) result |= pixel(renderer, xmcy, ypcx); - if (drawoct & 1) result |= pixel(renderer, xpcy, ypcx); - if (drawoct & 16) result |= pixel(renderer, xmcy, ymcx); - if (drawoct & 128) result |= pixel(renderer, xpcy, ymcx); - } else if (cx == 0) { - if (drawoct & 24) result |= pixel(renderer, xmcy, y); - if (drawoct & 129) result |= pixel(renderer, xpcy, y); - } - - /* - * Update whether we're drawing an octant - */ - if (stopval_start == cx) { - /* works like an on-off switch. */ - /* This is just in case start & end are in the same octant. */ - if (drawoct & (1 << startoct)) drawoct &= 255 - (1 << startoct); - else drawoct |= (1 << startoct); - } - if (stopval_end == cx) { - if (drawoct & (1 << endoct)) drawoct &= 255 - (1 << endoct); - else drawoct |= (1 << endoct); - } - - /* - * Update pixels - */ - if (df < 0) { - df += d_e; - d_e += 2; - d_se += 2; - } else { - df += d_se; - d_e += 2; - d_se += 4; - cy--; - } - cx++; - } while (cx <= cy); - - return (result); -} - -/* ----- AA Circle */ - -/*! -\brief Draw anti-aliased circle with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the aa-circle. -\param y Y coordinate of the center of the aa-circle. -\param rad Radius in pixels of the aa-circle. -\param color The color value of the aa-circle to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int aacircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return aaellipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw anti-aliased circle with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the aa-circle. -\param y Y coordinate of the center of the aa-circle. -\param rad Radius in pixels of the aa-circle. -\param r The red value of the aa-circle to draw. -\param g The green value of the aa-circle to draw. -\param b The blue value of the aa-circle to draw. -\param a The alpha value of the aa-circle to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int aacircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - /* - * Draw - */ - return aaellipseRGBA(renderer, x, y, rad, rad, r, g, b, a); -} - -/* ----- Ellipse */ - -/*! -\brief Internal function to draw pixels or lines in 4 quadrants. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the quadrant. -\param y Y coordinate of the center of the quadrant. -\param dx X offset in pixels of the corners of the quadrant. -\param dy Y offset in pixels of the corners of the quadrant. -\param f Flag indicating if the quadrant should be filled (1) or not (0). - -\returns Returns 0 on success, -1 on failure. -*/ -int _drawQuadrants(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 dx, Sint16 dy, Sint32 f) -{ - int result = 0; - Sint16 xpdx, xmdx; - Sint16 ypdy, ymdy; - - if (dx == 0) { - if (dy == 0) { - result |= pixel(renderer, x, y); - } else { - ypdy = y + dy; - ymdy = y - dy; - if (f) { - result |= vline(renderer, x, ymdy, ypdy); - } else { - result |= pixel(renderer, x, ypdy); - result |= pixel(renderer, x, ymdy); - } - } - } else { - xpdx = x + dx; - xmdx = x - dx; - ypdy = y + dy; - ymdy = y - dy; - if (f) { - result |= vline(renderer, xpdx, ymdy, ypdy); - result |= vline(renderer, xmdx, ymdy, ypdy); - } else { - result |= pixel(renderer, xpdx, ypdy); - result |= pixel(renderer, xmdx, ypdy); - result |= pixel(renderer, xpdx, ymdy); - result |= pixel(renderer, xmdx, ymdy); - } - } - - return result; -} - -/*! -\brief Internal function to draw ellipse or filled ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the ellipse. -\param y Y coordinate of the center of the ellipse. -\param rx Horizontal radius in pixels of the ellipse. -\param ry Vertical radius in pixels of the ellipse. -\param r The red value of the ellipse to draw. -\param g The green value of the ellipse to draw. -\param b The blue value of the ellipse to draw. -\param a The alpha value of the ellipse to draw. -\param f Flag indicating if the ellipse should be filled (1) or not (0). - -\returns Returns 0 on success, -1 on failure. -*/ -#define DEFAULT_ELLIPSE_OVERSCAN 4 -int _ellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Sint32 f) -{ - int result; - Sint32 rxi, ryi; - Sint32 rx2, ry2, rx22, ry22; - Sint32 error; - Sint32 curX, curY, curXp1, curYm1; - Sint32 scrX, scrY, oldX, oldY; - Sint32 deltaX, deltaY; - Sint32 ellipseOverscan; - - /* - * Sanity check radii - */ - if ((rx < 0) || (ry < 0)) { - return (-1); - } - - /* - * Set color - */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - - /* - * Special cases for rx=0 and/or ry=0: draw a hline/vline/pixel - */ - if (rx == 0) { - if (ry == 0) { - return (pixel(renderer, x, y)); - } else { - return (vline(renderer, x, y - ry, y + ry)); - } - } else { - if (ry == 0) { - return (hline(renderer, x - rx, x + rx, y)); - } - } - - /* - * Adjust overscan - */ - rxi = rx; - ryi = ry; - if (rxi >= 512 || ryi >= 512) - { - ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 4; - } - else if (rxi >= 256 || ryi >= 256) - { - ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 2; - } - else - { - ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 1; - } - - /* - * Top/bottom center points. - */ - oldX = scrX = 0; - oldY = scrY = ryi; - result |= _drawQuadrants(renderer, x, y, 0, ry, f); - - /* Midpoint ellipse algorithm with overdraw */ - rxi *= ellipseOverscan; - ryi *= ellipseOverscan; - rx2 = rxi * rxi; - rx22 = rx2 + rx2; - ry2 = ryi * ryi; - ry22 = ry2 + ry2; - curX = 0; - curY = ryi; - deltaX = 0; - deltaY = rx22 * curY; - - /* Points in segment 1 */ - error = ry2 - rx2 * ryi + rx2 / 4; - while (deltaX <= deltaY) - { - curX++; - deltaX += ry22; - - error += deltaX + ry2; - if (error >= 0) - { - curY--; - deltaY -= rx22; - error -= deltaY; - } - - scrX = curX / ellipseOverscan; - scrY = curY / ellipseOverscan; - if ((scrX != oldX && scrY == oldY) || (scrX != oldX && scrY != oldY)) { - result |= _drawQuadrants(renderer, x, y, scrX, scrY, f); - oldX = scrX; - oldY = scrY; - } - } - - /* Points in segment 2 */ - if (curY > 0) - { - curXp1 = curX + 1; - curYm1 = curY - 1; - error = ry2 * curX * curXp1 + ((ry2 + 3) / 4) + rx2 * curYm1 * curYm1 - rx2 * ry2; - while (curY > 0) - { - curY--; - deltaY -= rx22; - - error += rx2; - error -= deltaY; - - if (error <= 0) - { - curX++; - deltaX += ry22; - error += deltaX; - } - - scrX = curX / ellipseOverscan; - scrY = curY / ellipseOverscan; - if ((scrX != oldX && scrY == oldY) || (scrX != oldX && scrY != oldY)) { - oldY--; - for (;oldY >= scrY; oldY--) { - result |= _drawQuadrants(renderer, x, y, scrX, oldY, f); - /* prevent overdraw */ - if (f) { - oldY = scrY - 1; - } - } - oldX = scrX; - oldY = scrY; - } - } - - /* Remaining points in vertical */ - if (!f) { - oldY--; - for (;oldY >= 0; oldY--) { - result |= _drawQuadrants(renderer, x, y, scrX, oldY, f); - } - } - } - - return (result); -} - -/*! -\brief Draw ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the ellipse. -\param y Y coordinate of the center of the ellipse. -\param rx Horizontal radius in pixels of the ellipse. -\param ry Vertical radius in pixels of the ellipse. -\param color The color value of the ellipse to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int ellipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return _ellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3], 0); -} - -/*! -\brief Draw ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the ellipse. -\param y Y coordinate of the center of the ellipse. -\param rx Horizontal radius in pixels of the ellipse. -\param ry Vertical radius in pixels of the ellipse. -\param r The red value of the ellipse to draw. -\param g The green value of the ellipse to draw. -\param b The blue value of the ellipse to draw. -\param a The alpha value of the ellipse to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int ellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return _ellipseRGBA(renderer, x, y, rx, ry, r, g, b, a, 0); -} - -/* ----- Filled Circle */ - -/*! -\brief Draw filled circle with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the filled circle. -\param y Y coordinate of the center of the filled circle. -\param rad Radius in pixels of the filled circle. -\param color The color value of the filled circle to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int filledCircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return filledEllipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw filled circle with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the filled circle. -\param y Y coordinate of the center of the filled circle. -\param rad Radius in pixels of the filled circle. -\param r The red value of the filled circle to draw. -\param g The green value of the filled circle to draw. -\param b The blue value of the filled circle to draw. -\param a The alpha value of the filled circle to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int filledCircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return _ellipseRGBA(renderer, x, y, rad, rad, r, g ,b, a, 1); -} - - -/* ----- AA Ellipse */ - -/* Windows targets do not have lrint, so provide a local inline version */ -#if defined(_MSC_VER) -/* Detect 64bit and use intrinsic version */ -#ifdef _M_X64 -#include -static __inline long - lrint(float f) -{ - return _mm_cvtss_si32(_mm_load_ss(&f)); -} -#elif defined(_M_IX86) -__inline long int - lrint (double flt) -{ - int intgr; - _asm - { - fld flt - fistp intgr - }; - return intgr; -} -#elif defined(_M_ARM) -#include -#pragma warning(push) -#pragma warning(disable: 4716) -__declspec(naked) long int - lrint (double flt) -{ - __emit(0xEC410B10); // fmdrr d0, r0, r1 - __emit(0xEEBD0B40); // ftosid s0, d0 - __emit(0xEE100A10); // fmrs r0, s0 - __emit(0xE12FFF1E); // bx lr -} -#pragma warning(pop) -#else -#error lrint needed for MSVC on non X86/AMD64/ARM targets. -#endif -#endif - -/*! -\brief Draw anti-aliased ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the aa-ellipse. -\param y Y coordinate of the center of the aa-ellipse. -\param rx Horizontal radius in pixels of the aa-ellipse. -\param ry Vertical radius in pixels of the aa-ellipse. -\param color The color value of the aa-ellipse to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int aaellipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return aaellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw anti-aliased ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the aa-ellipse. -\param y Y coordinate of the center of the aa-ellipse. -\param rx Horizontal radius in pixels of the aa-ellipse. -\param ry Vertical radius in pixels of the aa-ellipse. -\param r The red value of the aa-ellipse to draw. -\param g The green value of the aa-ellipse to draw. -\param b The blue value of the aa-ellipse to draw. -\param a The alpha value of the aa-ellipse to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int aaellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result; - int i; - int a2, b2, ds, dt, dxt, t, s, d; - Sint16 xp, yp, xs, ys, dyt, od, xx, yy, xc2, yc2; - float cp; - double sab; - Uint8 weight, iweight; - - /* - * Sanity check radii - */ - if ((rx < 0) || (ry < 0)) { - return (-1); - } - - /* - * Special cases for rx=0 and/or ry=0: draw a hline/vline/pixel - */ - if (rx == 0) { - if (ry == 0) { - return (pixelRGBA(renderer, x, y, r, g, b, a)); - } else { - return (vlineRGBA(renderer, x, y - ry, y + ry, r, g, b, a)); - } - } else { - if (ry == 0) { - return (hlineRGBA(renderer, x - rx, x + rx, y, r, g, b, a)); - } - } - - /* Variable setup */ - a2 = rx * rx; - b2 = ry * ry; - - ds = 2 * a2; - dt = 2 * b2; - - xc2 = 2 * x; - yc2 = 2 * y; - - sab = sqrt((double)(a2 + b2)); - od = (Sint16)lrint(sab*0.01) + 1; /* introduce some overdraw */ - dxt = (Sint16)lrint((double)a2 / sab) + od; - - t = 0; - s = -2 * a2 * ry; - d = 0; - - xp = x; - yp = y - ry; - - /* Draw */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - - /* "End points" */ - result |= pixelRGBA(renderer, xp, yp, r, g, b, a); - result |= pixelRGBA(renderer, xc2 - xp, yp, r, g, b, a); - result |= pixelRGBA(renderer, xp, yc2 - yp, r, g, b, a); - result |= pixelRGBA(renderer, xc2 - xp, yc2 - yp, r, g, b, a); - - for (i = 1; i <= dxt; i++) { - xp--; - d += t - b2; - - if (d >= 0) - ys = yp - 1; - else if ((d - s - a2) > 0) { - if ((2 * d - s - a2) >= 0) - ys = yp + 1; - else { - ys = yp; - yp++; - d -= s + a2; - s += ds; - } - } else { - yp++; - ys = yp + 1; - d -= s + a2; - s += ds; - } - - t -= dt; - - /* Calculate alpha */ - if (s != 0) { - cp = (float) abs(d) / (float) abs(s); - if (cp > 1.0) { - cp = 1.0; - } - } else { - cp = 1.0; - } - - /* Calculate weights */ - weight = (Uint8) (cp * 255); - iweight = 255 - weight; - - /* Upper half */ - xx = xc2 - xp; - result |= pixelRGBAWeight(renderer, xp, yp, r, g, b, a, iweight); - result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, iweight); - - result |= pixelRGBAWeight(renderer, xp, ys, r, g, b, a, weight); - result |= pixelRGBAWeight(renderer, xx, ys, r, g, b, a, weight); - - /* Lower half */ - yy = yc2 - yp; - result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, iweight); - result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, iweight); - - yy = yc2 - ys; - result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, weight); - result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, weight); - } - - /* Replaces original approximation code dyt = abs(yp - yc); */ - dyt = (Sint16)lrint((double)b2 / sab ) + od; - - for (i = 1; i <= dyt; i++) { - yp++; - d -= s + a2; - - if (d <= 0) - xs = xp + 1; - else if ((d + t - b2) < 0) { - if ((2 * d + t - b2) <= 0) - xs = xp - 1; - else { - xs = xp; - xp--; - d += t - b2; - t -= dt; - } - } else { - xp--; - xs = xp - 1; - d += t - b2; - t -= dt; - } - - s += ds; - - /* Calculate alpha */ - if (t != 0) { - cp = (float) abs(d) / (float) abs(t); - if (cp > 1.0) { - cp = 1.0; - } - } else { - cp = 1.0; - } - - /* Calculate weight */ - weight = (Uint8) (cp * 255); - iweight = 255 - weight; - - /* Left half */ - xx = xc2 - xp; - yy = yc2 - yp; - result |= pixelRGBAWeight(renderer, xp, yp, r, g, b, a, iweight); - result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, iweight); - - result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, iweight); - result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, iweight); - - /* Right half */ - xx = xc2 - xs; - result |= pixelRGBAWeight(renderer, xs, yp, r, g, b, a, weight); - result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, weight); - - result |= pixelRGBAWeight(renderer, xs, yy, r, g, b, a, weight); - result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, weight); - } - - return (result); -} - -/* ---- Filled Ellipse */ - -/*! -\brief Draw filled ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the filled ellipse. -\param y Y coordinate of the center of the filled ellipse. -\param rx Horizontal radius in pixels of the filled ellipse. -\param ry Vertical radius in pixels of the filled ellipse. -\param color The color value of the filled ellipse to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int filledEllipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return _ellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3], 1); -} - -/*! -\brief Draw filled ellipse with blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the filled ellipse. -\param y Y coordinate of the center of the filled ellipse. -\param rx Horizontal radius in pixels of the filled ellipse. -\param ry Vertical radius in pixels of the filled ellipse. -\param r The red value of the filled ellipse to draw. -\param g The green value of the filled ellipse to draw. -\param b The blue value of the filled ellipse to draw. -\param a The alpha value of the filled ellipse to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int filledEllipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return _ellipseRGBA(renderer, x, y, rx, ry, r, g, b, a, 1); -} - -/* ----- Pie */ - -/*! -\brief Internal float (low-speed) pie-calc implementation by drawing polygons. - -Note: Determines vertex array and uses polygon or filledPolygon drawing routines to render. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the pie. -\param y Y coordinate of the center of the pie. -\param rad Radius in pixels of the pie. -\param start Starting radius in degrees of the pie. -\param end Ending radius in degrees of the pie. -\param r The red value of the pie to draw. -\param g The green value of the pie to draw. -\param b The blue value of the pie to draw. -\param a The alpha value of the pie to draw. -\param filled Flag indicating if the pie should be filled (=1) or not (=0). - -\returns Returns 0 on success, -1 on failure. -*/ -/* TODO: rewrite algorithm; pie is not always accurate */ -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) -{ - int result; - double angle, start_angle, end_angle; - double deltaAngle; - double dr; - int numpoints, i; - Sint16 *vx, *vy; - - /* - * Sanity check radii - */ - if (rad < 0) { - return (-1); - } - - /* - * Fixup angles - */ - start = start % 360; - end = end % 360; - - /* - * Special case for rad=0 - draw a point - */ - if (rad == 0) { - return (pixelRGBA(renderer, x, y, r, g, b, a)); - } - - /* - * Variable setup - */ - dr = (double) rad; - deltaAngle = 3.0 / dr; - start_angle = (double) start *(2.0 * M_PI / 360.0); - end_angle = (double) end *(2.0 * M_PI / 360.0); - if (start > end) { - end_angle += (2.0 * M_PI); - } - - /* We will always have at least 2 points */ - numpoints = 2; - - /* Count points (rather than calculating it) */ - angle = start_angle; - while (angle < end_angle) { - angle += deltaAngle; - numpoints++; - } - - /* Allocate combined vertex array */ - vx = vy = (Sint16 *) malloc(2 * sizeof(Uint16) * numpoints); - if (vx == NULL) { - return (-1); - } - - /* Update point to start of vy */ - vy += numpoints; - - /* Center */ - vx[0] = x; - vy[0] = y; - - /* First vertex */ - angle = start_angle; - vx[1] = x + (int) (dr * cos(angle)); - vy[1] = y + (int) (dr * sin(angle)); - - if (numpoints<3) - { - result = lineRGBA(renderer, vx[0], vy[0], vx[1], vy[1], r, g, b, a); - } - else - { - /* Calculate other vertices */ - i = 2; - angle = start_angle; - while (angle < end_angle) { - angle += deltaAngle; - if (angle>end_angle) - { - angle = end_angle; - } - vx[i] = x + (int) (dr * cos(angle)); - vy[i] = y + (int) (dr * sin(angle)); - i++; - } - - /* Draw */ - if (filled) { - result = filledPolygonRGBA(renderer, vx, vy, numpoints, r, g, b, a); - } else { - result = polygonRGBA(renderer, vx, vy, numpoints, r, g, b, a); - } - } - - /* Free combined vertex array */ - free(vx); - - return (result); -} - -/*! -\brief Draw pie (outline) with alpha blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the pie. -\param y Y coordinate of the center of the pie. -\param rad Radius in pixels of the pie. -\param start Starting radius in degrees of the pie. -\param end Ending radius in degrees of the pie. -\param color The color value of the pie to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int pieColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, - Sint16 start, Sint16 end, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return _pieRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], 0); -} - -/*! -\brief Draw pie (outline) with alpha blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the pie. -\param y Y coordinate of the center of the pie. -\param rad Radius in pixels of the pie. -\param start Starting radius in degrees of the pie. -\param end Ending radius in degrees of the pie. -\param r The red value of the pie to draw. -\param g The green value of the pie to draw. -\param b The blue value of the pie to draw. -\param a The alpha value of the pie to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int pieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, - Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return _pieRGBA(renderer, x, y, rad, start, end, r, g, b, a, 0); -} - -/*! -\brief Draw filled pie with alpha blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the filled pie. -\param y Y coordinate of the center of the filled pie. -\param rad Radius in pixels of the filled pie. -\param start Starting radius in degrees of the filled pie. -\param end Ending radius in degrees of the filled pie. -\param color The color value of the filled pie to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int filledPieColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return _pieRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], 1); -} - -/*! -\brief Draw filled pie with alpha blending. - -\param renderer The renderer to draw on. -\param x X coordinate of the center of the filled pie. -\param y Y coordinate of the center of the filled pie. -\param rad Radius in pixels of the filled pie. -\param start Starting radius in degrees of the filled pie. -\param end Ending radius in degrees of the filled pie. -\param r The red value of the filled pie to draw. -\param g The green value of the filled pie to draw. -\param b The blue value of the filled pie to draw. -\param a The alpha value of the filled pie to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int filledPieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, - Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return _pieRGBA(renderer, x, y, rad, start, end, r, g, b, a, 1); -} - -/* ------ Trigon */ - -/*! -\brief Draw trigon (triangle outline) with alpha blending. - -Note: Creates vertex array and uses polygon routine to render. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the trigon. -\param y1 Y coordinate of the first point of the trigon. -\param x2 X coordinate of the second point of the trigon. -\param y2 Y coordinate of the second point of the trigon. -\param x3 X coordinate of the third point of the trigon. -\param y3 Y coordinate of the third point of the trigon. -\param color The color value of the trigon to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int trigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color) -{ - Sint16 vx[3]; - Sint16 vy[3]; - - vx[0]=x1; - vx[1]=x2; - vx[2]=x3; - vy[0]=y1; - vy[1]=y2; - vy[2]=y3; - - return(polygonColor(renderer,vx,vy,3,color)); -} - -/*! -\brief Draw trigon (triangle outline) with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the trigon. -\param y1 Y coordinate of the first point of the trigon. -\param x2 X coordinate of the second point of the trigon. -\param y2 Y coordinate of the second point of the trigon. -\param x3 X coordinate of the third point of the trigon. -\param y3 Y coordinate of the third point of the trigon. -\param r The red value of the trigon to draw. -\param g The green value of the trigon to draw. -\param b The blue value of the trigon to draw. -\param a The alpha value of the trigon to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int trigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, - Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - Sint16 vx[3]; - Sint16 vy[3]; - - vx[0]=x1; - vx[1]=x2; - vx[2]=x3; - vy[0]=y1; - vy[1]=y2; - vy[2]=y3; - - return(polygonRGBA(renderer,vx,vy,3,r,g,b,a)); -} - -/* ------ AA-Trigon */ - -/*! -\brief Draw anti-aliased trigon (triangle outline) with alpha blending. - -Note: Creates vertex array and uses aapolygon routine to render. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the aa-trigon. -\param y1 Y coordinate of the first point of the aa-trigon. -\param x2 X coordinate of the second point of the aa-trigon. -\param y2 Y coordinate of the second point of the aa-trigon. -\param x3 X coordinate of the third point of the aa-trigon. -\param y3 Y coordinate of the third point of the aa-trigon. -\param color The color value of the aa-trigon to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int aatrigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color) -{ - Sint16 vx[3]; - Sint16 vy[3]; - - vx[0]=x1; - vx[1]=x2; - vx[2]=x3; - vy[0]=y1; - vy[1]=y2; - vy[2]=y3; - - return(aapolygonColor(renderer,vx,vy,3,color)); -} - -/*! -\brief Draw anti-aliased trigon (triangle outline) with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the aa-trigon. -\param y1 Y coordinate of the first point of the aa-trigon. -\param x2 X coordinate of the second point of the aa-trigon. -\param y2 Y coordinate of the second point of the aa-trigon. -\param x3 X coordinate of the third point of the aa-trigon. -\param y3 Y coordinate of the third point of the aa-trigon. -\param r The red value of the aa-trigon to draw. -\param g The green value of the aa-trigon to draw. -\param b The blue value of the aa-trigon to draw. -\param a The alpha value of the aa-trigon to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int aatrigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, - Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - Sint16 vx[3]; - Sint16 vy[3]; - - vx[0]=x1; - vx[1]=x2; - vx[2]=x3; - vy[0]=y1; - vy[1]=y2; - vy[2]=y3; - - return(aapolygonRGBA(renderer,vx,vy,3,r,g,b,a)); -} - -/* ------ Filled Trigon */ - -/*! -\brief Draw filled trigon (triangle) with alpha blending. - -Note: Creates vertex array and uses aapolygon routine to render. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the filled trigon. -\param y1 Y coordinate of the first point of the filled trigon. -\param x2 X coordinate of the second point of the filled trigon. -\param y2 Y coordinate of the second point of the filled trigon. -\param x3 X coordinate of the third point of the filled trigon. -\param y3 Y coordinate of the third point of the filled trigon. -\param color The color value of the filled trigon to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int filledTrigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color) -{ - Sint16 vx[3]; - Sint16 vy[3]; - - vx[0]=x1; - vx[1]=x2; - vx[2]=x3; - vy[0]=y1; - vy[1]=y2; - vy[2]=y3; - - return(filledPolygonColor(renderer,vx,vy,3,color)); -} - -/*! -\brief Draw filled trigon (triangle) with alpha blending. - -Note: Creates vertex array and uses aapolygon routine to render. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the filled trigon. -\param y1 Y coordinate of the first point of the filled trigon. -\param x2 X coordinate of the second point of the filled trigon. -\param y2 Y coordinate of the second point of the filled trigon. -\param x3 X coordinate of the third point of the filled trigon. -\param y3 Y coordinate of the third point of the filled trigon. -\param r The red value of the filled trigon to draw. -\param g The green value of the filled trigon to draw. -\param b The blue value of the filled trigon to draw. -\param a The alpha value of the filled trigon to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int filledTrigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, - Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - Sint16 vx[3]; - Sint16 vy[3]; - - vx[0]=x1; - vx[1]=x2; - vx[2]=x3; - vy[0]=y1; - vy[1]=y2; - vy[2]=y3; - - return(filledPolygonRGBA(renderer,vx,vy,3,r,g,b,a)); -} - -/* ---- Polygon */ - -/*! -\brief Draw polygon with alpha blending. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the polygon. -\param vy Vertex array containing Y coordinates of the points of the polygon. -\param n Number of points in the vertex array. Minimum number is 3. -\param color The color value of the polygon to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int polygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return polygonRGBA(renderer, vx, vy, n, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw polygon with the currently set color and blend mode. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the polygon. -\param vy Vertex array containing Y coordinates of the points of the polygon. -\param n Number of points in the vertex array. Minimum number is 3. - -\returns Returns 0 on success, -1 on failure. -*/ -int polygon(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n) -{ - /* - * Draw - */ - int result = 0; - int i, nn; - SDL_Point* points; - - /* - * Vertex array NULL check - */ - if (vx == NULL) { - return (-1); - } - if (vy == NULL) { - return (-1); - } - - /* - * Sanity check - */ - if (n < 3) { - return (-1); - } - - /* - * Create array of points - */ - nn = n + 1; - points = (SDL_Point*)malloc(sizeof(SDL_Point) * nn); - if (points == NULL) - { - return -1; - } - for (i=0; ib. -*/ -int _gfxPrimitivesCompareInt(const void *a, const void *b) -{ - return (*(const int *) a) - (*(const int *) b); -} - -/*! -\brief Global vertex array to use if optional parameters are not given in filledPolygonMT calls. - -Note: Used for non-multithreaded (default) operation of filledPolygonMT. -*/ -static int *gfxPrimitivesPolyIntsGlobal = NULL; - -/*! -\brief Flag indicating if global vertex array was already allocated. - -Note: Used for non-multithreaded (default) operation of filledPolygonMT. -*/ -static int gfxPrimitivesPolyAllocatedGlobal = 0; - -/*! -\brief Draw filled polygon with alpha blending (multi-threaded capable). - -Note: The last two parameters are optional; but are required for multithreaded operation. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the filled polygon. -\param vy Vertex array containing Y coordinates of the points of the filled polygon. -\param n Number of points in the vertex array. Minimum number is 3. -\param r The red value of the filled polygon to draw. -\param g The green value of the filled polygon to draw. -\param b The blue value of the filled polygon to draw. -\param a The alpha value of the filled polygon to draw. -\param polyInts Preallocated, temporary vertex array used for sorting vertices. Required for multithreaded operation; set to NULL otherwise. -\param polyAllocated Flag indicating if temporary vertex array was allocated. Required for multithreaded operation; set to NULL otherwise. - -\returns Returns 0 on success, -1 on failure. -*/ -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) -{ - int result; - int i; - int y, xa, xb; - int miny, maxy; - int x1, y1; - int x2, y2; - int ind1, ind2; - int ints; - int *gfxPrimitivesPolyInts = NULL; - int *gfxPrimitivesPolyIntsNew = NULL; - int gfxPrimitivesPolyAllocated = 0; - - /* - * Vertex array NULL check - */ - if (vx == NULL) { - return (-1); - } - if (vy == NULL) { - return (-1); - } - - /* - * Sanity check number of edges - */ - if (n < 3) { - return -1; - } - - /* - * Map polygon cache - */ - if ((polyInts==NULL) || (polyAllocated==NULL)) { - /* Use global cache */ - gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal; - gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal; - } else { - /* Use local cache */ - gfxPrimitivesPolyInts = *polyInts; - gfxPrimitivesPolyAllocated = *polyAllocated; - } - - /* - * Allocate temp array, only grow array - */ - if (!gfxPrimitivesPolyAllocated) { - gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); - gfxPrimitivesPolyAllocated = n; - } else { - if (gfxPrimitivesPolyAllocated < n) { - gfxPrimitivesPolyIntsNew = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); - if (!gfxPrimitivesPolyIntsNew) { - if (!gfxPrimitivesPolyInts) { - free(gfxPrimitivesPolyInts); - gfxPrimitivesPolyInts = NULL; - } - gfxPrimitivesPolyAllocated = 0; - } else { - gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsNew; - gfxPrimitivesPolyAllocated = n; - } - } - } - - /* - * Check temp array - */ - if (gfxPrimitivesPolyInts==NULL) { - gfxPrimitivesPolyAllocated = 0; - } - - /* - * Update cache variables - */ - if ((polyInts==NULL) || (polyAllocated==NULL)) { - gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts; - gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated; - } else { - *polyInts = gfxPrimitivesPolyInts; - *polyAllocated = gfxPrimitivesPolyAllocated; - } - - /* - * Check temp array again - */ - if (gfxPrimitivesPolyInts==NULL) { - return(-1); - } - - /* - * Determine Y maxima - */ - miny = vy[0]; - maxy = vy[0]; - for (i = 1; (i < n); i++) { - if (vy[i] < miny) { - miny = vy[i]; - } else if (vy[i] > maxy) { - maxy = vy[i]; - } - } - - /* - * Draw, scanning y - */ - result = 0; - for (y = miny; (y <= maxy); y++) { - ints = 0; - for (i = 0; (i < n); i++) { - if (!i) { - ind1 = n - 1; - ind2 = 0; - } else { - ind1 = i - 1; - ind2 = i; - } - y1 = vy[ind1]; - y2 = vy[ind2]; - if (y1 < y2) { - x1 = vx[ind1]; - x2 = vx[ind2]; - } else if (y1 > y2) { - y2 = vy[ind1]; - y1 = vy[ind2]; - x2 = vx[ind1]; - x1 = vx[ind2]; - } else { - continue; - } - if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) { - gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1); - } - } - - qsort(gfxPrimitivesPolyInts, ints, sizeof(int), _gfxPrimitivesCompareInt); - - /* - * Set color - */ - result = 0; - result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); - result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); - - for (i = 0; (i < ints); i += 2) { - xa = gfxPrimitivesPolyInts[i] + 1; - xa = (xa >> 16) + ((xa & 32768) >> 15); - xb = gfxPrimitivesPolyInts[i+1] - 1; - xb = (xb >> 16) + ((xb & 32768) >> 15); - result |= hline(renderer, xa, xb, y); - } - } - - return (result); -} - -/*! -\brief Draw filled polygon with alpha blending. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the filled polygon. -\param vy Vertex array containing Y coordinates of the points of the filled polygon. -\param n Number of points in the vertex array. Minimum number is 3. -\param color The color value of the filled polygon to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int filledPolygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return filledPolygonRGBAMT(renderer, vx, vy, n, c[0], c[1], c[2], c[3], NULL, NULL); -} - -/*! -\brief Draw filled polygon with alpha blending. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the filled polygon. -\param vy Vertex array containing Y coordinates of the points of the filled polygon. -\param n Number of points in the vertex array. Minimum number is 3. -\param r The red value of the filled polygon to draw. -\param g The green value of the filled polygon to draw. -\param b The blue value of the filed polygon to draw. -\param a The alpha value of the filled polygon to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int filledPolygonRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - return filledPolygonRGBAMT(renderer, vx, vy, n, r, g, b, a, NULL, NULL); -} - -/* ---- Textured Polygon */ - -/*! -\brief Internal function to draw a textured horizontal line. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point (i.e. left) of the line. -\param x2 X coordinate of the second point (i.e. right) of the line. -\param y Y coordinate of the points of the line. -\param texture The texture to retrieve color information from. -\param texture_w The width of the texture. -\param texture_h The height of the texture. -\param texture_dx The X offset for the texture lookup. -\param texture_dy The Y offset for the textured lookup. - -\returns Returns 0 on success, -1 on failure. -*/ -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) -{ - Sint16 w; - Sint16 xtmp; - int result = 0; - int texture_x_walker; - int texture_y_start; - SDL_Rect source_rect,dst_rect; - int pixels_written,write_width; - - /* - * Swap x1, x2 if required to ensure x1<=x2 - */ - if (x1 > x2) { - xtmp = x1; - x1 = x2; - x2 = xtmp; - } - - /* - * Calculate width to draw - */ - w = x2 - x1 + 1; - - /* - * Determine where in the texture we start drawing - */ - texture_x_walker = (x1 - texture_dx) % texture_w; - if (texture_x_walker < 0){ - texture_x_walker = texture_w + texture_x_walker ; - } - - texture_y_start = (y + texture_dy) % texture_h; - if (texture_y_start < 0){ - texture_y_start = texture_h + texture_y_start; - } - - /* setup the source rectangle; we are only drawing one horizontal line */ - source_rect.y = texture_y_start; - source_rect.x = texture_x_walker; - source_rect.h = 1; - - /* we will draw to the current y */ - dst_rect.y = y; - dst_rect.h = 1; - - /* if there are enough pixels left in the current row of the texture */ - /* draw it all at once */ - if (w <= texture_w -texture_x_walker){ - source_rect.w = w; - source_rect.x = texture_x_walker; - dst_rect.x= x1; - dst_rect.w = source_rect.w; - result = (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0); - } else { - /* we need to draw multiple times */ - /* draw the first segment */ - pixels_written = texture_w - texture_x_walker; - source_rect.w = pixels_written; - source_rect.x = texture_x_walker; - dst_rect.x= x1; - dst_rect.w = source_rect.w; - result |= (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0); - write_width = texture_w; - - /* now draw the rest */ - /* set the source x to 0 */ - source_rect.x = 0; - while (pixels_written < w){ - if (write_width >= w - pixels_written) { - write_width = w - pixels_written; - } - source_rect.w = write_width; - dst_rect.x = x1 + pixels_written; - dst_rect.w = source_rect.w; - result |= (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0); - pixels_written += write_width; - } - } - - return result; -} - -/*! -\brief Draws a polygon filled with the given texture (Multi-Threading Capable). - -\param renderer The renderer to draw on. -\param vx array of x vector components -\param vy array of x vector components -\param n the amount of vectors in the vx and vy array -\param texture the sdl surface to use to fill the polygon -\param texture_dx the offset of the texture relative to the screeen. If you move the polygon 10 pixels -to the left and want the texture to apear the same you need to increase the texture_dx value -\param texture_dy see texture_dx -\param polyInts Preallocated temp array storage for vertex sorting (used for multi-threaded operation) -\param polyAllocated Flag indicating oif the temp array was allocated (used for multi-threaded operation) - -\returns Returns 0 on success, -1 on failure. -*/ -int 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) -{ - int result; - int i; - int y, xa, xb; - int minx,maxx,miny, maxy; - int x1, y1; - int x2, y2; - int ind1, ind2; - int ints; - int *gfxPrimitivesPolyInts = NULL; - int *gfxPrimitivesPolyIntsTemp = NULL; - int gfxPrimitivesPolyAllocated = 0; - SDL_Texture *textureAsTexture = NULL; - - /* - * Sanity check number of edges - */ - if (n < 3) { - return -1; - } - - /* - * Map polygon cache - */ - if ((polyInts==NULL) || (polyAllocated==NULL)) { - /* Use global cache */ - gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal; - gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal; - } else { - /* Use local cache */ - gfxPrimitivesPolyInts = *polyInts; - gfxPrimitivesPolyAllocated = *polyAllocated; - } - - /* - * Allocate temp array, only grow array - */ - if (!gfxPrimitivesPolyAllocated) { - gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); - gfxPrimitivesPolyAllocated = n; - } else { - if (gfxPrimitivesPolyAllocated < n) { - gfxPrimitivesPolyIntsTemp = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); - if (gfxPrimitivesPolyIntsTemp == NULL) { - /* Realloc failed - keeps original memory block, but fails this operation */ - return(-1); - } - gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsTemp; - gfxPrimitivesPolyAllocated = n; - } - } - - /* - * Check temp array - */ - if (gfxPrimitivesPolyInts==NULL) { - gfxPrimitivesPolyAllocated = 0; - } - - /* - * Update cache variables - */ - if ((polyInts==NULL) || (polyAllocated==NULL)) { - gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts; - gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated; - } else { - *polyInts = gfxPrimitivesPolyInts; - *polyAllocated = gfxPrimitivesPolyAllocated; - } - - /* - * Check temp array again - */ - if (gfxPrimitivesPolyInts==NULL) { - return(-1); - } - - /* - * Determine X,Y minima,maxima - */ - miny = vy[0]; - maxy = vy[0]; - minx = vx[0]; - maxx = vx[0]; - for (i = 1; (i < n); i++) { - if (vy[i] < miny) { - miny = vy[i]; - } else if (vy[i] > maxy) { - maxy = vy[i]; - } - if (vx[i] < minx) { - minx = vx[i]; - } else if (vx[i] > maxx) { - maxx = vx[i]; - } - } - - /* Create texture for drawing */ - textureAsTexture = SDL_CreateTextureFromSurface(renderer, texture); - if (textureAsTexture == NULL) - { - return -1; - } - SDL_SetTextureBlendMode(textureAsTexture, SDL_BLENDMODE_BLEND); - - /* - * Draw, scanning y - */ - result = 0; - for (y = miny; (y <= maxy); y++) { - ints = 0; - for (i = 0; (i < n); i++) { - if (!i) { - ind1 = n - 1; - ind2 = 0; - } else { - ind1 = i - 1; - ind2 = i; - } - y1 = vy[ind1]; - y2 = vy[ind2]; - if (y1 < y2) { - x1 = vx[ind1]; - x2 = vx[ind2]; - } else if (y1 > y2) { - y2 = vy[ind1]; - y1 = vy[ind2]; - x2 = vx[ind1]; - x1 = vx[ind2]; - } else { - continue; - } - if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) { - gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1); - } - } - - qsort(gfxPrimitivesPolyInts, ints, sizeof(int), _gfxPrimitivesCompareInt); - - for (i = 0; (i < ints); i += 2) { - xa = gfxPrimitivesPolyInts[i] + 1; - xa = (xa >> 16) + ((xa & 32768) >> 15); - xb = gfxPrimitivesPolyInts[i+1] - 1; - xb = (xb >> 16) + ((xb & 32768) >> 15); - result |= _HLineTextured(renderer, xa, xb, y, textureAsTexture, texture->w, texture->h, texture_dx, texture_dy); - } - } - - SDL_RenderPresent(renderer); - SDL_DestroyTexture(textureAsTexture); - - return (result); -} - -/*! -\brief Draws a polygon filled with the given texture. - -This standard version is calling multithreaded versions with NULL cache parameters. - -\param renderer The renderer to draw on. -\param vx array of x vector components -\param vy array of x vector components -\param n the amount of vectors in the vx and vy array -\param texture the sdl surface to use to fill the polygon -\param texture_dx the offset of the texture relative to the screeen. if you move the polygon 10 pixels -to the left and want the texture to apear the same you need to increase the texture_dx value -\param texture_dy see texture_dx - -\returns Returns 0 on success, -1 on failure. -*/ -int texturedPolygon(SDL_Renderer *renderer, const Sint16 * vx, const Sint16 * vy, int n, SDL_Surface *texture, int texture_dx, int texture_dy) -{ - /* - * Draw - */ - return (texturedPolygonMT(renderer, vx, vy, n, texture, texture_dx, texture_dy, NULL, NULL)); -} - -/* ---- Character */ - -/*! -\brief Global cache for NxM pixel font textures created at runtime. -*/ -static SDL_Texture *gfxPrimitivesFont[256]; - -/*! -\brief Pointer to the current font data. Default is a 8x8 pixel internal font. -*/ -static const unsigned char *currentFontdata = gfxPrimitivesFontdata; - -/*! -\brief Width of the current font. Default is 8. -*/ -static Uint32 charWidth = 8; - -/*! -\brief Height of the current font. Default is 8. -*/ -static Uint32 charHeight = 8; - -/*! -\brief Width for rendering. Autocalculated. -*/ -static Uint32 charWidthLocal = 8; - -/*! -\brief Height for rendering. Autocalculated. -*/ -static Uint32 charHeightLocal = 8; - -/*! -\brief Pitch of the current font in bytes. Default is 1. -*/ -static Uint32 charPitch = 1; - -/*! -\brief Characters 90deg clockwise rotations. Default is 0. Max is 3. -*/ -static Uint32 charRotation = 0; - -/*! -\brief Character data size in bytes of the current font. Default is 8. -*/ -static Uint32 charSize = 8; - -/*! -\brief Sets or resets the current global font data. - -The font data array is organized in follows: -[fontdata] = [character 0][character 1]...[character 255] where -[character n] = [byte 1 row 1][byte 2 row 1]...[byte {pitch} row 1][byte 1 row 2] ...[byte {pitch} row height] where -[byte n] = [bit 0]...[bit 7] where -[bit n] = [0 for transparent pixel|1 for colored pixel] - -\param fontdata Pointer to array of font data. Set to NULL, to reset global font to the default 8x8 font. -\param cw Width of character in bytes. Ignored if fontdata==NULL. -\param ch Height of character in bytes. Ignored if fontdata==NULL. -*/ -void gfxPrimitivesSetFont(const void *fontdata, Uint32 cw, Uint32 ch) -{ - int i; - - if ((fontdata) && (cw) && (ch)) { - currentFontdata = (unsigned char *)fontdata; - charWidth = cw; - charHeight = ch; - } else { - currentFontdata = gfxPrimitivesFontdata; - charWidth = 8; - charHeight = 8; - } - - charPitch = (charWidth+7)/8; - charSize = charPitch * charHeight; - - /* Maybe flip width/height for rendering */ - if ((charRotation==1) || (charRotation==3)) - { - charWidthLocal = charHeight; - charHeightLocal = charWidth; - } - else - { - charWidthLocal = charWidth; - charHeightLocal = charHeight; - } - - /* Clear character cache */ - for (i = 0; i < 256; i++) { - if (gfxPrimitivesFont[i]) { - SDL_DestroyTexture(gfxPrimitivesFont[i]); - gfxPrimitivesFont[i] = NULL; - } - } -} - -/*! -\brief Sets current global font character rotation steps. - -Default is 0 (no rotation). 1 = 90deg clockwise. 2 = 180deg clockwise. 3 = 270deg clockwise. -Changing the rotation, will reset the character cache. - -\param rotation Number of 90deg clockwise steps to rotate -*/ -void gfxPrimitivesSetFontRotation(Uint32 rotation) -{ - int i; - - rotation = rotation & 3; - if (charRotation != rotation) - { - /* Store rotation */ - charRotation = rotation; - - /* Maybe flip width/height for rendering */ - if ((charRotation==1) || (charRotation==3)) - { - charWidthLocal = charHeight; - charHeightLocal = charWidth; - } - else - { - charWidthLocal = charWidth; - charHeightLocal = charHeight; - } - - /* Clear character cache */ - for (i = 0; i < 256; i++) { - if (gfxPrimitivesFont[i]) { - SDL_DestroyTexture(gfxPrimitivesFont[i]); - gfxPrimitivesFont[i] = NULL; - } - } - } -} - -/*! -\brief Draw a character of the currently set font. - -\param renderer The Renderer to draw on. -\param x X (horizontal) coordinate of the upper left corner of the character. -\param y Y (vertical) coordinate of the upper left corner of the character. -\param c The character to draw. -\param r The red value of the character to draw. -\param g The green value of the character to draw. -\param b The blue value of the character to draw. -\param a The alpha value of the character to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int characterRGBA(SDL_Renderer *renderer, Sint16 x, Sint16 y, char c, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - SDL_Rect srect; - SDL_Rect drect; - int result; - Uint32 ix, iy; - const unsigned char *charpos; - Uint8 *curpos; - Uint8 patt, mask; - Uint8 *linepos; - Uint32 pitch; - SDL_Surface *character; - SDL_Surface *rotatedCharacter; - Uint32 ci; - - /* - * Setup source rectangle - */ - srect.x = 0; - srect.y = 0; - srect.w = charWidthLocal; - srect.h = charHeightLocal; - - /* - * Setup destination rectangle - */ - drect.x = x; - drect.y = y; - drect.w = charWidthLocal; - drect.h = charHeightLocal; - - /* Character index in cache */ - ci = (unsigned char) c; - - /* - * Create new charWidth x charHeight bitmap surface if not already present. - * Might get rotated later. - */ - if (gfxPrimitivesFont[ci] == NULL) { - /* - * Redraw character into surface - */ - character = SDL_CreateRGBSurface(SDL_SWSURFACE, - charWidth, charHeight, 32, - 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); - if (character == NULL) { - return (-1); - } - - charpos = currentFontdata + ci * charSize; - linepos = (Uint8 *)character->pixels; - pitch = character->pitch; - - /* - * Drawing loop - */ - patt = 0; - for (iy = 0; iy < charHeight; iy++) { - mask = 0x00; - curpos = linepos; - for (ix = 0; ix < charWidth; ix++) { - if (!(mask >>= 1)) { - patt = *charpos++; - mask = 0x80; - } - if (patt & mask) { - *(Uint32 *)curpos = 0xffffffff; - } else { - *(Uint32 *)curpos = 0; - } - curpos += 4; - } - linepos += pitch; - } - - /* Maybe rotate and replace cached image */ - if (charRotation>0) - { - rotatedCharacter = rotateSurface90Degrees(character, charRotation); - SDL_FreeSurface(character); - character = rotatedCharacter; - } - - /* Convert temp surface into texture */ - gfxPrimitivesFont[ci] = SDL_CreateTextureFromSurface(renderer, character); - SDL_FreeSurface(character); - - /* - * Check pointer - */ - if (gfxPrimitivesFont[ci] == NULL) { - return (-1); - } - } - - /* - * Set color - */ - result = 0; - result |= SDL_SetTextureColorMod(gfxPrimitivesFont[ci], r, g, b); - result |= SDL_SetTextureAlphaMod(gfxPrimitivesFont[ci], a); - - /* - * Draw texture onto destination - */ - result |= SDL_RenderCopy(renderer, gfxPrimitivesFont[ci], &srect, &drect); - - return (result); -} - - -/*! -\brief Draw a character of the currently set font. - -\param renderer The renderer to draw on. -\param x X (horizontal) coordinate of the upper left corner of the character. -\param y Y (vertical) coordinate of the upper left corner of the character. -\param c The character to draw. -\param color The color value of the character to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int characterColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, char c, Uint32 color) -{ - Uint8 *co = (Uint8 *)&color; - return characterRGBA(renderer, x, y, c, co[0], co[1], co[2], co[3]); -} - - -/*! -\brief Draw a string in the currently set font. - -The spacing between consequtive characters in the string is the fixed number of pixels -of the character width of the current global font. - -\param renderer The renderer to draw on. -\param x X (horizontal) coordinate of the upper left corner of the string. -\param y Y (vertical) coordinate of the upper left corner of the string. -\param s The string to draw. -\param color The color value of the string to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int stringColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return stringRGBA(renderer, x, y, s, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw a string in the currently set font. - -\param renderer The renderer to draw on. -\param x X (horizontal) coordinate of the upper left corner of the string. -\param y Y (vertical) coordinate of the upper left corner of the string. -\param s The string to draw. -\param r The red value of the string to draw. -\param g The green value of the string to draw. -\param b The blue value of the string to draw. -\param a The alpha value of the string to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int stringRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result = 0; - Sint16 curx = x; - Sint16 cury = y; - const char *curchar = s; - - while (*curchar && !result) { - result |= characterRGBA(renderer, curx, cury, *curchar, r, g, b, a); - switch (charRotation) - { - case 0: - curx += charWidthLocal; - break; - case 2: - curx -= charWidthLocal; - break; - case 1: - cury += charHeightLocal; - break; - case 3: - cury -= charHeightLocal; - break; - } - curchar++; - } - - return (result); -} - -/* ---- Bezier curve */ - -/*! -\brief Internal function to calculate bezier interpolator of data array with ndata values at position 't'. - -\param data Array of values. -\param ndata Size of array. -\param t Position for which to calculate interpolated value. t should be between [0, ndata]. - -\returns Interpolated value at position t, value[0] when t<0, value[n-1] when t>n. -*/ -double _evaluateBezier (double *data, int ndata, double t) -{ - double mu, result; - int n,k,kn,nn,nkn; - double blend,muk,munk; - - /* Sanity check bounds */ - if (t<0.0) { - return(data[0]); - } - if (t>=(double)ndata) { - return(data[ndata-1]); - } - - /* Adjust t to the range 0.0 to 1.0 */ - mu=t/(double)ndata; - - /* Calculate interpolate */ - n=ndata-1; - result=0.0; - muk = 1; - munk = pow(1-mu,(double)n); - for (k=0;k<=n;k++) { - nn = n; - kn = k; - nkn = n - k; - blend = muk * munk; - muk *= mu; - munk /= (1-mu); - while (nn >= 1) { - blend *= nn; - nn--; - if (kn > 1) { - blend /= (double)kn; - kn--; - } - if (nkn > 1) { - blend /= (double)nkn; - nkn--; - } - } - result += data[k] * blend; - } - - return (result); -} - -/*! -\brief Draw a bezier curve with alpha blending. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the bezier curve. -\param vy Vertex array containing Y coordinates of the points of the bezier curve. -\param n Number of points in the vertex array. Minimum number is 3. -\param s Number of steps for the interpolation. Minimum number is 2. -\param color The color value of the bezier curve to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int bezierColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, int s, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return bezierRGBA(renderer, vx, vy, n, s, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw a bezier curve with alpha blending. - -\param renderer The renderer to draw on. -\param vx Vertex array containing X coordinates of the points of the bezier curve. -\param vy Vertex array containing Y coordinates of the points of the bezier curve. -\param n Number of points in the vertex array. Minimum number is 3. -\param s Number of steps for the interpolation. Minimum number is 2. -\param r The red value of the bezier curve to draw. -\param g The green value of the bezier curve to draw. -\param b The blue value of the bezier curve to draw. -\param a The alpha value of the bezier curve to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int bezierRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, int s, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int result; - int i; - double *x, *y, t, stepsize; - Sint16 x1, y1, x2, y2; - - /* - * Sanity check - */ - if (n < 3) { - return (-1); - } - if (s < 2) { - return (-1); - } - - /* - * Variable setup - */ - stepsize=(double)1.0/(double)s; - - /* Transfer vertices into float arrays */ - if ((x=(double *)malloc(sizeof(double)*(n+1)))==NULL) { - return(-1); - } - if ((y=(double *)malloc(sizeof(double)*(n+1)))==NULL) { - free(x); - return(-1); - } - for (i=0; i0. -\param color The color value of the line to draw (0xRRGGBBAA). - -\returns Returns 0 on success, -1 on failure. -*/ -int thickLineColor(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint32 color) -{ - Uint8 *c = (Uint8 *)&color; - return thickLineRGBA(renderer, x1, y1, x2, y2, width, c[0], c[1], c[2], c[3]); -} - -/*! -\brief Draw a thick line with alpha blending. - -\param renderer The renderer to draw on. -\param x1 X coordinate of the first point of the line. -\param y1 Y coordinate of the first point of the line. -\param x2 X coordinate of the second point of the line. -\param y2 Y coordinate of the second point of the line. -\param width Width of the line in pixels. Must be >0. -\param r The red value of the character to draw. -\param g The green value of the character to draw. -\param b The blue value of the character to draw. -\param a The alpha value of the character to draw. - -\returns Returns 0 on success, -1 on failure. -*/ -int thickLineRGBA(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - int wh; - double dx, dy, dx1, dy1, dx2, dy2; - double l, wl2, nx, ny, ang, adj; - Sint16 px[4], py[4]; - - if (renderer == NULL) { - return -1; - } - - if (width < 1) { - return -1; - } - - /* Special case: thick "point" */ - if ((x1 == x2) && (y1 == y2)) { - wh = width / 2; - return boxRGBA(renderer, x1 - wh, y1 - wh, x2 + width, y2 + width, r, g, b, a); - } - - /* Special case: width == 1 */ - if (width == 1) { - return lineRGBA(renderer, x1, y1, x2, y2, r, g, b, a); - } - - /* Calculate offsets for sides */ - dx = (double)(x2 - x1); - dy = (double)(y2 - y1); - l = SDL_sqrt(dx*dx + dy*dy); - ang = SDL_atan2(dx, dy); - adj = 0.1 + 0.9 * SDL_fabs(SDL_cos(2.0 * ang)); - wl2 = ((double)width - adj)/(2.0 * l); - nx = dx * wl2; - ny = dy * wl2; - - /* Build polygon */ - dx1 = (double)x1; - dy1 = (double)y1; - dx2 = (double)x2; - dy2 = (double)y2; - px[0] = (Sint16)(dx1 + ny); - px[1] = (Sint16)(dx1 - ny); - px[2] = (Sint16)(dx2 - ny); - px[3] = (Sint16)(dx2 + ny); - py[0] = (Sint16)(dy1 - nx); - py[1] = (Sint16)(dy1 + nx); - py[2] = (Sint16)(dy2 + nx); - py[3] = (Sint16)(dy2 - nx); - - /* Draw polygon */ - return filledPolygonRGBA(renderer, px, py, 4, r, g, b, a); -} +/* + +SDL2_gfxPrimitives.c: graphics primitives for SDL2 renderers + +Copyright (C) 2012-2014 Andreas Schiffler +Modifications and additions for BBC BASIC (C) 2016-2019 Richard Russell + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +Andreas Schiffler -- aschiffler at ferzkopp dot net +Richard Russell -- richard at rtrussell dot co dot uk + +*/ + +#include +#include +#include +#include + +#include "SDL2_gfxPrimitives.h" +#include "SDL2_rotozoom.h" +#include "SDL2_gfxPrimitives_font.h" + +/* ---- Structures */ + +/*! +\brief The structure passed to the internal Bresenham iterator. +*/ +typedef struct { + Sint16 x, y; + int dx, dy, s1, s2, swapdir, error; + Uint32 count; +} SDL2_gfxBresenhamIterator; + +/* ---- Pixel */ + +/*! +\brief Draw pixel in currently set color. + +\param renderer The renderer to draw on. +\param x X (horizontal) coordinate of the pixel. +\param y Y (vertical) coordinate of the pixel. + +\returns Returns 0 on success, -1 on failure. +*/ +int pixel(SDL_Renderer *renderer, Sint16 x, Sint16 y) +{ + return SDL_RenderDrawPoint(renderer, x, y); +} + +/*! +\brief Draw pixel with blending enabled if a<255. + +\param renderer The renderer to draw on. +\param x X (horizontal) coordinate of the pixel. +\param y Y (vertical) coordinate of the pixel. +\param color The color value of the pixel to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int pixelColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return pixelRGBA(renderer, x, y, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw pixel with blending enabled if a<255. + +\param renderer The renderer to draw on. +\param x X (horizontal) coordinate of the pixel. +\param y Y (vertical) coordinate of the pixel. +\param r The red color value of the pixel to draw. +\param g The green color value of the pixel to draw. +\param b The blue color value of the pixel to draw. +\param a The alpha value of the pixel to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int pixelRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + result |= SDL_RenderDrawPoint(renderer, x, y); + return result; +} + +/*! +\brief Draw pixel with blending enabled and using alpha weight on color. + +\param renderer The renderer to draw on. +\param x The horizontal coordinate of the pixel. +\param y The vertical position of the pixel. +\param r The red color value of the pixel to draw. +\param g The green color value of the pixel to draw. +\param b The blue color value of the pixel to draw. +\param a The alpha value of the pixel to draw. +\param weight The weight multiplied into the alpha value of the pixel. + +\returns Returns 0 on success, -1 on failure. +*/ +int pixelRGBAWeight(SDL_Renderer * renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint32 weight) +{ + /* + * Modify Alpha by weight + */ + Uint32 ax = a; + ax = ((ax * weight) >> 8); + if (ax > 255) { + a = 255; + } else { + a = (Uint8)(ax & 0x000000ff); + } + + return pixelRGBA(renderer, x, y, r, g, b, a); +} + +/* ---- Hline */ + +/*! +\brief Draw horizontal line in currently set color + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. left) of the line. +\param x2 X coordinate of the second point (i.e. right) of the line. +\param y Y coordinate of the points of the line. + +\returns Returns 0 on success, -1 on failure. +*/ +int hline(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y) +{ + return SDL_RenderDrawLine(renderer, x1, y, x2, y);; +} + + +/*! +\brief Draw horizontal line with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. left) of the line. +\param x2 X coordinate of the second point (i.e. right) of the line. +\param y Y coordinate of the points of the line. +\param color The color value of the line to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int hlineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return hlineRGBA(renderer, x1, x2, y, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw horizontal line with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. left) of the line. +\param x2 X coordinate of the second point (i.e. right) of the line. +\param y Y coordinate of the points of the line. +\param r The red value of the line to draw. +\param g The green value of the line to draw. +\param b The blue value of the line to draw. +\param a The alpha value of the line to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int hlineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 x2, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + result |= SDL_RenderDrawLine(renderer, x1, y, x2, y); + return result; +} + +/* ---- Vline */ + +/*! +\brief Draw vertical line in currently set color + +\param renderer The renderer to draw on. +\param x X coordinate of points of the line. +\param y1 Y coordinate of the first point (i.e. top) of the line. +\param y2 Y coordinate of the second point (i.e. bottom) of the line. + +\returns Returns 0 on success, -1 on failure. +*/ +int vline(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2) +{ + return SDL_RenderDrawLine(renderer, x, y1, x, y2);; +} + +/*! +\brief Draw vertical line with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the points of the line. +\param y1 Y coordinate of the first point (i.e. top) of the line. +\param y2 Y coordinate of the second point (i.e. bottom) of the line. +\param color The color value of the line to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int vlineColor(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return vlineRGBA(renderer, x, y1, y2, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw vertical line with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the points of the line. +\param y1 Y coordinate of the first point (i.e. top) of the line. +\param y2 Y coordinate of the second point (i.e. bottom) of the line. +\param r The red value of the line to draw. +\param g The green value of the line to draw. +\param b The blue value of the line to draw. +\param a The alpha value of the line to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int vlineRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y1, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + result |= SDL_RenderDrawLine(renderer, x, y1, x, y2); + return result; +} + +/* ---- Rectangle */ + +/*! +\brief Draw rectangle with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the rectangle. +\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. +\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. +\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. +\param color The color value of the rectangle to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int rectangleColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return rectangleRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw rectangle with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the rectangle. +\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. +\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. +\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. +\param r The red value of the rectangle to draw. +\param g The green value of the rectangle to draw. +\param b The blue value of the rectangle to draw. +\param a The alpha value of the rectangle to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int rectangleRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + Sint16 tmp; + SDL_Rect rect; + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 == y2) { + return (pixelRGBA(renderer, x1, y1, r, g, b, a)); + } else { + return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); + } + } else { + if (y1 == y2) { + return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); + } + } + + /* + * Swap x1, x2 if required + */ + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + + /* + * Swap y1, y2 if required + */ + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + + /* + * Create destination rect + */ + rect.x = x1; + rect.y = y1; + rect.w = x2 - x1; + rect.h = y2 - y1; + + /* + * Draw + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + result |= SDL_RenderDrawRect(renderer, &rect); + return result; +} + +/* ---- Rounded Rectangle */ + +/*! +\brief Draw rounded-corner rectangle with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the rectangle. +\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. +\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. +\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. +\param rad The radius of the corner arc. +\param color The color value of the rectangle to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int roundedRectangleColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return roundedRectangleRGBA(renderer, x1, y1, x2, y2, rad, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw rounded-corner rectangle with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the rectangle. +\param y1 Y coordinate of the first point (i.e. top right) of the rectangle. +\param x2 X coordinate of the second point (i.e. bottom left) of the rectangle. +\param y2 Y coordinate of the second point (i.e. bottom left) of the rectangle. +\param rad The radius of the corner arc. +\param r The red value of the rectangle to draw. +\param g The green value of the rectangle to draw. +\param b The blue value of the rectangle to draw. +\param a The alpha value of the rectangle to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int roundedRectangleRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result = 0; + Sint16 tmp; + Sint16 w, h; + Sint16 xx1, xx2; + Sint16 yy1, yy2; + + /* + * Check renderer + */ + if (renderer == NULL) + { + return -1; + } + + /* + * Check radius vor valid range + */ + if (rad < 0) { + return -1; + } + + /* + * Special case - no rounding + */ + if (rad <= 1) { + return rectangleRGBA(renderer, x1, y1, x2, y2, r, g, b, a); + } + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 == y2) { + return (pixelRGBA(renderer, x1, y1, r, g, b, a)); + } else { + return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); + } + } else { + if (y1 == y2) { + return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); + } + } + + /* + * Swap x1, x2 if required + */ + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + + /* + * Swap y1, y2 if required + */ + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + + /* + * Calculate width&height + */ + w = x2 - x1; + h = y2 - y1; + + /* + * Maybe adjust radius + */ + if ((rad * 2) > w) + { + rad = w / 2; + } + if ((rad * 2) > h) + { + rad = h / 2; + } + + /* + * Draw corners + */ + xx1 = x1 + rad; + xx2 = x2 - rad; + yy1 = y1 + rad; + yy2 = y2 - rad; + result |= arcRGBA(renderer, xx1, yy1, rad, 180, 270, r, g, b, a); + result |= arcRGBA(renderer, xx2, yy1, rad, 270, 360, r, g, b, a); + result |= arcRGBA(renderer, xx1, yy2, rad, 90, 180, r, g, b, a); + result |= arcRGBA(renderer, xx2, yy2, rad, 0, 90, r, g, b, a); + + /* + * Draw lines + */ + if (xx1 <= xx2) { + result |= hlineRGBA(renderer, xx1, xx2, y1, r, g, b, a); + result |= hlineRGBA(renderer, xx1, xx2, y2, r, g, b, a); + } + if (yy1 <= yy2) { + result |= vlineRGBA(renderer, x1, yy1, yy2, r, g, b, a); + result |= vlineRGBA(renderer, x2, yy1, yy2, r, g, b, a); + } + + return result; +} + +/* ---- Rounded Box */ + +/*! +\brief Draw rounded-corner box (filled rectangle) with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the box. +\param y1 Y coordinate of the first point (i.e. top right) of the box. +\param x2 X coordinate of the second point (i.e. bottom left) of the box. +\param y2 Y coordinate of the second point (i.e. bottom left) of the box. +\param rad The radius of the corner arcs of the box. +\param color The color value of the box to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int roundedBoxColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 rad, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return roundedBoxRGBA(renderer, x1, y1, x2, y2, rad, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw rounded-corner box (filled rectangle) with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the box. +\param y1 Y coordinate of the first point (i.e. top right) of the box. +\param x2 X coordinate of the second point (i.e. bottom left) of the box. +\param y2 Y coordinate of the second point (i.e. bottom left) of the box. +\param rad The radius of the corner arcs of the box. +\param r The red value of the box to draw. +\param g The green value of the box to draw. +\param b The blue value of the box to draw. +\param a The alpha value of the box to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int roundedBoxRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, + Sint16 y2, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + Sint16 w, h, r2, tmp; + Sint16 cx = 0; + Sint16 cy = rad; + Sint16 ocx = (Sint16) 0xffff; + Sint16 ocy = (Sint16) 0xffff; + Sint16 df = 1 - rad; + Sint16 d_e = 3; + Sint16 d_se = -2 * rad + 5; + Sint16 xpcx, xmcx, xpcy, xmcy; + Sint16 ypcy, ymcy, ypcx, ymcx; + Sint16 x, y, dx, dy; + + /* + * Check destination renderer + */ + if (renderer == NULL) + { + return -1; + } + + /* + * Check radius vor valid range + */ + if (rad < 0) { + return -1; + } + + /* + * Special case - no rounding + */ + if (rad <= 1) { + return boxRGBA(renderer, x1, y1, x2, y2, r, g, b, a); + } + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 == y2) { + return (pixelRGBA(renderer, x1, y1, r, g, b, a)); + } else { + return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); + } + } else { + if (y1 == y2) { + return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); + } + } + + /* + * Swap x1, x2 if required + */ + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + + /* + * Swap y1, y2 if required + */ + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + + /* + * Calculate width&height + */ + w = x2 - x1 + 1; + h = y2 - y1 + 1; + + /* + * Maybe adjust radius + */ + r2 = rad + rad; + if (r2 > w) + { + rad = w / 2; + r2 = rad + rad; + } + if (r2 > h) + { + rad = h / 2; + } + + /* Setup filled circle drawing for corners */ + x = x1 + rad; + y = y1 + rad; + dx = x2 - x1 - rad - rad; + dy = y2 - y1 - rad - rad; + + /* + * Set color + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + /* + * Draw corners + */ + do { + xpcx = x + cx; + xmcx = x - cx; + xpcy = x + cy; + xmcy = x - cy; + if (ocy != cy) { + if (cy > 0) { + ypcy = y + cy; + ymcy = y - cy; + result |= hline(renderer, xmcx, xpcx + dx, ypcy + dy); + result |= hline(renderer, xmcx, xpcx + dx, ymcy); + } else { + result |= hline(renderer, xmcx, xpcx + dx, y); + } + ocy = cy; + } + if (ocx != cx) { + if (cx != cy) { + if (cx > 0) { + ypcx = y + cx; + ymcx = y - cx; + result |= hline(renderer, xmcy, xpcy + dx, ymcx); + result |= hline(renderer, xmcy, xpcy + dx, ypcx + dy); + } else { + result |= hline(renderer, xmcy, xpcy + dx, y); + } + } + ocx = cx; + } + + /* + * Update + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); + + /* Inside */ + if (dx > 0 && dy > 0) { + result |= boxRGBA(renderer, x1, y1 + rad + 1, x2, y2 - rad, r, g, b, a); + } + + return (result); +} + +/* ---- Box */ + +/*! +\brief Draw box (filled rectangle) with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the box. +\param y1 Y coordinate of the first point (i.e. top right) of the box. +\param x2 X coordinate of the second point (i.e. bottom left) of the box. +\param y2 Y coordinate of the second point (i.e. bottom left) of the box. +\param color The color value of the box to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int boxColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return boxRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw box (filled rectangle) with blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. top right) of the box. +\param y1 Y coordinate of the first point (i.e. top right) of the box. +\param x2 X coordinate of the second point (i.e. bottom left) of the box. +\param y2 Y coordinate of the second point (i.e. bottom left) of the box. +\param r The red value of the box to draw. +\param g The green value of the box to draw. +\param b The blue value of the box to draw. +\param a The alpha value of the box to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int boxRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + Sint16 tmp; + SDL_Rect rect; + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 == y2) { + return (pixelRGBA(renderer, x1, y1, r, g, b, a)); + } else { + return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); + } + } else { + if (y1 == y2) { + return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); + } + } + + /* + * Swap x1, x2 if required + */ + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + + /* + * Swap y1, y2 if required + */ + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + + /* + * Create destination rect + */ + rect.x = x1; + rect.y = y1; + rect.w = x2 - x1 + 1; + rect.h = y2 - y1 + 1; + + /* + * Draw + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + result |= SDL_RenderFillRect(renderer, &rect); + return result; +} + +/* ----- Line */ + +/*! +\brief Draw line with alpha blending using the currently set color. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the line. +\param y1 Y coordinate of the first point of the line. +\param x2 X coordinate of the second point of the line. +\param y2 Y coordinate of the second point of the line. + +\returns Returns 0 on success, -1 on failure. +*/ +int line(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2) +{ + /* + * Draw + */ + return SDL_RenderDrawLine(renderer, x1, y1, x2, y2); +} + +/*! +\brief Draw line with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the line. +\param y1 Y coordinate of the first point of the line. +\param x2 X coordinate of the second point of the line. +\param y2 Y coordinate of the seond point of the line. +\param color The color value of the line to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int lineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return lineRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw line with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the line. +\param y1 Y coordinate of the first point of the line. +\param x2 X coordinate of the second point of the line. +\param y2 Y coordinate of the second point of the line. +\param r The red value of the line to draw. +\param g The green value of the line to draw. +\param b The blue value of the line to draw. +\param a The alpha value of the line to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int lineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + int result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + result |= SDL_RenderDrawLine(renderer, x1, y1, x2, y2); + return result; +} + +/* ---- AA Line */ + +#define AAlevels 256 +#define AAbits 8 + +/*! +\brief Internal function to draw anti-aliased line with alpha blending and endpoint control. + +This implementation of the Wu antialiasing code is based on Mike Abrash's +DDJ article which was reprinted as Chapter 42 of his Graphics Programming +Black Book, but has been optimized to work with SDL and utilizes 32-bit +fixed-point arithmetic by A. Schiffler. The endpoint control allows the +supression to draw the last pixel useful for rendering continous aa-lines +with alpha<255. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the aa-line. +\param y1 Y coordinate of the first point of the aa-line. +\param x2 X coordinate of the second point of the aa-line. +\param y2 Y coordinate of the second point of the aa-line. +\param r The red value of the aa-line to draw. +\param g The green value of the aa-line to draw. +\param b The blue value of the aa-line to draw. +\param a The alpha value of the aa-line to draw. +\param draw_endpoint Flag indicating if the endpoint should be drawn; draw if non-zero. + +\returns Returns 0 on success, -1 on failure. +*/ +int _aalineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a, int draw_endpoint) +{ + Sint32 xx0, yy0, xx1, yy1; + int result; + Uint32 intshift, erracc, erradj; + Uint32 erracctmp, wgt; + int dx, dy, tmp, xdir, y0p1, x0pxdir; + + /* + * Keep on working with 32bit numbers + */ + xx0 = x1; + yy0 = y1; + xx1 = x2; + yy1 = y2; + + /* + * Reorder points to make dy positive + */ + if (yy0 > yy1) { + tmp = yy0; + yy0 = yy1; + yy1 = tmp; + tmp = xx0; + xx0 = xx1; + xx1 = tmp; + } + + /* + * Calculate distance + */ + dx = xx1 - xx0; + dy = yy1 - yy0; + + /* + * Adjust for negative dx and set xdir + */ + if (dx >= 0) { + xdir = 1; + } else { + xdir = -1; + dx = (-dx); + } + + /* + * Check for special cases + */ + if (dx == 0) { + /* + * Vertical line + */ + if (draw_endpoint) + { + return (vlineRGBA(renderer, x1, y1, y2, r, g, b, a)); + } else { + if (dy > 0) { + return (vlineRGBA(renderer, x1, yy0, yy0+dy, r, g, b, a)); + } else { + return (pixelRGBA(renderer, x1, y1, r, g, b, a)); + } + } + } else if (dy == 0) { + /* + * Horizontal line + */ + if (draw_endpoint) + { + return (hlineRGBA(renderer, x1, x2, y1, r, g, b, a)); + } else { + if (dx > 0) { + return (hlineRGBA(renderer, xx0, xx0+(xdir*dx), y1, r, g, b, a)); + } else { + return (pixelRGBA(renderer, x1, y1, r, g, b, a)); + } + } + } else if ((dx == dy) && (draw_endpoint)) { + /* + * Diagonal line (with endpoint) + */ + return (lineRGBA(renderer, x1, y1, x2, y2, r, g, b, a)); + } + + + /* + * Line is not horizontal, vertical or diagonal (with endpoint) + */ + result = 0; + + /* + * Zero accumulator + */ + erracc = 0; + + /* + * # of bits by which to shift erracc to get intensity level + */ + intshift = 32 - AAbits; + + /* + * Draw the initial pixel in the foreground color + */ + result |= pixelRGBA(renderer, x1, y1, r, g, b, a); + + /* + * x-major or y-major? + */ + if (dy > dx) { + + /* + * y-major. Calculate 16-bit fixed point fractional part of a pixel that + * X advances every time Y advances 1 pixel, truncating the result so that + * we won't overrun the endpoint along the X axis + */ + /* + * Not-so-portable version: erradj = ((Uint64)dx << 32) / (Uint64)dy; + */ + erradj = ((dx << 16) / dy) << 16; + + /* + * draw all pixels other than the first and last + */ + x0pxdir = xx0 + xdir; + while (--dy) { + erracctmp = erracc; + erracc += erradj; + if (erracc <= erracctmp) { + /* + * rollover in error accumulator, x coord advances + */ + xx0 = x0pxdir; + x0pxdir += xdir; + } + yy0++; /* y-major so always advance Y */ + + /* + * the AAbits most significant bits of erracc give us the intensity + * weighting for this pixel, and the complement of the weighting for + * the paired pixel. + */ + wgt = (erracc >> intshift) & 255; + result |= pixelRGBAWeight (renderer, xx0, yy0, r, g, b, a, 255 - wgt); + result |= pixelRGBAWeight (renderer, x0pxdir, yy0, r, g, b, a, wgt); + } + + } else { + + /* + * x-major line. Calculate 16-bit fixed-point fractional part of a pixel + * that Y advances each time X advances 1 pixel, truncating the result so + * that we won't overrun the endpoint along the X axis. + */ + /* + * Not-so-portable version: erradj = ((Uint64)dy << 32) / (Uint64)dx; + */ + erradj = ((dy << 16) / dx) << 16; + + /* + * draw all pixels other than the first and last + */ + y0p1 = yy0 + 1; + while (--dx) { + + erracctmp = erracc; + erracc += erradj; + if (erracc <= erracctmp) { + /* + * Accumulator turned over, advance y + */ + yy0 = y0p1; + y0p1++; + } + xx0 += xdir; /* x-major so always advance X */ + /* + * the AAbits most significant bits of erracc give us the intensity + * weighting for this pixel, and the complement of the weighting for + * the paired pixel. + */ + wgt = (erracc >> intshift) & 255; + result |= pixelRGBAWeight (renderer, xx0, yy0, r, g, b, a, 255 - wgt); + result |= pixelRGBAWeight (renderer, xx0, y0p1, r, g, b, a, wgt); + } + } + + /* + * Do we have to draw the endpoint + */ + if (draw_endpoint) { + /* + * Draw final pixel, always exactly intersected by the line and doesn't + * need to be weighted. + */ + result |= pixelRGBA (renderer, x2, y2, r, g, b, a); + } + + return (result); +} + +/*! +\brief Draw anti-aliased line with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the aa-line. +\param y1 Y coordinate of the first point of the aa-line. +\param x2 X coordinate of the second point of the aa-line. +\param y2 Y coordinate of the second point of the aa-line. +\param color The color value of the aa-line to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int aalineColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return _aalineRGBA(renderer, x1, y1, x2, y2, c[0], c[1], c[2], c[3], 1); +} + +/*! +\brief Draw anti-aliased line with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the aa-line. +\param y1 Y coordinate of the first point of the aa-line. +\param x2 X coordinate of the second point of the aa-line. +\param y2 Y coordinate of the second point of the aa-line. +\param r The red value of the aa-line to draw. +\param g The green value of the aa-line to draw. +\param b The blue value of the aa-line to draw. +\param a The alpha value of the aa-line to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aalineRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return _aalineRGBA(renderer, x1, y1, x2, y2, r, g, b, a, 1); +} + +/* ----- Circle */ + +/*! +\brief Draw circle with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the circle. +\param y Y coordinate of the center of the circle. +\param rad Radius in pixels of the circle. +\param color The color value of the circle to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int circleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return ellipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw circle with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the circle. +\param y Y coordinate of the center of the circle. +\param rad Radius in pixels of the circle. +\param r The red value of the circle to draw. +\param g The green value of the circle to draw. +\param b The blue value of the circle to draw. +\param a The alpha value of the circle to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int circleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return ellipseRGBA(renderer, x, y, rad, rad, r, g, b, a); +} + +/* ----- Arc */ + +/*! +\brief Arc with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the arc. +\param y Y coordinate of the center of the arc. +\param rad Radius in pixels of the arc. +\param start Starting radius in degrees of the arc. 0 degrees is right, increasing clockwise. +\param end Ending radius in degrees of the arc. 0 degrees is right, increasing clockwise. +\param color The color value of the arc to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int arcColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return arcRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Arc with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the arc. +\param y Y coordinate of the center of the arc. +\param rad Radius in pixels of the arc. +\param start Starting radius in degrees of the arc. 0 degrees is right, increasing clockwise. +\param end Ending radius in degrees of the arc. 0 degrees is right, increasing clockwise. +\param r The red value of the arc to draw. +\param g The green value of the arc to draw. +\param b The blue value of the arc to draw. +\param a The alpha value of the arc to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +/* TODO: rewrite algorithm; arc endpoints are not always drawn */ +int arcRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + Sint16 cx = 0; + Sint16 cy = rad; + Sint16 df = 1 - rad; + Sint16 d_e = 3; + Sint16 d_se = -2 * rad + 5; + Sint16 xpcx, xmcx, xpcy, xmcy; + Sint16 ypcy, ymcy, ypcx, ymcx; + Uint8 drawoct; + int startoct, endoct, oct, stopval_start = 0, stopval_end = 0; + double dstart, dend, temp = 0.; + + /* + * Sanity check radius + */ + if (rad < 0) { + return (-1); + } + + /* + * Special case for rad=0 - draw a point + */ + if (rad == 0) { + return (pixelRGBA(renderer, x, y, r, g, b, a)); + } + + /* + Octant labelling + + \ 5 | 6 / + \ | / + 4 \ | / 7 + \|/ + ------+------ +x + /|\ + 3 / | \ 0 + / | \ + / 2 | 1 \ + +y + + Initially reset bitmask to 0x00000000 + the set whether or not to keep drawing a given octant. + For example: 0x00111100 means we're drawing in octants 2-5 + */ + drawoct = 0; + + /* + * Fixup angles + */ + start %= 360; + end %= 360; + /* 0 <= start & end < 360; note that sometimes start > end - if so, arc goes back through 0. */ + while (start < 0) start += 360; + while (end < 0) end += 360; + start %= 360; + end %= 360; + + /* now, we find which octants we're drawing in. */ + startoct = start / 45; + endoct = end / 45; + oct = startoct - 1; + + /* stopval_start, stopval_end; what values of cx to stop at. */ + do { + oct = (oct + 1) % 8; + + if (oct == startoct) { + /* need to compute stopval_start for this octant. Look at picture above if this is unclear */ + dstart = (double)start; + switch (oct) + { + case 0: + case 3: + temp = sin(dstart * M_PI / 180.); + break; + case 1: + case 6: + temp = cos(dstart * M_PI / 180.); + break; + case 2: + case 5: + temp = -cos(dstart * M_PI / 180.); + break; + case 4: + case 7: + temp = -sin(dstart * M_PI / 180.); + break; + } + temp *= rad; + stopval_start = (int)temp; + + /* + This isn't arbitrary, but requires graph paper to explain well. + The basic idea is that we're always changing drawoct after we draw, so we + stop immediately after we render the last sensible pixel at x = ((int)temp). + and whether to draw in this octant initially + */ + if (oct % 2) drawoct |= (1 << oct); /* this is basically like saying drawoct[oct] = true, if drawoct were a bool array */ + else drawoct &= 255 - (1 << oct); /* this is basically like saying drawoct[oct] = false */ + } + if (oct == endoct) { + /* need to compute stopval_end for this octant */ + dend = (double)end; + switch (oct) + { + case 0: + case 3: + temp = sin(dend * M_PI / 180); + break; + case 1: + case 6: + temp = cos(dend * M_PI / 180); + break; + case 2: + case 5: + temp = -cos(dend * M_PI / 180); + break; + case 4: + case 7: + temp = -sin(dend * M_PI / 180); + break; + } + temp *= rad; + stopval_end = (int)temp; + + /* and whether to draw in this octant initially */ + if (startoct == endoct) { + /* note: we start drawing, stop, then start again in this case */ + /* otherwise: we only draw in this octant, so initialize it to false, it will get set back to true */ + if (start > end) { + /* unfortunately, if we're in the same octant and need to draw over the whole circle, */ + /* we need to set the rest to true, because the while loop will end at the bottom. */ + drawoct = 255; + } else { + drawoct &= 255 - (1 << oct); + } + } + else if (oct % 2) drawoct &= 255 - (1 << oct); + else drawoct |= (1 << oct); + } else if (oct != startoct) { /* already verified that it's != endoct */ + drawoct |= (1 << oct); /* draw this entire segment */ + } + } while (oct != endoct); + + /* so now we have what octants to draw and when to draw them. all that's left is the actual raster code. */ + + /* + * Set color + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + /* + * Draw arc + */ + do { + ypcy = y + cy; + ymcy = y - cy; + if (cx > 0) { + xpcx = x + cx; + xmcx = x - cx; + + /* always check if we're drawing a certain octant before adding a pixel to that octant. */ + if (drawoct & 4) result |= pixel(renderer, xmcx, ypcy); + if (drawoct & 2) result |= pixel(renderer, xpcx, ypcy); + if (drawoct & 32) result |= pixel(renderer, xmcx, ymcy); + if (drawoct & 64) result |= pixel(renderer, xpcx, ymcy); + } else { + if (drawoct & 96) result |= pixel(renderer, x, ymcy); + if (drawoct & 6) result |= pixel(renderer, x, ypcy); + } + + xpcy = x + cy; + xmcy = x - cy; + if (cx > 0 && cx != cy) { + ypcx = y + cx; + ymcx = y - cx; + if (drawoct & 8) result |= pixel(renderer, xmcy, ypcx); + if (drawoct & 1) result |= pixel(renderer, xpcy, ypcx); + if (drawoct & 16) result |= pixel(renderer, xmcy, ymcx); + if (drawoct & 128) result |= pixel(renderer, xpcy, ymcx); + } else if (cx == 0) { + if (drawoct & 24) result |= pixel(renderer, xmcy, y); + if (drawoct & 129) result |= pixel(renderer, xpcy, y); + } + + /* + * Update whether we're drawing an octant + */ + if (stopval_start == cx) { + /* works like an on-off switch. */ + /* This is just in case start & end are in the same octant. */ + if (drawoct & (1 << startoct)) drawoct &= 255 - (1 << startoct); + else drawoct |= (1 << startoct); + } + if (stopval_end == cx) { + if (drawoct & (1 << endoct)) drawoct &= 255 - (1 << endoct); + else drawoct |= (1 << endoct); + } + + /* + * Update pixels + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); + + return (result); +} + +/* ----- AA Circle */ + +/*! +\brief Draw anti-aliased circle with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the aa-circle. +\param y Y coordinate of the center of the aa-circle. +\param rad Radius in pixels of the aa-circle. +\param color The color value of the aa-circle to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int aacircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return aaellipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw anti-aliased circle with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the aa-circle. +\param y Y coordinate of the center of the aa-circle. +\param rad Radius in pixels of the aa-circle. +\param r The red value of the aa-circle to draw. +\param g The green value of the aa-circle to draw. +\param b The blue value of the aa-circle to draw. +\param a The alpha value of the aa-circle to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aacircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return aaellipseRGBA(renderer, x, y, rad, rad, r, g, b, a); +} + +/* ----- Filled Circle */ + +/*! +\brief Draw filled circle with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the filled circle. +\param y Y coordinate of the center of the filled circle. +\param rad Radius in pixels of the filled circle. +\param color The color value of the filled circle to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int filledCircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return filledEllipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw filled circle with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the filled circle. +\param y Y coordinate of the center of the filled circle. +\param rad Radius in pixels of the filled circle. +\param r The red value of the filled circle to draw. +\param g The green value of the filled circle to draw. +\param b The blue value of the filled circle to draw. +\param a The alpha value of the filled circle to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int filledCircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return filledEllipseRGBA(renderer, x, y, rad, rad, r, g, b, a); +} + +/* ----- Ellipse */ + +/*! +\brief Draw ellipse with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the ellipse. +\param y Y coordinate of the center of the ellipse. +\param rx Horizontal radius in pixels of the ellipse. +\param ry Vertical radius in pixels of the ellipse. +\param color The color value of the ellipse to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int ellipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return ellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw ellipse with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the ellipse. +\param y Y coordinate of the center of the ellipse. +\param rx Horizontal radius in pixels of the ellipse. +\param ry Vertical radius in pixels of the ellipse. +\param r The red value of the ellipse to draw. +\param g The green value of the ellipse to draw. +\param b The blue value of the ellipse to draw. +\param a The alpha value of the ellipse to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int ellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + int ix, iy; + int h, i, j, k; + int oh, oi, oj, ok; + int xmh, xph, ypk, ymk; + int xmi, xpi, ymj, ypj; + int xmj, xpj, ymi, ypi; + int xmk, xpk, ymh, yph; + + /* + * Sanity check radii + */ + if ((rx < 0) || (ry < 0)) { + return (-1); + } + + /* + * Special case for rx=0 - draw a vline + */ + if (rx == 0) { + return (vlineRGBA(renderer, x, y - ry, y + ry, r, g, b, a)); + } + /* + * Special case for ry=0 - draw a hline + */ + if (ry == 0) { + return (hlineRGBA(renderer, x - rx, x + rx, y, r, g, b, a)); + } + + /* + * Set color + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + /* + * Init vars + */ + oh = oi = oj = ok = 0xFFFF; + + /* + * Draw + */ + if (rx > ry) { + ix = 0; + iy = rx * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if (((ok != k) && (oj != k)) || ((oj != j) && (ok != j)) || (k != j)) { + xph = x + h; + xmh = x - h; + if (k > 0) { + ypk = y + k; + ymk = y - k; + result |= pixel(renderer, xmh, ypk); + result |= pixel(renderer, xph, ypk); + result |= pixel(renderer, xmh, ymk); + result |= pixel(renderer, xph, ymk); + } else { + result |= pixel(renderer, xmh, y); + result |= pixel(renderer, xph, y); + } + ok = k; + xpi = x + i; + xmi = x - i; + if (j > 0) { + ypj = y + j; + ymj = y - j; + result |= pixel(renderer, xmi, ypj); + result |= pixel(renderer, xpi, ypj); + result |= pixel(renderer, xmi, ymj); + result |= pixel(renderer, xpi, ymj); + } else { + result |= pixel(renderer, xmi, y); + result |= pixel(renderer, xpi, y); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if (((oi != i) && (oh != i)) || ((oh != h) && (oi != h) && (i != h))) { + xmj = x - j; + xpj = x + j; + if (i > 0) { + ypi = y + i; + ymi = y - i; + result |= pixel(renderer, xmj, ypi); + result |= pixel(renderer, xpj, ypi); + result |= pixel(renderer, xmj, ymi); + result |= pixel(renderer, xpj, ymi); + } else { + result |= pixel(renderer, xmj, y); + result |= pixel(renderer, xpj, y); + } + oi = i; + xmk = x - k; + xpk = x + k; + if (h > 0) { + yph = y + h; + ymh = y - h; + result |= pixel(renderer, xmk, yph); + result |= pixel(renderer, xpk, yph); + result |= pixel(renderer, xmk, ymh); + result |= pixel(renderer, xpk, ymh); + } else { + result |= pixel(renderer, xmk, y); + result |= pixel(renderer, xpk, y); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + + return (result); +} + +/* ----- AA Ellipse */ + +/* Windows targets do not have lrint, so provide a local inline version */ +#if defined(_MSC_VER) +/* Detect 64bit and use intrinsic version */ +#ifdef _M_X64 +#include +static __inline long + lrint(float f) +{ + return _mm_cvtss_si32(_mm_load_ss(&f)); +} +#elif defined(_M_IX86) +__inline long int + lrint (double flt) +{ + int intgr; + _asm + { + fld flt + fistp intgr + }; + return intgr; +} +#elif defined(_M_ARM) +#include +#pragma warning(push) +#pragma warning(disable: 4716) +__declspec(naked) long int + lrint (double flt) +{ + __emit(0xEC410B10); // fmdrr d0, r0, r1 + __emit(0xEEBD0B40); // ftosid s0, d0 + __emit(0xEE100A10); // fmrs r0, s0 + __emit(0xE12FFF1E); // bx lr +} +#pragma warning(pop) +#else +#error lrint needed for MSVC on non X86/AMD64/ARM targets. +#endif +#endif + +/*! +\brief Draw anti-aliased ellipse with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the aa-ellipse. +\param y Y coordinate of the center of the aa-ellipse. +\param rx Horizontal radius in pixels of the aa-ellipse. +\param ry Vertical radius in pixels of the aa-ellipse. +\param color The color value of the aa-ellipse to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int aaellipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return aaellipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw anti-aliased ellipse with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the aa-ellipse. +\param y Y coordinate of the center of the aa-ellipse. +\param rx Horizontal radius in pixels of the aa-ellipse. +\param ry Vertical radius in pixels of the aa-ellipse. +\param r The red value of the aa-ellipse to draw. +\param g The green value of the aa-ellipse to draw. +\param b The blue value of the aa-ellipse to draw. +\param a The alpha value of the aa-ellipse to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aaellipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + int i; + int a2, b2, ds, dt, dxt, t, s, d; + Sint16 xp, yp, xs, ys, dyt, od, xx, yy, xc2, yc2; + float cp; + double sab; + Uint8 weight, iweight; + + /* + * Sanity check radii + */ + if ((rx < 0) || (ry < 0)) { + return (-1); + } + + /* + * Special cases for rx=0 and/or ry=0: draw a hline/vline/pixel + */ + if (rx == 0) { + if (ry == 0) { + return (pixel(renderer, x, y)); + } else { + return (vline(renderer, x, y - ry, y + ry)); + } + } else { + if (ry == 0) { + return (hline(renderer, x - rx, x + rx, y)); + } + } + + /* Variable setup */ + a2 = rx * rx; + b2 = ry * ry; + + ds = 2 * a2; + dt = 2 * b2; + + xc2 = 2 * x; + yc2 = 2 * y; + + sab = sqrt((double)(a2 + b2)); + od = (Sint16)lrint(sab*0.01) + 1; /* introduce some overdraw */ + dxt = (Sint16)lrint((double)a2 / sab) + od; + + t = 0; + s = -2 * a2 * ry; + d = 0; + + xp = x; + yp = y - ry; + + /* Draw */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + + /* "End points" */ + result |= pixelRGBA(renderer, xp, yp, r, g, b, a); + result |= pixelRGBA(renderer, xc2 - xp, yp, r, g, b, a); + result |= pixelRGBA(renderer, xp, yc2 - yp, r, g, b, a); + result |= pixelRGBA(renderer, xc2 - xp, yc2 - yp, r, g, b, a); + + for (i = 1; i <= dxt; i++) { + xp--; + d += t - b2; + + if (d >= 0) + ys = yp - 1; + else if ((d - s - a2) > 0) { + if ((2 * d - s - a2) >= 0) + ys = yp + 1; + else { + ys = yp; + yp++; + d -= s + a2; + s += ds; + } + } else { + yp++; + ys = yp + 1; + d -= s + a2; + s += ds; + } + + t -= dt; + + /* Calculate alpha */ + if (s != 0) { + cp = (float) abs(d) / (float) abs(s); + if (cp > 1.0) { + cp = 1.0; + } + } else { + cp = 1.0; + } + + /* Calculate weights */ + weight = (Uint8) (cp * 255); + iweight = 255 - weight; + + /* Upper half */ + xx = xc2 - xp; + result |= pixelRGBAWeight(renderer, xp, yp, r, g, b, a, iweight); + result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, iweight); + + result |= pixelRGBAWeight(renderer, xp, ys, r, g, b, a, weight); + result |= pixelRGBAWeight(renderer, xx, ys, r, g, b, a, weight); + + /* Lower half */ + yy = yc2 - yp; + result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, iweight); + result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, iweight); + + yy = yc2 - ys; + result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, weight); + result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, weight); + } + + /* Replaces original approximation code dyt = abs(yp - yc); */ + dyt = (Sint16)lrint((double)b2 / sab ) + od; + + for (i = 1; i <= dyt; i++) { + yp++; + d -= s + a2; + + if (d <= 0) + xs = xp + 1; + else if ((d + t - b2) < 0) { + if ((2 * d + t - b2) <= 0) + xs = xp - 1; + else { + xs = xp; + xp--; + d += t - b2; + t -= dt; + } + } else { + xp--; + xs = xp - 1; + d += t - b2; + t -= dt; + } + + s += ds; + + /* Calculate alpha */ + if (t != 0) { + cp = (float) abs(d) / (float) abs(t); + if (cp > 1.0) { + cp = 1.0; + } + } else { + cp = 1.0; + } + + /* Calculate weight */ + weight = (Uint8) (cp * 255); + iweight = 255 - weight; + + /* Left half */ + xx = xc2 - xp; + yy = yc2 - yp; + result |= pixelRGBAWeight(renderer, xp, yp, r, g, b, a, iweight); + result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, iweight); + + result |= pixelRGBAWeight(renderer, xp, yy, r, g, b, a, iweight); + result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, iweight); + + /* Right half */ + xx = xc2 - xs; + result |= pixelRGBAWeight(renderer, xs, yp, r, g, b, a, weight); + result |= pixelRGBAWeight(renderer, xx, yp, r, g, b, a, weight); + + result |= pixelRGBAWeight(renderer, xs, yy, r, g, b, a, weight); + result |= pixelRGBAWeight(renderer, xx, yy, r, g, b, a, weight); + } + + return (result); +} + +/* ---- Filled Ellipse */ + +/*! +\brief Draw filled ellipse with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the filled ellipse. +\param y Y coordinate of the center of the filled ellipse. +\param rx Horizontal radius in pixels of the filled ellipse. +\param ry Vertical radius in pixels of the filled ellipse. +\param color The color value of the filled ellipse to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int filledEllipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return filledEllipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw filled ellipse with blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the filled ellipse. +\param y Y coordinate of the center of the filled ellipse. +\param rx Horizontal radius in pixels of the filled ellipse. +\param ry Vertical radius in pixels of the filled ellipse. +\param r The red value of the filled ellipse to draw. +\param g The green value of the filled ellipse to draw. +\param b The blue value of the filled ellipse to draw. +\param a The alpha value of the filled ellipse to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int filledEllipseRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + int ix, iy; + int h, i, j, k; + int oh, oi, oj, ok; + int xmh, xph; + int xmi, xpi; + int xmj, xpj; + int xmk, xpk; + + /* + * Sanity check radii + */ + if ((rx < 0) || (ry < 0)) { + return (-1); + } + + /* + * Special case for rx=0 - draw a vline + */ + if (rx == 0) { + return (vlineRGBA(renderer, x, y - ry, y + ry, r, g, b, a)); + } + /* + * Special case for ry=0 - draw a hline + */ + if (ry == 0) { + return (hlineRGBA(renderer, x - rx, x + rx, y, r, g, b, a)); + } + + /* + * Set color + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + /* + * Init vars + */ + oh = oi = oj = ok = 0xFFFF; + + /* + * Draw + */ + if (rx > ry) { + ix = 0; + iy = rx * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if ((ok != k) && (oj != k)) { + xph = x + h; + xmh = x - h; + if (k > 0) { + result |= hline(renderer, xmh, xph, y + k); + result |= hline(renderer, xmh, xph, y - k); + } else { + result |= hline(renderer, xmh, xph, y); + } + ok = k; + } + if ((oj != j) && (ok != j) && (k != j)) { + xmi = x - i; + xpi = x + i; + if (j > 0) { + result |= hline(renderer, xmi, xpi, y + j); + result |= hline(renderer, xmi, xpi, y - j); + } else { + result |= hline(renderer, xmi, xpi, y); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if ((oi != i) && (oh != i)) { + xmj = x - j; + xpj = x + j; + if (i > 0) { + result |= hline(renderer, xmj, xpj, y + i); + result |= hline(renderer, xmj, xpj, y - i); + } else { + result |= hline(renderer, xmj, xpj, y); + } + oi = i; + } + if ((oh != h) && (oi != h) && (i != h)) { + xmk = x - k; + xpk = x + k; + if (h > 0) { + result |= hline(renderer, xmk, xpk, y + h); + result |= hline(renderer, xmk, xpk, y - h); + } else { + result |= hline(renderer, xmk, xpk, y); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + + return (result); +} + +/* ----- Pie */ + +/*! +\brief Internal float (low-speed) pie-calc implementation by drawing polygons. + +Note: Determines vertex array and uses polygon or filledPolygon drawing routines to render. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the pie. +\param y Y coordinate of the center of the pie. +\param rad Radius in pixels of the pie. +\param start Starting radius in degrees of the pie. +\param end Ending radius in degrees of the pie. +\param r The red value of the pie to draw. +\param g The green value of the pie to draw. +\param b The blue value of the pie to draw. +\param a The alpha value of the pie to draw. +\param filled Flag indicating if the pie should be filled (=1) or not (=0). + +\returns Returns 0 on success, -1 on failure. +*/ +/* TODO: rewrite algorithm; pie is not always accurate */ +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) +{ + int result; + double angle, start_angle, end_angle; + double deltaAngle; + double dr; + int numpoints, i; + Sint16 *vx, *vy; + + /* + * Sanity check radii + */ + if (rad < 0) { + return (-1); + } + + /* + * Fixup angles + */ + start = start % 360; + end = end % 360; + + /* + * Special case for rad=0 - draw a point + */ + if (rad == 0) { + return (pixelRGBA(renderer, x, y, r, g, b, a)); + } + + /* + * Variable setup + */ + dr = (double) rad; + deltaAngle = 3.0 / dr; + start_angle = (double) start *(2.0 * M_PI / 360.0); + end_angle = (double) end *(2.0 * M_PI / 360.0); + if (start > end) { + end_angle += (2.0 * M_PI); + } + + /* We will always have at least 2 points */ + numpoints = 2; + + /* Count points (rather than calculating it) */ + angle = start_angle; + while (angle < end_angle) { + angle += deltaAngle; + numpoints++; + } + + /* Allocate combined vertex array */ + vx = vy = (Sint16 *) malloc(2 * sizeof(Sint16) * numpoints); + if (vx == NULL) { + return (-1); + } + + /* Update point to start of vy */ + vy += numpoints; + + /* Center */ + vx[0] = x; + vy[0] = y; + + /* First vertex */ + angle = start_angle; + vx[1] = x + (int) (dr * cos(angle)); + vy[1] = y + (int) (dr * sin(angle)); + + if (numpoints<3) + { + result = lineRGBA(renderer, vx[0], vy[0], vx[1], vy[1], r, g, b, a); + } + else + { + /* Calculate other vertices */ + i = 2; + angle = start_angle; + while (angle < end_angle) { + angle += deltaAngle; + if (angle>end_angle) + { + angle = end_angle; + } + vx[i] = x + (int) (dr * cos(angle)); + vy[i] = y + (int) (dr * sin(angle)); + i++; + } + + /* Draw */ + if (filled) { + result = filledPolygonRGBA(renderer, vx, vy, numpoints, r, g, b, a); + } else { + result = polygonRGBA(renderer, vx, vy, numpoints, r, g, b, a); + } + } + + /* Free combined vertex array */ + free(vx); + + return (result); +} + +/*! +\brief Draw pie (outline) with alpha blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the pie. +\param y Y coordinate of the center of the pie. +\param rad Radius in pixels of the pie. +\param start Starting radius in degrees of the pie. +\param end Ending radius in degrees of the pie. +\param color The color value of the pie to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int pieColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, + Sint16 start, Sint16 end, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return _pieRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], 0); +} + +/*! +\brief Draw pie (outline) with alpha blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the pie. +\param y Y coordinate of the center of the pie. +\param rad Radius in pixels of the pie. +\param start Starting radius in degrees of the pie. +\param end Ending radius in degrees of the pie. +\param r The red value of the pie to draw. +\param g The green value of the pie to draw. +\param b The blue value of the pie to draw. +\param a The alpha value of the pie to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int pieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, + Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return _pieRGBA(renderer, x, y, rad, start, end, r, g, b, a, 0); +} + +/*! +\brief Draw filled pie with alpha blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the filled pie. +\param y Y coordinate of the center of the filled pie. +\param rad Radius in pixels of the filled pie. +\param start Starting radius in degrees of the filled pie. +\param end Ending radius in degrees of the filled pie. +\param color The color value of the filled pie to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int filledPieColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return _pieRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], 1); +} + +/*! +\brief Draw filled pie with alpha blending. + +\param renderer The renderer to draw on. +\param x X coordinate of the center of the filled pie. +\param y Y coordinate of the center of the filled pie. +\param rad Radius in pixels of the filled pie. +\param start Starting radius in degrees of the filled pie. +\param end Ending radius in degrees of the filled pie. +\param r The red value of the filled pie to draw. +\param g The green value of the filled pie to draw. +\param b The blue value of the filled pie to draw. +\param a The alpha value of the filled pie to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int filledPieRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, + Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return _pieRGBA(renderer, x, y, rad, start, end, r, g, b, a, 1); +} + +/* ------ Trigon */ + +/*! +\brief Draw trigon (triangle outline) with alpha blending. + +Note: Creates vertex array and uses polygon routine to render. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the trigon. +\param y1 Y coordinate of the first point of the trigon. +\param x2 X coordinate of the second point of the trigon. +\param y2 Y coordinate of the second point of the trigon. +\param x3 X coordinate of the third point of the trigon. +\param y3 Y coordinate of the third point of the trigon. +\param color The color value of the trigon to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int trigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color) +{ + Sint16 vx[3]; + Sint16 vy[3]; + + vx[0]=x1; + vx[1]=x2; + vx[2]=x3; + vy[0]=y1; + vy[1]=y2; + vy[2]=y3; + + return(polygonColor(renderer,vx,vy,3,color)); +} + +/*! +\brief Draw trigon (triangle outline) with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the trigon. +\param y1 Y coordinate of the first point of the trigon. +\param x2 X coordinate of the second point of the trigon. +\param y2 Y coordinate of the second point of the trigon. +\param x3 X coordinate of the third point of the trigon. +\param y3 Y coordinate of the third point of the trigon. +\param r The red value of the trigon to draw. +\param g The green value of the trigon to draw. +\param b The blue value of the trigon to draw. +\param a The alpha value of the trigon to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int trigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, + Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Sint16 vx[3]; + Sint16 vy[3]; + + vx[0]=x1; + vx[1]=x2; + vx[2]=x3; + vy[0]=y1; + vy[1]=y2; + vy[2]=y3; + + return(polygonRGBA(renderer,vx,vy,3,r,g,b,a)); +} + +/* ------ AA-Trigon */ + +/*! +\brief Draw anti-aliased trigon (triangle outline) with alpha blending. + +Note: Creates vertex array and uses aapolygon routine to render. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the aa-trigon. +\param y1 Y coordinate of the first point of the aa-trigon. +\param x2 X coordinate of the second point of the aa-trigon. +\param y2 Y coordinate of the second point of the aa-trigon. +\param x3 X coordinate of the third point of the aa-trigon. +\param y3 Y coordinate of the third point of the aa-trigon. +\param color The color value of the aa-trigon to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int aatrigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color) +{ + Sint16 vx[3]; + Sint16 vy[3]; + + vx[0]=x1; + vx[1]=x2; + vx[2]=x3; + vy[0]=y1; + vy[1]=y2; + vy[2]=y3; + + return(aapolygonColor(renderer,vx,vy,3,color)); +} + +/*! +\brief Draw anti-aliased trigon (triangle outline) with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the aa-trigon. +\param y1 Y coordinate of the first point of the aa-trigon. +\param x2 X coordinate of the second point of the aa-trigon. +\param y2 Y coordinate of the second point of the aa-trigon. +\param x3 X coordinate of the third point of the aa-trigon. +\param y3 Y coordinate of the third point of the aa-trigon. +\param r The red value of the aa-trigon to draw. +\param g The green value of the aa-trigon to draw. +\param b The blue value of the aa-trigon to draw. +\param a The alpha value of the aa-trigon to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aatrigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, + Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Sint16 vx[3]; + Sint16 vy[3]; + + vx[0]=x1; + vx[1]=x2; + vx[2]=x3; + vy[0]=y1; + vy[1]=y2; + vy[2]=y3; + + return(aapolygonRGBA(renderer,vx,vy,3,r,g,b,a)); +} + +/* ------ Filled Trigon */ + +/*! +\brief Draw filled trigon (triangle) with alpha blending. + +Note: Creates vertex array and uses aapolygon routine to render. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the filled trigon. +\param y1 Y coordinate of the first point of the filled trigon. +\param x2 X coordinate of the second point of the filled trigon. +\param y2 Y coordinate of the second point of the filled trigon. +\param x3 X coordinate of the third point of the filled trigon. +\param y3 Y coordinate of the third point of the filled trigon. +\param color The color value of the filled trigon to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int filledTrigonColor(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color) +{ + Sint16 vx[3]; + Sint16 vy[3]; + + vx[0]=x1; + vx[1]=x2; + vx[2]=x3; + vy[0]=y1; + vy[1]=y2; + vy[2]=y3; + + return(filledPolygonColor(renderer,vx,vy,3,color)); +} + +/*! +\brief Draw filled trigon (triangle) with alpha blending. + +Note: Creates vertex array and uses aapolygon routine to render. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the filled trigon. +\param y1 Y coordinate of the first point of the filled trigon. +\param x2 X coordinate of the second point of the filled trigon. +\param y2 Y coordinate of the second point of the filled trigon. +\param x3 X coordinate of the third point of the filled trigon. +\param y3 Y coordinate of the third point of the filled trigon. +\param r The red value of the filled trigon to draw. +\param g The green value of the filled trigon to draw. +\param b The blue value of the filled trigon to draw. +\param a The alpha value of the filled trigon to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int filledTrigonRGBA(SDL_Renderer * renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, + Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Sint16 vx[3]; + Sint16 vy[3]; + + vx[0]=x1; + vx[1]=x2; + vx[2]=x3; + vy[0]=y1; + vy[1]=y2; + vy[2]=y3; + + return(filledPolygonRGBA(renderer,vx,vy,3,r,g,b,a)); +} + +/* ---- Polygon */ + +/*! +\brief Draw polygon with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the polygon. +\param vy Vertex array containing Y coordinates of the points of the polygon. +\param n Number of points in the vertex array. Minimum number is 3. +\param color The color value of the polygon to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int polygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return polygonRGBA(renderer, vx, vy, n, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw polygon with the currently set color and blend mode. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the polygon. +\param vy Vertex array containing Y coordinates of the points of the polygon. +\param n Number of points in the vertex array. Minimum number is 3. + +\returns Returns 0 on success, -1 on failure. +*/ +int polygon(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n) +{ + /* + * Draw + */ + int result = 0; + int i, nn; + SDL_Point* points; + + /* + * Vertex array NULL check + */ + if (vx == NULL) { + return (-1); + } + if (vy == NULL) { + return (-1); + } + + /* + * Sanity check + */ + if (n < 3) { + return (-1); + } + + /* + * Create array of points + */ + nn = n + 1; + points = (SDL_Point*)malloc(sizeof(SDL_Point) * nn); + if (points == NULL) + { + return -1; + } + for (i=0; ib. +*/ +int _gfxPrimitivesCompareInt(const void *a, const void *b) +{ + return (*(const int *) a) - (*(const int *) b); +} + +/*! +\brief Global vertex array to use if optional parameters are not given in filledPolygonMT calls. + +Note: Used for non-multithreaded (default) operation of filledPolygonMT. +*/ +static int *gfxPrimitivesPolyIntsGlobal = NULL; + +/*! +\brief Flag indicating if global vertex array was already allocated. + +Note: Used for non-multithreaded (default) operation of filledPolygonMT. +*/ +static int gfxPrimitivesPolyAllocatedGlobal = 0; + +/*! +\brief Draw filled polygon with alpha blending (multi-threaded capable). + +Note: The last two parameters are optional; but are required for multithreaded operation. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the filled polygon. +\param vy Vertex array containing Y coordinates of the points of the filled polygon. +\param n Number of points in the vertex array. Minimum number is 3. +\param r The red value of the filled polygon to draw. +\param g The green value of the filled polygon to draw. +\param b The blue value of the filled polygon to draw. +\param a The alpha value of the filled polygon to draw. +\param polyInts Preallocated, temporary vertex array used for sorting vertices. Required for multithreaded operation; set to NULL otherwise. +\param polyAllocated Flag indicating if temporary vertex array was allocated. Required for multithreaded operation; set to NULL otherwise. + +\returns Returns 0 on success, -1 on failure. +*/ +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) +{ + int result; + int i; + int y, xa, xb; + int miny, maxy; + int x1, y1; + int x2, y2; + int ind1, ind2; + int ints; + int *gfxPrimitivesPolyInts = NULL; + int *gfxPrimitivesPolyIntsNew = NULL; + int gfxPrimitivesPolyAllocated = 0; + + /* + * Vertex array NULL check + */ + if (vx == NULL) { + return (-1); + } + if (vy == NULL) { + return (-1); + } + + /* + * Sanity check number of edges + */ + if (n < 3) { + return -1; + } + + /* + * Map polygon cache + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + /* Use global cache */ + gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal; + gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal; + } else { + /* Use local cache */ + gfxPrimitivesPolyInts = *polyInts; + gfxPrimitivesPolyAllocated = *polyAllocated; + } + + /* + * Allocate temp array, only grow array + */ + if (!gfxPrimitivesPolyAllocated) { + gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } else { + if (gfxPrimitivesPolyAllocated < n) { + gfxPrimitivesPolyIntsNew = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); + if (!gfxPrimitivesPolyIntsNew) { + if (!gfxPrimitivesPolyInts) { + free(gfxPrimitivesPolyInts); + gfxPrimitivesPolyInts = NULL; + } + gfxPrimitivesPolyAllocated = 0; + } else { + gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsNew; + gfxPrimitivesPolyAllocated = n; + } + } + } + + /* + * Check temp array + */ + if (gfxPrimitivesPolyInts==NULL) { + gfxPrimitivesPolyAllocated = 0; + } + + /* + * Update cache variables + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts; + gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated; + } else { + *polyInts = gfxPrimitivesPolyInts; + *polyAllocated = gfxPrimitivesPolyAllocated; + } + + /* + * Check temp array again + */ + if (gfxPrimitivesPolyInts==NULL) { + return(-1); + } + + /* + * Determine Y maxima + */ + miny = vy[0]; + maxy = vy[0]; + for (i = 1; (i < n); i++) { + if (vy[i] < miny) { + miny = vy[i]; + } else if (vy[i] > maxy) { + maxy = vy[i]; + } + } + + /* + * Draw, scanning y + */ + result = 0; + for (y = miny; (y <= maxy); y++) { + ints = 0; + for (i = 0; (i < n); i++) { + if (!i) { + ind1 = n - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + y1 = vy[ind1]; + y2 = vy[ind2]; + if (y1 < y2) { + x1 = vx[ind1]; + x2 = vx[ind2]; + } else if (y1 > y2) { + y2 = vy[ind1]; + y1 = vy[ind2]; + x2 = vx[ind1]; + x1 = vx[ind2]; + } else { + continue; + } + if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) { + gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1); + } + } + + qsort(gfxPrimitivesPolyInts, ints, sizeof(int), _gfxPrimitivesCompareInt); + + /* + * Set color + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + for (i = 0; (i < ints); i += 2) { + xa = gfxPrimitivesPolyInts[i] + 1; + xa = (xa >> 16) + ((xa & 32768) >> 15); + xb = gfxPrimitivesPolyInts[i+1] - 1; + xb = (xb >> 16) + ((xb & 32768) >> 15); + result |= hline(renderer, xa, xb, y); + } + } + + return (result); +} + +/*! +\brief Draw filled polygon with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the filled polygon. +\param vy Vertex array containing Y coordinates of the points of the filled polygon. +\param n Number of points in the vertex array. Minimum number is 3. +\param color The color value of the filled polygon to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int filledPolygonColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return filledPolygonRGBAMT(renderer, vx, vy, n, c[0], c[1], c[2], c[3], NULL, NULL); +} + +/*! +\brief Draw filled polygon with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the filled polygon. +\param vy Vertex array containing Y coordinates of the points of the filled polygon. +\param n Number of points in the vertex array. Minimum number is 3. +\param r The red value of the filled polygon to draw. +\param g The green value of the filled polygon to draw. +\param b The blue value of the filed polygon to draw. +\param a The alpha value of the filled polygon to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int filledPolygonRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return filledPolygonRGBAMT(renderer, vx, vy, n, r, g, b, a, NULL, NULL); +} + +/* ---- Textured Polygon */ + +/*! +\brief Internal function to draw a textured horizontal line. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point (i.e. left) of the line. +\param x2 X coordinate of the second point (i.e. right) of the line. +\param y Y coordinate of the points of the line. +\param texture The texture to retrieve color information from. +\param texture_w The width of the texture. +\param texture_h The height of the texture. +\param texture_dx The X offset for the texture lookup. +\param texture_dy The Y offset for the textured lookup. + +\returns Returns 0 on success, -1 on failure. +*/ +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) +{ + Sint16 w; + Sint16 xtmp; + int result = 0; + int texture_x_walker; + int texture_y_start; + SDL_Rect source_rect,dst_rect; + int pixels_written,write_width; + + /* + * Swap x1, x2 if required to ensure x1<=x2 + */ + if (x1 > x2) { + xtmp = x1; + x1 = x2; + x2 = xtmp; + } + + /* + * Calculate width to draw + */ + w = x2 - x1 + 1; + + /* + * Determine where in the texture we start drawing + */ + texture_x_walker = (x1 - texture_dx) % texture_w; + if (texture_x_walker < 0){ + texture_x_walker = texture_w + texture_x_walker ; + } + + texture_y_start = (y + texture_dy) % texture_h; + if (texture_y_start < 0){ + texture_y_start = texture_h + texture_y_start; + } + + /* setup the source rectangle; we are only drawing one horizontal line */ + source_rect.y = texture_y_start; + source_rect.x = texture_x_walker; + source_rect.h = 1; + + /* we will draw to the current y */ + dst_rect.y = y; + dst_rect.h = 1; + + /* if there are enough pixels left in the current row of the texture */ + /* draw it all at once */ + if (w <= texture_w -texture_x_walker){ + source_rect.w = w; + source_rect.x = texture_x_walker; + dst_rect.x= x1; + dst_rect.w = source_rect.w; + result = (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0); + } else { + /* we need to draw multiple times */ + /* draw the first segment */ + pixels_written = texture_w - texture_x_walker; + source_rect.w = pixels_written; + source_rect.x = texture_x_walker; + dst_rect.x= x1; + dst_rect.w = source_rect.w; + result |= (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0); + write_width = texture_w; + + /* now draw the rest */ + /* set the source x to 0 */ + source_rect.x = 0; + while (pixels_written < w){ + if (write_width >= w - pixels_written) { + write_width = w - pixels_written; + } + source_rect.w = write_width; + dst_rect.x = x1 + pixels_written; + dst_rect.w = source_rect.w; + result |= (SDL_RenderCopy(renderer, texture, &source_rect, &dst_rect) == 0); + pixels_written += write_width; + } + } + + return result; +} + +/*! +\brief Draws a polygon filled with the given texture (Multi-Threading Capable). + +\param renderer The renderer to draw on. +\param vx array of x vector components +\param vy array of x vector components +\param n the amount of vectors in the vx and vy array +\param texture the sdl surface to use to fill the polygon +\param texture_dx the offset of the texture relative to the screeen. If you move the polygon 10 pixels +to the left and want the texture to apear the same you need to increase the texture_dx value +\param texture_dy see texture_dx +\param polyInts Preallocated temp array storage for vertex sorting (used for multi-threaded operation) +\param polyAllocated Flag indicating oif the temp array was allocated (used for multi-threaded operation) + +\returns Returns 0 on success, -1 on failure. +*/ +int 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) +{ + int result; + int i; + int y, xa, xb; + int minx,maxx,miny, maxy; + int x1, y1; + int x2, y2; + int ind1, ind2; + int ints; + int *gfxPrimitivesPolyInts = NULL; + int *gfxPrimitivesPolyIntsTemp = NULL; + int gfxPrimitivesPolyAllocated = 0; + SDL_Texture *textureAsTexture = NULL; + + /* + * Sanity check number of edges + */ + if (n < 3) { + return -1; + } + + /* + * Map polygon cache + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + /* Use global cache */ + gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsGlobal; + gfxPrimitivesPolyAllocated = gfxPrimitivesPolyAllocatedGlobal; + } else { + /* Use local cache */ + gfxPrimitivesPolyInts = *polyInts; + gfxPrimitivesPolyAllocated = *polyAllocated; + } + + /* + * Allocate temp array, only grow array + */ + if (!gfxPrimitivesPolyAllocated) { + gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } else { + if (gfxPrimitivesPolyAllocated < n) { + gfxPrimitivesPolyIntsTemp = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); + if (gfxPrimitivesPolyIntsTemp == NULL) { + /* Realloc failed - keeps original memory block, but fails this operation */ + return(-1); + } + gfxPrimitivesPolyInts = gfxPrimitivesPolyIntsTemp; + gfxPrimitivesPolyAllocated = n; + } + } + + /* + * Check temp array + */ + if (gfxPrimitivesPolyInts==NULL) { + gfxPrimitivesPolyAllocated = 0; + } + + /* + * Update cache variables + */ + if ((polyInts==NULL) || (polyAllocated==NULL)) { + gfxPrimitivesPolyIntsGlobal = gfxPrimitivesPolyInts; + gfxPrimitivesPolyAllocatedGlobal = gfxPrimitivesPolyAllocated; + } else { + *polyInts = gfxPrimitivesPolyInts; + *polyAllocated = gfxPrimitivesPolyAllocated; + } + + /* + * Check temp array again + */ + if (gfxPrimitivesPolyInts==NULL) { + return(-1); + } + + /* + * Determine X,Y minima,maxima + */ + miny = vy[0]; + maxy = vy[0]; + minx = vx[0]; + maxx = vx[0]; + for (i = 1; (i < n); i++) { + if (vy[i] < miny) { + miny = vy[i]; + } else if (vy[i] > maxy) { + maxy = vy[i]; + } + if (vx[i] < minx) { + minx = vx[i]; + } else if (vx[i] > maxx) { + maxx = vx[i]; + } + } + + /* Create texture for drawing */ + textureAsTexture = SDL_CreateTextureFromSurface(renderer, texture); + if (textureAsTexture == NULL) + { + return -1; + } + SDL_SetTextureBlendMode(textureAsTexture, SDL_BLENDMODE_BLEND); + + /* + * Draw, scanning y + */ + result = 0; + for (y = miny; (y <= maxy); y++) { + ints = 0; + for (i = 0; (i < n); i++) { + if (!i) { + ind1 = n - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + y1 = vy[ind1]; + y2 = vy[ind2]; + if (y1 < y2) { + x1 = vx[ind1]; + x2 = vx[ind2]; + } else if (y1 > y2) { + y2 = vy[ind1]; + y1 = vy[ind2]; + x2 = vx[ind1]; + x1 = vx[ind2]; + } else { + continue; + } + if ( ((y >= y1) && (y < y2)) || ((y == maxy) && (y > y1) && (y <= y2)) ) { + gfxPrimitivesPolyInts[ints++] = ((65536 * (y - y1)) / (y2 - y1)) * (x2 - x1) + (65536 * x1); + } + } + + qsort(gfxPrimitivesPolyInts, ints, sizeof(int), _gfxPrimitivesCompareInt); + + for (i = 0; (i < ints); i += 2) { + xa = gfxPrimitivesPolyInts[i] + 1; + xa = (xa >> 16) + ((xa & 32768) >> 15); + xb = gfxPrimitivesPolyInts[i+1] - 1; + xb = (xb >> 16) + ((xb & 32768) >> 15); + result |= _HLineTextured(renderer, xa, xb, y, textureAsTexture, texture->w, texture->h, texture_dx, texture_dy); + } + } + + SDL_RenderPresent(renderer); + SDL_DestroyTexture(textureAsTexture); + + return (result); +} + +/*! +\brief Draws a polygon filled with the given texture. + +This standard version is calling multithreaded versions with NULL cache parameters. + +\param renderer The renderer to draw on. +\param vx array of x vector components +\param vy array of x vector components +\param n the amount of vectors in the vx and vy array +\param texture the sdl surface to use to fill the polygon +\param texture_dx the offset of the texture relative to the screeen. if you move the polygon 10 pixels +to the left and want the texture to apear the same you need to increase the texture_dx value +\param texture_dy see texture_dx + +\returns Returns 0 on success, -1 on failure. +*/ +int texturedPolygon(SDL_Renderer *renderer, const Sint16 * vx, const Sint16 * vy, int n, SDL_Surface *texture, int texture_dx, int texture_dy) +{ + /* + * Draw + */ + return (texturedPolygonMT(renderer, vx, vy, n, texture, texture_dx, texture_dy, NULL, NULL)); +} + +/* ---- Character */ + +/*! +\brief Global cache for NxM pixel font textures created at runtime. +*/ +static SDL_Texture *gfxPrimitivesFont[256]; + +/*! +\brief Pointer to the current font data. Default is a 8x8 pixel internal font. +*/ +static const unsigned char *currentFontdata = gfxPrimitivesFontdata; + +/*! +\brief Width of the current font. Default is 8. +*/ +static Uint32 charWidth = 8; + +/*! +\brief Height of the current font. Default is 8. +*/ +static Uint32 charHeight = 8; + +/*! +\brief Width for rendering. Autocalculated. +*/ +static Uint32 charWidthLocal = 8; + +/*! +\brief Height for rendering. Autocalculated. +*/ +static Uint32 charHeightLocal = 8; + +/*! +\brief Pitch of the current font in bytes. Default is 1. +*/ +static Uint32 charPitch = 1; + +/*! +\brief Characters 90deg clockwise rotations. Default is 0. Max is 3. +*/ +static Uint32 charRotation = 0; +static Uint32 charZoomX = 1; +static Uint32 charZoomY = 1; + +/*! +\brief Character data size in bytes of the current font. Default is 8. +*/ +static Uint32 charSize = 8; + +/*! +\brief Sets or resets the current global font data. + +The font data array is organized in follows: +[fontdata] = [character 0][character 1]...[character 255] where +[character n] = [byte 1 row 1][byte 2 row 1]...[byte {pitch} row 1][byte 1 row 2] ...[byte {pitch} row height] where +[byte n] = [bit 0]...[bit 7] where +[bit n] = [0 for transparent pixel|1 for colored pixel] + +\param fontdata Pointer to array of font data. Set to NULL, to reset global font to the default 8x8 font. +\param cw Width of character in bytes. Ignored if fontdata==NULL. +\param ch Height of character in bytes. Ignored if fontdata==NULL. +*/ +void gfxPrimitivesSetFont(const void *fontdata, Uint32 cw, Uint32 ch) +{ + int i; + + if ((fontdata) && (cw) && (ch)) { + currentFontdata = (unsigned char *)fontdata; + charWidth = cw; + charHeight = ch; + } else { + currentFontdata = gfxPrimitivesFontdata; + charWidth = 8; + charHeight = 8; + } + + charPitch = (charWidth+7)/8; + charSize = charPitch * charHeight; + + /* Maybe flip width/height for rendering */ + if ((charRotation==1) || (charRotation==3)) + { + charWidthLocal = charHeight; + charHeightLocal = charWidth; + } + else + { + charWidthLocal = charWidth; + charHeightLocal = charHeight; + } + + /* Clear character cache */ + for (i = 0; i < 256; i++) { + if (gfxPrimitivesFont[i]) { + SDL_DestroyTexture(gfxPrimitivesFont[i]); + gfxPrimitivesFont[i] = NULL; + } + } +} + +const void *gfxPrimitivesGetFont() +{ + return currentFontdata; +} + +/*! +\brief Sets current global font character rotation steps. + +Default is 0 (no rotation). 1 = 90deg clockwise. 2 = 180deg clockwise. 3 = 270deg clockwise. +Changing the rotation, will reset the character cache. + +\param rotation Number of 90deg clockwise steps to rotate +*/ +void gfxPrimitivesSetFontRotation(Uint32 rotation) +{ + int i; + + rotation = rotation & 3; + if (charRotation != rotation) + { + /* Store rotation */ + charRotation = rotation; + + /* Maybe flip width/height for rendering */ + if ((charRotation==1) || (charRotation==3)) + { + charWidthLocal = charHeight; + charHeightLocal = charWidth; + } + else + { + charWidthLocal = charWidth; + charHeightLocal = charHeight; + } + + /* Clear character cache */ + for (i = 0; i < 256; i++) { + if (gfxPrimitivesFont[i]) { + SDL_DestroyTexture(gfxPrimitivesFont[i]); + gfxPrimitivesFont[i] = NULL; + } + } + } +} + +void gfxPrimitivesSetFontZoom(Uint32 zoomx, Uint32 zoomy) +{ + int i; + + charZoomX = zoomx ; + charZoomY = zoomy ; + /* Clear character cache */ + for (i = 0; i < 256; i++) { + if (gfxPrimitivesFont[i]) { + SDL_DestroyTexture(gfxPrimitivesFont[i]); + gfxPrimitivesFont[i] = NULL; + } + } +} + +/*! +\brief Draw a character of the currently set font. + +\param renderer The Renderer to draw on. +\param x X (horizontal) coordinate of the upper left corner of the character. +\param y Y (vertical) coordinate of the upper left corner of the character. +\param c The character to draw. +\param r The red value of the character to draw. +\param g The green value of the character to draw. +\param b The blue value of the character to draw. +\param a The alpha value of the character to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int characterRGBA(SDL_Renderer *renderer, Sint16 x, Sint16 y, char c, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + SDL_Rect srect; + SDL_Rect drect; + int result; + Uint32 ix, iy; + const unsigned char *charpos; + Uint8 *curpos; + Uint8 patt, mask; + Uint8 *linepos; + Uint32 pitch; + SDL_Surface *character; + SDL_Surface *rotatedCharacter; + SDL_Surface *zoomedCharacter; + Uint32 ci; + + /* + * Setup source rectangle + */ + srect.x = 0; + srect.y = 0; + srect.w = charWidthLocal * charZoomX; + srect.h = charHeightLocal * charZoomY; + + /* + * Setup destination rectangle + */ + drect.x = x; + drect.y = y; + drect.w = charWidthLocal * charZoomX; + drect.h = charHeightLocal * charZoomY; + + /* Character index in cache */ + ci = (unsigned char) c; + + /* + * Create new charWidth x charHeight bitmap surface if not already present. + * Might get rotated later. + */ + if (gfxPrimitivesFont[ci] == NULL) { + /* + * Redraw character into surface + */ + character = SDL_CreateRGBSurface(SDL_SWSURFACE, + charWidth, charHeight, 32, + 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + if (character == NULL) { + return (-1); + } + + charpos = currentFontdata + ci * charSize; + linepos = (Uint8 *)character->pixels; + pitch = character->pitch; + + /* + * Drawing loop + */ + patt = 0; + for (iy = 0; iy < charHeight; iy++) { + mask = 0x00; + curpos = linepos; + for (ix = 0; ix < charWidth; ix++) { + if (!(mask >>= 1)) { + patt = *charpos++; + mask = 0x80; + } + if (patt & mask) { + *(Uint32 *)curpos = 0xffffffff; + } else { + *(Uint32 *)curpos = 0; + } + curpos += 4; + } + linepos += pitch; + } + + /* Maybe zoom character */ + if ((charZoomX != 1) || (charZoomY != 1)) + { + zoomedCharacter = zoomSurface(character, (double)charZoomX, + (double)charZoomY, SMOOTHING_OFF); + SDL_FreeSurface(character); + character = zoomedCharacter; + } + + /* Maybe rotate and replace cached image */ + if (charRotation>0) + { + rotatedCharacter = rotateSurface90Degrees(character, charRotation); + SDL_FreeSurface(character); + character = rotatedCharacter; + } + + /* Convert temp surface into texture */ + gfxPrimitivesFont[ci] = SDL_CreateTextureFromSurface(renderer, character); + SDL_FreeSurface(character); + + /* + * Check pointer + */ + if (gfxPrimitivesFont[ci] == NULL) { + return (-1); + } + } + + /* + * Set color + */ + result = 0; + result |= SDL_SetTextureColorMod(gfxPrimitivesFont[ci], r, g, b); + result |= SDL_SetTextureAlphaMod(gfxPrimitivesFont[ci], a); + + /* + * Draw texture onto destination + */ + result |= SDL_RenderCopy(renderer, gfxPrimitivesFont[ci], &srect, &drect); + + return (result); +} + + +/*! +\brief Draw a character of the currently set font. + +\param renderer The renderer to draw on. +\param x X (horizontal) coordinate of the upper left corner of the character. +\param y Y (vertical) coordinate of the upper left corner of the character. +\param c The character to draw. +\param color The color value of the character to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int characterColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, char c, Uint32 color) +{ + Uint8 *co = (Uint8 *)&color; + return characterRGBA(renderer, x, y, c, co[0], co[1], co[2], co[3]); +} + + +/*! +\brief Draw a string in the currently set font. + +The spacing between consequtive characters in the string is the fixed number of pixels +of the character width of the current global font. + +\param renderer The renderer to draw on. +\param x X (horizontal) coordinate of the upper left corner of the string. +\param y Y (vertical) coordinate of the upper left corner of the string. +\param s The string to draw. +\param color The color value of the string to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int stringColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return stringRGBA(renderer, x, y, s, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw a string in the currently set font. + +\param renderer The renderer to draw on. +\param x X (horizontal) coordinate of the upper left corner of the string. +\param y Y (vertical) coordinate of the upper left corner of the string. +\param s The string to draw. +\param r The red value of the string to draw. +\param g The green value of the string to draw. +\param b The blue value of the string to draw. +\param a The alpha value of the string to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int stringRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result = 0; + Sint16 curx = x; + Sint16 cury = y; + const char *curchar = s; + + while (*curchar && !result) { + result |= characterRGBA(renderer, curx, cury, *curchar, r, g, b, a); + switch (charRotation) + { + case 0: + curx += charWidthLocal * charZoomX; + break; + case 2: + curx -= charWidthLocal * charZoomX; + break; + case 1: + cury += charHeightLocal * charZoomY; + break; + case 3: + cury -= charHeightLocal * charZoomY; + break; + } + curchar++; + } + + return (result); +} + +/* ---- Bezier curve */ + +/*! +\brief Internal function to calculate bezier interpolator of data array with ndata values at position 't'. + +\param data Array of values. +\param ndata Size of array. +\param t Position for which to calculate interpolated value. t should be between [0, ndata]. + +\returns Interpolated value at position t, value[0] when t<0, value[n-1] when t>n. +*/ +double _evaluateBezier (double *data, int ndata, double t) +{ + double mu, result; + int n,k,kn,nn,nkn; + double blend,muk,munk; + + /* Sanity check bounds */ + if (t<0.0) { + return(data[0]); + } + if (t>=(double)ndata) { + return(data[ndata-1]); + } + + /* Adjust t to the range 0.0 to 1.0 */ + mu=t/(double)ndata; + + /* Calculate interpolate */ + n=ndata-1; + result=0.0; + muk = 1; + munk = pow(1-mu,(double)n); + for (k=0;k<=n;k++) { + nn = n; + kn = k; + nkn = n - k; + blend = muk * munk; + muk *= mu; + munk /= (1-mu); + while (nn >= 1) { + blend *= nn; + nn--; + if (kn > 1) { + blend /= (double)kn; + kn--; + } + if (nkn > 1) { + blend /= (double)nkn; + nkn--; + } + } + result += data[k] * blend; + } + + return (result); +} + +/*! +\brief Draw a bezier curve with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the bezier curve. +\param vy Vertex array containing Y coordinates of the points of the bezier curve. +\param n Number of points in the vertex array. Minimum number is 3. +\param s Number of steps for the interpolation. Minimum number is 2. +\param color The color value of the bezier curve to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int bezierColor(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, int s, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return bezierRGBA(renderer, vx, vy, n, s, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw a bezier curve with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the bezier curve. +\param vy Vertex array containing Y coordinates of the points of the bezier curve. +\param n Number of points in the vertex array. Minimum number is 3. +\param s Number of steps for the interpolation. Minimum number is 2. +\param r The red value of the bezier curve to draw. +\param g The green value of the bezier curve to draw. +\param b The blue value of the bezier curve to draw. +\param a The alpha value of the bezier curve to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int bezierRGBA(SDL_Renderer * renderer, const Sint16 * vx, const Sint16 * vy, int n, int s, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + int i; + double *x, *y, t, stepsize; + Sint16 x1, y1, x2, y2; + + /* + * Sanity check + */ + if (n < 3) { + return (-1); + } + if (s < 2) { + return (-1); + } + + /* + * Variable setup + */ + stepsize=(double)1.0/(double)s; + + /* Transfer vertices into float arrays */ + if ((x=(double *)malloc(sizeof(double)*(n+1)))==NULL) { + return(-1); + } + if ((y=(double *)malloc(sizeof(double)*(n+1)))==NULL) { + free(x); + return(-1); + } + for (i=0; ix = x1; + b->y = y1; + + /* dx = abs(x2-x1), s1 = sign(x2-x1) */ + if ((b->dx = x2 - x1) != 0) { + if (b->dx < 0) { + b->dx = -b->dx; + b->s1 = -1; + } else { + b->s1 = 1; + } + } else { + b->s1 = 0; + } + + /* dy = abs(y2-y1), s2 = sign(y2-y1) */ + if ((b->dy = y2 - y1) != 0) { + if (b->dy < 0) { + b->dy = -b->dy; + b->s2 = -1; + } else { + b->s2 = 1; + } + } else { + b->s2 = 0; + } + + if (b->dy > b->dx) { + temp = b->dx; + b->dx = b->dy; + b->dy = temp; + b->swapdir = 1; + } else { + b->swapdir = 0; + } + + b->count = (b->dx<0) ? 0 : (unsigned int)b->dx; + b->dy <<= 1; + b->error = b->dy - b->dx; + b->dx <<= 1; + + return(0); +} + + +/*! +\brief Internal function to move Bresenham line iterator to the next position. + +Maybe updates the x and y coordinates of the iterator struct. + +\param b Pointer to struct for bresenham line drawing state. + +\returns Returns 0 on success, 1 if last point was reached, 2 if moving past end-of-line, -1 on failure. +*/ +int _bresenhamIterate(SDL2_gfxBresenhamIterator *b) +{ + if (b==NULL) { + return (-1); + } + + /* last point check */ + if (b->count==0) { + return (2); + } + + while (b->error >= 0) { + if (b->swapdir) { + b->x += b->s1; + } else { + b->y += b->s2; + } + + b->error -= b->dx; + } + + if (b->swapdir) { + b->y += b->s2; + } else { + b->x += b->s1; + } + + b->error += b->dy; + b->count--; + + /* count==0 indicates "end-of-line" */ + return ((b->count) ? 0 : 1); +} + +/* Code for Murphy thick line algorithm from http://kt8216.unixcab.org/murphy/ */ + +/*********************************************************************** + * * + * X BASED LINES * + * * + ***********************************************************************/ + +static void x_perpendicular(SDL_Renderer *B, + int x0,int y0,int dx,int dy,int xstep, int ystep, + int einit,int w_left, int w_right,int winit) +{ + int x,y,threshold,E_diag,E_square; + int tk; + int error; + int p,q; + + threshold = dx - 2*dy; + E_diag= -2*dx; + E_square= 2*dy; + p=q=0; + + y= y0; + x= x0; + error= einit; + tk= dx+dy-winit; + + while(tk<=w_left) + { + SDL_RenderDrawPoint(B,x,y); + if (error>=threshold) + { + x= x + xstep; + error = error + E_diag; + tk= tk + 2*dy; + } + error = error + E_square; + y= y + ystep; + tk= tk + 2*dx; + q++; + } + + y= y0; + x= x0; + error= -einit; + tk= dx+dy+winit; + + while(tk<=w_right) + { + if (p) + SDL_RenderDrawPoint(B,x,y); + if (error>threshold) + { + x= x - xstep; + error = error + E_diag; + tk= tk + 2*dy; + } + error = error + E_square; + y= y - ystep; + tk= tk + 2*dx; + p++; + } + + if (q==0 && p<2) SDL_RenderDrawPoint(B,x0,y0); // we need this for very thin lines +} + +static void x_varthick_line + (SDL_Renderer *B, int style, + int x0,int y0,int dx,int dy,int xstep, int ystep, + double thickness, int pxstep,int pystep) +{ + int p_error, error, x,y, threshold, E_diag, E_square, length, p; + int w_left, w_right; + double D; + + p_error= 0; + error= 0; + y= y0; + x= x0; + threshold = dx - 2*dy; + E_diag= -2*dx; + E_square= 2*dy; + length = dx+1; + D= sqrt(dx*dx+dy*dy); + w_left= thickness*D + 0.5; + w_right= 2.0*thickness*D + 0.5; + w_right -= w_left; + + for(p=0;p=threshold) + { + y= y + ystep; + error = error + E_diag; + if (p_error>=threshold) + { + if (style < 0) + x_perpendicular(B,x,y, dx, dy, pxstep, pystep, + (p_error+E_diag+E_square), + w_left,w_right,error); + p_error= p_error + E_diag; + } + p_error= p_error + E_square; + } + error = error + E_square; + x= x + xstep; + } +} + +/*********************************************************************** + * * + * Y BASED LINES * + * * + ***********************************************************************/ + +static void y_perpendicular(SDL_Renderer *B, + int x0,int y0,int dx,int dy,int xstep, int ystep, + int einit,int w_left, int w_right,int winit) +{ + int x,y,threshold,E_diag,E_square; + int tk; + int error; + int p,q; + + p=q= 0; + threshold = dy - 2*dx; + E_diag= -2*dy; + E_square= 2*dx; + + y= y0; + x= x0; + error= -einit; + tk= dx+dy+winit; + + while(tk<=w_left) + { + SDL_RenderDrawPoint(B,x,y); + if (error>threshold) + { + y= y + ystep; + error = error + E_diag; + tk= tk + 2*dx; + } + error = error + E_square; + x= x + xstep; + tk= tk + 2*dy; + q++; + } + + y= y0; + x= x0; + error= einit; + tk= dx+dy-winit; + + while(tk<=w_right) + { + if (p) + SDL_RenderDrawPoint(B,x,y); + if (error>=threshold) + { + y= y - ystep; + error = error + E_diag; + tk= tk + 2*dx; + } + error = error + E_square; + x= x - xstep; + tk= tk + 2*dy; + p++; + } + + if (q==0 && p<2) SDL_RenderDrawPoint(B,x0,y0); // we need this for very thin lines +} + +static void y_varthick_line + (SDL_Renderer *B, int style, + int x0,int y0,int dx,int dy,int xstep, int ystep, + double thickness, int pxstep,int pystep) +{ + int p_error, error, x,y, threshold, E_diag, E_square, length, p; + int w_left, w_right; + double D; + + p_error= 0; + error= 0; + y= y0; + x= x0; + threshold = dy - 2*dx; + E_diag= -2*dy; + E_square= 2*dx; + length = dy+1; + D= sqrt(dx*dx+dy*dy); + w_left= thickness*D + 0.5; + w_right= 2.0*thickness*D + 0.5; + w_right -= w_left; + + for(p=0;p=threshold) + { + x= x + xstep; + error = error + E_diag; + if (p_error>=threshold) + { + if (style < 0) + y_perpendicular(B,x,y, dx, dy, pxstep, pystep, + p_error+E_diag+E_square, + w_left,w_right,error); + p_error= p_error + E_diag; + } + p_error= p_error + E_square; + } + error = error + E_square; + y= y + ystep; + } +} + +/*********************************************************************** + * * + * ENTRY * + * * + ***********************************************************************/ + +void draw_varthick_line(SDL_Renderer *B, int style, + int x0,int y0,int x1, int y1, double thickness) +{ + int dx,dy,xstep,ystep; + int pxstep = 0, pystep = 0; + + dx= x1-x0; + dy= y1-y0; + xstep= ystep= 1; + + if (dx<0) { dx= -dx; xstep= -1; } + if (dy<0) { dy= -dy; ystep= -1; } + + if (dx==0) xstep= 0; + if (dy==0) ystep= 0; + + switch(xstep + ystep*4) + { + case -1 + -1*4 : pystep= -1; pxstep= 1; break; // -5 + case -1 + 0*4 : pystep= -1; pxstep= 0; break; // -1 + case -1 + 1*4 : pystep= 1; pxstep= 1; break; // 3 + case 0 + -1*4 : pystep= 0; pxstep= -1; break; // -4 + case 0 + 0*4 : pystep= 0; pxstep= 0; break; // 0 + case 0 + 1*4 : pystep= 0; pxstep= 1; break; // 4 + case 1 + -1*4 : pystep= -1; pxstep= -1; break; // -3 + case 1 + 0*4 : pystep= -1; pxstep= 0; break; // 1 + case 1 + 1*4 : pystep= 1; pxstep= -1; break; // 5 + } + + if (dx>dy) x_varthick_line(B,style,x0,y0,dx,dy,xstep,ystep, + thickness+1.0, + pxstep,pystep); + else y_varthick_line(B,style,x0,y0,dx,dy,xstep,ystep, + thickness+1.0, + pxstep,pystep); + return; +} + +static int LineStyle = -1; + +/*! +\brief Draw a thick line with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the line. +\param y1 Y coordinate of the first point of the line. +\param x2 X coordinate of the second point of the line. +\param y2 Y coordinate of the second point of the line. +\param width Width of the line in pixels. Must be >0. +\param color The color value of the line to draw (0xAABBGGRR). + +\returns Returns 0 on success, -1 on failure. +*/ +int thickLineColor(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + LineStyle = -1; + return thickLineRGBA(renderer, x1, y1, x2, y2, width, c[0], c[1], c[2], c[3]); +} + +int thickLineColorStyle(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, + Uint8 width, Uint32 color, int style) +{ + Uint8 *c = (Uint8 *)&color; + LineStyle = style; + return thickLineRGBA(renderer, x1, y1, x2, y2, width, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw a thick line with alpha blending. + +\param renderer The renderer to draw on. +\param x1 X coordinate of the first point of the line. +\param y1 Y coordinate of the first point of the line. +\param x2 X coordinate of the second point of the line. +\param y2 Y coordinate of the second point of the line. +\param width Width of the line in pixels. Must be >0. +\param r The red value of the character to draw. +\param g The green value of the character to draw. +\param b The blue value of the character to draw. +\param a The alpha value of the character to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int thickLineRGBA(SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int result; + int wh; + + if (renderer == NULL) { + return -1; + } + if (width < 1) { + return -1; + } + + /* Special case: thick "point" */ + if ((x1 == x2) && (y1 == y2)) { + wh = width / 2; + return boxRGBA(renderer, x1 - wh, y1 - wh, x2 + width, y2 + width, r, g, b, a); + } + + /* + * Set color + */ + result = 0; + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + /* + * Draw + */ + draw_varthick_line(renderer, LineStyle, x1, y1, x2, y2, (double) width); + return(result); +} + +int RedefineChar(SDL_Renderer *renderer, char c, unsigned char *charpos, Uint32 width, Uint32 height) +{ + Uint32 ix, iy; + Uint8 *curpos; + Uint8 patt, mask; + Uint8 *linepos; + Uint32 pitch; + SDL_Surface *character; + SDL_Surface *zoomedCharacter; + Uint32 ci; + + /* Character index in cache */ + ci = (unsigned char) c; + + if (gfxPrimitivesFont[ci] != NULL) + SDL_DestroyTexture (gfxPrimitivesFont[ci]) ; + + // Redraw character into surface + character = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, + 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + if (character == NULL) + return (-1); + + linepos = (Uint8 *)character->pixels; + pitch = character->pitch; + + // Drawing loop + patt = 0; + for (iy = 0; iy < height; iy++) + { + mask = 0x00; + curpos = linepos; + for (ix = 0; ix < width; ix++) + { + if (!(mask >>= 1)) + { + patt = *charpos++; + mask = 0x80; + } + if (patt & mask) + { + *(Uint32 *)curpos = 0xffffffff; + } + else + { + *(Uint32 *)curpos = 0; + } + curpos += 4; + } + linepos += pitch; + } + + /* Maybe zoom character */ + if ((charZoomX != 1) || (charZoomY != 1)) + { + zoomedCharacter = zoomSurface(character, (double)charZoomX, + (double)charZoomY, SMOOTHING_OFF); + SDL_FreeSurface(character); + character = zoomedCharacter; + } + + /* Convert temp surface into texture */ + gfxPrimitivesFont[ci] = SDL_CreateTextureFromSurface(renderer, character); + SDL_FreeSurface(character); + + /* + * Check pointer + */ + if (gfxPrimitivesFont[ci] == NULL) { + return (-1); + } + return 0 ; +} + +// Extensions for thick outline ellipses and arcs by Richard Russell 19-Feb-2019 + +// SDL_RenderDrawLine() is documented as including both end points, but this isn't +// reliable in Linux so use SDL_RenderDrawPoints() instead, despite being slower. +static int renderdrawline(SDL_Renderer *renderer, int x1, int y1, int x2, int y2) +{ + int result ; + if ((x1 == x2) && (y1 == y2)) + result = SDL_RenderDrawPoint (renderer, x1, y1) ; + else if ((y1 == y2) && (x1 != x2)) + { + int x ; + if (x1 > x2) { x = x1 ; x1 = x2 ; x2 = x ; } + SDL_Point *points = (SDL_Point*) malloc ((x2 - x1 + 1) * sizeof(SDL_Point)) ; + if (points == NULL) return -1 ; + for (x = x1; x <= x2; x++) + { + points[x - x1].x = x ; + points[x - x1].y = y1 ; + } + result = SDL_RenderDrawPoints (renderer, points, x2 - x1 + 1) ; + free (points) ; + } + else if ((x1 == x2) && (y1 != y2)) + { + int y ; + if (y1 > y2) { y = y1 ; y1 = y2 ; y2 = y ; } + SDL_Point *points = (SDL_Point*) malloc ((y2 - y1 + 1) * sizeof(SDL_Point)) ; + if (points == NULL) return -1 ; + for (y = y1; y <= y2; y++) + { + points[y - y1].x = x1 ; + points[y - y1].y = y ; + } + result = SDL_RenderDrawPoints (renderer, points, y2 - y1 + 1) ; + free (points) ; + } + else + result = SDL_RenderDrawLine (renderer, x1, y1, x2, y2) ; + return result ; +} + +static int hlinecliparc(SDL_Renderer *renderer, int x1, int x2, int y, int xc, int yc, double s, double f) +{ + int result = 0 ; + double a1, a2 ; + a1 = atan2(y, x1) ; + a2 = atan2(y, x2) ; + if (a1 > a2) + { + double a = a1 ; a1 = a2 ; a2 = a ; + int x = x1 ; x1 = x2 ; x2 = x ; + } + if (f < s) + { + if ((a1 > f) && (a2 < s)) return result ; + if ((a1 < s) && (a1 > f)) x1 = y / tan(s) ; + if ((a2 > f) && (a2 < s)) x2 = y / tan(f) ; + if ((a1 < f) && (a2 > s)) + { + result |= renderdrawline(renderer, x1+xc, y+yc, y/tan(f)+xc, y+yc) ; + result |= renderdrawline(renderer, y/tan(s)+xc, y+yc, x2+xc, y+yc) ; + return result ; + } + } + else + { + if ((a1 > f) || (a2 < s)) return result ; + if (a1 < s) x1 = y / tan(s) ; + if (a2 > f) x2 = y / tan(f) ; + } + result |= renderdrawline(renderer, x1+xc, y+yc, x2+xc, y+yc) ; + return result ; +} + +/*! +\brief Draw thick ellipse with blending. + +\param renderer The renderer to draw on. +\param xc X coordinate of the center of the ellipse. +\param yc Y coordinate of the center of the ellipse. +\param xr Horizontal radius in pixels of the ellipse. +\param yr Vertical radius in pixels of the ellipse. +\param r The red value of the ellipse to draw. +\param g The green value of the ellipse to draw. +\param b The blue value of the ellipse to draw. +\param a The alpha value of the ellipse to draw. +\param thick The line thickness in pixels + +\returns Returns 0 on success, -1 on failure. +*/ +int thickEllipseRGBA(SDL_Renderer * renderer, Sint16 xc, Sint16 yc, Sint16 xr, Sint16 yr, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 thick) +{ + int result = 0 ; + int xi, yi, xo, yo, x, y, z ; + double xi2, yi2, xo2, yo2 ; + + if (thick <= 1) + return ellipseRGBA(renderer, xc, yc, xr, yr, r, g, b, a) ; + + xi = xr - thick / 2 ; + xo = xi + thick - 1 ; + yi = yr - thick / 2 ; + yo = yi + thick - 1 ; + + if ((xi <= 0) || (yi <= 0)) + return -1 ; + + xi2 = xi * xi ; + yi2 = yi * yi ; + xo2 = xo * xo ; + yo2 = yo * yo ; + + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + if (xr < yr) + { + for (x = -xo; x <= -xi; x++) + { + y = sqrt(yo2 * (1.0 - x*x/xo2)) + 0.5 ; + result |= renderdrawline(renderer, xc+x, yc-y, xc+x, yc+y) ; + } + for (x = -xi + 1; x <= xi - 1; x++) + { + y = sqrt(yo2 * (1.0 - x*x/xo2)) + 0.5 ; + z = sqrt(yi2 * (1.0 - x*x/xi2)) + 0.5 ; + result |= renderdrawline(renderer, xc+x, yc+z, xc+x, yc+y) ; + result |= renderdrawline(renderer, xc+x, yc-z, xc+x, yc-y) ; + } + for (x = xo; x >= xi; x--) + { + y = sqrt(yo2 * (1.0 - x*x/xo2)) + 0.5 ; + result |= renderdrawline(renderer, xc+x, yc-y, xc+x, yc+y) ; + } + } + else + { + for (y = -yo; y <= -yi; y++) + { + x = sqrt(xo2 * (1.0 - y*y/yo2)) + 0.5 ; + result |= renderdrawline(renderer, xc-x, yc+y, xc+x, yc+y) ; + } + for (y = -yi + 1; y <= yi - 1; y++) + { + x = sqrt(xo2 * (1.0 - y*y/yo2)) + 0.5 ; + z = sqrt(xi2 * (1.0 - y*y/yi2)) + 0.5 ; + result |= renderdrawline(renderer, xc+z, yc+y, xc+x, yc+y) ; + result |= renderdrawline(renderer, xc-z, yc+y, xc-x, yc+y) ; + } + for (y = yo; y >= yi; y--) + { + x = sqrt(xo2 * (1.0 - y*y/yo2)) + 0.5 ; + result |= renderdrawline(renderer, xc-x, yc+y, xc+x, yc+y) ; + } + } + return result ; +} + +/*! +\brief thick Arc with blending. + +\param renderer The renderer to draw on. +\param xc X coordinate of the center of the arc. +\param yc Y coordinate of the center of the arc. +\param rad Radius in pixels of the arc. +\param start Starting radius in degrees of the arc. 0 degrees is right, increasing clockwise. +\param end Ending radius in degrees of the arc. 0 degrees is right, increasing clockwise. +\param r The red value of the arc to draw. +\param g The green value of the arc to draw. +\param b The blue value of the arc to draw. +\param a The alpha value of the arc to draw. +\param thick The line thickness in pixels. + +\returns Returns 0 on success, -1 on failure. +*/ +int thickArcRGBA(SDL_Renderer * renderer, Sint16 xc, Sint16 yc, Sint16 rad, Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 thick) +{ + int result = 0 ; + int ri, ro, x, y, z ; + double ri2, ro2, s, f ; + + if (thick <= 1) + return arcRGBA(renderer, xc, yc, rad, start, end, r, g, b, a) ; + + while (start < -180) start += 360 ; + while (start >= 180) start -= 360 ; + while (end < -180) end += 360 ; + while (end >= 180) end -= 360 ; + s = M_PI * (double)start / 180.0 ; + f = M_PI * (double)end / 180.0 ; + if (start == end) return 0 ; + + ri = rad - thick / 2 ; + ro = ri + thick - 1 ; + if (ri <= 0) return -1 ; + + ri2 = ri * ri ; + ro2 = ro * ro ; + + result |= SDL_SetRenderDrawBlendMode(renderer, (a == 255) ? SDL_BLENDMODE_NONE : SDL_BLENDMODE_BLEND); + result |= SDL_SetRenderDrawColor(renderer, r, g, b, a); + + for (y = -ro; y <= -ri; y++) + { + x = sqrt(ro2 * (1.0 - y*y/ro2)) + 0.5 ; + result |= hlinecliparc(renderer, -x, x, y, xc, yc, s, f) ; + } + for (y = -ri + 1; y <= ri - 1; y++) + { + x = sqrt(ro2 * (1.0 - y*y/ro2)) + 0.5 ; + z = sqrt(ri2 * (1.0 - y*y/ri2)) + 0.5 ; + result |= hlinecliparc(renderer, z, x, y, xc, yc, s, f) ; + result |= hlinecliparc(renderer, -z, -x, y, xc, yc, s, f) ; + } + for (y = ro; y >= ri; y--) + { + x = sqrt(ro2 * (1.0 - y*y/ro2)) + 0.5 ; + result |= hlinecliparc(renderer, -x, x, y, xc, yc, s, f) ; + } + return result ; +} + +// returns Returns 0 on success, -1 on failure. +int thickCircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 thick) +{ + return thickEllipseRGBA(renderer, x, y, rad, rad, r, g, b, a, thick); +} + +// returns Returns 0 on success, -1 on failure. +int thickEllipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color, Uint8 thick) +{ + Uint8 *c = (Uint8 *)&color; + return thickEllipseRGBA(renderer, x, y, rx, ry, c[0], c[1], c[2], c[3], thick); +} + +// returns Returns 0 on success, -1 on failure. +int thickArcColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color, Uint8 thick) +{ + Uint8 *c = (Uint8 *)&color; + return thickArcRGBA(renderer, x, y, rad, start, end, c[0], c[1], c[2], c[3], thick); +} + +// returns Returns 0 on success, -1 on failure. +int thickCircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color, Uint8 thick) +{ + Uint8 *c = (Uint8 *)&color; + return thickEllipseRGBA(renderer, x, y, rad, rad, c[0], c[1], c[2], c[3], thick); +} + +// Extensions for anti-aliased filled ellipses and polygons by Richard Russell 20-Aug-2019 + +/*! +\brief Draw anti-aliased filled ellipse with blending. + +\param renderer The renderer to draw on. +\param cx X coordinate of the center of the filled ellipse. +\param cy Y coordinate of the center of the filled ellipse. +\param rx Horizontal radius in pixels of the filled ellipse. +\param ry Vertical radius in pixels of the filled ellipse. +\param r The red value of the filled ellipse to draw. +\param g The green value of the filled ellipse to draw. +\param b The blue value of the filled ellipse to draw. +\param a The alpha value of the filled ellipse to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aaFilledEllipseRGBA(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int n, xi, yi, result = 0 ; + double s, v, x, y, dx, dy ; + + if ((rx <= 0.0) || (ry <= 0.0)) + return -1 ; + + result |= SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND) ; + if (rx >= ry) + { + n = ry + 1 ; + for (yi = cy - n - 1; yi <= cy + n + 1; yi++) + { + if (yi < (cy - 0.5)) + y = yi ; + else + y = yi + 1 ; + s = (y - cy) / ry ; + s = s * s ; + x = 0.5 ; + if (s < 1.0) + { + x = rx * sqrt(1.0 - s) ; + if (x >= 0.5) + { + result |= SDL_SetRenderDrawColor (renderer, r, g, b, a ) ; + result |= renderdrawline (renderer, cx - x + 1, yi, cx + x - 1, yi) ; + } + } + s = 8 * ry * ry ; + dy = fabs(y - cy) - 1.0 ; + xi = cx - x ; // left + while (1) + { + dx = (cx - xi - 1) * ry / rx ; + v = s - 4 * (dx - dy) * (dx - dy) ; + if (v < 0) break ; + v = (sqrt(v) - 2 * (dx + dy)) / 4 ; + if (v < 0) break ; + if (v > 1.0) v = 1.0 ; + result |= SDL_SetRenderDrawColor (renderer, r, g, b, (double)a * v) ; + result |= SDL_RenderDrawPoint (renderer, xi, yi) ; + xi -= 1 ; + } + xi = cx + x ; // right + while (1) + { + dx = (xi - cx) * ry / rx ; + v = s - 4 * (dx - dy) * (dx - dy) ; + if (v < 0) break ; + v = (sqrt(v) - 2 * (dx + dy)) / 4 ; + if (v < 0) break ; + if (v > 1.0) v = 1.0 ; + result |= SDL_SetRenderDrawColor (renderer, r, g, b, (double)a * v) ; + result |= SDL_RenderDrawPoint (renderer, xi, yi) ; + xi += 1 ; + } + } + } + else + { + n = rx + 1 ; + for (xi = cx - n - 1; xi <= cx + n + 1; xi++) + { + if (xi < (cx - 0.5)) + x = xi ; + else + x = xi + 1 ; + s = (x - cx) / rx ; + s = s * s ; + y = 0.5 ; + if (s < 1.0) + { + y = ry * sqrt(1.0 - s) ; + if (y >= 0.5) + { + result |= SDL_SetRenderDrawColor (renderer, r, g, b, a ) ; + result |= renderdrawline (renderer, xi, cy - y + 1, xi, cy + y - 1) ; + } + } + s = 8 * rx * rx ; + dx = fabs(x - cx) - 1.0 ; + yi = cy - y ; // top + while (1) + { + dy = (cy - yi - 1) * rx / ry ; + v = s - 4 * (dy - dx) * (dy - dx) ; + if (v < 0) break ; + v = (sqrt(v) - 2 * (dy + dx)) / 4 ; + if (v < 0) break ; + if (v > 1.0) v = 1.0 ; + result |= SDL_SetRenderDrawColor (renderer, r, g, b, (double)a * v) ; + result |= SDL_RenderDrawPoint (renderer, xi, yi) ; + yi -= 1 ; + } + yi = cy + y ; // bottom + while (1) + { + dy = (yi - cy) * rx / ry ; + v = s - 4 * (dy - dx) * (dy - dx) ; + if (v < 0) break ; + v = (sqrt(v) - 2 * (dy + dx)) / 4 ; + if (v < 0) break ; + if (v > 1.0) v = 1.0 ; + result |= SDL_SetRenderDrawColor (renderer, r, g, b, (double)a * v) ; + result |= SDL_RenderDrawPoint (renderer, xi, yi) ; + yi += 1 ; + } + } + } + return result ; +} + +// returns Returns 0 on success, -1 on failure. +int aaFilledEllipseColor(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return aaFilledEllipseRGBA(renderer, cx, cy, rx, ry, c[0], c[1], c[2], c[3]); +} + +static int _gfxPrimitivesCompareFloat2(const void *a, const void *b) +{ + float diff = *(float *)(a + sizeof(float)) - *(float *)(b + sizeof(float)) ; + if (diff != 0.0) return (diff > 0) - (diff < 0) ; + diff = *(float *)a - *(float *)b ; + return (diff > 0) - (diff < 0) ; +} + +// This constant determines the maximum size and/or complexity of polygon that can be +// drawn. Set to 16K the maximum aaArc height is approximately 1100 lines. +#define POLYSIZE 16384 + +/*! +\brief Draw anti-aliased filled polygon with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the filled polygon. +\param vy Vertex array containing Y coordinates of the points of the filled polygon. +\param n Number of points in the vertex array. Minimum number is 3. +\param r The red value of the filled polygon to draw. +\param g The green value of the filled polygon to draw. +\param b The blue value of the filed polygon to draw. +\param a The alpha value of the filled polygon to draw. + +\returns Returns 0 on success, -1 on failure, or -2 if the polygon is too large and/or complex. +*/ +int aaFilledPolygonRGBA(SDL_Renderer * renderer, const double * vx, const double * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int i, j, xi, yi, result ; + double x1, x2, y0, y1, y2, minx, maxx, prec ; + float *list, *strip ; + + if (n < 3) + return -1 ; + + result = SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND) ; + + // Find extrema: + minx = 99999.0 ; + maxx = -99999.0 ; + prec = 0.00001 ; + for (i = 0; i < n; i++) + { + double x = vx[i] ; + double y = fabs(vy[i]) ; + if (x < minx) minx = x ; + if (x > maxx) maxx = x ; + if (y > prec) prec = y ; + } + minx = floor (minx) ; + maxx = floor (maxx) ; + prec = floor (pow(2,19) / prec) ; + + // Allocate main array, this determines the maximum polygon size and complexity: + list = (float *) malloc (POLYSIZE * sizeof(float)) ; + if (list == NULL) + return -2 ; + + // Build vertex list. Special x-values used to indicate vertex type: + // x = -100001.0 indicates /\, x = -100003.0 indicates \/, x = -100002.0 neither + yi = 0 ; + y0 = floor(vy[n - 1] * prec) / prec ; + y1 = floor(vy[0] * prec) / prec ; + for (i = 1; i <= n; i++) + { + if (yi > POLYSIZE - 4) + { + free (list) ; + return -2 ; + } + y2 = floor(vy[i % n] * prec) / prec ; + if (((y1 < y2) - (y1 > y2)) == ((y0 < y1) - (y0 > y1))) + { + list[yi++] = -100002.0 ; + list[yi++] = y1 ; + list[yi++] = -100002.0 ; + list[yi++] = y1 ; + } + else + { + if (y0 != y1) + { + list[yi++] = (y1 < y0) - (y1 > y0) - 100002.0 ; + list[yi++] = y1 ; + } + if (y1 != y2) + { + list[yi++] = (y1 < y2) - (y1 > y2) - 100002.0 ; + list[yi++] = y1 ; + } + } + y0 = y1 ; + y1 = y2 ; + } + xi = yi ; + + // Sort vertex list: + qsort (list, yi / 2, sizeof(float) * 2, _gfxPrimitivesCompareFloat2) ; + + // Append line list to vertex list: + for (i = 1; i <= n; i++) + { + double x, y ; + double d = 0.5 / prec ; + + x1 = vx[i - 1] ; + y1 = floor(vy[i - 1] * prec) / prec ; + x2 = vx[i % n] ; + y2 = floor(vy[i % n] * prec) / prec ; + + if (y2 < y1) + { + double tmp ; + tmp = x1 ; x1 = x2 ; x2 = tmp ; + tmp = y1 ; y1 = y2 ; y2 = tmp ; + } + if (y2 != y1) + y0 = (x2 - x1) / (y2 - y1) ; + + for (j = 1; j < xi; j += 4) + { + y = list[j] ; + if (((y + d) <= y1) || (y == list[j + 4])) + continue ; + if ((y -= d) >= y2) + break ; + if (yi > POLYSIZE - 4) + { + free (list) ; + return -2 ; + } + if (y > y1) + { + list[yi++] = x1 + y0 * (y - y1) ; + list[yi++] = y ; + } + y += d * 2.0 ; + if (y < y2) + { + list[yi++] = x1 + y0 * (y - y1) ; + list[yi++] = y ; + } + } + + y = floor(y1) + 1.0 ; + while (y <= y2) + { + x = x1 + y0 * (y - y1) ; + if (yi > POLYSIZE - 2) + { + free (list) ; + return -2 ; + } + list[yi++] = x ; + list[yi++] = y ; + y += 1.0 ; + } + } + + // Sort combined list: + qsort (list, yi / 2, sizeof(float) * 2, _gfxPrimitivesCompareFloat2) ; + + // Plot lines: + strip = (float *) malloc ((maxx - minx + 2) * sizeof(float)) ; + if (strip == NULL) + { + free (list) ; + return -1 ; + } + memset (strip, 0, (maxx - minx + 1) * sizeof(float)) ; + n = yi ; + yi = list[1] ; + j = 0 ; + + for (i = 0; i < n - 7; i += 4) + { + float x1 = list[i + 0] ; + float y1 = list[i + 1] ; + float x3 = list[i + 2] ; + float x2 = list[i + j + 0] ; + float y2 = list[i + j + 1] ; + float x4 = list[i + j + 2] ; + + if (x1 + x3 == -200002.0) + j += 4 ; + else if (x1 + x3 == -200006.0) + j -= 4 ; + else if ((x1 >= minx) && (x2 >= minx)) + { + if (x1 > x2) { float tmp = x1 ; x1 = x2 ; x2 = tmp ; } + if (x3 > x4) { float tmp = x3 ; x3 = x4 ; x4 = tmp ; } + + for ( xi = x1 - minx; xi <= x4 - minx; xi++ ) + { + float u, v ; + float x = minx + xi ; + if (x < x2) u = (x - x1 + 1) / (x2 - x1 + 1) ; else u = 1.0 ; + if (x >= x3 - 1) v = (x4 - x) / (x4 - x3 + 1) ; else v = 1.0 ; + if ((u > 0.0) && (v > 0.0)) + strip[xi] += (y2 - y1) * (u + v - 1.0) ; + } + } + + if ((yi == (list[i + 5] - 1.0)) || (i == n - 8)) + { + for (xi = 0; xi <= maxx - minx; xi++) + { + if (strip[xi] != 0.0) + { + if (strip[xi] >= 0.996) + { + int x0 = xi ; + while (strip[++xi] >= 0.996) ; + xi-- ; + result |= SDL_SetRenderDrawColor (renderer, r, g, b, a) ; + result |= renderdrawline (renderer, minx + x0, yi, minx + xi, yi) ; + } + else + { + result |= SDL_SetRenderDrawColor (renderer, r, g, b, a * strip[xi]) ; + result |= SDL_RenderDrawPoint (renderer, minx + xi, yi) ; + } + } + } + memset (strip, 0, (maxx - minx + 1) * sizeof(float)) ; + yi++ ; + + } + } + + // Free arrays: + free (list) ; + free (strip) ; + return result ; +} + +// returns Returns 0 on success, -1 on failure. +int aaFilledPolygonColor(SDL_Renderer * renderer, const double * vx, const double * vy, int n, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return aaFilledPolygonRGBA(renderer, vx, vy, n, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw anti-aliased filled ellipical pie (or chord) with alpha blending. + +\param renderer The renderer to draw on. +\param cx X coordinate of the center of the filled pie. +\param cy Y coordinate of the center of the filled pie. +\param rx Horizontal radius in pixels of the filled pie. +\param ry Vertical radius in pixels of the filled pie. +\param start Starting angle in degrees of the filled pie; zero is right, increasing clockwise. +\param end Ending angle in degrees of the filled pie; zero is right, increasing clockwise. +\param chord Set to 0 for a pie (sector) or 1 for a chord (segment). +\param r The red value of the filled pie to draw. +\param g The green value of the filled pie to draw. +\param b The blue value of the filled pie to draw. +\param a The alpha value of the filled pie to draw. +/ +\returns Returns 0 on success, -1 on failure. +*/ +int aaFilledPieRGBA(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, + float start, float end, Uint32 chord, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int nverts, i, result; + double *vx, *vy; + + // Sanity check radii + if ((rx <= 0) || (ry <= 0) || (start == end)) + return -1; + + // Convert degrees to radians + start = fmod(start, 360.0) * 2.0 * M_PI / 360.0 ; + end = fmod(end, 360.0) * 2.0 * M_PI / 360.0 ; + while (start >= end) + end += 2.0 * M_PI ; + + // Calculate number of vertices on perimeter + nverts = (end - start) * sqrt(rx * ry) / M_PI ; + if (nverts < 2) + nverts = 2 ; + if (nverts > 180) + nverts = 180 ; + + // Allocate combined vertex array + vx = vy = (double *) malloc(2 * sizeof(double) * (nverts + 1)); + if (vx == NULL) + return (-1); + + // Update pointer to start of vy + vy += nverts + 1 ; + + // Calculate vertices: + for (i = 0; i < nverts; i++) + { + double angle = start + (end - start) * (double) i / (double) (nverts - 1) ; + vx[i] = cx + rx * cos(angle); + vy[i] = cy + ry * sin(angle); + } + + // Center: + vx[i] = cx ; + vy[i] = cy ; + + result = aaFilledPolygonRGBA(renderer, vx, vy, nverts + 1 - (chord != 0), r, g, b, a); + + // Free combined vertex array + free(vx); + + return (result); +} + +// returns Returns 0 on success, -1 on failure. +int aaFilledPieColor(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, float start, float end, Uint32 chord, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return aaFilledPieRGBA(renderer, cx, cy, rx, ry, start, end, chord, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw anti-aliased ellipical arc with alpha blending. + +\param renderer The renderer to draw on. +\param cx X coordinate of the center of the filled pie. +\param cy Y coordinate of the center of the filled pie. +\param rx Horizontal radius in pixels of the filled pie. +\param ry Vertical radius in pixels of the filled pie. +\param start Starting angle in degrees of the filled pie; zero is right, increasing clockwise. +\param end Ending angle in degrees of the filled pie; zero is right, increasing clockwise. +\param thick The thickness of the line in pixels. +\param r The red value of the filled pie to draw. +\param g The green value of the filled pie to draw. +\param b The blue value of the filled pie to draw. +\param a The alpha value of the filled pie to draw. +/ +\returns Returns 0 on success, -1 on failure. +*/ +int aaArcRGBA(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, + float start, float end, float thick, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int nverts, i, result; + double *vx, *vy; + + // Sanity check radii and thickness + if ((rx <= 0) || (ry <= 0) || (start == end) || (thick <= 0)) + return -1; + + // Convert degrees to radians + start = fmod(start, 360.0) * 2.0 * M_PI / 360.0 ; + end = fmod(end, 360.0) * 2.0 * M_PI / 360.0 ; + while (start >= end) + end += 2.0 * M_PI ; + + // Calculate number of vertices + nverts = 2 * floor((end - start) * sqrt(rx * ry) / M_PI) ; + if (nverts < 2) + nverts = 2 ; + if (nverts > 360) + nverts = 360 ; + + // Allocate combined vertex array + vx = vy = (double *) malloc(2 * sizeof(double) * nverts); + if (vx == NULL) + return (-1); + + // Update pointer to start of vy + vy += nverts ; + + // Calculate vertices: + for (i = 0; i < nverts / 2; i++) + { + double angle = start + (end - start) * (double) i / (double) (nverts / 2 - 1) ; + vx[i] = cx + (rx + thick/2) * cos(angle); + vy[i] = cy + (ry + thick/2) * sin(angle); + vx[nverts - 1 - i] = cx + (rx - thick/2) * cos(angle); + vy[nverts - 1 - i] = cy + (ry - thick/2) * sin(angle); + + } + + result = aaFilledPolygonRGBA(renderer, vx, vy, nverts, r, g, b, a); + + // Free combined vertex array + free(vx); + + return (result); +} + +// returns Returns 0 on success, -1 on failure. +int aaArcColor(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, float start, float end, float thick, Uint32 color) +{ + Uint8 *c = (Uint8 *)&color; + return aaArcRGBA(renderer, cx, cy, rx, ry, start, end, thick, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Draw an anti-aliased bezier curve with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the bezier curve. +\param vy Vertex array containing Y coordinates of the points of the bezier curve. +\param n Number of points in the vertex array. Minimum number is 3. +\param s Number of steps for the interpolation. Minimum number is 2. +\param thick Thickness of line in pixels. +\param r The red value of the bezier curve to draw. +\param g The green value of the bezier curve to draw. +\param b The blue value of the bezier curve to draw. +\param a The alpha value of the bezier curve to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aaBezierRGBA(SDL_Renderer * renderer, double *x, double *y, int n, int s, float thick, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int i, nverts, result; + double d, t, stepsize; + double x1, y1, x2, y2, dx = 0.0, dy = 0.0 ; + double *vx, *vy ; + + // Sanity check + if ((n < 3) || (s < 2)) + return -1 ; + + // Create combined vertex array: + nverts = n * s * 2 + 2 ; + vx = (double *) malloc (nverts * 2 * sizeof(double)) ; + if (vx == NULL) + return -1 ; + vy = vx + nverts ; + + // Draw Bezier + t = 0.0 ; + stepsize = 1.0 / (double)s ; + x1 = _evaluateBezier(x, n, t) ; + y1 = _evaluateBezier(y, n, t) ; + for (i = 0; i < n*s; i++) + { + t += stepsize ; + x2 = _evaluateBezier(x, n, t) ; + y2 = _evaluateBezier(y, n, t) ; + + dx = x2 - x1 ; + dy = y2 - y1 ; + d = thick * 0.5L / sqrt(dx*dx + dy*dy) ; + dx *= d ; + dy *= d ; + + vx[i] = x1 + dy ; + vy[i] = y1 - dx ; + vx[nverts-1-i] = x1 - dy ; + vy[nverts-1-i] = y1 + dx ; + + x1 = x2 ; + y1 = y2 ; + } + + vx[i] = x1 + dy ; + vy[i] = y1 - dx ; + vx[nverts-1-i] = x1 - dy ; + vy[nverts-1-i] = y1 + dx ; + + result = aaFilledPolygonRGBA(renderer, vx, vy, nverts, r, g, b, a); + + free (vx) ; + return (result); +} + +// returns Returns 0 on success, -1 on failure. +int aaBezierColor(SDL_Renderer * renderer, double *x, double *y, int n, int s, float thick, int color) +{ + Uint8 *c = (Uint8 *)&color; + return aaBezierRGBA(renderer, x, y, n, s, thick, c[0], c[1], c[2], c[3]); +} + +/*! +\brief Fill a region bounded by cubic Bezier curves, with alpha blending. + +\param renderer The renderer to draw on. +\param vx Vertex array containing X coordinates of the points of the bezier curves. +\param vy Vertex array containing Y coordinates of the points of the bezier curves. +\param n Number of points in the vertex array. Should be 3n + 1 for n bezier curves. +\param s Number of steps for the interpolation. Minimum number is 2. +\param r The red value of the bezier curve to draw. +\param g The green value of the bezier curve to draw. +\param b The blue value of the bezier curve to draw. +\param a The alpha value of the bezier curve to draw. + +\returns Returns 0 on success, -1 on failure. +*/ +int aaFilledPolyBezierRGBA(SDL_Renderer * renderer, double *x, double *y, int n, int s, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + int i, j, nbeziers, nverts, result; + double t, stepsize; + double x1, y1, x2, y2 ; + double *vx, *vy ; + + // Sanity check + if ((n < 7) || (s < 2)) + return -1 ; + + // Create combined vertex array: + nbeziers = (n - 1) / 3 ; + nverts = nbeziers * 4 * s + 1 ; + vx = (double *) malloc (nverts * 2 * sizeof(double)) ; + if (vx == NULL) + return -1 ; + vy = vx + nverts ; + + // Draw Beziers + stepsize = 1.0 / (double)s ; + for (j = 0; j < nbeziers; j++) + { + t = 0.0 ; + x1 = _evaluateBezier(x + j * 3, 4, t) ; + y1 = _evaluateBezier(y + j * 3, 4, t) ; + for (i = 0; i < 4*s; i++) + { + t += stepsize ; + x2 = _evaluateBezier(x + j * 3, 4, t) ; + y2 = _evaluateBezier(y + j * 3, 4, t) ; + + vx[i + j * s * 4] = x1 ; + vy[i + j * s * 4] = y1 ; + + x1 = x2 ; + y1 = y2 ; + } + } + + vx[j * s * 4] = x1 ; + vy[j * s * 4] = y1 ; + + result = aaFilledPolygonRGBA(renderer, vx, vy, nverts, r, g, b, a); + + free (vx) ; + return (result); +} + +// returns Returns 0 on success, -1 on failure. +int aaFilledPolyBezierColor(SDL_Renderer * renderer, double *x, double *y, int n, int s, int color) +{ + Uint8 *c = (Uint8 *)&color; + return aaFilledPolyBezierRGBA(renderer, x, y, n, s, c[0], c[1], c[2], c[3]); +} diff --git a/lib/sdl2-gfx/SDL2_gfxPrimitives.h b/lib/sdl2-gfx/SDL2_gfxPrimitives.h index 3a85cfd..8da13ed 100755 --- a/lib/sdl2-gfx/SDL2_gfxPrimitives.h +++ b/lib/sdl2-gfx/SDL2_gfxPrimitives.h @@ -3,6 +3,7 @@ SDL2_gfxPrimitives.h: graphics primitives for SDL Copyright (C) 2012-2014 Andreas Schiffler +Additions for BBC BASIC (C) 2016-2019 Richard Russell This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -24,6 +25,7 @@ misrepresented as being the original software. distribution. Andreas Schiffler -- aschiffler at ferzkopp dot net +Richard Russell -- richard at rtrussell dot co dot uk */ @@ -35,7 +37,7 @@ Andreas Schiffler -- aschiffler at ferzkopp dot net #define M_PI 3.1415926535897932384626433832795 #endif -#include "SDL.h" +#include "SDL2/SDL.h" /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus @@ -46,7 +48,7 @@ extern "C" { #define SDL2_GFXPRIMITIVES_MAJOR 1 #define SDL2_GFXPRIMITIVES_MINOR 0 -#define SDL2_GFXPRIMITIVES_MICRO 4 +#define SDL2_GFXPRIMITIVES_MICRO 1 /* ---- Function Prototypes */ @@ -64,7 +66,8 @@ extern "C" { # define SDL2_GFXPRIMITIVES_SCOPE extern #endif - /* Note: all ___Color routines expect the color to be in format 0xRRGGBBAA */ + /* Note: all ___Color routines expect the color to be in format 0xAABBGGRR */ + /* assuming a little-endian CPU (or 0xRRGGBBAA for a big-endian CPU) */ /* Pixel */ @@ -228,11 +231,37 @@ extern "C" { SDL2_GFXPRIMITIVES_SCOPE void gfxPrimitivesSetFont(const void *fontdata, Uint32 cw, Uint32 ch); SDL2_GFXPRIMITIVES_SCOPE void gfxPrimitivesSetFontRotation(Uint32 rotation); + SDL2_GFXPRIMITIVES_SCOPE void gfxPrimitivesSetFontZoom(Uint32 zoomx, Uint32 zoomy); SDL2_GFXPRIMITIVES_SCOPE int characterColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, char c, Uint32 color); SDL2_GFXPRIMITIVES_SCOPE int characterRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, char c, Uint8 r, Uint8 g, Uint8 b, Uint8 a); SDL2_GFXPRIMITIVES_SCOPE int stringColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint32 color); SDL2_GFXPRIMITIVES_SCOPE int stringRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, const char *s, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + /* Richard Russell's additions */ + + SDL2_GFXPRIMITIVES_SCOPE int thickEllipseColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color, Uint8 thick); + SDL2_GFXPRIMITIVES_SCOPE int thickEllipseRGBA(SDL_Renderer * renderer, Sint16 xc, Sint16 yc, Sint16 xr, Sint16 yr, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 thick); + SDL2_GFXPRIMITIVES_SCOPE int thickArcColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color, Uint8 thick); + SDL2_GFXPRIMITIVES_SCOPE int thickArcRGBA(SDL_Renderer * renderer, Sint16 xc, Sint16 yc, Sint16 rad, Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 thick); + SDL2_GFXPRIMITIVES_SCOPE int thickCircleColor(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint32 color, Uint8 thick); + SDL2_GFXPRIMITIVES_SCOPE int thickCircleRGBA(SDL_Renderer * renderer, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a, Uint8 thick); + + SDL2_GFXPRIMITIVES_SCOPE int aaFilledEllipseColor(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, Uint32 color); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledEllipseRGBA(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledPolygonColor(SDL_Renderer * renderer, const double * vx, const double * vy, int n, Uint32 color); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledPolygonRGBA(SDL_Renderer * renderer, const double * vx, const double * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledPieColor(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, float start, float end, Uint32 chord, Uint32 color); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledPieRGBA(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, + float start, float end, Uint32 chord, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + + SDL2_GFXPRIMITIVES_SCOPE int aaArcColor(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, float start, float end, float thick, Uint32 color); + SDL2_GFXPRIMITIVES_SCOPE int aaArcRGBA(SDL_Renderer * renderer, float cx, float cy, float rx, float ry, + float start, float end, float thick, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + SDL2_GFXPRIMITIVES_SCOPE int aaBezierColor(SDL_Renderer * renderer, double *x, double *y, int n, int s, float thick, int color); + SDL2_GFXPRIMITIVES_SCOPE int aaBezierRGBA(SDL_Renderer * renderer, double *x, double *y, int n, int s, float thick, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledPolyBezierColor(SDL_Renderer * renderer, double *x, double *y, int n, int s, int color); + SDL2_GFXPRIMITIVES_SCOPE int aaFilledPolyBezierRGBA(SDL_Renderer * renderer, double *x, double *y, int n, int s, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/Box.cpp b/src/Box.cpp index f5241b4..0d7a46f 100644 --- a/src/Box.cpp +++ b/src/Box.cpp @@ -47,6 +47,11 @@ glm::vec2 Box::get_size() const return glm::vec2(get_w(), get_h()); } +float Box::get_area() const +{ + return get_w() * get_h(); +} + void Box::set_size(const glm::vec2& size, bool preserve_center) { glm::vec2 center = get_center(); @@ -78,29 +83,78 @@ float Box::get_left() const return get_x(); } +float Box::get_center_x() const +{ + return get_left() + get_w() / 2; +} + +float Box::get_center_y() const +{ + return get_top() + get_h() / 2; +} + void Box::set_top(float top) { set_y(top); } -void Box::set_right(float right) +void Box::set_right(float right, bool drag) { - move(glm::vec2(right - get_right(), 0)); + float delta = right - get_right(); + if (!drag) + { + move({delta, 0}); + } + else + { + drag_right(delta); + } +} + +void Box::drag_right(float delta) +{ + float previous_right = get_right(); + set_right(get_right() + delta); + set_w(get_w() + previous_right - get_right()); } void Box::set_bottom(float bottom) { - move(glm::vec2(0, bottom - get_bottom())); + move({0, bottom - get_bottom()}); } -void Box::set_left(float left) +void Box::set_left(float left, bool drag) { - set_x(left); + if (!drag) + { + set_x(left); + } + else + { + drag_left(left - get_left()); + } +} + +void Box::drag_left(float delta) +{ + float previous_left = get_left(); + set_left(get_left() + delta); + set_w(get_w() + previous_left - get_left()); +} + +void Box::set_center_x(float x) +{ + move({x - get_center_x(), 0}); +} + +void Box::set_center_y(float y) +{ + move({0, y - get_center_y()}); } glm::vec2 Box::get_nw() const { - return glm::vec2(get_x(), get_y()); + return {get_x(), get_y()}; } glm::vec2 Box::get_north() const @@ -115,12 +169,22 @@ glm::vec2 Box::get_ne() const glm::vec2 Box::get_east() const { - return glm::vec2(get_right(), get_y() + get_h() / 2); + return glm::vec2(get_right(), get_top() + get_h() / 2); +} + +glm::vec2 Box::get_se() const +{ + return glm::vec2(get_right(), get_bottom()); } glm::vec2 Box::get_south() const { - return glm::vec2(get_x() + get_w() / 2, get_bottom()); + return glm::vec2(get_left() + get_w() / 2, get_bottom()); +} + +glm::vec2 Box::get_sw() const +{ + return glm::vec2(get_left(), get_bottom()); } glm::vec2 Box::get_west() const @@ -153,6 +217,11 @@ void Box::set_east(const glm::vec2& e) move(e - get_east()); } +void Box::set_se(const glm::vec2& se) +{ + move(se - get_se()); +} + void Box::set_south(const glm::vec2& s) { move(s - get_south()); @@ -173,7 +242,7 @@ SDL_FRect* Box::get_rect() return ▭ } -SDL_Rect Box::get_int_rect() +SDL_Rect Box::get_int_rect() const { return {static_cast(rect.x), static_cast(rect.y), static_cast(rect.w), static_cast(rect.h)}; } diff --git a/src/Box.hpp b/src/Box.hpp index 94187ff..9d6e723 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -24,30 +24,40 @@ struct Box void set_h(float); glm::vec2 get_size() const; void set_size(const glm::vec2&, bool = false); + float get_area() const; float get_top() const; float get_right() const; float get_bottom() const; float get_left() const; + float get_center_x() const; + float get_center_y() const; void set_top(float); - void set_right(float); + void set_right(float, bool=false); + void drag_right(float); void set_bottom(float); - void set_left(float); + void set_left(float, bool=false); + void drag_left(float); + void set_center_x(float); + void set_center_y(float); glm::vec2 get_nw() const; glm::vec2 get_north() const; glm::vec2 get_ne() const; glm::vec2 get_east() const; + glm::vec2 get_se() const; glm::vec2 get_south() const; + glm::vec2 get_sw() const; glm::vec2 get_west() const; glm::vec2 get_center() const; void set_nw(const glm::vec2&); void set_north(const glm::vec2&); void set_ne(const glm::vec2&); void set_east(const glm::vec2&); + void set_se(const glm::vec2&); void set_south(const glm::vec2&); void set_west(const glm::vec2&); void set_center(const glm::vec2&); SDL_FRect* get_rect(); - SDL_Rect get_int_rect(); + SDL_Rect get_int_rect() const; void clear(); void scale(float, bool = false); void move(const glm::vec2&); diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 2c466c7..4d026b1 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -40,7 +40,8 @@ void Configuration::set_defaults() sys_config["display"] = { {"dimensions", {640, 480}}, {"framerate", 60}, - {"title", "sfw"} + {"title", "sfw"}, + {"debug", false} }; sys_config["recording"] = { {"enabled", false}, diff --git a/src/Game.cpp b/src/Game.cpp index a5aae54..3ee5661 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -455,6 +455,11 @@ SDL_Renderer* Game::get_renderer() return renderer; } +glm::vec2 Game::weight(glm::vec2 motion) +{ + return {weight(motion.x), weight(motion.y)}; +} + void Game::run() { SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); @@ -467,7 +472,7 @@ void Game::run() while (!done) { frame(SDL_GetTicks()); - SDL_Delay(0); + SDL_Delay(8); } #endif } @@ -538,11 +543,6 @@ void Game::flag_to_end() done = true; } -glm::vec2 Game::weight(glm::vec2 motion) -{ - return glm::vec2(weight(motion.x), weight(motion.y)); -} - void Game::set_framerate(int f) { if (f < 1) diff --git a/src/Game.hpp b/src/Game.hpp index f02eb9f..947a7dc 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -79,11 +79,11 @@ struct Game : Node std::string get_pixel_format_string(Uint32); SDL_Window* get_window(); SDL_Renderer* get_renderer(); + glm::vec2 weight(glm::vec2); void run(); void frame(float); void flag_to_end(); virtual void update() {}; - glm::vec2 weight(glm::vec2); void set_framerate(int); void handle_quit_event(SDL_Event&); void quit(); @@ -92,7 +92,6 @@ struct Game : Node template float weight(T amount) { - // std::cout << amount << " - " << last_frame_length << std::endl; return (last_frame_length / (1000.0 / 60)) * amount; } diff --git a/src/Node.cpp b/src/Node.cpp index 269e382..3a7c450 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -34,16 +34,6 @@ nlohmann::json& Node::get_configuration() return get_root()->configuration.config; } -// Game* Node::get_root() -// { -// Node* current = this; -// while (current->parent != NULL) -// { -// current = current->parent; -// } -// return static_cast(current); -// } - Delegate& Node::get_delegate() { return get_root()->delegate; @@ -54,6 +44,16 @@ Display& Node::get_display() return get_root()->display; } +SDL_Renderer* Node::get_renderer() +{ + return get_root()->get_renderer(); +} + +SDL_Window* Node::get_window() +{ + return get_root()->get_window(); +} + void Node::suppress_input_temporarily(int length) { get_root()->input.suppress(); diff --git a/src/Node.hpp b/src/Node.hpp index c20ae3c..1874289 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -3,6 +3,7 @@ #include +#include "glm/vec2.hpp" #include "json/json.hpp" #include "SDL.h" @@ -28,6 +29,8 @@ struct Node nlohmann::json& get_configuration(); Delegate& get_delegate(); Display& get_display(); + SDL_Renderer* get_renderer(); + SDL_Window* get_window(); void suppress_input_temporarily(int = 0); void print_branch(); virtual std::string get_class_name() { return "Node"; }; diff --git a/src/Segment.cpp b/src/Segment.cpp index cc77f44..a9e59bb 100644 --- a/src/Segment.cpp +++ b/src/Segment.cpp @@ -6,8 +6,34 @@ Segment::Segment(const glm::vec2& location) : Segment(location, location) {}; Segment::Segment(const glm::vec2& start, const glm::vec2& end) : start(start), end(end) {}; -glm::vec2 Segment::get_intersection() +glm::vec2 Segment::get_start() const { + return start; +} + +void Segment::set_start(const glm::vec2& s) +{ + start = s; +} + +glm::vec2 Segment::get_end() const +{ + return end; +} + +void Segment::set_end(const glm::vec2& e) +{ + end = e; +} + +bool Segment::intersect(const Segment& segment) +{ + return sfw::segments_intersect(*this, segment); +} + +bool Segment::intersect(const Segment& segment, glm::vec2& intersection) +{ + return sfw::segments_intersect(*this, segment, intersection); } float Segment::get_dx() @@ -28,10 +54,12 @@ void Segment::move(const glm::vec2& delta) glm::vec2 Segment::get_center() { + return sfw::get_segments(*this, 2)[0].end; } std::ostream& operator<<(std::ostream& out, const Segment& segment) { - out << "{(" << segment.start.x << ", " << segment.start.y << "), (" << segment.end.x << ", " << segment.end.y << ")}"; + out << "{(" << segment.start.x << ", " << segment.start.y << "), (" << + segment.end.x << ", " << segment.end.y << ")}"; return out; } diff --git a/src/Segment.hpp b/src/Segment.hpp index fe7ac31..b409da9 100644 --- a/src/Segment.hpp +++ b/src/Segment.hpp @@ -14,7 +14,12 @@ struct Segment Segment(); Segment(const glm::vec2&); Segment(const glm::vec2&, const glm::vec2&); - glm::vec2 get_intersection(); + glm::vec2 get_start() const; + void set_start(const glm::vec2&); + glm::vec2 get_end() const; + void set_end(const glm::vec2&); + bool intersect(const Segment&); + bool intersect(const Segment&, glm::vec2&); float get_dx(); float get_length(); void move(const glm::vec2&); @@ -24,4 +29,6 @@ struct Segment std::ostream& operator<<(std::ostream&, const Segment&); +#include "extension.hpp" + #endif diff --git a/src/Sprite.cpp b/src/Sprite.cpp index d1eb94c..7eade86 100644 --- a/src/Sprite.cpp +++ b/src/Sprite.cpp @@ -53,7 +53,16 @@ void Sprite::load_file(fs::path path) Game *game = get_root(); const char* previous_scale_quality = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scale_quality.c_str()); - SDL_Texture *texture = IMG_LoadTexture(game->renderer, path.string().c_str()); + SDL_Texture *base = IMG_LoadTexture(game->renderer, path.string().c_str()), *texture; + if (texture_access == SDL_TEXTUREACCESS_TARGET) + { + texture = sfw::duplicate_texture(get_renderer(), base); + SDL_DestroyTexture(base); + } + else + { + texture = base; + } SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, previous_scale_quality); if (not texture) { @@ -109,6 +118,11 @@ Frameset& Sprite::get_current_frameset() return framesets[current_frameset_name]; } +SDL_Texture* Sprite::get_current_frame() +{ + return frames[get_current_frameset().get_current_frame_index()]; +} + Box& Sprite::get_box(int index) { return boxes[index]; @@ -245,6 +259,16 @@ float Sprite::get_left(int index) return get_box(index).get_left(); } +float Sprite::get_center_x(int index) +{ + return get_box(index).get_center_x(); +} + +float Sprite::get_center_y(int index) +{ + return get_box(index).get_center_y(); +} + glm::vec2 Sprite::get_nw(int index) { return get_box(index).get_nw(); @@ -265,11 +289,21 @@ glm::vec2 Sprite::get_east(int index) return get_box(index).get_east(); } +glm::vec2 Sprite::get_se(int index) +{ + return get_box(index).get_se(); +} + glm::vec2 Sprite::get_south(int index) { return get_box(index).get_south(); } +glm::vec2 Sprite::get_sw(int index) +{ + return get_box(index).get_sw(); +} + glm::vec2 Sprite::get_west(int index) { return get_box(index).get_west(); @@ -300,11 +334,31 @@ void Sprite::set_left(float left) move({left - get_left(), 0}, false); } +void Sprite::set_center_x(float x) +{ + move({x - get_center_x(), 0}, false); +} + +void Sprite::set_center_y(float y) +{ + move({0, y - get_center_y()}, false); +} + +void Sprite::set_se(const glm::vec2& se) +{ + move(se - get_se(), false); +} + void Sprite::set_south(const glm::vec2& south) { move(south - get_south(), false); } +void Sprite::set_sw(const glm::vec2& sw) +{ + move(sw - get_sw(), false); +} + void Sprite::set_nw(const glm::vec2& nw) { move(nw - get_nw(), false); @@ -391,8 +445,7 @@ void Sprite::update() blink_animation.update(); if (is_loaded() && !is_hidden() && get_current_frameset().get_frame_count()) { - int index = get_current_frameset().get_current_frame_index(); - SDL_Texture* texture = frames[index]; + SDL_Texture* texture = get_current_frame(); SDL_Renderer* renderer = get_root()->renderer; SDL_SetTextureAlphaMod(texture, alpha_mod); SDL_SetRenderTarget(renderer, NULL); diff --git a/src/Sprite.hpp b/src/Sprite.hpp index 5eeefa5..e8ab8f6 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -34,6 +34,7 @@ struct Sprite : Node std::map framesets; std::string current_frameset_name; glm::bvec2 wrap = {false, false}; + int texture_access = SDL_TEXTUREACCESS_TARGET; Box wrap_frame; Sprite(); @@ -47,6 +48,7 @@ struct Sprite : Node Frameset& add_frameset(std::string); Frameset& set_frameset(std::string); Frameset& get_current_frameset(); + SDL_Texture* get_current_frame(); Box& get_box(int = 0); void add_box(glm::vec2, bool = false); void update_size(bool = false); @@ -70,20 +72,28 @@ struct Sprite : Node float get_right(int = 0); float get_bottom(int = 0); float get_left(int = 0); + float get_center_x(int = 0); + float get_center_y(int = 0); glm::vec2 get_nw(int = 0); glm::vec2 get_north(int = 0); glm::vec2 get_ne(int = 0); glm::vec2 get_east(int = 0); + glm::vec2 get_se(int = 0); glm::vec2 get_south(int = 0); + glm::vec2 get_sw(int = 0); glm::vec2 get_west(int = 0); glm::vec2 get_center(int = 0); void set_top(float); void set_right(float); void set_bottom(float); void set_left(float); + void set_center_x(float); + void set_center_y(float); void set_nw(const glm::vec2&); void set_ne(const glm::vec2&); + void set_se(const glm::vec2&); void set_south(const glm::vec2&); + void set_sw(const glm::vec2&); void set_center(const glm::vec2&); void add_wrap(bool, bool); void add_wrap(bool, bool, Box); diff --git a/src/extension.cpp b/src/extension.cpp index ee67b86..58a4ecb 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -1,26 +1,47 @@ #include "extension.hpp" -glm::vec2 sfw::get_step(glm::vec2 start, glm::vec2 end, float speed) +glm::vec2 sfw::get_step(const Segment& segment, float speed) { - float angle = glm::atan(end.x - start.x, end.y - start.y); + float angle = glm::atan(segment.end.x - segment.start.x, segment.end.y - segment.start.y); return glm::vec2(speed * glm::sin(angle), speed * glm::cos(angle)); } +glm::vec2 sfw::get_step_relative(const Segment& segment, float relative_length_per_step) +{ + return get_step( + segment, glm::distance(segment.start, segment.end) * relative_length_per_step); +} + +std::vector sfw::get_segments(const Segment& base, int count) +{ + glm::vec2 step = get_step_relative(base, 1.0f / count); + std::vector segments; + segments.reserve(count); + glm::vec2 start = base.start, end; + for (int ii = 0; ii < count; ii++) + { + end = start + step; + segments.emplace_back(start, end); + start = end; + } + return segments; +} + void sfw::set_magnitude(glm::vec2& vector, float magnitude) { vector = glm::normalize(vector) * magnitude; } -bool sfw::lines_intersect(const Segment& segment_a, const Segment& segment_b) +bool sfw::segments_intersect(const Segment& segment_a, const Segment& segment_b) { glm::vec2 intersection; - return lines_intersect(segment_a, segment_b, intersection); + return segments_intersect(segment_a, segment_b, intersection); } -/* - from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c -*/ -bool sfw::lines_intersect(const Segment& segment_a, const Segment& segment_b, glm::vec2& intersection) +// --- +// from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c +// --- +bool sfw::segments_intersect(const Segment& segment_a, const Segment& segment_b, glm::vec2& intersection) { float x1 = segment_a.start.x, y1 = segment_a.start.y, x2 = segment_a.end.x, y2 = segment_a.end.y, x3 = segment_b.start.x, y3 = segment_b.start.y, @@ -82,6 +103,63 @@ Box sfw::get_texture_box(SDL_Texture* texture) return Box(glm::vec2(0, 0), glm::vec2(w, h)); } +void sfw::populate_pixel_2d_array(SDL_Renderer* renderer, SDL_Texture* texture, std::vector>& pixels) +{ + populate_pixel_2d_array(renderer, texture, pixels, get_texture_box(texture)); +} + +void sfw::populate_pixel_2d_array( + SDL_Renderer* renderer, SDL_Texture* texture, std::vector>& pixels, const Box& region) +{ + int access; + if (SDL_QueryTexture(texture, NULL, &access, NULL, NULL) < 0) + { + print_sdl_error("Could not query texture for access flag"); + } + else + { + if (access != SDL_TEXTUREACCESS_TARGET) + { + texture = duplicate_texture(renderer, texture); + } + if (SDL_SetRenderTarget(renderer, texture) < 0) + { + print_sdl_error("Could not set render target"); + } + else + { + Uint32 format = SDL_PIXELFORMAT_RGBA32; + int bytes_per_pixel = SDL_BYTESPERPIXEL(format); + int bytes_per_row = bytes_per_pixel * region.get_w(); + int bytes_total = bytes_per_row * region.get_h(); + Uint8* source = new Uint8[bytes_total]; + SDL_Rect int_rect = region.get_int_rect(); + if (SDL_RenderReadPixels(renderer, &int_rect, format, source, bytes_per_row) < 0) + { + print_sdl_error("Could not read pixels after setting remapped texture as target"); + } + else + { + pixels.reserve(region.get_w()); + for (int x = 0; x < region.get_w(); x++) + { + std::vector column; + pixels.push_back(column); + pixels[x].reserve(region.get_h()); + } + for (int y = 0, ii = 0; y < region.get_h(); y++) + { + for (int x = 0; x < region.get_w(); x++) + { + pixels[x][y] = {source[ii++], source[ii++], source[ii++], source[ii++]}; + } + } + } + delete[] source; + } + } +} + void sfw::fill_texture(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { SDL_SetRenderTarget(renderer, texture); @@ -133,6 +211,8 @@ SDL_Texture* sfw::duplicate_texture(SDL_Renderer* renderer, SDL_Texture* base, U print_sdl_error("could not set render target to duplicate"); return NULL; } + SDL_SetTextureBlendMode(base, SDL_BLENDMODE_NONE); + SDL_SetTextureBlendMode(duplicate, SDL_BLENDMODE_BLEND); if ((SDL_RenderCopyF(renderer, base, NULL, NULL)) < 0) { print_sdl_error("could not render base onto duplicate"); @@ -408,6 +488,7 @@ bool operator==(const SDL_Color& color_1, const SDL_Color& color_2) std::ostream& operator<<(std::ostream& out, const SDL_Color& color) { - out << "{" << color.r << ", " << color.g << ", " << color.b << ", " << color.a << "}"; + out << "{" << static_cast(color.r) << ", " << static_cast(color.g) << ", " << + static_cast(color.b) << ", " << static_cast(color.a) << "}"; return out; } diff --git a/src/extension.hpp b/src/extension.hpp index 4329281..a11ee90 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -13,6 +13,7 @@ #include "SDL.h" #include "SDL_image.h" +#include "SDL_pixels.h" #define GLM_ENABLE_EXPERIMENTAL #include "glm/trigonometric.hpp" @@ -25,13 +26,18 @@ namespace sfw { + enum scaler {scale2x, xbr}; - glm::vec2 get_step(glm::vec2, glm::vec2, float); + glm::vec2 get_step(const Segment&, float); + glm::vec2 get_step_relative(const Segment&, float); + std::vector get_segments(const Segment&, int); void set_magnitude(glm::vec2&, float); - bool lines_intersect(const Segment&, const Segment&); - bool lines_intersect(const Segment&, const Segment&, glm::fvec2&); + bool segments_intersect(const Segment&, const Segment&); + bool segments_intersect(const Segment&, const Segment&, glm::fvec2&); Box get_texture_box(SDL_Texture*); + void populate_pixel_2d_array(SDL_Renderer*, SDL_Texture*, std::vector>&); + void populate_pixel_2d_array(SDL_Renderer*, SDL_Texture*, std::vector>&, const Box&); void fill_texture(SDL_Renderer*, SDL_Texture*, Uint8, Uint8, Uint8, Uint8 = 0xff); void fill_texture(SDL_Renderer*, SDL_Texture*, SDL_Texture*); SDL_Texture* get_filled_texture(SDL_Renderer*, glm::vec2, const SDL_Color&, Uint32 = SDL_PIXELFORMAT_RGBA32);