/* vim:expandtab:ts=2 sw=2: */ /* Grafx2 - The Ultimate 256-color bitmap paint program Copyright 2018 Thomas Bernard Copyright 2011 Pawel Góralski Copyright 2009 Petter Lindquist Copyright 2008 Yves Rizoud Copyright 2008 Franck Charlet Copyright 2007 Adrien Destugues Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) Grafx2 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; version 2 of the License. Grafx2 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 Grafx2; if not, see */ ///@file ifformat.c /// Saving and loading IFF picture formats. #include #include #include "fileformats.h" #include "loadsavefuncs.h" #include "io.h" #include "misc.h" #include "packbits.h" #include "gfx2mem.h" #include "gfx2log.h" #if defined(WIN32) #if defined(_MSC_VER) #define WIN32_BSWAP32 _byteswap_ulong #else #define WIN32_BSWAP32 __builtin_bswap32 #endif #endif #ifdef __APPLE__ #include #ifndef be32toh #define be32toh(x) OSSwapBigToHostInt32(x) #endif #endif //////////////////////////////////// IFF //////////////////////////////////// /** @defgroup IFF Interchange File Format * @ingroup loadsaveformats * Interchange File Format (Electronic Arts) * * This is the "native" format of Amiga. * We support ILBM/PBM/ACBM * @{ */ typedef struct { word Width; word Height; word X_org; // Inutile word Y_org; // Inutile byte BitPlanes; byte Mask; // 0=none, 1=mask, 2=transp color, 3=Lasso byte Compression; // 0=none, 1=packbits, 2=vertical RLE byte Pad1; // Inutile word Transp_col; // transparent color for masking mode 2 byte X_aspect; // Inutile byte Y_aspect; // Inutile word X_screen; word Y_screen; } T_IFF_Header; typedef struct { byte operation; // 0=normal body, 1=XOR, 2=Long Delta, 3=Short Delta, // 4=Generalized Long/Short Delta, 5=Byte Vertical Delta, // 7=short/long vertical delta, 74=Eric Graham's compression byte mask; // for XOR mode only word w,h; // for XOR mode only word x,y; // for XOR mode only dword abstime; dword reltime; byte interleave; byte pad0; dword bits; } T_IFF_AnimHeader; /// Test if a file is in IFF format void Test_IFF(FILE * IFF_file, const char *sub_type) { char format[4]; char section[4]; dword dummy; File_error=1; if (! Read_bytes(IFF_file,section,4)) return; if (memcmp(section,"FORM",4)) return; if (! Read_dword_be(IFF_file, &dummy)) return; // On aurait pu vérifier que ce long est égal à la taille // du fichier - 8, mais ça aurait interdit de charger des // fichiers tronqués (et déjà que c'est chiant de perdre // une partie du fichier il faut quand même pouvoir en // garder un peu... Sinon, moi je pleure :'( !!! ) if (! Read_bytes(IFF_file,format,4)) return; if (!memcmp(format,"ANIM",4)) { dword size; // An ANIM header: need to check that it encloses an image do { if (! Read_bytes(IFF_file,section,4)) return; if (memcmp(section,"FORM",4)) return; if (! Read_dword_be(IFF_file, &size)) return; if (! Read_bytes(IFF_file,format,4)) return; if (fseek(IFF_file, size - 4, SEEK_CUR) < 0) return; } while(0 == memcmp(format, "8SVX", 4)); // skip sound frames } else if(memcmp(format,"DPST",4) == 0) { if (! Read_bytes(IFF_file,section,4)) return; if (memcmp(section, "DPAH", 4) != 0) return; if (! Read_dword_be(IFF_file, &dummy)) return; fseek(IFF_file, dummy, SEEK_CUR); if (! Read_bytes(IFF_file,section,4)) return; if (memcmp(section,"FORM",4)) return; if (! Read_dword_be(IFF_file, &dummy)) return; if (! Read_bytes(IFF_file,format,4)) return; } if ( memcmp(format,sub_type,4)) return; // If we reach this part, file is indeed ILBM/PBM or ANIM File_error=0; } void Test_PBM(T_IO_Context * context, FILE * f) { (void)context; Test_IFF(f, "PBM "); } void Test_LBM(T_IO_Context * context, FILE * f) { (void)context; Test_IFF(f, "ILBM"); } void Test_ACBM(T_IO_Context * context, FILE * f) { (void)context; Test_IFF(f, "ACBM"); } // -- Lire un fichier au format IFF ----------------------------------------- typedef struct T_IFF_PCHG_Palette { struct T_IFF_PCHG_Palette * Next; short StartLine; T_Components Palette[1]; } T_IFF_PCHG_Palette; /// Skips the current section in an IFF file. /// This function should be called while the file pointer is right /// after the 4-character code that identifies the section. static int IFF_Skip_section(FILE * file) { dword size; if (!Read_dword_be(file,&size)) return 0; if (size&1) size++; if (fseek(file,size,SEEK_CUR)) return 0; return 1; } /// Wait for a specific IFF chunk static byte IFF_Wait_for(FILE * file, const char * expected_section) { // Valeur retournée: 1=Section trouvée, 0=Section non trouvée (erreur) byte section_read[4]; if (! Read_bytes(file,section_read,4)) return 0; while (memcmp(section_read,expected_section,4)) // Sect. pas encore trouvée { if (!IFF_Skip_section(file)) return 0; if (! Read_bytes(file,section_read,4)) return 0; } return 1; } // Les images ILBM sont stockés en bitplanes donc on doit trifouiller les bits pour // en faire du chunky /// /// Decodes the color of one pixel from the ILBM line buffer. /// Planar to chunky conversion /// @param buffer Planar buffer /// @param x_pos Position of the pixel in graphic line /// @param real_line_size Width of one bitplane in memory, in bytes /// @param bitplanes Number of bitplanes static dword Get_IFF_color(const byte * buffer, word x_pos, word real_line_size, byte bitplanes) { byte shift = 7 - (x_pos & 7); int address,masked_bit,plane; dword color=0; for(plane=bitplanes-1;plane>=0;plane--) { address = (real_line_size * plane + x_pos) >> 3; masked_bit = (buffer[address] >> shift) & 1; color = (color << 1) + masked_bit; } return color; } /// chunky to planar static void Set_IFF_color(byte * buffer, word x_pos, byte color, word real_line_size, byte bitplanes) { byte shift = 7 - (x_pos & 7); int address, plane; for(plane=0;plane> 3; buffer[address] |= (color&1) << shift; color = color >> 1; } } // ----------------------- Afficher une ligne ILBM ------------------------ /// Planar to chunky conversion of a line /// @param context the IO context /// @param buffer Planar buffer /// @param y_pos Current line /// @param real_line_size Width of one bitplane in memory, in bytes /// @param bitplanes Number of bitplanes void Draw_IFF_line(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes) { short x_pos; if (bitplanes > 8) { for (x_pos=0; x_posWidth; x_pos++) { // Default standard deep ILBM bit ordering: // saved first -----------------------------------------------> saved last // R0 R1 R2 R3 R4 R5 R6 R7 G0 G1 G2 G3 G4 G5 G6 G7 B0 B1 B2 B3 B4 B5 B6 B7 dword rgb = Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); Set_pixel_24b(context, x_pos,y_pos, rgb, rgb >> 8, rgb >> 16); // R is 8 LSB, etc. } } else for (x_pos=0; x_posWidth; x_pos++) { Set_pixel(context, x_pos, y_pos, Get_IFF_color(buffer, x_pos,real_line_size, bitplanes)); } } /// decode pixels with palette changes per line (copper list:) static void Draw_IFF_line_PCHG(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes, const T_IFF_PCHG_Palette * PCHG_palettes) { const T_IFF_PCHG_Palette * palette; short x_pos; palette = PCHG_palettes; // find the palette to use for the line if (palette == NULL) return; while (palette->Next != NULL && palette->Next->StartLine <= y_pos) palette = palette->Next; for (x_pos=0; x_posWidth; x_pos++) { dword c = Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); Set_pixel_24b(context, x_pos,y_pos, palette->Palette[c].R, palette->Palette[c].G, palette->Palette[c].B); } } /// Decode a HAM line to 24bits pixels static void Draw_IFF_line_HAM(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes, const T_IFF_PCHG_Palette * PCHG_palettes) { short x_pos; byte red, green, blue, temp; const T_Components * palette; if (PCHG_palettes == NULL) palette = context->Palette; else { // find the palette to use for the line while (PCHG_palettes->Next != NULL && PCHG_palettes->Next->StartLine <= y_pos) PCHG_palettes = PCHG_palettes->Next; palette = PCHG_palettes->Palette; } red = palette[0].R; green = palette[0].G; blue = palette[0].B; if (bitplanes == 6) { for (x_pos=0; x_posWidth; x_pos++) // HAM6 { temp=Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); switch (temp & 0x30) { case 0x10: // blue blue=(temp&0x0F)*0x11; break; case 0x20: // red red=(temp&0x0F)*0x11; break; case 0x30: // green green=(temp&0x0F)*0x11; break; default: // Nouvelle couleur red=palette[temp].R; green =palette[temp].G; blue =palette[temp].B; } Set_pixel_24b(context, x_pos,y_pos,red,green,blue); } } else { for (x_pos=0; x_posWidth; x_pos++) // HAM8 { temp=Get_IFF_color(buffer,x_pos,real_line_size, bitplanes); switch (temp >> 6) { case 0x01: // blue blue= (temp << 2) | ((temp & 0x30) >> 4); break; case 0x02: // red red= (temp << 2) | ((temp & 0x30) >> 4); break; case 0x03: // green green= (temp << 2) | ((temp & 0x30) >> 4); break; default: // Nouvelle couleur red=palette[temp].R; green =palette[temp].G; blue =palette[temp].B; } Set_pixel_24b(context, x_pos,y_pos,red,green,blue); } } } /// Decode PBM data /// /// Supports compressions : /// - 0 uncompressed /// - 1 compressed static void PBM_Decode(T_IO_Context * context, FILE * file, byte compression, word width, word height) { byte * line_buffer; word x_pos, y_pos; word real_line_size = (width+1)&~1; switch (compression) { case 0: // uncompressed line_buffer=(byte *)malloc(real_line_size); for (y_pos=0; ((y_pos127) { if(Read_byte(file, &color)!=1) { File_error=28; break; } do { Set_pixel(context, x_pos++,y_pos,color); } while(temp_byte++ != 0); } else do { if(Read_byte(file, &color)!=1) { File_error=29; break; } Set_pixel(context, x_pos++,y_pos,color); } while(temp_byte-- > 0); } } break; default: GFX2_Log(GFX2_ERROR, "PBM only supports compression type 0 and 1 (not %d)\n", compression); File_error = 50; } } /// Decode LBM data /// /// Supports packings : /// - 0 uncompressed /// - 1 packbits (Amiga) /// - 2 Vertical RLE (Atari ST) static void LBM_Decode(T_IO_Context * context, FILE * file, byte compression, byte Image_HAM, byte stored_bit_planes, byte real_bit_planes, const T_IFF_PCHG_Palette * PCHG_palettes) { int plane; byte * buffer; short x_pos, y_pos; // compute row size int real_line_size = (context->Width+15) & ~15; // size in bit for one bit plane int plane_line_size = real_line_size >> 3; // size in byte for one bit plane int line_size = plane_line_size * stored_bit_planes; // size in byte for all bitplanes switch(compression) { case 0: // uncompressed buffer = (byte *)GFX2_malloc(line_size); if (buffer == NULL) { File_error=1; return; } for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) { if (Read_bytes(file,buffer,line_size)) { if (Image_HAM > 1) Draw_IFF_line_HAM(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); else if (PCHG_palettes) Draw_IFF_line_PCHG(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); else Draw_IFF_line(context, buffer, y_pos,real_line_size, real_bit_planes); } else File_error=21; } free(buffer); break; case 1: // packbits compression (Amiga) buffer = (byte *)GFX2_malloc(line_size); if (buffer == NULL) { File_error=1; return; } for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) { for (x_pos=0; ((x_pos 127 => repeat (257-temp_byte) the next byte // temp_byte <= 127 => copy (temp_byte + 1) bytes if(temp_byte == 128) // 128 = NOP ! { GFX2_Log(GFX2_WARNING, "NOP in packbits stream\n"); } else if (temp_byte>127) { if(Read_byte(file, &color)!=1) { File_error=23; break; } do { if (x_pos=line_size || !Read_byte(file, &(buffer[x_pos++]))) { File_error=25; break; } } while (temp_byte-- > 0); } if (!File_error) { if (Image_HAM > 1) Draw_IFF_line_HAM(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); else if (PCHG_palettes) Draw_IFF_line_PCHG(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); else Draw_IFF_line(context, buffer, y_pos,real_line_size,real_bit_planes); } } free(buffer); break; case 2: // vertical RLE compression (Atari ST) buffer = (byte *)GFX2_malloc(line_size*context->Height); if (buffer == NULL) { File_error=1; return; } memset(buffer, 0, line_size*context->Height); for (plane = 0; plane < stored_bit_planes && !File_error; plane++) { dword section_size; word cmd_count; word cmd; signed char * commands; word count; y_pos = 0; x_pos = 0; if (!IFF_Wait_for(file, "VDAT")) { if (plane == 0) // don't cancel loading if at least 1 bitplane has been loaded File_error = 30; break; } Read_dword_be(file,§ion_size); Read_word_be(file,&cmd_count); cmd_count -= 2; commands = (signed char *)malloc(cmd_count); if (!Read_bytes(file,commands,cmd_count)) { File_error = 31; break; } section_size -= (cmd_count + 2); for (cmd = 0; cmd < cmd_count && x_pos < plane_line_size && section_size > 0; cmd++) { if (commands[cmd] <= 0) { // cmd=0 : load count from data, COPY // cmd < 0 : count = -cmd, COPY if (commands[cmd] == 0) { Read_word_be(file,&count); section_size -= 2; } else count = -commands[cmd]; while (count-- > 0 && x_pos < plane_line_size && section_size > 0) { Read_bytes(file,buffer+x_pos+y_pos*line_size+plane*plane_line_size,2); section_size -= 2; if(++y_pos >= context->Height) { y_pos = 0; x_pos += 2; } } } else if (commands[cmd] >= 1) { // cmd=1 : load count from data, RLE // cmd >1 : count = cmd, RLE byte data[2]; if (commands[cmd] == 1) { Read_word_be(file,&count); section_size -= 2; } else count = (word)commands[cmd]; if (section_size == 0) break; Read_bytes(file,data,2); section_size -= 2; while (count-- > 0 && x_pos < plane_line_size) { memcpy(buffer+x_pos+y_pos*line_size+plane*plane_line_size,data,2); if(++y_pos >= context->Height) { y_pos = 0; x_pos += 2; } } } } if(cmd < (cmd_count-1) || section_size > 0) GFX2_Log(GFX2_WARNING, "Early end in VDAT chunk\n"); if (section_size > 0) fseek(file, (section_size+1)&~1, SEEK_CUR); // skip bytes free(commands); } if (!File_error) { for (y_pos = 0; y_pos < context->Height; y_pos++) { Draw_IFF_line(context,buffer+line_size*y_pos,y_pos,real_line_size,real_bit_planes); } } free(buffer); break; default: GFX2_Log(GFX2_WARNING, "Unknown IFF compression %d\n", compression); File_error = 32; } } /// Decode RAST chunk (from Atari ST pictures) static void RAST_chunk_decode(T_IO_Context * context, FILE * file, dword section_size, T_IFF_PCHG_Palette ** PCHG_palettes) { int i; T_Components palette[16]; T_IFF_PCHG_Palette * prev_pal = NULL; T_IFF_PCHG_Palette * new_pal = NULL; // 17 words per palette : 1 for line, 16 for the colors while (section_size >= 34) { word line, value; Read_word_be(file, &line); for (i = 0; i < 16; i++) { Read_word_be(file, &value); // Decode STE Palette palette[i].R = ((value & 0x0700) >> 7 | (value & 0x0800) >> 11) * 0x11; palette[i].G = ((value & 0x0070) >> 3 | (value & 0x0080) >> 7) * 0x11; palette[i].B = ((value & 0x0007) << 1 | (value & 0x0008) >> 3) * 0x11; } section_size -= 34; if (prev_pal == NULL || (line > prev_pal->StartLine && (0 != memcmp(palette, prev_pal->Palette, sizeof(T_Components)*3)))) { new_pal = malloc(sizeof(T_IFF_PCHG_Palette) + sizeof(T_Components) * 16); if (new_pal == NULL) { File_error = 2; return; } memcpy(new_pal->Palette, palette, sizeof(T_Components) * 16); new_pal->StartLine = line; new_pal->Next = NULL; if (prev_pal != NULL) { prev_pal->Next = new_pal; prev_pal = new_pal; } else if (line == 0) { prev_pal = new_pal; *PCHG_palettes = prev_pal; } else // line > 0 && prev_pal == NULL { // create a palette for line 0 prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + sizeof(T_Components) * 16); if (prev_pal == NULL) { File_error = 2; return; } memcpy(prev_pal->Palette, context->Palette, sizeof(T_Components) * 16); prev_pal->StartLine = 0; prev_pal->Next = new_pal; *PCHG_palettes = prev_pal; prev_pal = new_pal; } } } } /// Sets 32 upper colors of EHB palette static void IFF_Set_EHB_Palette(T_Components * palette) { int i, j; /// 32 colors in the palette. for (i=0; i<32; i++) /// The next 32 colors are the same with values divided by 2 { j=i+32; palette[j].R = palette[i].R>>1; palette[j].G = palette[i].G>>1; palette[j].B = palette[i].B>>1; } } /// Load IFF picture (PBM/ILBM/ACBM) or animation void Load_IFF(T_IO_Context * context) { FILE * IFF_file; T_IFF_Header header; T_IFF_AnimHeader aheader; char format[4]; char section[4]; byte temp_byte; dword nb_colors = 0; // number of colors in the CMAP (color map) dword section_size; short x_pos; short y_pos; short line_size = 0; // Taille d'une ligne en octets short plane_line_size = 0; // Size of line in bytes for 1 plane short real_line_size = 0; // Taille d'une ligne en pixels unsigned long file_size; dword dummy; int iff_format = 0; int plane; dword AmigaViewModes = 0; enum PIXEL_RATIO ratio = PIXEL_SIMPLE; byte * buffer; byte bpp = 0; byte Image_HAM = 0; T_IFF_PCHG_Palette * PCHG_palettes = NULL; int current_frame = 0; byte * previous_frame = NULL; // For animations byte * anteprevious_frame = NULL; word frame_count = 0; word frame_duration = 0; word vdlt_plane = 0; // current plane during Atari ST animation decoding memset(&aheader, 0, sizeof(aheader)); File_error=0; if ((IFF_file=Open_file_read(context))) { file_size=File_length_file(IFF_file); // FORM + size(4) Read_bytes(IFF_file,section,4); Read_dword_be(IFF_file,&dummy); Read_bytes(IFF_file,format,4); if (!memcmp(format,"ANIM",4)) { // Skip a bit, brother Read_bytes(IFF_file,section,4); Read_dword_be(IFF_file,§ion_size); Read_bytes(IFF_file,format,4); while (0 == memcmp(format, "8SVX", 4)) { GFX2_Log(GFX2_DEBUG, "IFF : skip sound %.4s %u bytes\n", format, section_size); if (fseek(IFF_file, section_size - 4, SEEK_CUR) < 0) break; Read_bytes(IFF_file,section,4); Read_dword_be(IFF_file,§ion_size); Read_bytes(IFF_file,format,4); } } else if(memcmp(format,"DPST",4)==0) { // read DPAH while (File_error == 0) { if (!(Read_bytes(IFF_file,section,4) && Read_dword_be(IFF_file,§ion_size))) File_error = 1; if (memcmp(section, "FORM", 4) == 0) { Read_bytes(IFF_file,format,4); break; } else if (memcmp(section, "DPAH", 4) == 0) // Deluxe Paint Animation Header { word version; Read_word_be(IFF_file, &frame_duration); Read_word_be(IFF_file, &frame_count); Read_word_be(IFF_file, &version); section_size -= 6; Set_frame_duration(context, (frame_duration * 50) / 3); // convert 1/60th sec to msec } fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // skip unread bytes } } if (memcmp(format,"ILBM",4) == 0) iff_format = FORMAT_LBM; else if(memcmp(format,"PBM ",4) == 0) iff_format = FORMAT_PBM; else if(memcmp(format,"ACBM",4) == 0) iff_format = FORMAT_ACBM; else { GFX2_Log(GFX2_WARNING, "Unknown IFF format '%.4s'\n", format); File_error=1; } { byte real_bit_planes = 0; byte stored_bit_planes = 0; while (File_error == 0 && Read_bytes(IFF_file,section,4) && Read_dword_be(IFF_file,§ion_size)) { if (memcmp(section, "FORM", 4) == 0) { // special Read_bytes(IFF_file, format, 4); if (memcmp(format, "VDLT", 4) == 0) // Vertical DeLTa { // found in animation produced by DeluxePaint for Atari ST if (frame_count != 0 && current_frame >= (frame_count - 1)) break; // file contains the Delta from the last frame to the 1st one to allow looping, skip it real_line_size = (context->Width+15) & ~15; plane_line_size = real_line_size >> 3; // 8bits per byte line_size = plane_line_size * real_bit_planes; if (previous_frame == NULL) { previous_frame = calloc(line_size * context->Height,1); for (y_pos=0; y_posHeight; y_pos++) { const byte * pix_p = context->Target_address + y_pos * context->Pitch; // Dispatch the pixel into planes for (x_pos=0; x_posWidth; x_pos++) { Set_IFF_color(previous_frame+y_pos*line_size, x_pos, *pix_p++, real_line_size, real_bit_planes); } } } Set_loading_layer(context, ++current_frame); Set_frame_duration(context, (frame_duration * 50) / 3); // convert 1/60th sec to msec vdlt_plane = 0; } continue; } if (memcmp(section, "BMHD", 4) == 0) // BitMap HeaDer { if (!((Read_word_be(IFF_file,&header.Width)) && (Read_word_be(IFF_file,&header.Height)) && (Read_word_be(IFF_file,&header.X_org)) && (Read_word_be(IFF_file,&header.Y_org)) && (Read_byte(IFF_file,&header.BitPlanes)) && (Read_byte(IFF_file,&header.Mask)) && (Read_byte(IFF_file,&header.Compression)) && (Read_byte(IFF_file,&header.Pad1)) && (Read_word_be(IFF_file,&header.Transp_col)) && (Read_byte(IFF_file,&header.X_aspect)) && (Read_byte(IFF_file,&header.Y_aspect)) && (Read_word_be(IFF_file,&header.X_screen)) && (Read_word_be(IFF_file,&header.Y_screen))) ) { File_error = 1; break; } GFX2_Log(GFX2_DEBUG, "IFF BMHD : origin (%u,%u) size %ux%u BitPlanes=%u Mask=%u Compression=%u\n", header.X_org, header.Y_org, header.Width, header.Height, header.BitPlanes, header.Mask, header.Compression); GFX2_Log(GFX2_DEBUG, " Transp_col=#%u aspect ratio %u/%u screen size %ux%u\n", header.Transp_col, header.X_aspect, header.Y_aspect, header.X_screen, header.Y_screen); real_bit_planes = header.BitPlanes; stored_bit_planes = header.BitPlanes; if (header.Mask==1) stored_bit_planes++; Image_HAM=0; if (header.X_aspect != 0 && header.Y_aspect != 0) { if ((10 * header.X_aspect) <= (6 * header.Y_aspect)) ratio = PIXEL_TALL; // ratio <= 0.6 else if ((10 * header.X_aspect) <= (8 * header.Y_aspect)) ratio = PIXEL_TALL3; // 0.6 < ratio <= 0.8 else if ((10 * header.X_aspect) < (15 * header.Y_aspect)) ratio = PIXEL_SIMPLE; // 0.8 < ratio < 1.5 else ratio = PIXEL_WIDE; // 1.5 <= ratio } bpp = header.BitPlanes; if (bpp <= 8 && bpp > 0) { unsigned int i; // Set a default grayscale palette : if the // ILBM/PBM file has no palette, it should be assumed to be a // Gray scale picture. if (Config.Clear_palette) memset(context->Palette,0,sizeof(T_Palette)); nb_colors = 1 << bpp; for (i = 0; i < nb_colors; i++) context->Palette[i].R = context->Palette[i].G = context->Palette[i].B = (i * 255) / (nb_colors - 1); } } else if (memcmp(section, "ANHD", 4) == 0) // ANimation HeaDer { // http://www.textfiles.com/programming/FORMATS/anim7.txt Read_byte(IFF_file, &aheader.operation); Read_byte(IFF_file, &aheader.mask); Read_word_be(IFF_file, &aheader.w); Read_word_be(IFF_file, &aheader.h); Read_word_be(IFF_file, &aheader.x); Read_word_be(IFF_file, &aheader.y); Read_dword_be(IFF_file, &aheader.abstime); Read_dword_be(IFF_file, &aheader.reltime); Read_byte(IFF_file, &aheader.interleave); Read_byte(IFF_file, &aheader.pad0); Read_dword_be(IFF_file, &aheader.bits); section_size -= 24; if ((aheader.bits & 0xffffffc0) != 0) // invalid ? => clearing aheader.bits = 0; if (aheader.operation == 0) // ANHD for 1st frame (BODY) Set_frame_duration(context, (aheader.reltime * 50) / 3); // convert 1/60th sec to msec fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes } else if (memcmp(section, "DPAN", 4) == 0) // Deluxe Paint ANimation { word version; dword flags; if (section_size >= 8) { Read_word_be(IFF_file, &version); Read_word_be(IFF_file, &frame_count); Read_dword_be(IFF_file, &flags); section_size -= 8; } fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes } else if (memcmp(section, "DLTA", 4) == 0) // Animation DeLTA { int i, plane; dword offsets[16]; dword current_offset = 0; byte * frame; if (Image_HAM > 1) { GFX2_Log(GFX2_WARNING, "HAM animations are not supported\n"); //Verbose_message("Notice", "HAM animations are not supported, loading only first frame"); // TODO: causes an issue with colors break; } if (frame_count != 0 && current_frame >= (frame_count - 1)) break; // some animations have delta from last to first frame if (previous_frame == NULL) { real_line_size = (context->Width+15) & ~15; plane_line_size = real_line_size >> 3; // 8bits per byte line_size = plane_line_size * real_bit_planes; previous_frame = calloc(line_size * context->Height,1); for (y_pos=0; y_posHeight; y_pos++) { const byte * pix_p = context->Target_address + y_pos * context->Pitch; // Dispatch the pixel into planes for (x_pos=0; x_posWidth; x_pos++) { Set_IFF_color(previous_frame+y_pos*line_size, x_pos, *pix_p++, real_line_size, real_bit_planes); } } // many animations are designed for double buffering // and delta is against frame n-2 anteprevious_frame = malloc(line_size * context->Height); memcpy(anteprevious_frame, previous_frame, line_size * context->Height); } Set_loading_layer(context, ++current_frame); Set_frame_duration(context, (aheader.reltime * 50) / 3 ); // convert 1/60th sec in msec frame = previous_frame; if(aheader.operation == 5) // Byte Vertical Delta mode { if (aheader.interleave != 1) frame = anteprevious_frame; for (i = 0; i < 16; i++) { if (!Read_dword_be(IFF_file, offsets+i)) { File_error = 2; break; } current_offset += 4; } for (plane = 0; plane < 16; plane++) { byte op_count = 0; if (offsets[plane] == 0) continue; if (plane >= real_bit_planes) { GFX2_Log(GFX2_WARNING, "IFF : too much bitplanes in DLTA data %u >= %u (frame #%u/%u)\n", plane, real_bit_planes, current_frame + 1, frame_count); break; } while (current_offset < offsets[plane]) { Read_byte(IFF_file, &op_count); current_offset++; } if (current_offset > offsets[plane]) { GFX2_Log(GFX2_ERROR, "Loading ERROR in DLTA data\n"); File_error = 2; break; } for (x_pos = 0; x_pos < (context->Width+7) >> 3; x_pos++) { byte * p = frame + x_pos + plane * plane_line_size; y_pos = 0; Read_byte(IFF_file, &op_count); current_offset++; for (i = 0; i < op_count; i++) { byte op; if (y_pos >= context->Height) { } Read_byte(IFF_file, &op); current_offset++; if (op == 0) { // Same ops byte countb, datab; Read_byte(IFF_file, &countb); Read_byte(IFF_file, &datab); current_offset += 2; while(countb > 0 && y_pos < context->Height) { if(aheader.bits & 2) // XOR *p ^= datab; else // set *p = datab; p += line_size; y_pos++; countb--; } } else if (op & 0x80) { // Uniq Ops op &= 0x7f; while (op > 0) { byte datab; Read_byte(IFF_file, &datab); current_offset++; if (y_pos < context->Height) { if(aheader.bits & 2) // XOR *p ^= datab; else // set *p = datab; p += line_size; y_pos++; } op--; } } else { // skip ops p += op * line_size; y_pos += op; } } if (y_pos > context->Height) { } } } } else if(aheader.operation==74) { // from sources found on aminet : // http://aminet.net/package/gfx/conv/unmovie while (current_offset < section_size) { word change_type; word uni_flag; word y_size; word x_size; word num_blocks; word offset; word x_start, y_start; Read_word_be(IFF_file, &change_type); current_offset += 2; if (change_type == 0) break; else if (change_type == 1) { // "Wall" Read_word_be(IFF_file, &uni_flag); Read_word_be(IFF_file, &y_size); Read_word_be(IFF_file, &num_blocks); current_offset += 6; while (num_blocks-- > 0) { Read_word_be(IFF_file, &offset); current_offset += 2; x_start = offset % plane_line_size; y_start = offset / plane_line_size; for (plane = 0; plane < real_bit_planes; plane++) { byte * p = frame + plane * plane_line_size; p += x_start + y_start*line_size; for (y_pos=0; y_pos < y_size; y_pos++) { byte value; Read_byte(IFF_file, &value); current_offset++; if (uni_flag) *p ^= value; else *p = value; } p += line_size; } } } else if (change_type == 2) { // "Pile" Read_word_be(IFF_file, &uni_flag); Read_word_be(IFF_file, &y_size); Read_word_be(IFF_file, &x_size); Read_word_be(IFF_file, &num_blocks); current_offset += 8; while (num_blocks-- > 0) { Read_word_be(IFF_file, &offset); current_offset += 2; x_start = offset % plane_line_size; y_start = offset / plane_line_size; for (plane = 0; plane < real_bit_planes; plane++) { byte * p = frame + plane * plane_line_size; p += x_start + y_start*line_size; for (y_pos=0; y_pos < y_size; y_pos++) { for (x_pos=0; x_pos < x_size; x_pos++) { byte value; Read_byte(IFF_file, &value); current_offset++; if (uni_flag) p[x_pos] ^= value; else p[x_pos] = value; } p += line_size; } } } } else { GFX2_Log(GFX2_ERROR, "Unknown change type in type 74 DLTA\n"); File_error = 2; break; } if (current_offset & 1) // align to WORD boundary { byte junk; Read_byte(IFF_file, &junk); current_offset++; } } } else { GFX2_Log(GFX2_INFO, "IFF DLTA : Unsupported compression type %u\n", aheader.operation); } if (File_error == 0) { for (y_pos=0; y_posHeight; y_pos++) { Draw_IFF_line(context, frame+line_size*y_pos,y_pos,real_line_size,real_bit_planes); } } if (aheader.operation == 5 && aheader.interleave != 1) { anteprevious_frame = previous_frame; previous_frame = frame; } if (current_offset&1) { byte dummy_byte; Read_byte(IFF_file, &dummy_byte); current_offset++; } section_size -= current_offset; fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it } else if (memcmp(section, "ADAT", 4) == 0) { // Animation made with Deluxe Paint for Atari ST buffer = previous_frame; if (section_size > 0) { byte * p; byte data[2]; word cmd_count; word cmd; signed char * commands; word count; y_pos = 0; x_pos = 0; Read_word_be(IFF_file,&cmd_count); cmd_count -= 2; commands = (signed char *)malloc(cmd_count); if (!Read_bytes(IFF_file,commands,cmd_count)) { File_error = 31; break; } section_size -= (cmd_count + 2); for (cmd = 0; cmd < cmd_count && section_size > 0; cmd++) { if (commands[cmd] == 0) { // move pointer word offset; short x_ofs, y_ofs; Read_word_be(IFF_file,&offset); section_size -= 2; if((short)offset < 0) { y_ofs = ((short)offset - plane_line_size + 1) / plane_line_size; x_ofs = (short)offset - y_ofs * plane_line_size; } else { x_ofs = offset % plane_line_size; y_ofs = offset / plane_line_size; } y_pos += y_ofs; x_pos += x_ofs; } else if(commands[cmd] < 0) { // XOR with a string of Words if (commands[cmd] == -1 && section_size >= 2) { Read_word_be(IFF_file,&count); section_size -= 2; } else count = -commands[cmd] - 1; while (count-- > 0 && y_pos < context->Height && section_size > 0) { p = buffer+x_pos+y_pos*line_size+vdlt_plane*plane_line_size; Read_bytes(IFF_file,data,2); section_size -= 2; p[0] ^= data[0]; p[1] ^= data[1]; y_pos++; } } else // commands[cmd] > 0 { // XOR a word several times if (commands[cmd] == 1) { Read_word_be(IFF_file,&count); section_size -= 2; if (section_size < 2) break; } else count = commands[cmd] - 1; Read_bytes(IFF_file,data,2); section_size -= 2; do { p = buffer+x_pos+y_pos*line_size+vdlt_plane*plane_line_size; p[0] ^= data[0]; p[1] ^= data[1]; y_pos++; } while (count-- > 0 && y_pos < context->Height); } } free(commands); if(cmd < (cmd_count-1) || section_size > 0) { GFX2_Log(GFX2_WARNING, "Early end in ADAT chunk\n"); } } vdlt_plane++; if (vdlt_plane == real_bit_planes) { for (y_pos=0; y_posHeight; y_pos++) { Draw_IFF_line(context, buffer+line_size*y_pos,y_pos,real_line_size,real_bit_planes); } } fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes } else if (memcmp(section, "CMAP", 4) == 0) // Colour MAP { nb_colors = section_size/3; if (nb_colors > 256) { File_error = 1; break; } GFX2_Log(GFX2_DEBUG, "IFF CMAP : %u colors (header.BitPlanes=%u)\n", nb_colors, header.BitPlanes); if (current_frame != 0) GFX2_Log(GFX2_WARNING, "IFF : One CMAP per frame is not supported (frame #%u/%u)\n", current_frame + 1, frame_count); if ((header.BitPlanes==6 && nb_colors==16) || (header.BitPlanes==8 && nb_colors==64)) { Image_HAM=header.BitPlanes; bpp = 3 * (header.BitPlanes - 2); // HAM6 = 12bpp, HAM8 = 18bpp } if (Read_bytes(IFF_file,context->Palette,3*nb_colors)) { section_size -= 3*nb_colors; if (nb_colors <= 64) { unsigned int i; int is_4bits_colors = 1; // recoil also checks that header.Pad1 is not 0x80 for(i = 0; i < nb_colors && is_4bits_colors; i++) { is_4bits_colors = is_4bits_colors && ((context->Palette[i].R & 0x0f) == 0) && ((context->Palette[i].G & 0x0f) == 0) && ((context->Palette[i].B & 0x0f) == 0); } if (is_4bits_colors) // extend 4bits per component color palettes to 8bits per component for(i = 0; i < nb_colors; i++) { context->Palette[i].R |= (context->Palette[i].R >> 4); context->Palette[i].G |= (context->Palette[i].G >> 4); context->Palette[i].B |= (context->Palette[i].B >> 4); } } if (((nb_colors==32) || (AmigaViewModes & 0x80)) && (header.BitPlanes==6)) { IFF_Set_EHB_Palette(context->Palette); // This is a Extra Half-Brite (EHB) 64 color image. nb_colors = 64; } while(section_size > 0) // Read padding bytes { if (Read_byte(IFF_file,&temp_byte)) File_error=20; section_size--; } } else File_error=1; if (context->Type == CONTEXT_PALETTE || context->Type == CONTEXT_PREVIEW_PALETTE) break; // stop once the palette is loaded } else if (memcmp(section,"CRNG",4) == 0) { // The content of a CRNG is as follows: word padding; word rate; word flags; byte min_col; byte max_col; if ( (Read_word_be(IFF_file,&padding)) && (Read_word_be(IFF_file,&rate)) && (Read_word_be(IFF_file,&flags)) && (Read_byte(IFF_file,&min_col)) && (Read_byte(IFF_file,&max_col))) { GFX2_Log(GFX2_DEBUG, "IFF CRNG : [#%u #%u] rate=%u flags=%04x\n", min_col, max_col, rate, flags); if (section_size == 8 && min_col != max_col) { // Valid cycling range if (max_colColor_cycles >= 16) { GFX2_Log(GFX2_INFO, "IFF CRNG : Maximum CRNG number is 16\n"); } else { word speed; context->Cycle_range[context->Color_cycles].Start=min_col; context->Cycle_range[context->Color_cycles].End=max_col; context->Cycle_range[context->Color_cycles].Inverse=(flags&2)?1:0; speed = (flags&1) ? rate/78 : 0; context->Cycle_range[context->Color_cycles].Speed = (speed > COLOR_CYCLING_SPEED_MAX) ? COLOR_CYCLING_SPEED_MAX : speed; context->Color_cycles++; } } } else File_error=47; } else if (memcmp(section, "DRNG", 4) == 0) // DPaint IV enhanced color cycle chunk { /// @todo DRNG IFF chunk is read, but complex color cycling are not implemented. // see http://amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node0282.html byte min_col; byte max_col; word rate; word flags; byte ntruecolor; byte nregs; Read_byte(IFF_file, &min_col); Read_byte(IFF_file, &max_col); Read_word_be(IFF_file, &rate); Read_word_be(IFF_file, &flags); Read_byte(IFF_file, &ntruecolor); Read_byte(IFF_file, &nregs); section_size -= 8; GFX2_Log(GFX2_DEBUG, "IFF DRNG : [#%u #%u] rate=%u flags=%04x, %u tc cells, %u reg cells\n", min_col, max_col, rate, flags, ntruecolor, nregs); while (ntruecolor-- > 0 && section_size > 0) { byte cell, r, g, b; Read_byte(IFF_file, &cell); Read_byte(IFF_file, &r); Read_byte(IFF_file, &g); Read_byte(IFF_file, &b); section_size -= 4; GFX2_Log(GFX2_DEBUG, " #%u #%02X%02X%02X\n", cell, r, g, b); } while (nregs-- > 0 && section_size > 0) { byte cell, index; Read_byte(IFF_file, &cell); Read_byte(IFF_file, &index); section_size -= 2; GFX2_Log(GFX2_DEBUG, " #%u index %u\n", cell, index); } if (section_size > 0) { GFX2_Log(GFX2_DEBUG, "IFF DRNG : skipping %u bytes\n", section_size); fseek(IFF_file, section_size, SEEK_CUR); } } else if (memcmp(section, "CCRT", 4) == 0) // Color Cycling Range and Timing { // see http://amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node01BA.html word direction; byte start, end; dword seconds, microseconds; word pad; Read_word_be(IFF_file, &direction); Read_byte(IFF_file, &start); Read_byte(IFF_file, &end); Read_dword_be(IFF_file, &seconds); Read_dword_be(IFF_file, µseconds); Read_word_be(IFF_file, &pad); section_size -= 14; GFX2_Log(GFX2_DEBUG, "IFF CCRT : dir %04x [#%u #%u] delay %u.%06u\n", direction, start, end, seconds, microseconds); if (context->Color_cycles >= 16) { GFX2_Log(GFX2_INFO, "IFF CCRT : Maximum CRNG number is 16\n"); } else if (start != end) { // Speed resolution is 0.2856Hz // Speed = (1000000/delay) / 0.2856 = 3501400 / delay context->Cycle_range[context->Color_cycles].Start = start; context->Cycle_range[context->Color_cycles].End = end; if (direction != 0) { dword speed; context->Cycle_range[context->Color_cycles].Inverse = (~direction >> 15) & 1; speed = 3501400 / (seconds * 1000000 + microseconds); context->Cycle_range[context->Color_cycles].Speed = (speed > COLOR_CYCLING_SPEED_MAX) ? COLOR_CYCLING_SPEED_MAX : speed; } else { context->Cycle_range[context->Color_cycles].Speed = 0; } context->Color_cycles++; } } else if (memcmp(section, "DPI ", 4) == 0) { word dpi_x, dpi_y; Read_word_be(IFF_file, &dpi_x); Read_word_be(IFF_file, &dpi_y); section_size -= 4; GFX2_Log(GFX2_DEBUG, "IFF DPI %hu x %hu\n", dpi_x, dpi_y); } else if (memcmp(section, "CAMG", 4) == 0) // Amiga Viewport Modes { Read_dword_be(IFF_file, &AmigaViewModes); // HIRES=0x8000 LACE=0x4 HAM=0x800 HALFBRITE=0x80 section_size -= 4; GFX2_Log(GFX2_DEBUG, "CAMG = $%08x\n", AmigaViewModes); if (AmigaViewModes & 0x800 && (header.BitPlanes == 6 || header.BitPlanes == 8)) { Image_HAM = header.BitPlanes; bpp = 3 * (header.BitPlanes - 2); } if ((AmigaViewModes & 0x80) && (header.BitPlanes == 6)) // This is a Extra Half-Brite (EHB) 64 color image. { IFF_Set_EHB_Palette(context->Palette); // Set the palette in case CAMG is after CMAP nb_colors = 64; } } else if (memcmp(section, "DPPV", 4) == 0) // DPaint II ILBM perspective chunk { fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it } else if (memcmp(section, "CLUT", 4) == 0) // lookup table { dword lut_type; // 0 = A Monochrome, contrast or intensity LUT byte lut[256]; // 1 = RED, 2 = GREEN, 3 = BLUE, 4 = HUE, 5 = SATURATION Read_dword_be(IFF_file, &lut_type); Read_dword_be(IFF_file, &dummy); Read_bytes(IFF_file, lut, 256); section_size -= (4+4+256); if (section_size > 0) { GFX2_Log(GFX2_WARNING, "Extra bytes at the end of CLUT chunk\n"); fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); } } else if (memcmp(section, "DYCP", 4) == 0) // DYnamic Color Palette { // All files I've seen have 4 words (8bytes) : // { 0, 1, 16, 0} 16 is probably the number of colors in each palette fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it } else if (memcmp(section, "SHAM", 4) == 0) // Sliced HAM { int i; word version; dword SHAM_palette_count; T_IFF_PCHG_Palette * prev_pal = NULL; T_IFF_PCHG_Palette * new_pal = NULL; Image_HAM = header.BitPlanes; bpp = 3 * (header.BitPlanes - 2); Read_word_be(IFF_file, &version); // always 0 section_size -= 2; SHAM_palette_count = section_size >> 5; // 32 bytes per palette (16 colors * 2 bytes) // SHAM_palette_count should be the image height, or height/2 for "interlaced" images for (y_pos = 0; y_pos < header.Height && section_size >= 32; y_pos += (SHAM_palette_count < header.Height ? 2 : 1)) { new_pal = GFX2_malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); if (new_pal == NULL) { File_error = 1; break; } new_pal->Next = NULL; new_pal->StartLine = y_pos; for (i = 0; i < 16; i++) { Read_byte(IFF_file, &temp_byte); // 0R new_pal->Palette[i].R = (temp_byte & 0x0f) * 0x11; // 4 bits to 8 bits Read_byte(IFF_file, &temp_byte); // GB new_pal->Palette[i].G = (temp_byte & 0xf0) | (temp_byte >> 4); new_pal->Palette[i].B = (temp_byte & 0x0f) * 0x11; // 4 bits to 8 bits section_size -= 2; } if (prev_pal != NULL) prev_pal->Next = new_pal; else PCHG_palettes = new_pal; prev_pal = new_pal; } if (section_size > 0) { GFX2_Log(GFX2_WARNING, "Extra bytes at the end of SHAM chunk\n"); fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); } } else if (memcmp(section, "BEAM", 4) == 0 || memcmp(section, "CTBL", 4) == 0) { // One palette per line is stored if (section_size >= header.Height * nb_colors * 2) { T_Palette palette; T_IFF_PCHG_Palette * prev_pal = NULL; T_IFF_PCHG_Palette * new_pal = NULL; for (y_pos = 0; y_pos < header.Height; y_pos++) { unsigned int i; for (i = 0; i < nb_colors; i++) { word value; Read_word_be(IFF_file, &value); section_size -= 2; palette[i].R = ((value & 0x0f00) >> 8) * 0x11; palette[i].G = ((value & 0x00f0) >> 4) * 0x11; palette[i].B = (value & 0x000f) * 0x11; } if (y_pos == 0) { prev_pal = GFX2_malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); if (prev_pal == NULL) { File_error = 1; break; } prev_pal->Next = NULL; prev_pal->StartLine = 0; memcpy(prev_pal->Palette, palette, nb_colors*sizeof(T_Components)); PCHG_palettes = prev_pal; } else if (memcmp(palette, prev_pal->Palette, nb_colors*sizeof(T_Components)) != 0) { new_pal = GFX2_malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); if (new_pal == NULL) { File_error = 1; break; } new_pal->Next = NULL; new_pal->StartLine = y_pos; memcpy(new_pal->Palette, palette, nb_colors*sizeof(T_Components)); prev_pal->Next = new_pal; prev_pal = new_pal; } } if (PCHG_palettes != NULL) bpp = 12; } else GFX2_Log(GFX2_WARNING, "inconsistent size of BEAM/CTLB chunk, ignoring\n"); fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); } else if (memcmp(section, "PCHG", 4) == 0) // Palette CHanGes { dword * lineBitMask; int i; T_IFF_PCHG_Palette * prev_pal = NULL; T_IFF_PCHG_Palette * curr_pal = NULL; void * PCHGData = NULL; // http://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.PCHG // http://vigna.di.unimi.it/software.php // Header word Compression; // 0 = None, 1 = Huffman word Flags; // 0x1 = SmallLineChanges, 0x2 = BigLineChanges, 0x4 = use alpha short StartLine; // possibly negative word LineCount; word ChangedLines; word MinReg; word MaxReg; word MaxChanges; dword TotalChanges; if (!(Read_word_be(IFF_file, &Compression) && Read_word_be(IFF_file, &Flags) && Read_word_be(IFF_file, (word *)&StartLine) && Read_word_be(IFF_file, &LineCount) && Read_word_be(IFF_file, &ChangedLines) && Read_word_be(IFF_file, &MinReg) && Read_word_be(IFF_file, &MaxReg) && Read_word_be(IFF_file, &MaxChanges) && Read_dword_be(IFF_file, &TotalChanges))) File_error = 1; section_size -= 20; if (Compression) { short * TreeCode; const short * TreeP; //a3 int remaining_bits = 0; //d1 dword src_dword = 0; //d2 byte * dst_data; //a1 dword CompInfoSize; dword OriginalDataSize; //d0 Read_dword_be(IFF_file, &CompInfoSize); Read_dword_be(IFF_file, &OriginalDataSize); section_size -= 8; // read HuffMan tree TreeCode = malloc(CompInfoSize); for (i = 0; i < (int)(CompInfoSize / 2); i++) Read_word_be(IFF_file, (word *)(TreeCode + i)); section_size -= CompInfoSize; PCHGData = malloc(OriginalDataSize); dst_data = PCHGData; // HuffMan depacking TreeP = TreeCode+(CompInfoSize/2-1); // pointer to the last element while (OriginalDataSize > 0) { if (--remaining_bits < 0) { Read_dword_be(IFF_file, &src_dword); section_size -= 4; remaining_bits = 31; } if (src_dword & (1 << remaining_bits)) { if (*TreeP < 0) { // OffsetPointer TreeP += (*TreeP / 2); continue; // pick another bit } } else { // Case0 --TreeP; if ((*TreeP < 0) || !(*TreeP & 0x100)) continue; // pick another bit } // StoreValue *dst_data = (byte)(*TreeP & 0xff); dst_data++; TreeP = TreeCode+(CompInfoSize/2-1); // pointer to the last element OriginalDataSize--; } free(TreeCode); } else { PCHGData = malloc(section_size); Read_bytes(IFF_file, PCHGData, section_size); section_size = 0; } if (PCHGData != NULL) { const byte * data; // initialize first palette from CMAP prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); prev_pal->Next = NULL; prev_pal->StartLine = 0; memcpy(prev_pal->Palette, context->Palette, nb_colors*sizeof(T_Components)); PCHG_palettes = prev_pal; lineBitMask = (dword *)PCHGData; #if defined(SDL_BYTEORDER) && (SDL_BYTEORDER != SDL_BIG_ENDIAN) for (i = 0 ; i < ((LineCount + 31) >> 5); i++) lineBitMask[i] = SDL_Swap32(lineBitMask[i]); #elif defined(BYTE_ORDER) && (BYTE_ORDER != BIG_ENDIAN) for (i = 0 ; i < ((LineCount + 31) >> 5); i++) lineBitMask[i] = be32toh(lineBitMask[i]); #elif defined(WIN32) // assume WIN32 is little endian for (i = 0 ; i < ((LineCount + 31) >> 5); i++) lineBitMask[i] = WIN32_BSWAP32(lineBitMask[i]); #endif data = (const byte *)PCHGData + ((LineCount + 31) >> 5) * 4; for (y_pos = 0 ; y_pos < LineCount; y_pos++) { if (lineBitMask[y_pos >> 5] & (1 << (31-(y_pos & 31)))) { byte ChangeCount16, ChangeCount32; word PaletteChange; if ((y_pos + StartLine) < 0) curr_pal = PCHG_palettes; else { curr_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); curr_pal->Next = NULL; curr_pal->StartLine = StartLine + y_pos; memcpy(curr_pal->Palette, prev_pal->Palette, nb_colors*sizeof(T_Components)); prev_pal->Next = curr_pal; } ChangeCount16 = *data++; ChangeCount32 = *data++; for (i = 0; i < ChangeCount16; i++) { PaletteChange = data[0] << 8 | data[1]; // Big endian data += 2; curr_pal->Palette[(PaletteChange >> 12)].R = ((PaletteChange & 0x0f00) >> 8) * 0x11; curr_pal->Palette[(PaletteChange >> 12)].G = ((PaletteChange & 0x00f0) >> 4) * 0x11; curr_pal->Palette[(PaletteChange >> 12)].B = ((PaletteChange & 0x000f) >> 0) * 0x11; } for (i = 0; i < ChangeCount32; i++) { PaletteChange = data[0] << 8 | data[1]; // Big endian data += 2; curr_pal->Palette[16+(PaletteChange >> 12)].R = ((PaletteChange & 0x0f00) >> 8) * 0x11; curr_pal->Palette[16+(PaletteChange >> 12)].G = ((PaletteChange & 0x00f0) >> 4) * 0x11; curr_pal->Palette[16+(PaletteChange >> 12)].B = ((PaletteChange & 0x000f) >> 0) * 0x11; } if (nb_colors == 64) // Extend the 32 colors decoded to 64 IFF_Set_EHB_Palette(curr_pal->Palette); prev_pal = curr_pal; } } free(PCHGData); } fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); if (PCHG_palettes != NULL) bpp = 12; } else if (memcmp(section, "RAST", 4) == 0) // Atari ST { if (PCHG_palettes == NULL) { RAST_chunk_decode(context, IFF_file, section_size, &PCHG_palettes); if (PCHG_palettes != NULL) bpp = 12; } else fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip } else if (memcmp(section, "TINY", 4) == 0) { word tiny_width, tiny_height; Read_word_be(IFF_file,&tiny_width); Read_word_be(IFF_file,&tiny_height); section_size -= 4; // Load thumbnail if in preview mode if ((context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE) && tiny_width > 0 && tiny_height > 0) { context->Original_width = header.Width; context->Original_height = header.Height; Pre_load(context, tiny_width, tiny_height,file_size,iff_format,ratio,bpp); context->Background_transparent = header.Mask == 2; context->Transparent_color = context->Background_transparent ? header.Transp_col : 0; if (iff_format == FORMAT_PBM) PBM_Decode(context, IFF_file, header.Compression, tiny_width, tiny_height); else LBM_Decode(context, IFF_file, header.Compression, Image_HAM, stored_bit_planes, real_bit_planes, PCHG_palettes); fclose(IFF_file); IFF_file = NULL; return; } else fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); } else if (memcmp(section, "ANNO", 4) == 0) { dword length; section_size = (section_size + 1) & ~1; length = section_size; if (length > COMMENT_SIZE) length = COMMENT_SIZE; Read_bytes(IFF_file,context->Comment,length); context->Comment[length]='\0'; section_size -= length; fseek(IFF_file, section_size, SEEK_CUR); } else if (memcmp(section, "ABIT", 4) == 0) { // ACBM format : ABIT = Amiga BITplanes // The ABIT chunk contains contiguous bitplane data. // The chunk contains sequential data for bitplane 0 through bitplane n. if (header.Width == 0 || header.Height == 0) break; Pre_load(context, header.Width, header.Height, file_size, iff_format, ratio, bpp); // compute row size real_line_size = (context->Width+15) & ~15; plane_line_size = real_line_size >> 3; // 8bits per byte line_size = plane_line_size * stored_bit_planes; buffer = malloc(line_size * context->Height); if ((dword)(line_size * context->Height) == section_size) header.Compression = 0; // size is of uncompressed data. Forcing. for (plane = 0; plane < stored_bit_planes; plane++) { for (y_pos = 0; y_pos < context->Height; y_pos++) { if (header.Compression == 0) { if (!Read_bytes(IFF_file,buffer+line_size*y_pos+plane_line_size*plane,plane_line_size)) { File_error = 21; break; } } else { GFX2_Log(GFX2_WARNING, "Unhandled compression %d for ACBM ABIT chunk\n", header.Compression); File_error = 32; break; } } } if (File_error == 0) { for (y_pos = 0; y_pos < context->Height; y_pos++) { if (Image_HAM <= 1) Draw_IFF_line(context, buffer+y_pos*line_size, y_pos,real_line_size, real_bit_planes); else Draw_IFF_line_HAM(context, buffer+y_pos*line_size, y_pos,real_line_size, real_bit_planes, PCHG_palettes); } } free(buffer); buffer = NULL; } else if (memcmp(section, "BODY", 4) == 0) { long offset = ftell(IFF_file); if (file_size > (unsigned long)(offset + section_size + 8)) { // Chunk RAST is placed AFTER the BODY, but we need the palette now to decode the image // In addition, some files break the IFF standard by not aligning // the chunk on word boundaries. fseek(IFF_file, section_size, SEEK_CUR); Read_bytes(IFF_file, section, 1); if (section[0] == 'R') // we are good Read_bytes(IFF_file, section + 1, 3); // read the remaining 3 bytes else // skip 1 byte Read_bytes(IFF_file, section, 4); // read 4 bytes if (memcmp(section, "RAST", 4) == 0) { dword rast_size; Read_dword_be(IFF_file, &rast_size); RAST_chunk_decode(context, IFF_file, rast_size, &PCHG_palettes); if (PCHG_palettes != NULL) bpp = 12; } fseek(IFF_file, offset, SEEK_SET); // rewind } if (header.Width == 0 || header.Height == 0) break; Original_screen_X = header.X_screen; Original_screen_Y = header.Y_screen; Pre_load(context, header.Width, header.Height, file_size, iff_format, ratio, bpp); context->Background_transparent = header.Mask == 2; context->Transparent_color = context->Background_transparent ? header.Transp_col : 0; Set_image_mode(context, IMAGE_MODE_ANIMATION); if (iff_format == FORMAT_LBM) // "ILBM": InterLeaved BitMap { LBM_Decode(context, IFF_file, header.Compression, Image_HAM, stored_bit_planes, real_bit_planes, PCHG_palettes); } else // "PBM ": Packed BitMap { PBM_Decode(context, IFF_file, header.Compression, context->Width, context->Height); } if (ftell(IFF_file) & 1) fseek(IFF_file, 1, SEEK_CUR); // SKIP one byte if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE) { break; // dont load animations in Preview mode } } else { // skip section GFX2_Log(GFX2_INFO, "IFF : Skip unknown section '%.4s' of %u bytes at offset %06X\n", section, section_size, ftell(IFF_file)); if (section_size < 256) { byte buffer[256]; Read_bytes(IFF_file, buffer, (section_size+1)&~1); GFX2_LogHexDump(GFX2_DEBUG, "", buffer, 0, (section_size+1)&~1); } else fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); } } } fclose(IFF_file); IFF_file = NULL; } else File_error=1; if (previous_frame) free(previous_frame); if (anteprevious_frame) free(anteprevious_frame); while (PCHG_palettes != NULL) { T_IFF_PCHG_Palette * next = PCHG_palettes->Next; free(PCHG_palettes); PCHG_palettes = next; } } // -- Sauver un fichier au format IFF --------------------------------------- /// Save IFF file (LBM or PBM) void Save_IFF(T_IO_Context * context) { FILE * IFF_file; T_IFF_Header header; word x_pos; word y_pos; byte temp_byte; int i; int palette_entries; byte bit_depth; long body_offset = -1; int is_ehb = 0; // Extra half-bright if (context->Format == FORMAT_LBM) { // Check how many bits are used by pixel colors temp_byte = 0; for (y_pos=0; y_posHeight; y_pos++) for (x_pos=0; x_posWidth; x_pos++) temp_byte |= Get_pixel(context, x_pos,y_pos); bit_depth=0; do { bit_depth++; temp_byte>>=1; } while (temp_byte); if (bit_depth == 6) { for (i = 0; i < 32; i++) { if ( (context->Palette[i].R >> 5) != (context->Palette[i+32].R >> 4) || (context->Palette[i].G >> 5) != (context->Palette[i+32].G >> 4) || (context->Palette[i].B >> 5) != (context->Palette[i+32].B >> 4)) break; } if (i == 32) is_ehb = 1; } GFX2_Log(GFX2_DEBUG, "Saving ILBM with bit_depth = %d %s\n", bit_depth, is_ehb ? "(Extra Half-Bright)" : ""); palette_entries = 1 << (bit_depth - is_ehb); } else // FORMAT_PBM { bit_depth = 8; palette_entries = 256; } File_error=0; // Ouverture du fichier if ((IFF_file=Open_file_write(context))) { setvbuf(IFF_file, NULL, _IOFBF, 64*1024); Write_bytes(IFF_file,"FORM",4); Write_dword_be(IFF_file,0); // On mettra la taille à jour à la fin if (context->Format == FORMAT_LBM) Write_bytes(IFF_file,"ILBM",4); else Write_bytes(IFF_file,"PBM ",4); Write_bytes(IFF_file,"BMHD",4); Write_dword_be(IFF_file,20); header.Width=context->Width; header.Height=context->Height; header.X_org=0; header.Y_org=0; header.BitPlanes=bit_depth; header.Mask=context->Background_transparent ? 2 : 0; header.Compression=1; header.Pad1=0; header.Transp_col=context->Background_transparent ? context->Transparent_color : 0; header.X_aspect=10; // Amiga files are usually 10:11 header.Y_aspect=10; switch (context->Ratio) { case PIXEL_SIMPLE: case PIXEL_DOUBLE: case PIXEL_TRIPLE: case PIXEL_QUAD: default: break; case PIXEL_WIDE: case PIXEL_WIDE2: header.X_aspect *= 2; // 2:1 break; case PIXEL_TALL3: // 3:4 header.X_aspect = (header.X_aspect * 15) / 10; // *1.5 #if defined(__GNUC__) && (__GNUC__ >= 7) __attribute__ ((fallthrough)); #endif case PIXEL_TALL: case PIXEL_TALL2: header.Y_aspect *= 2; // 1:2 break; } header.X_screen = context->Width;// Screen_width?; header.Y_screen = context->Height;// Screen_height?; Write_word_be(IFF_file,header.Width); Write_word_be(IFF_file,header.Height); Write_word_be(IFF_file,header.X_org); Write_word_be(IFF_file,header.Y_org); Write_bytes(IFF_file,&header.BitPlanes,1); Write_bytes(IFF_file,&header.Mask,1); Write_bytes(IFF_file,&header.Compression,1); Write_bytes(IFF_file,&header.Pad1,1); Write_word_be(IFF_file,header.Transp_col); Write_bytes(IFF_file,&header.X_aspect,1); Write_bytes(IFF_file,&header.Y_aspect,1); Write_word_be(IFF_file,header.X_screen); Write_word_be(IFF_file,header.Y_screen); if (context->Format == FORMAT_LBM) { dword ViewMode = 0; // HIRES=0x8000 LACE=0x4 HAM=0x800 HALFBRITE=0x80 if (is_ehb) ViewMode |= 0x80; if (context->Width > 400) { ViewMode |= 0x8000; if (context->Width > 800) ViewMode |= 0x20; // Super High-Res } if (context->Height > 300) ViewMode |= 0x4; Write_bytes(IFF_file, "CAMG", 4); Write_dword_be(IFF_file, 4); // Section size Write_dword_be(IFF_file, ViewMode); } Write_bytes(IFF_file,"CMAP",4); Write_dword_be(IFF_file,palette_entries*3); Write_bytes(IFF_file,context->Palette,palette_entries*3); if (context->Comment[0]) // write ANNO { dword comment_size; Write_bytes(IFF_file,"ANNO",4); // Chunk name comment_size = strlen(context->Comment); // NULL termination is not needed Write_dword_be(IFF_file, comment_size); // Section size Write_bytes(IFF_file, context->Comment, comment_size); if (comment_size&1) Write_byte(IFF_file, 0); // align to WORD boundaries } for (i=0; iColor_cycles; i++) { word flags=0; flags|= context->Cycle_range[i].Speed?1:0; // Cycling or not flags|= context->Cycle_range[i].Inverse?2:0; // Inverted Write_bytes(IFF_file,"CRNG",4); Write_dword_be(IFF_file,8); // Section size Write_word_be(IFF_file,0); // Padding Write_word_be(IFF_file,context->Cycle_range[i].Speed*78); // Rate Write_word_be(IFF_file,flags); // Flags Write_byte(IFF_file,context->Cycle_range[i].Start); // Min color Write_byte(IFF_file,context->Cycle_range[i].End); // Max color // No padding, size is multiple of 2 } body_offset = ftell(IFF_file); Write_bytes(IFF_file,"BODY",4); Write_dword_be(IFF_file,0); // On mettra la taille à jour à la fin if (context->Format == FORMAT_LBM) { byte * buffer; short line_size; // Size of line in bytes short plane_line_size; // Size of line in bytes for 1 plane short real_line_size; // Size of line in pixels T_PackBits_data pb_data; // Calcul de la taille d'une ligne ILBM (pour les images ayant des dimensions exotiques) real_line_size = (context->Width+15) & ~15; plane_line_size = real_line_size >> 3; // 8bits per byte line_size = plane_line_size * header.BitPlanes; buffer=(byte *)malloc(line_size); // Start encoding PackBits_pack_init(&pb_data, IFF_file); for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) { // Dispatch the pixel into planes memset(buffer,0,line_size); for (x_pos=0; x_posWidth; x_pos++) Set_IFF_color(buffer, x_pos, Get_pixel(context, x_pos,y_pos), real_line_size, header.BitPlanes); if (context->Width&1) // odd width fix Set_IFF_color(buffer, x_pos, 0, real_line_size, header.BitPlanes); // encode the resulting sequence of bytes if (header.Compression) { int plane_width=line_size/header.BitPlanes; int plane; for (plane=0; planeHeight) && (!File_error)); y_pos++) { for (x_pos=0; ((x_posWidth) && (!File_error)); x_pos++) { if (PackBits_pack_add(&pb_data, Get_pixel(context, x_pos, y_pos)) < 0) File_error = 1; } if (context->Width & 1) // odd width fix { if (PackBits_pack_add(&pb_data, Get_pixel(context, context->Width - 1, y_pos)) < 0) File_error = 1; } if (!File_error) { if (PackBits_pack_flush(&pb_data) < 0) File_error = 1; } } } // Now update FORM and BODY size if (!File_error) { long file_size = ftell(IFF_file); if (file_size&1) { // PAD to even file size if (! Write_byte(IFF_file,0)) File_error=1; } // Write BODY size fseek(IFF_file, body_offset + 4, SEEK_SET); Write_dword_be(IFF_file, file_size-body_offset-8); // Write FORM size file_size = (file_size+1)&~1; fseek(IFF_file,4,SEEK_SET); Write_dword_be(IFF_file,file_size-8); } fclose(IFF_file); if (File_error != 0) // remove the file if there have been an error Remove_file(context); } else File_error=1; } /** @} */