| 
			| 
					
						| pitpan | 
								|  | «  : 18 de Mayo de 2007, 09:13:38 am » |  | 
 
 Well, this is it. A very simple yet functional image converter for TMS9918. At the moment it only supports TGA format, 24bpp uncompressed, up to 512x512 pixels. It converts the image to the best non-dithered solution that verifies the TMS9918 colour restrictions (2 colores for each 8x1 pixel block). The conversion algorithm is quite simple and it requieres a high clocked computer. The output of the program is the following: - file.CHR - uncompressed TMS9918 pattern information - file.CLR - uncompressed TMS9918 colour information - file_TMS.TGA - TGA including the converted image Enjoy. Any suggestions will be welcome. Please note that the RGB values included are very approximated. Better RGB palettes will output better fit images.    /*---------------------------------------------------------------
 TMSOPT v.0.02 - Eduardo A. Robsy Petrus, 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
 ---------------------------------------------------------------
 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>
 
 // 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 score[256][512],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.01 - TGA 24bpp to TMS9918 converter\nCoded by Eduardo A. Robsy Petrus, 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;x<MAXX;x++)
 for (k=0;k<3;k++)
 image[x][y][2-k]=fgetc(file);
 
 // Close file
 
 fclose(file);
 
 // Information
 
 printf("Converting %s (%i,%i) to TMS9918 format...    ",argv[1],MAXX,MAXY);
 
 // Create TMS output files (CHR, CLR)
 
 argv[1][strlen(argv[1])-3]='C';
 argv[1][strlen(argv[1])-2]='H';
 argv[1][strlen(argv[1])-1]='R';
 
 CHR=fopen(argv[1],"wb");
 
 argv[1][strlen(argv[1])-2]='L';
 CLR=fopen(argv[1],"wb");
 
 // Image processing
 
 for (y=0;y<(MAXY+7)>>3;y++)
 for (x=0;x<(MAXX+7)>>3;x++)
 for (j=0;j<8;j++)
 
 // Generate alternatives
 
 {
 bp=0;bc=0;
 score[0][0]=-1;
 for (p=0;p<256;p++)
 for (c=0x12;c<256;c++)
 if (c>>4<c&15)
 
 // Score calculation
 
 {
 score[p][c]=0;
 n=p;
 for (i=0;i<8;i++)
 {
 for (k=0;k<3;k++)
 score[p][c]+=abs(palette[(n&0x80)?c>>4:c&0x0f][k]-image[x<<3|i][y<<3|j][k]);
 n<<=1;
 }
 
 // Keep best solution found
 
 if (score[p][c]<score[bp][bc])
 {
 bp=p;bc=c;
 }
 total++;
 if (score[bp][bc]<6) {c=256;p=256;}
 }
 
 // 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++;
 
 }
 
 // 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<MAXX;x++)
 for (k=0;k<3;k++)
 fputc(image[x][y][2-k],file);
 
 // Close file
 
 fclose(file);
 
 // Prompt elapsed time
 
 printf("%.2f million combinations analysed in %.2f seconds",total/1e6,(float)clock()/(float)CLOCKS_PER_SEC);
 
 }
 |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| pitpan | 
								|  | « Respuesta #1 : 18 de Mayo de 2007, 09:18:31 am » |  | 
 
 By the way, I've only tested it with TGAs created by Photoshop. I haven't reviewed if other applications would use the same TGA header format   . If you do, please let me know the result. |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| pitpan | 
								|  | « Respuesta #2 : 18 de Mayo de 2007, 05:46:31 pm » |  | 
 
 Check some examples:  |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #3 : 18 de Mayo de 2007, 06:19:04 pm » |  | 
 
 You can optimize your inner code a lot!!
 1) why do you store all the tests situation in matrix score[][] ?
 you do not need it: simply evalue the current score, if it is better than the previous keep it, otherwise go haed
 
 2) why do you compute all the color/pattern combinations?
 your program tests all the 105*256=26880 combinations....
 you do not need it.
 
 do a outer loop on the 105 color couples
 like:
 for (c1=1;c1<15;c1++)
 for (c2=c1+1;c2<15;c2++)
 {
 etc.
 }
 
 
 In the inner loop do not scan all the 256 patterns!
 do this :
 compute the distance of the color of each point from c1, (costs 8 diffences + 8 products if you use squared errors)
 compute the distance of the color of each point from c2, (costs 8 diffences + 8 products if you use squared errors)
 
 select the minimum between the two sets of values for each point. (costs 8 comparisons)
 the sum of the minima is the current score (costs 7 sums)
 the results of your comparisons forms the current pattern p (the best given c1 and c2)
 
 This cuts the time a lot: instead of doing 256 tests (like you do now)
 you compute only 8+8 differences, 8 comparisons and 7 sums (+ 16 producs if you use squared errors)
 
 
 |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #4 : 19 de Mayo de 2007, 10:11:40 am » |  | 
 
 Now Floyd works!! minor adjustments (and testing) are still required.... /*---------------------------------------------------------------
 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;x<MAXX;x++)
 for (k=0;k<3;k++)
 image[x][y][2-k]=((short)fgetc(file))*scale;        // Scale image
 
 // Close file
 
 fclose(file);
 
 // Information
 
 printf("Converting %s (%i,%i) to TMS9918 format...    ",argv[1],MAXX,MAXY);
 
 // Create TMS output files (CHR, CLR)
 
 argv[1][strlen(argv[1])-3]='C';
 argv[1][strlen(argv[1])-2]='H';
 argv[1][strlen(argv[1])-1]='R';
 
 CHR=fopen(argv[1],"wb");
 
 argv[1][strlen(argv[1])-2]='L';
 CLR=fopen(argv[1],"wb");
 
 // Image processing
 
 for (y=0;y<(MAXY+7)>>3;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<bs)
 {
 bs=cs;
 bp=cp;
 bc=c2*16+c1;
 }
 }
 // Here we have the best colors and the best pattern for line j
 
 {
 short newpixel[3], quant_error[3];
 unsigned char bc1 = bc & 15, bc2 = bc/16;
 unsigned char cp = 0;
 
 for (i=0;i<8;i++) {
 int xx = (x<<3)|i;
 int yy = (y<<3)|j;
 
 // Decide again what is the best color between bc1 and bc2 for the current point - now dithered
 
 unsigned int mc1=0,mc2=0;
 
 xx = (DIR) ? (xx) : (MAXX - 1 - xx);
 
 for (k=0;k<3;k++) {
 int  d1 = abs(palette[bc1][k]-image[xx][yy][k]);
 int  d2 = abs(palette[bc2][k]-image[xx][yy][k]);
 mc1+=d1*d1;
 mc2+=d2*d2;
 }
 if (mc1>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<MAXX;x++)
 for (k=0;k<3;k++)
 fputc(inrange8(image[x][y][2-k]/scale),file);       // Scale to char
 
 // Close file
 
 fclose(file);
 
 // Prompt elapsed time
 
 printf("%.2f million combinations analysed in %.2f seconds",total/1e6,(float)clock()/(float)CLOCKS_PER_SEC);
 
 }
 
 
 
 Here the full package for who is interested in some testinghttp://ragozini.googlepages.com/sc2conv.rar |  
						| 
								|  |  
								| « Última modificación: 20 de Mayo de 2007, 12:01:08 am por ARTRAG » |  En línea | 
 |  |  | 
	| 
			| 
					
						| WYZ 
								Visitante
 | 
								|  | « Respuesta #5 : 19 de Mayo de 2007, 07:48:24 pm » |  | 
 
 Check some examples:
 
 Genial. Fijandose en los detalles como la cara de Roger, o las gafas y los brillos de la calva y la nariz de mortadelo es cuando se da uno cuenta de lo bueno que es. Muy bueno. |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| Darth_Fistro | 
								|  | « Respuesta #6 : 22 de Mayo de 2007, 10:44:14 am » |  | 
 
 Please, post the executable file! Fantastic!    |  
						| 
								|  |  
								|  |  En línea | 
 
 MSX FOREVER (hasta que saquen un ZX81 con TMS, PSG y 64K de RAM) |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #7 : 22 de Mayo de 2007, 11:59:13 am » |  | 
 
 The executable is into the RAR archive Change the extension of the file *.ex_ into *.exe
 |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| Darth_Fistro | 
								|  | « Respuesta #8 : 22 de Mayo de 2007, 12:19:03 pm » |  | 
 
 Thanks, ARTRAG!    |  
						| 
								|  |  
								|  |  En línea | 
 
 MSX FOREVER (hasta que saquen un ZX81 con TMS, PSG y 64K de RAM) |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #9 : 22 de Mayo de 2007, 11:56:53 pm » |  | 
 
 new package released:http://ragozini.googlepages.com/vdpenc.zip Anyone willing to test it? IMHO Jannone's conversions are still better (sc2 in the package), I do not know where I am wrong. NOTE The CHR and CLR files now can be b-loaded in screen 2 e.g. 10 screen 2: color 15,0,0 20 bload"lenna_.CHR",s 30 bload"lenna_.CLR",s 40 goto 40 |  
						| 
								|  |  
								|  |  En línea | 
 |  |  | 
	| 
			| 
					
						| jltursan | 
								|  | « Respuesta #10 : 23 de Mayo de 2007, 09:51:38 am » |  | 
 
 Have you tried to contact with Rafael himself?, maybe he can throw some light... |  
						| 
								|  |  
								|  |  En línea | 
 
 Doom dee doom dee doom |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #11 : 23 de Mayo de 2007, 10:48:09 am » |  | 
 
 All problems solved thanks to Rafael Jannone itself: here the full packagehttp://ragozini.googlepages.com/dithering Test and let me know |  
						| 
								|  |  
								| « Última modificación: 24 de Mayo de 2007, 11:57:01 am por ARTRAG » |  En línea | 
 |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #12 : 26 de Mayo de 2007, 04:33:17 pm » |  | 
 
 New algorithm online with perceptually uniform distances in the color spacehttp://ragozini.googlepages.com/dithering /*---------------------------------------------------------------
 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 selected 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<stdio.h>
 #include<time.h>
 #include<limits.h>
 
 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
 
 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;
 }
 
 // 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;x<MAXX;x++)
 for (k=0;k<3;k++)
 image[x+1][y+1][2-k]=((short)fgetc(file))*scale;        // Scale image
 
 for (x=0;x<MAXX;x++)
 for (k=0;k<3;k++)
 image[x][0][k] = image[x][1][k];
 
 for (y=0;y<MAXY;y++)
 for (k=0;k<3;k++)
 image[0][y][k] = image[1][0][k];
 
 
 // Close file
 
 fclose(file);
 
 // Information
 
 printf("Converting %s (%i,%i) to TMS9918 format ",argv[1],MAXX,MAXY);
 printf("in (%i,%i) screen 2 tiles...    ",((MAXX+7)>>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)<<i);
 
 xx++;
 if (mc1>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<bs)
 {
 bs  = cs;
 bv  = cv;
 bc1 = c1;
 bc2 = c2;
 }
 }
 }
 
 // Here we have the best colors and the best pattern for line j
 
 short quant_error;
 
 uint xx = 1+((x<<3));
 
 for (i=0;i<8;i++,xx++)
 for (k=0;k<3;k++)
 {
 // Compute the quantization error
 
 if (bv&(1<<i))
 {
 quant_error = (clamp(image[xx][yy][k]) - palette[bc2][k])/16;
 image[xx][yy][k] = palette[bc2][k];
 }
 else
 {
 quant_error = (clamp(image[xx][yy][k]) - palette[bc1][k])/16;
 image[xx][yy][k] = palette[bc1][k];
 }
 
 // Spread the quantization error
 
 short q2 = quant_error<<1;
 image[xx+1][yy+1][k] = clamp(image[xx+1][yy+1][k])+ quant_error; // 1 *
 quant_error += q2 ;
 image[xx-1][yy+1][k] = clamp(image[xx-1][yy+1][k])+ quant_error; // 3 *
 quant_error += q2 ;
 image[xx+0][yy+1][k] = clamp(image[xx+0][yy+1][k])+ quant_error; // 5 *
 quant_error += q2 ;
 image[xx+1][yy+0][k] = clamp(image[xx+1][yy+0][k])+ quant_error; // 7 *
 }
 
 
 // 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");
 
 
 // Create TMS output files (CHR, CLR)
 
 argv[1][strlen(argv[1])-3]='C';
 argv[1][strlen(argv[1])-2]='H';
 argv[1][strlen(argv[1])-1]='R';
 CHR=fopen(argv[1],"wb");
 
 argv[1][strlen(argv[1])-2]='L';
 CLR=fopen(argv[1],"wb");
 
 fputc(0xFE,CLR);    // Binary data
 fputc(0x00,CLR);    // Start at 2000h
 fputc(0x20,CLR);
 fputc(0xFF,CLR);    // Stop at 37FFh
 fputc(0x37,CLR);
 fputc(0x00,CLR);    // Run
 fputc(0x00,CLR);
 
 
 fputc(0xFE,CHR);    // Binary data
 fputc(0x00,CHR);    // Start at 0000h
 fputc(0x00,CHR);
 fputc(0xFF,CHR);    // Stop at 17FFh
 fputc(0x17,CHR);
 fputc(0x00,CHR);    // Run
 fputc(0x00,CHR);
 
 // Save best pattern and colour combination
 // NOTE1:
 // THIS PART CAN BE LARGELY CUTTED AND OPTIMIZED REUSING
 // RESULTS FROM THE PREVIOUS LOOP, BUT WHO CARES?
 // NOTE2:
 // This code can be used for conversion without dithering
 
 for (y=0;y<((MAXY+7)>>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<bs)
 {
 bs=cs;
 bp=cp;
 bc=c2*16+c1;
 }
 }
 }
 
 fputc(bc,CLR);
 fputc(bp,CHR);
 }
 
 
 fclose(CHR);
 fclose(CLR);
 
 // 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<MAXX;x++)
 for (k=0;k<3;k++)
 fputc(inrange8(image[1+x][1+y][2-k]/scale),file);       // Scale to char
 
 // Close file
 
 fclose(file);
 
 // Prompt elapsed time
 
 printf("%.2f million combinations analysed in %.2f seconds.\n",total/1e6,(float)clock()/(float)CLOCKS_PER_SEC);
 printf("Note: the .CLR and .CHR files have correct headers only for 256x192 images. \n");
 
 }
 
 |  
						| 
								|  |  
								| « Última modificación: 26 de Mayo de 2007, 04:35:00 pm por ARTRAG » |  En línea | 
 |  |  | 
	| 
			| 
					
						| ARTRAG 
								Visitante
 | 
								|  | « Respuesta #13 : 29 de Julio de 2007, 09:56:41 am » |  | 
 
 |  
						| 
								|  |  
								| « Última modificación: 29 de Julio de 2007, 11:27:38 am por ARTRAG » |  En línea | 
 |  |  | 
	|  |