src/render/software/SDL_render_sw.c
author Dimitris Zenios <dimitris.zenios@gmail.com>
Fri, 10 Jun 2011 12:13:06 +0300
changeset 5567 441c3036440c
parent 5535 96594ac5fd1a
child 6044 35448a5ea044
permissions -rw-r--r--
Fixes a bug in SDL_UpdateTexture when using SDL_RENDERER_SOFTWARE and texture access is SDL_TEXTUREACCESS_STATIC
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if !SDL_RENDER_DISABLED
    24 
    25 #include "../SDL_sysrender.h"
    26 
    27 #include "SDL_draw.h"
    28 #include "SDL_blendfillrect.h"
    29 #include "SDL_blendline.h"
    30 #include "SDL_blendpoint.h"
    31 #include "SDL_drawline.h"
    32 #include "SDL_drawpoint.h"
    33 
    34 
    35 /* SDL surface based renderer implementation */
    36 
    37 static SDL_Renderer *SW_CreateRenderer(SDL_Window * window, Uint32 flags);
    38 static void SW_WindowEvent(SDL_Renderer * renderer,
    39                            const SDL_WindowEvent *event);
    40 static int SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    41 static int SW_SetTextureColorMod(SDL_Renderer * renderer,
    42                                  SDL_Texture * texture);
    43 static int SW_SetTextureAlphaMod(SDL_Renderer * renderer,
    44                                  SDL_Texture * texture);
    45 static int SW_SetTextureBlendMode(SDL_Renderer * renderer,
    46                                   SDL_Texture * texture);
    47 static int SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    48                             const SDL_Rect * rect, const void *pixels,
    49                             int pitch);
    50 static int SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    51                           const SDL_Rect * rect, void **pixels, int *pitch);
    52 static void SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    53 static int SW_UpdateViewport(SDL_Renderer * renderer);
    54 static int SW_RenderClear(SDL_Renderer * renderer);
    55 static int SW_RenderDrawPoints(SDL_Renderer * renderer,
    56                                const SDL_Point * points, int count);
    57 static int SW_RenderDrawLines(SDL_Renderer * renderer,
    58                               const SDL_Point * points, int count);
    59 static int SW_RenderFillRects(SDL_Renderer * renderer,
    60                               const SDL_Rect * rects, int count);
    61 static int SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
    62                          const SDL_Rect * srcrect, const SDL_Rect * dstrect);
    63 static int SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
    64                                Uint32 format, void * pixels, int pitch);
    65 static void SW_RenderPresent(SDL_Renderer * renderer);
    66 static void SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    67 static void SW_DestroyRenderer(SDL_Renderer * renderer);
    68 
    69 
    70 SDL_RenderDriver SW_RenderDriver = {
    71     SW_CreateRenderer,
    72     {
    73      "software",
    74      SDL_RENDERER_SOFTWARE,
    75      8,
    76      {
    77       SDL_PIXELFORMAT_RGB555,
    78       SDL_PIXELFORMAT_RGB565,
    79       SDL_PIXELFORMAT_RGB888,
    80       SDL_PIXELFORMAT_BGR888,
    81       SDL_PIXELFORMAT_ARGB8888,
    82       SDL_PIXELFORMAT_RGBA8888,
    83       SDL_PIXELFORMAT_ABGR8888,
    84       SDL_PIXELFORMAT_BGRA8888
    85      },
    86      0,
    87      0}
    88 };
    89 
    90 typedef struct
    91 {
    92     SDL_Surface *surface;
    93 } SW_RenderData;
    94 
    95 
    96 static SDL_Surface *
    97 SW_ActivateRenderer(SDL_Renderer * renderer)
    98 {
    99     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   100 
   101     if (!data->surface) {
   102         data->surface = SDL_GetWindowSurface(renderer->window);
   103 
   104         SW_UpdateViewport(renderer);
   105     }
   106     return data->surface;
   107 }
   108 
   109 SDL_Renderer *
   110 SW_CreateRendererForSurface(SDL_Surface * surface)
   111 {
   112     SDL_Renderer *renderer;
   113     SW_RenderData *data;
   114 
   115     if (!surface) {
   116         SDL_SetError("Can't create renderer for NULL surface");
   117         return NULL;
   118     }
   119 
   120     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   121     if (!renderer) {
   122         SDL_OutOfMemory();
   123         return NULL;
   124     }
   125 
   126     data = (SW_RenderData *) SDL_calloc(1, sizeof(*data));
   127     if (!data) {
   128         SW_DestroyRenderer(renderer);
   129         SDL_OutOfMemory();
   130         return NULL;
   131     }
   132     data->surface = surface;
   133 
   134     renderer->WindowEvent = SW_WindowEvent;
   135     renderer->CreateTexture = SW_CreateTexture;
   136     renderer->SetTextureColorMod = SW_SetTextureColorMod;
   137     renderer->SetTextureAlphaMod = SW_SetTextureAlphaMod;
   138     renderer->SetTextureBlendMode = SW_SetTextureBlendMode;
   139     renderer->UpdateTexture = SW_UpdateTexture;
   140     renderer->LockTexture = SW_LockTexture;
   141     renderer->UnlockTexture = SW_UnlockTexture;
   142     renderer->UpdateViewport = SW_UpdateViewport;
   143     renderer->DestroyTexture = SW_DestroyTexture;
   144     renderer->RenderClear = SW_RenderClear;
   145     renderer->RenderDrawPoints = SW_RenderDrawPoints;
   146     renderer->RenderDrawLines = SW_RenderDrawLines;
   147     renderer->RenderFillRects = SW_RenderFillRects;
   148     renderer->RenderCopy = SW_RenderCopy;
   149     renderer->RenderReadPixels = SW_RenderReadPixels;
   150     renderer->RenderPresent = SW_RenderPresent;
   151     renderer->DestroyRenderer = SW_DestroyRenderer;
   152     renderer->info = SW_RenderDriver.info;
   153     renderer->driverdata = data;
   154 
   155     SW_ActivateRenderer(renderer);
   156 
   157     return renderer;
   158 }
   159 
   160 SDL_Renderer *
   161 SW_CreateRenderer(SDL_Window * window, Uint32 flags)
   162 {
   163     SDL_Surface *surface;
   164 
   165     surface = SDL_GetWindowSurface(window);
   166     if (!surface) {
   167         return NULL;
   168     }
   169     return SW_CreateRendererForSurface(surface);
   170 }
   171 
   172 static void
   173 SW_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   174 {
   175     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   176 
   177     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   178         data->surface = NULL;
   179     }
   180 }
   181 
   182 static int
   183 SW_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   184 {
   185     int bpp;
   186     Uint32 Rmask, Gmask, Bmask, Amask;
   187 
   188     if (!SDL_PixelFormatEnumToMasks
   189         (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
   190         SDL_SetError("Unknown texture format");
   191         return -1;
   192     }
   193 
   194     texture->driverdata =
   195         SDL_CreateRGBSurface(0, texture->w, texture->h, bpp, Rmask, Gmask,
   196                              Bmask, Amask);
   197     SDL_SetSurfaceColorMod(texture->driverdata, texture->r, texture->g,
   198                            texture->b);
   199     SDL_SetSurfaceAlphaMod(texture->driverdata, texture->a);
   200     SDL_SetSurfaceBlendMode(texture->driverdata, texture->blendMode);
   201 
   202     if (texture->access == SDL_TEXTUREACCESS_STATIC) {
   203         SDL_SetSurfaceRLE(texture->driverdata, 1);
   204     }
   205 
   206     if (!texture->driverdata) {
   207         return -1;
   208     }
   209     return 0;
   210 }
   211 
   212 static int
   213 SW_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture)
   214 {
   215     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   216     return SDL_SetSurfaceColorMod(surface, texture->r, texture->g,
   217                                   texture->b);
   218 }
   219 
   220 static int
   221 SW_SetTextureAlphaMod(SDL_Renderer * renderer, SDL_Texture * texture)
   222 {
   223     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   224     return SDL_SetSurfaceAlphaMod(surface, texture->a);
   225 }
   226 
   227 static int
   228 SW_SetTextureBlendMode(SDL_Renderer * renderer, SDL_Texture * texture)
   229 {
   230     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   231     return SDL_SetSurfaceBlendMode(surface, texture->blendMode);
   232 }
   233 
   234 static int
   235 SW_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   236                  const SDL_Rect * rect, const void *pixels, int pitch)
   237 {
   238     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   239     Uint8 *src, *dst;
   240     int row;
   241     size_t length;
   242 
   243     if(SDL_MUSTLOCK(surface))
   244         SDL_LockSurface(surface);
   245     src = (Uint8 *) pixels;
   246     dst = (Uint8 *) surface->pixels +
   247                         rect->y * surface->pitch +
   248                         rect->x * surface->format->BytesPerPixel;
   249     length = rect->w * surface->format->BytesPerPixel;
   250     for (row = 0; row < rect->h; ++row) {
   251         SDL_memcpy(dst, src, length);
   252         src += pitch;
   253         dst += surface->pitch;
   254     }
   255     if(SDL_MUSTLOCK(surface))
   256         SDL_UnlockSurface(surface);
   257     return 0;
   258 }
   259 
   260 static int
   261 SW_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   262                const SDL_Rect * rect, void **pixels, int *pitch)
   263 {
   264     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   265 
   266     *pixels =
   267         (void *) ((Uint8 *) surface->pixels + rect->y * surface->pitch +
   268                   rect->x * surface->format->BytesPerPixel);
   269     *pitch = surface->pitch;
   270     return 0;
   271 }
   272 
   273 static void
   274 SW_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   275 {
   276 }
   277 
   278 static int
   279 SW_UpdateViewport(SDL_Renderer * renderer)
   280 {
   281     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   282     SDL_Surface *surface = data->surface;
   283 
   284     if (!surface) {
   285         /* We'll update the viewport after we recreate the surface */
   286         return 0;
   287     }
   288 
   289     if (!renderer->viewport.w && !renderer->viewport.h) {
   290         /* There may be no window, so update the viewport directly */
   291         renderer->viewport.w = surface->w;
   292         renderer->viewport.h = surface->h;
   293     }
   294     SDL_SetClipRect(data->surface, &renderer->viewport);
   295     return 0;
   296 }
   297 
   298 static int
   299 SW_RenderClear(SDL_Renderer * renderer)
   300 {
   301     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   302     Uint32 color;
   303     SDL_Rect clip_rect;
   304 
   305     if (!surface) {
   306         return -1;
   307     }
   308 
   309     color = SDL_MapRGBA(surface->format,
   310                         renderer->r, renderer->g, renderer->b, renderer->a);
   311 
   312     /* By definition the clear ignores the clip rect */
   313     clip_rect = surface->clip_rect;
   314     SDL_SetClipRect(surface, NULL);
   315     SDL_FillRect(surface, NULL, color);
   316     SDL_SetClipRect(surface, &clip_rect);
   317     return 0;
   318 }
   319 
   320 static int
   321 SW_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points,
   322                     int count)
   323 {
   324     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   325     SDL_Point *temp = NULL;
   326     int status;
   327 
   328     if (!surface) {
   329         return -1;
   330     }
   331 
   332     if (renderer->viewport.x || renderer->viewport.y) {
   333         int i;
   334         int x = renderer->viewport.x;
   335         int y = renderer->viewport.y;
   336 
   337         temp = SDL_stack_alloc(SDL_Point, count);
   338         for (i = 0; i < count; ++i) {
   339             temp[i].x = x + points[i].x;
   340             temp[i].y = y + points[i].x;
   341         }
   342         points = temp;
   343     }
   344 
   345     /* Draw the points! */
   346     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   347         Uint32 color = SDL_MapRGBA(surface->format,
   348                                    renderer->r, renderer->g, renderer->b,
   349                                    renderer->a);
   350 
   351         status = SDL_DrawPoints(surface, points, count, color);
   352     } else {
   353         status = SDL_BlendPoints(surface, points, count,
   354                                 renderer->blendMode,
   355                                 renderer->r, renderer->g, renderer->b,
   356                                 renderer->a);
   357     }
   358 
   359     if (temp) {
   360         SDL_stack_free(temp);
   361     }
   362     return status;
   363 }
   364 
   365 static int
   366 SW_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points,
   367                    int count)
   368 {
   369     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   370     SDL_Point *temp = NULL;
   371     int status;
   372 
   373     if (!surface) {
   374         return -1;
   375     }
   376 
   377     if (renderer->viewport.x || renderer->viewport.y) {
   378         int i;
   379         int x = renderer->viewport.x;
   380         int y = renderer->viewport.y;
   381 
   382         temp = SDL_stack_alloc(SDL_Point, count);
   383         for (i = 0; i < count; ++i) {
   384             temp[i].x = x + points[i].x;
   385             temp[i].y = y + points[i].y;
   386         }
   387         points = temp;
   388     }
   389 
   390     /* Draw the lines! */
   391     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   392         Uint32 color = SDL_MapRGBA(surface->format,
   393                                    renderer->r, renderer->g, renderer->b,
   394                                    renderer->a);
   395 
   396         status = SDL_DrawLines(surface, points, count, color);
   397     } else {
   398         status = SDL_BlendLines(surface, points, count,
   399                                 renderer->blendMode,
   400                                 renderer->r, renderer->g, renderer->b,
   401                                 renderer->a);
   402     }
   403 
   404     if (temp) {
   405         SDL_stack_free(temp);
   406     }
   407     return status;
   408 }
   409 
   410 static int
   411 SW_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect * rects, int count)
   412 {
   413     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   414     SDL_Rect *temp = NULL;
   415     int status;
   416 
   417     if (!surface) {
   418         return -1;
   419     }
   420 
   421     if (renderer->viewport.x || renderer->viewport.y) {
   422         int i;
   423         int x = renderer->viewport.x;
   424         int y = renderer->viewport.y;
   425 
   426         temp = SDL_stack_alloc(SDL_Rect, count);
   427         for (i = 0; i < count; ++i) {
   428             temp[i].x = x + rects[i].x;
   429             temp[i].y = y + rects[i].y;
   430             temp[i].w = rects[i].w;
   431             temp[i].h = rects[i].h;
   432         }
   433         rects = temp;
   434     }
   435 
   436     if (renderer->blendMode == SDL_BLENDMODE_NONE) {
   437         Uint32 color = SDL_MapRGBA(surface->format,
   438                                    renderer->r, renderer->g, renderer->b,
   439                                    renderer->a);
   440         status = SDL_FillRects(surface, rects, count, color);
   441     } else {
   442         status = SDL_BlendFillRects(surface, rects, count,
   443                                     renderer->blendMode,
   444                                     renderer->r, renderer->g, renderer->b,
   445                                     renderer->a);
   446     }
   447 
   448     if (temp) {
   449         SDL_stack_free(temp);
   450     }
   451     return status;
   452 }
   453 
   454 static int
   455 SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   456               const SDL_Rect * srcrect, const SDL_Rect * dstrect)
   457 {
   458     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   459     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
   460     SDL_Rect final_rect = *dstrect;
   461 
   462     if (!surface) {
   463         return -1;
   464     }
   465 
   466     if (renderer->viewport.x || renderer->viewport.y) {
   467         final_rect.x += renderer->viewport.x;
   468         final_rect.y += renderer->viewport.y;
   469     }
   470     if ( srcrect->w == final_rect.w && srcrect->h == final_rect.h ) {
   471         return SDL_BlitSurface(src, srcrect, surface, &final_rect);
   472     } else {
   473         return SDL_BlitScaled(src, srcrect, surface, &final_rect);
   474     }
   475 }
   476 
   477 static int
   478 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   479                     Uint32 format, void * pixels, int pitch)
   480 {
   481     SDL_Surface *surface = SW_ActivateRenderer(renderer);
   482     Uint32 src_format;
   483     void *src_pixels;
   484     SDL_Rect final_rect;
   485 
   486     if (!surface) {
   487         return -1;
   488     }
   489 
   490     if (renderer->viewport.x || renderer->viewport.y) {
   491         final_rect.x = renderer->viewport.x + rect->x;
   492         final_rect.y = renderer->viewport.y + rect->y;
   493         final_rect.w = rect->w;
   494         final_rect.h = rect->h;
   495         rect = &final_rect;
   496     }
   497 
   498     if (rect->x < 0 || rect->x+rect->w > surface->w ||
   499         rect->y < 0 || rect->y+rect->h > surface->h) {
   500         SDL_SetError("Tried to read outside of surface bounds");
   501         return -1;
   502     }
   503 
   504     src_format = surface->format->format;
   505     src_pixels = (void*)((Uint8 *) surface->pixels +
   506                     rect->y * surface->pitch +
   507                     rect->x * surface->format->BytesPerPixel);
   508 
   509     return SDL_ConvertPixels(rect->w, rect->h,
   510                              src_format, src_pixels, surface->pitch,
   511                              format, pixels, pitch);
   512 }
   513 
   514 static void
   515 SW_RenderPresent(SDL_Renderer * renderer)
   516 {
   517     SDL_Window *window = renderer->window;
   518 
   519     if (window) {
   520         SDL_UpdateWindowSurface(window);
   521     }
   522 }
   523 
   524 static void
   525 SW_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   526 {
   527     SDL_Surface *surface = (SDL_Surface *) texture->driverdata;
   528 
   529     SDL_FreeSurface(surface);
   530 }
   531 
   532 static void
   533 SW_DestroyRenderer(SDL_Renderer * renderer)
   534 {
   535     SW_RenderData *data = (SW_RenderData *) renderer->driverdata;
   536 
   537     if (data) {
   538         SDL_free(data);
   539     }
   540     SDL_free(renderer);
   541 }
   542 
   543 #endif /* !SDL_RENDER_DISABLED */
   544 
   545 /* vi: set ts=4 sw=4 expandtab: */