// MOTION GIF - LZW komprimovana animace v rozliseni 320x180 256 barev avsak // upravena pro prehravani v HICOLOR pro konkretni rezim (32768 barev) // Format /* [ FRAME ] [CHUNK] !pozor delka je 3 bajtova (tj 16MB) 0 - PRADNY CHUNK 1 - LZW FULL SCREEN 2 - MGIF DELTA 3 - HICOLOR PALETE 4 - SOUND TRACK 5 - TEXT TRACK 6 - MGIF COPY 7 - SOUND INIT Popis Chunku: PRAZDNY_CHUNK - muze obsahovat soukrome informace LZW_FULL_SCREEN - ihned za hlavickou nasleduje LZW komprimovany obrazek v rozliseni 320x180 256 barev MGIF_DELTA - cely blok je LZW komprimovany. Po dekomprimace blok obsahuje dva druhy informaci. Prvni DWORD je ofset od pocatku dat ktery ukazuje na zacatek graficke informace. Po tomto DWORDU jsou ulozeny data o umisteni grafickych bodu v obrazu Prvni byte znaci, kolik je nutne preskocit slov. (tj *2 byte) Dalsi byte znaci, kolik je nutne zapsat slov (tj *2 byte) Po tomto bytu je nutne prenest presne tolik slov z grafickeho bloku na obrazovku. Dvojice bajtu se opakuji dokud ani jeden z nich nema nejvyssi 2 bity nastavene. Pak tento byte znaci ze uz na radce nic vic neni. Dolni cast byte (tj 6 bitu) pak udava pocet radku, ktere je treba pre- skocit. (nula=zadny). HICOLOR PALETTE - Prvni byte znamena pocet aktivnich barev. Pak nasleduje paleta ve formatu xRRRRRGGGGGBBBBB. Rozdil oproti vsem beznym paletam je v tom ze neni treba v palete udrzovat barvy, ktere uz na obrazovce jsou, protoze zmena palety se neprojevi na jiz zobrazenem obrazku. SOUND_TRACK - Kazdy frame obsahuje zvukovou stopu. Je to RAW format. Pro verzi MGIF97 zde je zvukova informace ulozena ve zvukove kvalite 16-bit stereo 22000Khz, ovsem komprimovana na 50% jako MUS. TEXT_TRACK - Navic je ve mozne ve frame umistit textovou zpravu (titulek) Prvni WORD udava dobu zobrazeni (pocet frame). Pote nasleduje text zakoncen znakem 0; MGIF_COPY - Za timto CHUNKEM je ulozen blok dat v nezakomprimovanem tvaru(tj 57600byte); */ #include #include #include #include #include #include #include #include "lzw.h" #include #include #include "jpegmgif.h" #define MGIF "MGIF" #define MGIF_Y "97" #define VER 0x100 #define MGIF_EMPTY 0 #define MGIF_LZW 1 #define MGIF_DELTA 2 #define MGIF_PAL 3 #define MGIF_SOUND 4 #define MGIF_TEXT 5 #define MGIF_COPY 6 #define MGIF_SINIT 7 #define FRAME_X 320 #define FRAME_Y 180 #define FRAME_LEN (FRAME_X*FRAME_Y) #define DELTA_LEN (3*FRAME_LEN) #define LZW_LEN (2*FRAME_LEN) #define SOUND_SPEED 44100 #define PRE_SOUND ((256*1024)/2) #define ZTRATA lowq #define BZTRATA colorq #define MAX_FRAME max_fr #define MIN_FRAME min_fr #define BOXES_X 80 #define BOXES_Y 45 #define BOXES_ALL (BOXES_X*BOXES_Y) #define SND_NAME ".SNF" short mult_table[64]; short square_table[4096]; int statpic=0; typedef struct mgif_header { char sign[4]; char year[2]; char eof; word ver; long frames; word snd_chans; int snd_freq; short ampl_table[256]; short reserved[32]; }; word last_frame[FRAME_Y][FRAME_X]; char frame_delta[FRAME_LEN]; char delta_data[DELTA_LEN]; char frame_buffer[FRAME_LEN]; word color_state[32*32+32*32+32*32]; char calc_data[FRAME_LEN]; char flc_paleta[256][3]; int delta_data_size; int frame_delta_size; int speed=0; int global_frame_counter=0; int last_frame_size; int total_frames; int lowq=0; int colorq=0; int max_fr=99999; int min_fr=0; int hranice; int hdiff=0; int difdif=0; TBOX *iib; TBOX *rbb; TBOX *gbb; char *rgb_mem; char frame_step=1; struct mgif_header gh; char lzw_buffer[LZW_LEN]; char pal_use[256],color_map[256]; word hipal[256],max_colors; FILE *mgf; long of_pos; int of_chunks; void open_frame() { of_chunks=0; of_pos=ftell(mgf); fwrite(&of_pos,1,4,mgf); } void close_frame() { long fsize,p; last_frame_size=fsize=(p=ftell(mgf))-of_pos-4; fseek(mgf,of_pos,SEEK_SET); fwrite(&of_chunks,1,1,mgf); fwrite(&fsize,1,3,mgf); fseek(mgf,p,SEEK_SET); total_frames++; } void write_chunk(char action,long size,void *data) { fwrite(&action,1,1,mgf); fwrite(&size,1,3,mgf); fwrite(data,1,size,mgf); of_chunks++; } /* void conv_2_hicolor() { int i,r,g,b; for(i=0;i<256;i++) { r=(flc_paleta[i][0]>>3); g=(flc_paleta[i][1]>>3); b=(flc_paleta[i][2]>>3); hipal[i]=((r<<10)+(g<<5)+b); } } */ void conv_2_rgb() { int i; for(i=0;i<256;i++) { flc_paleta[i][0]=(hipal[i]>>10) & 0x1f; flc_paleta[i][1]=(hipal[i]>>5)& 0x1f; flc_paleta[i][2]=hipal[i]& 0x1f; } } char find_color(word c); //najde barvu v palete a vraci byte #pragma aux find_color parm [eax]=\ "mov edi,offset hipal"\ "mov ecx,256"\ "repne scasw"\ "mov eax,edi"\ "sub eax,offset hipal"\ "shr eax,1"\ "dec eax"\ value [al] modify[edi ecx]; void init_palmap() { int i; memset(pal_use,0,sizeof(pal_use)); // nuluj pristupy k barvam for(i=0;i<256;i++) color_map[i]=find_color(hipal[i]); //redukuj paletu; max_colors=256; // pocet barev zatim 256; } int test_transp(char c,word w) { int r1,g1,b1; int r2,g2,b2; int diff; r1=flc_paleta[c][0]>>3; g1=flc_paleta[c][1]>>3; b1=flc_paleta[c][2]>>3; w&=0x7fff; b2=w;g2=b2>>5;r2=g2>>5;b2&=0x1f;g2&=0x1f; r1-=r2;g1-=g2;b1-=b2; diff=r1*r1+g1*g1+b1*b1; return diff<=hranice?diff:-1; } int change_color(char c1,char c2,int diff1) { int r1,g1,b1; int r3,g3,b3; int r4,g4,b4; int diff2; if (!diff1) return -1; r1=flc_paleta[c1][0]>>3; g1=flc_paleta[c1][1]>>3; b1=flc_paleta[c1][2]>>3; r3=flc_paleta[c2][0]>>3; g3=flc_paleta[c2][1]>>3; b3=flc_paleta[c2][2]>>3; r4=r3-r1;g4=g3-g1;b4=b3-b1; diff2=r4*r4+g4*g4+b4*b4; if (diff2*2=0) {/*if ((i=change_color(c1,c2old,d2))>=0) c1=i,d=0;else */d=1;} else d=0; c2old=c1; if (d && (d2=test_transp(c2,p[1]))>=0) {/*if (!d && (i=change_color(c2,c2old,d2))>=0) c2=i,d=0;else */d=1;} else d=0; c2old=c2; if (d!=skip) { if (skip) *delta++=skc;else *delta++=cpc; skip=!skip;skc=0;cpc=0; } if (!skip) { *data++=c1; *data++=c2; colors2=hipal[c1]+(hipal[c2]<<16); *(long *)p=colors2; cpc++; } else skc++; p+=2; pal_use[c1]=1; pal_use[c2]=1; } if (!skip) *delta++=cpc; if (skc==160 && *(delta-1)!=0xff && delta!=delta_data) delta[-1]++; //preskoc n radek else *delta++=0xc0; //oznac konec radky } delta_data_size=delta-delta_data; frame_delta_size=data-frame_delta; } void reduce_palette() //redukuje paletu na nejmensi nutny pocet barev { int i,j; if (!speed) pal_use[0]=1; for(i=0,j=0;i<256;i++) { if (pal_use[i]) { hipal[j]=hipal[i]; color_map[i]=j++; } } max_colors=j; } void filter_colors(void *block,int size,void *colormap); //prefiltruje blok dat podle color_map #pragma aux filter_colors parm [edi][ecx][ebx]=\ "lp1: mov al,[edi]"\ " xlatb"\ " stosb"\ " loop lp1"\ modify [eax]; void *join_two_blocks(void *d1,void *d2,int siz1,int siz2,long *celk) { long siz; char *d; void *x; siz=siz1+siz2+4; d=(char *)getmem(siz); x=d; memcpy(d,&siz1,4);d+=4; memcpy(d,d1,siz1);d+=siz1; memcpy(d,d2,siz2); *celk=siz; return x; } void create_last_frame(void *source,void *target,void *pal,int size); #pragma aux create_last_frame parm [esi][edi][ebx][ecx]=\ "lp1: lodsb"\ " movzx eax,al"\ " mov eax,[eax*2+ebx]"\ " stosw"\ " loop lp1"\ modify[eax] void create_mgif_pal() { write_chunk(MGIF_PAL,max_colors*2,hipal); } void create_mgif_lzw() { int siz; //conv_2_rgb(); init_palmap(); create_mgif_pal(); filter_colors(frame_buffer,FRAME_LEN,color_map); create_last_frame(frame_buffer,last_frame,hipal,FRAME_LEN); init_lzw_compressor(8); memset(lzw_buffer,0,sizeof(lzw_buffer)); siz=lzw_encode(frame_buffer,lzw_buffer,FRAME_LEN); if (siz>FRAME_LEN) write_chunk(MGIF_COPY,FRAME_LEN,frame_buffer); else write_chunk(MGIF_LZW,siz,lzw_buffer); done_lzw_compressor(8); } void create_color_state() { int i; int r1,g1,b1; int r2,g2,b2; int diff; char *c; word *w; memset(color_state,0,sizeof(color_state)); c=frame_buffer; w=last_frame; for(i=0;i>3; g1=flc_paleta[*c][1]>>3; b1=flc_paleta[*c][2]>>3; b2=*w;g2=b2>>5;r2=g2>>5;b2&=0x1f;g2&=0x1f; r1-=r2;g1-=g2;b1-=b2; diff=abs(r1)+abs(g1<<1)+abs(b1); color_state[diff]++; c++;w++; } } void set_max_color_change(int cchange) { int i,s; s=0;cchange=57600-cchange; for(i=sizeof(color_state)/sizeof(word)-1;s0;i--) s+=color_state[i]; if (s>=cchange) i++; hranice=i+hdiff;if (hranice<0) hranice=0; } int rozptyl(int v1,int v2) { int c1,c2,x,s; x=v1+v2>>1; c1=(x-v1); c2=(x-v2); s=c1*c1+c2*c2; return s; } void create_low_quality() { void *snd;int l; char *sn,*sr,c1,c2; int rs,bs,gs; l=FRAME_LEN; sr=frame_buffer; snd=calc_data; sn=(char *)snd; l--; while (l--) { c1=*sr++; c2=*sr; rs=rozptyl(flc_paleta[c1][0],flc_paleta[c2][0]); gs=rozptyl(flc_paleta[c1][1],flc_paleta[c2][1]); bs=rozptyl(flc_paleta[c1][2],flc_paleta[c2][2]); if (rs+gs+bsFRAME_LEN) { create_mgif_lzw(); return; } if (!frame_delta_size) return; reduce_palette(); filter_colors(frame_delta,frame_delta_size,color_map); d=join_two_blocks(delta_data,frame_delta,delta_data_size,frame_delta_size,&siz); init_lzw_compressor(8); memset(lzw_buffer,0,sizeof(lzw_buffer)); siz=lzw_encode(d,lzw_buffer,siz); done_lzw_compressor(); free(d); if (siz>FRAME_LEN) { create_mgif_lzw(); return; } write_chunk(MGIF_DELTA,siz,lzw_buffer); create_mgif_pal(); } void prepare_for_mjpg() { iib=getmem(sizeof(TBOX)*BOXES_ALL); rbb=getmem(sizeof(TBOX)*BOXES_ALL); gbb=getmem(sizeof(TBOX)*BOXES_ALL); memset(iib,0,sizeof(TBOX)*BOXES_ALL); memset(rbb,0,sizeof(TBOX)*BOXES_ALL); memset(gbb,0,sizeof(TBOX)*BOXES_ALL); ibox=iib; rbbox=rbb; gbbox=gbb; rgb_mem=getmem(FRAME_LEN*3); create_cos_tab(); } void create_jpg_frame() { int i,x,y,a; char *dl=delta_data; char *r=rgb_mem; char *p=frame_buffer; conv_2_rgb(); for(i=0;i0) { open_frame(); create_sound_track(size); close_frame(); celk-=size; } if (celk) { open_frame(); create_sound_track(celk); close_frame(); } } void fill_header(int frames) { memset(&gh,0,sizeof(gh)); strncpy(gh.sign,MGIF,4); strncpy(gh.year,MGIF_Y,2); gh.eof=26; gh.ver=VER; gh.frames=frames; } void show_screen() { word *c; int i,j; word *w; c=last_frame; w=screen; for(i=0;i<180;i++) { for(j=0;j<320;j++) { *w++=*c; *w++=*c; c++; } w+=640; } showview(0,0,640,360); showview(0,400,639,30); } void write_frame_size(int i,int col) { char s[200]; sprintf(s,"SIZE: %05d COLORS: %03d COMP.LEVEL %03d",i,col,hranice);position(0,400);outtext(s); } char *get_pcx_name(char *name,int x) { static char s[256]; char c[10],e[20],*z; int d; z=strchr(name,'#'); if (z==NULL) { memset(s,0,sizeof(s)); strncpy(s,name,240); d=strlen(s); if (x>9999) { s[d-1]=0; sprintf(s,"%s%05d.pcx",s,x); } else sprintf(s,"%s%04d.pcx",s,x); strupr(s); } else { int pocet,i; pocet=0; while(*z) if (*z++=='#') pocet++; sprintf(c,"%%0%dd",pocet); sprintf(e,c,x); strcpy(s,name); z=strchr(s,'#'); for(i=0;iMAX_FRAME) {difdif=difdif<=0?1:difdif+1;hdiff+=difdif;} else if (last_frame_size0) {difdif=difdif>=0?-1:difdif-1;hdiff+=difdif;} else if (hdiff>0) {difdif=difdif>=0?-1:difdif-1;hdiff+=difdif;}else if(hdiff<0) hdiff=0; show_screen (); if (statpic) statpic--;else x+=frame_step; global_frame_counter++; } } void create_mgf_file(char *name) { mgf=fopen(name,"wb+"); fwrite(&gh,1,sizeof(gh),mgf); total_frames=0; prereserve_tracks(); } void close_mgf_file() { fseek(mgf,0,SEEK_SET); fill_header(total_frames); fwrite(&gh,1,sizeof(gh),mgf); fclose(mgf); } void create_mult_and_square() { int i; puts("Creating tables..."); for(i=0;i<64;i++) mult_table[i]=(short)((i-32)*(i-32)); for(i=0;i<4096;i++) square_table[i]=(short)sqrt(i); } extern int boldcz; void script_compress(char *script_name) { FILE *scr,*snf; char s[256],name[256]; char snd_name[256],*c; int i; scr=fopen(script_name,"r"); strcpy(snd_name,script_name); c=strrchr(snd_name,'.'); if (c==NULL) strcat(snd_name,SND_NAME); else strcpy(c,SND_NAME); if (scr==NULL) { printf("Nemohu otev©¡t script: %s\n",script_name); exit(1); } snf=fopen(snd_name,"w"); i=fscanf(scr,"%255s",name); if (i!=1) { printf("Chyba ve script souboru: %s. Prvni mus¡ b˜t jm‚no c¡lov‚ho souboru.\n",scr); exit(1); } i=fscanf(scr,"%d",&speed); create_mgf_file(name); while ((i=fgetc(scr))!=EOF && i!='\n'); i=fgetc(scr); initmode32(); while (i!=EOF) { while (i<33 && i!=EOF) i=fgetc(scr); if (i!='#') { int j=lowq; ungetc(i,scr); i=fscanf(scr,"%s %d %d %d %d",s,&j,&min_fr,&max_fr,&colorq); strupr(s); if (!strcmp(s,"FRAMESTEP")) frame_step=j; else if (!strcmp(s,"STATIC")) statpic=j; else if (i>=1) { lowq=j; if (snf!=NULL) fprintf(snf,"%d ",global_frame_counter); compress_pcx(s); } } else while ((i=fgetc(scr))!=EOF && i!='\n'); i=fgetc(scr); } closemode(); fclose(scr); if (snf!=NULL) fclose(snf); close_mgf_file(); } main(int argc,char *argv[]) { puts("\n(C)1997 Komprimator Motion GIF v0.9 by Ondrej Novak"); if (argc<2) { puts("Pouziti:");putchar('\n');; puts("PCX2MGIF [d:\\path\\]source target.mgf [speed] [lowq] [min_fr] [max_fr] [colorq]");putchar('\n'); puts("source - Jsou 4 znaky kterymi zacina jmeno kazdeho frame (prvni 0). Takto je\n" " mozne vytvorit animaci az o 9999 frames. Pokud je potreba delsi,pak\n" " je ctvrty znak zmenen na cislo a je mozne vytvorit animaci dlouhou\n" " az 99999 frames. Priklad:\n\n" " PCX2MGIF logo logo.mgf \n\n" " Tento zapis vytvori animaci logo.mgf, jenz bude slozena z mnoha\n" " obrazku ve formatu PCX, jejichz jmena budou znit LOGO0000.PCX az\n" " LOGO9999.PCX a dale bude pokracovat LOG10000.PCX az LOG99999.PCX\n\n" "speed - Prehravaci rychlost ve snimkach za sekundu (pro 22050kHz)\n" " Pokud je dosazeno 0 pak neni do animace vkladana zvukova stopa\n" " (Default: 0)\n" "lowq - Nejnizsi mozny rozptyl barev sousednich bodu (snizuje kvalitu\n" " obrazu) (default: 0)\n" "min_fr - Pozadavek na nejmensi velikost frame. Pod touto hranici v \n" " nasledujicim frame snizi velikost ztraty (default: 0)"); cprintf("--- klavesu ---\r");getche(); puts("max_fr - Pozadavek na nejvetsi velikost frame. Pri prekroceni teto \n" " hranice se zvysi ztrata o jeden bod. (default 99999)\n" "colorq - Tato hodnota slouzi k predvidani velikosti ztraty. Udava\n" " kolik bodu z celkoveho poctu zmen ve frame se menit nebude\n" " Program pak vybere ty nejmensi rozdily a ty do rozdilove \n" " mapy nezahrne. (default: 0)\n\n" "Velikost nejmensiho rozdilu je zobrazen pri komprimaci jako treti cislo\n" "Nula udava ze se nezapisuji jen ty body, ktere se nemeni.\n\n" "Pozn1: Bezne neni nutne uvadet parametr colorq protoze stupen ztraty se \n" "voli podle vysledku predchoziho frame. Je dobre ho uvest pri velkych\n" "vykyvech velikosti frame. Tak zhruba pri ñ10KB. Hodnoty parametru se \n" "voli od 25000-35000, cim vyssi, tim vyssi stupen ztraty\n\n" "Pozn2: Pokud nektere parametry vynechas (treba znakem '-', nebo je na konci\n" "neuvedes, dosadi se default hodnoty. Ty jsou zvoleny tak, aby program\n" "nevypocitaval ztratu.\n"); exit(0); } curfont=(void *)&boldcz; if (argc>2) { switch (argc) { case 8:sscanf(argv[7],"%d",&colorq); case 7:sscanf(argv[6],"%d",&max_fr); case 6:sscanf(argv[5],"%d",&min_fr); case 5:sscanf(argv[4],"%d",&lowq); case 4:sscanf(argv[3],"%d",&speed); } create_mgf_file(argv[2]); initmode32(); compress_pcx(argv[1]); closemode(); } else { script_compress(argv[1]); } puts("Zaviram Motion GIF..."); close_mgf_file(); }