IMG_bmp.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 28 Jan 2012 10:51:24 -0500
changeset 326 fa6129ca8503
parent 280 ec4ae96c100c
child 332 5a8a45cd54c9
child 607 1a1189c2978f
permissions -rw-r--r--
Fixed SDL_image header location
slouken@0
     1
/*
slouken@280
     2
  SDL_image:  An example image loading library for use with SDL
slouken@280
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@280
     5
  This software is provided 'as-is', without any express or implied
slouken@280
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@280
     7
  arising from the use of this software.
slouken@0
     8
slouken@280
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@280
    10
  including commercial applications, and to alter it and redistribute it
slouken@280
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@280
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@280
    14
     claim that you wrote the original software. If you use this software
slouken@280
    15
     in a product, an acknowledgment in the product documentation would be
slouken@280
    16
     appreciated but is not required.
slouken@280
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@280
    18
     misrepresented as being the original software.
slouken@280
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@0
    21
slouken@237
    22
#if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
slouken@237
    23
slouken@0
    24
/* This is a BMP image file loading framework */
couriersud@189
    25
/* ICO/CUR file support is here as well since it uses similar internal
couriersud@189
    26
 * representation */
slouken@0
    27
slouken@0
    28
#include <stdio.h>
slouken@0
    29
#include <string.h>
slouken@0
    30
slouken@0
    31
#include "SDL_image.h"
slouken@0
    32
slouken@0
    33
#ifdef LOAD_BMP
slouken@0
    34
slouken@0
    35
/* See if an image is contained in a data source */
slouken@0
    36
int IMG_isBMP(SDL_RWops *src)
slouken@0
    37
{
slouken@117
    38
	int start;
slouken@0
    39
	int is_BMP;
slouken@0
    40
	char magic[2];
slouken@0
    41
icculus@154
    42
	if ( !src )
icculus@154
    43
		return 0;
slouken@117
    44
	start = SDL_RWtell(src);
slouken@0
    45
	is_BMP = 0;
slouken@117
    46
	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
slouken@0
    47
		if ( strncmp(magic, "BM", 2) == 0 ) {
slouken@0
    48
			is_BMP = 1;
slouken@0
    49
		}
slouken@0
    50
	}
slouken@236
    51
	SDL_RWseek(src, start, RW_SEEK_SET);
slouken@0
    52
	return(is_BMP);
slouken@0
    53
}
slouken@0
    54
couriersud@189
    55
static int IMG_isICOCUR(SDL_RWops *src, int type)
couriersud@189
    56
{
couriersud@189
    57
	int start;
couriersud@189
    58
	int is_ICOCUR;
couriersud@189
    59
couriersud@189
    60
	/* The Win32 ICO file header (14 bytes) */
couriersud@189
    61
    Uint16 bfReserved;
couriersud@189
    62
    Uint16 bfType;
couriersud@189
    63
    Uint16 bfCount;
couriersud@189
    64
couriersud@189
    65
	if ( !src )
couriersud@189
    66
		return 0;
couriersud@189
    67
	start = SDL_RWtell(src);
couriersud@189
    68
	is_ICOCUR = 0;
couriersud@189
    69
    bfReserved = SDL_ReadLE16(src);
couriersud@189
    70
    bfType = SDL_ReadLE16(src);
couriersud@189
    71
    bfCount = SDL_ReadLE16(src);
couriersud@189
    72
    if ((bfReserved == 0) && (bfType == type) && (bfCount != 0)) 
couriersud@189
    73
    	is_ICOCUR = 1;
slouken@236
    74
	SDL_RWseek(src, start, RW_SEEK_SET);
couriersud@189
    75
couriersud@189
    76
	return (is_ICOCUR);
couriersud@189
    77
}
couriersud@189
    78
couriersud@189
    79
int IMG_isICO(SDL_RWops *src)
couriersud@189
    80
{
couriersud@189
    81
	return IMG_isICOCUR(src, 1);
couriersud@189
    82
}
couriersud@189
    83
couriersud@189
    84
int IMG_isCUR(SDL_RWops *src)
couriersud@189
    85
{
couriersud@189
    86
	return IMG_isICOCUR(src, 2);
couriersud@189
    87
}
couriersud@189
    88
slouken@101
    89
#include "SDL_error.h"
slouken@101
    90
#include "SDL_video.h"
slouken@101
    91
#include "SDL_endian.h"
slouken@101
    92
slouken@101
    93
/* Compression encodings for BMP files */
slouken@101
    94
#ifndef BI_RGB
slouken@101
    95
#define BI_RGB		0
slouken@101
    96
#define BI_RLE8		1
slouken@101
    97
#define BI_RLE4		2
slouken@101
    98
#define BI_BITFIELDS	3
slouken@101
    99
#endif
slouken@101
   100
slouken@101
   101
static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
slouken@101
   102
{
slouken@101
   103
	/*
slouken@101
   104
	| Sets the surface pixels from src.  A bmp image is upside down.
slouken@101
   105
	*/
slouken@101
   106
	int pitch = surface->pitch;
slouken@101
   107
	int height = surface->h;
slouken@184
   108
	Uint8 *start = (Uint8 *)surface->pixels;
slouken@184
   109
	Uint8 *end = start + (height*pitch);
slouken@184
   110
	Uint8 *bits = end-pitch, *spot;
slouken@101
   111
	int ofs = 0;
slouken@101
   112
	Uint8 ch;
slouken@101
   113
	Uint8 needsPad;
slouken@101
   114
slouken@184
   115
#define COPY_PIXEL(x)	spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
slouken@184
   116
slouken@101
   117
	for (;;) {
slouken@101
   118
		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
slouken@101
   119
		/*
slouken@101
   120
		| encoded mode starts with a run length, and then a byte
slouken@101
   121
		| with two colour indexes to alternate between for the run
slouken@101
   122
		*/
slouken@101
   123
		if ( ch ) {
slouken@101
   124
			Uint8 pixel;
slouken@101
   125
			if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
slouken@101
   126
			if ( isRle8 ) {                 /* 256-color bitmap, compressed */
slouken@101
   127
				do {
slouken@184
   128
					COPY_PIXEL(pixel);
slouken@101
   129
				} while (--ch);
slouken@184
   130
			} else {                         /* 16-color bitmap, compressed */
slouken@101
   131
				Uint8 pixel0 = pixel >> 4;
slouken@101
   132
				Uint8 pixel1 = pixel & 0x0F;
slouken@101
   133
				for (;;) {
slouken@184
   134
					COPY_PIXEL(pixel0);	/* even count, high nibble */
slouken@101
   135
					if (!--ch) break;
slouken@184
   136
					COPY_PIXEL(pixel1);	/* odd count, low nibble */
slouken@101
   137
					if (!--ch) break;
slouken@101
   138
				}
slouken@101
   139
			}
slouken@101
   140
		} else {
slouken@101
   141
			/*
slouken@101
   142
			| A leading zero is an escape; it may signal the end of the bitmap,
slouken@101
   143
			| a cursor move, or some absolute data.
slouken@101
   144
			| zero tag may be absolute mode or an escape
slouken@101
   145
			*/
slouken@101
   146
			if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
slouken@101
   147
			switch (ch) {
slouken@101
   148
			case 0:                         /* end of line */
slouken@101
   149
				ofs = 0;
slouken@101
   150
				bits -= pitch;               /* go to previous */
slouken@101
   151
				break;
slouken@101
   152
			case 1:                         /* end of bitmap */
slouken@101
   153
				return 0;                    /* success! */
slouken@101
   154
			case 2:                         /* delta */
slouken@101
   155
				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
slouken@101
   156
				ofs += ch;
slouken@101
   157
				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
slouken@101
   158
				bits -= (ch * pitch);
slouken@101
   159
				break;
slouken@101
   160
			default:                        /* no compression */
slouken@101
   161
				if (isRle8) {
slouken@101
   162
					needsPad = ( ch & 1 );
slouken@101
   163
					do {
slouken@184
   164
						Uint8 pixel;
slouken@184
   165
						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
slouken@184
   166
						COPY_PIXEL(pixel);
slouken@101
   167
					} while (--ch);
slouken@101
   168
				} else {
slouken@101
   169
					needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
slouken@101
   170
					for (;;) {
slouken@101
   171
						Uint8 pixel;
slouken@101
   172
						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
slouken@184
   173
						COPY_PIXEL(pixel >> 4);
slouken@101
   174
						if (!--ch) break;
slouken@184
   175
						COPY_PIXEL(pixel & 0x0F);
slouken@101
   176
						if (!--ch) break;
slouken@101
   177
					}
slouken@101
   178
				}
slouken@101
   179
				/* pad at even boundary */
slouken@101
   180
				if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
slouken@101
   181
				break;
slouken@101
   182
			}
slouken@101
   183
		}
slouken@101
   184
	}
slouken@101
   185
}
slouken@101
   186
slouken@101
   187
static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
slouken@101
   188
{
slouken@202
   189
	SDL_bool was_error;
slouken@101
   190
	long fp_offset;
slouken@101
   191
	int bmpPitch;
slouken@101
   192
	int i, pad;
slouken@101
   193
	SDL_Surface *surface;
slouken@101
   194
	Uint32 Rmask;
slouken@101
   195
	Uint32 Gmask;
slouken@101
   196
	Uint32 Bmask;
slouken@106
   197
	Uint32 Amask;
slouken@101
   198
	SDL_Palette *palette;
slouken@101
   199
	Uint8 *bits;
slouken@202
   200
	Uint8 *top, *end;
slouken@202
   201
	SDL_bool topDown;
slouken@101
   202
	int ExpandBMP;
slouken@101
   203
slouken@101
   204
	/* The Win32 BMP file header (14 bytes) */
slouken@101
   205
	char   magic[2];
slouken@101
   206
	Uint32 bfSize;
slouken@101
   207
	Uint16 bfReserved1;
slouken@101
   208
	Uint16 bfReserved2;
slouken@101
   209
	Uint32 bfOffBits;
slouken@101
   210
slouken@101
   211
	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
slouken@101
   212
	Uint32 biSize;
slouken@101
   213
	Sint32 biWidth;
slouken@101
   214
	Sint32 biHeight;
slouken@101
   215
	Uint16 biPlanes;
slouken@101
   216
	Uint16 biBitCount;
slouken@101
   217
	Uint32 biCompression;
slouken@101
   218
	Uint32 biSizeImage;
slouken@101
   219
	Sint32 biXPelsPerMeter;
slouken@101
   220
	Sint32 biYPelsPerMeter;
slouken@101
   221
	Uint32 biClrUsed;
slouken@101
   222
	Uint32 biClrImportant;
slouken@101
   223
slouken@101
   224
	/* Make sure we are passed a valid data source */
slouken@101
   225
	surface = NULL;
slouken@202
   226
	was_error = SDL_FALSE;
slouken@101
   227
	if ( src == NULL ) {
slouken@202
   228
		was_error = SDL_TRUE;
slouken@101
   229
		goto done;
slouken@101
   230
	}
slouken@101
   231
slouken@101
   232
	/* Read in the BMP file header */
slouken@101
   233
	fp_offset = SDL_RWtell(src);
slouken@101
   234
	SDL_ClearError();
slouken@101
   235
	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
slouken@101
   236
		SDL_Error(SDL_EFREAD);
slouken@202
   237
		was_error = SDL_TRUE;
slouken@101
   238
		goto done;
slouken@101
   239
	}
slouken@101
   240
	if ( strncmp(magic, "BM", 2) != 0 ) {
slouken@253
   241
		IMG_SetError("File is not a Windows BMP file");
slouken@202
   242
		was_error = SDL_TRUE;
slouken@101
   243
		goto done;
slouken@101
   244
	}
slouken@101
   245
	bfSize		= SDL_ReadLE32(src);
slouken@101
   246
	bfReserved1	= SDL_ReadLE16(src);
slouken@101
   247
	bfReserved2	= SDL_ReadLE16(src);
slouken@101
   248
	bfOffBits	= SDL_ReadLE32(src);
slouken@101
   249
slouken@101
   250
	/* Read the Win32 BITMAPINFOHEADER */
slouken@101
   251
	biSize		= SDL_ReadLE32(src);
slouken@101
   252
	if ( biSize == 12 ) {
slouken@101
   253
		biWidth		= (Uint32)SDL_ReadLE16(src);
slouken@101
   254
		biHeight	= (Uint32)SDL_ReadLE16(src);
slouken@101
   255
		biPlanes	= SDL_ReadLE16(src);
slouken@101
   256
		biBitCount	= SDL_ReadLE16(src);
slouken@101
   257
		biCompression	= BI_RGB;
slouken@101
   258
		biSizeImage	= 0;
slouken@101
   259
		biXPelsPerMeter	= 0;
slouken@101
   260
		biYPelsPerMeter	= 0;
slouken@101
   261
		biClrUsed	= 0;
slouken@101
   262
		biClrImportant	= 0;
slouken@101
   263
	} else {
slouken@101
   264
		biWidth		= SDL_ReadLE32(src);
slouken@101
   265
		biHeight	= SDL_ReadLE32(src);
slouken@101
   266
		biPlanes	= SDL_ReadLE16(src);
slouken@101
   267
		biBitCount	= SDL_ReadLE16(src);
slouken@101
   268
		biCompression	= SDL_ReadLE32(src);
slouken@101
   269
		biSizeImage	= SDL_ReadLE32(src);
slouken@101
   270
		biXPelsPerMeter	= SDL_ReadLE32(src);
slouken@101
   271
		biYPelsPerMeter	= SDL_ReadLE32(src);
slouken@101
   272
		biClrUsed	= SDL_ReadLE32(src);
slouken@101
   273
		biClrImportant	= SDL_ReadLE32(src);
slouken@101
   274
	}
slouken@202
   275
	if (biHeight < 0) {
slouken@202
   276
		topDown = SDL_TRUE;
slouken@202
   277
		biHeight = -biHeight;
slouken@202
   278
	} else {
slouken@202
   279
		topDown = SDL_FALSE;
slouken@202
   280
	}
slouken@101
   281
slouken@101
   282
	/* Check for read error */
slouken@101
   283
	if ( strcmp(SDL_GetError(), "") != 0 ) {
slouken@202
   284
		was_error = SDL_TRUE;
slouken@101
   285
		goto done;
slouken@101
   286
	}
slouken@101
   287
slouken@101
   288
	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
slouken@101
   289
	switch (biBitCount) {
slouken@101
   290
		case 1:
slouken@101
   291
		case 4:
slouken@101
   292
			ExpandBMP = biBitCount;
slouken@101
   293
			biBitCount = 8;
slouken@101
   294
			break;
slouken@101
   295
		default:
slouken@101
   296
			ExpandBMP = 0;
slouken@101
   297
			break;
slouken@101
   298
	}
slouken@101
   299
slouken@101
   300
	/* RLE4 and RLE8 BMP compression is supported */
slouken@106
   301
	Rmask = Gmask = Bmask = Amask = 0;
slouken@101
   302
	switch (biCompression) {
slouken@101
   303
		case BI_RGB:
slouken@101
   304
			/* If there are no masks, use the defaults */
slouken@101
   305
			if ( bfOffBits == (14+biSize) ) {
slouken@101
   306
				/* Default values for the BMP format */
slouken@101
   307
				switch (biBitCount) {
slouken@101
   308
					case 15:
slouken@101
   309
					case 16:
slouken@101
   310
						Rmask = 0x7C00;
slouken@101
   311
						Gmask = 0x03E0;
slouken@101
   312
						Bmask = 0x001F;
slouken@101
   313
						break;
slouken@101
   314
					case 24:
slouken@101
   315
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@101
   316
					        Rmask = 0x000000FF;
slouken@101
   317
					        Gmask = 0x0000FF00;
slouken@101
   318
					        Bmask = 0x00FF0000;
slouken@106
   319
#else
slouken@106
   320
						Rmask = 0x00FF0000;
slouken@106
   321
						Gmask = 0x0000FF00;
slouken@106
   322
						Bmask = 0x000000FF;
slouken@106
   323
#endif
slouken@101
   324
						break;
slouken@101
   325
					case 32:
slouken@106
   326
						Amask = 0xFF000000;
slouken@101
   327
						Rmask = 0x00FF0000;
slouken@101
   328
						Gmask = 0x0000FF00;
slouken@101
   329
						Bmask = 0x000000FF;
slouken@101
   330
						break;
slouken@101
   331
					default:
slouken@101
   332
						break;
slouken@101
   333
				}
slouken@101
   334
				break;
slouken@101
   335
			}
slouken@101
   336
			/* Fall through -- read the RGB masks */
slouken@101
   337
slouken@101
   338
		default:
slouken@101
   339
			switch (biBitCount) {
slouken@101
   340
				case 15:
slouken@101
   341
				case 16:
slouken@247
   342
					Rmask = SDL_ReadLE32(src);
slouken@247
   343
					Gmask = SDL_ReadLE32(src);
slouken@247
   344
					Bmask = SDL_ReadLE32(src);
slouken@247
   345
					break;
slouken@101
   346
				case 32:
slouken@101
   347
					Rmask = SDL_ReadLE32(src);
slouken@101
   348
					Gmask = SDL_ReadLE32(src);
slouken@101
   349
					Bmask = SDL_ReadLE32(src);
slouken@106
   350
					Amask = SDL_ReadLE32(src);
slouken@101
   351
					break;
slouken@101
   352
				default:
slouken@101
   353
					break;
slouken@101
   354
			}
slouken@101
   355
			break;
slouken@101
   356
	}
slouken@101
   357
slouken@101
   358
	/* Create a compatible surface, note that the colors are RGB ordered */
slouken@101
   359
	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
slouken@106
   360
			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
slouken@101
   361
	if ( surface == NULL ) {
slouken@202
   362
		was_error = SDL_TRUE;
slouken@101
   363
		goto done;
slouken@101
   364
	}
slouken@101
   365
slouken@101
   366
	/* Load the palette, if any */
slouken@101
   367
	palette = (surface->format)->palette;
slouken@101
   368
	if ( palette ) {
slouken@236
   369
		if ( SDL_RWseek(src, fp_offset+14+biSize, RW_SEEK_SET) < 0 ) {
slouken@151
   370
			SDL_Error(SDL_EFSEEK);
slouken@202
   371
			was_error = SDL_TRUE;
slouken@151
   372
			goto done;
slouken@151
   373
		}
slouken@151
   374
slouken@101
   375
		/*
slouken@101
   376
		| guich: always use 1<<bpp b/c some bitmaps can bring wrong information
slouken@101
   377
		| for colorsUsed
slouken@101
   378
		*/
slouken@101
   379
		/* if ( biClrUsed == 0 ) {  */
slouken@101
   380
		biClrUsed = 1 << biBitCount;
slouken@101
   381
		/* } */
slouken@101
   382
		if ( biSize == 12 ) {
slouken@101
   383
			for ( i = 0; i < (int)biClrUsed; ++i ) {
slouken@101
   384
				SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@101
   385
				SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@101
   386
				SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@101
   387
				palette->colors[i].unused = 0;
slouken@101
   388
			}	
slouken@101
   389
		} else {
slouken@101
   390
			for ( i = 0; i < (int)biClrUsed; ++i ) {
slouken@101
   391
				SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@101
   392
				SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@101
   393
				SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@101
   394
				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
slouken@101
   395
			}	
slouken@101
   396
		}
slouken@101
   397
		palette->ncolors = biClrUsed;
slouken@101
   398
	}
slouken@101
   399
slouken@101
   400
	/* Read the surface pixels.  Note that the bmp image is upside down */
slouken@236
   401
	if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
slouken@101
   402
		SDL_Error(SDL_EFSEEK);
slouken@202
   403
		was_error = SDL_TRUE;
slouken@101
   404
		goto done;
slouken@101
   405
	}
slouken@101
   406
	if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
slouken@101
   407
		was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
slouken@253
   408
		if (was_error) IMG_SetError("Error reading from BMP");
slouken@101
   409
		goto done;
slouken@101
   410
	}
slouken@202
   411
	top = (Uint8 *)surface->pixels;
slouken@202
   412
	end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
slouken@101
   413
	switch (ExpandBMP) {
slouken@101
   414
		case 1:
slouken@101
   415
			bmpPitch = (biWidth + 7) >> 3;
slouken@101
   416
			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
slouken@101
   417
			break;
slouken@101
   418
		case 4:
slouken@101
   419
			bmpPitch = (biWidth + 1) >> 1;
slouken@101
   420
			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
slouken@101
   421
			break;
slouken@101
   422
		default:
slouken@101
   423
			pad  = ((surface->pitch%4) ?
slouken@101
   424
					(4-(surface->pitch%4)) : 0);
slouken@101
   425
			break;
slouken@101
   426
	}
slouken@202
   427
	if ( topDown ) {
slouken@202
   428
		bits = top;
slouken@202
   429
	} else {
slouken@202
   430
		bits = end - surface->pitch;
slouken@202
   431
	}
slouken@202
   432
	while ( bits >= top && bits < end ) {
slouken@101
   433
		switch (ExpandBMP) {
slouken@101
   434
			case 1:
slouken@101
   435
			case 4: {
slouken@101
   436
			Uint8 pixel = 0;
slouken@101
   437
			int   shift = (8-ExpandBMP);
slouken@101
   438
			for ( i=0; i<surface->w; ++i ) {
slouken@101
   439
				if ( i%(8/ExpandBMP) == 0 ) {
slouken@101
   440
					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
slouken@253
   441
						IMG_SetError(
slouken@101
   442
					"Error reading from BMP");
slouken@202
   443
						was_error = SDL_TRUE;
slouken@101
   444
						goto done;
slouken@101
   445
					}
slouken@101
   446
				}
slouken@101
   447
				*(bits+i) = (pixel>>shift);
slouken@101
   448
				pixel <<= ExpandBMP;
slouken@101
   449
			} }
slouken@101
   450
			break;
slouken@101
   451
slouken@101
   452
			default:
slouken@101
   453
			if ( SDL_RWread(src, bits, 1, surface->pitch)
slouken@101
   454
							 != surface->pitch ) {
slouken@101
   455
				SDL_Error(SDL_EFREAD);
slouken@202
   456
				was_error = SDL_TRUE;
slouken@101
   457
				goto done;
slouken@101
   458
			}
slouken@101
   459
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@101
   460
			/* Byte-swap the pixels if needed. Note that the 24bpp
slouken@101
   461
			   case has already been taken care of above. */
slouken@101
   462
			switch(biBitCount) {
slouken@101
   463
				case 15:
slouken@101
   464
				case 16: {
slouken@101
   465
				        Uint16 *pix = (Uint16 *)bits;
slouken@101
   466
					for(i = 0; i < surface->w; i++)
slouken@101
   467
					        pix[i] = SDL_Swap16(pix[i]);
slouken@101
   468
					break;
slouken@101
   469
				}
slouken@101
   470
slouken@101
   471
				case 32: {
slouken@101
   472
				        Uint32 *pix = (Uint32 *)bits;
slouken@101
   473
					for(i = 0; i < surface->w; i++)
slouken@101
   474
					        pix[i] = SDL_Swap32(pix[i]);
slouken@101
   475
					break;
slouken@101
   476
				}
slouken@101
   477
			}
slouken@101
   478
#endif
slouken@101
   479
			break;
slouken@101
   480
		}
slouken@101
   481
		/* Skip padding bytes, ugh */
slouken@101
   482
		if ( pad ) {
slouken@101
   483
			Uint8 padbyte;
slouken@101
   484
			for ( i=0; i<pad; ++i ) {
slouken@101
   485
				SDL_RWread(src, &padbyte, 1, 1);
slouken@101
   486
			}
slouken@101
   487
		}
slouken@202
   488
		if ( topDown ) {
slouken@202
   489
			bits += surface->pitch;
slouken@202
   490
		} else {
slouken@202
   491
			bits -= surface->pitch;
slouken@202
   492
		}
slouken@101
   493
	}
slouken@101
   494
done:
slouken@101
   495
	if ( was_error ) {
slouken@118
   496
		if ( src ) {
slouken@236
   497
			SDL_RWseek(src, fp_offset, RW_SEEK_SET);
slouken@118
   498
		}
slouken@101
   499
		if ( surface ) {
slouken@101
   500
			SDL_FreeSurface(surface);
slouken@101
   501
		}
slouken@101
   502
		surface = NULL;
slouken@101
   503
	}
slouken@101
   504
	if ( freesrc && src ) {
slouken@101
   505
		SDL_RWclose(src);
slouken@101
   506
	}
slouken@101
   507
	return(surface);
slouken@101
   508
}
slouken@101
   509
couriersud@189
   510
static Uint8
couriersud@189
   511
SDL_Read8(SDL_RWops * src)
couriersud@189
   512
{
couriersud@189
   513
    Uint8 value;
couriersud@189
   514
couriersud@189
   515
    SDL_RWread(src, &value, 1, 1);
couriersud@189
   516
    return (value);
couriersud@189
   517
}
couriersud@189
   518
couriersud@189
   519
static SDL_Surface *
couriersud@189
   520
LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
couriersud@189
   521
{
slouken@202
   522
    SDL_bool was_error;
couriersud@189
   523
    long fp_offset;
couriersud@189
   524
    int bmpPitch;
couriersud@189
   525
    int i, pad;
couriersud@189
   526
    SDL_Surface *surface;
couriersud@189
   527
    Uint32 Rmask;
couriersud@189
   528
    Uint32 Gmask;
couriersud@189
   529
    Uint32 Bmask;
couriersud@189
   530
    Uint8 *bits;
couriersud@189
   531
    int ExpandBMP;
couriersud@189
   532
    int maxCol = 0;
couriersud@189
   533
    int icoOfs = 0;
couriersud@189
   534
    Uint32 palette[256];
couriersud@189
   535
couriersud@189
   536
    /* The Win32 ICO file header (14 bytes) */
couriersud@189
   537
    Uint16 bfReserved;
couriersud@189
   538
    Uint16 bfType;
couriersud@189
   539
    Uint16 bfCount;
couriersud@189
   540
couriersud@189
   541
    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
couriersud@189
   542
    Uint32 biSize;
couriersud@189
   543
    Sint32 biWidth;
couriersud@189
   544
    Sint32 biHeight;
couriersud@189
   545
    Uint16 biPlanes;
couriersud@189
   546
    Uint16 biBitCount;
couriersud@189
   547
    Uint32 biCompression;
couriersud@189
   548
    Uint32 biSizeImage;
couriersud@189
   549
    Sint32 biXPelsPerMeter;
couriersud@189
   550
    Sint32 biYPelsPerMeter;
couriersud@189
   551
    Uint32 biClrUsed;
couriersud@189
   552
    Uint32 biClrImportant;
couriersud@189
   553
couriersud@189
   554
    /* Make sure we are passed a valid data source */
couriersud@189
   555
    surface = NULL;
slouken@202
   556
    was_error = SDL_FALSE;
couriersud@189
   557
    if (src == NULL) {
slouken@202
   558
        was_error = SDL_TRUE;
couriersud@189
   559
        goto done;
couriersud@189
   560
    }
couriersud@189
   561
couriersud@189
   562
    /* Read in the ICO file header */
couriersud@189
   563
    fp_offset = SDL_RWtell(src);
couriersud@189
   564
    SDL_ClearError();
couriersud@189
   565
couriersud@189
   566
    bfReserved = SDL_ReadLE16(src);
couriersud@189
   567
    bfType = SDL_ReadLE16(src);
couriersud@189
   568
    bfCount = SDL_ReadLE16(src);
couriersud@189
   569
    if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
slouken@253
   570
        IMG_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
slouken@202
   571
        was_error = SDL_TRUE;
couriersud@189
   572
        goto done;
couriersud@189
   573
    }
couriersud@189
   574
couriersud@189
   575
    /* Read the Win32 Icon Directory */
couriersud@189
   576
    for (i = 0; i < bfCount; i++) {
couriersud@189
   577
        /* Icon Directory Entries */
couriersud@189
   578
        int bWidth = SDL_Read8(src);    /* Uint8, but 0 = 256 ! */
couriersud@189
   579
        int bHeight = SDL_Read8(src);   /* Uint8, but 0 = 256 ! */
couriersud@189
   580
        int bColorCount = SDL_Read8(src);       /* Uint8, but 0 = 256 ! */
couriersud@189
   581
        Uint8 bReserved = SDL_Read8(src);
couriersud@189
   582
        Uint16 wPlanes = SDL_ReadLE16(src);
couriersud@189
   583
        Uint16 wBitCount = SDL_ReadLE16(src);
couriersud@189
   584
        Uint32 dwBytesInRes = SDL_ReadLE32(src);
couriersud@189
   585
        Uint32 dwImageOffset = SDL_ReadLE32(src);
couriersud@189
   586
couriersud@189
   587
        if (!bWidth)
couriersud@189
   588
            bWidth = 256;
couriersud@189
   589
        if (!bHeight)
couriersud@189
   590
            bHeight = 256;
couriersud@189
   591
        if (!bColorCount)
couriersud@189
   592
            bColorCount = 256;
couriersud@189
   593
couriersud@189
   594
        //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
couriersud@189
   595
        if (bColorCount > maxCol) {
couriersud@189
   596
            maxCol = bColorCount;
couriersud@189
   597
            icoOfs = dwImageOffset;
couriersud@189
   598
            //printf("marked\n");
couriersud@189
   599
        }
couriersud@189
   600
    }
couriersud@189
   601
couriersud@189
   602
    /* Advance to the DIB Data */
couriersud@189
   603
    if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
couriersud@189
   604
        SDL_Error(SDL_EFSEEK);
slouken@202
   605
        was_error = SDL_TRUE;
couriersud@189
   606
        goto done;
couriersud@189
   607
    }
couriersud@189
   608
couriersud@189
   609
    /* Read the Win32 BITMAPINFOHEADER */
couriersud@189
   610
    biSize = SDL_ReadLE32(src);
couriersud@189
   611
    if (biSize == 40) {
couriersud@189
   612
        biWidth = SDL_ReadLE32(src);
couriersud@189
   613
        biHeight = SDL_ReadLE32(src);
couriersud@189
   614
        biPlanes = SDL_ReadLE16(src);
couriersud@189
   615
        biBitCount = SDL_ReadLE16(src);
couriersud@189
   616
        biCompression = SDL_ReadLE32(src);
couriersud@189
   617
        biSizeImage = SDL_ReadLE32(src);
couriersud@189
   618
        biXPelsPerMeter = SDL_ReadLE32(src);
couriersud@189
   619
        biYPelsPerMeter = SDL_ReadLE32(src);
couriersud@189
   620
        biClrUsed = SDL_ReadLE32(src);
couriersud@189
   621
        biClrImportant = SDL_ReadLE32(src);
couriersud@189
   622
    } else {
slouken@253
   623
        IMG_SetError("Unsupported ICO bitmap format");
slouken@202
   624
        was_error = SDL_TRUE;
couriersud@189
   625
        goto done;
couriersud@189
   626
    }
couriersud@189
   627
couriersud@189
   628
    /* Check for read error */
couriersud@189
   629
    if (SDL_strcmp(SDL_GetError(), "") != 0) {
slouken@202
   630
        was_error = SDL_TRUE;
couriersud@189
   631
        goto done;
couriersud@189
   632
    }
couriersud@189
   633
couriersud@189
   634
    /* We don't support any BMP compression right now */
couriersud@189
   635
    switch (biCompression) {
couriersud@189
   636
    case BI_RGB:
couriersud@189
   637
        /* Default values for the BMP format */
couriersud@189
   638
        switch (biBitCount) {
couriersud@189
   639
        case 1:
couriersud@189
   640
        case 4:
couriersud@189
   641
            ExpandBMP = biBitCount;
couriersud@189
   642
            biBitCount = 8;
couriersud@189
   643
            break;
couriersud@189
   644
        case 8:
couriersud@189
   645
            ExpandBMP = 8;
couriersud@189
   646
            break;
couriersud@189
   647
        case 32:
couriersud@189
   648
            Rmask = 0x00FF0000;
couriersud@189
   649
            Gmask = 0x0000FF00;
couriersud@189
   650
            Bmask = 0x000000FF;
couriersud@189
   651
            ExpandBMP = 0;
couriersud@189
   652
            break;
couriersud@189
   653
        default:
slouken@253
   654
            IMG_SetError("ICO file with unsupported bit count");
slouken@202
   655
            was_error = SDL_TRUE;
couriersud@189
   656
            goto done;
couriersud@189
   657
        }
couriersud@189
   658
        break;
couriersud@189
   659
    default:
slouken@253
   660
        IMG_SetError("Compressed ICO files not supported");
slouken@202
   661
        was_error = SDL_TRUE;
couriersud@189
   662
        goto done;
couriersud@189
   663
    }
couriersud@189
   664
couriersud@189
   665
    /* Create a RGBA surface */
couriersud@189
   666
    biHeight = biHeight >> 1;
couriersud@189
   667
    //printf("%d x %d\n", biWidth, biHeight);
couriersud@189
   668
    surface =
couriersud@189
   669
        SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
couriersud@189
   670
                             0x0000FF00, 0x000000FF, 0xFF000000);
couriersud@189
   671
    if (surface == NULL) {
slouken@202
   672
        was_error = SDL_TRUE;
couriersud@189
   673
        goto done;
couriersud@189
   674
    }
couriersud@189
   675
couriersud@189
   676
    /* Load the palette, if any */
couriersud@189
   677
    //printf("bc %d bused %d\n", biBitCount, biClrUsed);
couriersud@189
   678
    if (biBitCount <= 8) {
couriersud@189
   679
        if (biClrUsed == 0) {
couriersud@189
   680
            biClrUsed = 1 << biBitCount;
couriersud@189
   681
        }
couriersud@189
   682
        for (i = 0; i < (int) biClrUsed; ++i) {
couriersud@189
   683
            SDL_RWread(src, &palette[i], 4, 1);
couriersud@189
   684
        }
couriersud@189
   685
    }
couriersud@189
   686
couriersud@189
   687
    /* Read the surface pixels.  Note that the bmp image is upside down */
couriersud@189
   688
    bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
couriersud@189
   689
    switch (ExpandBMP) {
couriersud@189
   690
    case 1:
couriersud@189
   691
        bmpPitch = (biWidth + 7) >> 3;
couriersud@189
   692
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@189
   693
        break;
couriersud@189
   694
    case 4:
couriersud@189
   695
        bmpPitch = (biWidth + 1) >> 1;
couriersud@189
   696
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@189
   697
        break;
couriersud@189
   698
    case 8:
couriersud@189
   699
        bmpPitch = biWidth;
couriersud@189
   700
        pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@189
   701
        break;
couriersud@189
   702
    default:
couriersud@189
   703
        bmpPitch = biWidth * 4;
couriersud@189
   704
        pad = 0;
couriersud@189
   705
        break;
couriersud@189
   706
    }
couriersud@189
   707
    while (bits > (Uint8 *) surface->pixels) {
couriersud@189
   708
        bits -= surface->pitch;
couriersud@189
   709
        switch (ExpandBMP) {
couriersud@189
   710
        case 1:
couriersud@189
   711
        case 4:
couriersud@189
   712
        case 8:
couriersud@189
   713
            {
couriersud@189
   714
                Uint8 pixel = 0;
couriersud@189
   715
                int shift = (8 - ExpandBMP);
couriersud@189
   716
                for (i = 0; i < surface->w; ++i) {
couriersud@189
   717
                    if (i % (8 / ExpandBMP) == 0) {
couriersud@189
   718
                        if (!SDL_RWread(src, &pixel, 1, 1)) {
slouken@253
   719
                            IMG_SetError("Error reading from ICO");
slouken@202
   720
                            was_error = SDL_TRUE;
couriersud@189
   721
                            goto done;
couriersud@189
   722
                        }
couriersud@189
   723
                    }
couriersud@189
   724
                    *((Uint32 *) bits + i) = (palette[pixel >> shift]);
couriersud@189
   725
                    pixel <<= ExpandBMP;
couriersud@189
   726
                }
couriersud@189
   727
            }
couriersud@189
   728
            break;
couriersud@189
   729
couriersud@189
   730
        default:
couriersud@189
   731
            if (SDL_RWread(src, bits, 1, surface->pitch)
couriersud@189
   732
                != surface->pitch) {
couriersud@189
   733
                SDL_Error(SDL_EFREAD);
slouken@202
   734
                was_error = SDL_TRUE;
couriersud@189
   735
                goto done;
couriersud@189
   736
            }
couriersud@189
   737
            break;
couriersud@189
   738
        }
couriersud@189
   739
        /* Skip padding bytes, ugh */
couriersud@189
   740
        if (pad) {
couriersud@189
   741
            Uint8 padbyte;
couriersud@189
   742
            for (i = 0; i < pad; ++i) {
couriersud@189
   743
                SDL_RWread(src, &padbyte, 1, 1);
couriersud@189
   744
            }
couriersud@189
   745
        }
couriersud@189
   746
    }
couriersud@189
   747
    /* Read the mask pixels.  Note that the bmp image is upside down */
couriersud@189
   748
    bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
couriersud@189
   749
    ExpandBMP = 1;
couriersud@189
   750
    bmpPitch = (biWidth + 7) >> 3;
couriersud@189
   751
    pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
couriersud@189
   752
    while (bits > (Uint8 *) surface->pixels) {
couriersud@189
   753
        Uint8 pixel = 0;
couriersud@189
   754
        int shift = (8 - ExpandBMP);
couriersud@189
   755
couriersud@189
   756
        bits -= surface->pitch;
couriersud@189
   757
        for (i = 0; i < surface->w; ++i) {
couriersud@189
   758
            if (i % (8 / ExpandBMP) == 0) {
couriersud@189
   759
                if (!SDL_RWread(src, &pixel, 1, 1)) {
slouken@253
   760
                    IMG_SetError("Error reading from ICO");
slouken@202
   761
                    was_error = SDL_TRUE;
couriersud@189
   762
                    goto done;
couriersud@189
   763
                }
couriersud@189
   764
            }
couriersud@189
   765
            *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
couriersud@189
   766
            pixel <<= ExpandBMP;
couriersud@189
   767
        }
couriersud@189
   768
        /* Skip padding bytes, ugh */
couriersud@189
   769
        if (pad) {
couriersud@189
   770
            Uint8 padbyte;
couriersud@189
   771
            for (i = 0; i < pad; ++i) {
couriersud@189
   772
                SDL_RWread(src, &padbyte, 1, 1);
couriersud@189
   773
            }
couriersud@189
   774
        }
couriersud@189
   775
    }
couriersud@189
   776
  done:
couriersud@189
   777
    if (was_error) {
couriersud@189
   778
        if (src) {
couriersud@189
   779
            SDL_RWseek(src, fp_offset, RW_SEEK_SET);
couriersud@189
   780
        }
couriersud@189
   781
        if (surface) {
couriersud@189
   782
            SDL_FreeSurface(surface);
couriersud@189
   783
        }
couriersud@189
   784
        surface = NULL;
couriersud@189
   785
    }
couriersud@189
   786
    if (freesrc && src) {
couriersud@189
   787
        SDL_RWclose(src);
couriersud@189
   788
    }
couriersud@189
   789
    return (surface);
couriersud@189
   790
}
couriersud@189
   791
slouken@0
   792
/* Load a BMP type image from an SDL datasource */
slouken@0
   793
SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
slouken@0
   794
{
slouken@101
   795
	return(LoadBMP_RW(src, 0));
slouken@0
   796
}
slouken@0
   797
couriersud@189
   798
/* Load a ICO type image from an SDL datasource */
couriersud@189
   799
SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
couriersud@189
   800
{
couriersud@189
   801
	return(LoadICOCUR_RW(src, 1, 0));
couriersud@189
   802
}
couriersud@189
   803
couriersud@189
   804
/* Load a CUR type image from an SDL datasource */
couriersud@189
   805
SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
couriersud@189
   806
{
couriersud@189
   807
	return(LoadICOCUR_RW(src, 2, 0));
couriersud@189
   808
}
couriersud@189
   809
slouken@0
   810
#else
slouken@0
   811
slouken@0
   812
/* See if an image is contained in a data source */
slouken@0
   813
int IMG_isBMP(SDL_RWops *src)
slouken@0
   814
{
slouken@0
   815
	return(0);
slouken@0
   816
}
slouken@0
   817
couriersud@189
   818
int IMG_isICO(SDL_RWops *src)
couriersud@189
   819
{
couriersud@189
   820
	return(0);
couriersud@189
   821
}
couriersud@189
   822
couriersud@189
   823
int IMG_isCUR(SDL_RWops *src)
couriersud@189
   824
{
couriersud@189
   825
	return(0);
couriersud@189
   826
}
couriersud@189
   827
slouken@0
   828
/* Load a BMP type image from an SDL datasource */
slouken@0
   829
SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
slouken@0
   830
{
slouken@0
   831
	return(NULL);
slouken@0
   832
}
slouken@0
   833
couriersud@189
   834
/* Load a BMP type image from an SDL datasource */
couriersud@189
   835
SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
couriersud@189
   836
{
couriersud@189
   837
	return(NULL);
couriersud@189
   838
}
couriersud@189
   839
couriersud@189
   840
/* Load a BMP type image from an SDL datasource */
couriersud@189
   841
SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
couriersud@189
   842
{
couriersud@189
   843
	return(NULL);
couriersud@189
   844
}
couriersud@189
   845
slouken@0
   846
#endif /* LOAD_BMP */
slouken@237
   847
slouken@237
   848
#endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */