First pass on SDL render clip rect functionality
authorSam Lantinga <slouken@libsdl.org>
Sat, 04 May 2013 04:46:00 -0700
changeset 7141 e276777b4247
parent 7140 e1896b95a8a7
child 7142 f4a670e51cde
First pass on SDL render clip rect functionality
include/SDL_render.h
src/render/SDL_render.c
src/render/SDL_sysrender.h
src/render/direct3d/SDL_render_d3d.c
src/render/opengl/SDL_render_gl.c
src/render/opengles/SDL_render_gles.c
src/render/opengles2/SDL_render_gles2.c
src/render/software/SDL_render_sw.c
src/test/SDL_test_common.c
src/video/directfb/SDL_DirectFB_render.c
--- a/include/SDL_render.h	Fri May 03 14:11:41 2013 +0930
+++ b/include/SDL_render.h	Sat May 04 04:46:00 2013 -0700
@@ -469,6 +469,8 @@
  *
  *  The x,y of the viewport rect represents the origin for rendering.
  *
+ *  \return 0 on success, or -1 on error
+ *
  *  \note When the window is resized, the current viewport is automatically
  *        centered within the new window size.
  *
@@ -487,6 +489,30 @@
                                                    SDL_Rect * rect);
 
 /**
+ *  \brief Set the clip rectangle for the current target.
+ *  
+ *  \param rect   A pointer to the rectangle to set as the clip rectangle, or
+ *                NULL to disable clipping.
+ *
+ *  \return 0 on success, or -1 on error
+ *
+ *  \sa SDL_RenderGetClipRect()
+ */
+extern DECLSPEC int SDLCALL SDL_RenderSetClipRect(SDL_Renderer * renderer,
+                                                  const SDL_Rect * rect);
+
+/**
+ *  \brief Get the clip rectangle for the current target.
+ *  
+ *  \param rect   A pointer filled in with the current clip rectangle, or
+ *                an empty rectangle if clipping is disabled.
+ *
+ *  \sa SDL_RenderSetClipRect()
+ */
+extern DECLSPEC void SDLCALL SDL_RenderGetClipRect(SDL_Renderer * renderer,
+                                                   SDL_Rect * rect);
+
+/**
  *  \brief Set the drawing scale for rendering on the current target.
  *
  *  \param scaleX The horizontal scaling factor
--- a/src/render/SDL_render.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/SDL_render.c	Sat May 04 04:46:00 2013 -0700
@@ -932,6 +932,7 @@
     if (texture && !renderer->target) {
         /* Make a backup of the viewport */
         renderer->viewport_backup = renderer->viewport;
+        renderer->clip_rect_backup = renderer->clip_rect;
         renderer->scale_backup = renderer->scale;
         renderer->logical_w_backup = renderer->logical_w;
         renderer->logical_h_backup = renderer->logical_h;
@@ -953,6 +954,7 @@
         renderer->logical_h = 0;
     } else {
         renderer->viewport = renderer->viewport_backup;
+        renderer->clip_rect = renderer->clip_rect_backup;
         renderer->scale = renderer->scale_backup;
         renderer->logical_w = renderer->logical_w_backup;
         renderer->logical_h = renderer->logical_h_backup;
@@ -960,6 +962,9 @@
     if (renderer->UpdateViewport(renderer) < 0) {
         return -1;
     }
+    if (renderer->UpdateClipRect(renderer) < 0) {
+        return -1;
+    }
 
     /* All set! */
     return 0;
@@ -1098,6 +1103,35 @@
 }
 
 int
+SDL_RenderSetClipRect(SDL_Renderer * renderer, const SDL_Rect * rect)
+{
+    CHECK_RENDERER_MAGIC(renderer, )
+
+    if (rect) {
+        renderer->clip_rect.x = (int)SDL_floor(rect->x * renderer->scale.x);
+        renderer->clip_rect.y = (int)SDL_floor(rect->y * renderer->scale.y);
+        renderer->clip_rect.w = (int)SDL_ceil(rect->w * renderer->scale.x);
+        renderer->clip_rect.h = (int)SDL_ceil(rect->h * renderer->scale.y);
+    } else {
+        SDL_zero(renderer->clip_rect);
+    }
+    return renderer->UpdateClipRect(renderer);
+}
+
+void
+SDL_RenderGetClipRect(SDL_Renderer * renderer, SDL_Rect * rect)
+{
+    CHECK_RENDERER_MAGIC(renderer, )
+
+    if (rect) {
+        rect->x = (int)(renderer->clip_rect.x / renderer->scale.x);
+        rect->y = (int)(renderer->clip_rect.y / renderer->scale.y);
+        rect->w = (int)(renderer->clip_rect.w / renderer->scale.x);
+        rect->h = (int)(renderer->clip_rect.h / renderer->scale.y);
+    }
+}
+
+int
 SDL_RenderSetScale(SDL_Renderer * renderer, float scaleX, float scaleY)
 {
     CHECK_RENDERER_MAGIC(renderer, -1);
--- a/src/render/SDL_sysrender.h	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/SDL_sysrender.h	Sat May 04 04:46:00 2013 -0700
@@ -93,6 +93,7 @@
     void (*UnlockTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
     int (*SetRenderTarget) (SDL_Renderer * renderer, SDL_Texture * texture);
     int (*UpdateViewport) (SDL_Renderer * renderer);
+    int (*UpdateClipRect) (SDL_Renderer * renderer);
     int (*RenderClear) (SDL_Renderer * renderer);
     int (*RenderDrawPoints) (SDL_Renderer * renderer, const SDL_FPoint * points,
                              int count);
@@ -133,6 +134,10 @@
     SDL_Rect viewport;
     SDL_Rect viewport_backup;
 
+    /* The clip rectangle within the window */
+    SDL_Rect clip_rect;
+    SDL_Rect clip_rect_backup;
+
     /* The render output coordinate scale */
     SDL_FPoint scale;
     SDL_FPoint scale_backup;
--- a/src/render/direct3d/SDL_render_d3d.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/direct3d/SDL_render_d3d.c	Sat May 04 04:46:00 2013 -0700
@@ -186,6 +186,7 @@
 static void D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
 static int D3D_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
 static int D3D_UpdateViewport(SDL_Renderer * renderer);
+static int D3D_UpdateClipRect(SDL_Renderer * renderer);
 static int D3D_RenderClear(SDL_Renderer * renderer);
 static int D3D_RenderDrawPoints(SDL_Renderer * renderer,
                                 const SDL_FPoint * points, int count);
@@ -510,6 +511,7 @@
     renderer->UnlockTexture = D3D_UnlockTexture;
     renderer->SetRenderTarget = D3D_SetRenderTarget;
     renderer->UpdateViewport = D3D_UpdateViewport;
+    renderer->UpdateClipRect = D3D_UpdateClipRect;
     renderer->RenderClear = D3D_RenderClear;
     renderer->RenderDrawPoints = D3D_RenderDrawPoints;
     renderer->RenderDrawLines = D3D_RenderDrawLines;
@@ -815,6 +817,39 @@
 }
 
 static int
+D3D_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    D3D_TextureData *texturedata;
+    HRESULT result;
+
+    D3D_ActivateRenderer(renderer);
+
+    /* Release the previous render target if it wasn't the default one */
+    if (data->currentRenderTarget != NULL) {
+        IDirect3DSurface9_Release(data->currentRenderTarget);
+        data->currentRenderTarget = NULL;
+    }
+
+    if (texture == NULL) {
+        IDirect3DDevice9_SetRenderTarget(data->device, 0, data->defaultRenderTarget);
+        return 0;
+    }
+
+    texturedata = (D3D_TextureData *) texture->driverdata;
+    result = IDirect3DTexture9_GetSurfaceLevel(texturedata->texture, 0, &data->currentRenderTarget);
+    if(FAILED(result)) {
+        return D3D_SetError("GetSurfaceLevel()", result);
+    }
+    result = IDirect3DDevice9_SetRenderTarget(data->device, 0, data->currentRenderTarget);
+    if(FAILED(result)) {
+        return D3D_SetError("SetRenderTarget()", result);
+    }
+
+    return 0;
+}
+
+static int
 D3D_UpdateViewport(SDL_Renderer * renderer)
 {
     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
@@ -853,35 +888,28 @@
 }
 
 static int
-D3D_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
+D3D_UpdateClipRect(SDL_Renderer * renderer)
 {
+    const SDL_Rect *rect = &renderer->clip_rect;
     D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
-    D3D_TextureData *texturedata;
+    RECT r;
     HRESULT result;
 
-    D3D_ActivateRenderer(renderer);
+    if (!SDL_RectEmpty(rect)) {
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, TRUE);
+        r.left = rect->x;
+        r.top = rect->y;
+        r.right = rect->w + rect->w;
+        r.bottom = rect->y + rect->h;
 
-    /* Release the previous render target if it wasn't the default one */
-    if (data->currentRenderTarget != NULL) {
-        IDirect3DSurface9_Release(data->currentRenderTarget);
-        data->currentRenderTarget = NULL;
-    }
-
-    if (texture == NULL) {
-        IDirect3DDevice9_SetRenderTarget(data->device, 0, data->defaultRenderTarget);
-        return 0;
+        result = IDirect3DDevice9_SetScissorRect(data->device, &r);
+        if (result != D3D_OK) {
+            D3D_SetError("SetScissor()", result);
+            return -1;
+        }
+    } else {
+        IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE);
     }
-
-    texturedata = (D3D_TextureData *) texture->driverdata;
-    result = IDirect3DTexture9_GetSurfaceLevel(texturedata->texture, 0, &data->currentRenderTarget);
-    if(FAILED(result)) {
-        return D3D_SetError("GetSurfaceLevel()", result);
-    }
-    result = IDirect3DDevice9_SetRenderTarget(data->device, 0, data->currentRenderTarget);
-    if(FAILED(result)) {
-        return D3D_SetError("SetRenderTarget()", result);
-    }
-
     return 0;
 }
 
--- a/src/render/opengl/SDL_render_gl.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/opengl/SDL_render_gl.c	Sat May 04 04:46:00 2013 -0700
@@ -56,6 +56,7 @@
 static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
 static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
 static int GL_UpdateViewport(SDL_Renderer * renderer);
+static int GL_UpdateClipRect(SDL_Renderer * renderer);
 static int GL_RenderClear(SDL_Renderer * renderer);
 static int GL_RenderDrawPoints(SDL_Renderer * renderer,
                                const SDL_FPoint * points, int count);
@@ -324,6 +325,7 @@
     renderer->UnlockTexture = GL_UnlockTexture;
     renderer->SetRenderTarget = GL_SetRenderTarget;
     renderer->UpdateViewport = GL_UpdateViewport;
+    renderer->UpdateClipRect = GL_UpdateClipRect;
     renderer->RenderClear = GL_RenderClear;
     renderer->RenderDrawPoints = GL_RenderDrawPoints;
     renderer->RenderDrawLines = GL_RenderDrawLines;
@@ -784,6 +786,21 @@
     return 0;
 }
 
+static int
+GL_UpdateClipRect(SDL_Renderer * renderer)
+{
+    const SDL_Rect *rect = &renderer->clip_rect;
+    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
+
+    if (!SDL_RectEmpty(rect)) {
+        data->glEnable(GL_SCISSOR_TEST);
+        data->glScissor(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
+    } else {
+        data->glDisable(GL_SCISSOR_TEST);
+    }
+    return 0;
+}
+
 static void
 GL_SetShader(GL_RenderData * data, GL_Shader shader)
 {
--- a/src/render/opengles/SDL_render_gles.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/opengles/SDL_render_gles.c	Sat May 04 04:46:00 2013 -0700
@@ -59,6 +59,7 @@
 static int GLES_SetRenderTarget(SDL_Renderer * renderer,
                                  SDL_Texture * texture);
 static int GLES_UpdateViewport(SDL_Renderer * renderer);
+static int GLES_UpdateClipRect(SDL_Renderer * renderer);
 static int GLES_RenderClear(SDL_Renderer * renderer);
 static int GLES_RenderDrawPoints(SDL_Renderer * renderer,
                                  const SDL_FPoint * points, int count);
@@ -303,6 +304,7 @@
     renderer->UnlockTexture = GLES_UnlockTexture;
     renderer->SetRenderTarget = GLES_SetRenderTarget;
     renderer->UpdateViewport = GLES_UpdateViewport;
+    renderer->UpdateClipRect = GLES_UpdateClipRect;
     renderer->RenderClear = GLES_RenderClear;
     renderer->RenderDrawPoints = GLES_RenderDrawPoints;
     renderer->RenderDrawLines = GLES_RenderDrawLines;
@@ -630,6 +632,20 @@
     return 0;
 }
 
+static int
+GLES_UpdateClipRect(SDL_Renderer * renderer)
+{
+    const SDL_Rect *rect = &renderer->clip_rect;
+
+    if (!SDL_RectEmpty(rect)) {
+        glEnable(GL_SCISSOR_TEST);
+        glScissor(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
+    } else {
+        glDisable(GL_SCISSOR_TEST);
+    }
+    return 0;
+}
+
 static void
 GLES_SetColor(GLES_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
 {
--- a/src/render/opengles2/SDL_render_gles2.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/opengles2/SDL_render_gles2.c	Sat May 04 04:46:00 2013 -0700
@@ -275,6 +275,20 @@
     return 0;
 }
 
+static int
+GLES2_UpdateClipRect(SDL_Renderer * renderer)
+{
+    const SDL_Rect *rect = &renderer->clip_rect;
+
+    if (!SDL_RectEmpty(rect)) {
+        glEnable(GL_SCISSOR_TEST);
+        glScissor(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
+    } else {
+        glDisable(GL_SCISSOR_TEST);
+    }
+    return 0;
+}
+
 static void
 GLES2_DestroyRenderer(SDL_Renderer *renderer)
 {
@@ -934,11 +948,11 @@
 static int GLES2_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count);
 static int GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect,
                             const SDL_FRect *dstrect);
-static int GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
-                    Uint32 pixel_format, void * pixels, int pitch);
 static int GLES2_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
                          const SDL_Rect * srcrect, const SDL_FRect * dstrect,
                          const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
+static int GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
+                    Uint32 pixel_format, void * pixels, int pitch);
 static void GLES2_RenderPresent(SDL_Renderer *renderer);
 
 
@@ -1708,13 +1722,14 @@
     renderer->UnlockTexture       = &GLES2_UnlockTexture;
     renderer->SetRenderTarget     = &GLES2_SetRenderTarget;
     renderer->UpdateViewport      = &GLES2_UpdateViewport;
+    renderer->UpdateClipRect      = &GLES2_UpdateClipRect;
     renderer->RenderClear         = &GLES2_RenderClear;
     renderer->RenderDrawPoints    = &GLES2_RenderDrawPoints;
     renderer->RenderDrawLines     = &GLES2_RenderDrawLines;
     renderer->RenderFillRects     = &GLES2_RenderFillRects;
     renderer->RenderCopy          = &GLES2_RenderCopy;
+    renderer->RenderCopyEx        = &GLES2_RenderCopyEx;
     renderer->RenderReadPixels    = &GLES2_RenderReadPixels;
-    renderer->RenderCopyEx        = &GLES2_RenderCopyEx;
     renderer->RenderPresent       = &GLES2_RenderPresent;
     renderer->DestroyTexture      = &GLES2_DestroyTexture;
     renderer->DestroyRenderer     = &GLES2_DestroyRenderer;
--- a/src/render/software/SDL_render_sw.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/render/software/SDL_render_sw.c	Sat May 04 04:46:00 2013 -0700
@@ -54,6 +54,7 @@
 static void SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
 static int SW_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
 static int SW_UpdateViewport(SDL_Renderer * renderer);
+static int SW_UpdateClipRect(SDL_Renderer * renderer);
 static int SW_RenderClear(SDL_Renderer * renderer);
 static int SW_RenderDrawPoints(SDL_Renderer * renderer,
                                const SDL_FPoint * points, int count);
@@ -151,6 +152,7 @@
     renderer->UnlockTexture = SW_UnlockTexture;
     renderer->SetRenderTarget = SW_SetRenderTarget;
     renderer->UpdateViewport = SW_UpdateViewport;
+    renderer->UpdateClipRect = SW_UpdateClipRect;
     renderer->RenderClear = SW_RenderClear;
     renderer->RenderDrawPoints = SW_RenderDrawPoints;
     renderer->RenderDrawLines = SW_RenderDrawLines;
@@ -321,6 +323,20 @@
 }
 
 static int
+SW_UpdateClipRect(SDL_Renderer * renderer)
+{
+    const SDL_Rect *rect = &renderer->clip_rect;
+    SDL_Surface* framebuffer = (SDL_Surface *) renderer->driverdata;
+
+    if (!SDL_RectEmpty(rect)) {
+        SDL_SetClipRect(framebuffer, rect);
+    } else {
+        SDL_SetClipRect(framebuffer, NULL);
+    }
+    return 0;
+}
+
+static int
 SW_RenderClear(SDL_Renderer * renderer)
 {
     SDL_Surface *surface = SW_ActivateRenderer(renderer);
--- a/src/test/SDL_test_common.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/test/SDL_test_common.c	Sat May 04 04:46:00 2013 -0700
@@ -1202,6 +1202,26 @@
                 SDL_SetClipboardText("SDL rocks!\nYou know it!");
                 printf("Copied text to clipboard\n");
             }
+            if (event->key.keysym.mod & KMOD_ALT) {
+                /* Alt-C toggle a render clip rectangle */
+                for (i = 0; i < state->num_windows; ++i) {
+                    int w, h;
+                    if (state->renderers[i]) {
+                        SDL_Rect clip;
+                        SDL_GetWindowSize(state->windows[i], &w, &h);
+                        SDL_RenderGetClipRect(state->renderers[i], &clip);
+                        if (SDL_RectEmpty(&clip)) {
+                            clip.x = w/4;
+                            clip.y = h/4;
+                            clip.w = w/2;
+                            clip.h = h/2;
+                            SDL_RenderSetClipRect(state->renderers[i], &clip);
+                        } else {
+                            SDL_RenderSetClipRect(state->renderers[i], NULL);
+                        }
+                    }
+                }
+            }
             break;
         case SDLK_v:
             if (event->key.keysym.mod & KMOD_CTRL) {
--- a/src/video/directfb/SDL_DirectFB_render.c	Fri May 03 14:11:41 2013 +0930
+++ b/src/video/directfb/SDL_DirectFB_render.c	Sat May 04 04:46:00 2013 -0700
@@ -116,6 +116,7 @@
 static int DirectFB_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                       Uint32 format, const void * pixels, int pitch);
 static int DirectFB_UpdateViewport(SDL_Renderer * renderer);
+static int DirectFB_UpdateClipRect(SDL_Renderer * renderer);
 static int DirectFB_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
 
 static int PrepareDraw(SDL_Renderer * renderer);
@@ -380,8 +381,6 @@
     /* SetDrawColor - no needed */
     renderer->RenderFillRects = DirectFB_RenderFillRects;
 
-    /* RenderDrawEllipse - no reference implementation yet */
-    /* RenderFillEllipse - no reference implementation yet */
     renderer->RenderCopy = DirectFB_RenderCopy;
     renderer->RenderPresent = DirectFB_RenderPresent;
 
@@ -392,6 +391,7 @@
     renderer->DestroyTexture = DirectFB_DestroyTexture;
     renderer->DestroyRenderer = DirectFB_DestroyRenderer;
     renderer->UpdateViewport = DirectFB_UpdateViewport;
+    renderer->UpdateClipRect = DirectFB_UpdateClipRect;
     renderer->SetRenderTarget = DirectFB_SetRenderTarget;
 
 #if 0
@@ -1259,6 +1259,28 @@
 }
 
 static int
+DirectFB_UpdateClipRect(SDL_Renderer * renderer)
+{
+    const SDL_Rect *rect = &renderer->clip_rect;
+    DirectFB_RenderData *data = (DirectFB_RenderData *) renderer->driverdata;
+    IDirectFBSurface *destsurf = get_dfb_surface(data->window);
+    DFBRegion region;
+
+    if (!SDL_RectEmpty(rect)) {
+        region.x1 = rect->x;
+        region.x2 = rect->x + rect->w;
+        region.y1 = rect->y;
+        region.y2 = rect->y + rect->h;
+        SDL_DFB_CHECKERR(destsurf->SetClip(destsurf, &region));
+    } else {
+        SDL_DFB_CHECKERR(destsurf->SetClip(destsurf, NULL));
+    }
+    return 0;
+  error:
+    return -1;
+}
+
+static int
 DirectFB_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                      Uint32 format, void * pixels, int pitch)
 {