/* --------------------------------------------------------------- TMSOPT v.0.1 - Eduardo A. Robsy Petrus & Arturo Ragozini 2007 Credits to Rafael Jannone for his Floyd-Steinberg implementation --------------------------------------------------------------- TGA image converter (24 bpp, uncompressed) to TMS9918 format --------------------------------------------------------------- Overview --------------------------------------------------------------- Selects the best solution for each 8x1 pixel block Optimization uses the following algorithm: (a) Select one 1x8 block, select a couple of colors, apply Floyd-Steinberg within the block, compute the squared error, repeat for all 105 color combinations, keep the best couple of colors. (b) Apply Floyd-Steinberg to the current 1x8 block with the best two colors seleted before and spread the errors to the adjacent blocks. (c) repeat (a) and (b) on the next 1x8 block, scan all lines. (d) Convert the image in pattern and color definitions (CHR & CLR) To load in MSX basic use something like this: 10 screen 2: color 15,0,0 20 bload"FILE.CHR",s 30 bload"FILE.CLR",s 40 goto 40 --------------------------------------------------------------- Compilation instructions --------------------------------------------------------------- Tested with GCC/Win32 [mingw]: GCC TMSopt.c -oTMSopt.exe -O3 -s It is standard C, so there is a fair chance of being portable! NOTE In the current release the name of the C file has become scr2floyd.c --------------------------------------------------------------- History --------------------------------------------------------------- Ages ago - algorithm created 16/05/2007 - first C version (RAW format) 17/05/2007 - TGA format included, some optimization included 18/05/2007 - Big optimization (200 times faster), support for square errors 19/05/2007 - Floyd-Stenberg added, scaling for better rounding 24/05/2007 - Floyd-Stenberg included in the color optimization. --------------------------------------------------------------- Legal disclaimer --------------------------------------------------------------- Do whatever you want to do with this code/program. Use at your own risk, all responsability would be declined. It would be nice if you credit the authors, though. --------------------------------------------------------------- */ // Headers! #include #include #include #include typedef unsigned int uint; typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; //#define DEBUG #define scale 16 #define inrange8(t) ((t)<0) ? 0 :(((t)>255) ? 255:(t)) #define clamp(t) ((t)<0) ? 0 :(((t)>255*scale) ? 255*scale : (t)) typedef struct { float r, g, b; } RGB; float ColourDistance(RGB e1, RGB e2) { float r,g,b; float rmean; e1.r/=scale; e1.g/=scale; e1.b/=scale; e2.r/=scale; e2.g/=scale; e2.b/=scale; rmean = ( (int)e1.r + (int)e2.r ) / 2 ; r = ((int)e1.r - (int)e2.r); g = ((int)e1.g - (int)e2.g); b = ((int)e1.b - (int)e2.b); // return r*r+g*g+b*b; return ((((512+rmean)*r*r)/256) + 4*g*g + (((767-rmean)*b*b)/256)); } // Just one function for everything int main(int argc, char **argv) { // Vars FILE *file,*CHR,*CLR; int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY; uint n,total=0,done=0,size; char *name; short image[512+2][512+2][3],header[18],palette[16][3]; // TMS9918 RGB palette - approximated 50Hz PAL values uint pal[16][3]= { { 0,0,0}, // 0 Transparent { 0,0,0}, // 1 Black 0 0 0 { 33,200,66}, // 2 Medium green 33 200 66 { 94,220,120}, // 3 Light green 94 220 120 { 84,85,237}, // 4 Dark blue 84 85 237 { 125,118,252}, // 5 Light blue 125 118 252 { 212,82,77}, // 6 Dark red 212 82 77 { 66,235,245}, // 7 Cyan 66 235 245 { 252,85,84}, // 8 Medium red 252 85 84 { 255,121,120}, // 9 Light red 255 121 120 { 212,193,84}, // A Dark yellow 212 193 84 { 230,206,128}, // B Light yellow 230 206 128 { 33,176,59}, // C Dark green 33 176 59 { 201,91,186}, // D Magenta 201 91 186 { 204,204,204}, // E Gray 204 204 204 { 255,255,255} // F White 255 255 255 }; // Scale palette for (i=0;i<16;i++) for (k=0;k<3;k++) palette[i][k] = scale*pal[i][k]; // Get time clock(); // Application prompt printf("TMSopt v.0.1 - TGA 24bpp to TMS9918 converter.\nCoded by Eduardo A. Robsy Petrus & Arturo Ragozini 2007.\n\n"); printf("Credits to Rafael Jannone for his Floyd-Steinberg implementation.\n \n"); // Guess the name of the image I used for testing #ifdef DEBUG argc = 2; argv[1] = malloc(20); argv[1][0] = 'l'; argv[1][1] = 'e'; argv[1][2] = 'n'; argv[1][3] = 'n'; argv[1][4] = 'a'; argv[1][5] = '_'; argv[1][6] = '.'; argv[1][7] = 't'; argv[1][8] = 'g'; argv[1][9] = 'a'; argv[1][10] = 0; #endif // Test if only one command-line parameter is available if (argc==1) { printf("Syntax: TMSopt [file.tga]\n"); return 1; } // Open source image (TGA, 24-bit, uncompressed) if ((file=fopen(argv[1],"rb"))==NULL) { printf("cannot open %s file!\n",argv[1]); return 2; } // Read TGA header for (i=0;i<18;i++) header[i]=fgetc(file); // Check header info for (i=0,n=0;i<12;i++) n+=header[i]; // I deleted the check on n, was it important ? if ((header[2]!=2)||(header[17])||(header[16]!=24)) { printf("Unsupported file format!\n"); return 3; } // Calculate size MAXX=header[12]|header[13]<<8; MAXY=header[14]|header[15]<<8; size=((MAXX+7)>>3)*MAXY; // Check size limits if ((!MAXX)||(MAXX>512)||(!MAXY)||(MAXY>512)) { printf("Unsupported size!"); return 4; } // Load image data for (y=MAXY-1;y>=0;y--) for (x=0;x>3),((MAXY+7)>>3)); // Image processing for (y=0;y<((MAXY+7)>>3);y++) for (j=0;j<8;j++) for (x=0;x<((MAXX+7)>>3);x++) { // Generate alternatives uchar c1, c2; uchar bc1, bc2; uint bv; uint bs = INT_MAX; uint yy = 1+((y<<3)|j); for (c1=1;c1<16;c1++) { RGB cp1 = {palette[c1][0],palette[c1][1],palette[c1][2]}; for (c2=c1+1;c2<16;c2++) { RGB cp2 = {palette[c2][0],palette[c2][1],palette[c2][2]}; uint xx = 1+(x<<3); RGB ppp = {clamp(image[xx][yy][0]),clamp(image[xx][yy][1]),clamp(image[xx][yy][2])}; uint cs = 0; uint cv = 0; for (i=0;i<8;i++) { short e10 = (ppp.r-cp1.r); short e11 = (ppp.g-cp1.g); short e12 = (ppp.b-cp1.b); long mc1 = ColourDistance(cp1,ppp); short e20 = (ppp.r-cp2.r); short e21 = (ppp.g-cp2.g); short e22 = (ppp.b-cp2.b); long mc2 = ColourDistance(cp2,ppp); cs += (mc1>mc2) ? mc2 : mc1; if (cs>bs) break; cv |= ((mc1>mc2)<mc2) { ppp.r = clamp(image[xx][yy][0]) + 7*e20/16; ppp.g = clamp(image[xx][yy][1]) + 7*e21/16; ppp.b = clamp(image[xx][yy][2]) + 7*e22/16; } else { ppp.r = clamp(image[xx][yy][0]) + 7*e10/16; ppp.g = clamp(image[xx][yy][1]) + 7*e11/16; ppp.b = clamp(image[xx][yy][2]) + 7*e12/16; } } if (cs>3);y++) for (x=0;(x<(MAXX+7)>>3);x++) for (j=0;j<8;j++) { uchar c1,c2; uint bs = INT_MAX; uchar bp = 0, bc = 0; uint yy = 1+((y<<3)|j); for (c1=1;c1<16;c1++) { RGB cp1 = {palette[c1][0],palette[c1][1],palette[c1][2]}; for (c2=c1+1;c2<16;c2++) { RGB cp2 = {palette[c2][0],palette[c2][1],palette[c2][2]}; uint cs = 0; uint cp = 0; for (i=0;i<8;i++) { uint xx = 1+((x<<3)|i); RGB ppp = {clamp(image[xx][yy][0]),clamp(image[xx][yy][1]),clamp(image[xx][yy][2])}; long mc1 = ColourDistance(cp1,ppp); long mc2 = ColourDistance(cp2,ppp); cp = (cp<<1) | (mc1>mc2); cs += (mc1>mc2) ? mc2 : mc1; if (cs>bs) break; } if (cs=0;y--) for (x=0;x