src/video/SDL_bmp.c
author Couriersud <couriersud@arcor.de>
Sun, 11 Jan 2009 23:56:19 +0000
changeset 3024 1a08749aebce
parent 2990 502adab079a4
child 3026 69ab1117dd3b
permissions -rw-r--r--
Add SDL_LoadICO_RW to SDL. Loads best quality icon from *.ico file.
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@2859
     3
    Copyright (C) 1997-2009 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
/* 
slouken@0
    25
   Code to load and save surfaces in Windows BMP format.
slouken@0
    26
slouken@0
    27
   Why support BMP format?  Well, it's a native format for Windows, and
slouken@0
    28
   most image processing programs can read and write it.  It would be nice
slouken@0
    29
   to be able to have at least one image format that we can natively load
slouken@0
    30
   and save, and since PNG is so complex that it would bloat the library,
slouken@0
    31
   BMP is a good alternative. 
slouken@0
    32
slouken@0
    33
   This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
slouken@0
    34
*/
slouken@0
    35
slouken@0
    36
#include "SDL_video.h"
slouken@0
    37
#include "SDL_endian.h"
slouken@2823
    38
#include "SDL_pixels_c.h"
slouken@0
    39
slouken@0
    40
/* Compression encodings for BMP files */
slouken@0
    41
#ifndef BI_RGB
slouken@0
    42
#define BI_RGB		0
slouken@0
    43
#define BI_RLE8		1
slouken@0
    44
#define BI_RLE4		2
slouken@0
    45
#define BI_BITFIELDS	3
slouken@0
    46
#endif
slouken@0
    47
slouken@0
    48
couriersud@3024
    49
static Uint8
couriersud@3024
    50
SDL_Read8(SDL_RWops * src)
couriersud@3024
    51
{
couriersud@3024
    52
    Uint8 value;
couriersud@3024
    53
couriersud@3024
    54
    SDL_RWread(src, &value, 1, 1);
couriersud@3024
    55
    return (value);
couriersud@3024
    56
}
couriersud@3024
    57
couriersud@3024
    58
SDL_Surface *
couriersud@3024
    59
SDL_LoadICO_RW(SDL_RWops * src, int freesrc)
couriersud@3024
    60
{
couriersud@3024
    61
    int was_error;
couriersud@3024
    62
    long fp_offset;
couriersud@3024
    63
    int bmpPitch;
couriersud@3024
    64
    int i, pad;
couriersud@3024
    65
    SDL_Surface *surface;
couriersud@3024
    66
    Uint32 Rmask;
couriersud@3024
    67
    Uint32 Gmask;
couriersud@3024
    68
    Uint32 Bmask;
couriersud@3024
    69
    Uint8 *bits;
couriersud@3024
    70
    int ExpandBMP;
couriersud@3024
    71
    int maxCol = 0;
couriersud@3024
    72
    int icoOfs = 0;
couriersud@3024
    73
    Uint32 palette[256];
couriersud@3024
    74
couriersud@3024
    75
    /* The Win32 ICO file header (14 bytes) */
couriersud@3024
    76
    Uint16 bfReserved;
couriersud@3024
    77
    Uint16 bfType;
couriersud@3024
    78
    Uint16 bfCount;
couriersud@3024
    79
couriersud@3024
    80
    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
couriersud@3024
    81
    Uint32 biSize;
couriersud@3024
    82
    Sint32 biWidth;
couriersud@3024
    83
    Sint32 biHeight;
couriersud@3024
    84
    Uint16 biPlanes;
couriersud@3024
    85
    Uint16 biBitCount;
couriersud@3024
    86
    Uint32 biCompression;
couriersud@3024
    87
    Uint32 biSizeImage;
couriersud@3024
    88
    Sint32 biXPelsPerMeter;
couriersud@3024
    89
    Sint32 biYPelsPerMeter;
couriersud@3024
    90
    Uint32 biClrUsed;
couriersud@3024
    91
    Uint32 biClrImportant;
couriersud@3024
    92
couriersud@3024
    93
    /* Make sure we are passed a valid data source */
couriersud@3024
    94
    surface = NULL;
couriersud@3024
    95
    was_error = 0;
couriersud@3024
    96
    if (src == NULL) {
couriersud@3024
    97
        was_error = 1;
couriersud@3024
    98
        goto done;
couriersud@3024
    99
    }
couriersud@3024
   100
couriersud@3024
   101
    /* Read in the ICO file header */
couriersud@3024
   102
    fp_offset = SDL_RWtell(src);
couriersud@3024
   103
    SDL_ClearError();
couriersud@3024
   104
couriersud@3024
   105
    bfReserved = SDL_ReadLE16(src);
couriersud@3024
   106
    bfType = SDL_ReadLE16(src);
couriersud@3024
   107
    bfCount = SDL_ReadLE16(src);
couriersud@3024
   108
    if ((bfType != 1 && bfType != 2) || (bfCount == 0)) {
couriersud@3024
   109
        SDL_SetError("File is not a Windows ICO file");
couriersud@3024
   110
        was_error = 1;
couriersud@3024
   111
        goto done;
couriersud@3024
   112
    }
couriersud@3024
   113
couriersud@3024
   114
    /* Read the Win32 Icon Directory */
couriersud@3024
   115
    for (i = 0; i < bfCount; i++) {
couriersud@3024
   116
        /* Icon Directory Entries */
couriersud@3024
   117
        int bWidth = SDL_Read8(src);    /* Uint8, but 0 = 256 ! */
couriersud@3024
   118
        int bHeight = SDL_Read8(src);   /* Uint8, but 0 = 256 ! */
couriersud@3024
   119
        int bColorCount = SDL_Read8(src);       /* Uint8, but 0 = 256 ! */
couriersud@3024
   120
        Uint8 bReserved = SDL_Read8(src);
couriersud@3024
   121
        Uint16 wPlanes = SDL_ReadLE16(src);
couriersud@3024
   122
        Uint16 wBitCount = SDL_ReadLE16(src);
couriersud@3024
   123
        Uint32 dwBytesInRes = SDL_ReadLE32(src);
couriersud@3024
   124
        Uint32 dwImageOffset = SDL_ReadLE32(src);
couriersud@3024
   125
couriersud@3024
   126
        if (!bWidth)
couriersud@3024
   127
            bWidth = 256;
couriersud@3024
   128
        if (!bHeight)
couriersud@3024
   129
            bHeight = 256;
couriersud@3024
   130
        if (!bColorCount)
couriersud@3024
   131
            bColorCount = 256;
couriersud@3024
   132
couriersud@3024
   133
        //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
couriersud@3024
   134
        if (bColorCount > maxCol) {
couriersud@3024
   135
            maxCol = bColorCount;
couriersud@3024
   136
            icoOfs = dwImageOffset;
couriersud@3024
   137
            //printf("marked\n");
couriersud@3024
   138
        }
couriersud@3024
   139
    }
couriersud@3024
   140
couriersud@3024
   141
    /* Advance to the DIB Data */
couriersud@3024
   142
    if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
couriersud@3024
   143
        SDL_Error(SDL_EFSEEK);
couriersud@3024
   144
        was_error = 1;
couriersud@3024
   145
        goto done;
couriersud@3024
   146
    }
couriersud@3024
   147
couriersud@3024
   148
    /* Read the Win32 BITMAPINFOHEADER */
couriersud@3024
   149
    biSize = SDL_ReadLE32(src);
couriersud@3024
   150
    if (biSize == 40) {
couriersud@3024
   151
        biWidth = SDL_ReadLE32(src);
couriersud@3024
   152
        biHeight = SDL_ReadLE32(src);
couriersud@3024
   153
        biPlanes = SDL_ReadLE16(src);
couriersud@3024
   154
        biBitCount = SDL_ReadLE16(src);
couriersud@3024
   155
        biCompression = SDL_ReadLE32(src);
couriersud@3024
   156
        biSizeImage = SDL_ReadLE32(src);
couriersud@3024
   157
        biXPelsPerMeter = SDL_ReadLE32(src);
couriersud@3024
   158
        biYPelsPerMeter = SDL_ReadLE32(src);
couriersud@3024
   159
        biClrUsed = SDL_ReadLE32(src);
couriersud@3024
   160
        biClrImportant = SDL_ReadLE32(src);
couriersud@3024
   161
    } else {
couriersud@3024
   162
        SDL_SetError("Unsupported ICO bitmap format");
couriersud@3024
   163
        was_error = 1;
couriersud@3024
   164
        goto done;
couriersud@3024
   165
    }
couriersud@3024
   166
couriersud@3024
   167
    /* Check for read error */
couriersud@3024
   168
    if (SDL_strcmp(SDL_GetError(), "") != 0) {
couriersud@3024
   169
        was_error = 1;
couriersud@3024
   170
        goto done;
couriersud@3024
   171
    }
couriersud@3024
   172
couriersud@3024
   173
    /* We don't support any BMP compression right now */
couriersud@3024
   174
    switch (biCompression) {
couriersud@3024
   175
    case BI_RGB:
couriersud@3024
   176
        /* Default values for the BMP format */
couriersud@3024
   177
        switch (biBitCount) {
couriersud@3024
   178
        case 1:
couriersud@3024
   179
        case 4:
couriersud@3024
   180
            ExpandBMP = biBitCount;
couriersud@3024
   181
            biBitCount = 8;
couriersud@3024
   182
            break;
couriersud@3024
   183
        case 8:
couriersud@3024
   184
            ExpandBMP = 8;
couriersud@3024
   185
            break;
couriersud@3024
   186
        case 32:
couriersud@3024
   187
            Rmask = 0x00FF0000;
couriersud@3024
   188
            Gmask = 0x0000FF00;
couriersud@3024
   189
            Bmask = 0x000000FF;
couriersud@3024
   190
            ExpandBMP = 0;
couriersud@3024
   191
            break;
couriersud@3024
   192
        default:
couriersud@3024
   193
            SDL_SetError("ICO file with unsupported bit count");
couriersud@3024
   194
            was_error = 1;
couriersud@3024
   195
            goto done;
couriersud@3024
   196
        }
couriersud@3024
   197
        break;
couriersud@3024
   198
    default:
couriersud@3024
   199
        SDL_SetError("Compressed ICO files not supported");
couriersud@3024
   200
        was_error = 1;
couriersud@3024
   201
        goto done;
couriersud@3024
   202
    }
couriersud@3024
   203
couriersud@3024
   204
    /* Create a RGBA surface */
couriersud@3024
   205
    biHeight = biHeight >> 1;
couriersud@3024
   206
    //printf("%d x %d\n", biWidth, biHeight);
couriersud@3024
   207
    surface =
couriersud@3024
   208
        SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
couriersud@3024
   209
                             0x0000FF00, 0x000000FF, 0xFF000000);
couriersud@3024
   210
    if (surface == NULL) {
couriersud@3024
   211
        was_error = 1;
couriersud@3024
   212
        goto done;
couriersud@3024
   213
    }
couriersud@3024
   214
couriersud@3024
   215
    /* Load the palette, if any */
couriersud@3024
   216
    //printf("bc %d bused %d\n", biBitCount, biClrUsed);
couriersud@3024
   217
    if (biBitCount <= 8) {
couriersud@3024
   218
        if (biClrUsed == 0) {
couriersud@3024
   219
            biClrUsed = 1 << biBitCount;
couriersud@3024
   220
        }
couriersud@3024
   221
        for (i = 0; i < (int) biClrUsed; ++i) {
couriersud@3024
   222
            SDL_RWread(src, &palette[i], 4, 1);
couriersud@3024
   223
        }
couriersud@3024
   224
    }
couriersud@3024
   225
couriersud@3024
   226
    /* Read the surface pixels.  Note that the bmp image is upside down */
couriersud@3024
   227
    bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
couriersud@3024
   228
    switch (ExpandBMP) {
couriersud@3024
   229
    case 1:
couriersud@3024
   230
        bmpPitch = (biWidth + 7) >> 3;
couriersud@3024
   231
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@3024
   232
        break;
couriersud@3024
   233
    case 4:
couriersud@3024
   234
        bmpPitch = (biWidth + 1) >> 1;
couriersud@3024
   235
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@3024
   236
        break;
couriersud@3024
   237
    case 8:
couriersud@3024
   238
        bmpPitch = biWidth;
couriersud@3024
   239
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@3024
   240
        break;
couriersud@3024
   241
    default:
couriersud@3024
   242
        bmpPitch = biWidth * 4;
couriersud@3024
   243
        pad = 0;
couriersud@3024
   244
        break;
couriersud@3024
   245
    }
couriersud@3024
   246
    while (bits > (Uint8 *) surface->pixels) {
couriersud@3024
   247
        bits -= surface->pitch;
couriersud@3024
   248
        switch (ExpandBMP) {
couriersud@3024
   249
        case 1:
couriersud@3024
   250
        case 4:
couriersud@3024
   251
        case 8:
couriersud@3024
   252
            {
couriersud@3024
   253
                Uint8 pixel = 0;
couriersud@3024
   254
                int shift = (8 - ExpandBMP);
couriersud@3024
   255
                for (i = 0; i < surface->w; ++i) {
couriersud@3024
   256
                    if (i % (8 / ExpandBMP) == 0) {
couriersud@3024
   257
                        if (!SDL_RWread(src, &pixel, 1, 1)) {
couriersud@3024
   258
                            SDL_SetError("Error reading from ICO");
couriersud@3024
   259
                            was_error = 1;
couriersud@3024
   260
                            goto done;
couriersud@3024
   261
                        }
couriersud@3024
   262
                    }
couriersud@3024
   263
                    *((Uint32 *) bits + i) = (palette[pixel >> shift]);
couriersud@3024
   264
                    pixel <<= ExpandBMP;
couriersud@3024
   265
                }
couriersud@3024
   266
            }
couriersud@3024
   267
            break;
couriersud@3024
   268
couriersud@3024
   269
        default:
couriersud@3024
   270
            if (SDL_RWread(src, bits, 1, surface->pitch)
couriersud@3024
   271
                != surface->pitch) {
couriersud@3024
   272
                SDL_Error(SDL_EFREAD);
couriersud@3024
   273
                was_error = 1;
couriersud@3024
   274
                goto done;
couriersud@3024
   275
            }
couriersud@3024
   276
            break;
couriersud@3024
   277
        }
couriersud@3024
   278
        /* Skip padding bytes, ugh */
couriersud@3024
   279
        if (pad) {
couriersud@3024
   280
            Uint8 padbyte;
couriersud@3024
   281
            for (i = 0; i < pad; ++i) {
couriersud@3024
   282
                SDL_RWread(src, &padbyte, 1, 1);
couriersud@3024
   283
            }
couriersud@3024
   284
        }
couriersud@3024
   285
    }
couriersud@3024
   286
    /* Read the mask pixels.  Note that the bmp image is upside down */
couriersud@3024
   287
    bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
couriersud@3024
   288
    ExpandBMP = 1;
couriersud@3024
   289
    bmpPitch = (biWidth + 7) >> 3;
couriersud@3024
   290
    pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@3024
   291
    while (bits > (Uint8 *) surface->pixels) {
couriersud@3024
   292
        Uint8 pixel = 0;
couriersud@3024
   293
        int shift = (8 - ExpandBMP);
couriersud@3024
   294
couriersud@3024
   295
        bits -= surface->pitch;
couriersud@3024
   296
        for (i = 0; i < surface->w; ++i) {
couriersud@3024
   297
            if (i % (8 / ExpandBMP) == 0) {
couriersud@3024
   298
                if (!SDL_RWread(src, &pixel, 1, 1)) {
couriersud@3024
   299
                    SDL_SetError("Error reading from ICO");
couriersud@3024
   300
                    was_error = 1;
couriersud@3024
   301
                    goto done;
couriersud@3024
   302
                }
couriersud@3024
   303
            }
couriersud@3024
   304
            *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
couriersud@3024
   305
            pixel <<= ExpandBMP;
couriersud@3024
   306
        }
couriersud@3024
   307
        /* Skip padding bytes, ugh */
couriersud@3024
   308
        if (pad) {
couriersud@3024
   309
            Uint8 padbyte;
couriersud@3024
   310
            for (i = 0; i < pad; ++i) {
couriersud@3024
   311
                SDL_RWread(src, &padbyte, 1, 1);
couriersud@3024
   312
            }
couriersud@3024
   313
        }
couriersud@3024
   314
    }
couriersud@3024
   315
  done:
couriersud@3024
   316
    if (was_error) {
couriersud@3024
   317
        if (src) {
couriersud@3024
   318
            SDL_RWseek(src, fp_offset, RW_SEEK_SET);
couriersud@3024
   319
        }
couriersud@3024
   320
        if (surface) {
couriersud@3024
   321
            SDL_FreeSurface(surface);
couriersud@3024
   322
        }
couriersud@3024
   323
        surface = NULL;
couriersud@3024
   324
    }
couriersud@3024
   325
    if (freesrc && src) {
couriersud@3024
   326
        SDL_RWclose(src);
couriersud@3024
   327
    }
couriersud@3024
   328
    return (surface);
couriersud@3024
   329
}
couriersud@3024
   330
slouken@1895
   331
SDL_Surface *
slouken@1895
   332
SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
slouken@0
   333
{
slouken@1895
   334
    int was_error;
slouken@1895
   335
    long fp_offset;
slouken@1895
   336
    int bmpPitch;
slouken@1895
   337
    int i, pad;
slouken@1895
   338
    SDL_Surface *surface;
slouken@1895
   339
    Uint32 Rmask;
slouken@1895
   340
    Uint32 Gmask;
slouken@1895
   341
    Uint32 Bmask;
slouken@1895
   342
    SDL_Palette *palette;
slouken@1895
   343
    Uint8 *bits;
slouken@1895
   344
    int ExpandBMP;
slouken@0
   345
slouken@1895
   346
    /* The Win32 BMP file header (14 bytes) */
slouken@1895
   347
    char magic[2];
slouken@1895
   348
    Uint32 bfSize;
slouken@1895
   349
    Uint16 bfReserved1;
slouken@1895
   350
    Uint16 bfReserved2;
slouken@1895
   351
    Uint32 bfOffBits;
slouken@0
   352
slouken@1895
   353
    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
slouken@1895
   354
    Uint32 biSize;
slouken@1895
   355
    Sint32 biWidth;
slouken@1895
   356
    Sint32 biHeight;
slouken@1895
   357
    Uint16 biPlanes;
slouken@1895
   358
    Uint16 biBitCount;
slouken@1895
   359
    Uint32 biCompression;
slouken@1895
   360
    Uint32 biSizeImage;
slouken@1895
   361
    Sint32 biXPelsPerMeter;
slouken@1895
   362
    Sint32 biYPelsPerMeter;
slouken@1895
   363
    Uint32 biClrUsed;
slouken@1895
   364
    Uint32 biClrImportant;
slouken@0
   365
slouken@1895
   366
    /* Make sure we are passed a valid data source */
slouken@1895
   367
    surface = NULL;
slouken@1895
   368
    was_error = 0;
slouken@1895
   369
    if (src == NULL) {
slouken@1895
   370
        was_error = 1;
slouken@1895
   371
        goto done;
slouken@1895
   372
    }
slouken@0
   373
slouken@1895
   374
    /* Read in the BMP file header */
slouken@1895
   375
    fp_offset = SDL_RWtell(src);
slouken@1895
   376
    SDL_ClearError();
slouken@1895
   377
    if (SDL_RWread(src, magic, 1, 2) != 2) {
slouken@1895
   378
        SDL_Error(SDL_EFREAD);
slouken@1895
   379
        was_error = 1;
slouken@1895
   380
        goto done;
slouken@1895
   381
    }
slouken@1895
   382
    if (SDL_strncmp(magic, "BM", 2) != 0) {
slouken@1895
   383
        SDL_SetError("File is not a Windows BMP file");
slouken@1895
   384
        was_error = 1;
slouken@1895
   385
        goto done;
slouken@1895
   386
    }
slouken@1895
   387
    bfSize = SDL_ReadLE32(src);
slouken@1895
   388
    bfReserved1 = SDL_ReadLE16(src);
slouken@1895
   389
    bfReserved2 = SDL_ReadLE16(src);
slouken@1895
   390
    bfOffBits = SDL_ReadLE32(src);
slouken@0
   391
slouken@1895
   392
    /* Read the Win32 BITMAPINFOHEADER */
slouken@1895
   393
    biSize = SDL_ReadLE32(src);
slouken@1895
   394
    if (biSize == 12) {
slouken@1895
   395
        biWidth = (Uint32) SDL_ReadLE16(src);
slouken@1895
   396
        biHeight = (Uint32) SDL_ReadLE16(src);
slouken@1895
   397
        biPlanes = SDL_ReadLE16(src);
slouken@1895
   398
        biBitCount = SDL_ReadLE16(src);
slouken@1895
   399
        biCompression = BI_RGB;
slouken@1895
   400
        biSizeImage = 0;
slouken@1895
   401
        biXPelsPerMeter = 0;
slouken@1895
   402
        biYPelsPerMeter = 0;
slouken@1895
   403
        biClrUsed = 0;
slouken@1895
   404
        biClrImportant = 0;
slouken@1895
   405
    } else {
slouken@1895
   406
        biWidth = SDL_ReadLE32(src);
slouken@1895
   407
        biHeight = SDL_ReadLE32(src);
slouken@1895
   408
        biPlanes = SDL_ReadLE16(src);
slouken@1895
   409
        biBitCount = SDL_ReadLE16(src);
slouken@1895
   410
        biCompression = SDL_ReadLE32(src);
slouken@1895
   411
        biSizeImage = SDL_ReadLE32(src);
slouken@1895
   412
        biXPelsPerMeter = SDL_ReadLE32(src);
slouken@1895
   413
        biYPelsPerMeter = SDL_ReadLE32(src);
slouken@1895
   414
        biClrUsed = SDL_ReadLE32(src);
slouken@1895
   415
        biClrImportant = SDL_ReadLE32(src);
slouken@1895
   416
    }
slouken@0
   417
slouken@1895
   418
    /* Check for read error */
slouken@1895
   419
    if (SDL_strcmp(SDL_GetError(), "") != 0) {
slouken@1895
   420
        was_error = 1;
slouken@1895
   421
        goto done;
slouken@1895
   422
    }
slouken@0
   423
slouken@1895
   424
    /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
slouken@1895
   425
    switch (biBitCount) {
slouken@1895
   426
    case 1:
slouken@1895
   427
    case 4:
slouken@1895
   428
        ExpandBMP = biBitCount;
slouken@1895
   429
        biBitCount = 8;
slouken@1895
   430
        break;
slouken@1895
   431
    default:
slouken@1895
   432
        ExpandBMP = 0;
slouken@1895
   433
        break;
slouken@1895
   434
    }
slouken@0
   435
slouken@1895
   436
    /* We don't support any BMP compression right now */
slouken@1895
   437
    Rmask = Gmask = Bmask = 0;
slouken@1895
   438
    switch (biCompression) {
slouken@1895
   439
    case BI_RGB:
slouken@1895
   440
        /* If there are no masks, use the defaults */
slouken@1895
   441
        if (bfOffBits == (14 + biSize)) {
slouken@1895
   442
            /* Default values for the BMP format */
slouken@1895
   443
            switch (biBitCount) {
slouken@1895
   444
            case 15:
slouken@1895
   445
            case 16:
slouken@1895
   446
                Rmask = 0x7C00;
slouken@1895
   447
                Gmask = 0x03E0;
slouken@1895
   448
                Bmask = 0x001F;
slouken@1895
   449
                break;
slouken@1895
   450
            case 24:
slouken@0
   451
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@1895
   452
                Rmask = 0x000000FF;
slouken@1895
   453
                Gmask = 0x0000FF00;
slouken@1895
   454
                Bmask = 0x00FF0000;
slouken@1895
   455
                break;
slouken@0
   456
#endif
slouken@1895
   457
            case 32:
slouken@1895
   458
                Rmask = 0x00FF0000;
slouken@1895
   459
                Gmask = 0x0000FF00;
slouken@1895
   460
                Bmask = 0x000000FF;
slouken@1895
   461
                break;
slouken@1895
   462
            default:
slouken@1895
   463
                break;
slouken@1895
   464
            }
slouken@1895
   465
            break;
slouken@1895
   466
        }
slouken@1895
   467
        /* Fall through -- read the RGB masks */
slouken@0
   468
slouken@1895
   469
    case BI_BITFIELDS:
slouken@1895
   470
        switch (biBitCount) {
slouken@1895
   471
        case 15:
slouken@1895
   472
        case 16:
slouken@1895
   473
        case 32:
slouken@1895
   474
            Rmask = SDL_ReadLE32(src);
slouken@1895
   475
            Gmask = SDL_ReadLE32(src);
slouken@1895
   476
            Bmask = SDL_ReadLE32(src);
slouken@1895
   477
            break;
slouken@1895
   478
        default:
slouken@1895
   479
            break;
slouken@1895
   480
        }
slouken@1895
   481
        break;
slouken@1895
   482
    default:
slouken@1895
   483
        SDL_SetError("Compressed BMP files not supported");
slouken@1895
   484
        was_error = 1;
slouken@1895
   485
        goto done;
slouken@1895
   486
    }
slouken@0
   487
slouken@1895
   488
    /* Create a compatible surface, note that the colors are RGB ordered */
slouken@1895
   489
    surface =
slouken@1895
   490
        SDL_CreateRGBSurface(0, biWidth, biHeight, biBitCount, Rmask, Gmask,
slouken@1895
   491
                             Bmask, 0);
slouken@1895
   492
    if (surface == NULL) {
slouken@1895
   493
        was_error = 1;
slouken@1895
   494
        goto done;
slouken@1895
   495
    }
slouken@0
   496
slouken@1895
   497
    /* Load the palette, if any */
slouken@1895
   498
    palette = (surface->format)->palette;
slouken@1895
   499
    if (palette) {
slouken@1895
   500
        if (biClrUsed == 0) {
slouken@1895
   501
            biClrUsed = 1 << biBitCount;
slouken@1895
   502
        }
slouken@2920
   503
        if ((int) biClrUsed > palette->ncolors) {
slouken@2851
   504
            palette->ncolors = biClrUsed;
slouken@2851
   505
            palette->colors =
slouken@2851
   506
                (SDL_Color *) SDL_realloc(palette->colors,
slouken@2851
   507
                                          palette->ncolors *
slouken@2851
   508
                                          sizeof(*palette->colors));
slouken@2851
   509
            if (!palette->colors) {
slouken@2851
   510
                SDL_OutOfMemory();
slouken@2851
   511
                was_error = 1;
slouken@2851
   512
                goto done;
slouken@2851
   513
            }
slouken@2920
   514
        } else if ((int) biClrUsed < palette->ncolors) {
slouken@2851
   515
            palette->ncolors = biClrUsed;
slouken@2851
   516
        }
slouken@1895
   517
        if (biSize == 12) {
slouken@1895
   518
            for (i = 0; i < (int) biClrUsed; ++i) {
slouken@1895
   519
                SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@1895
   520
                SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@1895
   521
                SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@1895
   522
                palette->colors[i].unused = SDL_ALPHA_OPAQUE;
slouken@1895
   523
            }
slouken@1895
   524
        } else {
slouken@1895
   525
            for (i = 0; i < (int) biClrUsed; ++i) {
slouken@1895
   526
                SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@1895
   527
                SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@1895
   528
                SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@1895
   529
                SDL_RWread(src, &palette->colors[i].unused, 1, 1);
slouken@1895
   530
            }
slouken@1895
   531
        }
slouken@1895
   532
    }
slouken@0
   533
slouken@1895
   534
    /* Read the surface pixels.  Note that the bmp image is upside down */
slouken@1895
   535
    if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
slouken@1895
   536
        SDL_Error(SDL_EFSEEK);
slouken@1895
   537
        was_error = 1;
slouken@1895
   538
        goto done;
slouken@1895
   539
    }
slouken@1895
   540
    bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
slouken@1895
   541
    switch (ExpandBMP) {
slouken@1895
   542
    case 1:
slouken@1895
   543
        bmpPitch = (biWidth + 7) >> 3;
slouken@1895
   544
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
slouken@1895
   545
        break;
slouken@1895
   546
    case 4:
slouken@1895
   547
        bmpPitch = (biWidth + 1) >> 1;
slouken@1895
   548
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
slouken@1895
   549
        break;
slouken@1895
   550
    default:
slouken@1895
   551
        pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
slouken@1895
   552
        break;
slouken@1895
   553
    }
slouken@1895
   554
    while (bits > (Uint8 *) surface->pixels) {
slouken@1895
   555
        bits -= surface->pitch;
slouken@1895
   556
        switch (ExpandBMP) {
slouken@1895
   557
        case 1:
slouken@1895
   558
        case 4:
slouken@1895
   559
            {
slouken@1895
   560
                Uint8 pixel = 0;
slouken@1895
   561
                int shift = (8 - ExpandBMP);
slouken@1895
   562
                for (i = 0; i < surface->w; ++i) {
slouken@1895
   563
                    if (i % (8 / ExpandBMP) == 0) {
slouken@1895
   564
                        if (!SDL_RWread(src, &pixel, 1, 1)) {
slouken@1895
   565
                            SDL_SetError("Error reading from BMP");
slouken@1895
   566
                            was_error = 1;
slouken@1895
   567
                            goto done;
slouken@1895
   568
                        }
slouken@1895
   569
                    }
slouken@1895
   570
                    *(bits + i) = (pixel >> shift);
slouken@1895
   571
                    pixel <<= ExpandBMP;
slouken@1895
   572
                }
slouken@1895
   573
            }
slouken@1895
   574
            break;
slouken@0
   575
slouken@1895
   576
        default:
slouken@1895
   577
            if (SDL_RWread(src, bits, 1, surface->pitch)
slouken@1895
   578
                != surface->pitch) {
slouken@1895
   579
                SDL_Error(SDL_EFREAD);
slouken@1895
   580
                was_error = 1;
slouken@1895
   581
                goto done;
slouken@1895
   582
            }
slouken@0
   583
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@1895
   584
            /* Byte-swap the pixels if needed. Note that the 24bpp
slouken@1895
   585
               case has already been taken care of above. */
slouken@1895
   586
            switch (biBitCount) {
slouken@1895
   587
            case 15:
slouken@1895
   588
            case 16:
slouken@1895
   589
                {
slouken@1895
   590
                    Uint16 *pix = (Uint16 *) bits;
slouken@1895
   591
                    for (i = 0; i < surface->w; i++)
slouken@1895
   592
                        pix[i] = SDL_Swap16(pix[i]);
slouken@1895
   593
                    break;
slouken@1895
   594
                }
slouken@0
   595
slouken@1895
   596
            case 32:
slouken@1895
   597
                {
slouken@1895
   598
                    Uint32 *pix = (Uint32 *) bits;
slouken@1895
   599
                    for (i = 0; i < surface->w; i++)
slouken@1895
   600
                        pix[i] = SDL_Swap32(pix[i]);
slouken@1895
   601
                    break;
slouken@1895
   602
                }
slouken@1895
   603
            }
slouken@0
   604
#endif
slouken@1895
   605
            break;
slouken@1895
   606
        }
slouken@1895
   607
        /* Skip padding bytes, ugh */
slouken@1895
   608
        if (pad) {
slouken@1895
   609
            Uint8 padbyte;
slouken@1895
   610
            for (i = 0; i < pad; ++i) {
slouken@1895
   611
                SDL_RWread(src, &padbyte, 1, 1);
slouken@1895
   612
            }
slouken@1895
   613
        }
slouken@1895
   614
    }
slouken@1895
   615
  done:
slouken@1895
   616
    if (was_error) {
slouken@1895
   617
        if (src) {
slouken@1895
   618
            SDL_RWseek(src, fp_offset, RW_SEEK_SET);
slouken@1895
   619
        }
slouken@1895
   620
        if (surface) {
slouken@1895
   621
            SDL_FreeSurface(surface);
slouken@1895
   622
        }
slouken@1895
   623
        surface = NULL;
slouken@1895
   624
    }
slouken@1895
   625
    if (freesrc && src) {
slouken@1895
   626
        SDL_RWclose(src);
slouken@1895
   627
    }
slouken@1895
   628
    return (surface);
slouken@0
   629
}
slouken@0
   630
slouken@1895
   631
int
slouken@1895
   632
SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
slouken@0
   633
{
slouken@1895
   634
    long fp_offset;
slouken@1895
   635
    int i, pad;
slouken@1895
   636
    SDL_Surface *surface;
slouken@1895
   637
    Uint8 *bits;
slouken@0
   638
slouken@1895
   639
    /* The Win32 BMP file header (14 bytes) */
slouken@1895
   640
    char magic[2] = { 'B', 'M' };
slouken@1895
   641
    Uint32 bfSize;
slouken@1895
   642
    Uint16 bfReserved1;
slouken@1895
   643
    Uint16 bfReserved2;
slouken@1895
   644
    Uint32 bfOffBits;
slouken@0
   645
slouken@1895
   646
    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
slouken@1895
   647
    Uint32 biSize;
slouken@1895
   648
    Sint32 biWidth;
slouken@1895
   649
    Sint32 biHeight;
slouken@1895
   650
    Uint16 biPlanes;
slouken@1895
   651
    Uint16 biBitCount;
slouken@1895
   652
    Uint32 biCompression;
slouken@1895
   653
    Uint32 biSizeImage;
slouken@1895
   654
    Sint32 biXPelsPerMeter;
slouken@1895
   655
    Sint32 biYPelsPerMeter;
slouken@1895
   656
    Uint32 biClrUsed;
slouken@1895
   657
    Uint32 biClrImportant;
slouken@0
   658
slouken@1895
   659
    /* Make sure we have somewhere to save */
slouken@1895
   660
    surface = NULL;
slouken@1895
   661
    if (dst) {
slouken@2969
   662
        SDL_bool save32bit = SDL_FALSE;
slouken@2969
   663
#ifdef SAVE_32BIT_BMP
slouken@2969
   664
        /* We can save alpha information in a 32-bit BMP */
slouken@2969
   665
        if (saveme->map->info.flags & SDL_COPY_COLORKEY ||
slouken@2969
   666
            saveme->format->Amask) {
slouken@2969
   667
            save32bit = SDL_TRUE;
slouken@2969
   668
        }
slouken@2969
   669
#endif /* SAVE_32BIT_BMP */
slouken@2969
   670
slouken@2969
   671
        if (saveme->format->palette && !save32bit) {
slouken@1895
   672
            if (saveme->format->BitsPerPixel == 8) {
slouken@1895
   673
                surface = saveme;
slouken@1895
   674
            } else {
slouken@1895
   675
                SDL_SetError("%d bpp BMP files not supported",
slouken@1895
   676
                             saveme->format->BitsPerPixel);
slouken@1895
   677
            }
slouken@1895
   678
        } else if ((saveme->format->BitsPerPixel == 24) &&
slouken@0
   679
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@1895
   680
                   (saveme->format->Rmask == 0x00FF0000) &&
slouken@1895
   681
                   (saveme->format->Gmask == 0x0000FF00) &&
slouken@1895
   682
                   (saveme->format->Bmask == 0x000000FF)
slouken@0
   683
#else
slouken@1895
   684
                   (saveme->format->Rmask == 0x000000FF) &&
slouken@1895
   685
                   (saveme->format->Gmask == 0x0000FF00) &&
slouken@1895
   686
                   (saveme->format->Bmask == 0x00FF0000)
slouken@0
   687
#endif
slouken@1895
   688
            ) {
slouken@1895
   689
            surface = saveme;
slouken@1895
   690
        } else {
slouken@2967
   691
            SDL_PixelFormat format;
slouken@0
   692
slouken@2969
   693
            /* If the surface has a colorkey or alpha channel we'll save a
slouken@2969
   694
               32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
slouken@2969
   695
            if (save32bit) {
slouken@2969
   696
                SDL_InitFormat(&format, 32,
slouken@2990
   697
                               0x00FF0000, 0x0000FF00, 0x000000FF,
slouken@2990
   698
                               0xFF000000);
slouken@2969
   699
            } else {
slouken@2969
   700
                SDL_InitFormat(&format, 24,
slouken@0
   701
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@2969
   702
                               0x00FF0000, 0x0000FF00, 0x000000FF,
slouken@0
   703
#else
slouken@2969
   704
                               0x000000FF, 0x0000FF00, 0x00FF0000,
slouken@0
   705
#endif
slouken@2969
   706
                               0);
slouken@2969
   707
            }
slouken@2967
   708
            surface = SDL_ConvertSurface(saveme, &format, 0);
slouken@2967
   709
            if (!surface) {
slouken@2990
   710
                SDL_SetError("Couldn't convert image to %d bpp",
slouken@2990
   711
                             format.BitsPerPixel);
slouken@1895
   712
            }
slouken@1895
   713
        }
slouken@1895
   714
    }
slouken@0
   715
slouken@1895
   716
    if (surface && (SDL_LockSurface(surface) == 0)) {
slouken@1895
   717
        const int bw = surface->w * surface->format->BytesPerPixel;
slouken@1012
   718
slouken@1895
   719
        /* Set the BMP file header values */
slouken@1895
   720
        bfSize = 0;             /* We'll write this when we're done */
slouken@1895
   721
        bfReserved1 = 0;
slouken@1895
   722
        bfReserved2 = 0;
slouken@1895
   723
        bfOffBits = 0;          /* We'll write this when we're done */
slouken@0
   724
slouken@1895
   725
        /* Write the BMP file header values */
slouken@1895
   726
        fp_offset = SDL_RWtell(dst);
slouken@1895
   727
        SDL_ClearError();
slouken@1895
   728
        SDL_RWwrite(dst, magic, 2, 1);
slouken@1895
   729
        SDL_WriteLE32(dst, bfSize);
slouken@1895
   730
        SDL_WriteLE16(dst, bfReserved1);
slouken@1895
   731
        SDL_WriteLE16(dst, bfReserved2);
slouken@1895
   732
        SDL_WriteLE32(dst, bfOffBits);
slouken@0
   733
slouken@1895
   734
        /* Set the BMP info values */
slouken@1895
   735
        biSize = 40;
slouken@1895
   736
        biWidth = surface->w;
slouken@1895
   737
        biHeight = surface->h;
slouken@1895
   738
        biPlanes = 1;
slouken@1895
   739
        biBitCount = surface->format->BitsPerPixel;
slouken@1895
   740
        biCompression = BI_RGB;
slouken@1895
   741
        biSizeImage = surface->h * surface->pitch;
slouken@1895
   742
        biXPelsPerMeter = 0;
slouken@1895
   743
        biYPelsPerMeter = 0;
slouken@1895
   744
        if (surface->format->palette) {
slouken@1895
   745
            biClrUsed = surface->format->palette->ncolors;
slouken@1895
   746
        } else {
slouken@1895
   747
            biClrUsed = 0;
slouken@1895
   748
        }
slouken@1895
   749
        biClrImportant = 0;
slouken@0
   750
slouken@1895
   751
        /* Write the BMP info values */
slouken@1895
   752
        SDL_WriteLE32(dst, biSize);
slouken@1895
   753
        SDL_WriteLE32(dst, biWidth);
slouken@1895
   754
        SDL_WriteLE32(dst, biHeight);
slouken@1895
   755
        SDL_WriteLE16(dst, biPlanes);
slouken@1895
   756
        SDL_WriteLE16(dst, biBitCount);
slouken@1895
   757
        SDL_WriteLE32(dst, biCompression);
slouken@1895
   758
        SDL_WriteLE32(dst, biSizeImage);
slouken@1895
   759
        SDL_WriteLE32(dst, biXPelsPerMeter);
slouken@1895
   760
        SDL_WriteLE32(dst, biYPelsPerMeter);
slouken@1895
   761
        SDL_WriteLE32(dst, biClrUsed);
slouken@1895
   762
        SDL_WriteLE32(dst, biClrImportant);
slouken@0
   763
slouken@1895
   764
        /* Write the palette (in BGR color order) */
slouken@1895
   765
        if (surface->format->palette) {
slouken@1895
   766
            SDL_Color *colors;
slouken@1895
   767
            int ncolors;
slouken@0
   768
slouken@1895
   769
            colors = surface->format->palette->colors;
slouken@1895
   770
            ncolors = surface->format->palette->ncolors;
slouken@1895
   771
            for (i = 0; i < ncolors; ++i) {
slouken@1895
   772
                SDL_RWwrite(dst, &colors[i].b, 1, 1);
slouken@1895
   773
                SDL_RWwrite(dst, &colors[i].g, 1, 1);
slouken@1895
   774
                SDL_RWwrite(dst, &colors[i].r, 1, 1);
slouken@1895
   775
                SDL_RWwrite(dst, &colors[i].unused, 1, 1);
slouken@1895
   776
            }
slouken@1895
   777
        }
slouken@0
   778
slouken@1895
   779
        /* Write the bitmap offset */
slouken@1895
   780
        bfOffBits = SDL_RWtell(dst) - fp_offset;
slouken@1895
   781
        if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
slouken@1895
   782
            SDL_Error(SDL_EFSEEK);
slouken@1895
   783
        }
slouken@1895
   784
        SDL_WriteLE32(dst, bfOffBits);
slouken@1895
   785
        if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
slouken@1895
   786
            SDL_Error(SDL_EFSEEK);
slouken@1895
   787
        }
slouken@0
   788
slouken@1895
   789
        /* Write the bitmap image upside down */
slouken@1895
   790
        bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
slouken@1895
   791
        pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
slouken@1895
   792
        while (bits > (Uint8 *) surface->pixels) {
slouken@1895
   793
            bits -= surface->pitch;
slouken@1895
   794
            if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
slouken@1895
   795
                SDL_Error(SDL_EFWRITE);
slouken@1895
   796
                break;
slouken@1895
   797
            }
slouken@1895
   798
            if (pad) {
slouken@1895
   799
                const Uint8 padbyte = 0;
slouken@1895
   800
                for (i = 0; i < pad; ++i) {
slouken@1895
   801
                    SDL_RWwrite(dst, &padbyte, 1, 1);
slouken@1895
   802
                }
slouken@1895
   803
            }
slouken@1895
   804
        }
slouken@0
   805
slouken@1895
   806
        /* Write the BMP file size */
slouken@1895
   807
        bfSize = SDL_RWtell(dst) - fp_offset;
slouken@1895
   808
        if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
slouken@1895
   809
            SDL_Error(SDL_EFSEEK);
slouken@1895
   810
        }
slouken@1895
   811
        SDL_WriteLE32(dst, bfSize);
slouken@1895
   812
        if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
slouken@1895
   813
            SDL_Error(SDL_EFSEEK);
slouken@1895
   814
        }
slouken@0
   815
slouken@1895
   816
        /* Close it up.. */
slouken@1895
   817
        SDL_UnlockSurface(surface);
slouken@1895
   818
        if (surface != saveme) {
slouken@1895
   819
            SDL_FreeSurface(surface);
slouken@1895
   820
        }
slouken@1895
   821
    }
slouken@0
   822
slouken@1895
   823
    if (freedst && dst) {
slouken@1895
   824
        SDL_RWclose(dst);
slouken@1895
   825
    }
slouken@1895
   826
    return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
slouken@0
   827
}
slouken@1895
   828
slouken@1895
   829
/* vi: set ts=4 sw=4 expandtab: */