diff --git a/src/miscfileformats.c b/src/miscfileformats.c index 73467792..2f5ead27 100644 --- a/src/miscfileformats.c +++ b/src/miscfileformats.c @@ -2800,6 +2800,240 @@ int Save_C64_hires(T_IO_Context *context, byte saveWhat, byte loadAddr) return 0; } +#ifdef __GNUC__ +/* use GCC built in's */ +#define count_set_bits __builtin_popcount +#define count_trailing_zeros __builtin_ctz +#else +/** + * Count the number of bit sets + */ +static int count_set_bits(unsigned int value) +{ + int count; + + for (count = 0; value != 0; value >>= 1) + count += (value & 1); + return count; +} + +/** + * Count the number of low order zero's before the first bit set + */ +static int count_trailing_zeros(unsigned int value) +{ + int count; + + if (value == 0) + return -1; + for (count = 0; (value & 1) == 0; value >>= 1) + count++; + return count; +} +#endif + +/** + * Save a C64 FLI (Flexible Line Interpretation) picture. + * + * This function is able to save a one layer picture, by finding + * itself the background colors and color RAM value to be used. + * + * The algorithm is : + * - first choose the lowest value for all possible background colors for each line + * - first the lowest value from the possible colors for color RAM + * - encode bitmap and screen RAMs + * + * The algorithm can fail by picking a "wrong" background color for a line, + * that make the choice for the color RAM value of one of the 40 blocks impossible. + * + * @param context the IO context + * @param saveWhat what part of the data to save + * @param loadAddr The load address + */ +int Save_C64_fli_monolayer(T_IO_Context *context, byte saveWhat, byte loadAddr) +{ + FILE * file; + int bx, by; // 4x8 block coordinates + int cx, cy; // coordinates inside block + byte bitmap[8000],screen_ram[1024*8],color_ram[1024]; + byte background[256]; + byte pixel; + + memset(bitmap, 0, sizeof(bitmap)); + memset(screen_ram, 0, sizeof(screen_ram)); + memset(color_ram, 0, sizeof(color_ram)); + memset(background, 0, sizeof(background)); + + for (by = 0; by < 25; by++) + { + word background_possible[8]; + word color_ram_possible[40]; + + for(cy = 0; cy < 8; cy++) + background_possible[cy] = 0xffff; + for(bx = 0; bx < 40; bx++) + color_ram_possible[bx] = 0xffff; + + // first we try to find the background color of the 8 lines + // and the Color RAM (color 11) of the 40 blocks + for(cy = 0; cy < 8; cy++) + { + for(bx = 0; bx < 40; bx++) + { + word colors_used = 0; + int n_color_used; + for(cx = 0; cx < 4; cx++) + { + pixel = Get_pixel(context, bx*4+cx, by*8+cy); + // if (pixel > 15) error + colors_used |= 1 << pixel; + } + n_color_used = count_set_bits(colors_used); + if (n_color_used == 4) + { + background_possible[cy] &= colors_used; // The background is among the color used in this 4x1 block + color_ram_possible[bx] &= colors_used; // The color 11 is among the color used in this 4x1 block + } + } + } + // Choose background (default is black #0) + for(cy = 0; cy < 8; cy++) + { + int n_possible_backgrounds = count_set_bits(background_possible[cy]); + if (n_possible_backgrounds == 0) + { + //ERROR + GFX2_Log(GFX2_WARNING, "Save_C64_fli_monolayer() no possible background color for line %d\n", by*8+cy); + return 1; + } + else + { + // pick the first one + background[by*8+cy] = count_trailing_zeros(background_possible[cy]); +#ifdef _DEBUG + if (background[by*8+cy] != 0) + GFX2_Log(GFX2_DEBUG, " y=%d background_possible=$%04x (count=%d) background color=#%d\n", + by*8+cy, background_possible[cy], n_possible_backgrounds, background[by*8+cy]); +#endif + } + } + // 2nd pass for color RAM values + for(cy = 0; cy < 8; cy++) + { + for(bx = 0; bx < 40; bx++) + { + word colors_used = 0; + int n_color_used; + for(cx = 0; cx < 4; cx++) + { + pixel = Get_pixel(context, bx*4+cx, by*8+cy); + colors_used |= 1 << pixel; + } + colors_used &= ~(1 << background[by*8+cy]); // remove background + n_color_used = count_set_bits(colors_used); + if (n_color_used == 3) + { + color_ram_possible[bx] &= colors_used; // The color 11 is among the color used in this 4x1 block + } + } + } + // choose the color RAM values (default to #0) + for(bx = 0; bx < 40; bx++) + { + int n_possibles_color11 = count_set_bits(color_ram_possible[bx]); + if (n_possibles_color11 == 0) + { + GFX2_Log(GFX2_WARNING, "Save_C64_fli_monolayer() no possible color RAM value for block (%d,%d) coordinates (%d,%d)\n", + bx, by, bx*4, by*8); + return 1; + } + else + { + color_ram[by*40+bx] = count_trailing_zeros(color_ram_possible[bx]); +#ifdef _DEBUG + if (color_ram[by*40+bx] != 0) + GFX2_Log(GFX2_DEBUG, "bx=%d by=%d color_ram_possible=%04x (count=%d) color11=#%d\n", + bx, by, color_ram_possible[bx], n_possibles_color11, color_ram[by*40+bx]); +#endif + } + } + // Now it is possible to encode Screen RAM and Bitmap + for(cy = 0; cy < 8; cy++) + { + for(bx = 0; bx < 40; bx++) + { + byte bits = 0; + byte c[4]; + c[0] = background[by*8+cy]; // color 00 = background + c[1] = c[0]; // color 01 (defaulting to same as background) + c[2] = c[1]; // color 10 (defaulting to same as background) + c[3] = color_ram[by*40+bx]; // color 11 = color RAM value + for(cx = 0; cx < 4; cx++) + { + bits <<= 2; + pixel = Get_pixel(context, bx*4+cx, by*8+cy); + if (pixel == c[0]) // background => color 00 + continue; + if(pixel == c[3]) // color RAM value => color 11 + bits |= 3; + else + { + if (c[1] == c[0]) + c[1] = pixel; // set color 01 = upper nibble of screen RAM + if (pixel == c[1]) + bits |= 1; + else + { + c[2] = pixel; // set color 02 = lower nibble of screen RAM + bits |= 2; + } + } + } + screen_ram[1024*cy + bx + by * 40] = (c[1] << 4) | c[2]; + bitmap[(by*40 + bx)*8 + cy] = bits; + } + } + } + + file = Open_file_write(context); + + if(!file) + { + Warning_message("File open failed"); + File_error = 1; + return 1; + } + + if (loadAddr) + { + Write_byte(file, 0); + Write_byte(file, loadAddr); + } + + if (saveWhat==0) + Write_bytes(file,background,256); // Background colors for lines 0-199 (+ 56bytes padding) + + if (saveWhat==0 || saveWhat==3) + Write_bytes(file,color_ram,1024); // Color RAM (1000 bytes + padding 24) + + if (saveWhat==0 || saveWhat==1) + Write_bytes(file,screen_ram,8192); // Screen RAMs 8 x (1000 bytes + padding 24) + + if (saveWhat==0 || saveWhat==2) + Write_bytes(file,bitmap,8000); // BitMap + + fclose(file); + + return 0; +} + +/** + * Save a C64 multicolor picture + * + * @param context the IO context + * @param saveWhat what part of the data to save + * @param loadAddr The load address + */ int Save_C64_multi(T_IO_Context *context, byte saveWhat, byte loadAddr) { /* @@ -3075,13 +3309,17 @@ void Save_C64(T_IO_Context * context) return; } + GFX2_Log(GFX2_DEBUG, "Save_C64() extension : %s\n", context->File_name + strlen(context->File_name) - 4); if (strcasecmp(context->File_name + strlen(context->File_name) - 4, ".fli") == 0) { // FIXME moving FLI to a separate format in the fileselector would be smarter - File_error = Save_C64_fli(context, saveWhat, loadAddr); - } else if (context->Width==320) + if (Main.backups->Pages->Nb_layers < 3) + File_error = Save_C64_fli_monolayer(context, saveWhat, loadAddr); + else + File_error = Save_C64_fli(context, saveWhat, loadAddr); + } else if (context->Width==320) { File_error = Save_C64_hires(context, saveWhat, loadAddr); - else { + } else { File_error = Save_C64_multi(context, saveWhat, loadAddr); } }