/* Current File: icoconvert.c * * Chris Moates 04/12/2003 * six@mox.net * * Please note that at this point, there's very little left of the original icod.c. Don't ask the original author (Simon) for help, please. * * AS OF VERSION 2.0, ALL CODE FOR XPM RENDERING HAS BEEN REMOVED! */ /* File : icod.c * Simon Drabble 02/05/99 madlather@syspac.com * (C) 1998 - 1999 Simon Drabble * This program and its associated code are released under the GNU Public Licence (GPL). This basically states you are free to modify and distribute the software provided you do not prevent anyone else doing the same. For a full copy of the licence, see www.gnu.org/copyleft/gpl.html * */ #include #include #include #include #include "global.h" #include "icoconvert.h" char *global_file_name = NULL; #define USAGE "Usage: icoconvert file1.ico [file2.ico .. filen.ico]" int main(int argc, char *argv[]) { int i; if(argc == 1) { printf("%s\n", USAGE); exit(0); } for(i = 1; i < argc; ++i) load_icon(argv[i]); return 0; } int load_icon(char *fname) { FILE *iconfile; if(!(iconfile = fopen(fname, "r b"))) return -1; free(global_file_name); CREATE(global_file_name, char, strlen(fname) + 1); strcpy(global_file_name, fname); if((strrchr(global_file_name, '.'))) *(strrchr(global_file_name, '.')) = '\0'; load_icon_file(iconfile); return 0; } int load_icon_file(FILE *iconfile) { ICONDIR *idir; int i, tot = 0; CREATE(idir, ICONDIR, 1); memset(idir, 0, sizeof(ICONDIR)); tot += fread(&idir->idReserved, sizeof(WORD), 1, iconfile); tot += fread(&idir->idType, sizeof(WORD), 1, iconfile); tot += fread(&idir->idCount, sizeof(WORD), 1, iconfile); tot *= sizeof(WORD); CREATE(idir->idEntries, ICONDIRENTRY, idir->idCount); tot = fread(idir->idEntries, sizeof(ICONDIRENTRY), idir->idCount, iconfile); printf("%s: %d icons.\n", global_file_name, idir->idCount); for(i = 0; i < idir->idCount; ++i) load_icon_image(iconfile, i, idir); return 0; } int load_icon_image(FILE *inf, int i, ICONDIR *idir) { ICONIMAGE *ii; int tot = 0; size_t numcolors; size_t c; CREATE(ii, ICONIMAGE, 1); memset(ii, 0, sizeof(*ii)); /* Am I really doing this? */ fseek(inf, idir->idEntries[i].dwImageOffset, SEEK_SET); tot = fread(&ii->icHeader, sizeof(BITMAPINFOHEADER), 1, inf); printf("Found icon: %2ldx%2ld %2ldbpp Sz:%ld (", ii->icHeader.biWidth, ii->icHeader.biHeight / 2, (long) ii->icHeader.biBitCount, (long) ii->icHeader.biSizeImage); if(ii->icHeader.biBitCount == 32) { // Allocate 4 bytes per pixel (RGBA) ii->icColours = NULL; c =(ii->icHeader.biWidth * ii->icHeader.biHeight / 2) * 4; } else if(ii->icHeader.biBitCount == 24) { // Allocate 3 bytes per pixel (RGB) ii->icColours = NULL; c =(ii->icHeader.biWidth * ii->icHeader.biHeight / 2) * 3; } else { // Create the icColours palette and read the data into the palette numcolors = 1 << ii->icHeader.biBitCount; CREATE(ii->icColours, RGBQUAD, numcolors); tot = fread(ii->icColours, sizeof(RGBQUAD), numcolors, inf); c =(ii->icHeader.biWidth * ii->icHeader.biHeight / 2) / (8 / ii->icHeader.biBitCount); } // Create the XOR (data) storage CREATE(ii->icXOR, BYTE, c); tot = fread(ii->icXOR, sizeof(BYTE), c, inf); // Create the AND (transparency mask) storage c = ii->icHeader.biWidth * ii->icHeader.biHeight/8; CREATE(ii->icAND, BYTE, c); tot = fread(ii->icAND, sizeof(BYTE), c, inf); #ifdef DEBUG // Print the transparency mask dump_andmask(ii); #endif if(ii->icHeader.biBitCount == 32) { // New WinXP True Color icon with 8 bit transparency process_32bit(ii); } else if(ii->icHeader.biBitCount == 24) { // True Color icon process_24bit(ii); } else { // Indexed Color icon ico2png(ii); } // Clean up free(ii->icAND); free(ii->icXOR); free(ii->icColours); free(ii); return 0; } // This function processes 24 bit icons. They are true color, but have only AND mask transparency. int process_24bit(ICONIMAGE *ii) { register int i, j, offset; int color[2]; gdImagePtr im; RGBQUAD rgb; im = gdImageCreateTrueColor(ii->icHeader.biWidth, ii->icHeader.biHeight/2); color[0]=gdImageColorResolve(im, 255,0,255); // Make a really ugly purple background color for transparency gdImageColorTransparent(im, color[0]); // Set the ugly purple to be the transparent color // Render the pixels into the gdImage for (i = 0; i < ii->icHeader.biHeight/2; ++i) { for (j = 0; j < ii->icHeader.biWidth*3; j+=3) { offset = i * ii->icHeader.biWidth*3 + j; rgb.blue = *(ii->icXOR + offset); rgb.green = *(ii->icXOR + offset + 1); rgb.red = *(ii->icXOR + offset + 2); // If the AND mask shows the pixel as being transparent, ignore the color data and render color[0] (transparent) if(check_transparency(i, j/3, ii)){ gdImageSetPixel(im, j/3, (ii->icHeader.biHeight/2)-i-1, color[0]); } else { // Otherwise, render the appropriate colored pixel color[1]=gdImageColorResolve(im, rgb.red, rgb.green, rgb.blue); gdImageSetPixel(im, j/3, (ii->icHeader.biHeight/2)-i-1, color[1]); } } } // Write to disk write_png(ii, im); return 0; } // This function processes 32 bit icons, new to WinXP. They are true color, with an 8 bit alpha channel. Additionally, they still // have an AND mask for 1 bit transparency, but this is ignored. int process_32bit(ICONIMAGE *ii) { register int row, j, offset, color; gdImagePtr im; RGBQUAD rgba; im = gdImageCreateTrueColor(ii->icHeader.biWidth, ii->icHeader.biHeight/2); // Create the gd Image in memory gdImageSaveAlpha(im, 1); // Enable saving of the alpha channel to the file gdImageAlphaBlending(im,0); // Disable realtime blending of alpha information // Render the pixels into the Image for (row = 0; row < ii->icHeader.biHeight/2; ++row) { for (j = 0; j < ii->icHeader.biWidth*4; j+=4) { offset = row * ii->icHeader.biWidth*4 + j; rgba.blue = *(ii->icXOR + offset + 0); rgba.green = *(ii->icXOR + offset + 1); rgba.red = *(ii->icXOR + offset + 2); rgba.alpha = *(ii->icXOR + offset + 3); color=gdImageColorAllocateAlpha(im, rgba.red, rgba.green, rgba.blue, 127-rgba.alpha/2); gdImageSetPixel(im, j/4, (ii->icHeader.biHeight/2)-row-1, color); } } write_png(ii, im); return 0; } // Convert an icon < 24bpp to png int ico2png(ICONIMAGE *ii) { register int i, j, depth, l, n, pixelsperbyte, numcolors; int color[65535]; BYTE byte; gdImagePtr im; im = gdImageCreate(ii->icHeader.biWidth, ii->icHeader.biHeight/2); depth = ii->icHeader.biBitCount; pixelsperbyte = 8 / depth; numcolors = (1 << depth) - 1; // Allocate the palette in the gdImage for(i=1; i<=numcolors;i++) { color[i]=gdImageColorResolve(im, ii->icColours[i].red, ii->icColours[i].green, ii->icColours[i].blue); #ifdef DEBUG printf("color %d: %d, %d, %d\n", i, ii->icColours[i].red, ii->icColours[i].green, ii->icColours[i].blue); #endif } gdImageColorTransparent(im, color[1]); // Set color[1] to be the transparent color. for(i = 0; i < ii->icHeader.biHeight/2; ++i) { for(j = 0; j < ii->icHeader.biWidth; ++j) { int offset =(i * ii->icHeader.biWidth + j); /* This is where we use the bit-depth info */ byte = *(ii->icXOR + offset / pixelsperbyte); for(l = 8 - depth, n = 0; l >= 0; l -= depth, ++n) { if(check_transparency(i,j,ii)) { gdImageSetPixel(im, j, (ii->icHeader.biHeight/2)-i-1, color[0]); } else { gdImageSetPixel(im, j, (ii->icHeader.biHeight/2)-i-1, color[(byte >> l) & numcolors]); } } } } write_png(ii, im); return 0; } // Write a gdImage to disk as a PNG int write_png(ICONIMAGE *ii, gdImage *im) { FILE *pngout; char *pngfname; CREATE(pngfname, char, strlen(global_file_name) + 64); sprintf(pngfname, "%s_%02dX%02d_%dbpp.png", global_file_name, (int) ii->icHeader.biWidth, (int) ii->icHeader.biHeight/2, ii->icHeader.biBitCount); pngout=fopen(pngfname, "wb"); gdImagePng(im, pngout); fclose(pngout); gdImageDestroy(im); // Finish up the screen output printf("%s)\n", pngfname); return 0; } // Check any pixel for transparency in the 1-bit transparency mask (NOT the 8 bit alpha channel for 32bpp icons!) int check_transparency(int y, int x, ICONIMAGE *ii) { int pos = 0; int offset = 0; int formula = 0; int padwidth; padwidth=ii->icHeader.biWidth+ii->icHeader.biWidth%32; formula =(y * padwidth + x) ; // Invert the coords since icAND is top->bottom and icXOR is bottom->top pos = formula / 8; offset = 7 - formula % 8; if(ii->icAND[pos] &(1 << offset)) { return 1; } return 0; } #ifdef DEBUG // Print the AND mask to the screen. Sanity checking. int dump_andmask(ICONIMAGE *ii) { register int i, j, k; int base = 0; int height, padwidth; height=ii->icHeader.biHeight/2; padwidth=ii->icHeader.biWidth+ii->icHeader.biWidth%32; for(i = height-1; i >= 0; --i) { for(j = 0; j < ii->icHeader.biWidth; j += 8) { for(k = 7; k >= 0; --k) { base = *(ii->icAND + ((i * padwidth + j) / 8)) &(1 << k); fprintf(stderr, "%c", base ? '_' : 'X'); } } fputc('\n', stderr); } fputc('\n', stderr); return 0; } #endif