Add ability to save C64 FLI pictures for a 1 layer image

add function Save_C64_fli_monolayer()

The algorithm used is :
- first choose the lowest value from all possible background colors for each line
- first the lowest value from the possible colors for color RAM
- encode bitmap and screen RAMs
This commit is contained in:
Thomas Bernard 2018-11-08 15:23:48 +01:00
parent 3720e45246
commit f3ba6d3205
No known key found for this signature in database
GPG Key ID: 0FF11B67A5C0863C

View File

@ -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);
}
}