Daniel Santos 2 роки тому
батько
коміт
2660449c6c

+ 3 - 1
CMakeLists.txt

@@ -2572,7 +2572,9 @@ elseif(PS2)
   if(SDL_VIDEO)
     set(SDL_VIDEO_DRIVER_PS2 1)
     set(SDL_VIDEO_RENDER_PS2 1)
-    file(GLOB PS2_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/ps2/*.c)
+    file(GLOB PS2_VIDEO_SOURCES 
+    ${SDL2_SOURCE_DIR}/src/video/ps2/*.c
+    ${SDL2_SOURCE_DIR}/src/render/ps2/*.c)
     set(SOURCE_FILES ${SOURCE_FILES} ${PS2_VIDEO_SOURCES})
     set(SDL_VIDEO_OPENGL 0)
     set(HAVE_SDL_VIDEO TRUE)

+ 3 - 0
src/render/SDL_render.c

@@ -114,6 +114,9 @@ static const SDL_RenderDriver *render_drivers[] = {
 #if SDL_VIDEO_RENDER_DIRECTFB
     &DirectFB_RenderDriver,
 #endif
+#if SDL_VIDEO_RENDER_PS2
+    &PS2_RenderDriver,
+#endif
 #if SDL_VIDEO_RENDER_PSP
     &PSP_RenderDriver,
 #endif

+ 1 - 0
src/render/SDL_sysrender.h

@@ -293,6 +293,7 @@ extern SDL_RenderDriver GLES2_RenderDriver;
 extern SDL_RenderDriver GLES_RenderDriver;
 extern SDL_RenderDriver DirectFB_RenderDriver;
 extern SDL_RenderDriver METAL_RenderDriver;
+extern SDL_RenderDriver PS2_RenderDriver;
 extern SDL_RenderDriver PSP_RenderDriver;
 extern SDL_RenderDriver SW_RenderDriver;
 extern SDL_RenderDriver VITA_GXM_RenderDriver;

+ 357 - 0
src/render/ps2/SDL_blendfillrect.c

@@ -0,0 +1,357 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "SDL_draw.h"
+#include "SDL_blendfillrect.h"
+
+
+static int
+SDL_BlendFillRect_RGB555(SDL_Surface * dst, const SDL_Rect * rect,
+                         SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_RGB555);
+        break;
+    case SDL_BLENDMODE_ADD:
+        FILLRECT(Uint16, DRAW_SETPIXEL_ADD_RGB555);
+        break;
+    case SDL_BLENDMODE_MOD:
+        FILLRECT(Uint16, DRAW_SETPIXEL_MOD_RGB555);
+        break;
+    case SDL_BLENDMODE_MUL:
+        FILLRECT(Uint16, DRAW_SETPIXEL_MUL_RGB555);
+        break;
+    default:
+        FILLRECT(Uint16, DRAW_SETPIXEL_RGB555);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendFillRect_RGB565(SDL_Surface * dst, const SDL_Rect * rect,
+                         SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_RGB565);
+        break;
+    case SDL_BLENDMODE_ADD:
+        FILLRECT(Uint16, DRAW_SETPIXEL_ADD_RGB565);
+        break;
+    case SDL_BLENDMODE_MOD:
+        FILLRECT(Uint16, DRAW_SETPIXEL_MOD_RGB565);
+        break;
+    case SDL_BLENDMODE_MUL:
+        FILLRECT(Uint16, DRAW_SETPIXEL_MUL_RGB565);
+        break;
+    default:
+        FILLRECT(Uint16, DRAW_SETPIXEL_RGB565);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendFillRect_RGB888(SDL_Surface * dst, const SDL_Rect * rect,
+                         SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_RGB888);
+        break;
+    case SDL_BLENDMODE_ADD:
+        FILLRECT(Uint32, DRAW_SETPIXEL_ADD_RGB888);
+        break;
+    case SDL_BLENDMODE_MOD:
+        FILLRECT(Uint32, DRAW_SETPIXEL_MOD_RGB888);
+        break;
+    case SDL_BLENDMODE_MUL:
+        FILLRECT(Uint32, DRAW_SETPIXEL_MUL_RGB888);
+        break;
+    default:
+        FILLRECT(Uint32, DRAW_SETPIXEL_RGB888);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendFillRect_ARGB8888(SDL_Surface * dst, const SDL_Rect * rect,
+                           SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_ARGB8888);
+        break;
+    case SDL_BLENDMODE_ADD:
+        FILLRECT(Uint32, DRAW_SETPIXEL_ADD_ARGB8888);
+        break;
+    case SDL_BLENDMODE_MOD:
+        FILLRECT(Uint32, DRAW_SETPIXEL_MOD_ARGB8888);
+        break;
+    case SDL_BLENDMODE_MUL:
+        FILLRECT(Uint32, DRAW_SETPIXEL_MUL_ARGB8888);
+        break;
+    default:
+        FILLRECT(Uint32, DRAW_SETPIXEL_ARGB8888);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendFillRect_RGB(SDL_Surface * dst, const SDL_Rect * rect,
+                      SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_PixelFormat *fmt = dst->format;
+    unsigned inva = 0xff - a;
+
+    switch (fmt->BytesPerPixel) {
+    case 2:
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            FILLRECT(Uint16, DRAW_SETPIXEL_BLEND_RGB);
+            break;
+        case SDL_BLENDMODE_ADD:
+            FILLRECT(Uint16, DRAW_SETPIXEL_ADD_RGB);
+            break;
+        case SDL_BLENDMODE_MOD:
+            FILLRECT(Uint16, DRAW_SETPIXEL_MOD_RGB);
+            break;
+        case SDL_BLENDMODE_MUL:
+            FILLRECT(Uint16, DRAW_SETPIXEL_MUL_RGB);
+            break;
+        default:
+            FILLRECT(Uint16, DRAW_SETPIXEL_RGB);
+            break;
+        }
+        return 0;
+    case 4:
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_RGB);
+            break;
+        case SDL_BLENDMODE_ADD:
+            FILLRECT(Uint32, DRAW_SETPIXEL_ADD_RGB);
+            break;
+        case SDL_BLENDMODE_MOD:
+            FILLRECT(Uint32, DRAW_SETPIXEL_MOD_RGB);
+            break;
+        case SDL_BLENDMODE_MUL:
+            FILLRECT(Uint32, DRAW_SETPIXEL_MUL_RGB);
+            break;
+        default:
+            FILLRECT(Uint32, DRAW_SETPIXEL_RGB);
+            break;
+        }
+        return 0;
+    default:
+        return SDL_Unsupported();
+    }
+}
+
+static int
+SDL_BlendFillRect_RGBA(SDL_Surface * dst, const SDL_Rect * rect,
+                       SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_PixelFormat *fmt = dst->format;
+    unsigned inva = 0xff - a;
+
+    switch (fmt->BytesPerPixel) {
+    case 4:
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            FILLRECT(Uint32, DRAW_SETPIXEL_BLEND_RGBA);
+            break;
+        case SDL_BLENDMODE_ADD:
+            FILLRECT(Uint32, DRAW_SETPIXEL_ADD_RGBA);
+            break;
+        case SDL_BLENDMODE_MOD:
+            FILLRECT(Uint32, DRAW_SETPIXEL_MOD_RGBA);
+            break;
+        case SDL_BLENDMODE_MUL:
+            FILLRECT(Uint32, DRAW_SETPIXEL_MUL_RGBA);
+            break;
+        default:
+            FILLRECT(Uint32, DRAW_SETPIXEL_RGBA);
+            break;
+        }
+        return 0;
+    default:
+        return SDL_Unsupported();
+    }
+}
+
+int
+SDL_BlendFillRect(SDL_Surface * dst, const SDL_Rect * rect,
+                  SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_Rect clipped;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_BlendFillRect(): dst");
+    }
+
+    /* This function doesn't work on surfaces < 8 bpp */
+    if (dst->format->BitsPerPixel < 8) {
+        return SDL_SetError("SDL_BlendFillRect(): Unsupported surface format");
+    }
+
+    /* If 'rect' == NULL, then fill the whole surface */
+    if (rect) {
+        /* Perform clipping */
+        if (!SDL_IntersectRect(rect, &dst->clip_rect, &clipped)) {
+            return 0;
+        }
+        rect = &clipped;
+    } else {
+        rect = &dst->clip_rect;
+    }
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(r, a);
+        g = DRAW_MUL(g, a);
+        b = DRAW_MUL(b, a);
+    }
+
+    switch (dst->format->BitsPerPixel) {
+    case 15:
+        switch (dst->format->Rmask) {
+        case 0x7C00:
+            return SDL_BlendFillRect_RGB555(dst, rect, blendMode, r, g, b, a);
+        }
+        break;
+    case 16:
+        switch (dst->format->Rmask) {
+        case 0xF800:
+            return SDL_BlendFillRect_RGB565(dst, rect, blendMode, r, g, b, a);
+        }
+        break;
+    case 32:
+        switch (dst->format->Rmask) {
+        case 0x00FF0000:
+            if (!dst->format->Amask) {
+                return SDL_BlendFillRect_RGB888(dst, rect, blendMode, r, g, b, a);
+            } else {
+                return SDL_BlendFillRect_ARGB8888(dst, rect, blendMode, r, g, b, a);
+            }
+            /* break; -Wunreachable-code-break */
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (!dst->format->Amask) {
+        return SDL_BlendFillRect_RGB(dst, rect, blendMode, r, g, b, a);
+    } else {
+        return SDL_BlendFillRect_RGBA(dst, rect, blendMode, r, g, b, a);
+    }
+}
+
+int
+SDL_BlendFillRects(SDL_Surface * dst, const SDL_Rect * rects, int count,
+                   SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_Rect rect;
+    int i;
+    int (*func)(SDL_Surface * dst, const SDL_Rect * rect,
+                SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) = NULL;
+    int status = 0;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_BlendFillRects(): dst");
+    }
+
+    /* This function doesn't work on surfaces < 8 bpp */
+    if (dst->format->BitsPerPixel < 8) {
+        return SDL_SetError("SDL_BlendFillRects(): Unsupported surface format");
+    }
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(r, a);
+        g = DRAW_MUL(g, a);
+        b = DRAW_MUL(b, a);
+    }
+
+    /* FIXME: Does this function pointer slow things down significantly? */
+    switch (dst->format->BitsPerPixel) {
+    case 15:
+        switch (dst->format->Rmask) {
+        case 0x7C00:
+            func = SDL_BlendFillRect_RGB555;
+        }
+        break;
+    case 16:
+        switch (dst->format->Rmask) {
+        case 0xF800:
+            func = SDL_BlendFillRect_RGB565;
+        }
+        break;
+    case 32:
+        switch (dst->format->Rmask) {
+        case 0x00FF0000:
+            if (!dst->format->Amask) {
+                func = SDL_BlendFillRect_RGB888;
+            } else {
+                func = SDL_BlendFillRect_ARGB8888;
+            }
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (!func) {
+        if (!dst->format->Amask) {
+            func = SDL_BlendFillRect_RGB;
+        } else {
+            func = SDL_BlendFillRect_RGBA;
+        }
+    }
+
+    for (i = 0; i < count; ++i) {
+        /* Perform clipping */
+        if (!SDL_IntersectRect(&rects[i], &dst->clip_rect, &rect)) {
+            continue;
+        }
+        status = func(dst, &rect, blendMode, r, g, b, a);
+    }
+    return status;
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 33 - 0
src/render/ps2/SDL_blendfillrect.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_blendfillrect_h_
+#define SDL_blendfillrect_h_
+
+#include "../../SDL_internal.h"
+
+
+extern int SDL_BlendFillRect(SDL_Surface * dst, const SDL_Rect * rect, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+extern int SDL_BlendFillRects(SDL_Surface * dst, const SDL_Rect * rects, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+
+#endif /* SDL_blendfillrect_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 875 - 0
src/render/ps2/SDL_blendline.c

@@ -0,0 +1,875 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "SDL_draw.h"
+#include "SDL_blendline.h"
+#include "SDL_blendpoint.h"
+
+
+static void
+SDL_BlendLine_RGB2(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                   SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                   SDL_bool draw_end)
+{
+    const SDL_PixelFormat *fmt = dst->format;
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint16, DRAW_SETPIXEL_ADD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint16, DRAW_SETPIXEL_MOD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint16, DRAW_SETPIXEL_MUL_RGB, draw_end);
+            break;
+        default:
+            HLINE(Uint16, DRAW_SETPIXEL_RGB, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint16, DRAW_SETPIXEL_ADD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint16, DRAW_SETPIXEL_MOD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint16, DRAW_SETPIXEL_MUL_RGB, draw_end);
+            break;
+        default:
+            VLINE(Uint16, DRAW_SETPIXEL_RGB, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint16, DRAW_SETPIXEL_ADD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint16, DRAW_SETPIXEL_MOD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint16, DRAW_SETPIXEL_MUL_RGB, draw_end);
+            break;
+        default:
+            DLINE(Uint16, DRAW_SETPIXEL_RGB, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY2_BLEND_RGB, DRAW_SETPIXELXY2_BLEND_RGB,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY2_ADD_RGB, DRAW_SETPIXELXY2_ADD_RGB,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY2_MOD_RGB, DRAW_SETPIXELXY2_MOD_RGB,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY2_MUL_RGB, DRAW_SETPIXELXY2_MUL_RGB,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY2_RGB, DRAW_SETPIXELXY2_BLEND_RGB,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+static void
+SDL_BlendLine_RGB555(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                     SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                     SDL_bool draw_end)
+{
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint16, DRAW_SETPIXEL_ADD_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint16, DRAW_SETPIXEL_MOD_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint16, DRAW_SETPIXEL_MUL_RGB555, draw_end);
+            break;
+        default:
+            HLINE(Uint16, DRAW_SETPIXEL_RGB555, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint16, DRAW_SETPIXEL_ADD_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint16, DRAW_SETPIXEL_MOD_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint16, DRAW_SETPIXEL_MUL_RGB555, draw_end);
+            break;
+        default:
+            VLINE(Uint16, DRAW_SETPIXEL_RGB555, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint16, DRAW_SETPIXEL_ADD_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint16, DRAW_SETPIXEL_MOD_RGB555, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint16, DRAW_SETPIXEL_MUL_RGB555, draw_end);
+            break;
+        default:
+            DLINE(Uint16, DRAW_SETPIXEL_RGB555, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_BLEND_RGB555, DRAW_SETPIXELXY_BLEND_RGB555,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_ADD_RGB555, DRAW_SETPIXELXY_ADD_RGB555,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MOD_RGB555, DRAW_SETPIXELXY_MOD_RGB555,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MUL_RGB555, DRAW_SETPIXELXY_MUL_RGB555,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_RGB555, DRAW_SETPIXELXY_BLEND_RGB555,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+static void
+SDL_BlendLine_RGB565(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                     SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                     SDL_bool draw_end)
+{
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint16, DRAW_SETPIXEL_ADD_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint16, DRAW_SETPIXEL_MOD_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint16, DRAW_SETPIXEL_MUL_RGB565, draw_end);
+            break;
+        default:
+            HLINE(Uint16, DRAW_SETPIXEL_RGB565, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint16, DRAW_SETPIXEL_ADD_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint16, DRAW_SETPIXEL_MOD_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint16, DRAW_SETPIXEL_MUL_RGB565, draw_end);
+            break;
+        default:
+            VLINE(Uint16, DRAW_SETPIXEL_RGB565, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint16, DRAW_SETPIXEL_BLEND_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint16, DRAW_SETPIXEL_ADD_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint16, DRAW_SETPIXEL_MOD_RGB565, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint16, DRAW_SETPIXEL_MUL_RGB565, draw_end);
+            break;
+        default:
+            DLINE(Uint16, DRAW_SETPIXEL_RGB565, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_BLEND_RGB565, DRAW_SETPIXELXY_BLEND_RGB565,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_ADD_RGB565, DRAW_SETPIXELXY_ADD_RGB565,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MOD_RGB565, DRAW_SETPIXELXY_MOD_RGB565,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MUL_RGB565, DRAW_SETPIXELXY_MUL_RGB565,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_RGB565, DRAW_SETPIXELXY_BLEND_RGB565,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+static void
+SDL_BlendLine_RGB4(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                   SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                   SDL_bool draw_end)
+{
+    const SDL_PixelFormat *fmt = dst->format;
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint32, DRAW_SETPIXEL_BLEND_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint32, DRAW_SETPIXEL_ADD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint32, DRAW_SETPIXEL_MOD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint32, DRAW_SETPIXEL_MUL_RGB, draw_end);
+            break;
+        default:
+            HLINE(Uint32, DRAW_SETPIXEL_RGB, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint32, DRAW_SETPIXEL_BLEND_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint32, DRAW_SETPIXEL_ADD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint32, DRAW_SETPIXEL_MOD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint32, DRAW_SETPIXEL_MUL_RGB, draw_end);
+            break;
+        default:
+            VLINE(Uint32, DRAW_SETPIXEL_RGB, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint32, DRAW_SETPIXEL_BLEND_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint32, DRAW_SETPIXEL_ADD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint32, DRAW_SETPIXEL_MOD_RGB, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint32, DRAW_SETPIXEL_MUL_RGB, draw_end);
+            break;
+        default:
+            DLINE(Uint32, DRAW_SETPIXEL_RGB, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_BLEND_RGB, DRAW_SETPIXELXY4_BLEND_RGB,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_ADD_RGB, DRAW_SETPIXELXY4_ADD_RGB,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_MOD_RGB, DRAW_SETPIXELXY4_MOD_RGB,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_MUL_RGB, DRAW_SETPIXELXY4_MUL_RGB,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_RGB, DRAW_SETPIXELXY4_BLEND_RGB,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+static void
+SDL_BlendLine_RGBA4(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                    SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                    SDL_bool draw_end)
+{
+    const SDL_PixelFormat *fmt = dst->format;
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint32, DRAW_SETPIXEL_BLEND_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint32, DRAW_SETPIXEL_ADD_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint32, DRAW_SETPIXEL_MOD_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint32, DRAW_SETPIXEL_MUL_RGBA, draw_end);
+            break;
+        default:
+            HLINE(Uint32, DRAW_SETPIXEL_RGBA, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint32, DRAW_SETPIXEL_BLEND_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint32, DRAW_SETPIXEL_ADD_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint32, DRAW_SETPIXEL_MOD_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint32, DRAW_SETPIXEL_MUL_RGBA, draw_end);
+            break;
+        default:
+            VLINE(Uint32, DRAW_SETPIXEL_RGBA, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint32, DRAW_SETPIXEL_BLEND_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint32, DRAW_SETPIXEL_ADD_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint32, DRAW_SETPIXEL_MOD_RGBA, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint32, DRAW_SETPIXEL_MUL_RGBA, draw_end);
+            break;
+        default:
+            DLINE(Uint32, DRAW_SETPIXEL_RGBA, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_BLEND_RGBA, DRAW_SETPIXELXY4_BLEND_RGBA,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_ADD_RGBA, DRAW_SETPIXELXY4_ADD_RGBA,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_MOD_RGBA, DRAW_SETPIXELXY4_MOD_RGBA,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_MUL_RGBA, DRAW_SETPIXELXY4_MUL_RGBA,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY4_RGBA, DRAW_SETPIXELXY4_BLEND_RGBA,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+static void
+SDL_BlendLine_RGB888(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                     SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                     SDL_bool draw_end)
+{
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint32, DRAW_SETPIXEL_BLEND_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint32, DRAW_SETPIXEL_ADD_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint32, DRAW_SETPIXEL_MOD_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint32, DRAW_SETPIXEL_MUL_RGB888, draw_end);
+            break;
+        default:
+            HLINE(Uint32, DRAW_SETPIXEL_RGB888, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint32, DRAW_SETPIXEL_BLEND_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint32, DRAW_SETPIXEL_ADD_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint32, DRAW_SETPIXEL_MOD_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint32, DRAW_SETPIXEL_MUL_RGB888, draw_end);
+            break;
+        default:
+            VLINE(Uint32, DRAW_SETPIXEL_RGB888, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint32, DRAW_SETPIXEL_BLEND_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint32, DRAW_SETPIXEL_ADD_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint32, DRAW_SETPIXEL_MOD_RGB888, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint32, DRAW_SETPIXEL_MUL_RGB888, draw_end);
+            break;
+        default:
+            DLINE(Uint32, DRAW_SETPIXEL_RGB888, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_BLEND_RGB888, DRAW_SETPIXELXY_BLEND_RGB888,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_ADD_RGB888, DRAW_SETPIXELXY_ADD_RGB888,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MOD_RGB888, DRAW_SETPIXELXY_MOD_RGB888,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MUL_RGB888, DRAW_SETPIXELXY_MUL_RGB888,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_RGB888, DRAW_SETPIXELXY_BLEND_RGB888,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+static void
+SDL_BlendLine_ARGB8888(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+                       SDL_BlendMode blendMode, Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a,
+                       SDL_bool draw_end)
+{
+    unsigned r, g, b, a, inva;
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(_r, _a);
+        g = DRAW_MUL(_g, _a);
+        b = DRAW_MUL(_b, _a);
+        a = _a;
+    } else {
+        r = _r;
+        g = _g;
+        b = _b;
+        a = _a;
+    }
+    inva = (a ^ 0xff);
+
+    if (y1 == y2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            HLINE(Uint32, DRAW_SETPIXEL_BLEND_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            HLINE(Uint32, DRAW_SETPIXEL_ADD_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            HLINE(Uint32, DRAW_SETPIXEL_MOD_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            HLINE(Uint32, DRAW_SETPIXEL_MUL_ARGB8888, draw_end);
+            break;
+        default:
+            HLINE(Uint32, DRAW_SETPIXEL_ARGB8888, draw_end);
+            break;
+        }
+    } else if (x1 == x2) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            VLINE(Uint32, DRAW_SETPIXEL_BLEND_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            VLINE(Uint32, DRAW_SETPIXEL_ADD_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            VLINE(Uint32, DRAW_SETPIXEL_MOD_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            VLINE(Uint32, DRAW_SETPIXEL_MUL_ARGB8888, draw_end);
+            break;
+        default:
+            VLINE(Uint32, DRAW_SETPIXEL_ARGB8888, draw_end);
+            break;
+        }
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DLINE(Uint32, DRAW_SETPIXEL_BLEND_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DLINE(Uint32, DRAW_SETPIXEL_ADD_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DLINE(Uint32, DRAW_SETPIXEL_MOD_ARGB8888, draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DLINE(Uint32, DRAW_SETPIXEL_MUL_ARGB8888, draw_end);
+            break;
+        default:
+            DLINE(Uint32, DRAW_SETPIXEL_ARGB8888, draw_end);
+            break;
+        }
+    } else {
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_BLEND_ARGB8888, DRAW_SETPIXELXY_BLEND_ARGB8888,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_ADD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_ADD_ARGB8888, DRAW_SETPIXELXY_ADD_ARGB8888,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MOD:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MOD_ARGB8888, DRAW_SETPIXELXY_MOD_ARGB8888,
+                   draw_end);
+            break;
+        case SDL_BLENDMODE_MUL:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_MUL_ARGB8888, DRAW_SETPIXELXY_MUL_ARGB8888,
+                   draw_end);
+            break;
+        default:
+            AALINE(x1, y1, x2, y2,
+                   DRAW_SETPIXELXY_ARGB8888, DRAW_SETPIXELXY_BLEND_ARGB8888,
+                   draw_end);
+            break;
+        }
+    }
+}
+
+typedef void (*BlendLineFunc) (SDL_Surface * dst,
+                               int x1, int y1, int x2, int y2,
+                               SDL_BlendMode blendMode,
+                               Uint8 r, Uint8 g, Uint8 b, Uint8 a,
+                               SDL_bool draw_end);
+
+static BlendLineFunc
+SDL_CalculateBlendLineFunc(const SDL_PixelFormat * fmt)
+{
+    switch (fmt->BytesPerPixel) {
+    case 2:
+        if (fmt->Rmask == 0x7C00) {
+            return SDL_BlendLine_RGB555;
+        } else if (fmt->Rmask == 0xF800) {
+            return SDL_BlendLine_RGB565;
+        } else {
+            return SDL_BlendLine_RGB2;
+        }
+        /* break; -Wunreachable-code-break */
+    case 4:
+        if (fmt->Rmask == 0x00FF0000) {
+            if (fmt->Amask) {
+                return SDL_BlendLine_ARGB8888;
+            } else {
+                return SDL_BlendLine_RGB888;
+            }
+        } else {
+            if (fmt->Amask) {
+                return SDL_BlendLine_RGBA4;
+            } else {
+                return SDL_BlendLine_RGB4;
+            }
+        }
+    }
+    return NULL;
+}
+
+int
+SDL_BlendLine(SDL_Surface * dst, int x1, int y1, int x2, int y2,
+              SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    BlendLineFunc func;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_BlendLine(): dst");
+    }
+
+    func = SDL_CalculateBlendLineFunc(dst->format);
+    if (!func) {
+        return SDL_SetError("SDL_BlendLine(): Unsupported surface format");
+    }
+
+    /* Perform clipping */
+    /* FIXME: We don't actually want to clip, as it may change line slope */
+    if (!SDL_IntersectRectAndLine(&dst->clip_rect, &x1, &y1, &x2, &y2)) {
+        return 0;
+    }
+
+    func(dst, x1, y1, x2, y2, blendMode, r, g, b, a, SDL_TRUE);
+    return 0;
+}
+
+int
+SDL_BlendLines(SDL_Surface * dst, const SDL_Point * points, int count,
+               SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    int i;
+    int x1, y1;
+    int x2, y2;
+    SDL_bool draw_end;
+    BlendLineFunc func;
+
+    if (!dst) {
+        return SDL_SetError("SDL_BlendLines(): Passed NULL destination surface");
+    }
+
+    func = SDL_CalculateBlendLineFunc(dst->format);
+    if (!func) {
+        return SDL_SetError("SDL_BlendLines(): Unsupported surface format");
+    }
+
+    for (i = 1; i < count; ++i) {
+        x1 = points[i-1].x;
+        y1 = points[i-1].y;
+        x2 = points[i].x;
+        y2 = points[i].y;
+
+        /* Perform clipping */
+        /* FIXME: We don't actually want to clip, as it may change line slope */
+        if (!SDL_IntersectRectAndLine(&dst->clip_rect, &x1, &y1, &x2, &y2)) {
+            continue;
+        }
+
+        /* Draw the end if it was clipped */
+        draw_end = (x2 != points[i].x || y2 != points[i].y);
+
+        func(dst, x1, y1, x2, y2, blendMode, r, g, b, a, draw_end);
+    }
+    if (points[0].x != points[count-1].x || points[0].y != points[count-1].y) {
+        SDL_BlendPoint(dst, points[count-1].x, points[count-1].y,
+                       blendMode, r, g, b, a);
+    }
+    return 0;
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 33 - 0
src/render/ps2/SDL_blendline.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_blendline_h_
+#define SDL_blendline_h_
+
+#include "../../SDL_internal.h"
+
+
+extern int SDL_BlendLine(SDL_Surface * dst, int x1, int y1, int x2, int y2, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+extern int SDL_BlendLines(SDL_Surface * dst, const SDL_Point * points, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+
+#endif /* SDL_blendline_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 362 - 0
src/render/ps2/SDL_blendpoint.c

@@ -0,0 +1,362 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "SDL_draw.h"
+#include "SDL_blendpoint.h"
+
+
+static int
+SDL_BlendPoint_RGB555(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r,
+                      Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        DRAW_SETPIXELXY_BLEND_RGB555(x, y);
+        break;
+    case SDL_BLENDMODE_ADD:
+        DRAW_SETPIXELXY_ADD_RGB555(x, y);
+        break;
+    case SDL_BLENDMODE_MOD:
+        DRAW_SETPIXELXY_MOD_RGB555(x, y);
+        break;
+    case SDL_BLENDMODE_MUL:
+        DRAW_SETPIXELXY_MUL_RGB555(x, y);
+        break;
+    default:
+        DRAW_SETPIXELXY_RGB555(x, y);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendPoint_RGB565(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r,
+                      Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        DRAW_SETPIXELXY_BLEND_RGB565(x, y);
+        break;
+    case SDL_BLENDMODE_ADD:
+        DRAW_SETPIXELXY_ADD_RGB565(x, y);
+        break;
+    case SDL_BLENDMODE_MOD:
+        DRAW_SETPIXELXY_MOD_RGB565(x, y);
+        break;
+    case SDL_BLENDMODE_MUL:
+        DRAW_SETPIXELXY_MUL_RGB565(x, y);
+        break;
+    default:
+        DRAW_SETPIXELXY_RGB565(x, y);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendPoint_RGB888(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r,
+                      Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        DRAW_SETPIXELXY_BLEND_RGB888(x, y);
+        break;
+    case SDL_BLENDMODE_ADD:
+        DRAW_SETPIXELXY_ADD_RGB888(x, y);
+        break;
+    case SDL_BLENDMODE_MOD:
+        DRAW_SETPIXELXY_MOD_RGB888(x, y);
+        break;
+    case SDL_BLENDMODE_MUL:
+        DRAW_SETPIXELXY_MUL_RGB888(x, y);
+        break;
+    default:
+        DRAW_SETPIXELXY_RGB888(x, y);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendPoint_ARGB8888(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode,
+                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    unsigned inva = 0xff - a;
+
+    switch (blendMode) {
+    case SDL_BLENDMODE_BLEND:
+        DRAW_SETPIXELXY_BLEND_ARGB8888(x, y);
+        break;
+    case SDL_BLENDMODE_ADD:
+        DRAW_SETPIXELXY_ADD_ARGB8888(x, y);
+        break;
+    case SDL_BLENDMODE_MOD:
+        DRAW_SETPIXELXY_MOD_ARGB8888(x, y);
+        break;
+    case SDL_BLENDMODE_MUL:
+        DRAW_SETPIXELXY_MUL_ARGB8888(x, y);
+        break;
+    default:
+        DRAW_SETPIXELXY_ARGB8888(x, y);
+        break;
+    }
+    return 0;
+}
+
+static int
+SDL_BlendPoint_RGB(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r,
+                   Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_PixelFormat *fmt = dst->format;
+    unsigned inva = 0xff - a;
+
+    switch (fmt->BytesPerPixel) {
+    case 2:
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DRAW_SETPIXELXY2_BLEND_RGB(x, y);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DRAW_SETPIXELXY2_ADD_RGB(x, y);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DRAW_SETPIXELXY2_MOD_RGB(x, y);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DRAW_SETPIXELXY2_MUL_RGB(x, y);
+            break;
+        default:
+            DRAW_SETPIXELXY2_RGB(x, y);
+            break;
+        }
+        return 0;
+    case 4:
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DRAW_SETPIXELXY4_BLEND_RGB(x, y);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DRAW_SETPIXELXY4_ADD_RGB(x, y);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DRAW_SETPIXELXY4_MOD_RGB(x, y);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DRAW_SETPIXELXY4_MUL_RGB(x, y);
+            break;
+        default:
+            DRAW_SETPIXELXY4_RGB(x, y);
+            break;
+        }
+        return 0;
+    default:
+        return SDL_Unsupported();
+    }
+}
+
+static int
+SDL_BlendPoint_RGBA(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r,
+                    Uint8 g, Uint8 b, Uint8 a)
+{
+    SDL_PixelFormat *fmt = dst->format;
+    unsigned inva = 0xff - a;
+
+    switch (fmt->BytesPerPixel) {
+    case 4:
+        switch (blendMode) {
+        case SDL_BLENDMODE_BLEND:
+            DRAW_SETPIXELXY4_BLEND_RGBA(x, y);
+            break;
+        case SDL_BLENDMODE_ADD:
+            DRAW_SETPIXELXY4_ADD_RGBA(x, y);
+            break;
+        case SDL_BLENDMODE_MOD:
+            DRAW_SETPIXELXY4_MOD_RGBA(x, y);
+            break;
+        case SDL_BLENDMODE_MUL:
+            DRAW_SETPIXELXY4_MUL_RGBA(x, y);
+            break;
+        default:
+            DRAW_SETPIXELXY4_RGBA(x, y);
+            break;
+        }
+        return 0;
+    default:
+        return SDL_Unsupported();
+    }
+}
+
+int
+SDL_BlendPoint(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r,
+               Uint8 g, Uint8 b, Uint8 a)
+{
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_BlendPoint(): dst");
+    }
+
+    /* This function doesn't work on surfaces < 8 bpp */
+    if (dst->format->BitsPerPixel < 8) {
+        return SDL_SetError("SDL_BlendPoint(): Unsupported surface format");
+    }
+
+    /* Perform clipping */
+    if (x < dst->clip_rect.x || y < dst->clip_rect.y ||
+        x >= (dst->clip_rect.x + dst->clip_rect.w) ||
+        y >= (dst->clip_rect.y + dst->clip_rect.h)) {
+        return 0;
+    }
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(r, a);
+        g = DRAW_MUL(g, a);
+        b = DRAW_MUL(b, a);
+    }
+
+    switch (dst->format->BitsPerPixel) {
+    case 15:
+        switch (dst->format->Rmask) {
+        case 0x7C00:
+            return SDL_BlendPoint_RGB555(dst, x, y, blendMode, r, g, b, a);
+        }
+        break;
+    case 16:
+        switch (dst->format->Rmask) {
+        case 0xF800:
+            return SDL_BlendPoint_RGB565(dst, x, y, blendMode, r, g, b, a);
+        }
+        break;
+    case 32:
+        switch (dst->format->Rmask) {
+        case 0x00FF0000:
+            if (!dst->format->Amask) {
+                return SDL_BlendPoint_RGB888(dst, x, y, blendMode, r, g, b, a);
+            } else {
+                return SDL_BlendPoint_ARGB8888(dst, x, y, blendMode, r, g, b, a);
+            }
+            /* break; -Wunreachable-code-break */
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (!dst->format->Amask) {
+        return SDL_BlendPoint_RGB(dst, x, y, blendMode, r, g, b, a);
+    } else {
+        return SDL_BlendPoint_RGBA(dst, x, y, blendMode, r, g, b, a);
+    }
+}
+
+int
+SDL_BlendPoints(SDL_Surface * dst, const SDL_Point * points, int count,
+                SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    int minx, miny;
+    int maxx, maxy;
+    int i;
+    int x, y;
+    int (*func)(SDL_Surface * dst, int x, int y,
+                SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a) = NULL;
+    int status = 0;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_BlendPoints(): dst");
+    }
+
+    /* This function doesn't work on surfaces < 8 bpp */
+    if (dst->format->BitsPerPixel < 8) {
+        return SDL_SetError("SDL_BlendPoints(): Unsupported surface format");
+    }
+
+    if (blendMode == SDL_BLENDMODE_BLEND || blendMode == SDL_BLENDMODE_ADD) {
+        r = DRAW_MUL(r, a);
+        g = DRAW_MUL(g, a);
+        b = DRAW_MUL(b, a);
+    }
+
+    /* FIXME: Does this function pointer slow things down significantly? */
+    switch (dst->format->BitsPerPixel) {
+    case 15:
+        switch (dst->format->Rmask) {
+        case 0x7C00:
+            func = SDL_BlendPoint_RGB555;
+            break;
+        }
+        break;
+    case 16:
+        switch (dst->format->Rmask) {
+        case 0xF800:
+            func = SDL_BlendPoint_RGB565;
+            break;
+        }
+        break;
+    case 32:
+        switch (dst->format->Rmask) {
+        case 0x00FF0000:
+            if (!dst->format->Amask) {
+                func = SDL_BlendPoint_RGB888;
+            } else {
+                func = SDL_BlendPoint_ARGB8888;
+            }
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (!func) {
+        if (!dst->format->Amask) {
+            func = SDL_BlendPoint_RGB;
+        } else {
+            func = SDL_BlendPoint_RGBA;
+        }
+    }
+
+    minx = dst->clip_rect.x;
+    maxx = dst->clip_rect.x + dst->clip_rect.w - 1;
+    miny = dst->clip_rect.y;
+    maxy = dst->clip_rect.y + dst->clip_rect.h - 1;
+
+    for (i = 0; i < count; ++i) {
+        x = points[i].x;
+        y = points[i].y;
+
+        if (x < minx || x > maxx || y < miny || y > maxy) {
+            continue;
+        }
+        status = func(dst, x, y, blendMode, r, g, b, a);
+    }
+    return status;
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 33 - 0
src/render/ps2/SDL_blendpoint.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_blendpoint_h_
+#define SDL_blendpoint_h_
+
+#include "../../SDL_internal.h"
+
+
+extern int SDL_BlendPoint(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+extern int SDL_BlendPoints(SDL_Surface * dst, const SDL_Point * points, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+
+#endif /* SDL_blendpoint_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 632 - 0
src/render/ps2/SDL_draw.h

@@ -0,0 +1,632 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#include "../../video/SDL_blit.h"
+
+/* This code assumes that r, g, b, a are the source color,
+ * and in the blend and add case, the RGB values are premultiplied by a.
+ */
+
+#define DRAW_MUL(_a, _b) (((unsigned)(_a)*(_b))/255)
+
+#define DRAW_FASTSETPIXEL(type) \
+    *pixel = (type) color
+
+#define DRAW_FASTSETPIXEL1 DRAW_FASTSETPIXEL(Uint8)
+#define DRAW_FASTSETPIXEL2 DRAW_FASTSETPIXEL(Uint16)
+#define DRAW_FASTSETPIXEL4 DRAW_FASTSETPIXEL(Uint32)
+
+#define DRAW_FASTSETPIXELXY(x, y, type, bpp, color) \
+    *(type *)((Uint8 *)dst->pixels + (y) * dst->pitch \
+                                   + (x) * bpp) = (type) color
+
+#define DRAW_FASTSETPIXELXY1(x, y) DRAW_FASTSETPIXELXY(x, y, Uint8, 1, color)
+#define DRAW_FASTSETPIXELXY2(x, y) DRAW_FASTSETPIXELXY(x, y, Uint16, 2, color)
+#define DRAW_FASTSETPIXELXY4(x, y) DRAW_FASTSETPIXELXY(x, y, Uint32, 4, color)
+
+#define DRAW_SETPIXEL(setpixel) \
+do { \
+    unsigned sr = r, sg = g, sb = b, sa = a; (void) sa; \
+    setpixel; \
+} while (0)
+
+#define DRAW_SETPIXEL_BLEND(getpixel, setpixel) \
+do { \
+    unsigned sr, sg, sb, sa = 0xFF; \
+    getpixel; \
+    sr = DRAW_MUL(inva, sr) + r; \
+    sg = DRAW_MUL(inva, sg) + g; \
+    sb = DRAW_MUL(inva, sb) + b; \
+    sa = DRAW_MUL(inva, sa) + a; \
+    setpixel; \
+} while (0)
+
+#define DRAW_SETPIXEL_ADD(getpixel, setpixel) \
+do { \
+    unsigned sr, sg, sb, sa; (void) sa; \
+    getpixel; \
+    sr += r; if (sr > 0xff) sr = 0xff; \
+    sg += g; if (sg > 0xff) sg = 0xff; \
+    sb += b; if (sb > 0xff) sb = 0xff; \
+    setpixel; \
+} while (0)
+
+#define DRAW_SETPIXEL_MOD(getpixel, setpixel) \
+do { \
+    unsigned sr, sg, sb, sa; (void) sa; \
+    getpixel; \
+    sr = DRAW_MUL(sr, r); \
+    sg = DRAW_MUL(sg, g); \
+    sb = DRAW_MUL(sb, b); \
+    setpixel; \
+} while (0)
+
+#define DRAW_SETPIXEL_MUL(getpixel, setpixel) \
+do { \
+    unsigned sr, sg, sb, sa; sa = 0xFF; \
+    getpixel; \
+    sr = DRAW_MUL(sr, r) + DRAW_MUL(inva, sr); if (sr > 0xff) sr = 0xff; \
+    sg = DRAW_MUL(sg, g) + DRAW_MUL(inva, sg); if (sg > 0xff) sg = 0xff; \
+    sb = DRAW_MUL(sb, b) + DRAW_MUL(inva, sb); if (sb > 0xff) sb = 0xff; \
+    sa = DRAW_MUL(sa, a) + DRAW_MUL(inva, sa); if (sa > 0xff) sa = 0xff; \
+    setpixel; \
+} while (0)
+
+#define DRAW_SETPIXELXY(x, y, type, bpp, op) \
+do { \
+    type *pixel = (type *)((Uint8 *)dst->pixels + (y) * dst->pitch \
+                                                + (x) * bpp); \
+    op; \
+} while (0)
+
+/*
+ * Define draw operators for RGB555
+ */
+
+#define DRAW_SETPIXEL_RGB555 \
+    DRAW_SETPIXEL(RGB555_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_BLEND_RGB555 \
+    DRAW_SETPIXEL_BLEND(RGB_FROM_RGB555(*pixel, sr, sg, sb), \
+                        RGB555_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_ADD_RGB555 \
+    DRAW_SETPIXEL_ADD(RGB_FROM_RGB555(*pixel, sr, sg, sb), \
+                      RGB555_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MOD_RGB555 \
+    DRAW_SETPIXEL_MOD(RGB_FROM_RGB555(*pixel, sr, sg, sb), \
+                      RGB555_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MUL_RGB555 \
+    DRAW_SETPIXEL_MUL(RGB_FROM_RGB555(*pixel, sr, sg, sb), \
+                      RGB555_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXELXY_RGB555(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_RGB555)
+
+#define DRAW_SETPIXELXY_BLEND_RGB555(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_BLEND_RGB555)
+
+#define DRAW_SETPIXELXY_ADD_RGB555(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_ADD_RGB555)
+
+#define DRAW_SETPIXELXY_MOD_RGB555(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_MOD_RGB555)
+
+#define DRAW_SETPIXELXY_MUL_RGB555(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_MUL_RGB555)
+
+/*
+ * Define draw operators for RGB565
+ */
+
+#define DRAW_SETPIXEL_RGB565 \
+    DRAW_SETPIXEL(RGB565_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_BLEND_RGB565 \
+    DRAW_SETPIXEL_BLEND(RGB_FROM_RGB565(*pixel, sr, sg, sb), \
+                        RGB565_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_ADD_RGB565 \
+    DRAW_SETPIXEL_ADD(RGB_FROM_RGB565(*pixel, sr, sg, sb), \
+                      RGB565_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MOD_RGB565 \
+    DRAW_SETPIXEL_MOD(RGB_FROM_RGB565(*pixel, sr, sg, sb), \
+                      RGB565_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MUL_RGB565 \
+    DRAW_SETPIXEL_MUL(RGB_FROM_RGB565(*pixel, sr, sg, sb), \
+                      RGB565_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXELXY_RGB565(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_RGB565)
+
+#define DRAW_SETPIXELXY_BLEND_RGB565(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_BLEND_RGB565)
+
+#define DRAW_SETPIXELXY_ADD_RGB565(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_ADD_RGB565)
+
+#define DRAW_SETPIXELXY_MOD_RGB565(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_MOD_RGB565)
+
+#define DRAW_SETPIXELXY_MUL_RGB565(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_MUL_RGB565)
+
+/*
+ * Define draw operators for RGB888
+ */
+
+#define DRAW_SETPIXEL_RGB888 \
+    DRAW_SETPIXEL(RGB888_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_BLEND_RGB888 \
+    DRAW_SETPIXEL_BLEND(RGB_FROM_RGB888(*pixel, sr, sg, sb), \
+                        RGB888_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_ADD_RGB888 \
+    DRAW_SETPIXEL_ADD(RGB_FROM_RGB888(*pixel, sr, sg, sb), \
+                      RGB888_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MOD_RGB888 \
+    DRAW_SETPIXEL_MOD(RGB_FROM_RGB888(*pixel, sr, sg, sb), \
+                      RGB888_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MUL_RGB888 \
+    DRAW_SETPIXEL_MUL(RGB_FROM_RGB888(*pixel, sr, sg, sb), \
+                      RGB888_FROM_RGB(*pixel, sr, sg, sb))
+
+#define DRAW_SETPIXELXY_RGB888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_RGB888)
+
+#define DRAW_SETPIXELXY_BLEND_RGB888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_BLEND_RGB888)
+
+#define DRAW_SETPIXELXY_ADD_RGB888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_ADD_RGB888)
+
+#define DRAW_SETPIXELXY_MOD_RGB888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MOD_RGB888)
+
+#define DRAW_SETPIXELXY_MUL_RGB888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MUL_RGB888)
+
+/*
+ * Define draw operators for ARGB8888
+ */
+
+#define DRAW_SETPIXEL_ARGB8888 \
+    DRAW_SETPIXEL(ARGB8888_FROM_RGBA(*pixel, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_BLEND_ARGB8888 \
+    DRAW_SETPIXEL_BLEND(RGBA_FROM_ARGB8888(*pixel, sr, sg, sb, sa), \
+                        ARGB8888_FROM_RGBA(*pixel, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_ADD_ARGB8888 \
+    DRAW_SETPIXEL_ADD(RGBA_FROM_ARGB8888(*pixel, sr, sg, sb, sa), \
+                      ARGB8888_FROM_RGBA(*pixel, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_MOD_ARGB8888 \
+    DRAW_SETPIXEL_MOD(RGBA_FROM_ARGB8888(*pixel, sr, sg, sb, sa), \
+                      ARGB8888_FROM_RGBA(*pixel, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_MUL_ARGB8888 \
+    DRAW_SETPIXEL_MUL(RGBA_FROM_ARGB8888(*pixel, sr, sg, sb, sa), \
+                      ARGB8888_FROM_RGBA(*pixel, sr, sg, sb, sa))
+
+#define DRAW_SETPIXELXY_ARGB8888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_ARGB8888)
+
+#define DRAW_SETPIXELXY_BLEND_ARGB8888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_BLEND_ARGB8888)
+
+#define DRAW_SETPIXELXY_ADD_ARGB8888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_ADD_ARGB8888)
+
+#define DRAW_SETPIXELXY_MOD_ARGB8888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MOD_ARGB8888)
+
+#define DRAW_SETPIXELXY_MUL_ARGB8888(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MUL_ARGB8888)
+
+/*
+ * Define draw operators for general RGB
+ */
+
+#define DRAW_SETPIXEL_RGB \
+    DRAW_SETPIXEL(PIXEL_FROM_RGB(*pixel, fmt, sr, sg, sb))
+
+#define DRAW_SETPIXEL_BLEND_RGB \
+    DRAW_SETPIXEL_BLEND(RGB_FROM_PIXEL(*pixel, fmt, sr, sg, sb), \
+                        PIXEL_FROM_RGB(*pixel, fmt, sr, sg, sb))
+
+#define DRAW_SETPIXEL_ADD_RGB \
+    DRAW_SETPIXEL_ADD(RGB_FROM_PIXEL(*pixel, fmt, sr, sg, sb), \
+                      PIXEL_FROM_RGB(*pixel, fmt, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MOD_RGB \
+    DRAW_SETPIXEL_MOD(RGB_FROM_PIXEL(*pixel, fmt, sr, sg, sb), \
+                      PIXEL_FROM_RGB(*pixel, fmt, sr, sg, sb))
+
+#define DRAW_SETPIXEL_MUL_RGB \
+    DRAW_SETPIXEL_MUL(RGB_FROM_PIXEL(*pixel, fmt, sr, sg, sb), \
+                      PIXEL_FROM_RGB(*pixel, fmt, sr, sg, sb))
+
+#define DRAW_SETPIXELXY2_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_RGB)
+
+#define DRAW_SETPIXELXY4_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_RGB)
+
+#define DRAW_SETPIXELXY2_BLEND_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_BLEND_RGB)
+
+#define DRAW_SETPIXELXY4_BLEND_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_BLEND_RGB)
+
+#define DRAW_SETPIXELXY2_ADD_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_ADD_RGB)
+
+#define DRAW_SETPIXELXY4_ADD_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_ADD_RGB)
+
+#define DRAW_SETPIXELXY2_MOD_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_MOD_RGB)
+
+#define DRAW_SETPIXELXY4_MOD_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MOD_RGB)
+
+#define DRAW_SETPIXELXY2_MUL_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint16, 2, DRAW_SETPIXEL_MUL_RGB)
+
+#define DRAW_SETPIXELXY4_MUL_RGB(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MUL_RGB)
+
+
+/*
+ * Define draw operators for general RGBA
+ */
+
+#define DRAW_SETPIXEL_RGBA \
+    DRAW_SETPIXEL(PIXEL_FROM_RGBA(*pixel, fmt, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_BLEND_RGBA \
+    DRAW_SETPIXEL_BLEND(RGBA_FROM_PIXEL(*pixel, fmt, sr, sg, sb, sa), \
+                        PIXEL_FROM_RGBA(*pixel, fmt, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_ADD_RGBA \
+    DRAW_SETPIXEL_ADD(RGBA_FROM_PIXEL(*pixel, fmt, sr, sg, sb, sa), \
+                      PIXEL_FROM_RGBA(*pixel, fmt, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_MOD_RGBA \
+    DRAW_SETPIXEL_MOD(RGBA_FROM_PIXEL(*pixel, fmt, sr, sg, sb, sa), \
+                      PIXEL_FROM_RGBA(*pixel, fmt, sr, sg, sb, sa))
+
+#define DRAW_SETPIXEL_MUL_RGBA \
+    DRAW_SETPIXEL_MUL(RGBA_FROM_PIXEL(*pixel, fmt, sr, sg, sb, sa), \
+                      PIXEL_FROM_RGBA(*pixel, fmt, sr, sg, sb, sa))
+
+#define DRAW_SETPIXELXY4_RGBA(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_RGBA)
+
+#define DRAW_SETPIXELXY4_BLEND_RGBA(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_BLEND_RGBA)
+
+#define DRAW_SETPIXELXY4_ADD_RGBA(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_ADD_RGBA)
+
+#define DRAW_SETPIXELXY4_MOD_RGBA(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MOD_RGBA)
+
+#define DRAW_SETPIXELXY4_MUL_RGBA(x, y) \
+    DRAW_SETPIXELXY(x, y, Uint32, 4, DRAW_SETPIXEL_MUL_RGBA)
+
+/*
+ * Define line drawing macro
+ */
+
+#define ABS(_x) ((_x) < 0 ? -(_x) : (_x))
+
+/* Horizontal line */
+#define HLINE(type, op, draw_end) \
+{ \
+    int length; \
+    int pitch = (dst->pitch / dst->format->BytesPerPixel); \
+    type *pixel; \
+    if (x1 <= x2) { \
+        pixel = (type *)dst->pixels + y1 * pitch + x1; \
+        length = draw_end ? (x2-x1+1) : (x2-x1); \
+    } else { \
+        pixel = (type *)dst->pixels + y1 * pitch + x2; \
+        if (!draw_end) { \
+            ++pixel; \
+        } \
+        length = draw_end ? (x1-x2+1) : (x1-x2); \
+    } \
+    while (length--) { \
+        op; \
+        ++pixel; \
+    } \
+}
+
+/* Vertical line */
+#define VLINE(type, op, draw_end) \
+{ \
+    int length; \
+    int pitch = (dst->pitch / dst->format->BytesPerPixel); \
+    type *pixel; \
+    if (y1 <= y2) { \
+        pixel = (type *)dst->pixels + y1 * pitch + x1; \
+        length = draw_end ? (y2-y1+1) : (y2-y1); \
+    } else { \
+        pixel = (type *)dst->pixels + y2 * pitch + x1; \
+        if (!draw_end) { \
+            pixel += pitch; \
+        } \
+        length = draw_end ? (y1-y2+1) : (y1-y2); \
+    } \
+    while (length--) { \
+        op; \
+        pixel += pitch; \
+    } \
+}
+
+/* Diagonal line */
+#define DLINE(type, op, draw_end) \
+{ \
+    int length; \
+    int pitch = (dst->pitch / dst->format->BytesPerPixel); \
+    type *pixel; \
+    if (y1 <= y2) { \
+        pixel = (type *)dst->pixels + y1 * pitch + x1; \
+        if (x1 <= x2) { \
+            ++pitch; \
+        } else { \
+            --pitch; \
+        } \
+        length = (y2-y1); \
+    } else { \
+        pixel = (type *)dst->pixels + y2 * pitch + x2; \
+        if (x2 <= x1) { \
+            ++pitch; \
+        } else { \
+            --pitch; \
+        } \
+        if (!draw_end) { \
+            pixel += pitch; \
+        } \
+        length = (y1-y2); \
+    } \
+    if (draw_end) { \
+        ++length; \
+    } \
+    while (length--) { \
+        op; \
+        pixel += pitch; \
+    } \
+}
+
+/* Bresenham's line algorithm */
+#define BLINE(x1, y1, x2, y2, op, draw_end) \
+{ \
+    int i, deltax, deltay, numpixels; \
+    int d, dinc1, dinc2; \
+    int x, xinc1, xinc2; \
+    int y, yinc1, yinc2; \
+ \
+    deltax = ABS(x2 - x1); \
+    deltay = ABS(y2 - y1); \
+ \
+    if (deltax >= deltay) { \
+        numpixels = deltax + 1; \
+        d = (2 * deltay) - deltax; \
+        dinc1 = deltay * 2; \
+        dinc2 = (deltay - deltax) * 2; \
+        xinc1 = 1; \
+        xinc2 = 1; \
+        yinc1 = 0; \
+        yinc2 = 1; \
+    } else { \
+        numpixels = deltay + 1; \
+        d = (2 * deltax) - deltay; \
+        dinc1 = deltax * 2; \
+        dinc2 = (deltax - deltay) * 2; \
+        xinc1 = 0; \
+        xinc2 = 1; \
+        yinc1 = 1; \
+        yinc2 = 1; \
+    } \
+ \
+    if (x1 > x2) { \
+        xinc1 = -xinc1; \
+        xinc2 = -xinc2; \
+    } \
+    if (y1 > y2) { \
+        yinc1 = -yinc1; \
+        yinc2 = -yinc2; \
+    } \
+ \
+    x = x1; \
+    y = y1; \
+ \
+    if (!draw_end) { \
+        --numpixels; \
+    } \
+    for (i = 0; i < numpixels; ++i) { \
+        op(x, y); \
+        if (d < 0) { \
+            d += dinc1; \
+            x += xinc1; \
+            y += yinc1; \
+        } else { \
+            d += dinc2; \
+            x += xinc2; \
+            y += yinc2; \
+        } \
+    } \
+}
+
+/* Xiaolin Wu's line algorithm, based on Michael Abrash's implementation */
+#define WULINE(x1, y1, x2, y2, opaque_op, blend_op, draw_end) \
+{ \
+    Uint16 ErrorAdj, ErrorAcc; \
+    Uint16 ErrorAccTemp, Weighting; \
+    int DeltaX, DeltaY, Temp, XDir; \
+    unsigned r, g, b, a, inva; \
+ \
+    /* Draw the initial pixel, which is always exactly intersected by \
+       the line and so needs no weighting */ \
+    opaque_op(x1, y1); \
+ \
+    /* Draw the final pixel, which is always exactly intersected by the line \
+       and so needs no weighting */ \
+    if (draw_end) { \
+        opaque_op(x2, y2); \
+    } \
+ \
+    /* Make sure the line runs top to bottom */ \
+    if (y1 > y2) { \
+        Temp = y1; y1 = y2; y2 = Temp; \
+        Temp = x1; x1 = x2; x2 = Temp; \
+    } \
+    DeltaY = y2 - y1; \
+ \
+    if ((DeltaX = x2 - x1) >= 0) { \
+        XDir = 1; \
+    } else { \
+        XDir = -1; \
+        DeltaX = -DeltaX; /* make DeltaX positive */ \
+    } \
+ \
+    /* line is not horizontal, diagonal, or vertical */ \
+    ErrorAcc = 0;  /* initialize the line error accumulator to 0 */ \
+ \
+    /* Is this an X-major or Y-major line? */ \
+    if (DeltaY > DeltaX) { \
+        /* Y-major line; calculate 16-bit fixed-point fractional part of a \
+          pixel that X advances each time Y advances 1 pixel, truncating the \
+          result so that we won't overrun the endpoint along the X axis */ \
+        ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY; \
+        /* Draw all pixels other than the first and last */ \
+        while (--DeltaY) { \
+            ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */ \
+            ErrorAcc += ErrorAdj;      /* calculate error for next pixel */ \
+            if (ErrorAcc <= ErrorAccTemp) { \
+                /* The error accumulator turned over, so advance the X coord */ \
+                x1 += XDir; \
+            } \
+            y1++; /* Y-major, so always advance Y */ \
+            /* The IntensityBits most significant bits of ErrorAcc give us the \
+             intensity weighting for this pixel, and the complement of the \
+             weighting for the paired pixel */ \
+            Weighting = ErrorAcc >> 8; \
+            { \
+                a = DRAW_MUL(_a, (Weighting ^ 255)); \
+                r = DRAW_MUL(_r, a); \
+                g = DRAW_MUL(_g, a); \
+                b = DRAW_MUL(_b, a); \
+                inva = (a ^ 0xFF); \
+                blend_op(x1, y1); \
+            } \
+            { \
+                a = DRAW_MUL(_a, Weighting); \
+                r = DRAW_MUL(_r, a); \
+                g = DRAW_MUL(_g, a); \
+                b = DRAW_MUL(_b, a); \
+                inva = (a ^ 0xFF); \
+                blend_op(x1 + XDir, y1); \
+            } \
+        } \
+    } 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 to avoid overrunning the endpoint along the X axis */ \
+        ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX; \
+        /* Draw all pixels other than the first and last */ \
+        while (--DeltaX) { \
+            ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */ \
+            ErrorAcc += ErrorAdj;      /* calculate error for next pixel */ \
+            if (ErrorAcc <= ErrorAccTemp) { \
+                /* The error accumulator turned over, so advance the Y coord */ \
+                y1++; \
+            } \
+            x1 += XDir; /* X-major, so always advance X */ \
+            /* The IntensityBits most significant bits of ErrorAcc give us the \
+              intensity weighting for this pixel, and the complement of the \
+              weighting for the paired pixel */ \
+            Weighting = ErrorAcc >> 8; \
+            { \
+                a = DRAW_MUL(_a, (Weighting ^ 255)); \
+                r = DRAW_MUL(_r, a); \
+                g = DRAW_MUL(_g, a); \
+                b = DRAW_MUL(_b, a); \
+                inva = (a ^ 0xFF); \
+                blend_op(x1, y1); \
+            } \
+            { \
+                a = DRAW_MUL(_a, Weighting); \
+                r = DRAW_MUL(_r, a); \
+                g = DRAW_MUL(_g, a); \
+                b = DRAW_MUL(_b, a); \
+                inva = (a ^ 0xFF); \
+                blend_op(x1, y1 + 1); \
+            } \
+        } \
+    } \
+}
+
+#ifdef AA_LINES
+#define AALINE(x1, y1, x2, y2, opaque_op, blend_op, draw_end) \
+            WULINE(x1, y1, x2, y2, opaque_op, blend_op, draw_end)
+#else
+#define AALINE(x1, y1, x2, y2, opaque_op, blend_op, draw_end) \
+            BLINE(x1, y1, x2, y2, opaque_op, draw_end)
+#endif
+
+/*
+ * Define fill rect macro
+ */
+
+#define FILLRECT(type, op) \
+do { \
+    int width = rect->w; \
+    int height = rect->h; \
+    int pitch = (dst->pitch / dst->format->BytesPerPixel); \
+    int skip = pitch - width; \
+    type *pixel = (type *)dst->pixels + rect->y * pitch + rect->x; \
+    while (height--) { \
+        { int n = (width+3)/4; \
+            switch (width & 3) { \
+            case 0: do {   op; pixel++; SDL_FALLTHROUGH; \
+            case 3:        op; pixel++; SDL_FALLTHROUGH; \
+            case 2:        op; pixel++; SDL_FALLTHROUGH; \
+            case 1:        op; pixel++; \
+                    } while ( --n > 0 ); \
+            } \
+        } \
+        pixel += skip; \
+    } \
+} while (0)
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 213 - 0
src/render/ps2/SDL_drawline.c

@@ -0,0 +1,213 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "SDL_draw.h"
+#include "SDL_drawline.h"
+#include "SDL_drawpoint.h"
+
+#include <gsKit.h>
+#include <dmaKit.h>
+
+static void
+SDL_DrawLine1(SDL_Surface * dst, int x1, int y1, int x2, int y2, Uint32 color,
+              SDL_bool draw_end)
+{
+    if (y1 == y2) {
+        int length;
+        int pitch = (dst->pitch / dst->format->BytesPerPixel);
+        Uint8 *pixel;
+        if (x1 <= x2) {
+            pixel = (Uint8 *)dst->pixels + y1 * pitch + x1;
+            length = draw_end ? (x2-x1+1) : (x2-x1);
+        } else {
+            pixel = (Uint8 *)dst->pixels + y1 * pitch + x2;
+            if (!draw_end) {
+                ++pixel;
+            }
+            length = draw_end ? (x1-x2+1) : (x1-x2);
+        }
+        SDL_memset(pixel, color, length);
+    } else if (x1 == x2) {
+        VLINE(Uint8, DRAW_FASTSETPIXEL1, draw_end);
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        DLINE(Uint8, DRAW_FASTSETPIXEL1, draw_end);
+    } else {
+        BLINE(x1, y1, x2, y2, DRAW_FASTSETPIXELXY1, draw_end);
+    }
+}
+
+static void
+SDL_DrawLine2(SDL_Surface * dst, int x1, int y1, int x2, int y2, Uint32 color,
+              SDL_bool draw_end)
+{
+    if (y1 == y2) {
+        HLINE(Uint16, DRAW_FASTSETPIXEL2, draw_end);
+    } else if (x1 == x2) {
+        VLINE(Uint16, DRAW_FASTSETPIXEL2, draw_end);
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        DLINE(Uint16, DRAW_FASTSETPIXEL2, draw_end);
+    } else {
+        Uint8 _r, _g, _b, _a;
+        const SDL_PixelFormat * fmt = dst->format;
+        SDL_GetRGBA(color, fmt, &_r, &_g, &_b, &_a);
+        if (fmt->Rmask == 0x7C00) {
+            AALINE(x1, y1, x2, y2,
+                   DRAW_FASTSETPIXELXY2, DRAW_SETPIXELXY_BLEND_RGB555,
+                   draw_end);
+        } else if (fmt->Rmask == 0xF800) {
+            AALINE(x1, y1, x2, y2,
+                   DRAW_FASTSETPIXELXY2, DRAW_SETPIXELXY_BLEND_RGB565,
+                   draw_end);
+        } else {
+            AALINE(x1, y1, x2, y2,
+                   DRAW_FASTSETPIXELXY2, DRAW_SETPIXELXY2_BLEND_RGB,
+                   draw_end);
+        }
+    }
+}
+
+static void
+SDL_DrawLine4(SDL_Surface * dst, int x1, int y1, int x2, int y2, Uint32 color,
+              SDL_bool draw_end)
+{
+    if (y1 == y2) {
+        HLINE(Uint32, DRAW_FASTSETPIXEL4, draw_end);
+    } else if (x1 == x2) {
+        VLINE(Uint32, DRAW_FASTSETPIXEL4, draw_end);
+    } else if (ABS(x1 - x2) == ABS(y1 - y2)) {
+        DLINE(Uint32, DRAW_FASTSETPIXEL4, draw_end);
+    } else {
+        Uint8 _r, _g, _b, _a;
+        const SDL_PixelFormat * fmt = dst->format;
+        SDL_GetRGBA(color, fmt, &_r, &_g, &_b, &_a);
+        if (fmt->Rmask == 0x00FF0000) {
+            if (!fmt->Amask) {
+                AALINE(x1, y1, x2, y2,
+                       DRAW_FASTSETPIXELXY4, DRAW_SETPIXELXY_BLEND_RGB888,
+                       draw_end);
+            } else {
+                AALINE(x1, y1, x2, y2,
+                       DRAW_FASTSETPIXELXY4, DRAW_SETPIXELXY_BLEND_ARGB8888,
+                       draw_end);
+            }
+        } else {
+            AALINE(x1, y1, x2, y2,
+                   DRAW_FASTSETPIXELXY4, DRAW_SETPIXELXY4_BLEND_RGB,
+                   draw_end);
+        }
+    }
+}
+
+typedef void (*DrawLineFunc) (SDL_Surface * dst,
+                              int x1, int y1, int x2, int y2,
+                              Uint32 color, SDL_bool draw_end);
+
+static DrawLineFunc
+SDL_CalculateDrawLineFunc(const SDL_PixelFormat * fmt)
+{
+    switch (fmt->BytesPerPixel) {
+    case 1:
+        if (fmt->BitsPerPixel < 8) {
+            break;
+        }
+        return SDL_DrawLine1;
+    case 2:
+        return SDL_DrawLine2;
+    case 4:
+        return SDL_DrawLine4;
+    }
+
+    return NULL;
+}
+
+int
+SDL_DrawLine(SDL_Surface * dst, int x1, int y1, int x2, int y2, Uint32 color)
+{
+    DrawLineFunc func;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_DrawLine(): dst");
+    }
+
+    func = SDL_CalculateDrawLineFunc(dst->format);
+    if (!func) {
+        return SDL_SetError("SDL_DrawLine(): Unsupported surface format");
+    }
+
+    /* Perform clipping */
+    /* FIXME: We don't actually want to clip, as it may change line slope */
+    if (!SDL_IntersectRectAndLine(&dst->clip_rect, &x1, &y1, &x2, &y2)) {
+        return 0;
+    }
+
+    func(dst, x1, y1, x2, y2, color, SDL_TRUE);
+    return 0;
+}
+
+int
+SDL_DrawLines(SDL_Surface * dst, const SDL_Point * points, int count,
+              Uint32 color)
+{
+    int i;
+    int x1, y1;
+    int x2, y2;
+    SDL_bool draw_end;
+    DrawLineFunc func;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_DrawLines(): dst");
+    }
+
+    func = SDL_CalculateDrawLineFunc(dst->format);
+    if (!func) {
+        return SDL_SetError("SDL_DrawLines(): Unsupported surface format");
+    }
+
+    for (i = 1; i < count; ++i) {
+        x1 = points[i-1].x;
+        y1 = points[i-1].y;
+        x2 = points[i].x;
+        y2 = points[i].y;
+
+        /* Perform clipping */
+        /* FIXME: We don't actually want to clip, as it may change line slope */
+        if (!SDL_IntersectRectAndLine(&dst->clip_rect, &x1, &y1, &x2, &y2)) {
+            continue;
+        }
+
+        /* Draw the end if the whole line is a single point or it was clipped */
+        draw_end = ((x1 == x2) && (y1 == y2)) || (x2 != points[i].x || y2 != points[i].y);
+
+        func(dst, x1, y1, x2, y2, color, draw_end);
+        //gsKit_prim_line(dst->userdata, 10.0, 10.0, 15.0, 15.0, 1, GS_SETREG_RGBAQ(0x20,0,0x80,0x80,0x00));
+    }
+    if (points[0].x != points[count-1].x || points[0].y != points[count-1].y) {
+        SDL_DrawPoint(dst, points[count-1].x, points[count-1].y, color);
+    }
+    return 0;
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 33 - 0
src/render/ps2/SDL_drawline.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_drawline_h_
+#define SDL_drawline_h_
+
+#include "../../SDL_internal.h"
+
+
+extern int SDL_DrawLine(SDL_Surface * dst, int x1, int y1, int x2, int y2, Uint32 color);
+extern int SDL_DrawLines(SDL_Surface * dst, const SDL_Point * points, int count, Uint32 color);
+
+#endif /* SDL_drawline_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 114 - 0
src/render/ps2/SDL_drawpoint.c

@@ -0,0 +1,114 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "SDL_draw.h"
+#include "SDL_drawpoint.h"
+
+
+int
+SDL_DrawPoint(SDL_Surface * dst, int x, int y, Uint32 color)
+{
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_DrawPoint(): dst");
+    }
+
+    /* This function doesn't work on surfaces < 8 bpp */
+    if (dst->format->BitsPerPixel < 8) {
+        return SDL_SetError("SDL_DrawPoint(): Unsupported surface format");
+    }
+
+    /* Perform clipping */
+    if (x < dst->clip_rect.x || y < dst->clip_rect.y ||
+        x >= (dst->clip_rect.x + dst->clip_rect.w) ||
+        y >= (dst->clip_rect.y + dst->clip_rect.h)) {
+        return 0;
+    }
+
+    switch (dst->format->BytesPerPixel) {
+    case 1:
+        DRAW_FASTSETPIXELXY1(x, y);
+        break;
+    case 2:
+        DRAW_FASTSETPIXELXY2(x, y);
+        break;
+    case 3:
+        return SDL_Unsupported();
+    case 4:
+        DRAW_FASTSETPIXELXY4(x, y);
+        break;
+    }
+    return 0;
+}
+
+int
+SDL_DrawPoints(SDL_Surface * dst, const SDL_Point * points, int count,
+               Uint32 color)
+{
+    int minx, miny;
+    int maxx, maxy;
+    int i;
+    int x, y;
+
+    if (!dst) {
+        return SDL_InvalidParamError("SDL_DrawPoints(): dst");
+    }
+
+    /* This function doesn't work on surfaces < 8 bpp */
+    if (dst->format->BitsPerPixel < 8) {
+        return SDL_SetError("SDL_DrawPoints(): Unsupported surface format");
+    }
+
+    minx = dst->clip_rect.x;
+    maxx = dst->clip_rect.x + dst->clip_rect.w - 1;
+    miny = dst->clip_rect.y;
+    maxy = dst->clip_rect.y + dst->clip_rect.h - 1;
+
+    for (i = 0; i < count; ++i) {
+        x = points[i].x;
+        y = points[i].y;
+
+        if (x < minx || x > maxx || y < miny || y > maxy) {
+            continue;
+        }
+
+        switch (dst->format->BytesPerPixel) {
+        case 1:
+            DRAW_FASTSETPIXELXY1(x, y);
+            break;
+        case 2:
+            DRAW_FASTSETPIXELXY2(x, y);
+            break;
+        case 3:
+            return SDL_Unsupported();
+        case 4:
+            DRAW_FASTSETPIXELXY4(x, y);
+            break;
+        }
+    }
+    return 0;
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 33 - 0
src/render/ps2/SDL_drawpoint.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_drawpoint_h_
+#define SDL_drawpoint_h_
+
+#include "../../SDL_internal.h"
+
+
+extern int SDL_DrawPoint(SDL_Surface * dst, int x, int y, Uint32 color);
+extern int SDL_DrawPoints(SDL_Surface * dst, const SDL_Point * points, int count, Uint32 color);
+
+#endif /* SDL_drawpoint_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 1229 - 0
src/render/ps2/SDL_render_sw.c

@@ -0,0 +1,1229 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "../SDL_sysrender.h"
+#include "SDL_render_sw_c.h"
+#include "SDL_hints.h"
+
+#include "SDL_draw.h"
+#include "SDL_blendfillrect.h"
+#include "SDL_blendline.h"
+#include "SDL_blendpoint.h"
+#include "SDL_drawline.h"
+#include "SDL_drawpoint.h"
+#include "SDL_rotate.h"
+#include "SDL_triangle.h"
+
+/* SDL surface based renderer implementation */
+
+typedef struct
+{
+    const SDL_Rect *viewport;
+    const SDL_Rect *cliprect;
+    SDL_bool surface_cliprect_dirty;
+} PS2_DrawStateCache;
+
+typedef struct
+{
+    SDL_Surface *surface;
+    SDL_Surface *window;
+} PS2_RenderData;
+
+
+static GSGLOBAL *gsGlobal = NULL;
+static int vsync_sema_id = 0;
+
+
+/* PRIVATE METHODS */
+static int vsync_handler()
+{
+   iSignalSema(vsync_sema_id);
+
+   ExitHandler();
+   return 0;
+}
+
+/* Copy of gsKit_sync_flip, but without the 'flip' */
+static void gsKit_sync(GSGLOBAL *gsGlobal)
+{
+   if (!gsGlobal->FirstFrame) WaitSema(vsync_sema_id);
+   while (PollSema(vsync_sema_id) >= 0)
+   	;
+}
+
+/* Copy of gsKit_sync_flip, but without the 'sync' */
+static void gsKit_flip(GSGLOBAL *gsGlobal)
+{
+   if (!gsGlobal->FirstFrame)
+   {
+      if (gsGlobal->DoubleBuffering == GS_SETTING_ON)
+      {
+         GS_SET_DISPFB2( gsGlobal->ScreenBuffer[
+               gsGlobal->ActiveBuffer & 1] / 8192,
+               gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 );
+
+         gsGlobal->ActiveBuffer ^= 1;
+      }
+
+   }
+
+   gsKit_setactive(gsGlobal);
+}
+
+
+static SDL_Surface *
+PS2_ActivateRenderer(SDL_Renderer * renderer)
+{
+    PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
+
+    if (!data->surface) {
+        data->surface = data->window;
+    }
+    if (!data->surface) {
+        SDL_Surface *surface = SDL_GetWindowSurface(renderer->window);
+        if (surface) {
+            data->surface = data->window = surface;
+        }
+    }
+    return data->surface;
+}
+
+static void
+PS2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
+{
+    PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
+
+    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
+        data->surface = NULL;
+        data->window = NULL;
+    }
+}
+
+static int
+PS2_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
+{
+    PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
+
+    if (data->surface) {
+        if (w) {
+            *w = data->surface->w;
+        }
+        if (h) {
+            *h = data->surface->h;
+        }
+        return 0;
+    }
+
+    if (renderer->window) {
+        SDL_GetWindowSize(renderer->window, w, h);
+        return 0;
+    }
+
+    return SDL_SetError("Software renderer doesn't have an output surface");
+}
+
+static int
+PS2_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    int bpp;
+    Uint32 Rmask, Gmask, Bmask, Amask;
+
+    if (!SDL_PixelFormatEnumToMasks
+        (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
+        return SDL_SetError("Unknown texture format");
+    }
+
+    texture->driverdata =
+        SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
+                             Bmask, Amask);
+    SDL_SetSurfaceColorMod(texture->driverdata, texture->color.r, texture->color.g, texture->color.b);
+    SDL_SetSurfaceAlphaMod(texture->driverdata, texture->color.a);
+    SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
+
+    /* Only RLE encode textures without an alpha channel since the RLE coder
+     * discards the color values of pixels with an alpha value of zero.
+     */
+    if (texture->access == SDL_TEXTUREACCESS_STATIC && !Amask) {
+        SDL_SetSurfaceRLE(texture->driverdata, 1);
+    }
+
+    if (!texture->driverdata) {
+        return -1;
+    }
+    return 0;
+}
+
+static int
+PS2_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
+                 const SDL_Rect * rect, const void *pixels, int pitch)
+{
+    SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
+    Uint8 *src, *dst;
+    int row;
+    size_t length;
+
+    if(SDL_MUSTLOCK(surface))
+        SDL_LockSurface(surface);
+    src = (Uint8 *) pixels;
+    dst = (Uint8 *) surface->pixels +
+                        rect->y * surface->pitch +
+                        rect->x * surface->format->BytesPerPixel;
+    length = rect->w * surface->format->BytesPerPixel;
+    for (row = 0; row < rect->h; ++row) {
+        SDL_memcpy(dst, src, length);
+        src += pitch;
+        dst += surface->pitch;
+    }
+    if(SDL_MUSTLOCK(surface))
+        SDL_UnlockSurface(surface);
+    return 0;
+}
+
+static int
+PS2_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
+               const SDL_Rect * rect, void **pixels, int *pitch)
+{
+    SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
+
+    *pixels =
+        (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
+                  rect->x * surface->format->BytesPerPixel);
+    *pitch = surface->pitch;
+    return 0;
+}
+
+static void
+PS2_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+}
+
+static void
+PS2_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode)
+{
+}
+
+static int
+PS2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
+
+    if (texture) {
+        data->surface = (SDL_Surface *) texture->driverdata;
+    } else {
+        data->surface = data->window;
+    }
+    return 0;
+}
+
+static int
+PS2_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
+{
+    return 0;  /* nothing to do in this backend. */
+}
+
+static int
+PS2_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
+{
+    SDL_Point *verts = (SDL_Point *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Point), 0, &cmd->data.draw.first);
+    int i;
+
+    if (!verts) {
+        return -1;
+    }
+
+    cmd->data.draw.count = count;
+
+    for (i = 0; i < count; i++, verts++, points++) {
+        verts->x = (int)points->x;
+        verts->y = (int)points->y;
+    }
+
+    return 0;
+}
+
+static int
+PS2_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
+{
+    SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, count * sizeof (SDL_Rect), 0, &cmd->data.draw.first);
+    int i;
+
+    if (!verts) {
+        return -1;
+    }
+
+    cmd->data.draw.count = count;
+
+    for (i = 0; i < count; i++, verts++, rects++) {
+        verts->x = (int)rects->x;
+        verts->y = (int)rects->y;
+        verts->w = SDL_max((int)rects->w, 1);
+        verts->h = SDL_max((int)rects->h, 1);
+    }
+
+    return 0;
+}
+
+static int
+PS2_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
+             const SDL_Rect * srcrect, const SDL_FRect * dstrect)
+{
+    SDL_Rect *verts = (SDL_Rect *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (SDL_Rect), 0, &cmd->data.draw.first);
+
+    if (!verts) {
+        return -1;
+    }
+
+    cmd->data.draw.count = 1;
+
+    SDL_copyp(verts, srcrect);
+    verts++;
+
+    verts->x = (int)dstrect->x;
+    verts->y = (int)dstrect->y;
+    verts->w = (int)dstrect->w;
+    verts->h = (int)dstrect->h;
+
+    return 0;
+}
+
+typedef struct CopyExData
+{
+    SDL_Rect srcrect;
+    SDL_Rect dstrect;
+    double angle;
+    SDL_FPoint center;
+    SDL_RendererFlip flip;
+    float scale_x;
+    float scale_y;
+} CopyExData;
+
+static int
+PS2_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
+               const SDL_Rect * srcrect, const SDL_FRect * dstrect,
+               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y)
+{
+    CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first);
+
+    if (!verts) {
+        return -1;
+    }
+
+    cmd->data.draw.count = 1;
+
+    SDL_copyp(&verts->srcrect, srcrect);
+
+    verts->dstrect.x = (int)dstrect->x;
+    verts->dstrect.y = (int)dstrect->y;
+    verts->dstrect.w = (int)dstrect->w;
+    verts->dstrect.h = (int)dstrect->h;
+    verts->angle = angle;
+    SDL_copyp(&verts->center, center);
+    verts->flip = flip;
+    verts->scale_x = scale_x;
+    verts->scale_y = scale_y;
+
+    return 0;
+}
+
+static int
+Blit_to_Screen(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *surface, SDL_Rect *dstrect,
+        float scale_x, float scale_y, SDL_ScaleMode scaleMode)
+{
+    int retval;
+    /* Renderer scaling, if needed */
+    if (scale_x != 1.0f || scale_y != 1.0f) {
+        SDL_Rect r;
+        r.x = (int)((float) dstrect->x * scale_x);
+        r.y = (int)((float) dstrect->y * scale_y);
+        r.w = (int)((float) dstrect->w * scale_x);
+        r.h = (int)((float) dstrect->h * scale_y);
+        retval = SDL_PrivateUpperBlitScaled(src, srcrect, surface, &r, scaleMode);
+    } else {
+        retval = SDL_BlitSurface(src, srcrect, surface, dstrect);
+    }
+    return retval;
+}
+
+static int
+PS2_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * texture,
+                const SDL_Rect * srcrect, const SDL_Rect * final_rect,
+                const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip, float scale_x, float scale_y)
+{
+    SDL_Surface *src = (SDL_Surface *) texture->driverdata;
+    SDL_Rect tmp_rect;
+    SDL_Surface *src_clone, *src_rotated, *src_scaled;
+    SDL_Surface *mask = NULL, *mask_rotated = NULL;
+    int retval = 0;
+    SDL_BlendMode blendmode;
+    Uint8 alphaMod, rMod, gMod, bMod;
+    int applyModulation = SDL_FALSE;
+    int blitRequired = SDL_FALSE;
+    int isOpaque = SDL_FALSE;
+
+    if (!surface) {
+        return -1;
+    }
+
+    tmp_rect.x = 0;
+    tmp_rect.y = 0;
+    tmp_rect.w = final_rect->w;
+    tmp_rect.h = final_rect->h;
+
+    /* It is possible to encounter an RLE encoded surface here and locking it is
+     * necessary because this code is going to access the pixel buffer directly.
+     */
+    if (SDL_MUSTLOCK(src)) {
+        SDL_LockSurface(src);
+    }
+
+    /* Clone the source surface but use its pixel buffer directly.
+     * The original source surface must be treated as read-only.
+     */
+    src_clone = SDL_CreateRGBSurfaceFrom(src->pixels, src->w, src->h, src->format->BitsPerPixel, src->pitch,
+                                         src->format->Rmask, src->format->Gmask,
+                                         src->format->Bmask, src->format->Amask);
+    if (src_clone == NULL) {
+        if (SDL_MUSTLOCK(src)) {
+            SDL_UnlockSurface(src);
+        }
+        return -1;
+    }
+
+    SDL_GetSurfaceBlendMode(src, &blendmode);
+    SDL_GetSurfaceAlphaMod(src, &alphaMod);
+    SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
+
+    /* SDLgfx_rotateSurface only accepts 32-bit surfaces with a 8888 layout. Everything else has to be converted. */
+    if (src->format->BitsPerPixel != 32 || SDL_PIXELLAYOUT(src->format->format) != SDL_PACKEDLAYOUT_8888 || !src->format->Amask) {
+        blitRequired = SDL_TRUE;
+    }
+
+    /* If scaling and cropping is necessary, it has to be taken care of before the rotation. */
+    if (!(srcrect->w == final_rect->w && srcrect->h == final_rect->h && srcrect->x == 0 && srcrect->y == 0)) {
+        blitRequired = SDL_TRUE;
+    }
+
+    /* srcrect is not selecting the whole src surface, so cropping is needed */
+    if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) {
+        blitRequired = SDL_TRUE;
+    }
+
+    /* The color and alpha modulation has to be applied before the rotation when using the NONE, MOD or MUL blend modes. */
+    if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) && (alphaMod & rMod & gMod & bMod) != 255) {
+        applyModulation = SDL_TRUE;
+        SDL_SetSurfaceAlphaMod(src_clone, alphaMod);
+        SDL_SetSurfaceColorMod(src_clone, rMod, gMod, bMod);
+    }
+
+    /* Opaque surfaces are much easier to handle with the NONE blend mode. */
+    if (blendmode == SDL_BLENDMODE_NONE && !src->format->Amask && alphaMod == 255) {
+        isOpaque = SDL_TRUE;
+    }
+
+    /* The NONE blend mode requires a mask for non-opaque surfaces. This mask will be used
+     * to clear the pixels in the destination surface. The other steps are explained below.
+     */
+    if (blendmode == SDL_BLENDMODE_NONE && !isOpaque) {
+        mask = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
+                                    0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
+        if (mask == NULL) {
+            retval = -1;
+        } else {
+            SDL_SetSurfaceBlendMode(mask, SDL_BLENDMODE_MOD);
+        }
+    }
+
+    /* Create a new surface should there be a format mismatch or if scaling, cropping,
+     * or modulation is required. It's possible to use the source surface directly otherwise.
+     */
+    if (!retval && (blitRequired || applyModulation)) {
+        SDL_Rect scale_rect = tmp_rect;
+        src_scaled = SDL_CreateRGBSurface(0, final_rect->w, final_rect->h, 32,
+                                          0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
+        if (src_scaled == NULL) {
+            retval = -1;
+        } else {
+            SDL_SetSurfaceBlendMode(src_clone, SDL_BLENDMODE_NONE);
+            retval = SDL_PrivateUpperBlitScaled(src_clone, srcrect, src_scaled, &scale_rect, texture->scaleMode);
+            SDL_FreeSurface(src_clone);
+            src_clone = src_scaled;
+            src_scaled = NULL;
+        }
+    }
+
+    /* SDLgfx_rotateSurface is going to make decisions depending on the blend mode. */
+    SDL_SetSurfaceBlendMode(src_clone, blendmode);
+
+    if (!retval) {
+        SDL_Rect rect_dest;
+        double cangle, sangle;
+
+        SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, center,
+                &rect_dest, &cangle, &sangle);
+        src_rotated = SDLgfx_rotateSurface(src_clone, angle,
+                (texture->scaleMode == SDL_ScaleModeNearest) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL,
+                &rect_dest, cangle, sangle, center);
+        if (src_rotated == NULL) {
+            retval = -1;
+        }
+        if (!retval && mask != NULL) {
+            /* The mask needed for the NONE blend mode gets rotated with the same parameters. */
+            mask_rotated = SDLgfx_rotateSurface(mask, angle,
+                    SDL_FALSE, 0, 0,
+                    &rect_dest, cangle, sangle, center);
+            if (mask_rotated == NULL) {
+                retval = -1;
+            }
+        }
+        if (!retval) {
+
+            tmp_rect.x = final_rect->x + rect_dest.x;
+            tmp_rect.y = final_rect->y + rect_dest.y;
+            tmp_rect.w = rect_dest.w;
+            tmp_rect.h = rect_dest.h;
+
+            /* The NONE blend mode needs some special care with non-opaque surfaces.
+             * Other blend modes or opaque surfaces can be blitted directly.
+             */
+            if (blendmode != SDL_BLENDMODE_NONE || isOpaque) {
+                if (applyModulation == SDL_FALSE) {
+                    /* If the modulation wasn't already applied, make it happen now. */
+                    SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
+                    SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
+                }
+                /* Renderer scaling, if needed */
+                retval = Blit_to_Screen(src_rotated, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode);
+            } else {
+                /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
+                 * First, the area where the rotated pixels will be blitted to get set to zero.
+                 * This is accomplished by simply blitting a mask with the NONE blend mode.
+                 * The colorkey set by the rotate function will discard the correct pixels.
+                 */
+                SDL_Rect mask_rect = tmp_rect;
+                SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
+                /* Renderer scaling, if needed */
+                retval = Blit_to_Screen(mask_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode);
+                if (!retval) {
+                    /* The next step copies the alpha value. This is done with the BLEND blend mode and
+                     * by modulating the source colors with 0. Since the destination is all zeros, this
+                     * will effectively set the destination alpha to the source alpha.
+                     */
+                    SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
+                    mask_rect = tmp_rect;
+                    /* Renderer scaling, if needed */
+                    retval = Blit_to_Screen(src_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode);
+                    if (!retval) {
+                        /* The last step gets the color values in place. The ADD blend mode simply adds them to
+                         * the destination (where the color values are all zero). However, because the ADD blend
+                         * mode modulates the colors with the alpha channel, a surface without an alpha mask needs
+                         * to be created. This makes all source pixels opaque and the colors get copied correctly.
+                         */
+                        SDL_Surface *src_rotated_rgb;
+                        src_rotated_rgb = SDL_CreateRGBSurfaceFrom(src_rotated->pixels, src_rotated->w, src_rotated->h,
+                                                                   src_rotated->format->BitsPerPixel, src_rotated->pitch,
+                                                                   src_rotated->format->Rmask, src_rotated->format->Gmask,
+                                                                   src_rotated->format->Bmask, 0);
+                        if (src_rotated_rgb == NULL) {
+                            retval = -1;
+                        } else {
+                            SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
+                            /* Renderer scaling, if needed */
+                            retval = Blit_to_Screen(src_rotated_rgb, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode);
+                            SDL_FreeSurface(src_rotated_rgb);
+                        }
+                    }
+                }
+                SDL_FreeSurface(mask_rotated);
+            }
+            if (src_rotated != NULL) {
+                SDL_FreeSurface(src_rotated);
+            }
+        }
+    }
+
+    if (SDL_MUSTLOCK(src)) {
+        SDL_UnlockSurface(src);
+    }
+    if (mask != NULL) {
+        SDL_FreeSurface(mask);
+    }
+    if (src_clone != NULL) {
+        SDL_FreeSurface(src_clone);
+    }
+    return retval;
+}
+
+
+typedef struct GeometryFillData
+{
+    SDL_Point dst;
+    SDL_Color color;
+} GeometryFillData;
+
+typedef struct GeometryCopyData
+{
+    SDL_Point src;
+    SDL_Point dst;
+    SDL_Color color;
+} GeometryCopyData;
+
+static int
+PS2_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
+        const float *xy, int xy_stride, const SDL_Color *color, int color_stride, const float *uv, int uv_stride,
+        int num_vertices, const void *indices, int num_indices, int size_indices,
+        float scale_x, float scale_y)
+{
+    int i;
+    int count = indices ? num_indices : num_vertices;
+    void *verts;
+    int sz = texture ? sizeof (GeometryCopyData) : sizeof (GeometryFillData);
+
+    verts = (void *) SDL_AllocateRenderVertices(renderer, count * sz, 0, &cmd->data.draw.first);
+    if (!verts) {
+        return -1;
+    }
+
+    cmd->data.draw.count = count;
+    size_indices = indices ? size_indices : 0;
+
+    if (texture) {
+        GeometryCopyData *ptr = (GeometryCopyData *) verts;
+        for (i = 0; i < count; i++) {
+            int j;
+            float *xy_;
+            SDL_Color col_;
+            float *uv_;
+            if (size_indices == 4) {
+                j = ((const Uint32 *)indices)[i];
+            } else if (size_indices == 2) {
+                j = ((const Uint16 *)indices)[i];
+            } else if (size_indices == 1) {
+                j = ((const Uint8 *)indices)[i];
+            } else {
+                j = i;
+            }
+
+            xy_ = (float *)((char*)xy + j * xy_stride);
+            col_ = *(SDL_Color *)((char*)color + j * color_stride);
+
+            uv_ = (float *)((char*)uv + j * uv_stride);
+
+            ptr->src.x = (int)(uv_[0] * texture->w);
+            ptr->src.y = (int)(uv_[1] * texture->h);
+
+            ptr->dst.x = (int)(xy_[0] * scale_x);
+            ptr->dst.y = (int)(xy_[1] * scale_y);
+            trianglepoint_2_fixedpoint(&ptr->dst);
+
+            ptr->color = col_;
+
+            ptr++;
+       }
+    } else {
+        GeometryFillData *ptr = (GeometryFillData *) verts;
+
+        for (i = 0; i < count; i++) {
+            int j;
+            float *xy_;
+            SDL_Color col_;
+            if (size_indices == 4) {
+                j = ((const Uint32 *)indices)[i];
+            } else if (size_indices == 2) {
+                j = ((const Uint16 *)indices)[i];
+            } else if (size_indices == 1) {
+                j = ((const Uint8 *)indices)[i];
+            } else {
+                j = i;
+            }
+
+            xy_ = (float *)((char*)xy + j * xy_stride);
+            col_ = *(SDL_Color *)((char*)color + j * color_stride);
+
+            ptr->dst.x = (int)(xy_[0] * scale_x);
+            ptr->dst.y = (int)(xy_[1] * scale_y);
+            trianglepoint_2_fixedpoint(&ptr->dst);
+
+            ptr->color = col_;
+
+            ptr++;
+       }
+    }
+    return 0;
+}
+
+static void
+PrepTextureForCopy(const SDL_RenderCommand *cmd)
+{
+    const Uint8 r = cmd->data.draw.r;
+    const Uint8 g = cmd->data.draw.g;
+    const Uint8 b = cmd->data.draw.b;
+    const Uint8 a = cmd->data.draw.a;
+    const SDL_BlendMode blend = cmd->data.draw.blend;
+    SDL_Texture *texture = cmd->data.draw.texture;
+    SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
+    const SDL_bool colormod = ((r & g & b) != 0xFF);
+    const SDL_bool alphamod = (a != 0xFF);
+    const SDL_bool blending = ((blend == SDL_BLENDMODE_ADD) || (blend == SDL_BLENDMODE_MOD) || (blend == SDL_BLENDMODE_MUL));
+
+    if (colormod || alphamod || blending) {
+        SDL_SetSurfaceRLE(surface, 0);
+    }
+
+    /* !!! FIXME: we can probably avoid some of these calls. */
+    SDL_SetSurfaceColorMod(surface, r, g, b);
+    SDL_SetSurfaceAlphaMod(surface, a);
+    SDL_SetSurfaceBlendMode(surface, blend);
+}
+
+static void
+SetDrawState(SDL_Surface *surface, PS2_DrawStateCache *drawstate)
+{
+    if (drawstate->surface_cliprect_dirty) {
+        const SDL_Rect *viewport = drawstate->viewport;
+        const SDL_Rect *cliprect = drawstate->cliprect;
+        SDL_assert(viewport != NULL);  /* the higher level should have forced a SDL_RENDERCMD_SETVIEWPORT */
+
+        if (cliprect != NULL) {
+            SDL_Rect clip_rect;
+            clip_rect.x = cliprect->x + viewport->x;
+            clip_rect.y = cliprect->y + viewport->y;
+            clip_rect.w = cliprect->w;
+            clip_rect.h = cliprect->h;
+            SDL_IntersectRect(viewport, &clip_rect, &clip_rect);
+            SDL_SetClipRect(surface, &clip_rect);
+        } else {
+            SDL_SetClipRect(surface, drawstate->viewport);
+        }
+        drawstate->surface_cliprect_dirty = SDL_FALSE;
+    }
+}
+
+static int
+PS2_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
+{
+    SDL_Surface *surface = PS2_ActivateRenderer(renderer);
+    PS2_DrawStateCache drawstate;
+
+    if (!surface) {
+        return -1;
+    }
+
+    drawstate.viewport = NULL;
+    drawstate.cliprect = NULL;
+    drawstate.surface_cliprect_dirty = SDL_TRUE;
+
+    while (cmd) {
+        switch (cmd->command) {
+            case SDL_RENDERCMD_SETDRAWCOLOR: {
+                break;  /* Not used in this backend. */
+            }
+
+            case SDL_RENDERCMD_SETVIEWPORT: {
+                drawstate.viewport = &cmd->data.viewport.rect;
+                drawstate.surface_cliprect_dirty = SDL_TRUE;
+                break;
+            }
+
+            case SDL_RENDERCMD_SETCLIPRECT: {
+                drawstate.cliprect = cmd->data.cliprect.enabled ? &cmd->data.cliprect.rect : NULL;
+                drawstate.surface_cliprect_dirty = SDL_TRUE;
+                break;
+            }
+
+            case SDL_RENDERCMD_CLEAR: {
+                const Uint8 r = cmd->data.color.r;
+                const Uint8 g = cmd->data.color.g;
+                const Uint8 b = cmd->data.color.b;
+                const Uint8 a = cmd->data.color.a;
+                gsKit_clear(gsGlobal, GS_SETREG_RGBAQ(r,g,b,a/2,0x00));
+                drawstate.surface_cliprect_dirty = SDL_TRUE;
+                break;
+            }
+
+            case SDL_RENDERCMD_DRAW_POINTS: {
+                const Uint8 r = cmd->data.draw.r;
+                const Uint8 g = cmd->data.draw.g;
+                const Uint8 b = cmd->data.draw.b;
+                const Uint8 a = cmd->data.draw.a;
+                const int count = (int) cmd->data.draw.count;
+                SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const SDL_BlendMode blend = cmd->data.draw.blend;
+                SetDrawState(surface, &drawstate);
+
+                /* Apply viewport */
+                if (drawstate.viewport->x || drawstate.viewport->y) {
+                    int i;
+                    for (i = 0; i < count; i++) {
+                        verts[i].x += drawstate.viewport->x;
+                        verts[i].y += drawstate.viewport->y;
+                    }
+                }
+
+                if (blend == SDL_BLENDMODE_NONE) {
+                    SDL_DrawPoints(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
+                } else {
+                    SDL_BlendPoints(surface, verts, count, blend, r, g, b, a);
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_DRAW_LINES: {
+                const Uint8 r = cmd->data.draw.r;
+                const Uint8 g = cmd->data.draw.g;
+                const Uint8 b = cmd->data.draw.b;
+                const Uint8 a = cmd->data.draw.a;
+                const int count = (int) cmd->data.draw.count;
+                SDL_Point *verts = (SDL_Point *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const SDL_BlendMode blend = cmd->data.draw.blend;
+                SetDrawState(surface, &drawstate);
+
+                /* Apply viewport */
+                if (drawstate.viewport->x || drawstate.viewport->y) {
+                    int i;
+                    for (i = 0; i < count; i++) {
+                        verts[i].x += drawstate.viewport->x;
+                        verts[i].y += drawstate.viewport->y;
+                    }
+                }
+
+                if (blend == SDL_BLENDMODE_NONE) {
+                    SDL_DrawLines(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
+                } else {
+                    SDL_BlendLines(surface, verts, count, blend, r, g, b, a);
+                }
+
+                break;
+            }
+
+            case SDL_RENDERCMD_FILL_RECTS: {
+                const Uint8 r = cmd->data.draw.r;
+                const Uint8 g = cmd->data.draw.g;
+                const Uint8 b = cmd->data.draw.b;
+                const Uint8 a = cmd->data.draw.a;
+                const int count = (int) cmd->data.draw.count;
+                SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const SDL_BlendMode blend = cmd->data.draw.blend;
+                SetDrawState(surface, &drawstate);
+
+                /* Apply viewport */
+                if (drawstate.viewport->x || drawstate.viewport->y) {
+                    int i;
+                    for (i = 0; i < count; i++) {
+                        verts[i].x += drawstate.viewport->x;
+                        verts[i].y += drawstate.viewport->y;
+                    }
+                }
+
+                if (blend == SDL_BLENDMODE_NONE) {
+                    SDL_FillRects(surface, verts, count, SDL_MapRGBA(surface->format, r, g, b, a));
+                } else {
+                    SDL_BlendFillRects(surface, verts, count, blend, r, g, b, a);
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_COPY: {
+                SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const SDL_Rect *srcrect = verts;
+                SDL_Rect *dstrect = verts + 1;
+                SDL_Texture *texture = cmd->data.draw.texture;
+                SDL_Surface *src = (SDL_Surface *) texture->driverdata;
+
+                SetDrawState(surface, &drawstate);
+
+                PrepTextureForCopy(cmd);
+
+                /* Apply viewport */
+                if (drawstate.viewport->x || drawstate.viewport->y) {
+                    dstrect->x += drawstate.viewport->x;
+                    dstrect->y += drawstate.viewport->y;
+                }
+
+                if ( srcrect->w == dstrect->w && srcrect->h == dstrect->h ) {
+                    SDL_BlitSurface(src, srcrect, surface, dstrect);
+                } else {
+                    /* If scaling is ever done, permanently disable RLE (which doesn't support scaling)
+                     * to avoid potentially frequent RLE encoding/decoding.
+                     */
+                    SDL_SetSurfaceRLE(surface, 0);
+
+                    /* Prevent to do scaling + clipping on viewport boundaries as it may lose proportion */
+                    if (dstrect->x < 0 || dstrect->y < 0 || dstrect->x + dstrect->w > surface->w || dstrect->y + dstrect->h > surface->h) {
+                        SDL_Surface *tmp = SDL_CreateRGBSurfaceWithFormat(0, dstrect->w, dstrect->h, 0, src->format->format);
+                        /* Scale to an intermediate surface, then blit */
+                        if (tmp) {
+                            SDL_Rect r;
+                            SDL_BlendMode blendmode;
+                            Uint8 alphaMod, rMod, gMod, bMod;
+
+                            SDL_GetSurfaceBlendMode(src, &blendmode);
+                            SDL_GetSurfaceAlphaMod(src, &alphaMod);
+                            SDL_GetSurfaceColorMod(src, &rMod, &gMod, &bMod);
+
+                            r.x = 0;
+                            r.y = 0;
+                            r.w = dstrect->w;
+                            r.h = dstrect->h;
+
+                            SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_NONE);
+                            SDL_SetSurfaceColorMod(src, 255, 255, 255);
+                            SDL_SetSurfaceAlphaMod(src, 255);
+
+                            SDL_PrivateUpperBlitScaled(src, srcrect, tmp, &r, texture->scaleMode);
+
+                            SDL_SetSurfaceColorMod(tmp, rMod, gMod, bMod);
+                            SDL_SetSurfaceAlphaMod(tmp, alphaMod);
+                            SDL_SetSurfaceBlendMode(tmp, blendmode);
+
+                            SDL_BlitSurface(tmp, NULL, surface, dstrect);
+                            SDL_FreeSurface(tmp);
+                            /* No need to set back r/g/b/a/blendmode to 'src' since it's done in PrepTextureForCopy() */
+                        }
+                    } else{
+                        SDL_PrivateUpperBlitScaled(src, srcrect, surface, dstrect, texture->scaleMode);
+                    }
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_COPY_EX: {
+                CopyExData *copydata = (CopyExData *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                SetDrawState(surface, &drawstate);
+                PrepTextureForCopy(cmd);
+
+                /* Apply viewport */
+                if (drawstate.viewport->x || drawstate.viewport->y) {
+                    copydata->dstrect.x += drawstate.viewport->x;
+                    copydata->dstrect.y += drawstate.viewport->y;
+                }
+
+                PS2_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
+                                &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip,
+                                copydata->scale_x, copydata->scale_y);
+                break;
+            }
+
+            case SDL_RENDERCMD_GEOMETRY: {
+                int i;
+                SDL_Rect *verts = (SDL_Rect *) (((Uint8 *) vertices) + cmd->data.draw.first);
+                const int count = (int) cmd->data.draw.count;
+                SDL_Texture *texture = cmd->data.draw.texture;
+                const SDL_BlendMode blend = cmd->data.draw.blend;
+
+                SetDrawState(surface, &drawstate);
+
+                if (texture) {
+                    SDL_Surface *src = (SDL_Surface *) texture->driverdata;
+
+                    GeometryCopyData *ptr = (GeometryCopyData *) verts;
+
+                    PrepTextureForCopy(cmd);
+
+                    /* Apply viewport */
+                    if (drawstate.viewport->x || drawstate.viewport->y) {
+                        SDL_Point vp;
+                        vp.x = drawstate.viewport->x;
+                        vp.y = drawstate.viewport->y;
+                        trianglepoint_2_fixedpoint(&vp);
+                        for (i = 0; i < count; i++) {
+                            ptr[i].dst.x += vp.x;
+                            ptr[i].dst.y += vp.y;
+                        }
+                    }
+
+                    for (i = 0; i < count; i += 3, ptr += 3) {
+                        SDL_PS2_BlitTriangle(
+                                src,
+                                &(ptr[0].src), &(ptr[1].src), &(ptr[2].src),
+                                surface,
+                                &(ptr[0].dst), &(ptr[1].dst), &(ptr[2].dst),
+                                ptr[0].color, ptr[1].color, ptr[2].color);
+                    }
+                } else {
+                    GeometryFillData *ptr = (GeometryFillData *) verts;
+
+                    /* Apply viewport */
+                    if (drawstate.viewport->x || drawstate.viewport->y) {
+                        SDL_Point vp;
+                        vp.x = drawstate.viewport->x;
+                        vp.y = drawstate.viewport->y;
+                        trianglepoint_2_fixedpoint(&vp);
+                        for (i = 0; i < count; i++) {
+                            ptr[i].dst.x += vp.x;
+                            ptr[i].dst.y += vp.y;
+                        }
+                    }
+
+                    for (i = 0; i < count; i += 3, ptr += 3) {
+                        SDL_PS2_FillTriangle(surface, &(ptr[0].dst), &(ptr[1].dst), &(ptr[2].dst), blend, ptr[0].color, ptr[1].color, ptr[2].color);
+                    }
+                }
+                break;
+            }
+
+            case SDL_RENDERCMD_NO_OP:
+                break;
+        }
+
+        cmd = cmd->next;
+    }
+
+    return 0;
+}
+
+static int
+PS2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
+                    Uint32 format, void * pixels, int pitch)
+{
+    SDL_Surface *surface = PS2_ActivateRenderer(renderer);
+    Uint32 src_format;
+    void *src_pixels;
+
+    if (!surface) {
+        return -1;
+    }
+
+    /* NOTE: The rect is already adjusted according to the viewport by
+     * SDL_RenderReadPixels.
+     */
+
+    if (rect->x < 0 || rect->x+rect->w > surface->w ||
+        rect->y < 0 || rect->y+rect->h > surface->h) {
+        return SDL_SetError("Tried to read outside of surface bounds");
+    }
+
+    src_format = surface->format->format;
+    src_pixels = (void*)((Uint8 *) surface->pixels +
+                    rect->y * surface->pitch +
+                    rect->x * surface->format->BytesPerPixel);
+
+    return SDL_ConvertPixels(rect->w, rect->h,
+                             src_format, src_pixels, surface->pitch,
+                             format, pixels, pitch);
+}
+
+static void
+PS2_RenderPresent(SDL_Renderer * renderer)
+{
+    SDL_Window *window = renderer->window;
+
+    if (window) {
+        SDL_UpdateWindowSurface(window);
+    }
+    if (gsGlobal->DoubleBuffering == GS_SETTING_OFF) {
+		gsKit_sync(gsGlobal);
+		gsKit_queue_exec(gsGlobal);
+    } else {
+		gsKit_queue_exec(gsGlobal);
+		gsKit_finish();
+		gsKit_sync(gsGlobal);
+		gsKit_flip(gsGlobal);
+	}
+	gsKit_TexManager_nextFrame(gsGlobal);
+}
+
+static void
+PS2_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
+
+    SDL_FreeSurface(surface);
+}
+
+static void
+PS2_DestroyRenderer(SDL_Renderer * renderer)
+{
+    PS2_RenderData *data = (PS2_RenderData *) renderer->driverdata;
+
+    SDL_free(data);
+    SDL_free(renderer);
+}
+
+SDL_Renderer *
+PS2_CreateRendererForSurface(SDL_Surface * surface)
+{
+    SDL_Renderer *renderer;
+    PS2_RenderData *data;
+
+    if (!surface) {
+        SDL_InvalidParamError("surface");
+        return NULL;
+    }
+
+    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
+    if (!renderer) {
+        SDL_OutOfMemory();
+        return NULL;
+    }
+
+    data = (PS2_RenderData *) SDL_calloc(1, sizeof(*data));
+    if (!data) {
+        PS2_DestroyRenderer(renderer);
+        SDL_OutOfMemory();
+        return NULL;
+    }
+    data->surface = surface;
+    data->window = surface;
+
+    renderer->WindowEvent = PS2_WindowEvent;
+    renderer->GetOutputSize = PS2_GetOutputSize;
+    renderer->CreateTexture = PS2_CreateTexture;
+    renderer->UpdateTexture = PS2_UpdateTexture;
+    renderer->LockTexture = PS2_LockTexture;
+    renderer->UnlockTexture = PS2_UnlockTexture;
+    renderer->SetTextureScaleMode = PS2_SetTextureScaleMode;
+    renderer->SetRenderTarget = PS2_SetRenderTarget;
+    renderer->QueueSetViewport = PS2_QueueSetViewport;
+    renderer->QueueSetDrawColor = PS2_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */
+    renderer->QueueDrawPoints = PS2_QueueDrawPoints;
+    renderer->QueueDrawLines = PS2_QueueDrawPoints;  /* lines and points queue vertices the same way. */
+    renderer->QueueFillRects = PS2_QueueFillRects;
+    renderer->QueueCopy = PS2_QueueCopy;
+    renderer->QueueCopyEx = PS2_QueueCopyEx;
+    renderer->QueueGeometry = PS2_QueueGeometry;
+    renderer->RunCommandQueue = PS2_RunCommandQueue;
+    renderer->RenderReadPixels = PS2_RenderReadPixels;
+    renderer->RenderPresent = PS2_RenderPresent;
+    renderer->DestroyTexture = PS2_DestroyTexture;
+    renderer->DestroyRenderer = PS2_DestroyRenderer;
+    renderer->info = PS2_RenderDriver.info;
+    renderer->driverdata = data;
+
+    PS2_ActivateRenderer(renderer);
+
+    return renderer;
+}
+
+static SDL_Renderer *
+PS2_CreateRenderer(SDL_Window * window, Uint32 flags)
+{
+    const char *hint;
+    SDL_Surface *surface;
+    SDL_bool no_hint_set;
+
+    /* Set the vsync hint based on our flags, if it's not already set */
+    hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC);
+    if (!hint || !*hint) {
+        no_hint_set = SDL_TRUE;
+    } else {
+        no_hint_set = SDL_FALSE;
+    }
+
+    if (no_hint_set) {
+        SDL_SetHint(SDL_HINT_RENDER_VSYNC, (flags & SDL_RENDERER_PRESENTVSYNC) ? "1" : "0");
+    }
+
+    surface = SDL_GetWindowSurface(window);
+
+    /* Reset the vsync hint if we set it above */
+    if (no_hint_set) {
+        SDL_SetHint(SDL_HINT_RENDER_VSYNC, "");
+    }
+
+    ee_sema_t sema;
+    sema.init_count = 0;
+    sema.max_count = 1;
+    sema.option = 0;
+    vsync_sema_id = CreateSema(&sema);
+
+	gsGlobal = gsKit_init_global();
+
+	gsGlobal->Mode = gsKit_check_rom();
+	if (gsGlobal->Mode == GS_MODE_PAL){
+		gsGlobal->Height = 512;
+	} else {
+		gsGlobal->Height = 448;
+	}
+
+	gsGlobal->PSM  = GS_PSM_CT24;
+	gsGlobal->PSMZ = GS_PSMZ_16S;
+	gsGlobal->ZBuffering = GS_SETTING_OFF;
+	gsGlobal->DoubleBuffering = GS_SETTING_ON;
+	gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
+	gsGlobal->Dithering = GS_SETTING_OFF;
+
+	gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0, 1, 0, 1, 0), 0);
+
+	dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
+	dmaKit_chan_init(DMA_CHANNEL_GIF);
+
+	printf("\nGraphics: created %ix%i video surface\n",
+		gsGlobal->Width, gsGlobal->Height);
+
+	gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT);
+
+	gsKit_vram_clear(gsGlobal);
+
+	gsKit_init_screen(gsGlobal);
+
+	gsKit_TexManager_init(gsGlobal);
+
+	gsKit_add_vsync_handler(vsync_handler);
+
+	gsKit_mode_switch(gsGlobal, GS_ONESHOT);
+
+    gsKit_clear(gsGlobal, GS_SETREG_RGBAQ(0x80,0x00,0x00,0x80,0x00));	
+
+	if (gsGlobal->DoubleBuffering == GS_SETTING_OFF) {
+		gsKit_sync(gsGlobal);
+		gsKit_queue_exec(gsGlobal);
+    } else {
+		gsKit_queue_exec(gsGlobal);
+		gsKit_finish();
+		gsKit_sync(gsGlobal);
+		gsKit_flip(gsGlobal);
+	}
+	gsKit_TexManager_nextFrame(gsGlobal);
+
+    surface->userdata = gsGlobal;
+
+    if (!surface) {
+        return NULL;
+    }
+    return PS2_CreateRendererForSurface(surface);
+}
+
+SDL_RenderDriver PS2_RenderDriver = {
+    PS2_CreateRenderer,
+    {
+     "PS2 gsKit",
+     SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE,
+     8,
+     {
+      SDL_PIXELFORMAT_ARGB8888,
+      SDL_PIXELFORMAT_ABGR8888,
+      SDL_PIXELFORMAT_RGBA8888,
+      SDL_PIXELFORMAT_BGRA8888,
+      SDL_PIXELFORMAT_RGB888,
+      SDL_PIXELFORMAT_BGR888,
+      SDL_PIXELFORMAT_RGB565,
+      SDL_PIXELFORMAT_RGB555
+     },
+    .max_texture_width = 512,
+    .max_texture_height = 512,}
+};
+
+#endif /* SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 37 - 0
src/render/ps2/SDL_render_sw_c.h

@@ -0,0 +1,37 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_render_sw_c_h_
+#define SDL_render_sw_c_h_
+
+#include <kernel.h>
+
+#include <gsKit.h>
+#include <dmaKit.h>
+
+#include <gsToolkit.h>
+#include <gsInline.h>
+
+extern SDL_Renderer * SW_CreateRendererForSurface(SDL_Surface * surface);
+
+#endif /* SDL_render_sw_c_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 577 - 0
src/render/ps2/SDL_rotate.c

@@ -0,0 +1,577 @@
+/*
+
+SDL_rotate.c: rotates 32bit or 8bit surfaces
+
+Shamelessly stolen from SDL_gfx by Andreas Schiffler. Original copyright follows:
+
+Copyright (C) 2001-2011  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 "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#if defined(__WIN32__) || defined(__GDK__)
+#include "../../core/windows/SDL_windows.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "SDL.h"
+#include "SDL_rotate.h"
+
+/* ---- Internally used structures */
+
+/* !
+\brief A 32 bit RGBA pixel.
+*/
+typedef struct tColorRGBA {
+    Uint8 r;
+    Uint8 g;
+    Uint8 b;
+    Uint8 a;
+} tColorRGBA;
+
+/* !
+\brief A 8bit Y/palette pixel.
+*/
+typedef struct tColorY {
+    Uint8 y;
+} tColorY;
+
+/* !
+\brief Number of guard rows added to destination surfaces.
+
+This is a simple but effective workaround for observed issues.
+These rows allocate extra memory and are then hidden from the surface.
+Rows are added to the end of destination surfaces when they are allocated.
+This catches any potential overflows which seem to happen with
+just the right src image dimensions and scale/rotation and can lead
+to a situation where the program can segfault.
+*/
+#define GUARD_ROWS (2)
+
+/* !
+\brief Returns colorkey info for a surface
+*/
+static Uint32
+get_colorkey(SDL_Surface *src)
+{
+    Uint32 key = 0;
+    if (SDL_HasColorKey(src)) {
+        SDL_GetColorKey(src, &key);
+    }
+    return key;
+}
+
+/* rotate (sx, sy) by (angle, center) into (dx, dy) */
+static void
+rotate(double sx, double sy, double sinangle, double cosangle, const SDL_FPoint *center, double *dx, double *dy) {
+    sx -= center->x;
+    sy -= center->y;
+
+    *dx = cosangle * sx - sinangle * sy;
+    *dy = sinangle * sx + cosangle * sy;
+
+    *dx += center->x;
+    *dy += center->y;
+}
+
+/* !
+\brief Internal target surface sizing function for rotations with trig result return.
+
+\param width The source surface width.
+\param height The source surface height.
+\param angle The angle to rotate in degrees.
+\param dstwidth The calculated width of the destination surface.
+\param dstheight The calculated height of the destination surface.
+\param cangle The sine of the angle
+\param sangle The cosine of the angle
+
+*/
+void
+SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle, const SDL_FPoint *center,
+                               SDL_Rect *rect_dest, double *cangle, double *sangle)
+{
+    int minx, maxx, miny, maxy;
+    double radangle;
+    double x0, x1, x2, x3;
+    double y0, y1, y2, y3;
+    double sinangle;
+    double cosangle;
+
+    radangle = angle * (M_PI / 180.0);
+    sinangle = SDL_sin(radangle);
+    cosangle = SDL_cos(radangle);
+
+    /*
+     * Determine destination width and height by rotating a source box, at pixel center
+     */
+    rotate(0.5,         0.5,          sinangle, cosangle, center, &x0, &y0);
+    rotate(width - 0.5, 0.5,          sinangle, cosangle, center, &x1, &y1);
+    rotate(0.5,         height - 0.5, sinangle, cosangle, center, &x2, &y2);
+    rotate(width - 0.5, height - 0.5, sinangle, cosangle, center, &x3, &y3);
+
+    minx = (int)SDL_floor( SDL_min( SDL_min(x0, x1), SDL_min(x2, x3) ) );
+    maxx = (int)SDL_ceil(  SDL_max( SDL_max(x0, x1), SDL_max(x2, x3) ) );
+
+    miny = (int)SDL_floor( SDL_min( SDL_min(y0, y1), SDL_min(y2, y3) ) );
+    maxy = (int)SDL_ceil(  SDL_max( SDL_max(y0, y1), SDL_max(y2, y3) ) );
+
+    rect_dest->w = maxx - minx;
+    rect_dest->h = maxy - miny;
+    rect_dest->x = minx;
+    rect_dest->y = miny;
+
+    /* reverse the angle because our rotations are clockwise */
+    *sangle = -sinangle;
+    *cangle = cosangle;
+
+    {
+        /* The trig code below gets the wrong size (due to FP inaccuracy?) when angle is a multiple of 90 degrees */
+        int angle90 = (int)(angle/90);
+        if(angle90 == angle/90) { /* if the angle is a multiple of 90 degrees */
+            angle90 %= 4;
+            if(angle90 < 0) angle90 += 4; /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */
+            if(angle90 & 1) {
+                rect_dest->w  = height;
+                rect_dest->h = width;
+                *cangle = 0;
+                *sangle = angle90 == 1 ? -1 : 1; /* reversed because our rotations are clockwise */
+            } else {
+                rect_dest->w  = width;
+                rect_dest->h = height;
+                *cangle = angle90 == 0 ? 1 : -1;
+                *sangle = 0;
+            }
+        }
+    }
+}
+
+/* Computes source pointer X/Y increments for a rotation that's a multiple of 90 degrees. */
+static void
+computeSourceIncrements90(SDL_Surface * src, int bpp, int angle, int flipx, int flipy,
+                          int *sincx, int *sincy, int *signx, int *signy)
+{
+    int pitch = flipy ? -src->pitch : src->pitch;
+    if (flipx) {
+        bpp = -bpp;
+    }
+    switch (angle) { /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */
+    case 0: *sincx = bpp; *sincy = pitch - src->w * *sincx; *signx = *signy = 1; break;
+    case 1: *sincx = -pitch; *sincy = bpp - *sincx * src->h; *signx = 1; *signy = -1; break;
+    case 2: *sincx = -bpp; *sincy = -src->w * *sincx - pitch; *signx = *signy = -1; break;
+    case 3: default: *sincx = pitch; *sincy = -*sincx * src->h - bpp; *signx = -1; *signy = 1; break;
+    }
+    if (flipx) {
+        *signx = -*signx;
+    }
+    if (flipy) {
+        *signy = -*signy;
+    }
+}
+
+/* Performs a relatively fast rotation/flip when the angle is a multiple of 90 degrees. */
+#define TRANSFORM_SURFACE_90(pixelType) \
+    int dy, dincy = dst->pitch - dst->w*sizeof(pixelType), sincx, sincy, signx, signy;                      \
+    Uint8 *sp = (Uint8*)src->pixels, *dp = (Uint8*)dst->pixels, *de;                                        \
+                                                                                                            \
+    computeSourceIncrements90(src, sizeof(pixelType), angle, flipx, flipy, &sincx, &sincy, &signx, &signy); \
+    if (signx < 0) sp += (src->w-1)*sizeof(pixelType);                                                      \
+    if (signy < 0) sp += (src->h-1)*src->pitch;                                                             \
+                                                                                                            \
+    for (dy = 0; dy < dst->h; sp += sincy, dp += dincy, dy++) {                                             \
+        if (sincx == sizeof(pixelType)) { /* if advancing src and dest equally, use SDL_memcpy */           \
+            SDL_memcpy(dp, sp, dst->w*sizeof(pixelType));                                                   \
+            sp += dst->w*sizeof(pixelType);                                                                 \
+            dp += dst->w*sizeof(pixelType);                                                                 \
+        } else {                                                                                            \
+            for (de = dp + dst->w*sizeof(pixelType); dp != de; sp += sincx, dp += sizeof(pixelType)) {      \
+                *(pixelType*)dp = *(pixelType*)sp;                                                          \
+            }                                                                                               \
+        }                                                                                                   \
+    }
+
+static void
+transformSurfaceRGBA90(SDL_Surface * src, SDL_Surface * dst, int angle, int flipx, int flipy)
+{
+    TRANSFORM_SURFACE_90(tColorRGBA);
+}
+
+static void
+transformSurfaceY90(SDL_Surface * src, SDL_Surface * dst, int angle, int flipx, int flipy)
+{
+    TRANSFORM_SURFACE_90(tColorY);
+}
+
+#undef TRANSFORM_SURFACE_90
+
+/* !
+\brief Internal 32 bit rotozoomer with optional anti-aliasing.
+
+Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
+parameters by scanning the destination surface and applying optionally anti-aliasing
+by bilinear interpolation.
+Assumes src and dst surfaces are of 32 bit depth.
+Assumes dst surface was allocated with the correct dimensions.
+
+\param src Source surface.
+\param dst Destination surface.
+\param isin Integer version of sine of angle.
+\param icos Integer version of cosine of angle.
+\param flipx Flag indicating horizontal mirroring should be applied.
+\param flipy Flag indicating vertical mirroring should be applied.
+\param smooth Flag indicating anti-aliasing should be used.
+\param dst_rect destination coordinates
+\param center true center.
+*/
+static void
+transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int isin, int icos,
+        int flipx, int flipy, int smooth,
+        const SDL_Rect *rect_dest,
+        const SDL_FPoint *center)
+{
+    int sw, sh;
+    int cx, cy;
+    tColorRGBA c00, c01, c10, c11, cswap;
+    tColorRGBA *pc, *sp;
+    int gap;
+    const int fp_half = (1<<15);
+
+    /*
+    * Variable setup
+    */
+    sw = src->w - 1;
+    sh = src->h - 1;
+    pc = (tColorRGBA*) dst->pixels;
+    gap = dst->pitch - dst->w * 4;
+    cx = (int)(center->x * 65536.0);
+    cy = (int)(center->y * 65536.0);
+
+    /*
+    * Switch between interpolating and non-interpolating code
+    */
+    if (smooth) {
+        int y;
+        for (y = 0; y < dst->h; y++) {
+            int x;
+            double src_x = (rect_dest->x + 0 + 0.5 - center->x);
+            double src_y = (rect_dest->y + y + 0.5 - center->y);
+            int sdx = (int)((icos * src_x - isin * src_y) + cx - fp_half);
+            int sdy = (int)((isin * src_x + icos * src_y) + cy - fp_half);
+            for (x = 0; x < dst->w; x++) {
+                int dx = (sdx >> 16);
+                int dy = (sdy >> 16);
+                if (flipx) dx = sw - dx;
+                if (flipy) dy = sh - dy;
+                if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) {
+                    int ex, ey;
+                    int t1, t2;
+                    sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy) + dx;
+                    c00 = *sp;
+                    sp += 1;
+                    c01 = *sp;
+                    sp += (src->pitch/4);
+                    c11 = *sp;
+                    sp -= 1;
+                    c10 = *sp;
+                    if (flipx) {
+                        cswap = c00; c00=c01; c01=cswap;
+                        cswap = c10; c10=c11; c11=cswap;
+                    }
+                    if (flipy) {
+                        cswap = c00; c00=c10; c10=cswap;
+                        cswap = c01; c01=c11; c11=cswap;
+                    }
+                    /*
+                    * Interpolate colors
+                    */
+                    ex = (sdx & 0xffff);
+                    ey = (sdy & 0xffff);
+                    t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
+                    t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
+                    pc->r = (((t2 - t1) * ey) >> 16) + t1;
+                    t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
+                    t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
+                    pc->g = (((t2 - t1) * ey) >> 16) + t1;
+                    t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
+                    t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
+                    pc->b = (((t2 - t1) * ey) >> 16) + t1;
+                    t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
+                    t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
+                    pc->a = (((t2 - t1) * ey) >> 16) + t1;
+                }
+                sdx += icos;
+                sdy += isin;
+                pc++;
+            }
+            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+        }
+    } else {
+        int y;
+        for (y = 0; y < dst->h; y++) {
+            int x;
+            double src_x = (rect_dest->x + 0 + 0.5 - center->x);
+            double src_y = (rect_dest->y + y + 0.5 - center->y);
+            int sdx = (int)((icos * src_x - isin * src_y) + cx - fp_half);
+            int sdy = (int)((isin * src_x + icos * src_y) + cy - fp_half);
+            for (x = 0; x < dst->w; x++) {
+                int dx = (sdx >> 16);
+                int dy = (sdy >> 16);
+                if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) {
+                    if(flipx) dx = sw - dx;
+                    if(flipy) dy = sh - dy;
+                    *pc = *((tColorRGBA *)((Uint8 *)src->pixels + src->pitch * dy) + dx);
+                }
+                sdx += icos;
+                sdy += isin;
+                pc++;
+            }
+            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+        }
+    }
+}
+
+/* !
+
+\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
+
+Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
+parameters by scanning the destination surface.
+Assumes src and dst surfaces are of 8 bit depth.
+Assumes dst surface was allocated with the correct dimensions.
+
+\param src Source surface.
+\param dst Destination surface.
+\param isin Integer version of sine of angle.
+\param icos Integer version of cosine of angle.
+\param flipx Flag indicating horizontal mirroring should be applied.
+\param flipy Flag indicating vertical mirroring should be applied.
+\param dst_rect destination coordinates
+\param center true center.
+*/
+static void
+transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int isin, int icos, int flipx, int flipy,
+        const SDL_Rect *rect_dest,
+        const SDL_FPoint *center)
+{
+    int sw, sh;
+    int cx, cy;
+    tColorY *pc;
+    int gap;
+    const int fp_half = (1<<15);
+    int y;
+
+    /*
+    * Variable setup
+    */
+    sw = src->w - 1;
+    sh = src->h - 1;
+    pc = (tColorY*) dst->pixels;
+    gap = dst->pitch - dst->w;
+    cx = (int)(center->x * 65536.0);
+    cy = (int)(center->y * 65536.0);
+
+    /*
+    * Clear surface to colorkey
+    */
+    SDL_memset(pc, (int)(get_colorkey(src) & 0xff), dst->pitch * dst->h);
+    /*
+    * Iterate through destination surface
+    */
+    for (y = 0; y < dst->h; y++) {
+        int x;
+        double src_x = (rect_dest->x + 0 + 0.5 - center->x);
+        double src_y = (rect_dest->y + y + 0.5 - center->y);
+        int sdx = (int)((icos * src_x - isin * src_y) + cx - fp_half);
+        int sdy = (int)((isin * src_x + icos * src_y) + cy - fp_half);
+        for (x = 0; x < dst->w; x++) {
+            int dx = (sdx >> 16);
+            int dy = (sdy >> 16);
+            if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) {
+                if (flipx) dx = sw - dx;
+                if (flipy) dy = sh- dy;
+                *pc = *((tColorY *)src->pixels + src->pitch * dy + dx);
+            }
+            sdx += icos;
+            sdy += isin;
+            pc++;
+        }
+        pc += gap;
+    }
+}
+
+
+/* !
+\brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
+
+Rotates a 32-bit or 8-bit 'src' surface to newly created 'dst' surface.
+'angle' is the rotation in degrees, 'center' the rotation center. If 'smooth' is set
+then the destination 32-bit surface is anti-aliased. 8-bit surfaces must have a colorkey. 32-bit
+surfaces must have a 8888 layout with red, green, blue and alpha masks (any ordering goes).
+The blend mode of the 'src' surface has some effects on generation of the 'dst' surface: The NONE
+mode will set the BLEND mode on the 'dst' surface. The MOD mode either generates a white 'dst'
+surface and sets the colorkey or fills the it with the colorkey before copying the pixels.
+When using the NONE and MOD modes, color and alpha modulation must be applied before using this function.
+
+\param src The surface to rotozoom.
+\param angle The angle to rotate in degrees.
+\param zoomy The vertical coordinate of the center of rotation
+\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
+\param flipx Set to 1 to flip the image horizontally
+\param flipy Set to 1 to flip the image vertically
+\param rect_dest The destination rect bounding box
+\param cangle The angle cosine
+\param sangle The angle sine
+\param center The true coordinate of the center of rotation
+\return The new rotated surface.
+
+*/
+
+SDL_Surface *
+SDLgfx_rotateSurface(SDL_Surface * src, double angle, int smooth, int flipx, int flipy,
+        const SDL_Rect *rect_dest, double cangle, double sangle, const SDL_FPoint *center)
+{
+    SDL_Surface *rz_dst;
+    int is8bit, angle90;
+    int i;
+    SDL_BlendMode blendmode;
+    Uint32 colorkey = 0;
+    int colorKeyAvailable = SDL_FALSE;
+    double sangleinv, cangleinv;
+
+    /* Sanity check */
+    if (src == NULL)
+        return NULL;
+
+    if (SDL_HasColorKey(src)) {
+        if (SDL_GetColorKey(src, &colorkey) == 0) {
+            colorKeyAvailable = SDL_TRUE;
+        }
+    }
+    /* This function requires a 32-bit surface or 8-bit surface with a colorkey */
+    is8bit = src->format->BitsPerPixel == 8 && colorKeyAvailable;
+    if (!(is8bit || (src->format->BitsPerPixel == 32 && src->format->Amask)))
+        return NULL;
+
+    /* Calculate target factors from sine/cosine and zoom */
+    sangleinv = sangle*65536.0;
+    cangleinv = cangle*65536.0;
+
+    /* Alloc space to completely contain the rotated surface */
+    rz_dst = NULL;
+    if (is8bit) {
+        /* Target surface is 8 bit */
+        rz_dst = SDL_CreateRGBSurfaceWithFormat(0, rect_dest->w, rect_dest->h + GUARD_ROWS, 8, src->format->format);
+        if (rz_dst != NULL) {
+            if (src->format->palette) {
+                for (i = 0; i < src->format->palette->ncolors; i++) {
+                    rz_dst->format->palette->colors[i] = src->format->palette->colors[i];
+                }
+                rz_dst->format->palette->ncolors = src->format->palette->ncolors;
+            }
+        }
+    } else {
+        /* Target surface is 32 bit with source RGBA ordering */
+        rz_dst = SDL_CreateRGBSurface(0, rect_dest->w, rect_dest->h + GUARD_ROWS, 32,
+                                      src->format->Rmask, src->format->Gmask,
+                                      src->format->Bmask, src->format->Amask);
+    }
+
+    /* Check target */
+    if (rz_dst == NULL)
+        return NULL;
+
+    /* Adjust for guard rows */
+    rz_dst->h = rect_dest->h;
+
+    SDL_GetSurfaceBlendMode(src, &blendmode);
+
+    if (colorKeyAvailable == SDL_TRUE) {
+        /* If available, the colorkey will be used to discard the pixels that are outside of the rotated area. */
+        SDL_SetColorKey(rz_dst, SDL_TRUE, colorkey);
+        SDL_FillRect(rz_dst, NULL, colorkey);
+    } else if (blendmode == SDL_BLENDMODE_NONE) {
+        blendmode = SDL_BLENDMODE_BLEND;
+    } else if (blendmode == SDL_BLENDMODE_MOD || blendmode == SDL_BLENDMODE_MUL) {
+        /* Without a colorkey, the target texture has to be white for the MOD and MUL blend mode so
+         * that the pixels outside the rotated area don't affect the destination surface.
+         */
+        colorkey = SDL_MapRGBA(rz_dst->format, 255, 255, 255, 0);
+        SDL_FillRect(rz_dst, NULL, colorkey);
+        /* Setting a white colorkey for the destination surface makes the final blit discard
+         * all pixels outside of the rotated area. This doesn't interfere with anything because
+         * white pixels are already a no-op and the MOD blend mode does not interact with alpha.
+         */
+        SDL_SetColorKey(rz_dst, SDL_TRUE, colorkey);
+    }
+
+    SDL_SetSurfaceBlendMode(rz_dst, blendmode);
+
+    /* Lock source surface */
+    if (SDL_MUSTLOCK(src)) {
+        SDL_LockSurface(src);
+    }
+
+    /* check if the rotation is a multiple of 90 degrees so we can take a fast path and also somewhat reduce
+     * the off-by-one problem in transformSurfaceRGBA that expresses itself when the rotation is near
+     * multiples of 90 degrees.
+     */
+    angle90 = (int)(angle/90);
+    if (angle90 == angle/90) {
+        angle90 %= 4;
+        if (angle90 < 0) angle90 += 4; /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */
+    } else {
+        angle90 = -1;
+    }
+
+    if (is8bit) {
+        /* Call the 8-bit transformation routine to do the rotation */
+        if(angle90 >= 0) {
+            transformSurfaceY90(src, rz_dst, angle90, flipx, flipy);
+        } else {
+            transformSurfaceY(src, rz_dst, (int)sangleinv, (int)cangleinv,
+                              flipx, flipy, rect_dest, center);
+        }
+    } else {
+        /* Call the 32-bit transformation routine to do the rotation */
+        if (angle90 >= 0) {
+            transformSurfaceRGBA90(src, rz_dst, angle90, flipx, flipy);
+        } else {
+            transformSurfaceRGBA(src, rz_dst, (int)sangleinv, (int)cangleinv,
+                                  flipx, flipy, smooth, rect_dest, center);
+        }
+    }
+
+    /* Unlock source surface */
+    if (SDL_MUSTLOCK(src)) {
+        SDL_UnlockSurface(src);
+    }
+
+    /* Return rotated surface */
+    return rz_dst;
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */

+ 30 - 0
src/render/ps2/SDL_rotate.h

@@ -0,0 +1,30 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_rotate_h_
+#define SDL_rotate_h_
+
+extern SDL_Surface *SDLgfx_rotateSurface(SDL_Surface * src, double angle, int smooth, int flipx, int flipy,
+        const SDL_Rect *rect_dest, double cangle, double sangle, const SDL_FPoint *center);
+extern void SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle, const SDL_FPoint *center,
+        SDL_Rect *rect_dest, double *cangle, double *sangle);
+
+#endif /* SDL_rotate_h_ */

+ 888 - 0
src/render/ps2/SDL_triangle.c

@@ -0,0 +1,888 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_PS2 && !SDL_RENDER_DISABLED
+
+#include "SDL_surface.h"
+#include "SDL_triangle.h"
+
+#include "../../video/SDL_blit.h"
+
+/* fixed points bits precision
+ * Set to 1, so that it can start rendering wth middle of a pixel precision.
+ * It doesn't need to be increased.
+ * But, if increased too much, it overflows (srcx, srcy) coordinates used for filling with texture.
+ * (which could be turned to int64).
+ */
+#define FP_BITS   1
+
+
+#define COLOR_EQ(c1, c2)    ((c1).r == (c2).r && (c1).g == (c2).g && (c1).b == (c2).b && (c1).a == (c2).a)
+
+static void SDL_BlitTriangle_Slow(SDL_BlitInfo * info,
+        SDL_Point s2_x_area, SDL_Rect dstrect, int area, int bias_w0, int bias_w1, int bias_w2,
+    int d2d1_y, int d1d2_x, int d0d2_y, int d2d0_x, int d1d0_y, int d0d1_x,
+    int s2s0_x, int s2s1_x, int s2s0_y, int s2s1_y, int w0_row, int w1_row, int w2_row,
+    SDL_Color c0, SDL_Color c1, SDL_Color c2, int is_uniform);
+
+#if 0
+int SDL_BlitTriangle(SDL_Surface *src, const SDL_Point srcpoints[3], SDL_Surface *dst, const SDL_Point dstpoints[3])
+{
+    int i;
+    SDL_Point points[6];
+
+    if (src == NULL || dst == NULL) {
+        return -1;
+    }
+
+    for (i = 0; i < 3; i++) {
+        if (srcpoints[i].x < 0 || srcpoints[i].y < 0 || srcpoints[i].x >= src->w || srcpoints[i].y >= src->h) {
+            return SDL_SetError("Values of 'srcpoints' out of bounds");
+        }
+    }
+
+    points[0] = srcpoints[0];
+    points[1] = dstpoints[0];
+    points[2] = srcpoints[1];
+    points[3] = dstpoints[1];
+    points[4] = srcpoints[2];
+    points[5] = dstpoints[2];
+    for (i = 0; i < 3; i++) {
+        trianglepoint_2_fixedpoint(&points[2 * i + 1]);
+    }
+    return SDL_SW_BlitTriangle(src, dst, points);
+}
+
+int SDL_FillTriangle(SDL_Surface *dst, const SDL_Point points[3], Uint32 color)
+{
+    int i;
+    SDL_Point points_tmp[3];
+    if (dst == NULL) {
+        return -1;
+    }
+    for (i = 0; i < 3; i++) {
+        points_tmp[i] = points[i];
+        trianglepoint_2_fixedpoint(&points_tmp[i]);
+    }
+    return SDL_SW_FillTriangle(dst, points_tmp, SDL_BLENDMODE_NONE, color);
+}
+#endif
+
+/* cross product AB x AC */
+static int cross_product(const SDL_Point *a, const SDL_Point *b, int c_x, int c_y)
+{
+    return (b->x - a->x) * (c_y - a->y) - (b->y - a->y) * (c_x - a->x);
+}
+
+/* check for top left rules */
+static int is_top_left(const SDL_Point *a, const SDL_Point *b, int is_clockwise)
+{
+    if (is_clockwise) {
+        if (a->y == b->y && a->x < b->x) {
+            return 1;
+        }
+        if (b->y < a->y) {
+            return 1;
+        }
+    } else {
+        if (a->y == b->y && b->x < a->x) {
+            return 1;
+        }
+        if (a->y < b->y) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+void trianglepoint_2_fixedpoint(SDL_Point *a) {
+    a->x <<= FP_BITS;
+    a->y <<= FP_BITS;
+}
+
+/* bounding rect of three points (in fixed point) */
+static void bounding_rect_fixedpoint(const SDL_Point *a, const SDL_Point *b, const SDL_Point *c, SDL_Rect *r)
+{
+    int min_x = SDL_min(a->x, SDL_min(b->x, c->x));
+    int max_x = SDL_max(a->x, SDL_max(b->x, c->x));
+    int min_y = SDL_min(a->y, SDL_min(b->y, c->y));
+    int max_y = SDL_max(a->y, SDL_max(b->y, c->y));
+    /* points are in fixed point, shift back */
+    r->x = min_x >> FP_BITS;
+    r->y = min_y >> FP_BITS;
+    r->w = (max_x - min_x) >> FP_BITS;
+    r->h = (max_y - min_y) >> FP_BITS;
+}
+
+/* bounding rect of three points */
+static void bounding_rect(const SDL_Point *a, const SDL_Point *b, const SDL_Point *c, SDL_Rect *r)
+{
+    int min_x = SDL_min(a->x, SDL_min(b->x, c->x));
+    int max_x = SDL_max(a->x, SDL_max(b->x, c->x));
+    int min_y = SDL_min(a->y, SDL_min(b->y, c->y));
+    int max_y = SDL_max(a->y, SDL_max(b->y, c->y));
+    r->x = min_x;
+    r->y = min_y;
+    r->w = (max_x - min_x);
+    r->h = (max_y - min_y);
+}
+
+
+/* Triangle rendering, using Barycentric coordinates (w0, w1, w2)
+ *
+ * The cross product isn't computed from scratch at each iteration,
+ * but optimized using constant step increments
+ *
+ */
+
+#define TRIANGLE_BEGIN_LOOP                                                                             \
+    {                                                                                                   \
+        int x, y;                                                                                       \
+        for (y = 0; y < dstrect.h; y++) {                                                               \
+            /* y start */                                                                               \
+            int w0 = w0_row;                                                                            \
+            int w1 = w1_row;                                                                            \
+            int w2 = w2_row;                                                                            \
+            for (x = 0; x < dstrect.w; x++) {                                                           \
+                /* In triangle */                                                                       \
+                if (w0 + bias_w0 >= 0 && w1 + bias_w1 >= 0 && w2 + bias_w2 >= 0) {                      \
+                    Uint8 *dptr = (Uint8 *) dst_ptr + x * dstbpp;                                       \
+
+
+/* Use 64 bits precision to prevent overflow when interpolating color / texture with wide triangles */
+#define TRIANGLE_GET_TEXTCOORD                                                                          \
+                    int srcx = (int)(((Sint64)w0 * s2s0_x + (Sint64)w1 * s2s1_x + s2_x_area.x) / area); \
+                    int srcy = (int)(((Sint64)w0 * s2s0_y + (Sint64)w1 * s2s1_y + s2_x_area.y) / area); \
+
+#define TRIANGLE_GET_MAPPED_COLOR                                                                       \
+                    int r = (int)(((Sint64)w0 * c0.r + (Sint64)w1 * c1.r + (Sint64)w2 * c2.r) / area);  \
+                    int g = (int)(((Sint64)w0 * c0.g + (Sint64)w1 * c1.g + (Sint64)w2 * c2.g) / area);  \
+                    int b = (int)(((Sint64)w0 * c0.b + (Sint64)w1 * c1.b + (Sint64)w2 * c2.b) / area);  \
+                    int a = (int)(((Sint64)w0 * c0.a + (Sint64)w1 * c1.a + (Sint64)w2 * c2.a) / area);  \
+                    int color = SDL_MapRGBA(format, r, g, b, a);                                        \
+
+#define TRIANGLE_GET_COLOR                                                                              \
+                    int r = (int)(((Sint64)w0 * c0.r + (Sint64)w1 * c1.r + (Sint64)w2 * c2.r) / area);  \
+                    int g = (int)(((Sint64)w0 * c0.g + (Sint64)w1 * c1.g + (Sint64)w2 * c2.g) / area);  \
+                    int b = (int)(((Sint64)w0 * c0.b + (Sint64)w1 * c1.b + (Sint64)w2 * c2.b) / area);  \
+                    int a = (int)(((Sint64)w0 * c0.a + (Sint64)w1 * c1.a + (Sint64)w2 * c2.a) / area);  \
+
+
+#define TRIANGLE_END_LOOP                                                                               \
+                }                                                                                       \
+                /* x += 1 */                                                                            \
+                w0 += d2d1_y;                                                                           \
+                w1 += d0d2_y;                                                                           \
+                w2 += d1d0_y;                                                                           \
+            }                                                                                           \
+            /* y += 1 */                                                                                \
+            w0_row += d1d2_x;                                                                           \
+            w1_row += d2d0_x;                                                                           \
+            w2_row += d0d1_x;                                                                           \
+            dst_ptr += dst_pitch;                                                                       \
+        }                                                                                               \
+    }                                                                                                   \
+
+int SDL_SW_FillTriangle(SDL_Surface *dst, SDL_Point *d0, SDL_Point *d1, SDL_Point *d2, SDL_BlendMode blend, SDL_Color c0, SDL_Color c1, SDL_Color c2)
+{
+    int ret = 0;
+    int dst_locked = 0;
+
+    SDL_Rect dstrect;
+
+    int dstbpp;
+    Uint8 *dst_ptr;
+    int dst_pitch;
+
+    int area, is_clockwise;
+
+    int d2d1_y, d1d2_x, d0d2_y, d2d0_x, d1d0_y, d0d1_x;
+    int w0_row, w1_row, w2_row;
+    int bias_w0, bias_w1, bias_w2;
+
+    int is_uniform;
+
+    SDL_Surface *tmp = NULL;
+
+    if (dst == NULL) {
+        return -1;
+    }
+
+    area = cross_product(d0, d1, d2->x, d2->y);
+
+    is_uniform = COLOR_EQ(c0, c1) && COLOR_EQ(c1, c2);
+
+    /* Flat triangle */
+    if (area == 0) {
+        return 0;
+    }
+
+    /* Lock the destination, if needed */
+    if (SDL_MUSTLOCK(dst)) {
+        if (SDL_LockSurface(dst) < 0) {
+            ret = -1;
+            goto end;
+        } else {
+            dst_locked = 1;
+        }
+    }
+
+    bounding_rect_fixedpoint(d0, d1, d2, &dstrect);
+
+    {
+        /* Clip triangle rect with surface rect */
+        SDL_Rect rect;
+        rect.x = 0;
+        rect.y = 0;
+        rect.w = dst->w;
+        rect.h = dst->h;
+        SDL_IntersectRect(&dstrect, &rect, &dstrect);
+    }
+
+    {
+        /* Clip triangle with surface clip rect */
+        SDL_Rect rect;
+        SDL_GetClipRect(dst, &rect);
+        SDL_IntersectRect(&dstrect, &rect, &dstrect);
+    }
+
+
+    if (blend != SDL_BLENDMODE_NONE) {
+        int format = dst->format->format;
+
+        /* need an alpha format */
+        if (! dst->format->Amask) {
+            format = SDL_PIXELFORMAT_ARGB8888;
+        }
+
+        /* Use an intermediate surface */
+        tmp = SDL_CreateRGBSurfaceWithFormat(0, dstrect.w, dstrect.h, 0, format);
+        if (tmp == NULL) {
+            ret = -1;
+            goto end;
+        }
+
+        if (blend == SDL_BLENDMODE_MOD) {
+            Uint32 c = SDL_MapRGBA(tmp->format, 255, 255, 255, 255);
+            SDL_FillRect(tmp, NULL, c);
+        }
+
+        SDL_SetSurfaceBlendMode(tmp, blend);
+
+        dstbpp = tmp->format->BytesPerPixel;
+        dst_ptr = tmp->pixels;
+        dst_pitch = tmp->pitch;
+
+    } else {
+        /* Write directly to destination surface */
+        dstbpp = dst->format->BytesPerPixel;
+        dst_ptr = (Uint8 *)dst->pixels + dstrect.x * dstbpp + dstrect.y * dst->pitch;
+        dst_pitch = dst->pitch;
+    }
+
+    is_clockwise = area > 0;
+    area = SDL_abs(area);
+
+    d2d1_y = (d1->y - d2->y) << FP_BITS;
+    d0d2_y = (d2->y - d0->y) << FP_BITS;
+    d1d0_y = (d0->y - d1->y) << FP_BITS;
+    d1d2_x = (d2->x - d1->x) << FP_BITS;
+    d2d0_x = (d0->x - d2->x) << FP_BITS;
+    d0d1_x = (d1->x - d0->x) << FP_BITS;
+
+    /* Starting point for rendering, at the middle of a pixel */
+    {
+        SDL_Point p;
+        p.x = dstrect.x;
+        p.y = dstrect.y;
+        trianglepoint_2_fixedpoint(&p);
+        p.x += (1 << FP_BITS) / 2;
+        p.y += (1 << FP_BITS) / 2;
+        w0_row = cross_product(d1, d2, p.x, p.y);
+        w1_row = cross_product(d2, d0, p.x, p.y);
+        w2_row = cross_product(d0, d1, p.x, p.y);
+    }
+
+    /* Handle anti-clockwise triangles */
+    if (! is_clockwise) {
+        d2d1_y *= -1;
+        d0d2_y *= -1;
+        d1d0_y *= -1;
+        d1d2_x *= -1;
+        d2d0_x *= -1;
+        d0d1_x *= -1;
+        w0_row *= -1;
+        w1_row *= -1;
+        w2_row *= -1;
+    }
+
+    /* Add a bias to respect top-left rasterization rule */
+    bias_w0 = (is_top_left(d1, d2, is_clockwise) ? 0 : -1);
+    bias_w1 = (is_top_left(d2, d0, is_clockwise) ? 0 : -1);
+    bias_w2 = (is_top_left(d0, d1, is_clockwise) ? 0 : -1);
+
+    if (is_uniform) {
+        Uint32 color;
+        if (tmp) {
+            if (dst->format->Amask) {
+                color = SDL_MapRGBA(tmp->format, c0.r, c0.g, c0.b, c0.a);
+            } else {
+                //color = SDL_MapRGB(tmp->format, c0.r, c0.g, c0.b);
+                color = SDL_MapRGBA(tmp->format, c0.r, c0.g, c0.b, c0.a);
+            }
+        } else {
+            color = SDL_MapRGBA(dst->format, c0.r, c0.g, c0.b, c0.a);
+        }
+
+        if (dstbpp == 4) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                *(Uint32 *)dptr = color;
+            }
+            TRIANGLE_END_LOOP
+        } else if (dstbpp == 3) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                Uint8 *s = (Uint8*)&color;
+                dptr[0] = s[0];
+                dptr[1] = s[1];
+                dptr[2] = s[2];
+            }
+            TRIANGLE_END_LOOP
+        } else if (dstbpp == 2) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                *(Uint16 *)dptr = color;
+            }
+            TRIANGLE_END_LOOP
+        } else if (dstbpp == 1) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                *dptr = color;
+            }
+            TRIANGLE_END_LOOP
+        }
+    } else {
+        SDL_PixelFormat *format = dst->format;
+        if (tmp) {
+            format = tmp->format;
+        }
+        if (dstbpp == 4) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                TRIANGLE_GET_MAPPED_COLOR
+                *(Uint32 *)dptr = color;
+            }
+            TRIANGLE_END_LOOP
+        } else if (dstbpp == 3) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                TRIANGLE_GET_MAPPED_COLOR
+                Uint8 *s = (Uint8*)&color;
+                dptr[0] = s[0];
+                dptr[1] = s[1];
+                dptr[2] = s[2];
+            }
+            TRIANGLE_END_LOOP
+        } else if (dstbpp == 2) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                TRIANGLE_GET_MAPPED_COLOR
+                *(Uint16 *)dptr = color;
+            }
+            TRIANGLE_END_LOOP
+        } else if (dstbpp == 1) {
+            TRIANGLE_BEGIN_LOOP
+            {
+                TRIANGLE_GET_MAPPED_COLOR
+                *dptr = color;
+            }
+            TRIANGLE_END_LOOP
+        }
+    }
+
+    if (tmp) {
+        SDL_BlitSurface(tmp, NULL, dst, &dstrect);
+        SDL_FreeSurface(tmp);
+    }
+
+end:
+    if (dst_locked) {
+        SDL_UnlockSurface(dst);
+    }
+
+    return ret;
+}
+
+
+
+
+
+
+
+int SDL_SW_BlitTriangle(
+        SDL_Surface *src,
+        SDL_Point *s0, SDL_Point *s1, SDL_Point *s2,
+        SDL_Surface *dst,
+        SDL_Point *d0, SDL_Point *d1, SDL_Point *d2,
+        SDL_Color c0, SDL_Color c1, SDL_Color c2)
+{
+    int ret = 0;
+    int src_locked = 0;
+    int dst_locked = 0;
+
+    SDL_BlendMode blend;
+
+    SDL_Rect dstrect;
+
+    SDL_Point s2_x_area;
+
+    int dstbpp;
+    Uint8 *dst_ptr;
+    int dst_pitch;
+
+    int *src_ptr;
+    int src_pitch;
+
+    int area, is_clockwise;
+
+    int d2d1_y, d1d2_x, d0d2_y, d2d0_x, d1d0_y, d0d1_x;
+    int s2s0_x, s2s1_x, s2s0_y, s2s1_y;
+
+    int w0_row, w1_row, w2_row;
+    int bias_w0, bias_w1, bias_w2;
+
+    int is_uniform;
+
+    int has_modulation;
+
+    if (src == NULL || dst == NULL) {
+        return -1;
+    }
+
+    area = cross_product(d0, d1, d2->x, d2->y);
+
+    /* Flat triangle */
+    if (area == 0) {
+        return 0;
+    }
+
+    /* Lock the destination, if needed */
+    if (SDL_MUSTLOCK(dst)) {
+        if (SDL_LockSurface(dst) < 0) {
+            ret = -1;
+            goto end;
+        } else {
+            dst_locked = 1;
+        }
+    }
+
+    /* Lock the source, if needed */
+    if (SDL_MUSTLOCK(src)) {
+        if (SDL_LockSurface(src) < 0) {
+            ret = -1;
+            goto end;
+        } else {
+            src_locked = 1;
+        }
+    }
+
+    is_uniform = COLOR_EQ(c0, c1) && COLOR_EQ(c1, c2);
+
+    bounding_rect_fixedpoint(d0, d1, d2, &dstrect);
+
+    SDL_GetSurfaceBlendMode(src, &blend);
+
+    /* TRIANGLE_GET_TEXTCOORD interpolates up to the max values included, so reduce by 1 */
+    {
+        SDL_Rect srcrect;
+        int maxx, maxy;
+        bounding_rect(s0, s1, s2, &srcrect);
+        maxx = srcrect.x + srcrect.w;
+        maxy = srcrect.y + srcrect.h;
+        if (srcrect.w > 0) {
+            if (s0->x == maxx) s0->x--;
+            if (s1->x == maxx) s1->x--;
+            if (s2->x == maxx) s2->x--;
+        }
+        if (srcrect.h > 0) {
+            if (s0->y == maxy) s0->y--;
+            if (s1->y == maxy) s1->y--;
+            if (s2->y == maxy) s2->y--;
+        }
+    }
+
+    if (is_uniform) {
+        // SDL_GetSurfaceColorMod(src, &r, &g, &b);
+        has_modulation = c0.r != 255 || c0.g != 255 || c0.b != 255 || c0.a != 255;;
+    } else {
+        has_modulation = SDL_TRUE;
+    }
+
+    {
+        /* Clip triangle rect with surface rect */
+        SDL_Rect rect;
+        rect.x = 0;
+        rect.y = 0;
+        rect.w = dst->w;
+        rect.h = dst->h;
+
+        SDL_IntersectRect(&dstrect, &rect, &dstrect);
+    }
+
+    {
+        /* Clip triangle with surface clip rect */
+        SDL_Rect rect;
+        SDL_GetClipRect(dst, &rect);
+        SDL_IntersectRect(&dstrect, &rect, &dstrect);
+    }
+
+    /* Set destination pointer */
+    dstbpp = dst->format->BytesPerPixel;
+    dst_ptr = (Uint8 *)dst->pixels + dstrect.x * dstbpp + dstrect.y * dst->pitch;
+    dst_pitch = dst->pitch;
+
+    /* Set source pointer */
+    src_ptr = src->pixels;
+    src_pitch = src->pitch;
+
+    is_clockwise = area > 0;
+    area = SDL_abs(area);
+
+    d2d1_y = (d1->y - d2->y) << FP_BITS;
+    d0d2_y = (d2->y - d0->y) << FP_BITS;
+    d1d0_y = (d0->y - d1->y) << FP_BITS;
+
+
+    d1d2_x = (d2->x - d1->x) << FP_BITS;
+    d2d0_x = (d0->x - d2->x) << FP_BITS;
+    d0d1_x = (d1->x - d0->x) << FP_BITS;
+
+    s2s0_x = s0->x - s2->x;
+    s2s1_x = s1->x - s2->x;
+    s2s0_y = s0->y - s2->y;
+    s2s1_y = s1->y - s2->y;
+
+    /* Starting point for rendering, at the middle of a pixel */
+    {
+        SDL_Point p;
+        p.x = dstrect.x;
+        p.y = dstrect.y;
+        trianglepoint_2_fixedpoint(&p);
+        p.x += (1 << FP_BITS) / 2;
+        p.y += (1 << FP_BITS) / 2;
+        w0_row = cross_product(d1, d2, p.x, p.y);
+        w1_row = cross_product(d2, d0, p.x, p.y);
+        w2_row = cross_product(d0, d1, p.x, p.y);
+    }
+
+    /* Handle anti-clockwise triangles */
+    if (! is_clockwise) {
+        d2d1_y *= -1;
+        d0d2_y *= -1;
+        d1d0_y *= -1;
+        d1d2_x *= -1;
+        d2d0_x *= -1;
+        d0d1_x *= -1;
+        w0_row *= -1;
+        w1_row *= -1;
+        w2_row *= -1;
+    }
+
+    /* Add a bias to respect top-left rasterization rule */
+    bias_w0 = (is_top_left(d1, d2, is_clockwise) ? 0 : -1);
+    bias_w1 = (is_top_left(d2, d0, is_clockwise) ? 0 : -1);
+    bias_w2 = (is_top_left(d0, d1, is_clockwise) ? 0 : -1);
+
+    /* precompute constant 's2->x * area' used in TRIANGLE_GET_TEXTCOORD */
+    s2_x_area.x = s2->x * area;
+    s2_x_area.y = s2->y * area;
+
+    if (blend != SDL_BLENDMODE_NONE || src->format->format != dst->format->format || has_modulation || ! is_uniform) {
+        /* Use SDL_BlitTriangle_Slow */
+
+        SDL_BlitInfo *info = &src->map->info;
+        SDL_BlitInfo tmp_info;
+
+        SDL_zero(tmp_info);
+
+        tmp_info.src_fmt = src->format;
+        tmp_info.dst_fmt = dst->format;
+        tmp_info.flags = info->flags;
+        /*
+        tmp_info.r = info->r;
+        tmp_info.g = info->g;
+        tmp_info.b = info->b;
+        tmp_info.a = info->a;
+        */
+        tmp_info.r = c0.r;
+        tmp_info.g = c0.g;
+        tmp_info.b = c0.b;
+        tmp_info.a = c0.a;
+
+
+        tmp_info.flags &= ~(SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA);
+
+        if (c0.r != 255  || c1.r != 255 || c2.r != 255 ||
+                c0.g != 255  || c1.g != 255 || c2.g != 255 ||
+                c0.b != 255  || c1.b != 255 || c2.b != 255) {
+            tmp_info.flags |= SDL_COPY_MODULATE_COLOR;
+        }
+
+        if (c0.a != 255 || c1.a != 255 || c2.a != 255) {
+            tmp_info.flags |= SDL_COPY_MODULATE_ALPHA;
+        }
+
+        tmp_info.colorkey = info->colorkey;
+
+        /* src */
+        tmp_info.src = (Uint8 *) src_ptr;
+        tmp_info.src_pitch = src_pitch;
+
+        /* dst */
+        tmp_info.dst = (Uint8 *) dst_ptr;
+        tmp_info.dst_pitch = dst_pitch;
+
+        SDL_BlitTriangle_Slow(&tmp_info, s2_x_area, dstrect, area, bias_w0, bias_w1, bias_w2,
+                d2d1_y, d1d2_x, d0d2_y, d2d0_x, d1d0_y, d0d1_x,
+                s2s0_x, s2s1_x, s2s0_y, s2s1_y, w0_row, w1_row, w2_row,
+                c0, c1, c2, is_uniform);
+
+        goto end;
+    }
+
+    if (dstbpp == 4) {
+        TRIANGLE_BEGIN_LOOP
+        {
+            TRIANGLE_GET_TEXTCOORD
+            Uint32 *sptr = (Uint32 *)((Uint8 *) src_ptr + srcy * src_pitch);
+            *(Uint32 *)dptr = sptr[srcx];
+        }
+        TRIANGLE_END_LOOP
+    } else if (dstbpp == 3) {
+        TRIANGLE_BEGIN_LOOP
+        {
+            TRIANGLE_GET_TEXTCOORD
+            Uint8 *sptr = (Uint8 *)((Uint8 *) src_ptr + srcy * src_pitch);
+            dptr[0] = sptr[3 * srcx];
+            dptr[1] = sptr[3 * srcx + 1];
+            dptr[2] = sptr[3 * srcx + 2];
+        }
+        TRIANGLE_END_LOOP
+    } else if (dstbpp == 2) {
+        TRIANGLE_BEGIN_LOOP
+        {
+            TRIANGLE_GET_TEXTCOORD
+            Uint16 *sptr = (Uint16 *)((Uint8 *) src_ptr + srcy * src_pitch);
+            *(Uint16 *)dptr = sptr[srcx];
+        }
+        TRIANGLE_END_LOOP
+    } else if (dstbpp == 1) {
+        TRIANGLE_BEGIN_LOOP
+        {
+            TRIANGLE_GET_TEXTCOORD
+            Uint8 *sptr = (Uint8 *)((Uint8 *) src_ptr + srcy * src_pitch);
+            *dptr = sptr[srcx];
+        }
+        TRIANGLE_END_LOOP
+    }
+
+end:
+    if (dst_locked) {
+        SDL_UnlockSurface(dst);
+    }
+    if (src_locked) {
+        SDL_UnlockSurface(src);
+    }
+
+    return ret;
+}
+
+
+#define FORMAT_ALPHA      0
+#define FORMAT_NO_ALPHA  -1
+#define FORMAT_2101010    1
+#define FORMAT_HAS_ALPHA(format)        format == 0
+#define FORMAT_HAS_NO_ALPHA(format)     format < 0
+static int SDL_INLINE detect_format(SDL_PixelFormat *pf) {
+    if (pf->format == SDL_PIXELFORMAT_ARGB2101010) {
+        return FORMAT_2101010;
+    } else if (pf->Amask) {
+        return FORMAT_ALPHA;
+    } else {
+        return FORMAT_NO_ALPHA;
+    }
+}
+
+static void
+SDL_BlitTriangle_Slow(SDL_BlitInfo *info,
+        SDL_Point s2_x_area, SDL_Rect dstrect, int area, int bias_w0, int bias_w1, int bias_w2,
+    int d2d1_y, int d1d2_x, int d0d2_y, int d2d0_x, int d1d0_y, int d0d1_x,
+    int s2s0_x, int s2s1_x, int s2s0_y, int s2s1_y, int w0_row, int w1_row, int w2_row,
+    SDL_Color c0, SDL_Color c1, SDL_Color c2, int is_uniform)
+{
+    const int flags = info->flags;
+    Uint32 modulateR = info->r;
+    Uint32 modulateG = info->g;
+    Uint32 modulateB = info->b;
+    Uint32 modulateA = info->a;
+    Uint32 srcpixel;
+    Uint32 srcR, srcG, srcB, srcA;
+    Uint32 dstpixel;
+    Uint32 dstR, dstG, dstB, dstA;
+    SDL_PixelFormat *src_fmt = info->src_fmt;
+    SDL_PixelFormat *dst_fmt = info->dst_fmt;
+    int srcbpp = src_fmt->BytesPerPixel;
+    int dstbpp = dst_fmt->BytesPerPixel;
+    int srcfmt_val;
+    int dstfmt_val;
+    Uint32 rgbmask = ~src_fmt->Amask;
+    Uint32 ckey = info->colorkey & rgbmask;
+
+    Uint8 *dst_ptr = info->dst;
+    int dst_pitch = info->dst_pitch;;
+
+    srcfmt_val = detect_format(src_fmt);
+    dstfmt_val = detect_format(dst_fmt);
+
+    TRIANGLE_BEGIN_LOOP
+    {
+        Uint8 *src;
+        Uint8 *dst = dptr;
+        TRIANGLE_GET_TEXTCOORD
+        src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp));
+        if (FORMAT_HAS_ALPHA(srcfmt_val)) {
+            DISEMBLE_RGBA(src, srcbpp, src_fmt, srcpixel, srcR, srcG, srcB, srcA);
+        } else if (FORMAT_HAS_NO_ALPHA(srcfmt_val)) {
+            DISEMBLE_RGB(src, srcbpp, src_fmt, srcpixel, srcR, srcG, srcB);
+            srcA = 0xFF;
+        } else {
+            /* SDL_PIXELFORMAT_ARGB2101010 */
+            srcpixel = *((Uint32 *)(src));
+            RGBA_FROM_ARGB2101010(srcpixel, srcR, srcG, srcB, srcA);
+        }
+        if (flags & SDL_COPY_COLORKEY) {
+            /* srcpixel isn't set for 24 bpp */
+            if (srcbpp == 3) {
+                srcpixel = (srcR << src_fmt->Rshift) |
+                    (srcG << src_fmt->Gshift) | (srcB << src_fmt->Bshift);
+            }
+            if ((srcpixel & rgbmask) == ckey) {
+                continue;
+            }
+        }
+        if (FORMAT_HAS_ALPHA(dstfmt_val)) {
+            DISEMBLE_RGBA(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB, dstA);
+        } else if (FORMAT_HAS_NO_ALPHA(dstfmt_val)) {
+            DISEMBLE_RGB(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB);
+            dstA = 0xFF;
+        } else {
+            /* SDL_PIXELFORMAT_ARGB2101010 */
+            dstpixel = *((Uint32 *)(dst));
+            RGBA_FROM_ARGB2101010(dstpixel, dstR, dstG, dstB, dstA);
+        }
+
+        if (! is_uniform) {
+            TRIANGLE_GET_COLOR
+            modulateR = r;
+            modulateG = g;
+            modulateB = b;
+            modulateA = a;
+        }
+
+        if (flags & SDL_COPY_MODULATE_COLOR) {
+            srcR = (srcR * modulateR) / 255;
+            srcG = (srcG * modulateG) / 255;
+            srcB = (srcB * modulateB) / 255;
+        }
+        if (flags & SDL_COPY_MODULATE_ALPHA) {
+            srcA = (srcA * modulateA) / 255;
+        }
+        if (flags & (SDL_COPY_BLEND | SDL_COPY_ADD)) {
+            /* This goes away if we ever use premultiplied alpha */
+            if (srcA < 255) {
+                srcR = (srcR * srcA) / 255;
+                srcG = (srcG * srcA) / 255;
+                srcB = (srcB * srcA) / 255;
+            }
+        }
+        switch (flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) {
+        case 0:
+            dstR = srcR;
+            dstG = srcG;
+            dstB = srcB;
+            dstA = srcA;
+            break;
+        case SDL_COPY_BLEND:
+            dstR = srcR + ((255 - srcA) * dstR) / 255;
+            dstG = srcG + ((255 - srcA) * dstG) / 255;
+            dstB = srcB + ((255 - srcA) * dstB) / 255;
+            dstA = srcA + ((255 - srcA) * dstA) / 255;
+            break;
+        case SDL_COPY_ADD:
+            dstR = srcR + dstR;
+            if (dstR > 255)
+                dstR = 255;
+            dstG = srcG + dstG;
+            if (dstG > 255)
+                dstG = 255;
+            dstB = srcB + dstB;
+            if (dstB > 255)
+                dstB = 255;
+            break;
+        case SDL_COPY_MOD:
+            dstR = (srcR * dstR) / 255;
+            dstG = (srcG * dstG) / 255;
+            dstB = (srcB * dstB) / 255;
+            break;
+        case SDL_COPY_MUL:
+            dstR = ((srcR * dstR) + (dstR * (255 - srcA))) / 255;
+            if (dstR > 255)
+                dstR = 255;
+            dstG = ((srcG * dstG) + (dstG * (255 - srcA))) / 255;
+            if (dstG > 255)
+                dstG = 255;
+            dstB = ((srcB * dstB) + (dstB * (255 - srcA))) / 255;
+            if (dstB > 255)
+                dstB = 255;
+            dstA = ((srcA * dstA) + (dstA * (255 - srcA))) / 255;
+            if (dstA > 255)
+                dstA = 255;
+            break;
+        }
+        if (FORMAT_HAS_ALPHA(dstfmt_val)) {
+            ASSEMBLE_RGBA(dst, dstbpp, dst_fmt, dstR, dstG, dstB, dstA);
+        } else if (FORMAT_HAS_NO_ALPHA(dstfmt_val)) {
+            ASSEMBLE_RGB(dst, dstbpp, dst_fmt, dstR, dstG, dstB);
+        } else {
+            /* SDL_PIXELFORMAT_ARGB2101010 */
+            Uint32 pixel;
+            ARGB2101010_FROM_RGBA(pixel, dstR, dstG, dstB, dstA);
+            *(Uint32 *)dst = pixel;
+        }
+    }
+    TRIANGLE_END_LOOP
+}
+
+#endif /* SDL_VIDEO_RENDER_SW && !SDL_RENDER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 42 - 0
src/render/ps2/SDL_triangle.h

@@ -0,0 +1,42 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  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.
+*/
+
+#ifndef SDL_triangle_h_
+#define SDL_triangle_h_
+
+#include "../../SDL_internal.h"
+
+extern int SDL_SW_FillTriangle(SDL_Surface *dst,
+        SDL_Point *d0, SDL_Point *d1, SDL_Point *d2,
+        SDL_BlendMode blend, SDL_Color c0, SDL_Color c1, SDL_Color c2);
+
+extern int SDL_SW_BlitTriangle(
+        SDL_Surface *src,
+        SDL_Point *s0, SDL_Point *s1, SDL_Point *s2,
+        SDL_Surface *dst,
+        SDL_Point *d0, SDL_Point *d1, SDL_Point *d2,
+        SDL_Color c0, SDL_Color c1, SDL_Color c2);
+
+extern void trianglepoint_2_fixedpoint(SDL_Point *a);
+
+#endif /* SDL_triangle_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 6 - 71
src/video/ps2/SDL_ps2video.c

@@ -57,9 +57,6 @@ static void PS2_VideoQuit(_THIS);
 
 /* PS2 driver bootstrap functions */
 
-static GSGLOBAL *gsGlobal = NULL;
-static int vsync_sema_id = 0;
-
 static void
 PS2_DeleteDevice(SDL_VideoDevice * device)
 {
@@ -98,46 +95,10 @@ VideoBootStrap PS2_bootstrap = {
     PS2_CreateDevice
 };
 
-
-/* PRIVATE METHODS */
-static int vsync_handler()
-{
-   iSignalSema(vsync_sema_id);
-
-   ExitHandler();
-   return 0;
-}
-
-/* Copy of gsKit_sync_flip, but without the 'flip' */
-static void gsKit_sync(GSGLOBAL *gsGlobal)
-{
-   if (!gsGlobal->FirstFrame) WaitSema(vsync_sema_id);
-   while (PollSema(vsync_sema_id) >= 0)
-   	;
-}
-
-/* Copy of gsKit_sync_flip, but without the 'sync' */
-static void gsKit_flip(GSGLOBAL *gsGlobal)
-{
-   if (!gsGlobal->FirstFrame)
-   {
-      if (gsGlobal->DoubleBuffering == GS_SETTING_ON)
-      {
-         GS_SET_DISPFB2( gsGlobal->ScreenBuffer[
-               gsGlobal->ActiveBuffer & 1] / 8192,
-               gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 );
-
-         gsGlobal->ActiveBuffer ^= 1;
-      }
-
-   }
-
-   gsKit_setactive(gsGlobal);
-}
-
 int
 PS2_VideoInit(_THIS)
 {
+    /*
 	ee_sema_t sema;
     sema.init_count = 0;
     sema.max_count = 1;
@@ -192,6 +153,7 @@ PS2_VideoInit(_THIS)
 		gsKit_flip(gsGlobal);
 	}
 	gsKit_TexManager_nextFrame(gsGlobal);
+    */
 
     SDL_DisplayMode mode;
 
@@ -199,11 +161,12 @@ PS2_VideoInit(_THIS)
     SDL_zero(mode);
     mode.format = SDL_PIXELFORMAT_RGB888;
     mode.w = 640;
-    if (gsGlobal->Mode == GS_MODE_PAL){
+    /*if (gsGlobal->Mode == GS_MODE_PAL){
         mode.h = 512;
     } else {
         mode.h = 448;
-    }
+    }*/
+    mode.h = 448;
     mode.refresh_rate = 60;
     mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
@@ -234,41 +197,13 @@ SDL_to_PS2_PFM(Uint32 format)
 static int
 PS2_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
 {
-    gsGlobal->Mode = GS_MODE_NTSC;
-	gsGlobal->Width = mode->w;
-	if ((gsGlobal->Interlace == GS_INTERLACED) && (gsGlobal->Field == GS_FRAME))
-		gsGlobal->Height = mode->h / 2;
-	else
-		gsGlobal->Height = mode->h;
-
-	gsGlobal->PSM = SDL_to_PS2_PFM(mode->format);
-	gsGlobal->PSMZ = GS_PSMZ_16S;
-
-	gsGlobal->ZBuffering = GS_SETTING_OFF;
-	gsGlobal->DoubleBuffering = GS_SETTING_ON;
-	gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
-	gsGlobal->Dithering = GS_SETTING_OFF;
-
-	gsGlobal->Interlace = GS_INTERLACED;
-	gsGlobal->Field = GS_FIELD;
-
-	gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0, 1, 0, 1, 0), 0);
-
-	gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT);
-	gsKit_vram_clear(gsGlobal);
-	gsKit_init_screen(gsGlobal);
-	gsKit_set_display_offset(gsGlobal, -0.5f, -0.5f);
-	gsKit_sync_flip(gsGlobal);
-
-	gsKit_mode_switch(gsGlobal, GS_ONESHOT);
-    gsKit_clear(gsGlobal, GS_SETREG_RGBAQ(0x00,0x00,0x00,0x80,0x00));	
     return 0;
 }
 
 void
 PS2_VideoQuit(_THIS)
 {
-    gsKit_deinit_global(gsGlobal);
+    /*gsKit_deinit_global(gsGlobal);*/
 }
 
 #endif /* SDL_VIDEO_DRIVER_PS2 */