It works only with 8 or 16 bit per pixel.
And it is more than 25MB!!
Only to convert a file!!
About TGA format, it should be 24 bpp uncompressed (Photoshop rules!)
Thanks for your suggestions to improve the routine. They do make sense. Anyway, I don't see the point in improving the header selection. If the linker is good enough, all unused functions are linked. By the way, my own executable is something like 7 KB. Tiny enough!
Regarding the interest of storing scores is to be able to compare if there are more than one combination with the same score. It is not implemented yet.
And again, it is just my first attemp! I'm sure that I can be improved a lot. That's why I published just the source for you to help . I'll try to publish a new version with your suggestions.
Thank you guys!
This is what I was meaning. This code runs in 1/200 of the time of the first version and the result is exactly the same
// Headers! #include<stdio.h> #include<time.h> // Just one function for everything main(int argc, char **argv) { // Vars FILE *file,*CHR,*CLR; int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY; unsigned int n,total=0,done=0,size; char *name; unsigned char image[512][512][3],header[18],palette[16][3]= // TMS9918 RGB palette - approximated 50Hz PAL values {{0x00,0x00,0x00}, // 0 Transparent - black (not used) {0x00,0x00,0x00}, // 1 Black {0x12,0xFF,0x00}, // 2 Green {0x76,0xFF,0x6F}, // 3 Light green {0x12,0x00,0xFF}, // 4 Blue {0x69,0x69,0xFF}, // 5 Light blue {0xA1,0x10,0x10}, // 6 Dark red {0x00,0xFF,0xFC}, // 7 Cyan {0xFF,0x00,0x00}, // 8 Red {0xFF,0x80,0x80}, // 9 Light red {0xFF,0xEA,0x00}, // 10 Yellow {0xFF,0xFB,0x80}, // 11 Light yellow {0x1D,0xA8,0x02}, // 12 Dark green {0xFF,0x00,0xFC}, // 13 Magenta {0xCC,0xCC,0xCC}, // 14 Light gray {0xFF,0xFF,0xFF}};// 15 White // Get time clock(); // Application prompt printf("TMSopt v.0.02 - TGA 24bpp to TMS9918 converter\nCoded by Eduardo A. Robsy Petrus & Arturo Ragozini, 2007\n"); // Test if only one command-line parameter is available if (argc==1) { printf("Syntax: TMSopt [file.tga]\n"); return; } // Open source image (TGA, 24-bit, uncompressed) if ((file=fopen(argv[1],"rb"))==NULL) { printf("cannot open %s file!\n",argv[1]); return; } // 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]; if ((n!=2)||(header[2]!=2)||(header[17])||(header[16]!=24)) { printf("Unsupported file format!\n"); return; } // 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; } // Load image data for (y=MAXY-1;y>=0;y--) for (x=0;x3;y++) for (x=0;x<(MAXX+7)>>3;x++) { for (j=0;j<8;j++) // Generate alternatives { unsigned char c1,c2; unsigned long bs=32000; unsigned char bp =0, bc = 0; for (c1=1;c1<16;c1++) for (c2=c1+1;c2<16;c2++) { unsigned int cs = 0; unsigned int cp = 0; for (i=0;i<8;i++) { unsigned int mc1=0,mc2=0; for (k=0;k<3;k++) { mc1+=abs(palette[c1][k]-image[x<<3|i][y<<3|j][k]); mc2+=abs(palette[c2][k]-image[x<<3|i][y<<3|j][k]); } if (mc1>mc2) { cp = (cp<<1) | (0x01); cs += mc2; } else { cp = (cp<<1) | (0x00); cs += mc1; } } if (cs 4:bc&0x0f][k]; n<<=1; } // Update status counter if (done*100/size<(done+1)*100/size) printf("\b\b\b%2i%%",100*done/size); done++; total++; } } // Conversion done printf("\b\b\bOk \n"); // Generate new name name=malloc(0x100); argv[1][strlen(argv[1])-4]=0; strcpy(name,argv[1]); strcat(name,"_tms.tga"); // Save file header file=fopen(name,"wb"); for (i=0;i<18;i++) fputc(header[i],file); // Save image data for (y=MAXY-1;y>=0;y--) for (x=0;x Some cleanup is needed in order to skip variables now not necessary anymore
Now it's time for Floyd...
Now Floyd works!!
But some adjustments are required....
// Headers! #include<stdio.h> #include<time.h> #define uprange(t) (t>255) ? 255:(t) #define inrange(t) (t<0) ? 0:(uprange(t)) // Just one function for everything main(int argc, char **argv) { // Vars FILE *file,*CHR,*CLR; int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY; unsigned int n,total=0,done=0,size; char *name; unsigned char image[512][512][3],header[18],palette[16][3]= // TMS9918 RGB palette - approximated 50Hz PAL values {{0x00,0x00,0x00}, // 0 Transparent - black (not used) {0x00,0x00,0x00}, // 1 Black {0x12,0xFF,0x00}, // 2 Green {0x76,0xFF,0x6F}, // 3 Light green {0x12,0x00,0xFF}, // 4 Blue {0x69,0x69,0xFF}, // 5 Light blue {0xA1,0x10,0x10}, // 6 Dark red {0x00,0xFF,0xFC}, // 7 Cyan {0xFF,0x00,0x00}, // 8 Red {0xFF,0x80,0x80}, // 9 Light red {0xFF,0xEA,0x00}, // 10 Yellow {0xFF,0xFB,0x80}, // 11 Light yellow {0x1D,0xA8,0x02}, // 12 Dark green {0xFF,0x00,0xFC}, // 13 Magenta {0xCC,0xCC,0xCC}, // 14 Light gray {0xFF,0xFF,0xFF}};// 15 White // Get time clock(); // Application prompt printf("TMSopt v.0.03 - TGA 24bpp to TMS9918 converter\nCoded by Eduardo A. Robsy Petrus & Arturo Ragozini, 2007\n"); // Test if only one command-line parameter is available argc = 2; argv[1] = malloc(20); argv[1][0] = 'e'; argv[1][1] = 'a'; argv[1][2] = 'r'; argv[1][3] = 't'; argv[1][4] = 'h'; argv[1][5] = '.'; argv[1][6] = 't'; argv[1][7] = 'g'; argv[1][8] = 'a'; argv[1][9] = 0; if (argc==1) { printf("Syntax: TMSopt [file.tga]\n"); return; } // Open source image (TGA, 24-bit, uncompressed) if ((file=fopen(argv[1],"rb"))==NULL) { printf("cannot open %s file!\n",argv[1]); return; } // 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]; if ((n!=2)||(header[2]!=2)||(header[17])||(header[16]!=24)) { printf("Unsupported file format!\n"); return; } // 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; } // Load image data for (y=MAXY-1;y>=0;y--) for (x=0;x3;y++) for (x=0;x<(MAXX+7)>>3;x++) for (j=0;j<8;j++) { // Generate alternatives unsigned char c1,c2; unsigned long bs=32000; unsigned char bp =0, bc = 0; for (c1=1;c1<16;c1++) for (c2=c1+1;c2<16;c2++) { unsigned int cs = 0; unsigned int cp = 0; for (i=0;i<8;i++) { unsigned int mc1=0,mc2=0; for (k=0;k<3;k++) { mc1+=abs(palette[c1][k]-image[x<<3|i][y<<3|j][k]); mc2+=abs(palette[c2][k]-image[x<<3|i][y<<3|j][k]); } if (mc1>mc2) { cp = (cp<<1) | (0x01); cs += mc2; } else { cp = (cp<<1) | (0x00); cs += mc1; } } if (cs mc2) { cp = (cp<<1) | (0x01); for (k=0;k<3;k++) newpixel[k] = palette[bc2][k]; } else { cp = (cp<<1) | (0x00); for (k=0;k<3;k++) newpixel[k] = palette[bc1][k]; } // save the current point for (k=0;k<3;k++) oldpixel[k] = image[x<<3|i][y<<3|j][k]; // store the current point - now quantized for (k=0;k<3;k++) image[x<<3|i][y<<3|j][k] = newpixel[k]; // compute the quantization error for (k=0;k<3;k++) quant_error[k] = oldpixel[k] - newpixel[k]; // spread the quantization error if ((x<<3|(i+1)) 4:bc&0x0f][k]; // n<<=1; // } // Update status counter if (done*100/size<(done+1)*100/size) printf("\b\b\b%2i%%",100*done/size); done++; total++; } // Conversion done printf("\b\b\bOk \n"); // Generate new name name=malloc(0x100); argv[1][strlen(argv[1])-4]=0; strcpy(name,argv[1]); strcat(name,"_tms.tga"); // Save file header file=fopen(name,"wb"); for (i=0;i<18;i++) fputc(header[i],file); // Save image data for (y=MAXY-1;y>=0;y--) for (x=0;x
It could use the <string.h> and <malloc.h> headers for the strlen/strcpy/strcat and malloc functions.That'll cost ya one beer
Okay, I'll bite...
"why?"Because I can't read *_* (I read 'I ' instead of 'It'). Nevah mind.
Very cool, ARTRAG!
I was about to code the 2nd alternative when you came with that one and then another one with Floyd-Steinberg!
Amazing!
note that this Floyd has some "rounding" problems.
The errors loose a lot of usefully digits.
I'm on a version with better math proprieties.
FULLY WORKING NOW!!
Pitpan, have a look!
PS I tested it only on one image, and I have no idea about the CHR & CLR files
l wait for feedback
/* --------------------------------------------------------------- TMSOPT v.0.04 - Eduardo A. Robsy Petrus & Arturo Ragozini, 2007 --------------------------------------------------------------- TGA image converter (24 bpp, uncompressed) to TMS9918 format --------------------------------------------------------------- Overview --------------------------------------------------------------- Selects the best local solution for each 8x1 pixel block Optimization uses the following algorithm: (a) For each pixel, RGB differences are calculated - All RGB channels have the same weight - It uses absolute error instead of squared error (b) This error value is added for all pixels in each 8x1 block (c) The CHR-CLR combination with lowest error rate is selected It is a brute-force method, computationally very expensive. Some patience and a high-clocked CPU computed is recommended. --------------------------------------------------------------- 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! --------------------------------------------------------------- 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 --------------------------------------------------------------- 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 author, though. --------------------------------------------------------------- */ // Headers! #include<stdio.h> #include<time.h> #include<limits.h> #define scale 32 #define inrange8(t) ((t)<0) ? 0 :(((t)>255) ? 255:(t)) #define clamp(t) ((t)<0) ? 0 :(((t)>255*scale) ? 255*scale : (t)) ///////////////// // scanning direction // DIR=1 -> // DIR=0 <- #define DIR 1 ///////////////////////////// // switch palette MSX1 MSX2 // (approssimate) #define MSX1 // Just one function for everything main(int argc, char **argv) { // Vars FILE *file,*CHR,*CLR; int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY; unsigned int n,total=0,done=0,size; char *name; short image[512][512][3],header[18],palette[16][3]; // TMS9918 RGB palette - approximated 50Hz PAL values unsigned int pal[16][3]= { #ifdef MSX1 { 0,0,0}, { 0,0,0}, { 33,200,66}, { 94,220,120}, { 84,85,237}, { 125,118,252}, { 212,82,77}, { 66,235,245}, { 252,85,84}, { 255,121,120}, { 212,193,84}, { 230,206,128}, { 33,176,59}, { 201,91,186}, { 204,204,204}, { 255,255,255} #endif #ifdef MSX2 {0x00,0x00,0x00}, // 0 Transparent - black (not used) {0x00,0x00,0x00}, // 1 Black {0x12,0xFF,0x00}, // 2 Green {0x76,0xFF,0x6F}, // 3 Light green {0x12,0x00,0xFF}, // 4 Blue {0x69,0x69,0xFF}, // 5 Light blue {0xA1,0x10,0x10}, // 6 Dark red {0x00,0xFF,0xFC}, // 7 Cyan {0xFF,0x00,0x00}, // 8 Red {0xFF,0x80,0x80}, // 9 Light red {0xFF,0xEA,0x00}, // 10 Yellow {0xFF,0xFB,0x80}, // 11 Light yellow {0x1D,0xA8,0x02}, // 12 Dark green {0xFF,0x00,0xFC}, // 13 Magenta {0xCC,0xCC,0xCC}, // 14 Light gray {0xFF,0xFF,0xFF} // 15 White #endif }; // 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.04 - TGA 24bpp to TMS9918 converter\nCoded by Eduardo A. Robsy Petrus & Arturo Ragozini, 2007\n"); // Test if only one command-line parameter is available // Guess the name of the image I used for testing #ifdef DEBUG argc = 2; argv[1] = malloc(20); argv[1][0] = 'e'; argv[1][1] = 'a'; argv[1][2] = 'r'; argv[1][3] = 't'; argv[1][4] = 'h'; argv[1][5] = '.'; argv[1][6] = 't'; argv[1][7] = 'g'; argv[1][8] = 'a'; argv[1][9] = 0; #endif if (argc==1) { printf("Syntax: TMSopt [file.tga]\n"); return; } // Open source image (TGA, 24-bit, uncompressed) if ((file=fopen(argv[1],"rb"))==NULL) { printf("cannot open %s file!\n",argv[1]); return; } // 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; } // 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; } // Load image data for (y=MAXY-1;y>=0;y--) for (x=0;x3;y++) for (x=0;x<(MAXX+7)>>3;x++) for (j=0;j<8;j++) { // Generate alternatives unsigned char c1,c2; unsigned int bs = INT_MAX; unsigned char bp = 0, bc = 0; for (c1=1;c1<16;c1++) for (c2=c1+1;c2<16;c2++) { unsigned int cs = 0; unsigned int cp = 0; for (i=0;i<8;i++) { int xx = (x<<3)|i; int yy = (y<<3)|j; unsigned int mc1=0,mc2=0; xx = (DIR) ? (xx) : (MAXX - 1 - xx); for (k=0;k<3;k++) { int d1 = abs(palette[c1][k]-image[xx][yy][k]); int d2 = abs(palette[c2][k]-image[xx][yy][k]); mc1+=d1*d1; mc2+=d2*d2; } if (mc1>mc2) { cp = (cp<<1) | (0x01); cs += mc2; } else { cp = (cp<<1) | (0x00); cs += mc1; } } if (cs mc2) { cp = (cp<<1) | (0x01); for (k=0;k<3;k++) newpixel[k] = palette[bc2][k]; } else { cp = (cp<<1) | (0x00); for (k=0;k<3;k++) newpixel[k] = palette[bc1][k]; } // compute the quantization error for (k=0;k<3;k++) quant_error[k] = image[xx][yy][k] - newpixel[k]; // store the current point - now quantized for (k=0;k<3;k++) image[xx][yy][k] = newpixel[k]; // spread the quantization error if (DIR){ if (xx+1<=MAXX-1) for (k=0;k<3;k++) image[xx+1][yy+0][k] = clamp(image[xx+1][yy+0][k]+(7 * quant_error[k])/16); if (yy+1<=MAXY-1) { for (k=0;k<3;k++) image[xx+0][yy+1][k] = clamp(image[xx+0][yy+1][k]+(5 * quant_error[k])/16); if (xx-1 >=0) for (k=0;k<3;k++) image[xx-1][yy+1][k] = clamp(image[xx-1][yy+1][k]+(3 * quant_error[k])/16); if (xx+1 <=MAXX-1) for (k=0;k<3;k++) image[xx+1][yy+1][k] = clamp(image[xx+1][yy+1][k]+(quant_error[k])/16); } } else { if (xx-1>=0) for (k=0;k<3;k++) image[xx-1][yy+0][k] = clamp(image[xx-1][yy+0][k]+(7 * quant_error[k])/16); if (yy+1<=MAXY-1) { for (k=0;k<3;k++) image[xx+0][yy+1][k] = clamp(image[xx+0][yy+1][k]+(5 * quant_error[k])/16); if (xx-1 >=0) for (k=0;k<3;k++) image[xx-1][yy+1][k] = clamp(image[xx-1][yy+1][k]+( quant_error[k])/16); if (xx+1 <=MAXX-1) for (k=0;k<3;k++) image[xx+1][yy+1][k] = clamp(image[xx+1][yy+1][k]+(3 * quant_error[k])/16); } } } // Forget the previous pattern not based on dithering bp = cp; } // Save best pattern and colour combination fputc(bp,CHR); fputc(bc,CLR); // // Replace original image data with TMS result // n=bp; // for (i=0;i<8;i++) // { // for (k=0;k<3;k++) // image[x<<3|i][y<<3|j][k]=palette[n&0x80?bc>>4:bc&0x0f][k]; // n<<=1; // } // Update status counter if (done*100/size<(done+1)*100/size) printf("\b\b\b%2i%%",100*done/size); done++; total++; } // Conversion done printf("\b\b\bOk \n"); // Generate new name name = malloc(0x100); argv[1][strlen(argv[1])-4]=0; strcpy(name,argv[1]); strcat(name,"_tms.tga"); // Save file header file=fopen(name,"wb"); for (i=0;i<18;i++) fputc(header[i],file); // Save image data for (y=MAXY-1;y>=0;y--) for (x=0;x
here the full package for who wants to test it
http://ragozini.googlepages.com/sc2conv.rar