I am creating an automated tool that produces an fpg archive with 17 maps in it. The first one is the colored foreground, and the other ones are 50% size scale and should become grayscale. unfortunatly for me, I can't use the standard mod_effects grayscale function because my maps are 8 bit. I used to use DIV to this repetitive manual job, but I figured that making an automated tool for this job saves me a lot of manual work and time.
DIV has a very interessting approach to grayscale, it changes the pixels in an image to other color indexes (it picks about 8 gray colors from the palette, found out by checking some sample graphics).
I basically want to recreate this, but so far my results don't look as nice, so I wonder if DIV uses a specific formula to change a colored pixel to a good grayscale equivilant.
Here's a bit from my code that changes the pixels:
// so far so good, we can create an fpg archive
exportfile = fpg_new ();
// apply the malvado.pal color palette to the loaded png/map file.
pal_status = pal_map_assign (0, color_input_map, game.palette);
say("pal_status: "+pal_status);
// create a monochrome copy of the map (changes the colors but preserves the palette) and scale it to 50%.
// create a new map, 50% the size of the original map.
grayscale_copy_map = map_new ((map_info (0, color_input_map , G_WIDTH) / 2),
(map_info (0, color_input_map , G_HEIGHT) / 2),;
say("grayscale_copy_map: "+grayscale_copy_map);
// blit the orignal map on the new map, with a 50% scale
xput_status = map_xput (0, grayscale_copy_map, color_input_map,
map_info (0, grayscale_copy_map , G_X_CENTER), // x posistion
map_info (0, grayscale_copy_map , G_y_CENTER), // y position
0, 50, 0);
say("xput_status: "+xput_status);
// change all the colors to grayscale, preserving the palette.
// because the grayscale() function from mod_effects doesn't work with 8 bit maps we'll have to
// covert every pixel manually to the right color. wich is a bummer.
// therefore, we'll have to change every pixel that is not color value 0, line by line. basically
// it's a pixel swap. we'll reduce the colors to index value 21-31 all of these have r,g,b : 0,0,b
// where b is: 168, 176, 184, 192, 200, 208, 216, 224, 232, 240 and 248. the "b" value for color 21
// is 168, and for color 31 it is 248. this applies only for malvado.pal.
// the trick is to do this in such way that it will look nice. we'll convert every group of 8 colors
// from the 256 color palette to one of these, in the order that they appear. when a pixel has one of
// these colors in the first placem then that pixel stays the way it is. of course a pixel with color 0
// will stay color 0.
// colors found with grapx2 that are used by a grayscale map generated by div with malvado.pal:
// - color 22 : r,g,b, = 0,0,176
// - color 24 : r,g,b, = 0,0,192
// - color 31 : r,g,b, = 0,0,248
// - color 179 : r,g,b, = 0,20,152
// - color 180 : r,g,b, = 0,20,160
// - color 182 : r,g,b, = 0,20,176
// - color 184 : r,g,b, = 0,20,192
// - color 191 : r,g,b, = 0,20,248
// it looks like that these 8 colors are used and they seem to have a pattern. what I noticed when looking
// at malvado.pal, is that when you look at each horizontal line of 16, the darkest color has a small value
// for "b", and the light color the highest value. This apllies for all 16 lines of 16 colors. actually, it
// would be more logical to use 16 grey colors from the palette instead of 8......
// scan the lines from top to botton
FOR (line_count = 0; line_count < map_info (0, grayscale_copy_map , G_HEIGHT); line_count ++)
FOR (row_count = 0; row_count < map_info (0, grayscale_copy_map , G_WIDTH); row_count ++)
say("line_count: "+line_count+" ; row_count: "+row_count);
// change the color of a pixel to one of the 8 grayscale colors, but keep color 0 intact.
IF (map_get_pixel (0, grayscale_copy_map, row_count, line_count) == 0)
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 0);
ELSE
tempcolor = map_get_pixel (0, grayscale_copy_map, row_count, line_count);
rgb_get (map_get_pixel (0, grayscale_copy_map, row_count, line_count), &my_r, &my_g, &my_b);
say("tempcolor (the detected index): "+tempcolor+" ; r="+my_r+"; g="+my_g+" ; b="+my_b);
// compare the g and b values of against the 8 grayscale ones. a bigger value for b or g means that the
// color is lighter, a smaller value means darker. so, for each pixel we compare how dark/light it is
// relative to one of the 8 grayscale colors.
SWITCH (my_b)
CASE 0..20:
SWITCH (my_g)
CASE 0..176:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 22); //22
END
CASE 177..192:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 24); //24
END
CASE 193..248:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 31); //31
END
END
END
CASE 21..255:
SWITCH (my_g)
CASE 0..152:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 179); //179
END
CASE 153..160:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 180); //180
END
CASE 161..176:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 182); //182
END
CASE 177..192:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 184); //184
END
CASE 193..248:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 191); //191
END
END
END
END
END
END
END
The comments in the code are just a part of my thought process, so I would like to know if someone has any ideas on this or knows how DIV actually does this.
In the attached image you'll see two grayscale images, the left one was produced by DIV, and the right one by my code. Both use the exact same palette.
you are mixing C and PRG?
please, show a capture of the palettes that you use...
No, I am not mixing C and PRG, I am simply trying to substitute non-gray colors for gray colors, that all exist within the the same palette.
right... is prg, sorry.
I have solved it! :) It's really not that hard after reading a wikipedia article on the subject and using the weigt factors for the r,g,b components.
I've fiddled a bit with a spreadsheet and selecting some gray colors from the palette. This way, I created a conversion table (it's only for malvado.pal) I still have to create this in a more algorithmic form but this it the code for now:
// scan the lines from top to botton
FOR (line_count = 0; line_count < map_info (0, grayscale_copy_map , G_HEIGHT); line_count ++)
FOR (row_count = 0; row_count < map_info (0, grayscale_copy_map , G_WIDTH); row_count ++)
// change the colors, based on an weight value calculated with a spreadsheet for malvado.pal
// the methode used by the spreadsheet is: there are 28 grayscale colors in the palette, so
// there are about (255-28) / 28 = 8.107 ( colors per one gray color. index 0 is not changed.
// the weight value is based on this formula: (R*0,3) + (G*0,59) + (G*0,11).
// for specic details see these files: src/editor-editart/colorpalette-grayscale-weigtvalues.txt
SWITCH ( map_get_pixel (0, grayscale_copy_map, row_count, line_count) )
CASE 0:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 0);
END
CASE 144,226,227,228,229,230,243,244:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 21);
END
CASE 23,42,61,121,148,204,205,237:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 23);
END
CASE 11,24,59,150,186,201,202,236:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 24);
END
CASE 8,9,17,25,39,40,56,78,79,152,153,197,198,221,235,250:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 25);
END
CASE 7,26,38,77,111,234,253:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 26);
END
CASE 20,72,92,105,124,135,158,216,249:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 28);
END
CASE 29,45,67,68,69,85,86,87,101,102,118,125,137,138,180,213:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 29);
END
CASE 30,46,81,82,99,116,140,211:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 30);
END
CASE 31,96,114,142,170,171,177,209:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 31);
END
CASE 112,176:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 127);
END
CASE 113,127,160,161,162,163:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 127);
END
CASE 143,164,165,166,167,168,169,208:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 177);
END
CASE 47,64,65,80,97,98,115,126,141,172,173,174,175,178,210,247:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 178);
END
CASE 66,83,84,100,117,139,179,212:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 179);
END
CASE 44,48,88,89,103,119,136,214:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 180);
END
CASE 1,2,28,32,33,49,50,70,71,90,91,104,159,181,215,248:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 181);
END
CASE 3,4,19,34,35,51,52,73,93,94,95,106,157,182,217,232:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 182);
END
CASE 5,6,18,27,36,37,53,54,74,75,76,107,108,109,110,123,134,155,156,183,218,219,233,252:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 183);
END
CASE 55,133,154,184,199,220,251,254:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 184);
END
CASE 43,57,122,132,185,192,193,196:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 185);
END
CASE 10,41,58,151,194,195,200,222:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 186);
END
CASE 12, 16,60,131,149,187,203,223:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 187);
END
CASE 13,22,62,130,146,147,188,206:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 188);
END
CASE 14,63,119,120,127,145,189,207,238:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 189);
END
CASE 15,21,190,224,225,240,241,242:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 190);
END
CASE 128,191,231,239,245,246,255:
map_put_pixel (0, grayscale_copy_map, row_count, line_count, 191);
END
END
END
END
The relult can be seen in the attached file. I have also attached an txt export of my spreadsheet, I used it to create weight values
for each color with the formula: (R*0,3) + (G*0,59) + (G*0,11) and then sorted some of the tables to create a lookup table.
Stangly, wikipedia http://en.wikipedia.org/wiki/Grayscale (http://en.wikipedia.org/wiki/Grayscale) mentions several different weight factors for the r,g,b components, but they all have in common that the green component is more dominant because the human eye is the most sensitive to this color.
I have updated the program, I have converted the spreadsheet alogorithm into code. It can detect gray tones from the palette based on a formula and mark those colors as grayscale. The code is a bit too long to put up here, so see attached a textfile. It is a part of another progam, so it does not compile on it's own, but it creates an fpg file with 17 maps, and 16 of them have to be grayscale.
You can find the udated spreadsheet and source in the zip archive as well.