/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * TGA reader. * * By Tim Gunn. * * RLE support added by Michal Mertl and Salvador Eduardo Tropea. * * Palette reading improved by Peter Wang. * * Big-endian support added by Eric Botcazou. * * See readme.txt for copyright information. */ #include "allegro.h" #include "allegro/internal/aintern.h" /* raw_tga_read8: * Helper for reading 256-color raw data from TGA files. */ static INLINE unsigned char *raw_tga_read8(unsigned char *b, int w, PACKFILE *f) { return b + pack_fread(b, w, f); } /* rle_tga_read8: * Helper for reading 256-color RLE data from TGA files. */ static void rle_tga_read8(unsigned char *b, int w, PACKFILE *f) { int value, count, c = 0; do { count = pack_getc(f); if (count & 0x80) { /* run-length packet */ count = (count & 0x7F) + 1; c += count; value = pack_getc(f); while (count--) *b++ = value; } else { /* raw packet */ count++; c += count; b = raw_tga_read8(b, count, f); } } while (c < w); } /* single_tga_read32: * Helper for reading a single 32-bit data from TGA files. */ static INLINE int single_tga_read32(PACKFILE *f) { RGB value; int alpha; value.b = pack_getc(f); value.g = pack_getc(f); value.r = pack_getc(f); alpha = pack_getc(f); return makeacol32(value.r, value.g, value.b, alpha); } /* raw_tga_read32: * Helper for reading 32-bit raw data from TGA files. */ static unsigned int *raw_tga_read32(unsigned int *b, int w, PACKFILE *f) { while (w--) *b++ = single_tga_read32(f); return b; } /* rle_tga_read32: * Helper for reading 32-bit RLE data from TGA files. */ static void rle_tga_read32(unsigned int *b, int w, PACKFILE *f) { int color, count, c = 0; do { count = pack_getc(f); if (count & 0x80) { /* run-length packet */ count = (count & 0x7F) + 1; c += count; color = single_tga_read32(f); while (count--) *b++ = color; } else { /* raw packet */ count++; c += count; b = raw_tga_read32(b, count, f); } } while (c < w); } /* single_tga_read24: * Helper for reading a single 24-bit data from TGA files. */ static INLINE int single_tga_read24(PACKFILE *f) { RGB value; value.b = pack_getc(f); value.g = pack_getc(f); value.r = pack_getc(f); return makecol24(value.r, value.g, value.b); } /* raw_tga_read24: * Helper for reading 24-bit raw data from TGA files. */ static unsigned char *raw_tga_read24(unsigned char *b, int w, PACKFILE *f) { int color; while (w--) { color = single_tga_read24(f); WRITE3BYTES(b, color); b += 3; } return b; } /* rle_tga_read24: * Helper for reading 24-bit RLE data from TGA files. */ static void rle_tga_read24(unsigned char *b, int w, PACKFILE *f) { int color, count, c = 0; do { count = pack_getc(f); if (count & 0x80) { /* run-length packet */ count = (count & 0x7F) + 1; c += count; color = single_tga_read24(f); while (count--) { WRITE3BYTES(b, color); b += 3; } } else { /* raw packet */ count++; c += count; b = raw_tga_read24(b, count, f); } } while (c < w); } /* single_tga_read16: * Helper for reading a single 16-bit data from TGA files. */ static INLINE int single_tga_read16(PACKFILE *f) { int value; value = pack_igetw(f); return (((value >> 10) & 0x1F) << _rgb_r_shift_15) | (((value >> 5) & 0x1F) << _rgb_g_shift_15) | ((value & 0x1F) << _rgb_b_shift_15); } /* raw_tga_read16: * Helper for reading 16-bit raw data from TGA files. */ static unsigned short *raw_tga_read16(unsigned short *b, int w, PACKFILE *f) { while (w--) *b++ = single_tga_read16(f); return b; } /* rle_tga_read16: * Helper for reading 16-bit RLE data from TGA files. */ static void rle_tga_read16(unsigned short *b, int w, PACKFILE *f) { int color, count, c = 0; do { count = pack_getc(f); if (count & 0x80) { /* run-length packet */ count = (count & 0x7F) + 1; c += count; color = single_tga_read16(f); while (count--) *b++ = color; } else { /* raw packet */ count++; c += count; b = raw_tga_read16(b, count, f); } } while (c < w); } /* load_tga: * Loads a TGA file, returning a bitmap structure and storing the * palette data in the specified palette (this should be an array * of at least 256 RGB structures). */ BITMAP *load_tga(AL_CONST char *filename, RGB *pal) { PACKFILE *f; BITMAP *bmp; ASSERT(filename); f = pack_fopen(filename, F_READ); if (!f) return NULL; bmp = load_tga_pf(f, pal); pack_fclose(f); return bmp; } /* load_tga_pf: * Like load_tga, but starts loading from the current place in the PACKFILE * specified. If successful the offset into the file will be left just after * the image data. If unsuccessful the offset into the file is unspecified, * i.e. you must either reset the offset to some known place or close the * packfile. The packfile is not closed by this function. */ BITMAP *load_tga_pf(PACKFILE *f, RGB *pal) { unsigned char image_id[256], image_palette[256][3]; unsigned char id_length, palette_type, image_type, palette_entry_size; unsigned char bpp, descriptor_bits; short unsigned int first_color, palette_colors; short unsigned int left, top, image_width, image_height; unsigned int c, i, y, yc; int dest_depth; int compressed; BITMAP *bmp; PALETTE tmppal; int want_palette = TRUE; ASSERT(f); /* we really need a palette */ if (!pal) { want_palette = FALSE; pal = tmppal; } id_length = pack_getc(f); palette_type = pack_getc(f); image_type = pack_getc(f); first_color = pack_igetw(f); palette_colors = pack_igetw(f); palette_entry_size = pack_getc(f); left = pack_igetw(f); top = pack_igetw(f); image_width = pack_igetw(f); image_height = pack_igetw(f); bpp = pack_getc(f); descriptor_bits = pack_getc(f); pack_fread(image_id, id_length, f); if (palette_type == 1) { for (i = 0; i < palette_colors; i++) { switch (palette_entry_size) { case 16: c = pack_igetw(f); image_palette[i][0] = (c & 0x1F) << 3; image_palette[i][1] = ((c >> 5) & 0x1F) << 3; image_palette[i][2] = ((c >> 10) & 0x1F) << 3; break; case 24: case 32: image_palette[i][0] = pack_getc(f); image_palette[i][1] = pack_getc(f); image_palette[i][2] = pack_getc(f); if (palette_entry_size == 32) pack_getc(f); break; } } } else if (palette_type != 0) { return NULL; } /* Image type: * 0 = no image data * 1 = uncompressed color mapped * 2 = uncompressed true color * 3 = grayscale * 9 = RLE color mapped * 10 = RLE true color * 11 = RLE grayscale */ compressed = (image_type & 8); image_type &= 7; if ((image_type < 1) || (image_type > 3)) { return NULL; } switch (image_type) { case 1: /* paletted image */ if ((palette_type != 1) || (bpp != 8)) { return NULL; } for(i=0; i> 2; pal[i].g = image_palette[i][1] >> 2; pal[i].b = image_palette[i][0] >> 2; } dest_depth = _color_load_depth(8, FALSE); break; case 2: /* truecolor image */ if ((palette_type == 0) && ((bpp == 15) || (bpp == 16))) { bpp = 15; dest_depth = _color_load_depth(15, FALSE); } else if ((palette_type == 0) && ((bpp == 24) || (bpp == 32))) { dest_depth = _color_load_depth(bpp, (bpp == 32)); } else { return NULL; } break; case 3: /* grayscale image */ if ((palette_type != 0) || (bpp != 8)) { return NULL; } for (i=0; i<256; i++) { pal[i].r = i>>2; pal[i].g = i>>2; pal[i].b = i>>2; } dest_depth = _color_load_depth(bpp, FALSE); break; default: return NULL; } bmp = create_bitmap_ex(bpp, image_width, image_height); if (!bmp) { return NULL; } *allegro_errno = 0; for (y=image_height; y; y--) { yc = (descriptor_bits & 0x20) ? image_height-y : y-1; switch (image_type) { case 1: case 3: if (compressed) rle_tga_read8(bmp->line[yc], image_width, f); else raw_tga_read8(bmp->line[yc], image_width, f); break; case 2: if (bpp == 32) { if (compressed) rle_tga_read32((unsigned int *)bmp->line[yc], image_width, f); else raw_tga_read32((unsigned int *)bmp->line[yc], image_width, f); } else if (bpp == 24) { if (compressed) rle_tga_read24(bmp->line[yc], image_width, f); else raw_tga_read24(bmp->line[yc], image_width, f); } else { if (compressed) rle_tga_read16((unsigned short *)bmp->line[yc], image_width, f); else raw_tga_read16((unsigned short *)bmp->line[yc], image_width, f); } break; } } if (*allegro_errno) { destroy_bitmap(bmp); return NULL; } if (dest_depth != bpp) { /* restore original palette except if it comes from the bitmap */ if ((bpp != 8) && (!want_palette)) pal = NULL; bmp = _fixup_loaded_bitmap(bmp, pal, dest_depth); } /* construct a fake palette if 8-bit mode is not involved */ if ((bpp != 8) && (dest_depth != 8) && want_palette) generate_332_palette(pal); return bmp; } /* bitmap_has_alpha: * Checks whether this bitmap has an alpha channel. */ static int bitmap_has_alpha(BITMAP *bmp) { int x, y, c; if (bitmap_color_depth(bmp) != 32) return FALSE; for (y = 0; y < bmp->h; y++) { for (x = 0; x < bmp->w; x++) { c = getpixel(bmp, x, y); if (geta32(c)) return TRUE; } } return FALSE; } /* save_tga: * Writes a bitmap into a TGA file, using the specified palette (this * should be an array of at least 256 RGB structures). */ int save_tga(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal) { PACKFILE *f; int ret; ASSERT(filename); f = pack_fopen(filename, F_WRITE); if (!f) return -1; ret = save_tga_pf(f, bmp, pal); pack_fclose(f); return ret; } /* save_tga_pf: * Like save_tga but writes into the PACKFILE given instead of a new file. * The packfile is not closed after writing is completed. On success the * offset into the file is left after the TGA file just written. On failure * the offset is left at the end of whatever incomplete data was written. */ int save_tga_pf(PACKFILE *f, BITMAP *bmp, AL_CONST RGB *pal) { unsigned char image_palette[256][3]; int x, y, c, r, g, b; int depth; PALETTE tmppal; ASSERT(f); ASSERT(bmp); if (!pal) { get_palette(tmppal); pal = tmppal; } depth = bitmap_color_depth(bmp); if (depth == 15) depth = 16; *allegro_errno = 0; pack_putc(0, f); /* id length (no id saved) */ pack_putc((depth == 8) ? 1 : 0, f); /* palette type */ pack_putc((depth == 8) ? 1 : 2, f); /* image type */ pack_iputw(0, f); /* first colour */ pack_iputw((depth == 8) ? 256 : 0, f); /* number of colours */ pack_putc((depth == 8) ? 24 : 0, f); /* palette entry size */ pack_iputw(0, f); /* left */ pack_iputw(0, f); /* top */ pack_iputw(bmp->w, f); /* width */ pack_iputw(bmp->h, f); /* height */ pack_putc(depth, f); /* bits per pixel */ pack_putc(bitmap_has_alpha (bmp) ? 8 : 0, f); /* descriptor (bottom to top, 8-bit alpha) */ if (depth == 8) { for (y=0; y<256; y++) { image_palette[y][2] = _rgb_scale_6[pal[y].r]; image_palette[y][1] = _rgb_scale_6[pal[y].g]; image_palette[y][0] = _rgb_scale_6[pal[y].b]; } pack_fwrite(image_palette, 768, f); } switch (bitmap_color_depth(bmp)) { #ifdef ALLEGRO_COLOR8 case 8: for (y=bmp->h; y; y--) for (x=0; xw; x++) pack_putc(getpixel(bmp, x, y-1), f); break; #endif #ifdef ALLEGRO_COLOR16 case 15: for (y=bmp->h; y; y--) { for (x=0; xw; x++) { c = getpixel(bmp, x, y-1); r = getr15(c); g = getg15(c); b = getb15(c); c = ((r<<7)&0x7C00) | ((g<<2)&0x3E0) | ((b>>3)&0x1F); pack_iputw(c, f); } } break; case 16: for (y=bmp->h; y; y--) { for (x=0; xw; x++) { c = getpixel(bmp, x, y-1); r = getr16(c); g = getg16(c); b = getb16(c); c = ((r<<7)&0x7C00) | ((g<<2)&0x3E0) | ((b>>3)&0x1F); pack_iputw(c, f); } } break; #endif #ifdef ALLEGRO_COLOR24 case 24: for (y=bmp->h; y; y--) { for (x=0; xw; x++) { c = getpixel(bmp, x, y-1); pack_putc(getb24(c), f); pack_putc(getg24(c), f); pack_putc(getr24(c), f); } } break; #endif #ifdef ALLEGRO_COLOR32 case 32: for (y=bmp->h; y; y--) { for (x=0; xw; x++) { c = getpixel(bmp, x, y-1); pack_putc(getb32(c), f); pack_putc(getg32(c), f); pack_putc(getr32(c), f); pack_putc(geta32(c), f); } } break; #endif } if (*allegro_errno) return -1; else return 0; }