/* XMMS - Cross-platform multimedia player * Copyright (C) 1998-1999 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include typedef struct tagRGBQUAD { guchar rgbBlue; guchar rgbGreen; guchar rgbRed; guchar rgbReserved; } RGBQUAD; #define BI_RGB 0L #define BI_RLE8 1L #define BI_RLE4 2L #define BI_BITFIELDS 3L static GdkGC *bmp_gc = NULL; static int read_le_short(FILE * file, gushort * ret) { guchar b[2]; if (fread(b, sizeof (guchar), 2, file) != 2) return 0; *ret = (b[1] << 8) | b[0]; return 1; } static int read_le_long(FILE * file, gulong * ret) { guchar b[4]; if (fread(b, sizeof (guchar), 4, file) != 4) return 0; *ret = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; return 1; } GdkPixmap *msa_read_bmp(GdkWindow * window, gchar * filename) { FILE *file; gchar type[2]; gulong size, offset, headSize, w, h, comp, imgsize, j, k, l; gushort tmpShort, planes, bitcount, ncols, skip; guchar *data, *data_end, byte, g, b, r, *buffer, *buffer_end; struct stat statbuf; register guchar *ptr, *buffer_ptr; register gulong i; register gushort x, y; RGBQUAD rgbQuads[256]; GdkPixmap *ret; if (stat(filename, &statbuf) == -1) return NULL; size = statbuf.st_size; file = fopen(filename, "rb"); if (!file) return NULL; if (fread(type, 1, 2, file) != 2) { fclose(file); return NULL; } if (strncmp(type, "BM", 2)) { fprintf(stderr, "Error in BMP file: wrong type\n"); fclose(file); return NULL; } fseek(file, 8, SEEK_CUR); read_le_long(file, &offset); read_le_long(file, &headSize); if (headSize == 12) /* BITMAPCOREINFO */ { read_le_short(file, &tmpShort); w = tmpShort; read_le_short(file, &tmpShort); h = tmpShort; read_le_short(file, &planes); read_le_short(file, &bitcount); imgsize = size - offset; comp = BI_RGB; } else if (headSize == 40) /* BITMAPINFO */ { read_le_long(file, &w); read_le_long(file, &h); read_le_short(file, &planes); read_le_short(file, &bitcount); read_le_long(file, &comp); read_le_long(file, &imgsize); imgsize = size - offset; fseek(file, 16, SEEK_CUR); } else { fprintf(stderr, "Error in BMP file: Unknown header size\n"); fclose(file); return NULL; } if (bitcount != 24) { ncols = (offset - headSize - 14); if (headSize == 12) { ncols /= 3; for (i = 0; i < ncols; i++) { fread(&rgbQuads[i], 3, 1, file); } } else { ncols /= 4; fread(rgbQuads, 4, ncols, file); } } fseek(file, offset, SEEK_SET); buffer = g_malloc(imgsize); fread(buffer, imgsize, 1, file); fclose(file); buffer_ptr = buffer; buffer_end = buffer + imgsize; data = (guchar *) g_malloc0((w * 3 * h) + 3); /* +3 is just for safety */ data_end = data + (w * 3 * h); if (!data) { fprintf(stderr, "Error: Couldn't alloc memory for RGB data\n"); fclose(file); return NULL; } ptr = data + ((h - 1) * w * 3); if (bitcount == 4) { if (comp == BI_RLE4) { x = 0; y = 0; for (i = 0, g = 1; i < imgsize && g && buffer_ptr < buffer_end; i++) { byte = *(buffer_ptr++); if (byte) { guchar t1, t2; l = byte; byte = *(buffer_ptr++); t1 = byte & 0xF; t2 = (byte >> 4) & 0xF; for (j = 0; j < l; j++) { k = (j & 1) ? t1 : t2; if (x >= w) break; *ptr++ = rgbQuads[k].rgbRed; *ptr++ = rgbQuads[k].rgbGreen; *ptr++ = rgbQuads[k].rgbBlue; x++; if (ptr > data_end) ptr = data_end; } } else { guchar t1, t2; byte = *(buffer_ptr++); switch (byte) { case 0: /* End of line */ x = 0; y++; ptr = data + ((h - y - 1) * w * 3) + (x * 3); if (ptr > data_end) ptr = data_end; break; case 1: /* End of bitmap */ g = 0; break; case 2: /* Delta */ x += *(buffer_ptr++); y += *(buffer_ptr++); ptr = data + ((h - y - 1) * w * 3) + (x * 3); if (ptr > data_end) ptr = data_end; break; default: /* * "Absolute mode" * non RLE encoded */ l = byte; for (j = 0; j < l; j++) { if ((j & 1) == 0) /* FIXME: triggers a compiler warning */ { byte = *(buffer_ptr++); t1 = byte & 0xF; t2 = (byte >> 4) & 0xF; } k = (j & 1) ? t1 : t2; if (x >= w) { buffer_ptr += (l - j) / 2; break; } *ptr++ = rgbQuads[k].rgbRed; *ptr++ = rgbQuads[k].rgbGreen; *ptr++ = rgbQuads[k].rgbBlue; x++; if (ptr > data_end) ptr = data_end; } /* Skip padding */ if ((l & 3) == 1) buffer_ptr += 2; else if ((l & 3) == 2) buffer_ptr++; break; } } } } else if (comp == BI_RGB) { skip = ((((w + 7) / 8) * 8) - w) / 2; for (y = 0; y < h; y++) { for (x = 0; x < w && buffer_ptr < buffer_end; x++) { if ((x & 1) == 0) /* FIXME: triggers a compiler warning */ { byte = *(buffer_ptr++); } k = (byte & 0xF0) >> 4; *ptr++ = rgbQuads[k].rgbRed; *ptr++ = rgbQuads[k].rgbGreen; *ptr++ = rgbQuads[k].rgbBlue; byte <<= 4; } buffer_ptr += skip; ptr -= w * 6; } } } else if (bitcount == 8) { if (comp == BI_RLE8) { x = 0; y = 0; for (i = 0, g = 1; i < imgsize && buffer_ptr < buffer_end && g; i++) { byte = *(buffer_ptr++); if (byte) { l = byte; byte = *(buffer_ptr++); for (j = 0; j < l; j++) { if (x >= w) break; *ptr++ = rgbQuads[byte].rgbRed; *ptr++ = rgbQuads[byte].rgbGreen; *ptr++ = rgbQuads[byte].rgbBlue; x++; if (ptr > data_end) ptr = data_end; } } else { byte = *(buffer_ptr++); switch (byte) { case 0: x = 0; y++; ptr = data + ((h - y - 1) * w * 3) + (x * 3); if (ptr > data_end) ptr = data_end; break; case 1: g = 0; break; case 2: x += *(buffer_ptr++); y += *(buffer_ptr++); ptr = data + ((h - y - 1) * w * 3) + (x * 3); if (ptr > data_end) ptr = data_end; break; default: l = byte; for (j = 0; j < l; j++) { byte = *(buffer_ptr++); if (x >= w) { buffer_ptr += l - j; break; } *ptr++ = rgbQuads[byte].rgbRed; *ptr++ = rgbQuads[byte].rgbGreen; *ptr++ = rgbQuads[byte].rgbBlue; x++; if (ptr > data_end) ptr = data_end; } if (l & 1) buffer_ptr++; break; } } } } else if (comp == BI_RGB) { skip = (((w + 3) / 4) * 4) - w; for (y = 0; y < h; y++) { for (x = 0; x < w && buffer_ptr < buffer_end; x++) { byte = *(buffer_ptr++); *ptr++ = rgbQuads[byte].rgbRed; *ptr++ = rgbQuads[byte].rgbGreen; *ptr++ = rgbQuads[byte].rgbBlue; } ptr -= w * 6; buffer_ptr += skip; } } } else if (bitcount == 16) { guint16 rgb16; skip = (4 - ((w * 2) % 4)) & 3; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { rgb16 = ((*(buffer_ptr + 1)) << 8) | (*buffer_ptr); buffer_ptr += 2; *ptr++ = (rgb16 & 0x7C00) >> 7; *ptr++ = (rgb16 & 0x03E0) >> 2; *ptr++ = (rgb16 & 0x001F) << 3; } ptr -= w * 6; buffer_ptr += skip; } } else if (bitcount == 24) { skip = (4 - ((w * 3) % 4)) & 3; for (y = 0; y < h; y++) { for (x = 0; x < w && buffer_ptr < buffer_end; x++) { b = *(buffer_ptr++); g = *(buffer_ptr++); r = *(buffer_ptr++); *ptr++ = r; *ptr++ = g; *ptr++ = b; } ptr -= w * 6; buffer_ptr += skip; } } ret = gdk_pixmap_new(window, w, h, gdk_rgb_get_visual()->depth); if (!bmp_gc) bmp_gc = gdk_gc_new(window); gdk_draw_rgb_image(ret, bmp_gc, 0, 0, w, h, GDK_RGB_DITHER_MAX, data, w * 3); g_free(data); g_free(buffer); return ret; }