src/video/SDL_surface.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Mar 2011 00:30:05 -0800
changeset 5438 b705640cb34a
parent 5375 16877f74123c
child 5499 2b8e6d1e3817
permissions -rw-r--r--
Fixed bitmap order interpretation; SDL defaults to MSB ordering so a bitstream corresponds to a pixel stream.

The bitmap ordering is defined such that the numbering refers to the pixel index from left to right, and the number position refers to the bit position in the byte.

SDL_BITMAPORDER_4321 is the fourth pixel at the high bit and the first pixel at the low bit (LSBFirst)

SDL_BITMAPORDER_1234 is the first pixel at the high bit and the fourth pixel at the low bit (MSBFirst)
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@5262
     3
    Copyright (C) 1997-2011 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@0
    23
slouken@0
    24
#include "SDL_video.h"
slouken@1895
    25
#include "SDL_compat.h"
slouken@0
    26
#include "SDL_sysvideo.h"
slouken@0
    27
#include "SDL_blit.h"
slouken@0
    28
#include "SDL_RLEaccel_c.h"
slouken@0
    29
#include "SDL_pixels_c.h"
slouken@0
    30
icculus@1251
    31
slouken@0
    32
/* Public routines */
slouken@0
    33
/*
slouken@0
    34
 * Create an empty RGB surface of the appropriate depth
slouken@0
    35
 */
slouken@1895
    36
SDL_Surface *
slouken@1895
    37
SDL_CreateRGBSurface(Uint32 flags,
slouken@1895
    38
                     int width, int height, int depth,
slouken@1895
    39
                     Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
slouken@0
    40
{
slouken@1895
    41
    SDL_Surface *surface;
slouken@5288
    42
    Uint32 format;
slouken@0
    43
slouken@2807
    44
    /* The flags are no longer used, make the compiler happy */
slouken@4461
    45
    (void)flags;
slouken@2807
    46
slouken@5288
    47
    /* Get the pixel format */
slouken@5288
    48
    format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask);
slouken@5288
    49
    if (format == SDL_PIXELFORMAT_UNKNOWN) {
slouken@5288
    50
        SDL_SetError("Unknown pixel format");
slouken@5288
    51
        return NULL;
slouken@5288
    52
    }
slouken@5288
    53
slouken@1895
    54
    /* Allocate the surface */
slouken@1920
    55
    surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface));
slouken@1895
    56
    if (surface == NULL) {
slouken@1895
    57
        SDL_OutOfMemory();
slouken@1895
    58
        return NULL;
slouken@1895
    59
    }
slouken@940
    60
slouken@5288
    61
    surface->format = SDL_AllocFormat(format);
slouken@1895
    62
    if (!surface->format) {
slouken@1895
    63
        SDL_FreeSurface(surface);
slouken@1895
    64
        return NULL;
slouken@1895
    65
    }
slouken@1895
    66
    surface->w = width;
slouken@1895
    67
    surface->h = height;
slouken@1895
    68
    surface->pitch = SDL_CalculatePitch(surface);
slouken@1895
    69
    SDL_SetClipRect(surface, NULL);
slouken@0
    70
slouken@5288
    71
    if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) {
slouken@1895
    72
        SDL_Palette *palette =
slouken@1895
    73
            SDL_AllocPalette((1 << surface->format->BitsPerPixel));
slouken@1895
    74
        if (!palette) {
slouken@1895
    75
            SDL_FreeSurface(surface);
slouken@1895
    76
            return NULL;
slouken@1895
    77
        }
slouken@5438
    78
        if (palette->ncolors == 2) {
slouken@1895
    79
            /* Create a black and white bitmap palette */
slouken@1895
    80
            palette->colors[0].r = 0xFF;
slouken@1895
    81
            palette->colors[0].g = 0xFF;
slouken@1895
    82
            palette->colors[0].b = 0xFF;
slouken@1895
    83
            palette->colors[1].r = 0x00;
slouken@1895
    84
            palette->colors[1].g = 0x00;
slouken@1895
    85
            palette->colors[1].b = 0x00;
slouken@1895
    86
        }
slouken@1895
    87
        SDL_SetSurfacePalette(surface, palette);
slouken@1895
    88
        SDL_FreePalette(palette);
slouken@1895
    89
    }
slouken@1895
    90
slouken@1895
    91
    /* Get the pixels */
slouken@1895
    92
    if (surface->w && surface->h) {
slouken@1895
    93
        surface->pixels = SDL_malloc(surface->h * surface->pitch);
slouken@1895
    94
        if (!surface->pixels) {
slouken@1895
    95
            SDL_FreeSurface(surface);
slouken@1895
    96
            SDL_OutOfMemory();
slouken@1895
    97
            return NULL;
slouken@1895
    98
        }
slouken@1895
    99
        /* This is important for bitmaps */
slouken@1895
   100
        SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
slouken@1895
   101
    }
slouken@1895
   102
slouken@1895
   103
    /* Allocate an empty mapping */
slouken@1895
   104
    surface->map = SDL_AllocBlitMap();
slouken@1895
   105
    if (!surface->map) {
slouken@1895
   106
        SDL_FreeSurface(surface);
slouken@1895
   107
        return NULL;
slouken@1895
   108
    }
slouken@1895
   109
slouken@2267
   110
    /* By default surface with an alpha mask are set up for blending */
slouken@2267
   111
    if (Amask) {
slouken@2884
   112
        SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
slouken@2267
   113
    }
slouken@2267
   114
slouken@1895
   115
    /* The surface is ready to go */
slouken@1895
   116
    surface->refcount = 1;
slouken@1895
   117
    return surface;
slouken@0
   118
}
slouken@1895
   119
slouken@0
   120
/*
slouken@0
   121
 * Create an RGB surface from an existing memory buffer
slouken@0
   122
 */
slouken@1895
   123
SDL_Surface *
slouken@1895
   124
SDL_CreateRGBSurfaceFrom(void *pixels,
slouken@1895
   125
                         int width, int height, int depth, int pitch,
slouken@1895
   126
                         Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
slouken@1895
   127
                         Uint32 Amask)
slouken@0
   128
{
slouken@1895
   129
    SDL_Surface *surface;
slouken@0
   130
slouken@1895
   131
    surface =
slouken@1895
   132
        SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
slouken@1895
   133
    if (surface != NULL) {
slouken@1895
   134
        surface->flags |= SDL_PREALLOC;
slouken@1895
   135
        surface->pixels = pixels;
slouken@1895
   136
        surface->w = width;
slouken@1895
   137
        surface->h = height;
slouken@1895
   138
        surface->pitch = pitch;
slouken@1895
   139
        SDL_SetClipRect(surface, NULL);
slouken@1895
   140
    }
slouken@1895
   141
    return surface;
slouken@0
   142
}
slouken@1895
   143
slouken@1895
   144
int
slouken@1895
   145
SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette)
slouken@1895
   146
{
slouken@5288
   147
    if (!surface) {
slouken@1895
   148
        SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface");
slouken@1895
   149
        return -1;
slouken@1895
   150
    }
slouken@5288
   151
    return SDL_SetPixelFormatPalette(surface->format, palette);
slouken@1895
   152
}
slouken@1895
   153
slouken@2267
   154
int
slouken@2267
   155
SDL_SetSurfaceRLE(SDL_Surface * surface, int flag)
slouken@0
   156
{
slouken@2267
   157
    int flags;
slouken@0
   158
slouken@2266
   159
    if (!surface) {
slouken@2266
   160
        return -1;
slouken@1895
   161
    }
slouken@0
   162
slouken@2267
   163
    flags = surface->map->info.flags;
slouken@1895
   164
    if (flag) {
slouken@2267
   165
        surface->map->info.flags |= SDL_COPY_RLE_DESIRED;
slouken@1895
   166
    } else {
slouken@2267
   167
        surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED;
slouken@1895
   168
    }
slouken@2267
   169
    if (surface->map->info.flags != flags) {
slouken@1895
   170
        SDL_InvalidateMap(surface->map);
slouken@1895
   171
    }
slouken@1895
   172
    return 0;
slouken@431
   173
}
slouken@0
   174
slouken@2267
   175
int
slouken@3560
   176
SDL_SetColorKey(SDL_Surface * surface, int flag, Uint32 key)
slouken@2266
   177
{
slouken@2266
   178
    int flags;
slouken@2266
   179
slouken@2266
   180
    if (!surface) {
slouken@2266
   181
        return -1;
slouken@2266
   182
    }
slouken@2266
   183
slouken@2266
   184
    if (flag & SDL_RLEACCEL) {
slouken@2266
   185
        SDL_SetSurfaceRLE(surface, 1);
slouken@2266
   186
    }
slouken@2266
   187
slouken@2266
   188
    flags = surface->map->info.flags;
slouken@2266
   189
    if (flag) {
slouken@2266
   190
        surface->map->info.flags |= SDL_COPY_COLORKEY;
slouken@2266
   191
        surface->map->info.colorkey = key;
slouken@2266
   192
    } else {
slouken@2266
   193
        surface->map->info.flags &= ~SDL_COPY_COLORKEY;
slouken@2266
   194
    }
slouken@2266
   195
    if (surface->map->info.flags != flags) {
slouken@2266
   196
        SDL_InvalidateMap(surface->map);
slouken@2266
   197
    }
slouken@2267
   198
slouken@2267
   199
    /* Compatibility mode */
slouken@2267
   200
    if (surface->map->info.flags & SDL_COPY_COLORKEY) {
slouken@2267
   201
        surface->flags |= SDL_SRCCOLORKEY;
slouken@2267
   202
    } else {
slouken@2267
   203
        surface->flags &= ~SDL_SRCCOLORKEY;
slouken@2267
   204
    }
slouken@2267
   205
slouken@2266
   206
    return 0;
slouken@2266
   207
}
slouken@2266
   208
slouken@3103
   209
int
slouken@3103
   210
SDL_GetColorKey(SDL_Surface * surface, Uint32 * key)
slouken@3103
   211
{
slouken@3103
   212
    if (!surface) {
slouken@3103
   213
        return -1;
slouken@3103
   214
    }
slouken@3103
   215
slouken@3103
   216
    if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
slouken@3103
   217
        return -1;
slouken@3103
   218
    }
slouken@3103
   219
slouken@3103
   220
    if (key) {
slouken@3103
   221
        *key = surface->map->info.colorkey;
slouken@3103
   222
    }
slouken@3103
   223
    return 0;
slouken@3103
   224
}
slouken@3103
   225
slouken@2785
   226
/* This is a fairly slow function to switch from colorkey to alpha */
slouken@2787
   227
static void
slouken@2786
   228
SDL_ConvertColorkeyToAlpha(SDL_Surface * surface)
slouken@2785
   229
{
slouken@2786
   230
    int x, y;
slouken@2785
   231
slouken@2786
   232
    if (!surface) {
slouken@2786
   233
        return;
slouken@2786
   234
    }
slouken@2785
   235
slouken@2786
   236
    if (!(surface->map->info.flags & SDL_COPY_COLORKEY) ||
slouken@2786
   237
        !surface->format->Amask) {
slouken@2786
   238
        return;
slouken@2786
   239
    }
slouken@2785
   240
slouken@2786
   241
    SDL_LockSurface(surface);
slouken@2785
   242
slouken@2786
   243
    switch (surface->format->BytesPerPixel) {
slouken@2786
   244
    case 2:
slouken@2786
   245
        {
slouken@2786
   246
            Uint16 *row, *spot;
slouken@2786
   247
            Uint16 ckey = (Uint16) surface->map->info.colorkey;
slouken@2786
   248
            Uint16 mask = (Uint16) (~surface->format->Amask);
slouken@2785
   249
slouken@2786
   250
            row = (Uint16 *) surface->pixels;
slouken@2786
   251
            for (y = surface->h; y--;) {
slouken@2786
   252
                spot = row;
slouken@2786
   253
                for (x = surface->w; x--;) {
slouken@2786
   254
                    if (*spot == ckey) {
slouken@2786
   255
                        *spot &= mask;
slouken@2786
   256
                    }
slouken@2786
   257
                    ++spot;
slouken@2786
   258
                }
slouken@2786
   259
                row += surface->pitch / 2;
slouken@2786
   260
            }
slouken@2786
   261
        }
slouken@2786
   262
        break;
slouken@2786
   263
    case 3:
slouken@2786
   264
        /* FIXME */
slouken@2786
   265
        break;
slouken@2786
   266
    case 4:
slouken@2786
   267
        {
slouken@2786
   268
            Uint32 *row, *spot;
slouken@2786
   269
            Uint32 ckey = surface->map->info.colorkey;
slouken@2786
   270
            Uint32 mask = ~surface->format->Amask;
slouken@2785
   271
slouken@2786
   272
            row = (Uint32 *) surface->pixels;
slouken@2786
   273
            for (y = surface->h; y--;) {
slouken@2786
   274
                spot = row;
slouken@2786
   275
                for (x = surface->w; x--;) {
slouken@2786
   276
                    if (*spot == ckey) {
slouken@2786
   277
                        *spot &= mask;
slouken@2786
   278
                    }
slouken@2786
   279
                    ++spot;
slouken@2786
   280
                }
slouken@2786
   281
                row += surface->pitch / 4;
slouken@2786
   282
            }
slouken@2786
   283
        }
slouken@2786
   284
        break;
slouken@2786
   285
    }
slouken@2785
   286
slouken@2786
   287
    SDL_UnlockSurface(surface);
slouken@2785
   288
slouken@2786
   289
    SDL_SetColorKey(surface, 0, 0);
slouken@2884
   290
    SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
slouken@2785
   291
}
slouken@2785
   292
slouken@2267
   293
int
slouken@2267
   294
SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b)
slouken@2266
   295
{
slouken@2266
   296
    int flags;
slouken@2266
   297
slouken@2266
   298
    if (!surface) {
slouken@2266
   299
        return -1;
slouken@2266
   300
    }
slouken@2266
   301
slouken@2266
   302
    surface->map->info.r = r;
slouken@2266
   303
    surface->map->info.g = g;
slouken@2266
   304
    surface->map->info.b = b;
slouken@2266
   305
slouken@2266
   306
    flags = surface->map->info.flags;
slouken@2266
   307
    if (r != 0xFF || g != 0xFF || b != 0xFF) {
slouken@2266
   308
        surface->map->info.flags |= SDL_COPY_MODULATE_COLOR;
slouken@2266
   309
    } else {
slouken@2266
   310
        surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR;
slouken@2266
   311
    }
slouken@2266
   312
    if (surface->map->info.flags != flags) {
slouken@2266
   313
        SDL_InvalidateMap(surface->map);
slouken@2266
   314
    }
slouken@2266
   315
    return 0;
slouken@2266
   316
}
slouken@2266
   317
slouken@2266
   318
slouken@2267
   319
int
slouken@2267
   320
SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b)
slouken@2266
   321
{
slouken@2266
   322
    if (!surface) {
slouken@2266
   323
        return -1;
slouken@2266
   324
    }
slouken@2266
   325
slouken@2266
   326
    if (r) {
slouken@2266
   327
        *r = surface->map->info.r;
slouken@2266
   328
    }
slouken@2266
   329
    if (g) {
slouken@2266
   330
        *g = surface->map->info.g;
slouken@2266
   331
    }
slouken@2266
   332
    if (b) {
slouken@2266
   333
        *b = surface->map->info.b;
slouken@2266
   334
    }
slouken@2266
   335
    return 0;
slouken@2266
   336
}
slouken@2266
   337
slouken@2267
   338
int
slouken@2267
   339
SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha)
slouken@2266
   340
{
slouken@2266
   341
    int flags;
slouken@2266
   342
slouken@2266
   343
    if (!surface) {
slouken@2266
   344
        return -1;
slouken@2266
   345
    }
slouken@2266
   346
slouken@2266
   347
    surface->map->info.a = alpha;
slouken@2266
   348
slouken@2266
   349
    flags = surface->map->info.flags;
slouken@2266
   350
    if (alpha != 0xFF) {
slouken@2266
   351
        surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA;
slouken@2266
   352
    } else {
slouken@2266
   353
        surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA;
slouken@2266
   354
    }
slouken@2266
   355
    if (surface->map->info.flags != flags) {
slouken@2266
   356
        SDL_InvalidateMap(surface->map);
slouken@2266
   357
    }
slouken@2266
   358
    return 0;
slouken@2266
   359
}
slouken@2266
   360
slouken@2267
   361
int
slouken@2267
   362
SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha)
slouken@2266
   363
{
slouken@2266
   364
    if (!surface) {
slouken@2266
   365
        return -1;
slouken@2266
   366
    }
slouken@2266
   367
slouken@2266
   368
    if (alpha) {
slouken@2266
   369
        *alpha = surface->map->info.a;
slouken@2266
   370
    }
slouken@2266
   371
    return 0;
slouken@2266
   372
}
slouken@2266
   373
slouken@2267
   374
int
slouken@4929
   375
SDL_SetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode blendMode)
slouken@2266
   376
{
slouken@2266
   377
    int flags, status;
slouken@2266
   378
slouken@2266
   379
    if (!surface) {
slouken@2266
   380
        return -1;
slouken@2266
   381
    }
slouken@2266
   382
slouken@2266
   383
    status = 0;
slouken@2266
   384
    flags = surface->map->info.flags;
slouken@5184
   385
    surface->map->info.flags &=
slouken@5184
   386
        ~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD);
slouken@2266
   387
    switch (blendMode) {
slouken@2884
   388
    case SDL_BLENDMODE_NONE:
slouken@2266
   389
        break;
slouken@2884
   390
    case SDL_BLENDMODE_BLEND:
slouken@2266
   391
        surface->map->info.flags |= SDL_COPY_BLEND;
slouken@2266
   392
        break;
slouken@2884
   393
    case SDL_BLENDMODE_ADD:
slouken@2266
   394
        surface->map->info.flags |= SDL_COPY_ADD;
slouken@2266
   395
        break;
slouken@5184
   396
    case SDL_BLENDMODE_MOD:
slouken@5184
   397
        surface->map->info.flags |= SDL_COPY_MOD;
slouken@5184
   398
        break;
slouken@2266
   399
    default:
slouken@2266
   400
        SDL_Unsupported();
slouken@2266
   401
        status = -1;
slouken@2266
   402
        break;
slouken@2266
   403
    }
slouken@2266
   404
slouken@2266
   405
    if (surface->map->info.flags != flags) {
slouken@2266
   406
        SDL_InvalidateMap(surface->map);
slouken@2266
   407
    }
slouken@2267
   408
slouken@2267
   409
    /* Compatibility mode */
slouken@2267
   410
    if (surface->map->info.flags & SDL_COPY_BLEND) {
slouken@2267
   411
        surface->flags |= SDL_SRCALPHA;
slouken@2267
   412
    } else {
slouken@2267
   413
        surface->flags &= ~SDL_SRCALPHA;
slouken@2267
   414
    }
slouken@2267
   415
slouken@2266
   416
    return status;
slouken@2266
   417
}
slouken@2266
   418
slouken@2267
   419
int
slouken@4929
   420
SDL_GetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode *blendMode)
slouken@2266
   421
{
slouken@2266
   422
    if (!surface) {
slouken@2266
   423
        return -1;
slouken@2266
   424
    }
slouken@2266
   425
slouken@2266
   426
    if (!blendMode) {
slouken@2266
   427
        return 0;
slouken@2266
   428
    }
slouken@2266
   429
slouken@5184
   430
    switch (surface->map->
slouken@5184
   431
            info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD)) {
slouken@2266
   432
    case SDL_COPY_BLEND:
slouken@2884
   433
        *blendMode = SDL_BLENDMODE_BLEND;
slouken@2266
   434
        break;
slouken@2266
   435
    case SDL_COPY_ADD:
slouken@2884
   436
        *blendMode = SDL_BLENDMODE_ADD;
slouken@2266
   437
        break;
slouken@5184
   438
    case SDL_COPY_MOD:
slouken@5184
   439
        *blendMode = SDL_BLENDMODE_MOD;
slouken@5184
   440
        break;
slouken@2266
   441
    default:
slouken@2884
   442
        *blendMode = SDL_BLENDMODE_NONE;
slouken@2266
   443
        break;
slouken@2266
   444
    }
slouken@2266
   445
    return 0;
slouken@2266
   446
}
slouken@2266
   447
slouken@1895
   448
SDL_bool
slouken@1895
   449
SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect)
slouken@0
   450
{
slouken@1895
   451
    SDL_Rect full_rect;
slouken@0
   452
slouken@1895
   453
    /* Don't do anything if there's no surface to act on */
slouken@1895
   454
    if (!surface) {
slouken@1895
   455
        return SDL_FALSE;
slouken@1895
   456
    }
slouken@0
   457
slouken@1895
   458
    /* Set up the full surface rectangle */
slouken@1895
   459
    full_rect.x = 0;
slouken@1895
   460
    full_rect.y = 0;
slouken@1895
   461
    full_rect.w = surface->w;
slouken@1895
   462
    full_rect.h = surface->h;
slouken@0
   463
slouken@1895
   464
    /* Set the clipping rectangle */
slouken@1895
   465
    if (!rect) {
slouken@1895
   466
        surface->clip_rect = full_rect;
slouken@4966
   467
        return SDL_TRUE;
slouken@1895
   468
    }
slouken@1895
   469
    return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
slouken@0
   470
}
slouken@1895
   471
slouken@1895
   472
void
slouken@1895
   473
SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect)
slouken@0
   474
{
slouken@1895
   475
    if (surface && rect) {
slouken@1895
   476
        *rect = surface->clip_rect;
slouken@1895
   477
    }
slouken@0
   478
}
slouken@1895
   479
slouken@0
   480
/* 
slouken@0
   481
 * Set up a blit between two surfaces -- split into three parts:
slouken@0
   482
 * The upper part, SDL_UpperBlit(), performs clipping and rectangle 
slouken@0
   483
 * verification.  The lower part is a pointer to a low level
slouken@0
   484
 * accelerated blitting function.
slouken@0
   485
 *
slouken@0
   486
 * These parts are separated out and each used internally by this 
slouken@0
   487
 * library in the optimimum places.  They are exported so that if
slouken@0
   488
 * you know exactly what you are doing, you can optimize your code
slouken@0
   489
 * by calling the one(s) you need.
slouken@0
   490
 */
slouken@1895
   491
int
slouken@1895
   492
SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect,
slouken@1895
   493
              SDL_Surface * dst, SDL_Rect * dstrect)
slouken@0
   494
{
slouken@1895
   495
    /* Check to make sure the blit mapping is valid */
slouken@1895
   496
    if ((src->map->dst != dst) ||
slouken@5288
   497
        (dst->format->palette &&
slouken@5288
   498
         src->map->palette_version != dst->format->palette->version)) {
slouken@1895
   499
        if (SDL_MapSurface(src, dst) < 0) {
slouken@1895
   500
            return (-1);
slouken@1895
   501
        }
bob@2328
   502
        /* just here for debugging */
bob@2329
   503
/*         printf */
bob@2329
   504
/*             ("src = 0x%08X src->flags = %08X src->map->info.flags = %08x\ndst = 0x%08X dst->flags = %08X dst->map->info.flags = %08X\nsrc->map->blit = 0x%08x\n", */
bob@2329
   505
/*              src, dst->flags, src->map->info.flags, dst, dst->flags, */
bob@2329
   506
/*              dst->map->info.flags, src->map->blit); */
slouken@1895
   507
    }
slouken@2257
   508
    return (src->map->blit(src, srcrect, dst, dstrect));
slouken@0
   509
}
slouken@0
   510
slouken@0
   511
slouken@1895
   512
int
slouken@4949
   513
SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect,
slouken@1895
   514
              SDL_Surface * dst, SDL_Rect * dstrect)
slouken@0
   515
{
slouken@1895
   516
    SDL_Rect fulldst;
slouken@1895
   517
    int srcx, srcy, w, h;
slouken@0
   518
slouken@1895
   519
    /* Make sure the surfaces aren't locked */
slouken@1895
   520
    if (!src || !dst) {
slouken@1895
   521
        SDL_SetError("SDL_UpperBlit: passed a NULL surface");
slouken@1895
   522
        return (-1);
slouken@1895
   523
    }
slouken@1895
   524
    if (src->locked || dst->locked) {
slouken@1895
   525
        SDL_SetError("Surfaces must not be locked during blit");
slouken@1895
   526
        return (-1);
slouken@1895
   527
    }
slouken@0
   528
slouken@1895
   529
    /* If the destination rectangle is NULL, use the entire dest surface */
slouken@1895
   530
    if (dstrect == NULL) {
slouken@1895
   531
        fulldst.x = fulldst.y = 0;
slouken@1895
   532
        dstrect = &fulldst;
slouken@1895
   533
    }
slouken@0
   534
slouken@1895
   535
    /* clip the source rectangle to the source surface */
slouken@1895
   536
    if (srcrect) {
slouken@1895
   537
        int maxw, maxh;
slouken@0
   538
slouken@1895
   539
        srcx = srcrect->x;
slouken@1895
   540
        w = srcrect->w;
slouken@1895
   541
        if (srcx < 0) {
slouken@1895
   542
            w += srcx;
slouken@1895
   543
            dstrect->x -= srcx;
slouken@1895
   544
            srcx = 0;
slouken@1895
   545
        }
slouken@1895
   546
        maxw = src->w - srcx;
slouken@1895
   547
        if (maxw < w)
slouken@1895
   548
            w = maxw;
slouken@0
   549
slouken@1895
   550
        srcy = srcrect->y;
slouken@1895
   551
        h = srcrect->h;
slouken@1895
   552
        if (srcy < 0) {
slouken@1895
   553
            h += srcy;
slouken@1895
   554
            dstrect->y -= srcy;
slouken@1895
   555
            srcy = 0;
slouken@1895
   556
        }
slouken@1895
   557
        maxh = src->h - srcy;
slouken@1895
   558
        if (maxh < h)
slouken@1895
   559
            h = maxh;
slouken@0
   560
slouken@1895
   561
    } else {
slouken@1895
   562
        srcx = srcy = 0;
slouken@1895
   563
        w = src->w;
slouken@1895
   564
        h = src->h;
slouken@1895
   565
    }
slouken@0
   566
slouken@1895
   567
    /* clip the destination rectangle against the clip rectangle */
slouken@1895
   568
    {
slouken@1895
   569
        SDL_Rect *clip = &dst->clip_rect;
slouken@1895
   570
        int dx, dy;
slouken@0
   571
slouken@1895
   572
        dx = clip->x - dstrect->x;
slouken@1895
   573
        if (dx > 0) {
slouken@1895
   574
            w -= dx;
slouken@1895
   575
            dstrect->x += dx;
slouken@1895
   576
            srcx += dx;
slouken@1895
   577
        }
slouken@1895
   578
        dx = dstrect->x + w - clip->x - clip->w;
slouken@1895
   579
        if (dx > 0)
slouken@1895
   580
            w -= dx;
slouken@1895
   581
slouken@1895
   582
        dy = clip->y - dstrect->y;
slouken@1895
   583
        if (dy > 0) {
slouken@1895
   584
            h -= dy;
slouken@1895
   585
            dstrect->y += dy;
slouken@1895
   586
            srcy += dy;
slouken@1895
   587
        }
slouken@1895
   588
        dy = dstrect->y + h - clip->y - clip->h;
slouken@1895
   589
        if (dy > 0)
slouken@1895
   590
            h -= dy;
slouken@1895
   591
    }
slouken@1895
   592
slouken@1895
   593
    if (w > 0 && h > 0) {
slouken@1895
   594
        SDL_Rect sr;
slouken@1895
   595
        sr.x = srcx;
slouken@1895
   596
        sr.y = srcy;
slouken@1895
   597
        sr.w = dstrect->w = w;
slouken@1895
   598
        sr.h = dstrect->h = h;
slouken@1895
   599
        return SDL_LowerBlit(src, &sr, dst, dstrect);
slouken@1895
   600
    }
slouken@1895
   601
    dstrect->w = dstrect->h = 0;
slouken@1895
   602
    return 0;
slouken@0
   603
}
slouken@0
   604
slouken@0
   605
/*
ken@5296
   606
 * Scale and blit a surface 
ken@5296
   607
*/
ken@5296
   608
int
ken@5296
   609
SDL_BlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,
ken@5296
   610
               SDL_Surface * dst, const SDL_Rect * dstrect)
ken@5296
   611
{
ken@5296
   612
    /* Save off the original dst width, height */
ken@5296
   613
    int dstW = dstrect->w;
ken@5296
   614
    int dstH = dstrect->h;
ken@5296
   615
    SDL_Rect final_dst = *dstrect;
ken@5296
   616
    SDL_Rect final_src = *srcrect;
ken@5296
   617
ken@5296
   618
    /* Clip the dst surface to the dstrect */
ken@5296
   619
    SDL_SetClipRect( dst, &final_dst );
ken@5296
   620
ken@5296
   621
    /* If the dest was clipped to a zero sized rect then exit */
ken@5296
   622
    if ( dst->clip_rect.w <= 0 || dst->clip_rect.h <= 0 ) {
ken@5296
   623
        return -1;
ken@5296
   624
    }
ken@5296
   625
ken@5296
   626
    /* Did the dst width change? */
ken@5296
   627
    if ( dstW != dst->clip_rect.w ) {
ken@5296
   628
        /* scale the src width appropriately */
ken@5296
   629
        final_src.w = final_src.w * dst->clip_rect.w / dstW;
ken@5296
   630
    }
ken@5296
   631
ken@5296
   632
    /* Did the dst height change? */
ken@5296
   633
    if ( dstH != dst->clip_rect.h ) {
ken@5296
   634
        /* scale the src width appropriately */
ken@5296
   635
        final_src.h = final_src.h * dst->clip_rect.h / dstH;
ken@5296
   636
    }
ken@5296
   637
ken@5296
   638
    /* Clip the src surface to the srcrect */
ken@5296
   639
    SDL_SetClipRect( src, &final_src );
ken@5296
   640
ken@5296
   641
    src->map->info.flags |= SDL_COPY_NEAREST;
ken@5296
   642
ken@5296
   643
    if ( src->format->format == dst->format->format && !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) {
ken@5296
   644
        return SDL_SoftStretch( src, &final_src, dst, &final_dst );
ken@5296
   645
    } else {
ken@5296
   646
        return SDL_LowerBlit( src, &final_src, dst, &final_dst );
ken@5296
   647
    }
ken@5296
   648
}
ken@5296
   649
ken@5296
   650
/*
slouken@0
   651
 * Lock a surface to directly access the pixels
slouken@0
   652
 */
slouken@1895
   653
int
slouken@1895
   654
SDL_LockSurface(SDL_Surface * surface)
slouken@0
   655
{
slouken@1895
   656
    if (!surface->locked) {
slouken@1895
   657
        /* Perform the lock */
slouken@1895
   658
        if (surface->flags & SDL_RLEACCEL) {
slouken@1895
   659
            SDL_UnRLESurface(surface, 1);
slouken@1895
   660
            surface->flags |= SDL_RLEACCEL;     /* save accel'd state */
slouken@1895
   661
        }
slouken@1895
   662
    }
slouken@0
   663
slouken@1895
   664
    /* Increment the surface lock count, for recursive locks */
slouken@1895
   665
    ++surface->locked;
slouken@0
   666
slouken@1895
   667
    /* Ready to go.. */
slouken@1895
   668
    return (0);
slouken@0
   669
}
slouken@1895
   670
slouken@0
   671
/*
slouken@0
   672
 * Unlock a previously locked surface
slouken@0
   673
 */
slouken@1895
   674
void
slouken@1895
   675
SDL_UnlockSurface(SDL_Surface * surface)
slouken@0
   676
{
slouken@1895
   677
    /* Only perform an unlock if we are locked */
slouken@1895
   678
    if (!surface->locked || (--surface->locked > 0)) {
slouken@1895
   679
        return;
slouken@1895
   680
    }
slouken@0
   681
slouken@1895
   682
    /* Update RLE encoded surface with new data */
slouken@1895
   683
    if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
slouken@1895
   684
        surface->flags &= ~SDL_RLEACCEL;        /* stop lying */
slouken@1895
   685
        SDL_RLESurface(surface);
slouken@1895
   686
    }
slouken@0
   687
}
slouken@0
   688
slouken@0
   689
/* 
slouken@0
   690
 * Convert a surface into the specified pixel format.
slouken@0
   691
 */
slouken@1895
   692
SDL_Surface *
slouken@2807
   693
SDL_ConvertSurface(SDL_Surface * surface, SDL_PixelFormat * format,
slouken@2807
   694
                   Uint32 flags)
slouken@0
   695
{
slouken@1895
   696
    SDL_Surface *convert;
slouken@2266
   697
    Uint32 copy_flags;
slouken@1895
   698
    SDL_Rect bounds;
slouken@0
   699
slouken@1895
   700
    /* Check for empty destination palette! (results in empty image) */
slouken@1895
   701
    if (format->palette != NULL) {
slouken@1895
   702
        int i;
slouken@1895
   703
        for (i = 0; i < format->palette->ncolors; ++i) {
slouken@1895
   704
            if ((format->palette->colors[i].r != 0xFF) ||
slouken@1895
   705
                (format->palette->colors[i].g != 0xFF) ||
slouken@1895
   706
                (format->palette->colors[i].b != 0xFF))
slouken@1895
   707
                break;
slouken@1895
   708
        }
slouken@1895
   709
        if (i == format->palette->ncolors) {
slouken@1895
   710
            SDL_SetError("Empty destination palette");
slouken@1895
   711
            return (NULL);
slouken@1895
   712
        }
slouken@1895
   713
    }
slouken@0
   714
slouken@1895
   715
    /* Create a new surface with the desired format */
slouken@2807
   716
    convert = SDL_CreateRGBSurface(flags, surface->w, surface->h,
slouken@1895
   717
                                   format->BitsPerPixel, format->Rmask,
slouken@1895
   718
                                   format->Gmask, format->Bmask,
slouken@1895
   719
                                   format->Amask);
slouken@1895
   720
    if (convert == NULL) {
slouken@1895
   721
        return (NULL);
slouken@1895
   722
    }
slouken@264
   723
slouken@1895
   724
    /* Copy the palette if any */
slouken@1895
   725
    if (format->palette && convert->format->palette) {
slouken@1895
   726
        SDL_memcpy(convert->format->palette->colors,
slouken@1895
   727
                   format->palette->colors,
slouken@1895
   728
                   format->palette->ncolors * sizeof(SDL_Color));
slouken@1895
   729
        convert->format->palette->ncolors = format->palette->ncolors;
slouken@1895
   730
    }
slouken@0
   731
slouken@2266
   732
    /* Save the original copy flags */
slouken@2266
   733
    copy_flags = surface->map->info.flags;
slouken@2266
   734
    surface->map->info.flags = 0;
slouken@0
   735
slouken@1895
   736
    /* Copy over the image data */
slouken@1895
   737
    bounds.x = 0;
slouken@1895
   738
    bounds.y = 0;
slouken@1895
   739
    bounds.w = surface->w;
slouken@1895
   740
    bounds.h = surface->h;
slouken@1895
   741
    SDL_LowerBlit(surface, &bounds, convert, &bounds);
slouken@0
   742
slouken@1895
   743
    /* Clean up the original surface, and update converted surface */
slouken@2824
   744
    convert->map->info.r = surface->map->info.r;
slouken@2824
   745
    convert->map->info.g = surface->map->info.g;
slouken@2824
   746
    convert->map->info.b = surface->map->info.b;
slouken@2824
   747
    convert->map->info.a = surface->map->info.a;
slouken@2824
   748
    convert->map->info.flags =
slouken@2824
   749
        (copy_flags &
slouken@2824
   750
         ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND
slouken@2824
   751
           | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY |
slouken@2824
   752
           SDL_COPY_RLE_ALPHAKEY));
slouken@2824
   753
    surface->map->info.flags = copy_flags;
slouken@2266
   754
    if (copy_flags & SDL_COPY_COLORKEY) {
slouken@2266
   755
        Uint8 keyR, keyG, keyB, keyA;
slouken@2266
   756
slouken@2267
   757
        SDL_GetRGBA(surface->map->info.colorkey, surface->format, &keyR,
slouken@2267
   758
                    &keyG, &keyB, &keyA);
slouken@2266
   759
        SDL_SetColorKey(convert, 1,
slouken@2266
   760
                        SDL_MapRGBA(convert->format, keyR, keyG, keyB, keyA));
slouken@2824
   761
        /* This is needed when converting for 3D texture upload */
slouken@2787
   762
        SDL_ConvertColorkeyToAlpha(convert);
slouken@1895
   763
    }
slouken@2824
   764
    SDL_SetClipRect(convert, &surface->clip_rect);
slouken@0
   765
slouken@2266
   766
    /* Enable alpha blending by default if the new surface has an
slouken@2266
   767
     * alpha channel or alpha modulation */
slouken@2824
   768
    if ((surface->format->Amask && format->Amask) ||
slouken@4491
   769
        (copy_flags & (SDL_COPY_COLORKEY|SDL_COPY_MODULATE_ALPHA))) {
slouken@2884
   770
        SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND);
slouken@1895
   771
    }
slouken@2824
   772
    if ((copy_flags & SDL_COPY_RLE_DESIRED) || (flags & SDL_RLEACCEL)) {
slouken@2824
   773
        SDL_SetSurfaceRLE(convert, SDL_RLEACCEL);
slouken@2824
   774
    }
slouken@0
   775
slouken@1895
   776
    /* We're ready to go! */
slouken@1895
   777
    return (convert);
slouken@0
   778
}
slouken@0
   779
slouken@5375
   780
SDL_Surface *
slouken@5375
   781
SDL_ConvertSurfaceFormat(SDL_Surface * surface, Uint32 pixel_format,
slouken@5375
   782
                         Uint32 flags)
slouken@5375
   783
{
slouken@5375
   784
    SDL_PixelFormat *fmt;
slouken@5375
   785
    SDL_Surface *convert;
slouken@5375
   786
slouken@5375
   787
    fmt = SDL_AllocFormat(pixel_format);
slouken@5375
   788
    if (fmt) {
slouken@5375
   789
        convert = SDL_ConvertSurface(surface, fmt, flags);
slouken@5375
   790
        SDL_FreeFormat(fmt);
slouken@5375
   791
    }
slouken@5375
   792
    return convert;
slouken@5375
   793
}
slouken@5375
   794
slouken@0
   795
/*
slouken@3434
   796
 * Create a surface on the stack for quick blit operations
slouken@3434
   797
 */
slouken@3434
   798
static __inline__ SDL_bool
slouken@3434
   799
SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format,
slouken@3434
   800
                         void * pixels, int pitch, SDL_Surface * surface, 
slouken@3434
   801
                         SDL_PixelFormat * format, SDL_BlitMap * blitmap)
slouken@3434
   802
{
slouken@5288
   803
    if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) {
slouken@5288
   804
        SDL_SetError("Indexed pixel formats not supported");
slouken@3434
   805
        return SDL_FALSE;
slouken@3434
   806
    }
slouken@5288
   807
    if (SDL_InitFormat(format, pixel_format) < 0) {
slouken@3434
   808
        return SDL_FALSE;
slouken@3434
   809
    }
slouken@3434
   810
slouken@3434
   811
    SDL_zerop(surface);
slouken@3434
   812
    surface->flags = SDL_PREALLOC;
slouken@5288
   813
    surface->format = format;
slouken@3434
   814
    surface->pixels = pixels;
slouken@3434
   815
    surface->w = width;
slouken@3434
   816
    surface->h = height;
slouken@3434
   817
    surface->pitch = pitch;
slouken@3434
   818
    /* We don't actually need to set up the clip rect for our purposes */
slouken@3434
   819
    /*SDL_SetClipRect(surface, NULL);*/
slouken@3434
   820
slouken@3434
   821
    /* Allocate an empty mapping */
slouken@3434
   822
    SDL_zerop(blitmap);
slouken@3434
   823
    blitmap->info.r = 0xFF;
slouken@3434
   824
    blitmap->info.g = 0xFF;
slouken@3434
   825
    blitmap->info.b = 0xFF;
slouken@3434
   826
    blitmap->info.a = 0xFF;
slouken@3434
   827
    surface->map = blitmap;
slouken@3434
   828
slouken@3434
   829
    /* The surface is ready to go */
slouken@3434
   830
    surface->refcount = 1;
slouken@3434
   831
    return SDL_TRUE;
slouken@3434
   832
}
slouken@3434
   833
slouken@3434
   834
/*
slouken@3434
   835
 * Copy a block of pixels of one format to another format
slouken@3434
   836
 */
slouken@3434
   837
int SDL_ConvertPixels(int width, int height,
slouken@3434
   838
                      Uint32 src_format, const void * src, int src_pitch,
slouken@3434
   839
                      Uint32 dst_format, void * dst, int dst_pitch)
slouken@3434
   840
{
slouken@3434
   841
    SDL_Surface src_surface, dst_surface;
slouken@3434
   842
    SDL_PixelFormat src_fmt, dst_fmt;
slouken@3434
   843
    SDL_BlitMap src_blitmap, dst_blitmap;
slouken@3434
   844
    SDL_Rect rect;
slouken@3434
   845
slouken@3434
   846
    /* Fast path for same format copy */
slouken@3434
   847
    if (src_format == dst_format) {
slouken@3434
   848
        int bpp;
slouken@3434
   849
slouken@3434
   850
        if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
slouken@3434
   851
            switch (src_format) {
slouken@3434
   852
            case SDL_PIXELFORMAT_YV12:
slouken@3434
   853
            case SDL_PIXELFORMAT_IYUV:
slouken@3434
   854
            case SDL_PIXELFORMAT_YUY2:
slouken@3434
   855
            case SDL_PIXELFORMAT_UYVY:
slouken@3434
   856
            case SDL_PIXELFORMAT_YVYU:
slouken@3434
   857
                bpp = 2;
slouken@3434
   858
            default:
slouken@3434
   859
                SDL_SetError("Unknown FOURCC pixel format");
slouken@3434
   860
                return -1;
slouken@3434
   861
            }
slouken@3434
   862
        } else {
slouken@3434
   863
            bpp = SDL_BYTESPERPIXEL(src_format);
slouken@3434
   864
        }
slouken@3434
   865
        width *= bpp;
slouken@3434
   866
slouken@3434
   867
        while (height-- > 0) {
slouken@3434
   868
            SDL_memcpy(dst, src, width);
slouken@3434
   869
            src = (Uint8*)src + src_pitch;
slouken@3434
   870
            dst = (Uint8*)dst + dst_pitch;
slouken@3434
   871
        }
slouken@3434
   872
        return SDL_TRUE;
slouken@3434
   873
    }
slouken@3434
   874
slouken@3434
   875
    if (!SDL_CreateSurfaceOnStack(width, height, src_format, (void*)src,
slouken@3434
   876
                                  src_pitch,
slouken@3434
   877
                                  &src_surface, &src_fmt, &src_blitmap)) {
slouken@3434
   878
        return -1;
slouken@3434
   879
    }
slouken@3434
   880
    if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch,
slouken@3434
   881
                                  &dst_surface, &dst_fmt, &dst_blitmap)) {
slouken@3434
   882
        return -1;
slouken@3434
   883
    }
slouken@3434
   884
slouken@3434
   885
    /* Set up the rect and go! */
slouken@3434
   886
    rect.x = 0;
slouken@3434
   887
    rect.y = 0;
slouken@3434
   888
    rect.w = width;
slouken@3436
   889
    rect.h = height;
slouken@3434
   890
    return SDL_LowerBlit(&src_surface, &rect, &dst_surface, &rect);
slouken@3434
   891
}
slouken@3434
   892
slouken@3434
   893
/*
slouken@0
   894
 * Free a surface created by the above function.
slouken@0
   895
 */
slouken@1895
   896
void
slouken@1895
   897
SDL_FreeSurface(SDL_Surface * surface)
slouken@0
   898
{
slouken@1895
   899
    if (surface == NULL) {
slouken@1895
   900
        return;
slouken@1895
   901
    }
slouken@5288
   902
    if (surface->flags & SDL_DONTFREE) {
slouken@5288
   903
        return;
slouken@5288
   904
    }
slouken@1895
   905
    if (--surface->refcount > 0) {
slouken@1895
   906
        return;
slouken@1895
   907
    }
slouken@1895
   908
    while (surface->locked > 0) {
slouken@1895
   909
        SDL_UnlockSurface(surface);
slouken@1895
   910
    }
slouken@1895
   911
    if (surface->flags & SDL_RLEACCEL) {
slouken@1895
   912
        SDL_UnRLESurface(surface, 0);
slouken@1895
   913
    }
slouken@1895
   914
    if (surface->format) {
slouken@1895
   915
        SDL_SetSurfacePalette(surface, NULL);
slouken@1895
   916
        SDL_FreeFormat(surface->format);
slouken@1895
   917
        surface->format = NULL;
slouken@1895
   918
    }
slouken@1895
   919
    if (surface->map != NULL) {
slouken@1895
   920
        SDL_FreeBlitMap(surface->map);
slouken@1895
   921
        surface->map = NULL;
slouken@1895
   922
    }
slouken@1895
   923
    if (surface->pixels && ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC)) {
slouken@1895
   924
        SDL_free(surface->pixels);
slouken@1895
   925
    }
slouken@1895
   926
    SDL_free(surface);
slouken@0
   927
}
slouken@1895
   928
slouken@1895
   929
/* vi: set ts=4 sw=4 expandtab: */