src/video/nds/SDL_ndsrender.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 31 Jan 2011 22:44:43 -0800
changeset 5138 da10636e5eca
parent 4929 aa8888658021
child 5140 e743b9c3f6d6
permissions -rw-r--r--
Making the API simpler, scaling is always defined as linear interpolation and should be supported as much as possible on all renderers.
     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 
    23 #include <stdio.h>
    24 #include <stdlib.h>
    25 #include <nds.h>
    26 //#include <nds/arm9/video.h>
    27 //#include <nds/arm9/sprite.h>
    28 //#include <nds/arm9/trig_lut.h>
    29 
    30 #include "SDL_config.h"
    31 
    32 #include "SDL_video.h"
    33 #include "../SDL_sysvideo.h"
    34 #include "../SDL_yuv_sw_c.h"
    35 #include "../SDL_renderer_sw.h"
    36 
    37 /* SDL NDS renderer implementation */
    38 
    39 static SDL_Renderer *NDS_CreateRenderer(SDL_Window * window, Uint32 flags);
    40 static int NDS_ActivateRenderer(SDL_Renderer * renderer);
    41 static int NDS_DisplayModeChanged(SDL_Renderer * renderer);
    42 static int NDS_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    43 static int NDS_QueryTexturePixels(SDL_Renderer * renderer,
    44                                   SDL_Texture * texture, void **pixels,
    45                                   int *pitch);
    46 static int NDS_SetTexturePalette(SDL_Renderer * renderer,
    47                                  SDL_Texture * texture,
    48                                  const SDL_Color * colors, int firstcolor,
    49                                  int ncolors);
    50 static int NDS_GetTexturePalette(SDL_Renderer * renderer,
    51                                  SDL_Texture * texture, SDL_Color * colors,
    52                                  int firstcolor, int ncolors);
    53 static int NDS_SetTextureColorMod(SDL_Renderer * renderer,
    54                                   SDL_Texture * texture);
    55 static int NDS_SetTextureAlphaMod(SDL_Renderer * renderer,
    56                                   SDL_Texture * texture);
    57 static int NDS_SetTextureBlendMode(SDL_Renderer * renderer,
    58                                    SDL_Texture * texture);
    59 static int NDS_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    60                              const SDL_Rect * rect, const void *pixels,
    61                              int pitch);
    62 static int NDS_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    63                            const SDL_Rect * rect, int markDirty,
    64                            void **pixels, int *pitch);
    65 static void NDS_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    66 static int NDS_RenderFill(SDL_Renderer * renderer, Uint8 r, Uint8 g,
    67                           Uint8 b, Uint8 a, const SDL_Rect * rect);
    68 static int NDS_RenderCopy(SDL_Renderer * renderer,
    69                           SDL_Texture * texture,
    70                           const SDL_Rect * srcrect, const SDL_Rect * dstrect);
    71 static void NDS_RenderPresent(SDL_Renderer * renderer);
    72 static void NDS_DestroyTexture(SDL_Renderer * renderer,
    73                                SDL_Texture * texture);
    74 static void NDS_DestroyRenderer(SDL_Renderer * renderer);
    75 
    76 
    77 SDL_RenderDriver NDS_RenderDriver = {
    78     NDS_CreateRenderer,
    79     {"nds",                     /* char* name */
    80      (SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTDISCARD | SDL_RENDERER_PRESENTVSYNC),  /* u32 flags */
    81      (SDL_TEXTUREMODULATE_NONE),        /* u32 mod_modes */
    82      (SDL_BLENDMODE_MASK),      /* u32 blend_modes */
    83      3,                         /* u32 num_texture_formats */
    84      {
    85       SDL_PIXELFORMAT_INDEX8,
    86       SDL_PIXELFORMAT_ABGR1555,
    87       SDL_PIXELFORMAT_BGR555,
    88       },                        /* u32 texture_formats[20] */
    89      (256),                     /* int max_texture_width */
    90      (256),                     /* int max_texture_height */
    91      }
    92 };
    93 
    94 typedef struct
    95 {
    96     u8 bg_taken[4];
    97     OamState *oam;
    98     int sub;
    99 } NDS_RenderData;
   100 
   101 typedef struct
   102 {
   103     enum
   104     { NDSTX_BG, NDSTX_SPR } type;       /* represented in a bg or sprite. */
   105     int hw_index;               /* index of sprite in OAM or bg from libnds */
   106     int pitch, bpp;             /* useful information about the texture */
   107     struct
   108     {
   109         int x, y;
   110     } scale;                    /* x/y stretch (24.8 fixed point) */
   111     struct
   112     {
   113         int x, y;
   114     } scroll;                   /* x/y offset */
   115     int rotate;                 /* -32768 to 32767, texture rotation */
   116     u16 *vram_pixels;           /* where the pixel data is stored (a pointer into VRAM) */
   117     u16 *vram_palette;          /* where the palette data is stored if it's indexed. */
   118     /*int size; */
   119 } NDS_TextureData;
   120 
   121 
   122 
   123 SDL_Renderer *
   124 NDS_CreateRenderer(SDL_Window * window, Uint32 flags)
   125 {
   126     SDL_VideoDisplay *display = window->display;
   127     SDL_DisplayMode *displayMode = &display->current_mode;
   128     SDL_Renderer *renderer;
   129     NDS_RenderData *data;
   130     int i, n;
   131     int bpp;
   132     Uint32 Rmask, Gmask, Bmask, Amask;
   133 
   134     if (!SDL_PixelFormatEnumToMasks(displayMode->format, &bpp,
   135                                     &Rmask, &Gmask, &Bmask, &Amask)) {
   136         SDL_SetError("Unknown display format");
   137         return NULL;
   138     }
   139     switch (displayMode->format) {
   140     case SDL_PIXELFORMAT_INDEX8:
   141     case SDL_PIXELFORMAT_ABGR1555:
   142     case SDL_PIXELFORMAT_BGR555:
   143         /* okay */
   144         break;
   145     case SDL_PIXELFORMAT_RGB555:
   146     case SDL_PIXELFORMAT_RGB565:
   147     case SDL_PIXELFORMAT_ARGB1555:
   148         /* we'll take these too for now */
   149         break;
   150     default:
   151         SDL_SetError("Warning: wrong display format for NDS!\n");
   152         break;
   153     }
   154 
   155     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   156     if (!renderer) {
   157         SDL_OutOfMemory();
   158         return NULL;
   159     }
   160 
   161     data = (NDS_RenderData *) SDL_malloc(sizeof(*data));
   162     if (!data) {
   163         NDS_DestroyRenderer(renderer);
   164         SDL_OutOfMemory();
   165         return NULL;
   166     }
   167     SDL_zerop(data);
   168 
   169     renderer->RenderFill = NDS_RenderFill;
   170     renderer->RenderCopy = NDS_RenderCopy;
   171     renderer->RenderPresent = NDS_RenderPresent;
   172     renderer->DestroyRenderer = NDS_DestroyRenderer;
   173     renderer->info.name = NDS_RenderDriver.info.name;
   174     renderer->info.flags = 0;
   175     renderer->window = window;
   176     renderer->driverdata = data;
   177     renderer->CreateTexture = NDS_CreateTexture;
   178     renderer->QueryTexturePixels = NDS_QueryTexturePixels;
   179     renderer->SetTexturePalette = NDS_SetTexturePalette;
   180     renderer->GetTexturePalette = NDS_GetTexturePalette;
   181     renderer->SetTextureColorMod = NDS_SetTextureColorMod;
   182     renderer->SetTextureAlphaMod = NDS_SetTextureAlphaMod;
   183     renderer->SetTextureBlendMode = NDS_SetTextureBlendMode;
   184     renderer->UpdateTexture = NDS_UpdateTexture;
   185     renderer->LockTexture = NDS_LockTexture;
   186     renderer->UnlockTexture = NDS_UnlockTexture;
   187     renderer->DestroyTexture = NDS_DestroyTexture;
   188 
   189     renderer->info.mod_modes = NDS_RenderDriver.info.mod_modes;
   190     renderer->info.blend_modes = NDS_RenderDriver.info.blend_modes;
   191     renderer->info.num_texture_formats =
   192         NDS_RenderDriver.info.num_texture_formats;
   193     SDL_memcpy(renderer->info.texture_formats,
   194                NDS_RenderDriver.info.texture_formats,
   195                sizeof(renderer->info.texture_formats));
   196     renderer->info.max_texture_width =
   197         NDS_RenderDriver.info.max_texture_width;
   198     renderer->info.max_texture_height =
   199         NDS_RenderDriver.info.max_texture_height;
   200 
   201     data->sub = 0;              /* TODO: this is hard-coded to the "main" screen.
   202                                    figure out how to detect whether to set it to
   203                                    "sub" screen.  window->id, perhaps? */
   204     data->bg_taken[2] = data->bg_taken[3] = 0;
   205 
   206     return renderer;
   207 }
   208 
   209 static int
   210 NDS_ActivateRenderer(SDL_Renderer * renderer)
   211 {
   212     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   213 
   214     return 0;
   215 }
   216 
   217 static int
   218 NDS_DisplayModeChanged(SDL_Renderer * renderer)
   219 {
   220     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   221 
   222     return 0;
   223 }
   224 
   225 static int
   226 NDS_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   227 {
   228     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   229     NDS_TextureData *txdat = NULL;
   230     int i;
   231     int bpp;
   232     Uint32 Rmask, Gmask, Bmask, Amask;
   233 
   234     if (!SDL_PixelFormatEnumToMasks
   235         (texture->format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
   236         SDL_SetError("Unknown texture format");
   237         return -1;
   238     }
   239 
   240     /* conditional statements on w/h to place it as bg/sprite
   241        depending on which one it fits. */
   242     if (texture->w <= 64 && texture->h <= 64) {
   243         int whichspr = -1;
   244         printf("NDS_CreateTexture: Tried to make a sprite.\n");
   245         txdat->type = NDSTX_SPR;
   246 #if 0
   247         for (i = 0; i < SPRITE_COUNT; ++i) {
   248             if (data->oam_copy.spriteBuffer[i].attribute[0] & ATTR0_DISABLED) {
   249                 whichspr = i;
   250                 break;
   251             }
   252         }
   253         if (whichspr >= 0) {
   254             SpriteEntry *sprent = &(data->oam_copy.spriteBuffer[whichspr]);
   255             int maxside = texture->w > texture->h ? texture->w : texture->h;
   256             int pitch;
   257 
   258             texture->driverdata = SDL_calloc(1, sizeof(NDS_TextureData));
   259             txdat = (NDS_TextureData *) texture->driverdata;
   260             if (!txdat) {
   261                 SDL_OutOfMemory();
   262                 return -1;
   263             }
   264 
   265             sprent->objMode = OBJMODE_BITMAP;
   266             sprent->posX = 0;
   267             sprent->posY = 0;
   268             sprent->colMode = OBJCOLOR_16;      /* OBJCOLOR_256 for INDEX8 */
   269 
   270             /* the first 32 sprites get transformation matrices.
   271                first come, first served */
   272             if (whichspr < MATRIX_COUNT) {
   273                 sprent->isRotoscale = 1;
   274                 sprent->rsMatrixIdx = whichspr;
   275             }
   276 
   277             /* containing shape (square or 2:1 rectangles) */
   278             sprent->objShape = OBJSHAPE_SQUARE;
   279             if (texture->w / 2 >= texture->h) {
   280                 sprent->objShape = OBJSHAPE_WIDE;
   281             } else if (texture->h / 2 >= texture->w) {
   282                 sprent->objShape = OBJSHAPE_TALL;
   283             }
   284 
   285             /* size in pixels */
   286             /* FIXME: "pitch" is hardcoded for 2bytes per pixel. */
   287             sprent->objSize = OBJSIZE_64;
   288             pitch = 128;
   289             if (maxside <= 8) {
   290                 sprent->objSize = OBJSIZE_8;
   291                 pitch = 16;
   292             } else if (maxside <= 16) {
   293                 sprent->objSize = OBJSIZE_16;
   294                 pitch = 32;
   295             } else if (maxside <= 32) {
   296                 sprent->objSize = OBJSIZE_32;
   297                 pitch = 64;
   298             }
   299 
   300             /* FIXME: this is hard-coded and will obviously only work for one
   301                sprite-texture.  tells it to look at the beginning of SPRITE_GFX
   302                for its pixels. */
   303             sprent->tileIdx = 0;
   304 
   305             /* now for the texture data */
   306             txdat->type = NDSTX_SPR;
   307             txdat->hw_index = whichspr;
   308             txdat->dim.hdx = 0x100;
   309             txdat->dim.hdy = 0;
   310             txdat->dim.vdx = 0;
   311             txdat->dim.vdy = 0x100;
   312             txdat->dim.pitch = pitch;
   313             txdat->dim.bpp = bpp;
   314             txdat->vram_pixels =
   315                 (u16 *) (data->sub ? SPRITE_GFX_SUB : SPRITE_GFX);
   316             /* FIXME: use tileIdx*boundary
   317                to point to proper location */
   318         } else {
   319             SDL_SetError("Out of NDS sprites.");
   320         }
   321 #endif
   322     } else if (texture->w <= 256 && texture->h <= 256) {
   323         int whichbg = -1, base = 0;
   324         if (!data->bg_taken[2]) {
   325             whichbg = 2;
   326         } else if (!data->bg_taken[3]) {
   327             whichbg = 3;
   328             base = 4;
   329         }
   330         if (whichbg >= 0) {
   331             texture->driverdata = SDL_calloc(1, sizeof(NDS_TextureData));
   332             txdat = (NDS_TextureData *) texture->driverdata;
   333             if (!txdat) {
   334                 SDL_OutOfMemory();
   335                 return -1;
   336             }
   337 // hard-coded for 256x256 for now...
   338 // TODO: a series of if-elseif-else's to find the closest but larger size.
   339             if (!data->sub) {
   340                 if (bpp == 8) {
   341                     txdat->hw_index =
   342                         bgInit(whichbg, BgType_Bmp8, BgSize_B8_256x256, 0, 0);
   343                 } else {
   344                     txdat->hw_index =
   345                         bgInit(whichbg, BgType_Bmp16, BgSize_B16_256x256, 0,
   346                                0);
   347                 }
   348             } else {
   349                 if (bpp == 8) {
   350                     txdat->hw_index =
   351                         bgInitSub(whichbg, BgType_Bmp8, BgSize_B8_256x256, 0,
   352                                   0);
   353                 } else {
   354                     txdat->hw_index =
   355                         bgInitSub(whichbg, BgType_Bmp16, BgSize_B16_256x256,
   356                                   0, 0);
   357                 }
   358             }
   359 
   360 /*   useful functions
   361         bgGetGfxPtr(bg3);            
   362 		bgSetCenter(bg3, rcX, rcY);
   363 		bgSetRotateScale(bg3, angle, scaleX, scaleY);
   364 		bgSetScroll(bg3, scrollX, scrollY);
   365 		bgUpdate(bg3);
   366 */
   367             txdat->type = NDSTX_BG;
   368             txdat->pitch = (texture->w) * (bpp / 8);
   369             txdat->bpp = bpp;
   370             txdat->rotate = 0;
   371             txdat->scale.x = 0x100;
   372             txdat->scale.y = 0x100;
   373             txdat->scroll.x = 0;
   374             txdat->scroll.y = 0;
   375             txdat->vram_pixels = (u16 *) bgGetGfxPtr(txdat->hw_index);
   376 
   377             bgSetCenter(txdat->hw_index, 0, 0);
   378             bgSetRotateScale(txdat->hw_index, txdat->rotate, txdat->scale.x,
   379                              txdat->scale.y);
   380             bgSetScroll(txdat->hw_index, txdat->scroll.x, txdat->scroll.y);
   381             bgUpdate(txdat->hw_index);
   382 
   383             data->bg_taken[whichbg] = 1;
   384             /*txdat->size = txdat->dim.pitch * texture->h; */
   385         } else {
   386             SDL_SetError("Out of NDS backgrounds.");
   387         }
   388     } else {
   389         SDL_SetError("Texture too big for NDS hardware.");
   390     }
   391 
   392     if (!texture->driverdata) {
   393         return -1;
   394     }
   395 
   396     return 0;
   397 }
   398 
   399 static int
   400 NDS_QueryTexturePixels(SDL_Renderer * renderer, SDL_Texture * texture,
   401                        void **pixels, int *pitch)
   402 {
   403     NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
   404     *pixels = txdat->vram_pixels;
   405     *pitch = txdat->pitch;
   406     return 0;
   407 }
   408 
   409 static int
   410 NDS_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   411                   const SDL_Rect * rect, const void *pixels, int pitch)
   412 {
   413     NDS_TextureData *txdat;
   414     Uint8 *src, *dst;
   415     int row;
   416     size_t length;
   417 
   418     txdat = (NDS_TextureData *) texture->driverdata;
   419 
   420     src = (Uint8 *) pixels;
   421     dst =
   422         (Uint8 *) txdat->vram_pixels + rect->y * txdat->pitch + rect->x *
   423         ((txdat->bpp + 1) / 8);
   424     length = rect->w * ((txdat->bpp + 1) / 8);
   425 
   426     if (rect->w == texture->w) {
   427         dmaCopy(src, dst, length * rect->h);
   428     } else {
   429         for (row = 0; row < rect->h; ++row) {
   430             dmaCopy(src, dst, length);
   431             src += pitch;
   432             dst += txdat->pitch;
   433         }
   434     }
   435 
   436     return 0;
   437 }
   438 
   439 static int
   440 NDS_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   441                 const SDL_Rect * rect, int markDirty, void **pixels,
   442                 int *pitch)
   443 {
   444     NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
   445 
   446     *pixels = (void *) ((u8 *) txdat->vram_pixels + rect->y * txdat->pitch +
   447                         rect->x * ((txdat->bpp + 1) / 8));
   448     *pitch = txdat->pitch;
   449 
   450     return 0;
   451 }
   452 
   453 static void
   454 NDS_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   455 {
   456     /* stub! */
   457 }
   458 
   459 static int
   460 NDS_RenderFill(SDL_Renderer * renderer, Uint8 r, Uint8 g, Uint8 b,
   461                Uint8 a, const SDL_Rect * rect)
   462 {
   463     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   464     SDL_Rect real_rect = *rect;
   465     u16 color;
   466     int i, j;
   467 
   468     printf("NDS_RenderFill: stub\n");
   469     color = RGB8(r, g, b);      /* macro in libnds that makes an ARGB1555 pixel */
   470     /* TODO: make a single-color sprite and stretch it.
   471        calculate the "HDX" width modifier of the sprite by:
   472        let S be the actual sprite's width (like, 32 pixels for example)
   473        let R be the rectangle's width (maybe 50 pixels)
   474        HDX = (R<<8) / S;
   475        (it's fixed point, hence the bit shift.  same goes for vertical.
   476        be sure to use 32-bit int's for the bit shift before the division!)
   477      */
   478 
   479     return 0;
   480 }
   481 
   482 static int
   483 NDS_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   484                const SDL_Rect * srcrect, const SDL_Rect * dstrect)
   485 {
   486     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   487     NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
   488     SDL_Window *window = renderer->window;
   489     SDL_VideoDisplay *display = window->display;
   490     int Bpp = SDL_BYTESPERPIXEL(texture->format);
   491 
   492     if (txdat->type == NDSTX_BG) {
   493         txdat->scroll.x = dstrect->x;
   494         txdat->scroll.y = dstrect->y;
   495     } else {
   496         /* sprites not fully implemented yet */
   497         printf("NDS_RenderCopy: used sprite!\n");
   498 //        SpriteEntry *spr = &(data->oam_copy.spriteBuffer[txdat->hw_index]);
   499 //        spr->posX = dstrect->x;
   500 //        spr->posY = dstrect->y;
   501 //        if (txdat->hw_index < MATRIX_COUNT && spr->isRotoscale) {          
   502 //        }
   503     }
   504 
   505     return 0;
   506 }
   507 
   508 
   509 static void
   510 NDS_RenderPresent(SDL_Renderer * renderer)
   511 {
   512     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   513     SDL_Window *window = renderer->window;
   514     SDL_VideoDisplay *display = window->display;
   515 
   516     /* update sprites */
   517 //    NDS_OAM_Update(&(data->oam_copy), data->sub);
   518     /* vsync for NDS */
   519     if (renderer->info.flags & SDL_RENDERER_PRESENTVSYNC) {
   520         swiWaitForVBlank();
   521     }
   522 }
   523 
   524 static void
   525 NDS_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   526 {
   527     NDS_TextureData *txdat = texture->driverdata;
   528     /* free anything else allocated for texture */
   529     SDL_free(txdat);
   530 }
   531 
   532 static void
   533 NDS_DestroyRenderer(SDL_Renderer * renderer)
   534 {
   535     NDS_RenderData *data = (NDS_RenderData *) renderer->driverdata;
   536     int i;
   537 
   538     if (data) {
   539         /* free anything else relevant if anything else is allocated. */
   540         SDL_free(data);
   541     }
   542     SDL_free(renderer);
   543 }
   544 
   545 static int
   546 NDS_SetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture,
   547                       const SDL_Color * colors, int firstcolor, int ncolors)
   548 {
   549     NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
   550     /* set 8-bit modes in the background control registers
   551        for backgrounds, BGn_CR |= BG_256_COLOR */
   552 
   553     return 0;
   554 }
   555 
   556 static int
   557 NDS_GetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture,
   558                       SDL_Color * colors, int firstcolor, int ncolors)
   559 {
   560     NDS_TextureData *txdat = (NDS_TextureData *) texture->driverdata;
   561     /* stub! */
   562     return 0;
   563 }
   564 
   565 static int
   566 NDS_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture)
   567 {
   568     /* stub! */
   569     return 0;
   570 }
   571 
   572 static int
   573 NDS_SetTextureAlphaMod(SDL_Renderer * renderer, SDL_Texture * texture)
   574 {
   575     /* stub! */
   576     return 0;
   577 }
   578 
   579 static int
   580 NDS_SetTextureBlendMode(SDL_Renderer * renderer, SDL_Texture * texture)
   581 {
   582     /* stub! */
   583     return 0;
   584 }
   585 
   586 /* vi: set ts=4 sw=4 expandtab: */