From 8f59101a7772ed67c86270b3da2c1f8f243c2629 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 4 May 2013 04:46:00 -0700 Subject: [PATCH] First pass on SDL render clip rect functionality --- include/SDL_render.h | 26 +++++++++ src/render/SDL_render.c | 34 +++++++++++ src/render/SDL_sysrender.h | 5 ++ src/render/direct3d/SDL_render_d3d.c | 74 ++++++++++++++++-------- src/render/opengl/SDL_render_gl.c | 17 ++++++ src/render/opengles/SDL_render_gles.c | 16 +++++ src/render/opengles2/SDL_render_gles2.c | 21 ++++++- src/render/software/SDL_render_sw.c | 16 +++++ src/test/SDL_test_common.c | 20 +++++++ src/video/directfb/SDL_DirectFB_render.c | 26 ++++++++- 10 files changed, 227 insertions(+), 28 deletions(-) diff --git a/include/SDL_render.h b/include/SDL_render.h index 591de81b0..fea64f617 100644 --- a/include/SDL_render.h +++ b/include/SDL_render.h @@ -469,6 +469,8 @@ extern DECLSPEC void SDLCALL SDL_RenderGetLogicalSize(SDL_Renderer * renderer, i * * 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. * @@ -486,6 +488,30 @@ extern DECLSPEC int SDLCALL SDL_RenderSetViewport(SDL_Renderer * renderer, extern DECLSPEC void SDLCALL SDL_RenderGetViewport(SDL_Renderer * renderer, 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. * diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 9ec64a13d..8f597606d 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -932,6 +932,7 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 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 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 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 @@ SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) if (renderer->UpdateViewport(renderer) < 0) { return -1; } + if (renderer->UpdateClipRect(renderer) < 0) { + return -1; + } /* All set! */ return 0; @@ -1097,6 +1102,35 @@ SDL_RenderGetViewport(SDL_Renderer * renderer, SDL_Rect * rect) } } +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) { diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 17f506ed8..4f0795274 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -93,6 +93,7 @@ struct SDL_Renderer 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 @@ struct SDL_Renderer 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; diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index f75d3a98e..e6bc65ba2 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -186,6 +186,7 @@ static int D3D_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, 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 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags) 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; @@ -814,6 +816,39 @@ D3D_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) IDirect3DTexture9_UnlockRect(data->texture, 0); } +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) { @@ -853,35 +888,28 @@ D3D_UpdateViewport(SDL_Renderer * renderer) } 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; - } - - 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); + result = IDirect3DDevice9_SetScissorRect(data->device, &r); + if (result != D3D_OK) { + D3D_SetError("SetScissor()", result); + return -1; + } + } else { + IDirect3DDevice9_SetRenderState(data->device, D3DRS_SCISSORTESTENABLE, FALSE); } - return 0; } diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 097f84faa..08933fa2e 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -56,6 +56,7 @@ static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, 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 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) 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 @@ GL_UpdateViewport(SDL_Renderer * renderer) 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) { diff --git a/src/render/opengles/SDL_render_gles.c b/src/render/opengles/SDL_render_gles.c index 62b547b2c..3a7a44152 100644 --- a/src/render/opengles/SDL_render_gles.c +++ b/src/render/opengles/SDL_render_gles.c @@ -59,6 +59,7 @@ static void GLES_UnlockTexture(SDL_Renderer * renderer, 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 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags) 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 @@ GLES_UpdateViewport(SDL_Renderer * renderer) 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) { diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 0d6fe5d0e..f55283f69 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -275,6 +275,20 @@ GLES2_UpdateViewport(SDL_Renderer * renderer) 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_RenderDrawLines(SDL_Renderer *renderer, const SDL_FPoint *point 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 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) 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->RenderReadPixels = &GLES2_RenderReadPixels; renderer->RenderCopyEx = &GLES2_RenderCopyEx; + renderer->RenderReadPixels = &GLES2_RenderReadPixels; renderer->RenderPresent = &GLES2_RenderPresent; renderer->DestroyTexture = &GLES2_DestroyTexture; renderer->DestroyRenderer = &GLES2_DestroyRenderer; diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 21ff1a9ae..8c84da143 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -54,6 +54,7 @@ static int SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, 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 @@ SW_CreateRendererForSurface(SDL_Surface * surface) 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; @@ -320,6 +322,20 @@ SW_UpdateViewport(SDL_Renderer * renderer) return 0; } +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) { diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 04103a3e4..b5fdc3f5b 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1202,6 +1202,26 @@ SDLTest_CommonEvent(SDLTest_CommonState * state, SDL_Event * event, int *done) 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) { diff --git a/src/video/directfb/SDL_DirectFB_render.c b/src/video/directfb/SDL_DirectFB_render.c index db69a5c44..07aa77d11 100644 --- a/src/video/directfb/SDL_DirectFB_render.c +++ b/src/video/directfb/SDL_DirectFB_render.c @@ -116,6 +116,7 @@ static int DirectFB_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * r 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 @@ DirectFB_CreateRenderer(SDL_Window * window, Uint32 flags) /* 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 @@ DirectFB_CreateRenderer(SDL_Window * window, Uint32 flags) renderer->DestroyTexture = DirectFB_DestroyTexture; renderer->DestroyRenderer = DirectFB_DestroyRenderer; renderer->UpdateViewport = DirectFB_UpdateViewport; + renderer->UpdateClipRect = DirectFB_UpdateClipRect; renderer->SetRenderTarget = DirectFB_SetRenderTarget; #if 0 @@ -1258,6 +1258,28 @@ DirectFB_UpdateViewport(SDL_Renderer * renderer) return 0; } +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, ®ion)); + } 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)