From 4cae38328ccfd23deeb5c0f3dc74c1f52ec73252 Mon Sep 17 00:00:00 2001 From: Couriersud Date: Sun, 11 Jan 2009 23:56:19 +0000 Subject: [PATCH] Add SDL_LoadICO_RW to SDL. Loads best quality icon from *.ico file. --- include/SDL_surface.h | 6 + src/video/SDL_bmp.c | 282 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) diff --git a/include/SDL_surface.h b/include/SDL_surface.h index 3a4abba93..88a739ae6 100644 --- a/include/SDL_surface.h +++ b/include/SDL_surface.h @@ -157,6 +157,12 @@ extern DECLSPEC void SDLCALL SDL_UnlockSurface(SDL_Surface * surface); extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadBMP_RW(SDL_RWops * src, int freesrc); +/* + * Load Icon best quality icon from ICO file and return as RGBA surface + */ +extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadICO_RW(SDL_RWops * src, + int freesrc); + /* Convenience macro -- load a surface from a file */ #define SDL_LoadBMP(file) SDL_LoadBMP_RW(SDL_RWFromFile(file, "rb"), 1) diff --git a/src/video/SDL_bmp.c b/src/video/SDL_bmp.c index c09c46a0d..5566bc81a 100644 --- a/src/video/SDL_bmp.c +++ b/src/video/SDL_bmp.c @@ -46,6 +46,288 @@ #endif +static Uint8 +SDL_Read8(SDL_RWops * src) +{ + Uint8 value; + + SDL_RWread(src, &value, 1, 1); + return (value); +} + +SDL_Surface * +SDL_LoadICO_RW(SDL_RWops * src, int freesrc) +{ + int was_error; + long fp_offset; + int bmpPitch; + int i, pad; + SDL_Surface *surface; + Uint32 Rmask; + Uint32 Gmask; + Uint32 Bmask; + Uint8 *bits; + int ExpandBMP; + int maxCol = 0; + int icoOfs = 0; + Uint32 palette[256]; + + /* The Win32 ICO file header (14 bytes) */ + Uint16 bfReserved; + Uint16 bfType; + Uint16 bfCount; + + /* The Win32 BITMAPINFOHEADER struct (40 bytes) */ + Uint32 biSize; + Sint32 biWidth; + Sint32 biHeight; + Uint16 biPlanes; + Uint16 biBitCount; + Uint32 biCompression; + Uint32 biSizeImage; + Sint32 biXPelsPerMeter; + Sint32 biYPelsPerMeter; + Uint32 biClrUsed; + Uint32 biClrImportant; + + /* Make sure we are passed a valid data source */ + surface = NULL; + was_error = 0; + if (src == NULL) { + was_error = 1; + goto done; + } + + /* Read in the ICO file header */ + fp_offset = SDL_RWtell(src); + SDL_ClearError(); + + bfReserved = SDL_ReadLE16(src); + bfType = SDL_ReadLE16(src); + bfCount = SDL_ReadLE16(src); + if ((bfType != 1 && bfType != 2) || (bfCount == 0)) { + SDL_SetError("File is not a Windows ICO file"); + was_error = 1; + goto done; + } + + /* Read the Win32 Icon Directory */ + for (i = 0; i < bfCount; i++) { + /* Icon Directory Entries */ + int bWidth = SDL_Read8(src); /* Uint8, but 0 = 256 ! */ + int bHeight = SDL_Read8(src); /* Uint8, but 0 = 256 ! */ + int bColorCount = SDL_Read8(src); /* Uint8, but 0 = 256 ! */ + Uint8 bReserved = SDL_Read8(src); + Uint16 wPlanes = SDL_ReadLE16(src); + Uint16 wBitCount = SDL_ReadLE16(src); + Uint32 dwBytesInRes = SDL_ReadLE32(src); + Uint32 dwImageOffset = SDL_ReadLE32(src); + + if (!bWidth) + bWidth = 256; + if (!bHeight) + bHeight = 256; + if (!bColorCount) + bColorCount = 256; + + //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset); + if (bColorCount > maxCol) { + maxCol = bColorCount; + icoOfs = dwImageOffset; + //printf("marked\n"); + } + } + + /* Advance to the DIB Data */ + if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) { + SDL_Error(SDL_EFSEEK); + was_error = 1; + goto done; + } + + /* Read the Win32 BITMAPINFOHEADER */ + biSize = SDL_ReadLE32(src); + if (biSize == 40) { + biWidth = SDL_ReadLE32(src); + biHeight = SDL_ReadLE32(src); + biPlanes = SDL_ReadLE16(src); + biBitCount = SDL_ReadLE16(src); + biCompression = SDL_ReadLE32(src); + biSizeImage = SDL_ReadLE32(src); + biXPelsPerMeter = SDL_ReadLE32(src); + biYPelsPerMeter = SDL_ReadLE32(src); + biClrUsed = SDL_ReadLE32(src); + biClrImportant = SDL_ReadLE32(src); + } else { + SDL_SetError("Unsupported ICO bitmap format"); + was_error = 1; + goto done; + } + + /* Check for read error */ + if (SDL_strcmp(SDL_GetError(), "") != 0) { + was_error = 1; + goto done; + } + + /* We don't support any BMP compression right now */ + switch (biCompression) { + case BI_RGB: + /* Default values for the BMP format */ + switch (biBitCount) { + case 1: + case 4: + ExpandBMP = biBitCount; + biBitCount = 8; + break; + case 8: + ExpandBMP = 8; + break; + case 32: + Rmask = 0x00FF0000; + Gmask = 0x0000FF00; + Bmask = 0x000000FF; + ExpandBMP = 0; + break; + default: + SDL_SetError("ICO file with unsupported bit count"); + was_error = 1; + goto done; + } + break; + default: + SDL_SetError("Compressed ICO files not supported"); + was_error = 1; + goto done; + } + + /* Create a RGBA surface */ + biHeight = biHeight >> 1; + //printf("%d x %d\n", biWidth, biHeight); + surface = + SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000, + 0x0000FF00, 0x000000FF, 0xFF000000); + if (surface == NULL) { + was_error = 1; + goto done; + } + + /* Load the palette, if any */ + //printf("bc %d bused %d\n", biBitCount, biClrUsed); + if (biBitCount <= 8) { + if (biClrUsed == 0) { + biClrUsed = 1 << biBitCount; + } + for (i = 0; i < (int) biClrUsed; ++i) { + SDL_RWread(src, &palette[i], 4, 1); + } + } + + /* Read the surface pixels. Note that the bmp image is upside down */ + bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch); + switch (ExpandBMP) { + case 1: + bmpPitch = (biWidth + 7) >> 3; + pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); + break; + case 4: + bmpPitch = (biWidth + 1) >> 1; + pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); + break; + case 8: + bmpPitch = biWidth; + pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); + break; + default: + bmpPitch = biWidth * 4; + pad = 0; + break; + } + while (bits > (Uint8 *) surface->pixels) { + bits -= surface->pitch; + switch (ExpandBMP) { + case 1: + case 4: + case 8: + { + Uint8 pixel = 0; + int shift = (8 - ExpandBMP); + for (i = 0; i < surface->w; ++i) { + if (i % (8 / ExpandBMP) == 0) { + if (!SDL_RWread(src, &pixel, 1, 1)) { + SDL_SetError("Error reading from ICO"); + was_error = 1; + goto done; + } + } + *((Uint32 *) bits + i) = (palette[pixel >> shift]); + pixel <<= ExpandBMP; + } + } + break; + + default: + if (SDL_RWread(src, bits, 1, surface->pitch) + != surface->pitch) { + SDL_Error(SDL_EFREAD); + was_error = 1; + goto done; + } + break; + } + /* Skip padding bytes, ugh */ + if (pad) { + Uint8 padbyte; + for (i = 0; i < pad; ++i) { + SDL_RWread(src, &padbyte, 1, 1); + } + } + } + /* Read the mask pixels. Note that the bmp image is upside down */ + bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch); + ExpandBMP = 1; + bmpPitch = (biWidth + 7) >> 3; + pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); + while (bits > (Uint8 *) surface->pixels) { + Uint8 pixel = 0; + int shift = (8 - ExpandBMP); + + bits -= surface->pitch; + for (i = 0; i < surface->w; ++i) { + if (i % (8 / ExpandBMP) == 0) { + if (!SDL_RWread(src, &pixel, 1, 1)) { + SDL_SetError("Error reading from ICO"); + was_error = 1; + goto done; + } + } + *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000); + pixel <<= ExpandBMP; + } + /* Skip padding bytes, ugh */ + if (pad) { + Uint8 padbyte; + for (i = 0; i < pad; ++i) { + SDL_RWread(src, &padbyte, 1, 1); + } + } + } + done: + if (was_error) { + if (src) { + SDL_RWseek(src, fp_offset, RW_SEEK_SET); + } + if (surface) { + SDL_FreeSurface(surface); + } + surface = NULL; + } + if (freesrc && src) { + SDL_RWclose(src); + } + return (surface); +} + SDL_Surface * SDL_LoadBMP_RW(SDL_RWops * src, int freesrc) {