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