src/video/SDL_bmp.c
author Sam Lantinga
Sun, 07 Jul 2013 18:23:04 -0700
changeset 7378 3d5f62bac8e7
parent 7376 a2ea9757bfa0
child 7677 871d43c6968a
permissions -rw-r--r--
Fixed bug 1943 - Wrong handling of legacy 32bpp BMP files

Kang Seonghoon

While BMP format supports alpha channel, it is enabled only when the header is at least 56 bytes long (BITMAPV3INFOHEADER and later). For very common 40-byte-long header (BITMAPINFOHEADER) 32bpp format should be interpreted as BGRX format, but currently SDL interprets them as BGRA format and causes a significant compatibility problem as many 32bpp files use a padding byte of 0 ("transparent" in BGRA interpretation).

---

I fixed this by checking to see if the alpha channel is all 0, and if so, setting it opaque.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@1402
    21
#include "SDL_config.h"
slouken@0
    22
slouken@7191
    23
/*
slouken@0
    24
   Code to load and save surfaces in Windows BMP format.
slouken@0
    25
slouken@0
    26
   Why support BMP format?  Well, it's a native format for Windows, and
slouken@0
    27
   most image processing programs can read and write it.  It would be nice
slouken@0
    28
   to be able to have at least one image format that we can natively load
slouken@0
    29
   and save, and since PNG is so complex that it would bloat the library,
slouken@7191
    30
   BMP is a good alternative.
slouken@0
    31
slouken@0
    32
   This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
slouken@0
    33
*/
slouken@0
    34
slouken@0
    35
#include "SDL_video.h"
slouken@0
    36
#include "SDL_endian.h"
slouken@2823
    37
#include "SDL_pixels_c.h"
slouken@0
    38
slouken@6094
    39
#define SAVE_32BIT_BMP
slouken@6094
    40
slouken@0
    41
/* Compression encodings for BMP files */
slouken@0
    42
#ifndef BI_RGB
slouken@7191
    43
#define BI_RGB      0
slouken@7191
    44
#define BI_RLE8     1
slouken@7191
    45
#define BI_RLE4     2
slouken@7191
    46
#define BI_BITFIELDS    3
slouken@0
    47
#endif
slouken@0
    48
slouken@0
    49
slouken@7378
    50
static void CorrectAlphaChannel(SDL_Surface *surface)
slouken@7378
    51
{
slouken@7378
    52
    /* Check to see if there is any alpha channel data */
slouken@7378
    53
    SDL_bool hasAlpha = SDL_FALSE;
slouken@7378
    54
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@7378
    55
    int alphaChannelOffset = 0;
slouken@7378
    56
#else
slouken@7378
    57
    int alphaChannelOffset = 3;
slouken@7378
    58
#endif
slouken@7378
    59
    Uint8 *alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
slouken@7378
    60
    Uint8 *end = alpha + surface->h * surface->pitch;
slouken@7378
    61
slouken@7378
    62
    while (alpha < end) {
slouken@7378
    63
        if (*alpha != 0) {
slouken@7378
    64
            hasAlpha = SDL_TRUE;
slouken@7378
    65
            break;
slouken@7378
    66
        }
slouken@7378
    67
        alpha += 4;
slouken@7378
    68
    }
slouken@7378
    69
slouken@7378
    70
    if (!hasAlpha) {
slouken@7378
    71
        alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
slouken@7378
    72
        while (alpha < end) {
slouken@7378
    73
            *alpha = SDL_ALPHA_OPAQUE;
slouken@7378
    74
            alpha += 4;
slouken@7378
    75
        }
slouken@7378
    76
    }
slouken@7378
    77
}
slouken@7378
    78
slouken@1895
    79
SDL_Surface *
slouken@1895
    80
SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
slouken@0
    81
{
slouken@3310
    82
    SDL_bool was_error;
slouken@6666
    83
    Sint64 fp_offset = 0;
slouken@1895
    84
    int bmpPitch;
slouken@1895
    85
    int i, pad;
slouken@1895
    86
    SDL_Surface *surface;
slouken@1895
    87
    Uint32 Rmask;
slouken@1895
    88
    Uint32 Gmask;
slouken@1895
    89
    Uint32 Bmask;
slouken@3497
    90
    Uint32 Amask;
slouken@1895
    91
    SDL_Palette *palette;
slouken@1895
    92
    Uint8 *bits;
slouken@3310
    93
    Uint8 *top, *end;
slouken@3310
    94
    SDL_bool topDown;
slouken@1895
    95
    int ExpandBMP;
slouken@7378
    96
    SDL_bool correctAlpha = SDL_FALSE;
slouken@0
    97
slouken@1895
    98
    /* The Win32 BMP file header (14 bytes) */
slouken@1895
    99
    char magic[2];
icculus@6389
   100
    /*Uint32 bfSize = 0;*/
icculus@6389
   101
    /*Uint16 bfReserved1 = 0;*/
icculus@6389
   102
    /*Uint16 bfReserved2 = 0;*/
icculus@6389
   103
    Uint32 bfOffBits = 0;
slouken@0
   104
slouken@1895
   105
    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
icculus@6389
   106
    Uint32 biSize = 0;
icculus@6389
   107
    Sint32 biWidth = 0;
icculus@6389
   108
    Sint32 biHeight = 0;
icculus@6389
   109
    /*Uint16 biPlanes = 0;*/
icculus@6389
   110
    Uint16 biBitCount = 0;
icculus@6389
   111
    Uint32 biCompression = 0;
icculus@6389
   112
    /*Uint32 biSizeImage = 0;*/
icculus@6389
   113
    /*Sint32 biXPelsPerMeter = 0;*/
icculus@6389
   114
    /*Sint32 biYPelsPerMeter = 0;*/
icculus@6389
   115
    Uint32 biClrUsed = 0;
icculus@6389
   116
    /*Uint32 biClrImportant = 0;*/
slouken@0
   117
slouken@1895
   118
    /* Make sure we are passed a valid data source */
slouken@1895
   119
    surface = NULL;
slouken@3310
   120
    was_error = SDL_FALSE;
slouken@1895
   121
    if (src == NULL) {
slouken@3310
   122
        was_error = SDL_TRUE;
slouken@1895
   123
        goto done;
slouken@1895
   124
    }
slouken@0
   125
slouken@1895
   126
    /* Read in the BMP file header */
slouken@1895
   127
    fp_offset = SDL_RWtell(src);
slouken@1895
   128
    SDL_ClearError();
slouken@1895
   129
    if (SDL_RWread(src, magic, 1, 2) != 2) {
slouken@1895
   130
        SDL_Error(SDL_EFREAD);
slouken@3310
   131
        was_error = SDL_TRUE;
slouken@1895
   132
        goto done;
slouken@1895
   133
    }
slouken@1895
   134
    if (SDL_strncmp(magic, "BM", 2) != 0) {
slouken@1895
   135
        SDL_SetError("File is not a Windows BMP file");
slouken@3310
   136
        was_error = SDL_TRUE;
slouken@1895
   137
        goto done;
slouken@1895
   138
    }
icculus@6389
   139
    /*bfSize =*/ SDL_ReadLE32(src);
icculus@6389
   140
    /*bfReserved1 =*/ SDL_ReadLE16(src);
icculus@6389
   141
    /*bfReserved2 =*/ SDL_ReadLE16(src);
slouken@1895
   142
    bfOffBits = SDL_ReadLE32(src);
slouken@0
   143
slouken@1895
   144
    /* Read the Win32 BITMAPINFOHEADER */
slouken@1895
   145
    biSize = SDL_ReadLE32(src);
slouken@1895
   146
    if (biSize == 12) {
slouken@1895
   147
        biWidth = (Uint32) SDL_ReadLE16(src);
slouken@1895
   148
        biHeight = (Uint32) SDL_ReadLE16(src);
icculus@6389
   149
        /*biPlanes =*/ SDL_ReadLE16(src);
slouken@1895
   150
        biBitCount = SDL_ReadLE16(src);
slouken@1895
   151
        biCompression = BI_RGB;
slouken@1895
   152
    } else {
slouken@1895
   153
        biWidth = SDL_ReadLE32(src);
slouken@1895
   154
        biHeight = SDL_ReadLE32(src);
icculus@6389
   155
        /*biPlanes =*/ SDL_ReadLE16(src);
slouken@1895
   156
        biBitCount = SDL_ReadLE16(src);
slouken@1895
   157
        biCompression = SDL_ReadLE32(src);
icculus@6389
   158
        /*biSizeImage =*/ SDL_ReadLE32(src);
icculus@6389
   159
        /*biXPelsPerMeter =*/ SDL_ReadLE32(src);
icculus@6389
   160
        /*biYPelsPerMeter =*/ SDL_ReadLE32(src);
slouken@1895
   161
        biClrUsed = SDL_ReadLE32(src);
icculus@6389
   162
        /*biClrImportant =*/ SDL_ReadLE32(src);
slouken@1895
   163
    }
slouken@3310
   164
    if (biHeight < 0) {
slouken@3310
   165
        topDown = SDL_TRUE;
slouken@3310
   166
        biHeight = -biHeight;
slouken@3310
   167
    } else {
slouken@3310
   168
        topDown = SDL_FALSE;
slouken@3310
   169
    }
slouken@0
   170
slouken@1895
   171
    /* Check for read error */
slouken@1895
   172
    if (SDL_strcmp(SDL_GetError(), "") != 0) {
slouken@3310
   173
        was_error = SDL_TRUE;
slouken@1895
   174
        goto done;
slouken@1895
   175
    }
slouken@0
   176
slouken@1895
   177
    /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
slouken@1895
   178
    switch (biBitCount) {
slouken@1895
   179
    case 1:
slouken@1895
   180
    case 4:
slouken@1895
   181
        ExpandBMP = biBitCount;
slouken@1895
   182
        biBitCount = 8;
slouken@1895
   183
        break;
slouken@1895
   184
    default:
slouken@1895
   185
        ExpandBMP = 0;
slouken@1895
   186
        break;
slouken@1895
   187
    }
slouken@0
   188
slouken@1895
   189
    /* We don't support any BMP compression right now */
slouken@3497
   190
    Rmask = Gmask = Bmask = Amask = 0;
slouken@1895
   191
    switch (biCompression) {
slouken@1895
   192
    case BI_RGB:
slouken@1895
   193
        /* If there are no masks, use the defaults */
slouken@1895
   194
        if (bfOffBits == (14 + biSize)) {
slouken@1895
   195
            /* Default values for the BMP format */
slouken@1895
   196
            switch (biBitCount) {
slouken@1895
   197
            case 15:
slouken@1895
   198
            case 16:
slouken@1895
   199
                Rmask = 0x7C00;
slouken@1895
   200
                Gmask = 0x03E0;
slouken@1895
   201
                Bmask = 0x001F;
slouken@1895
   202
                break;
slouken@1895
   203
            case 24:
slouken@0
   204
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@1895
   205
                Rmask = 0x000000FF;
slouken@1895
   206
                Gmask = 0x0000FF00;
slouken@1895
   207
                Bmask = 0x00FF0000;
slouken@3497
   208
#else
slouken@3497
   209
                Rmask = 0x00FF0000;
slouken@3497
   210
                Gmask = 0x0000FF00;
slouken@3497
   211
                Bmask = 0x000000FF;
slouken@0
   212
#endif
slouken@3497
   213
                break;
slouken@1895
   214
            case 32:
slouken@7378
   215
                /* We don't know if this has alpha channel or not */
slouken@7378
   216
                correctAlpha = SDL_TRUE;
slouken@7376
   217
                Amask = 0xFF000000;
slouken@1895
   218
                Rmask = 0x00FF0000;
slouken@1895
   219
                Gmask = 0x0000FF00;
slouken@1895
   220
                Bmask = 0x000000FF;
slouken@1895
   221
                break;
slouken@1895
   222
            default:
slouken@1895
   223
                break;
slouken@1895
   224
            }
slouken@1895
   225
            break;
slouken@1895
   226
        }
slouken@1895
   227
        /* Fall through -- read the RGB masks */
slouken@0
   228
slouken@1895
   229
    case BI_BITFIELDS:
slouken@1895
   230
        switch (biBitCount) {
slouken@1895
   231
        case 15:
slouken@1895
   232
        case 16:
slouken@3497
   233
            Rmask = SDL_ReadLE32(src);
slouken@3497
   234
            Gmask = SDL_ReadLE32(src);
slouken@3497
   235
            Bmask = SDL_ReadLE32(src);
slouken@3497
   236
            break;
slouken@1895
   237
        case 32:
slouken@1895
   238
            Rmask = SDL_ReadLE32(src);
slouken@1895
   239
            Gmask = SDL_ReadLE32(src);
slouken@1895
   240
            Bmask = SDL_ReadLE32(src);
slouken@3497
   241
            Amask = SDL_ReadLE32(src);
slouken@1895
   242
            break;
slouken@1895
   243
        default:
slouken@1895
   244
            break;
slouken@1895
   245
        }
slouken@1895
   246
        break;
slouken@1895
   247
    default:
slouken@1895
   248
        SDL_SetError("Compressed BMP files not supported");
slouken@3310
   249
        was_error = SDL_TRUE;
slouken@1895
   250
        goto done;
slouken@1895
   251
    }
slouken@0
   252
slouken@1895
   253
    /* Create a compatible surface, note that the colors are RGB ordered */
slouken@1895
   254
    surface =
slouken@1895
   255
        SDL_CreateRGBSurface(0, biWidth, biHeight, biBitCount, Rmask, Gmask,
slouken@3497
   256
                             Bmask, Amask);
slouken@1895
   257
    if (surface == NULL) {
slouken@3310
   258
        was_error = SDL_TRUE;
slouken@1895
   259
        goto done;
slouken@1895
   260
    }
slouken@0
   261
slouken@1895
   262
    /* Load the palette, if any */
slouken@1895
   263
    palette = (surface->format)->palette;
slouken@1895
   264
    if (palette) {
slouken@1895
   265
        if (biClrUsed == 0) {
slouken@1895
   266
            biClrUsed = 1 << biBitCount;
slouken@1895
   267
        }
slouken@2920
   268
        if ((int) biClrUsed > palette->ncolors) {
slouken@2851
   269
            palette->ncolors = biClrUsed;
slouken@2851
   270
            palette->colors =
slouken@2851
   271
                (SDL_Color *) SDL_realloc(palette->colors,
slouken@2851
   272
                                          palette->ncolors *
slouken@2851
   273
                                          sizeof(*palette->colors));
slouken@2851
   274
            if (!palette->colors) {
slouken@2851
   275
                SDL_OutOfMemory();
slouken@3310
   276
                was_error = SDL_TRUE;
slouken@2851
   277
                goto done;
slouken@2851
   278
            }
slouken@2920
   279
        } else if ((int) biClrUsed < palette->ncolors) {
slouken@2851
   280
            palette->ncolors = biClrUsed;
slouken@2851
   281
        }
slouken@1895
   282
        if (biSize == 12) {
slouken@1895
   283
            for (i = 0; i < (int) biClrUsed; ++i) {
slouken@1895
   284
                SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@1895
   285
                SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@1895
   286
                SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@7024
   287
                palette->colors[i].a = SDL_ALPHA_OPAQUE;
slouken@1895
   288
            }
slouken@1895
   289
        } else {
slouken@1895
   290
            for (i = 0; i < (int) biClrUsed; ++i) {
slouken@1895
   291
                SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@1895
   292
                SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@1895
   293
                SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@7024
   294
                SDL_RWread(src, &palette->colors[i].a, 1, 1);
slouken@7068
   295
slouken@7068
   296
                /* According to Microsoft documentation, the fourth element
slouken@7068
   297
                   is reserved and must be zero, so we shouldn't treat it as
slouken@7068
   298
                   alpha.
slouken@7068
   299
                */
slouken@7068
   300
                palette->colors[i].a = SDL_ALPHA_OPAQUE;
slouken@1895
   301
            }
slouken@1895
   302
        }
slouken@1895
   303
    }
slouken@0
   304
slouken@1895
   305
    /* Read the surface pixels.  Note that the bmp image is upside down */
slouken@1895
   306
    if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
slouken@1895
   307
        SDL_Error(SDL_EFSEEK);
slouken@3310
   308
        was_error = SDL_TRUE;
slouken@1895
   309
        goto done;
slouken@1895
   310
    }
slouken@3310
   311
    top = (Uint8 *)surface->pixels;
slouken@3310
   312
    end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
slouken@1895
   313
    switch (ExpandBMP) {
slouken@1895
   314
    case 1:
slouken@1895
   315
        bmpPitch = (biWidth + 7) >> 3;
slouken@1895
   316
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
slouken@1895
   317
        break;
slouken@1895
   318
    case 4:
slouken@1895
   319
        bmpPitch = (biWidth + 1) >> 1;
slouken@1895
   320
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
slouken@1895
   321
        break;
slouken@1895
   322
    default:
slouken@1895
   323
        pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
slouken@1895
   324
        break;
slouken@1895
   325
    }
slouken@3310
   326
    if (topDown) {
slouken@3310
   327
        bits = top;
slouken@3310
   328
    } else {
slouken@3310
   329
        bits = end - surface->pitch;
slouken@3310
   330
    }
slouken@3310
   331
    while (bits >= top && bits < end) {
slouken@1895
   332
        switch (ExpandBMP) {
slouken@1895
   333
        case 1:
slouken@3310
   334
        case 4:{
slouken@1895
   335
                Uint8 pixel = 0;
slouken@1895
   336
                int shift = (8 - ExpandBMP);
slouken@1895
   337
                for (i = 0; i < surface->w; ++i) {
slouken@1895
   338
                    if (i % (8 / ExpandBMP) == 0) {
slouken@1895
   339
                        if (!SDL_RWread(src, &pixel, 1, 1)) {
slouken@1895
   340
                            SDL_SetError("Error reading from BMP");
slouken@3310
   341
                            was_error = SDL_TRUE;
slouken@1895
   342
                            goto done;
slouken@1895
   343
                        }
slouken@1895
   344
                    }
slouken@1895
   345
                    *(bits + i) = (pixel >> shift);
slouken@1895
   346
                    pixel <<= ExpandBMP;
slouken@1895
   347
                }
slouken@1895
   348
            }
slouken@1895
   349
            break;
slouken@0
   350
slouken@1895
   351
        default:
slouken@1895
   352
            if (SDL_RWread(src, bits, 1, surface->pitch)
slouken@1895
   353
                != surface->pitch) {
slouken@1895
   354
                SDL_Error(SDL_EFREAD);
slouken@3310
   355
                was_error = SDL_TRUE;
slouken@1895
   356
                goto done;
slouken@1895
   357
            }
slouken@0
   358
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@1895
   359
            /* Byte-swap the pixels if needed. Note that the 24bpp
slouken@1895
   360
               case has already been taken care of above. */
slouken@1895
   361
            switch (biBitCount) {
slouken@1895
   362
            case 15:
slouken@3310
   363
            case 16:{
slouken@1895
   364
                    Uint16 *pix = (Uint16 *) bits;
slouken@1895
   365
                    for (i = 0; i < surface->w; i++)
slouken@1895
   366
                        pix[i] = SDL_Swap16(pix[i]);
slouken@1895
   367
                    break;
slouken@1895
   368
                }
slouken@0
   369
slouken@3310
   370
            case 32:{
slouken@1895
   371
                    Uint32 *pix = (Uint32 *) bits;
slouken@1895
   372
                    for (i = 0; i < surface->w; i++)
slouken@1895
   373
                        pix[i] = SDL_Swap32(pix[i]);
slouken@1895
   374
                    break;
slouken@1895
   375
                }
slouken@1895
   376
            }
slouken@0
   377
#endif
slouken@1895
   378
            break;
slouken@1895
   379
        }
slouken@1895
   380
        /* Skip padding bytes, ugh */
slouken@1895
   381
        if (pad) {
slouken@1895
   382
            Uint8 padbyte;
slouken@1895
   383
            for (i = 0; i < pad; ++i) {
slouken@1895
   384
                SDL_RWread(src, &padbyte, 1, 1);
slouken@1895
   385
            }
slouken@1895
   386
        }
slouken@3310
   387
        if (topDown) {
slouken@3310
   388
            bits += surface->pitch;
slouken@3310
   389
        } else {
slouken@3310
   390
            bits -= surface->pitch;
slouken@3310
   391
        }
slouken@1895
   392
    }
slouken@7378
   393
    if (correctAlpha) {
slouken@7378
   394
        CorrectAlphaChannel(surface);
slouken@7378
   395
    }
slouken@1895
   396
  done:
slouken@1895
   397
    if (was_error) {
slouken@1895
   398
        if (src) {
slouken@1895
   399
            SDL_RWseek(src, fp_offset, RW_SEEK_SET);
slouken@1895
   400
        }
slouken@1895
   401
        if (surface) {
slouken@1895
   402
            SDL_FreeSurface(surface);
slouken@1895
   403
        }
slouken@1895
   404
        surface = NULL;
slouken@1895
   405
    }
slouken@1895
   406
    if (freesrc && src) {
slouken@1895
   407
        SDL_RWclose(src);
slouken@1895
   408
    }
slouken@1895
   409
    return (surface);
slouken@0
   410
}
slouken@0
   411
slouken@1895
   412
int
slouken@1895
   413
SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
slouken@0
   414
{
slouken@6666
   415
    Sint64 fp_offset;
slouken@1895
   416
    int i, pad;
slouken@1895
   417
    SDL_Surface *surface;
slouken@1895
   418
    Uint8 *bits;
slouken@0
   419
slouken@1895
   420
    /* The Win32 BMP file header (14 bytes) */
slouken@1895
   421
    char magic[2] = { 'B', 'M' };
slouken@1895
   422
    Uint32 bfSize;
slouken@1895
   423
    Uint16 bfReserved1;
slouken@1895
   424
    Uint16 bfReserved2;
slouken@1895
   425
    Uint32 bfOffBits;
slouken@0
   426
slouken@1895
   427
    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
slouken@1895
   428
    Uint32 biSize;
slouken@1895
   429
    Sint32 biWidth;
slouken@1895
   430
    Sint32 biHeight;
slouken@1895
   431
    Uint16 biPlanes;
slouken@1895
   432
    Uint16 biBitCount;
slouken@1895
   433
    Uint32 biCompression;
slouken@1895
   434
    Uint32 biSizeImage;
slouken@1895
   435
    Sint32 biXPelsPerMeter;
slouken@1895
   436
    Sint32 biYPelsPerMeter;
slouken@1895
   437
    Uint32 biClrUsed;
slouken@1895
   438
    Uint32 biClrImportant;
slouken@0
   439
slouken@1895
   440
    /* Make sure we have somewhere to save */
slouken@1895
   441
    surface = NULL;
slouken@1895
   442
    if (dst) {
slouken@2969
   443
        SDL_bool save32bit = SDL_FALSE;
slouken@2969
   444
#ifdef SAVE_32BIT_BMP
slouken@2969
   445
        /* We can save alpha information in a 32-bit BMP */
slouken@2969
   446
        if (saveme->map->info.flags & SDL_COPY_COLORKEY ||
slouken@2969
   447
            saveme->format->Amask) {
slouken@2969
   448
            save32bit = SDL_TRUE;
slouken@2969
   449
        }
slouken@2969
   450
#endif /* SAVE_32BIT_BMP */
slouken@2969
   451
slouken@2969
   452
        if (saveme->format->palette && !save32bit) {
slouken@1895
   453
            if (saveme->format->BitsPerPixel == 8) {
slouken@1895
   454
                surface = saveme;
slouken@1895
   455
            } else {
slouken@1895
   456
                SDL_SetError("%d bpp BMP files not supported",
slouken@1895
   457
                             saveme->format->BitsPerPixel);
slouken@1895
   458
            }
slouken@1895
   459
        } else if ((saveme->format->BitsPerPixel == 24) &&
slouken@0
   460
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@1895
   461
                   (saveme->format->Rmask == 0x00FF0000) &&
slouken@1895
   462
                   (saveme->format->Gmask == 0x0000FF00) &&
slouken@1895
   463
                   (saveme->format->Bmask == 0x000000FF)
slouken@0
   464
#else
slouken@1895
   465
                   (saveme->format->Rmask == 0x000000FF) &&
slouken@1895
   466
                   (saveme->format->Gmask == 0x0000FF00) &&
slouken@1895
   467
                   (saveme->format->Bmask == 0x00FF0000)
slouken@0
   468
#endif
slouken@1895
   469
            ) {
slouken@1895
   470
            surface = saveme;
slouken@1895
   471
        } else {
slouken@2967
   472
            SDL_PixelFormat format;
slouken@0
   473
slouken@2969
   474
            /* If the surface has a colorkey or alpha channel we'll save a
slouken@2969
   475
               32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
slouken@2969
   476
            if (save32bit) {
slouken@7191
   477
                SDL_InitFormat(&format,
slouken@0
   478
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@5288
   479
                               SDL_PIXELFORMAT_ARGB8888
slouken@0
   480
#else
slouken@5288
   481
                               SDL_PIXELFORMAT_BGRA8888
slouken@0
   482
#endif
slouken@5288
   483
                               );
slouken@5288
   484
            } else {
slouken@5288
   485
                SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
slouken@2969
   486
            }
slouken@2967
   487
            surface = SDL_ConvertSurface(saveme, &format, 0);
slouken@2967
   488
            if (!surface) {
slouken@2990
   489
                SDL_SetError("Couldn't convert image to %d bpp",
slouken@2990
   490
                             format.BitsPerPixel);
slouken@1895
   491
            }
slouken@1895
   492
        }
slouken@1895
   493
    }
slouken@0
   494
slouken@1895
   495
    if (surface && (SDL_LockSurface(surface) == 0)) {
slouken@1895
   496
        const int bw = surface->w * surface->format->BytesPerPixel;
slouken@1895
   497
slouken@1895
   498
        /* Set the BMP file header values */
slouken@1895
   499
        bfSize = 0;             /* We'll write this when we're done */
slouken@1895
   500
        bfReserved1 = 0;
slouken@1895
   501
        bfReserved2 = 0;
slouken@1895
   502
        bfOffBits = 0;          /* We'll write this when we're done */
slouken@1012
   503
slouken@1895
   504
        /* Write the BMP file header values */
slouken@1895
   505
        fp_offset = SDL_RWtell(dst);
slouken@1895
   506
        SDL_ClearError();
slouken@1895
   507
        SDL_RWwrite(dst, magic, 2, 1);
slouken@1895
   508
        SDL_WriteLE32(dst, bfSize);
slouken@1895
   509
        SDL_WriteLE16(dst, bfReserved1);
slouken@1895
   510
        SDL_WriteLE16(dst, bfReserved2);
slouken@1895
   511
        SDL_WriteLE32(dst, bfOffBits);
slouken@0
   512
slouken@1895
   513
        /* Set the BMP info values */
slouken@1895
   514
        biSize = 40;
slouken@1895
   515
        biWidth = surface->w;
slouken@1895
   516
        biHeight = surface->h;
slouken@1895
   517
        biPlanes = 1;
slouken@1895
   518
        biBitCount = surface->format->BitsPerPixel;
slouken@1895
   519
        biCompression = BI_RGB;
slouken@1895
   520
        biSizeImage = surface->h * surface->pitch;
slouken@1895
   521
        biXPelsPerMeter = 0;
slouken@1895
   522
        biYPelsPerMeter = 0;
slouken@1895
   523
        if (surface->format->palette) {
slouken@1895
   524
            biClrUsed = surface->format->palette->ncolors;
slouken@1895
   525
        } else {
slouken@1895
   526
            biClrUsed = 0;
slouken@1895
   527
        }
slouken@1895
   528
        biClrImportant = 0;
slouken@0
   529
slouken@1895
   530
        /* Write the BMP info values */
slouken@1895
   531
        SDL_WriteLE32(dst, biSize);
slouken@1895
   532
        SDL_WriteLE32(dst, biWidth);
slouken@1895
   533
        SDL_WriteLE32(dst, biHeight);
slouken@1895
   534
        SDL_WriteLE16(dst, biPlanes);
slouken@1895
   535
        SDL_WriteLE16(dst, biBitCount);
slouken@1895
   536
        SDL_WriteLE32(dst, biCompression);
slouken@1895
   537
        SDL_WriteLE32(dst, biSizeImage);
slouken@1895
   538
        SDL_WriteLE32(dst, biXPelsPerMeter);
slouken@1895
   539
        SDL_WriteLE32(dst, biYPelsPerMeter);
slouken@1895
   540
        SDL_WriteLE32(dst, biClrUsed);
slouken@1895
   541
        SDL_WriteLE32(dst, biClrImportant);
slouken@0
   542
slouken@1895
   543
        /* Write the palette (in BGR color order) */
slouken@1895
   544
        if (surface->format->palette) {
slouken@1895
   545
            SDL_Color *colors;
slouken@1895
   546
            int ncolors;
slouken@0
   547
slouken@1895
   548
            colors = surface->format->palette->colors;
slouken@1895
   549
            ncolors = surface->format->palette->ncolors;
slouken@1895
   550
            for (i = 0; i < ncolors; ++i) {
slouken@1895
   551
                SDL_RWwrite(dst, &colors[i].b, 1, 1);
slouken@1895
   552
                SDL_RWwrite(dst, &colors[i].g, 1, 1);
slouken@1895
   553
                SDL_RWwrite(dst, &colors[i].r, 1, 1);
slouken@7024
   554
                SDL_RWwrite(dst, &colors[i].a, 1, 1);
slouken@1895
   555
            }
slouken@1895
   556
        }
slouken@0
   557
slouken@1895
   558
        /* Write the bitmap offset */
slouken@6666
   559
        bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset);
slouken@1895
   560
        if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
slouken@1895
   561
            SDL_Error(SDL_EFSEEK);
slouken@1895
   562
        }
slouken@1895
   563
        SDL_WriteLE32(dst, bfOffBits);
slouken@1895
   564
        if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
slouken@1895
   565
            SDL_Error(SDL_EFSEEK);
slouken@1895
   566
        }
slouken@0
   567
slouken@1895
   568
        /* Write the bitmap image upside down */
slouken@1895
   569
        bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
slouken@1895
   570
        pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
slouken@1895
   571
        while (bits > (Uint8 *) surface->pixels) {
slouken@1895
   572
            bits -= surface->pitch;
slouken@1895
   573
            if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
slouken@1895
   574
                SDL_Error(SDL_EFWRITE);
slouken@1895
   575
                break;
slouken@1895
   576
            }
slouken@1895
   577
            if (pad) {
slouken@1895
   578
                const Uint8 padbyte = 0;
slouken@1895
   579
                for (i = 0; i < pad; ++i) {
slouken@1895
   580
                    SDL_RWwrite(dst, &padbyte, 1, 1);
slouken@1895
   581
                }
slouken@1895
   582
            }
slouken@1895
   583
        }
slouken@0
   584
slouken@1895
   585
        /* Write the BMP file size */
slouken@6666
   586
        bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset);
slouken@1895
   587
        if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
slouken@1895
   588
            SDL_Error(SDL_EFSEEK);
slouken@1895
   589
        }
slouken@1895
   590
        SDL_WriteLE32(dst, bfSize);
slouken@1895
   591
        if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
slouken@1895
   592
            SDL_Error(SDL_EFSEEK);
slouken@1895
   593
        }
slouken@0
   594
slouken@1895
   595
        /* Close it up.. */
slouken@1895
   596
        SDL_UnlockSurface(surface);
slouken@1895
   597
        if (surface != saveme) {
slouken@1895
   598
            SDL_FreeSurface(surface);
slouken@1895
   599
        }
slouken@1895
   600
    }
slouken@0
   601
slouken@1895
   602
    if (freedst && dst) {
slouken@1895
   603
        SDL_RWclose(dst);
slouken@1895
   604
    }
slouken@1895
   605
    return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
slouken@0
   606
}
slouken@1895
   607
slouken@1895
   608
/* vi: set ts=4 sw=4 expandtab: */