src/render/SDL_render.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 03 Feb 2011 00:19:40 -0800
changeset 5156 307ccc9c135e
parent 5154 fb424691cfc7
child 5157 657543cc92f9
permissions -rw-r--r--
Made it possible to create a texture of any format, even if not supported by the renderer.
This allows me to reduce the set of formats supported by the renderers to the most optimal set, for a nice speed boost.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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 /* The SDL 2D rendering system */
    25 
    26 #include "SDL_render.h"
    27 #include "SDL_sysrender.h"
    28 #include "../video/SDL_pixels_c.h"
    29 
    30 
    31 #define CHECK_RENDERER_MAGIC(renderer, retval) \
    32     if (!renderer || renderer->magic != &renderer_magic) { \
    33         SDL_SetError("Invalid renderer"); \
    34         return retval; \
    35     }
    36 
    37 #define CHECK_TEXTURE_MAGIC(texture, retval) \
    38     if (!texture || texture->magic != &texture_magic) { \
    39         SDL_SetError("Invalid texture"); \
    40         return retval; \
    41     }
    42 
    43 
    44 static const SDL_RenderDriver *render_drivers[] = {
    45 #if SDL_VIDEO_RENDER_D3D
    46     &D3D_RenderDriver,
    47 #endif
    48 #if SDL_VIDEO_RENDER_OGL
    49     &GL_RenderDriver,
    50 #endif
    51 #if SDL_VIDEO_RENDER_OGL_ES
    52     &GL_ES_RenderDriver,
    53 #endif
    54     &SW_RenderDriver
    55 };
    56 static char renderer_magic;
    57 static char texture_magic;
    58 
    59 int
    60 SDL_GetNumRenderDrivers(void)
    61 {
    62     return SDL_arraysize(render_drivers);
    63 }
    64 
    65 int
    66 SDL_GetRenderDriverInfo(int index, SDL_RendererInfo * info)
    67 {
    68     if (index < 0 || index >= SDL_GetNumRenderDrivers()) {
    69         SDL_SetError("index must be in the range of 0 - %d",
    70                      SDL_GetNumRenderDrivers() - 1);
    71         return -1;
    72     }
    73     *info = render_drivers[index]->info;
    74     return 0;
    75 }
    76 
    77 static int
    78 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
    79 {
    80     SDL_Renderer *renderer = (SDL_Renderer *)userdata;
    81 
    82     if (event->type == SDL_WINDOWEVENT && renderer->WindowEvent) {
    83         SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
    84         if (window == renderer->window) {
    85             renderer->WindowEvent(renderer, &event->window);
    86         }
    87     }
    88     return 0;
    89 }
    90 
    91 SDL_Renderer *
    92 SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
    93 {
    94     SDL_Renderer *renderer = NULL;
    95     int n = SDL_GetNumRenderDrivers();
    96 
    97     if (index < 0) {
    98         char *override = SDL_getenv("SDL_VIDEO_RENDERER");
    99 
   100         if (override) {
   101             for (index = 0; index < n; ++index) {
   102                 const SDL_RenderDriver *driver = render_drivers[index];
   103 
   104                 if (SDL_strcasecmp(override, driver->info.name) == 0) {
   105                     /* Create a new renderer instance */
   106                     renderer = driver->CreateRenderer(window, flags);
   107                     break;
   108                 }
   109             }
   110         } else {
   111             for (index = 0; index < n; ++index) {
   112                 const SDL_RenderDriver *driver = render_drivers[index];
   113 
   114                 if ((driver->info.flags & flags) == flags) {
   115                     /* Create a new renderer instance */
   116                     renderer = driver->CreateRenderer(window, flags);
   117                     if (renderer) {
   118                         /* Yay, we got one! */
   119                         break;
   120                     }
   121                 }
   122             }
   123         }
   124         if (index == n) {
   125             SDL_SetError("Couldn't find matching render driver");
   126             return NULL;
   127         }
   128     } else {
   129         if (index >= SDL_GetNumRenderDrivers()) {
   130             SDL_SetError("index must be -1 or in the range of 0 - %d",
   131                          SDL_GetNumRenderDrivers() - 1);
   132             return NULL;
   133         }
   134         /* Create a new renderer instance */
   135         renderer = render_drivers[index]->CreateRenderer(window, flags);
   136     }
   137 
   138     if (renderer) {
   139         renderer->magic = &renderer_magic;
   140 
   141         SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
   142     }
   143     return renderer;
   144 }
   145 
   146 int
   147 SDL_GetRendererInfo(SDL_Renderer * renderer, SDL_RendererInfo * info)
   148 {
   149     CHECK_RENDERER_MAGIC(renderer, -1);
   150 
   151     *info = renderer->info;
   152     return 0;
   153 }
   154 
   155 static SDL_bool
   156 IsSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   157 {
   158     Uint32 i;
   159 
   160     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   161         if (renderer->info.texture_formats[i] == format) {
   162             return SDL_TRUE;
   163         }
   164     }
   165     return SDL_FALSE;
   166 }
   167 
   168 static Uint32
   169 GetClosestSupportedFormat(SDL_Renderer * renderer, Uint32 format)
   170 {
   171     Uint32 i;
   172     SDL_bool hasAlpha = SDL_ISPIXELFORMAT_ALPHA(format);
   173 
   174     /* We just want to match the first format that has the same channels */
   175     for (i = 0; i < renderer->info.num_texture_formats; ++i) {
   176         if (SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == hasAlpha) {
   177             return renderer->info.texture_formats[i];
   178         }
   179     }
   180     return renderer->info.texture_formats[0];
   181 }
   182 
   183 SDL_Texture *
   184 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
   185 {
   186     SDL_Texture *texture;
   187 
   188     CHECK_RENDERER_MAGIC(renderer, NULL);
   189 
   190     if (SDL_ISPIXELFORMAT_INDEXED(format)) {
   191         SDL_SetError("Palettized textures are not supported");
   192         return NULL;
   193     }
   194     if (w <= 0 || h <= 0) {
   195         SDL_SetError("Texture dimensions can't be 0");
   196         return NULL;
   197     }
   198     texture = (SDL_Texture *) SDL_calloc(1, sizeof(*texture));
   199     if (!texture) {
   200         SDL_OutOfMemory();
   201         return NULL;
   202     }
   203     texture->magic = &texture_magic;
   204     texture->format = format;
   205     texture->access = access;
   206     texture->w = w;
   207     texture->h = h;
   208     texture->r = 255;
   209     texture->g = 255;
   210     texture->b = 255;
   211     texture->a = 255;
   212     texture->renderer = renderer;
   213     texture->next = renderer->textures;
   214     if (renderer->textures) {
   215         renderer->textures->prev = texture;
   216     }
   217     renderer->textures = texture;
   218 
   219     if (IsSupportedFormat(renderer, format)) {
   220         if (renderer->CreateTexture(renderer, texture) < 0) {
   221             SDL_DestroyTexture(texture);
   222             return 0;
   223         }
   224     } else {
   225         texture->native = SDL_CreateTexture(renderer,
   226                                 GetClosestSupportedFormat(renderer, format),
   227                                 access, w, h);
   228         if (!texture->native) {
   229             SDL_DestroyTexture(texture);
   230             return NULL;
   231         }
   232 
   233         if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
   234             texture->yuv = SDL_SW_CreateYUVTexture(format, w, h);
   235             if (!texture->yuv) {
   236                 SDL_DestroyTexture(texture);
   237                 return NULL;
   238             }
   239         } else if (access == SDL_TEXTUREACCESS_STREAMING) {
   240             /* The pitch is 4 byte aligned */
   241             texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3);
   242             texture->pixels = SDL_malloc(texture->pitch * h);
   243             if (!texture->pixels) {
   244                 SDL_DestroyTexture(texture);
   245                 return NULL;
   246             }
   247         }
   248     }
   249     return texture;
   250 }
   251 
   252 SDL_Texture *
   253 SDL_CreateTextureFromSurface(SDL_Renderer * renderer, Uint32 format, SDL_Surface * surface)
   254 {
   255     SDL_Texture *texture;
   256     Uint32 requested_format = format;
   257     SDL_PixelFormat *fmt;
   258     int bpp;
   259     Uint32 Rmask, Gmask, Bmask, Amask;
   260 
   261     CHECK_RENDERER_MAGIC(renderer, NULL);
   262 
   263     if (!surface) {
   264         SDL_SetError("SDL_CreateTextureFromSurface() passed NULL surface");
   265         return NULL;
   266     }
   267     fmt = surface->format;
   268 
   269     if (format) {
   270         if (!SDL_PixelFormatEnumToMasks
   271             (format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
   272             SDL_SetError("Unknown pixel format");
   273             return 0;
   274         }
   275     } else {
   276         SDL_bool hasColorkey;
   277         SDL_BlendMode blendMode;
   278         SDL_bool hasBlending;
   279 
   280         hasColorkey = (SDL_GetColorKey(surface, NULL) == 0);
   281 
   282         SDL_GetSurfaceBlendMode(surface, &blendMode);
   283         hasBlending = (blendMode == SDL_BLENDMODE_BLEND);
   284 
   285         if (surface->format->Amask || (!hasColorkey && !hasBlending)) {
   286             Uint32 it;
   287             int pfmt;
   288 
   289             /* Pixel formats, sorted by best first */
   290             static const Uint32 sdl_pformats[] = {
   291                 SDL_PIXELFORMAT_ARGB8888,
   292                 SDL_PIXELFORMAT_RGBA8888,
   293                 SDL_PIXELFORMAT_ABGR8888,
   294                 SDL_PIXELFORMAT_BGRA8888,
   295                 SDL_PIXELFORMAT_RGB888,
   296                 SDL_PIXELFORMAT_BGR888,
   297                 SDL_PIXELFORMAT_RGB24,
   298                 SDL_PIXELFORMAT_BGR24,
   299                 SDL_PIXELFORMAT_RGB565,
   300                 SDL_PIXELFORMAT_BGR565,
   301                 SDL_PIXELFORMAT_ARGB1555,
   302                 SDL_PIXELFORMAT_RGBA5551,
   303                 SDL_PIXELFORMAT_ABGR1555,
   304                 SDL_PIXELFORMAT_BGRA5551,
   305                 SDL_PIXELFORMAT_RGB555,
   306                 SDL_PIXELFORMAT_BGR555,
   307                 SDL_PIXELFORMAT_ARGB4444,
   308                 SDL_PIXELFORMAT_RGBA4444,
   309                 SDL_PIXELFORMAT_ABGR4444,
   310                 SDL_PIXELFORMAT_BGRA4444,
   311                 SDL_PIXELFORMAT_RGB444,
   312                 SDL_PIXELFORMAT_ARGB2101010,
   313                 SDL_PIXELFORMAT_RGB332,
   314                 SDL_PIXELFORMAT_UNKNOWN
   315             };
   316 
   317             bpp = fmt->BitsPerPixel;
   318             Rmask = fmt->Rmask;
   319             Gmask = fmt->Gmask;
   320             Bmask = fmt->Bmask;
   321             Amask = fmt->Amask;
   322 
   323             format =
   324                 SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask);
   325             if (!format) {
   326                 SDL_SetError("Unknown pixel format");
   327                 return 0;
   328             }
   329 
   330             /* Search requested format in the supported texture */
   331             /* formats by current renderer                      */
   332             for (it = 0; it < renderer->info.num_texture_formats; it++) {
   333                 if (renderer->info.texture_formats[it] == format) {
   334                     break;
   335                 }
   336             }
   337 
   338             /* If requested format can't be found, search any best */
   339             /* format which renderer provides                      */
   340             if (it == renderer->info.num_texture_formats) {
   341                 pfmt = 0;
   342                 for (;;) {
   343                     if (sdl_pformats[pfmt] == SDL_PIXELFORMAT_UNKNOWN) {
   344                         break;
   345                     }
   346 
   347                     for (it = 0; it < renderer->info.num_texture_formats;
   348                          it++) {
   349                         if (renderer->info.texture_formats[it] ==
   350                             sdl_pformats[pfmt]) {
   351                             break;
   352                         }
   353                     }
   354 
   355                     if (it != renderer->info.num_texture_formats) {
   356                         /* The best format has been found */
   357                         break;
   358                     }
   359                     pfmt++;
   360                 }
   361 
   362                 /* If any format can't be found, then return an error */
   363                 if (it == renderer->info.num_texture_formats) {
   364                     SDL_SetError
   365                         ("Any of the supported pixel formats can't be found");
   366                     return 0;
   367                 }
   368 
   369                 /* Convert found pixel format back to color masks */
   370                 if (SDL_PixelFormatEnumToMasks
   371                     (renderer->info.texture_formats[it], &bpp, &Rmask, &Gmask,
   372                      &Bmask, &Amask) != SDL_TRUE) {
   373                     SDL_SetError("Unknown pixel format");
   374                     return 0;
   375                 }
   376             }
   377         } else {
   378             /* Need a format with alpha */
   379             Uint32 it;
   380             int apfmt;
   381 
   382             /* Pixel formats with alpha, sorted by best first */
   383             static const Uint32 sdl_alpha_pformats[] = {
   384                 SDL_PIXELFORMAT_ARGB8888,
   385                 SDL_PIXELFORMAT_RGBA8888,
   386                 SDL_PIXELFORMAT_ABGR8888,
   387                 SDL_PIXELFORMAT_BGRA8888,
   388                 SDL_PIXELFORMAT_ARGB1555,
   389                 SDL_PIXELFORMAT_RGBA5551,
   390                 SDL_PIXELFORMAT_ABGR1555,
   391                 SDL_PIXELFORMAT_BGRA5551,
   392                 SDL_PIXELFORMAT_ARGB4444,
   393                 SDL_PIXELFORMAT_RGBA4444,
   394                 SDL_PIXELFORMAT_ABGR4444,
   395                 SDL_PIXELFORMAT_BGRA4444,
   396                 SDL_PIXELFORMAT_ARGB2101010,
   397                 SDL_PIXELFORMAT_UNKNOWN
   398             };
   399 
   400             if (surface->format->Amask) {
   401                 /* If surface already has alpha, then try an original */
   402                 /* surface format first                               */
   403                 bpp = fmt->BitsPerPixel;
   404                 Rmask = fmt->Rmask;
   405                 Gmask = fmt->Gmask;
   406                 Bmask = fmt->Bmask;
   407                 Amask = fmt->Amask;
   408             } else {
   409                 bpp = 32;
   410                 Rmask = 0x00FF0000;
   411                 Gmask = 0x0000FF00;
   412                 Bmask = 0x000000FF;
   413                 Amask = 0xFF000000;
   414             }
   415 
   416             format =
   417                 SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask);
   418             if (!format) {
   419                 SDL_SetError("Unknown pixel format");
   420                 return 0;
   421             }
   422 
   423             /* Search this format in the supported texture formats */
   424             /* by current renderer                                 */
   425             for (it = 0; it < renderer->info.num_texture_formats; it++) {
   426                 if (renderer->info.texture_formats[it] == format) {
   427                     break;
   428                 }
   429             }
   430 
   431             /* If this format can't be found, search any best       */
   432             /* compatible format with alpha which renderer provides */
   433             if (it == renderer->info.num_texture_formats) {
   434                 apfmt = 0;
   435                 for (;;) {
   436                     if (sdl_alpha_pformats[apfmt] == SDL_PIXELFORMAT_UNKNOWN) {
   437                         break;
   438                     }
   439 
   440                     for (it = 0; it < renderer->info.num_texture_formats;
   441                          it++) {
   442                         if (renderer->info.texture_formats[it] ==
   443                             sdl_alpha_pformats[apfmt]) {
   444                             break;
   445                         }
   446                     }
   447 
   448                     if (it != renderer->info.num_texture_formats) {
   449                         /* Compatible format has been found */
   450                         break;
   451                     }
   452                     apfmt++;
   453                 }
   454 
   455                 /* If compatible format can't be found, then return an error */
   456                 if (it == renderer->info.num_texture_formats) {
   457                     SDL_SetError("Compatible pixel format can't be found");
   458                     return 0;
   459                 }
   460 
   461                 /* Convert found pixel format back to color masks */
   462                 if (SDL_PixelFormatEnumToMasks
   463                     (renderer->info.texture_formats[it], &bpp, &Rmask, &Gmask,
   464                      &Bmask, &Amask) != SDL_TRUE) {
   465                     SDL_SetError("Unknown pixel format");
   466                     return 0;
   467                 }
   468             }
   469         }
   470 
   471         format = SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask);
   472         if (!format) {
   473             SDL_SetError("Unknown pixel format");
   474             return 0;
   475         }
   476     }
   477 
   478     texture =
   479         SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
   480                           surface->w, surface->h);
   481     if (!texture && !requested_format) {
   482         SDL_DisplayMode desktop_mode;
   483         SDL_GetDesktopDisplayMode(&desktop_mode);
   484         format = desktop_mode.format;
   485         texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC,
   486                                     surface->w, surface->h);
   487     }
   488     if (!texture) {
   489         return 0;
   490     }
   491     if (bpp == fmt->BitsPerPixel && Rmask == fmt->Rmask && Gmask == fmt->Gmask
   492         && Bmask == fmt->Bmask && Amask == fmt->Amask) {
   493         if (SDL_MUSTLOCK(surface)) {
   494             SDL_LockSurface(surface);
   495             SDL_UpdateTexture(texture, NULL, surface->pixels,
   496                               surface->pitch);
   497             SDL_UnlockSurface(surface);
   498         } else {
   499             SDL_UpdateTexture(texture, NULL, surface->pixels,
   500                               surface->pitch);
   501         }
   502     } else {
   503         SDL_PixelFormat dst_fmt;
   504         SDL_Surface *dst = NULL;
   505 
   506         /* Set up a destination surface for the texture update */
   507         SDL_InitFormat(&dst_fmt, bpp, Rmask, Gmask, Bmask, Amask);
   508         dst = SDL_ConvertSurface(surface, &dst_fmt, 0);
   509         if (dst) {
   510             SDL_UpdateTexture(texture, NULL, dst->pixels, dst->pitch);
   511             SDL_FreeSurface(dst);
   512         }
   513         if (!dst) {
   514             SDL_DestroyTexture(texture);
   515             return 0;
   516         }
   517     }
   518 
   519     {
   520         Uint8 r, g, b, a;
   521         SDL_BlendMode blendMode;
   522 
   523         SDL_GetSurfaceColorMod(surface, &r, &g, &b);
   524         SDL_SetTextureColorMod(texture, r, g, b);
   525 
   526         SDL_GetSurfaceAlphaMod(surface, &a);
   527         SDL_SetTextureAlphaMod(texture, a);
   528 
   529         if (SDL_GetColorKey(surface, NULL) == 0) {
   530             /* We converted to a texture with alpha format */
   531             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
   532         } else {
   533             SDL_GetSurfaceBlendMode(surface, &blendMode);
   534             SDL_SetTextureBlendMode(texture, blendMode);
   535         }
   536     }
   537     return texture;
   538 }
   539 
   540 int
   541 SDL_QueryTexture(SDL_Texture * texture, Uint32 * format, int *access,
   542                  int *w, int *h)
   543 {
   544     CHECK_TEXTURE_MAGIC(texture, -1);
   545 
   546     if (format) {
   547         *format = texture->format;
   548     }
   549     if (access) {
   550         *access = texture->access;
   551     }
   552     if (w) {
   553         *w = texture->w;
   554     }
   555     if (h) {
   556         *h = texture->h;
   557     }
   558     return 0;
   559 }
   560 
   561 int
   562 SDL_SetTextureColorMod(SDL_Texture * texture, Uint8 r, Uint8 g, Uint8 b)
   563 {
   564     SDL_Renderer *renderer;
   565 
   566     CHECK_TEXTURE_MAGIC(texture, -1);
   567 
   568     renderer = texture->renderer;
   569     if (r < 255 || g < 255 || b < 255) {
   570         texture->modMode |= SDL_TEXTUREMODULATE_COLOR;
   571     } else {
   572         texture->modMode &= ~SDL_TEXTUREMODULATE_COLOR;
   573     }
   574     texture->r = r;
   575     texture->g = g;
   576     texture->b = b;
   577     if (texture->native) {
   578         return SDL_SetTextureColorMod(texture->native, r, g, b);
   579     } else if (renderer->SetTextureColorMod) {
   580         return renderer->SetTextureColorMod(renderer, texture);
   581     } else {
   582         return 0;
   583     }
   584 }
   585 
   586 int
   587 SDL_GetTextureColorMod(SDL_Texture * texture, Uint8 * r, Uint8 * g,
   588                        Uint8 * b)
   589 {
   590     SDL_Renderer *renderer;
   591 
   592     CHECK_TEXTURE_MAGIC(texture, -1);
   593 
   594     renderer = texture->renderer;
   595     if (r) {
   596         *r = texture->r;
   597     }
   598     if (g) {
   599         *g = texture->g;
   600     }
   601     if (b) {
   602         *b = texture->b;
   603     }
   604     return 0;
   605 }
   606 
   607 int
   608 SDL_SetTextureAlphaMod(SDL_Texture * texture, Uint8 alpha)
   609 {
   610     SDL_Renderer *renderer;
   611 
   612     CHECK_TEXTURE_MAGIC(texture, -1);
   613 
   614     renderer = texture->renderer;
   615     if (alpha < 255) {
   616         texture->modMode |= SDL_TEXTUREMODULATE_ALPHA;
   617     } else {
   618         texture->modMode &= ~SDL_TEXTUREMODULATE_ALPHA;
   619     }
   620     texture->a = alpha;
   621     if (texture->native) {
   622         return SDL_SetTextureAlphaMod(texture->native, alpha);
   623     } else if (renderer->SetTextureAlphaMod) {
   624         return renderer->SetTextureAlphaMod(renderer, texture);
   625     } else {
   626         return 0;
   627     }
   628 }
   629 
   630 int
   631 SDL_GetTextureAlphaMod(SDL_Texture * texture, Uint8 * alpha)
   632 {
   633     CHECK_TEXTURE_MAGIC(texture, -1);
   634 
   635     if (alpha) {
   636         *alpha = texture->a;
   637     }
   638     return 0;
   639 }
   640 
   641 int
   642 SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
   643 {
   644     SDL_Renderer *renderer;
   645 
   646     CHECK_TEXTURE_MAGIC(texture, -1);
   647 
   648     renderer = texture->renderer;
   649     texture->blendMode = blendMode;
   650     if (texture->native) {
   651         return SDL_SetTextureBlendMode(texture, blendMode);
   652     } else if (renderer->SetTextureBlendMode) {
   653         return renderer->SetTextureBlendMode(renderer, texture);
   654     } else {
   655         return 0;
   656     }
   657 }
   658 
   659 int
   660 SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
   661 {
   662     CHECK_TEXTURE_MAGIC(texture, -1);
   663 
   664     if (blendMode) {
   665         *blendMode = texture->blendMode;
   666     }
   667     return 0;
   668 }
   669 
   670 static int
   671 SDL_UpdateTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
   672                      const void *pixels, int pitch)
   673 {
   674     SDL_Texture *native = texture->native;
   675     SDL_Rect full_rect;
   676 
   677     if (SDL_SW_UpdateYUVTexture(texture->yuv, rect, pixels, pitch) < 0) {
   678         return -1;
   679     }
   680 
   681     full_rect.x = 0;
   682     full_rect.y = 0;
   683     full_rect.w = texture->w;
   684     full_rect.h = texture->h;
   685     rect = &full_rect;
   686 
   687     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   688         /* We can lock the texture and copy to it */
   689         void *native_pixels;
   690         int native_pitch;
   691 
   692         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   693             return -1;
   694         }
   695         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   696                             rect->w, rect->h, native_pixels, native_pitch);
   697         SDL_UnlockTexture(native);
   698     } else {
   699         /* Use a temporary buffer for updating */
   700         void *temp_pixels;
   701         int temp_pitch;
   702 
   703         temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   704         temp_pixels = SDL_malloc(rect->h * temp_pitch);
   705         if (!temp_pixels) {
   706             SDL_OutOfMemory();
   707             return -1;
   708         }
   709         SDL_SW_CopyYUVToRGB(texture->yuv, rect, native->format,
   710                             rect->w, rect->h, temp_pixels, temp_pitch);
   711         SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   712         SDL_free(temp_pixels);
   713     }
   714     return 0;
   715 }
   716 
   717 static int
   718 SDL_UpdateTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
   719                         const void *pixels, int pitch)
   720 {
   721     SDL_Texture *native = texture->native;
   722 
   723     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
   724         /* We can lock the texture and copy to it */
   725         void *native_pixels;
   726         int native_pitch;
   727 
   728         if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   729             return -1;
   730         }
   731         SDL_ConvertPixels(rect->w, rect->h,
   732                           texture->format, pixels, pitch,
   733                           native->format, native_pixels, native_pitch);
   734         SDL_UnlockTexture(native);
   735     } else {
   736         /* Use a temporary buffer for updating */
   737         void *temp_pixels;
   738         int temp_pitch;
   739 
   740         temp_pitch = (((rect->w * SDL_BYTESPERPIXEL(native->format)) + 3) & ~3);
   741         temp_pixels = SDL_malloc(rect->h * temp_pitch);
   742         if (!temp_pixels) {
   743             SDL_OutOfMemory();
   744             return -1;
   745         }
   746         SDL_ConvertPixels(rect->w, rect->h,
   747                           texture->format, pixels, pitch,
   748                           native->format, temp_pixels, temp_pitch);
   749         SDL_UpdateTexture(native, rect, temp_pixels, temp_pitch);
   750         SDL_free(temp_pixels);
   751     }
   752     return 0;
   753 }
   754 
   755 int
   756 SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect,
   757                   const void *pixels, int pitch)
   758 {
   759     SDL_Renderer *renderer;
   760     SDL_Rect full_rect;
   761 
   762     CHECK_TEXTURE_MAGIC(texture, -1);
   763 
   764     if (!rect) {
   765         full_rect.x = 0;
   766         full_rect.y = 0;
   767         full_rect.w = texture->w;
   768         full_rect.h = texture->h;
   769         rect = &full_rect;
   770     }
   771 
   772     if (texture->yuv) {
   773         return SDL_UpdateTextureYUV(texture, rect, pixels, pitch);
   774     } else if (texture->native) {
   775         return SDL_UpdateTextureNative(texture, rect, pixels, pitch);
   776     } else {
   777         renderer = texture->renderer;
   778         return renderer->UpdateTexture(renderer, texture, rect, pixels, pitch);
   779     }
   780 }
   781 
   782 static int
   783 SDL_LockTextureYUV(SDL_Texture * texture, const SDL_Rect * rect,
   784                    void **pixels, int *pitch)
   785 {
   786     return SDL_SW_LockYUVTexture(texture->yuv, rect, pixels, pitch);
   787 }
   788 
   789 static int
   790 SDL_LockTextureNative(SDL_Texture * texture, const SDL_Rect * rect,
   791                       void **pixels, int *pitch)
   792 {
   793     texture->locked_rect = *rect;
   794     *pixels = (void *) ((Uint8 *) texture->pixels +
   795                         rect->y * texture->pitch +
   796                         rect->x * SDL_BYTESPERPIXEL(texture->format));
   797     *pitch = texture->pitch;
   798     return 0;
   799 }
   800 
   801 int
   802 SDL_LockTexture(SDL_Texture * texture, const SDL_Rect * rect,
   803                 void **pixels, int *pitch)
   804 {
   805     SDL_Renderer *renderer;
   806     SDL_Rect full_rect;
   807 
   808     CHECK_TEXTURE_MAGIC(texture, -1);
   809 
   810     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   811         SDL_SetError("SDL_LockTexture(): texture must be streaming");
   812         return -1;
   813     }
   814 
   815     if (!rect) {
   816         full_rect.x = 0;
   817         full_rect.y = 0;
   818         full_rect.w = texture->w;
   819         full_rect.h = texture->h;
   820         rect = &full_rect;
   821     }
   822 
   823     if (texture->yuv) {
   824         return SDL_LockTextureYUV(texture, rect, pixels, pitch);
   825     } else if (texture->native) {
   826         return SDL_LockTextureNative(texture, rect, pixels, pitch);
   827     } else {
   828         renderer = texture->renderer;
   829         return renderer->LockTexture(renderer, texture, rect, pixels, pitch);
   830     }
   831 }
   832 
   833 static void
   834 SDL_UnlockTextureYUV(SDL_Texture * texture)
   835 {
   836     SDL_Texture *native = texture->native;
   837     void *native_pixels;
   838     int native_pitch;
   839     SDL_Rect rect;
   840 
   841     rect.x = 0;
   842     rect.y = 0;
   843     rect.w = texture->w;
   844     rect.h = texture->h;
   845 
   846     if (SDL_LockTexture(native, &rect, &native_pixels, &native_pitch) < 0) {
   847         return;
   848     }
   849     SDL_SW_CopyYUVToRGB(texture->yuv, &rect, native->format,
   850                         rect.w, rect.h, native_pixels, native_pitch);
   851     SDL_UnlockTexture(native);
   852 }
   853 
   854 void
   855 SDL_UnlockTextureNative(SDL_Texture * texture)
   856 {
   857     SDL_Texture *native = texture->native;
   858     void *native_pixels;
   859     int native_pitch;
   860     const SDL_Rect *rect = &texture->locked_rect;
   861     const void* pixels = (void *) ((Uint8 *) texture->pixels +
   862                         rect->y * texture->pitch +
   863                         rect->x * SDL_BYTESPERPIXEL(texture->format));
   864     int pitch = texture->pitch;
   865 
   866     if (SDL_LockTexture(native, rect, &native_pixels, &native_pitch) < 0) {
   867         return;
   868     }
   869     SDL_ConvertPixels(rect->w, rect->h,
   870                       texture->format, pixels, pitch,
   871                       native->format, native_pixels, native_pitch);
   872     SDL_UnlockTexture(native);
   873 }
   874 
   875 void
   876 SDL_UnlockTexture(SDL_Texture * texture)
   877 {
   878     SDL_Renderer *renderer;
   879 
   880     CHECK_TEXTURE_MAGIC(texture, );
   881 
   882     if (texture->access != SDL_TEXTUREACCESS_STREAMING) {
   883         return;
   884     }
   885     if (texture->yuv) {
   886         SDL_UnlockTextureYUV(texture);
   887     } else if (texture->native) {
   888         SDL_UnlockTextureNative(texture);
   889     } else {
   890         renderer = texture->renderer;
   891         renderer->UnlockTexture(renderer, texture);
   892     }
   893 }
   894 
   895 int
   896 SDL_SetRenderDrawColor(SDL_Renderer * renderer,
   897                        Uint8 r, Uint8 g, Uint8 b, Uint8 a)
   898 {
   899     CHECK_RENDERER_MAGIC(renderer, -1);
   900 
   901     renderer->r = r;
   902     renderer->g = g;
   903     renderer->b = b;
   904     renderer->a = a;
   905     return 0;
   906 }
   907 
   908 int
   909 SDL_GetRenderDrawColor(SDL_Renderer * renderer,
   910                        Uint8 * r, Uint8 * g, Uint8 * b, Uint8 * a)
   911 {
   912     CHECK_RENDERER_MAGIC(renderer, -1);
   913 
   914     if (r) {
   915         *r = renderer->r;
   916     }
   917     if (g) {
   918         *g = renderer->g;
   919     }
   920     if (b) {
   921         *b = renderer->b;
   922     }
   923     if (a) {
   924         *a = renderer->a;
   925     }
   926     return 0;
   927 }
   928 
   929 int
   930 SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
   931 {
   932     CHECK_RENDERER_MAGIC(renderer, -1);
   933 
   934     renderer->blendMode = blendMode;
   935     return 0;
   936 }
   937 
   938 int
   939 SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, SDL_BlendMode *blendMode)
   940 {
   941     CHECK_RENDERER_MAGIC(renderer, -1);
   942 
   943     *blendMode = renderer->blendMode;
   944     return 0;
   945 }
   946 
   947 int
   948 SDL_RenderClear(SDL_Renderer * renderer)
   949 {
   950     CHECK_RENDERER_MAGIC(renderer, -1);
   951 
   952     if (!renderer->RenderClear) {
   953         SDL_BlendMode blendMode = renderer->blendMode;
   954         int status;
   955 
   956         if (blendMode >= SDL_BLENDMODE_BLEND) {
   957             SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
   958         }
   959 
   960         status = SDL_RenderFillRect(renderer, NULL);
   961 
   962         if (blendMode >= SDL_BLENDMODE_BLEND) {
   963             SDL_SetRenderDrawBlendMode(renderer, blendMode);
   964         }
   965         return status;
   966     }
   967     return renderer->RenderClear(renderer);
   968 }
   969 
   970 int
   971 SDL_RenderDrawPoint(SDL_Renderer * renderer, int x, int y)
   972 {
   973     SDL_Point point;
   974 
   975     point.x = x;
   976     point.y = y;
   977     return SDL_RenderDrawPoints(renderer, &point, 1);
   978 }
   979 
   980 int
   981 SDL_RenderDrawPoints(SDL_Renderer * renderer,
   982                      const SDL_Point * points, int count)
   983 {
   984     CHECK_RENDERER_MAGIC(renderer, -1);
   985 
   986     if (!points) {
   987         SDL_SetError("SDL_RenderDrawPoints(): Passed NULL points");
   988         return -1;
   989     }
   990     if (count < 1) {
   991         return 0;
   992     }
   993     return renderer->RenderDrawPoints(renderer, points, count);
   994 }
   995 
   996 int
   997 SDL_RenderDrawLine(SDL_Renderer * renderer, int x1, int y1, int x2, int y2)
   998 {
   999     SDL_Point points[2];
  1000 
  1001     points[0].x = x1;
  1002     points[0].y = y1;
  1003     points[1].x = x2;
  1004     points[1].y = y2;
  1005     return SDL_RenderDrawLines(renderer, points, 2);
  1006 }
  1007 
  1008 int
  1009 SDL_RenderDrawLines(SDL_Renderer * renderer,
  1010                     const SDL_Point * points, int count)
  1011 {
  1012     CHECK_RENDERER_MAGIC(renderer, -1);
  1013 
  1014     if (!points) {
  1015         SDL_SetError("SDL_RenderDrawLines(): Passed NULL points");
  1016         return -1;
  1017     }
  1018     if (count < 2) {
  1019         return 0;
  1020     }
  1021     return renderer->RenderDrawLines(renderer, points, count);
  1022 }
  1023 
  1024 int
  1025 SDL_RenderDrawRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  1026 {
  1027     SDL_Rect full_rect;
  1028     SDL_Point points[5];
  1029 
  1030     CHECK_RENDERER_MAGIC(renderer, -1);
  1031 
  1032     /* If 'rect' == NULL, then outline the whole surface */
  1033     if (!rect) {
  1034         SDL_Window *window = renderer->window;
  1035 
  1036         full_rect.x = 0;
  1037         full_rect.y = 0;
  1038         SDL_GetWindowSize(window, &full_rect.w, &full_rect.h);
  1039         rect = &full_rect;
  1040     }
  1041 
  1042     points[0].x = rect->x;
  1043     points[0].y = rect->y;
  1044     points[1].x = rect->x+rect->w-1;
  1045     points[1].y = rect->y;
  1046     points[2].x = rect->x+rect->w-1;
  1047     points[2].y = rect->y+rect->h-1;
  1048     points[3].x = rect->x;
  1049     points[3].y = rect->y+rect->h-1;
  1050     points[4].x = rect->x;
  1051     points[4].y = rect->y;
  1052     return SDL_RenderDrawLines(renderer, points, 5);
  1053 }
  1054 
  1055 int
  1056 SDL_RenderDrawRects(SDL_Renderer * renderer,
  1057                     const SDL_Rect ** rects, int count)
  1058 {
  1059     int i;
  1060 
  1061     CHECK_RENDERER_MAGIC(renderer, -1);
  1062 
  1063     if (!rects) {
  1064         SDL_SetError("SDL_RenderDrawRects(): Passed NULL rects");
  1065         return -1;
  1066     }
  1067     if (count < 1) {
  1068         return 0;
  1069     }
  1070 
  1071     /* Check for NULL rect, which means fill entire window */
  1072     for (i = 0; i < count; ++i) {
  1073         if (SDL_RenderDrawRect(renderer, rects[i]) < 0) {
  1074             return -1;
  1075         }
  1076     }
  1077     return 0;
  1078 }
  1079 
  1080 int
  1081 SDL_RenderFillRect(SDL_Renderer * renderer, const SDL_Rect * rect)
  1082 {
  1083     return SDL_RenderFillRects(renderer, &rect, 1);
  1084 }
  1085 
  1086 int
  1087 SDL_RenderFillRects(SDL_Renderer * renderer,
  1088                     const SDL_Rect ** rects, int count)
  1089 {
  1090     int i;
  1091 
  1092     CHECK_RENDERER_MAGIC(renderer, -1);
  1093 
  1094     if (!rects) {
  1095         SDL_SetError("SDL_RenderFillRects(): Passed NULL rects");
  1096         return -1;
  1097     }
  1098     if (count < 1) {
  1099         return 0;
  1100     }
  1101 
  1102     /* Check for NULL rect, which means fill entire window */
  1103     for (i = 0; i < count; ++i) {
  1104         if (rects[i] == NULL) {
  1105             SDL_Window *window = renderer->window;
  1106             SDL_Rect full_rect;
  1107             const SDL_Rect *rect;
  1108 
  1109             full_rect.x = 0;
  1110             full_rect.y = 0;
  1111             SDL_GetWindowSize(window, &full_rect.w, &full_rect.h);
  1112             rect = &full_rect;
  1113             return renderer->RenderFillRects(renderer, &rect, 1);
  1114         }
  1115     }
  1116     return renderer->RenderFillRects(renderer, rects, count);
  1117 }
  1118 
  1119 int
  1120 SDL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
  1121                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
  1122 {
  1123     SDL_Window *window;
  1124     SDL_Rect real_srcrect;
  1125     SDL_Rect real_dstrect;
  1126 
  1127     CHECK_RENDERER_MAGIC(renderer, -1);
  1128     CHECK_TEXTURE_MAGIC(texture, -1);
  1129 
  1130     if (renderer != texture->renderer) {
  1131         SDL_SetError("Texture was not created with this renderer");
  1132         return -1;
  1133     }
  1134     window = renderer->window;
  1135 
  1136     real_srcrect.x = 0;
  1137     real_srcrect.y = 0;
  1138     real_srcrect.w = texture->w;
  1139     real_srcrect.h = texture->h;
  1140     if (srcrect) {
  1141         if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
  1142             return 0;
  1143         }
  1144     }
  1145 
  1146     real_dstrect.x = 0;
  1147     real_dstrect.y = 0;
  1148     SDL_GetWindowSize(window, &real_dstrect.w, &real_dstrect.h);
  1149     if (dstrect) {
  1150         if (!SDL_IntersectRect(dstrect, &real_dstrect, &real_dstrect)) {
  1151             return 0;
  1152         }
  1153         /* Clip srcrect by the same amount as dstrect was clipped */
  1154         if (dstrect->w != real_dstrect.w) {
  1155             int deltax = (real_dstrect.x - dstrect->x);
  1156             int deltaw = (real_dstrect.w - dstrect->w);
  1157             real_srcrect.x += (deltax * real_srcrect.w) / dstrect->w;
  1158             real_srcrect.w += (deltaw * real_srcrect.w) / dstrect->w;
  1159         }
  1160         if (dstrect->h != real_dstrect.h) {
  1161             int deltay = (real_dstrect.y - dstrect->y);
  1162             int deltah = (real_dstrect.h - dstrect->h);
  1163             real_srcrect.y += (deltay * real_srcrect.h) / dstrect->h;
  1164             real_srcrect.h += (deltah * real_srcrect.h) / dstrect->h;
  1165         }
  1166     }
  1167 
  1168     if (texture->native) {
  1169         texture = texture->native;
  1170     }
  1171 
  1172     return renderer->RenderCopy(renderer, texture, &real_srcrect,
  1173                                 &real_dstrect);
  1174 }
  1175 
  1176 int
  1177 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1178                      Uint32 format, void * pixels, int pitch)
  1179 {
  1180     SDL_Window *window;
  1181     SDL_Rect real_rect;
  1182 
  1183     CHECK_RENDERER_MAGIC(renderer, -1);
  1184 
  1185     if (!renderer->RenderReadPixels) {
  1186         SDL_Unsupported();
  1187         return -1;
  1188     }
  1189     window = renderer->window;
  1190 
  1191     if (!format) {
  1192         format = SDL_GetWindowPixelFormat(window);
  1193     }
  1194 
  1195     real_rect.x = 0;
  1196     real_rect.y = 0;
  1197     SDL_GetWindowSize(window, &real_rect.w, &real_rect.h);
  1198     if (rect) {
  1199         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
  1200             return 0;
  1201         }
  1202         if (real_rect.y > rect->y) {
  1203             pixels = (Uint8 *)pixels + pitch * (real_rect.y - rect->y);
  1204         }
  1205         if (real_rect.x > rect->x) {
  1206             int bpp = SDL_BYTESPERPIXEL(SDL_GetWindowPixelFormat(window));
  1207             pixels = (Uint8 *)pixels + bpp * (real_rect.x - rect->x);
  1208         }
  1209     }
  1210 
  1211     return renderer->RenderReadPixels(renderer, &real_rect,
  1212                                       format, pixels, pitch);
  1213 }
  1214 
  1215 int
  1216 SDL_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1217                       Uint32 format, const void * pixels, int pitch)
  1218 {
  1219     SDL_Window *window;
  1220     SDL_Rect real_rect;
  1221 
  1222     CHECK_RENDERER_MAGIC(renderer, -1);
  1223 
  1224     if (!renderer->RenderWritePixels) {
  1225         SDL_Unsupported();
  1226         return -1;
  1227     }
  1228     window = renderer->window;
  1229 
  1230     if (!format) {
  1231         format = SDL_GetWindowPixelFormat(window);
  1232     }
  1233 
  1234     real_rect.x = 0;
  1235     real_rect.y = 0;
  1236     SDL_GetWindowSize(window, &real_rect.w, &real_rect.h);
  1237     if (rect) {
  1238         if (!SDL_IntersectRect(rect, &real_rect, &real_rect)) {
  1239             return 0;
  1240         }
  1241         if (real_rect.y > rect->y) {
  1242             pixels = (const Uint8 *)pixels + pitch * (real_rect.y - rect->y);
  1243         }
  1244         if (real_rect.x > rect->x) {
  1245             int bpp = SDL_BYTESPERPIXEL(SDL_GetWindowPixelFormat(window));
  1246             pixels = (const Uint8 *)pixels + bpp * (real_rect.x - rect->x);
  1247         }
  1248     }
  1249 
  1250     return renderer->RenderWritePixels(renderer, &real_rect,
  1251                                        format, pixels, pitch);
  1252 }
  1253 
  1254 void
  1255 SDL_RenderPresent(SDL_Renderer * renderer)
  1256 {
  1257     CHECK_RENDERER_MAGIC(renderer, );
  1258 
  1259     renderer->RenderPresent(renderer);
  1260 }
  1261 
  1262 void
  1263 SDL_DestroyTexture(SDL_Texture * texture)
  1264 {
  1265     SDL_Renderer *renderer;
  1266 
  1267     CHECK_TEXTURE_MAGIC(texture, );
  1268     texture->magic = NULL;
  1269 
  1270     renderer = texture->renderer;
  1271     if (texture->next) {
  1272         texture->next->prev = texture->prev;
  1273     }
  1274     if (texture->prev) {
  1275         texture->prev->next = texture->next;
  1276     } else {
  1277         renderer->textures = texture->next;
  1278     }
  1279 
  1280     if (texture->native) {
  1281         SDL_DestroyTexture(texture->native);
  1282     }
  1283     if (texture->yuv) {
  1284         SDL_SW_DestroyYUVTexture(texture->yuv);
  1285     }
  1286     if (texture->pixels) {
  1287         SDL_free(texture->pixels);
  1288     }
  1289 
  1290     renderer->DestroyTexture(renderer, texture);
  1291     SDL_free(texture);
  1292 }
  1293 
  1294 void
  1295 SDL_DestroyRenderer(SDL_Renderer * renderer)
  1296 {
  1297     CHECK_RENDERER_MAGIC(renderer, );
  1298 
  1299     SDL_DelEventWatch(SDL_RendererEventWatch, renderer);
  1300 
  1301     /* Free existing textures for this renderer */
  1302     while (renderer->textures) {
  1303         SDL_DestroyTexture(renderer->textures);
  1304     }
  1305 
  1306     /* It's no longer magical... */
  1307     renderer->magic = NULL;
  1308 
  1309     /* Free the renderer instance */
  1310     renderer->DestroyRenderer(renderer);
  1311 }
  1312 
  1313 /* vi: set ts=4 sw=4 expandtab: */